Fixing some dependencies in script canvas and emfx
This commit is contained in:
@@ -1,448 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EMotionFX_precompiled.h"
|
||||
#include <Integration/Components/AnimGraphNetSyncComponent.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <GridMate/Replica/ReplicaFunctions.h>
|
||||
#include <GridMate/Serialize/MathMarshal.h>
|
||||
#include <AzFramework/Network/NetBindingHandlerBus.h>
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
namespace Integration
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/**
|
||||
* \brief This is a GridMate chunk that replicates Anim Graph parameters.
|
||||
* It's challenge is to replicate any of the supported parameter types where
|
||||
* the types are only known at runtime. To solve that, many datasets are created
|
||||
* with helper macros to avoid code duplication (@PARAM_DATASET and @PARAM_DATASET_NAME).
|
||||
*
|
||||
* For maximum compression, one should build a custom component that specifies the anim graph parameters by hand, for example:
|
||||
*
|
||||
* DataSet<float> m_param0;
|
||||
*
|
||||
* or if using delta compression feature of GridMate:
|
||||
*
|
||||
* DeltaCompressedDataSet<float, 1> m_param1;
|
||||
*
|
||||
* Active nodes (@m_activeNodes) change infrequently.
|
||||
*
|
||||
* Warning: @m_motionNodes motion nodes often do change frequently as their motion play time ticks down.
|
||||
* Care must be applied when aiming for the network budget of a project.
|
||||
*/
|
||||
class AnimGraphNetSyncComponent::Chunk : public GridMate::ReplicaChunkBase
|
||||
{
|
||||
public:
|
||||
GM_CLASS_ALLOCATOR(Chunk);
|
||||
|
||||
Chunk() : m_activeNodes("Active Nodes", NodeIndexContainer{}), m_motionNodes("Motion Nodes", MotionNodePlaytimeContainer{}) {}
|
||||
|
||||
static const char* GetChunkName() { return "AnimGraphNetSyncComponent::Chunk"; }
|
||||
bool IsReplicaMigratable() override { return true; }
|
||||
|
||||
using AnimDataSetType = GridMate::DataSet<AnimParameter, AnimParameterMarshaler, AnimParameterThrottler>;
|
||||
|
||||
template <void (AnimGraphNetSyncComponent::* CallbackMethod)(const AnimParameter&, const GridMate::TimeContext&)>
|
||||
using AnimDataSet = AnimDataSetType::BindInterface<AnimGraphNetSyncComponent, CallbackMethod>;
|
||||
|
||||
// A helper macro that creates a variable like this one:
|
||||
// AnimDataSet<&AnimGraphNetSyncComponent::OnAnimParameterChanged<0>> m_parameter0 = { "Param 0" };
|
||||
#define PARAM_DATASET( N ) AnimDataSet<&AnimGraphNetSyncComponent::OnAnimParameterChanged< N >> m_parameter##N = { "Param " #N }
|
||||
|
||||
PARAM_DATASET(0);
|
||||
PARAM_DATASET(1);
|
||||
PARAM_DATASET(2);
|
||||
PARAM_DATASET(3);
|
||||
PARAM_DATASET(4);
|
||||
PARAM_DATASET(5);
|
||||
PARAM_DATASET(6);
|
||||
PARAM_DATASET(7);
|
||||
PARAM_DATASET(8);
|
||||
PARAM_DATASET(9);
|
||||
|
||||
/*
|
||||
* Note: GridMate by default supports up to 32 DataSets per ReplicaChunk: @GM_MAX_DATASETS_IN_CHUNK.
|
||||
* That means that a component can sync up to 32 separate network fields. One can vary the number of supported number
|
||||
* of parameters by simply creating new entries of @PARAM_DATASET above and @PARAM_DATASET_NAME below.
|
||||
*/
|
||||
|
||||
// A collection of datasets that are used to synchronize anim graph parameters.
|
||||
AZStd::array<AnimDataSetType*, 10> m_parameters = { { // clang pre-6.0 requires double "{{" here but doesn't perform compile length verification :(
|
||||
&m_parameter0,
|
||||
&m_parameter1,
|
||||
&m_parameter2,
|
||||
&m_parameter3,
|
||||
&m_parameter4,
|
||||
&m_parameter5,
|
||||
&m_parameter6,
|
||||
&m_parameter7,
|
||||
&m_parameter8,
|
||||
&m_parameter9,
|
||||
} };
|
||||
|
||||
GridMate::DataSet<NodeIndexContainer, NodeIndexContainerMarshaler>::
|
||||
BindInterface<AnimGraphNetSyncComponent, &AnimGraphNetSyncComponent::OnActiveNodesChanged> m_activeNodes;
|
||||
GridMate::DataSet<MotionNodePlaytimeContainer, MotionNodePlaytimeContainerMarshaler>::
|
||||
BindInterface<AnimGraphNetSyncComponent, &AnimGraphNetSyncComponent::OnMotionNodesChanged> m_motionNodes;
|
||||
};
|
||||
|
||||
void AnimGraphNetSyncComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
GridMate::ReplicaChunkDescriptorTable& descTable = GridMate::ReplicaChunkDescriptorTable::Get();
|
||||
if (!descTable.FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(Chunk::GetChunkName())))
|
||||
{
|
||||
descTable.RegisterChunkType<Chunk>();
|
||||
}
|
||||
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AnimGraphNetSyncComponent, AZ::Component>()
|
||||
->Version(1)
|
||||
->Field( "Sync parameters", &AnimGraphNetSyncComponent::m_syncParameters )
|
||||
->Field( "Sync active nodes", &AnimGraphNetSyncComponent::m_syncActiveNodes )
|
||||
->Field( "Sync motion nodes", &AnimGraphNetSyncComponent::m_syncMotionNodes )
|
||||
;
|
||||
|
||||
AZ::EditContext* editContent = serializeContext->GetEditContext();
|
||||
if (editContent)
|
||||
{
|
||||
editContent->Class<AnimGraphNetSyncComponent>("Anim Graph Net Sync",
|
||||
"Replicates anim graph parameters over the network using GridMate")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Networking")
|
||||
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/AnimGraphNetSync.svg")
|
||||
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncParameters, "Sync parameters",
|
||||
"Synchronize parameters of the anim graph on the entity" )
|
||||
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncActiveNodes, "Sync active nodes",
|
||||
"Synchronize active nodes in the anim graph on the entity" )
|
||||
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncMotionNodes, "Sync motion nodes",
|
||||
"Synchronize motion nodes in the anim graph on the entity. Warning: this may take a significant amount of network bandwidth" )
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::Activate()
|
||||
{
|
||||
AnimGraphComponentNotificationBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
if (m_syncMotionNodes || m_syncActiveNodes) // if there is anything synchronize over the network
|
||||
{
|
||||
const bool isAuthoritative = AzFramework::NetQuery::IsEntityAuthoritative(GetEntityId());
|
||||
if (isAuthoritative)
|
||||
{
|
||||
// Only the server (or authoritative entity) needs to watch the nodes values.
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
// We need to get anim graph instance. It will be either available to us now or later via a notification bus. See @OnAnimGraphInstanceCreated
|
||||
AnimGraphComponentRequestBus::EventResult(m_instance, GetEntityId(), &AnimGraphComponentRequestBus::Events::GetAnimGraphInstance);
|
||||
if (m_instance)
|
||||
{
|
||||
if (!m_instance->GetSnapshot())
|
||||
{
|
||||
m_instance->CreateSnapshot(isAuthoritative);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::Deactivate()
|
||||
{
|
||||
AnimGraphComponentNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::SetParameterOnClient(const AnimParameter& value, AZ::u8 index)
|
||||
{
|
||||
switch (value.m_type)
|
||||
{
|
||||
case AnimParameter::Type::Unsupported:
|
||||
break;
|
||||
case AnimParameter::Type::Float:
|
||||
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterFloat, index, value.m_value.f);
|
||||
break;
|
||||
case AnimParameter::Type::Bool:
|
||||
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterBool, index, value.m_value.b);
|
||||
break;
|
||||
case AnimParameter::Type::Vector2:
|
||||
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterVector2, index, value.m_value.v2);
|
||||
break;
|
||||
case AnimParameter::Type::Vector3:
|
||||
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterVector3, index, value.m_value.v3);
|
||||
break;
|
||||
case AnimParameter::Type::Quaternion:
|
||||
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterRotation, index, value.m_value.q);
|
||||
break;
|
||||
default:
|
||||
AZ_Assert(false, "Unsupported type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <AZ::u8 Index>
|
||||
void AnimGraphNetSyncComponent::OnAnimParameterChanged(const AnimParameter& value, const GridMate::TimeContext&)
|
||||
{
|
||||
SetParameterOnClient(value, Index);
|
||||
}
|
||||
|
||||
template <AnimParameter::Type AnimParameterType, typename FieldType>
|
||||
void AnimGraphNetSyncComponent::SetParameterOnServer(AZ::u8 parameterIndex, const FieldType& newValue)
|
||||
{
|
||||
if (m_syncParameters)
|
||||
{
|
||||
if (Chunk* chunk = GetChunk())
|
||||
{
|
||||
if (parameterIndex < chunk->m_parameters.size())
|
||||
{
|
||||
AnimParameter param;
|
||||
param.m_type = AnimParameterType;
|
||||
|
||||
static_assert(sizeof(FieldType) <= sizeof(param.m_value), "The largest value param.m_value can store is a Quaternion");
|
||||
// This is to simplify writing a value into a union.
|
||||
// Ideally, one would use std::variant (C++17) instead of a union.
|
||||
memcpy(¶m.m_value, &newValue, sizeof(FieldType));
|
||||
|
||||
chunk->m_parameters[parameterIndex]->Set(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("EMotionFX", false, "AnimGraphNetSyncComponent does not support synchronizing more than %u parameters", chunk->m_parameters.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphFloatParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
float beforeValue,
|
||||
float afterValue)
|
||||
{
|
||||
AZ_UNUSED(beforeValue);
|
||||
SetParameterOnServer<AnimParameter::Type::Float>(static_cast<AZ::u8>(parameterIndex), afterValue);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphBoolParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
bool beforeValue,
|
||||
bool afterValue)
|
||||
{
|
||||
AZ_UNUSED(beforeValue);
|
||||
SetParameterOnServer<AnimParameter::Type::Bool>(static_cast<AZ::u8>(parameterIndex), afterValue);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphStringParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const char* beforeValue,
|
||||
const char* afterValue)
|
||||
{
|
||||
AZ_UNUSED(parameterIndex);
|
||||
AZ_UNUSED(beforeValue);
|
||||
AZ_UNUSED(afterValue);
|
||||
AZ_Warning("EMotionFX", false, "AnimGraphNetSync component does not supported synchronizing string parameters, please consider refactoring your anim graph to replace strings with integers or enum values.");
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphVector2ParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Vector2& beforeValue,
|
||||
const AZ::Vector2& afterValue)
|
||||
{
|
||||
AZ_UNUSED(beforeValue);
|
||||
SetParameterOnServer<AnimParameter::Type::Vector2>(static_cast<AZ::u8>(parameterIndex), afterValue);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphVector3ParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Vector3& beforeValue,
|
||||
const AZ::Vector3& afterValue)
|
||||
{
|
||||
AZ_UNUSED(beforeValue);
|
||||
SetParameterOnServer<AnimParameter::Type::Vector3>(static_cast<AZ::u8>(parameterIndex), afterValue);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphRotationParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Quaternion& beforeValue,
|
||||
const AZ::Quaternion& afterValue)
|
||||
{
|
||||
AZ_UNUSED(beforeValue);
|
||||
SetParameterOnServer<AnimParameter::Type::Quaternion>(static_cast<AZ::u8>(parameterIndex), afterValue);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnActiveNodesChanged(const NodeIndexContainer& activeNodes, const GridMate::TimeContext& tc)
|
||||
{
|
||||
AZ_UNUSED(tc);
|
||||
// Client receiving values
|
||||
if (m_instance)
|
||||
{
|
||||
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
|
||||
{
|
||||
snapshot->SetActiveNodes(activeNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnMotionNodesChanged(const MotionNodePlaytimeContainer& motionNodes, const GridMate::TimeContext& tc)
|
||||
{
|
||||
AZ_UNUSED(tc);
|
||||
// Client receiving values
|
||||
if (m_instance)
|
||||
{
|
||||
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
|
||||
{
|
||||
snapshot->SetMotionNodePlaytimes(motionNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimGraphNetSyncComponent::IsDifferent(const MotionNodePlaytimeContainer& oldList, const MotionNodePlaytimeContainer& newList) const
|
||||
{
|
||||
if (oldList.size() != newList.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::size_t i = 0;
|
||||
for (auto& value : oldList)
|
||||
{
|
||||
if (value.first != newList[i].first || value.second != newList[i].second)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnimGraphNetSyncComponent::IsDifferent(const NodeIndexContainer& oldList, const NodeIndexContainer& newList) const
|
||||
{
|
||||
if (oldList.size() != newList.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::size_t i = 0;
|
||||
for (AZ::u32 value : oldList)
|
||||
{
|
||||
if (value != newList[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
|
||||
{
|
||||
AZ_UNUSED(deltaTime);
|
||||
AZ_UNUSED(time);
|
||||
|
||||
if (!GetChunk())
|
||||
{
|
||||
return; // network is not ready yet
|
||||
}
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
|
||||
{
|
||||
if (m_syncActiveNodes)
|
||||
{
|
||||
const NodeIndexContainer& activeNodes = snapshot->GetActiveNodes();
|
||||
const NodeIndexContainer& currentValue = GetChunk()->m_activeNodes.Get();
|
||||
if (IsDifferent(currentValue, activeNodes))
|
||||
{
|
||||
GetChunk()->m_activeNodes.Set(activeNodes); // Server sending the values
|
||||
}
|
||||
}
|
||||
|
||||
if (m_syncMotionNodes)
|
||||
{
|
||||
const MotionNodePlaytimeContainer& playTimes = snapshot->GetMotionNodePlaytimes();
|
||||
const MotionNodePlaytimeContainer& currentTimes = GetChunk()->m_motionNodes.Get();
|
||||
if (IsDifferent(currentTimes, playTimes))
|
||||
{
|
||||
GetChunk()->m_motionNodes.Set(playTimes); // Server sending the values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphInstanceCreated(EMotionFX::AnimGraphInstance* instance)
|
||||
{
|
||||
m_instance = instance;
|
||||
if (m_instance)
|
||||
{
|
||||
const bool isAuthoritative = AzFramework::NetQuery::IsEntityAuthoritative(GetEntityId());
|
||||
if (!m_instance->GetSnapshot())
|
||||
{
|
||||
m_instance->CreateSnapshot(isAuthoritative);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::OnAnimGraphInstanceDestroyed(EMotionFX::AnimGraphInstance*)
|
||||
{
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
AnimGraphNetSyncComponent::Chunk* AnimGraphNetSyncComponent::GetChunk() const
|
||||
{
|
||||
return static_cast<Chunk*>(m_chunk.get());
|
||||
}
|
||||
|
||||
GridMate::ReplicaChunkPtr AnimGraphNetSyncComponent::GetNetworkBinding()
|
||||
{
|
||||
m_chunk = GridMate::CreateReplicaChunk<Chunk>();
|
||||
AZ_Assert(m_chunk, "Failed to create a chunk");
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
if (!m_instance->GetSnapshot())
|
||||
{
|
||||
m_instance->CreateSnapshot(true /* authoritative */);
|
||||
}
|
||||
}
|
||||
|
||||
return m_chunk;
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::SetNetworkBinding(GridMate::ReplicaChunkPtr chunk)
|
||||
{
|
||||
m_chunk = chunk;
|
||||
m_chunk->SetHandler(this);
|
||||
}
|
||||
|
||||
void AnimGraphNetSyncComponent::UnbindFromNetwork()
|
||||
{
|
||||
AZ_Assert(m_chunk, "There wasn't any chunk present");
|
||||
if (m_chunk)
|
||||
{
|
||||
m_chunk->SetHandler(nullptr);
|
||||
m_chunk = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Integration
|
||||
} // namespace EMotionFXAnimation
|
||||
@@ -1,153 +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/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzFramework/Network/NetBindable.h>
|
||||
#include <Integration/AnimGraphComponentBus.h>
|
||||
#include <Integration/Components/AnimGraphNetSyncTypes.h>
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
namespace Integration
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/**
|
||||
* \brief Generic solution for synchronizing parameters of Anim Graph component.
|
||||
* Synchronization is done over GridMate.
|
||||
*
|
||||
* Note that this is not the most optimal synchronization but it does
|
||||
* work for just about all Anim Graphs.
|
||||
*
|
||||
* Disclaimer: string parameters are not supported! Because one should not synchronize
|
||||
* strings over the network. They ought to be converted to enum/int values beforehand.
|
||||
*/
|
||||
class AnimGraphNetSyncComponent
|
||||
: public AZ::Component
|
||||
, public AzFramework::NetBindable
|
||||
, public AnimGraphComponentNotificationBus::Handler
|
||||
, public AZ::TickBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AnimGraphNetSyncComponent, "{2F9428C1-0F07-4667-B052-40D9BC473AD3}", NetBindable);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component interface implementation
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("EMotionFXAnimGraphNetSyncService", 0x42e6f127));
|
||||
}
|
||||
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("EMotionFXAnimGraphNetSyncService", 0x42e6f127));
|
||||
}
|
||||
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC("EMotionFXAnimGraphService", 0x9ec3c819));
|
||||
required.push_back(AZ_CRC("ReplicaChunkService", 0xf86b88a8));
|
||||
}
|
||||
|
||||
protected:
|
||||
// NetBindable interface implementation
|
||||
GridMate::ReplicaChunkPtr GetNetworkBinding() override;
|
||||
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk) override;
|
||||
void UnbindFromNetwork() override;
|
||||
|
||||
// AnimGraphComponentNotificationBus interface implementation
|
||||
void OnAnimGraphFloatParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
float beforeValue,
|
||||
float afterValue) override;
|
||||
void OnAnimGraphBoolParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
bool beforeValue,
|
||||
bool afterValue) override;
|
||||
void OnAnimGraphStringParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const char* beforeValue,
|
||||
const char* afterValue) override;
|
||||
void OnAnimGraphVector2ParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Vector2& beforeValue,
|
||||
const AZ::Vector2& afterValue) override;
|
||||
void OnAnimGraphVector3ParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Vector3& beforeValue,
|
||||
const AZ::Vector3& afterValue) override;
|
||||
void OnAnimGraphRotationParameterChanged(EMotionFX::AnimGraphInstance*,
|
||||
AZ::u32 parameterIndex,
|
||||
const AZ::Quaternion& beforeValue,
|
||||
const AZ::Quaternion& afterValue) override;
|
||||
|
||||
// TickBus
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
// AnimGraphComponentNotificationBus
|
||||
void OnAnimGraphInstanceCreated(EMotionFX::AnimGraphInstance* instance) override;
|
||||
void OnAnimGraphInstanceDestroyed(EMotionFX::AnimGraphInstance* instance) override;
|
||||
|
||||
private:
|
||||
class Chunk;
|
||||
GridMate::ReplicaChunkPtr m_chunk;
|
||||
Chunk* GetChunk() const;
|
||||
|
||||
// DataSet callback, it's a template to avoid duplicating similar callbacks
|
||||
template <AZ::u8 Index>
|
||||
void OnAnimParameterChanged(const AnimParameter& value, const GridMate::TimeContext& tc);
|
||||
|
||||
// Helper on a client side
|
||||
void SetParameterOnClient(const AnimParameter& value, AZ::u8 index);
|
||||
|
||||
// Helper on the server side to avoid duplicating very similar callbacks
|
||||
template <AnimParameter::Type AnimParameterType, typename FieldType>
|
||||
void SetParameterOnServer(AZ::u8 parameterIndex, const FieldType& newValue);
|
||||
|
||||
/**
|
||||
* \brief Optionally turn on or off replicating parameters of an anim graph on the same entity as this component.
|
||||
*/
|
||||
bool m_syncParameters = true;
|
||||
|
||||
/**
|
||||
* \brief Optionally turn on or off replicating active nodes of an anim graph on the same entity as this component.
|
||||
*/
|
||||
bool m_syncActiveNodes = false;
|
||||
/**
|
||||
* \brief Optionally turn on or off replicating motion playtime nodes of an anim graph on the same entity as this component.
|
||||
*
|
||||
* It's off by default because these nodes are very frequently changing and would result in a high network bandwidth use.
|
||||
*/
|
||||
bool m_syncMotionNodes = false;
|
||||
|
||||
// GridMate DataSet callback on clients
|
||||
void OnActiveNodesChanged(const NodeIndexContainer& activeNodes, const GridMate::TimeContext& tc);
|
||||
// GridMate DataSet callback on clients
|
||||
void OnMotionNodesChanged(const MotionNodePlaytimeContainer& motionNodes, const GridMate::TimeContext& tc);
|
||||
|
||||
// Helper comparison method to avoid sending the same data
|
||||
bool IsDifferent(const NodeIndexContainer& oldList, const NodeIndexContainer& newList) const;
|
||||
// Helper comparison method to avoid sending the same data
|
||||
bool IsDifferent(const MotionNodePlaytimeContainer& oldList, const MotionNodePlaytimeContainer& newList) const;
|
||||
|
||||
EMotionFX::AnimGraphInstance* m_instance = nullptr;
|
||||
};
|
||||
}
|
||||
} // namespace Integration
|
||||
} // namespace EMotionFX
|
||||
@@ -1,285 +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 <GridMate/Serialize/Buffer.h>
|
||||
#include <GridMate/Serialize/MathMarshal.h>
|
||||
#include <GridMate/Serialize/CompressionMarshal.h>
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
namespace Integration
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/**
|
||||
* \brief A general storage for an anim graph parameter.
|
||||
*/
|
||||
class AnimParameter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief String type is not supported because one should not be syncing strings over the network.
|
||||
*/
|
||||
enum class Type : AZ::u8
|
||||
{
|
||||
Unsupported,
|
||||
Float,
|
||||
Bool,
|
||||
Vector2,
|
||||
Vector3,
|
||||
Quaternion,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A storage for all possible supported types in @AnimGraphNetSyncComponent
|
||||
*/
|
||||
union Value
|
||||
{
|
||||
Value()
|
||||
{
|
||||
q = AZ::Quaternion::CreateZero();
|
||||
}
|
||||
|
||||
float f;
|
||||
bool b = false;
|
||||
AZ::Vector2 v2;
|
||||
AZ::Vector3 v3;
|
||||
AZ::Quaternion q;
|
||||
};
|
||||
|
||||
AnimParameter() : m_type(Type::Unsupported) {}
|
||||
|
||||
Type m_type;
|
||||
Value m_value;
|
||||
|
||||
AnimParameter(const AnimParameter& other)
|
||||
{
|
||||
m_type = other.m_type;
|
||||
CopyValue(other);
|
||||
}
|
||||
|
||||
AnimParameter& operator=(const AnimParameter& other)
|
||||
{
|
||||
m_type = other.m_type;
|
||||
CopyValue(other);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==(const AnimParameter& lhs, const AnimParameter& rhs)
|
||||
{
|
||||
if (lhs.m_type != rhs.m_type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (lhs.m_type)
|
||||
{
|
||||
case Type::Float:
|
||||
return lhs.m_value.f == rhs.m_value.f;
|
||||
case Type::Bool:
|
||||
return lhs.m_value.b == rhs.m_value.b;
|
||||
case Type::Vector2:
|
||||
return lhs.m_value.v2 == rhs.m_value.v2;
|
||||
case Type::Vector3:
|
||||
return lhs.m_value.v3 == rhs.m_value.v3;
|
||||
case Type::Quaternion:
|
||||
return lhs.m_value.q == rhs.m_value.q;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void CopyValue(const AnimParameter& other)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::Float:
|
||||
m_value.f = other.m_value.f;
|
||||
break;
|
||||
case Type::Bool:
|
||||
m_value.b = other.m_value.b;
|
||||
break;
|
||||
case Type::Vector2:
|
||||
m_value.v2 = other.m_value.v2;
|
||||
break;
|
||||
case Type::Vector3:
|
||||
m_value.v3 = other.m_value.v3;
|
||||
break;
|
||||
case Type::Quaternion:
|
||||
m_value.q = other.m_value.q;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Custom GridMate throttler. See GridMate:: @BasicThrottle
|
||||
*/
|
||||
class AnimParameterThrottler
|
||||
{
|
||||
public:
|
||||
bool WithinThreshold(const AnimParameter& newValue) const
|
||||
{
|
||||
return m_baseline == newValue;
|
||||
}
|
||||
|
||||
void UpdateBaseline(const AnimParameter& baseline)
|
||||
{
|
||||
m_baseline = baseline;
|
||||
}
|
||||
|
||||
private:
|
||||
AnimParameter m_baseline;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A custom GridMate marshaler.
|
||||
* 1 byte is spend on the type. And a variable number of bytes afterwards for the value.
|
||||
*/
|
||||
class AnimParameterMarshaler
|
||||
{
|
||||
public:
|
||||
void Marshal(GridMate::WriteBuffer& wb, const AnimParameter& parameter)
|
||||
{
|
||||
wb.Write(AZ::u8(parameter.m_type));
|
||||
|
||||
switch (parameter.m_type)
|
||||
{
|
||||
case AnimParameter::Type::Float:
|
||||
wb.Write(parameter.m_value.f);
|
||||
break;
|
||||
case AnimParameter::Type::Bool:
|
||||
wb.Write(parameter.m_value.b);
|
||||
break;
|
||||
case AnimParameter::Type::Vector2:
|
||||
wb.Write(parameter.m_value.v2);
|
||||
break;
|
||||
case AnimParameter::Type::Vector3:
|
||||
wb.Write(parameter.m_value.v3);
|
||||
break;
|
||||
case AnimParameter::Type::Quaternion:
|
||||
wb.Write(parameter.m_value.q);
|
||||
break;
|
||||
default:
|
||||
// other types are not supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Unmarshal(AnimParameter& parameter, GridMate::ReadBuffer& rb)
|
||||
{
|
||||
AZ::u8 type;
|
||||
rb.Read(type);
|
||||
parameter.m_type = static_cast<AnimParameter::Type>(type);
|
||||
|
||||
switch (parameter.m_type)
|
||||
{
|
||||
case AnimParameter::Type::Float:
|
||||
rb.Read(parameter.m_value.f);
|
||||
break;
|
||||
case AnimParameter::Type::Bool:
|
||||
rb.Read(parameter.m_value.b);
|
||||
break;
|
||||
case AnimParameter::Type::Vector2:
|
||||
rb.Read(parameter.m_value.v2);
|
||||
break;
|
||||
case AnimParameter::Type::Vector3:
|
||||
rb.Read(parameter.m_value.v3);
|
||||
break;
|
||||
case AnimParameter::Type::Quaternion:
|
||||
rb.Read(parameter.m_value.q);
|
||||
break;
|
||||
default:
|
||||
// other types are not supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Custom marshaler for Animation node index that is used by Activate Nodes list
|
||||
*/
|
||||
struct NodeIndexContainerMarshaler
|
||||
{
|
||||
void Marshal(GridMate::WriteBuffer& wb, const NodeIndexContainer& source) const
|
||||
{
|
||||
GridMate::VlqU64Marshaler m64;
|
||||
GridMate::VlqU32Marshaler m32;
|
||||
|
||||
m64.Marshal(wb, source.size()); // 1 byte most of the time (if the size is less than 127)
|
||||
for (AZ::u32 item : source)
|
||||
{
|
||||
m32.Marshal(wb, item); // 1 byte most of the time (if the value is less than 127)
|
||||
}
|
||||
}
|
||||
|
||||
void Unmarshal(NodeIndexContainer& target, GridMate::ReadBuffer& rb) const
|
||||
{
|
||||
target.clear();
|
||||
GridMate::VlqU64Marshaler m64;
|
||||
GridMate::VlqU32Marshaler m32;
|
||||
|
||||
AZ::u64 arraySize;
|
||||
m64.Unmarshal(arraySize, rb);
|
||||
target.resize(arraySize);
|
||||
|
||||
for (AZ::u64 i = 0; i < arraySize; ++i)
|
||||
{
|
||||
m32.Unmarshal(target[i], rb);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Custom marshaler for Animation motion node information that is used by motion node playtime list
|
||||
*/
|
||||
struct MotionNodePlaytimeContainerMarshaler
|
||||
{
|
||||
void Marshal(GridMate::WriteBuffer& wb, const MotionNodePlaytimeContainer& source) const
|
||||
{
|
||||
GridMate::VlqU64Marshaler m64;
|
||||
GridMate::VlqU32Marshaler m32;
|
||||
|
||||
m64.Marshal(wb, source.size());
|
||||
for (const AZStd::pair<AZ::u32, float>& item : source)
|
||||
{
|
||||
m32.Marshal(wb, item.first); // average of 1 byte
|
||||
wb.Write(item.second); // 4 bytes
|
||||
}
|
||||
}
|
||||
|
||||
void Unmarshal(MotionNodePlaytimeContainer& target, GridMate::ReadBuffer& rb) const
|
||||
{
|
||||
target.clear();
|
||||
GridMate::VlqU64Marshaler m64;
|
||||
GridMate::VlqU32Marshaler m32;
|
||||
|
||||
AZ::u64 arraySize;
|
||||
m64.Unmarshal(arraySize, rb);
|
||||
target.resize(arraySize);
|
||||
|
||||
for (AZ::u64 i = 0; i < arraySize; ++i)
|
||||
{
|
||||
m32.Unmarshal(target[i].first, rb);
|
||||
rb.Read(target[i].second);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace Integration
|
||||
} // namespace EMotionFXAnimation
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <Integration/Components/ActorComponent.h>
|
||||
#include <Integration/Components/AnimAudioComponent.h>
|
||||
#include <Integration/Components/AnimGraphComponent.h>
|
||||
#include <Integration/Components/AnimGraphNetSyncComponent.h>
|
||||
#include <Integration/Components/SimpleMotionComponent.h>
|
||||
#include <Integration/Components/SimpleLODComponent.h>
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
@@ -74,8 +73,6 @@ namespace EMotionFX
|
||||
AnimGraphComponent::CreateDescriptor(),
|
||||
SimpleMotionComponent::CreateDescriptor(),
|
||||
SimpleLODComponent::CreateDescriptor(),
|
||||
|
||||
Network::AnimGraphNetSyncComponent::CreateDescriptor(),
|
||||
|
||||
#if defined(EMOTIONFXANIMATION_EDITOR)
|
||||
// Pipeline components
|
||||
|
||||
@@ -31,9 +31,6 @@ set(FILES
|
||||
Source/Integration/Components/ActorComponent.cpp
|
||||
Source/Integration/Components/AnimAudioComponent.h
|
||||
Source/Integration/Components/AnimAudioComponent.cpp
|
||||
Source/Integration/Components/AnimGraphNetSyncComponent.h
|
||||
Source/Integration/Components/AnimGraphNetSyncTypes.h
|
||||
Source/Integration/Components/AnimGraphNetSyncComponent.cpp
|
||||
Source/Integration/Components/AnimGraphComponent.h
|
||||
Source/Integration/Components/AnimGraphComponent.cpp
|
||||
Source/Integration/Components/SimpleMotionComponent.h
|
||||
|
||||
@@ -19,11 +19,9 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzFramework/Network/NetBindingHandlerBus.h>
|
||||
#include <ScriptCanvas/Execution/ExecutionBus.h>
|
||||
#include <ScriptCanvas/Execution/ExecutionContext.h>
|
||||
#include <ScriptCanvas/Execution/ExecutionState.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
|
||||
|
||||
#if !defined(_RELEASE) && !defined(PERFORMANCE_BUILD)
|
||||
#define SCRIPT_CANVAS_RUNTIME_ASSET_CHECK
|
||||
@@ -163,8 +161,6 @@ namespace ScriptCanvas
|
||||
->Field("m_variableOverrides", &RuntimeComponent::m_variableOverrides)
|
||||
;
|
||||
}
|
||||
|
||||
GraphVariableNetBindingTable::Reflect(context);
|
||||
}
|
||||
|
||||
void RuntimeComponent::SetVariableOverrides(const VariableData& overrideData)
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
|
||||
#include <AzFramework/Network/NetBindable.h>
|
||||
|
||||
#include <ScriptCanvas/Asset/RuntimeAsset.h>
|
||||
#include <ScriptCanvas/Core/Core.h>
|
||||
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
|
||||
@@ -41,11 +39,10 @@ namespace ScriptCanvas
|
||||
//! This component should only be used at runtime
|
||||
class RuntimeComponent
|
||||
: public AZ::Component
|
||||
, public AzFramework::NetBindable
|
||||
, public AZ::EntityBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(RuntimeComponent, "{95BFD916-E832-4956-837D-525DE8384282}", NetBindable);
|
||||
AZ_COMPONENT(RuntimeComponent, "{95BFD916-E832-4956-837D-525DE8384282}", AZ::Component);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
@@ -67,8 +64,6 @@ namespace ScriptCanvas
|
||||
|
||||
const VariableData& GetVariableOverrides() const;
|
||||
|
||||
void SetNetworkBinding(GridMate::ReplicaChunkPtr) {}
|
||||
|
||||
void SetVariableOverrides(const VariableData& overrideData);
|
||||
|
||||
protected:
|
||||
@@ -103,8 +98,6 @@ namespace ScriptCanvas
|
||||
|
||||
void StopExecution();
|
||||
|
||||
void UnbindFromNetwork(void) {}
|
||||
|
||||
private:
|
||||
AZ::Data::Asset<RuntimeAsset> m_runtimeAsset;
|
||||
ExecutionStatePtr m_executionState;
|
||||
|
||||
@@ -1,245 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ScriptCanvas/Variable/GraphVariableMarshal.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariable.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
|
||||
#include <ScriptCanvas/Core/ModifiableDatumView.h>
|
||||
#include <AzFramework/Network/EntityIdMarshaler.h>
|
||||
#include <GridMate/Serialize/MathMarshal.h>
|
||||
#include <GridMate/Serialize/UtilityMarshal.h>
|
||||
#include <GridMate/Serialize/UuidMarshal.h>
|
||||
|
||||
namespace ScriptCanvas
|
||||
{
|
||||
void DatumMarshaler::SetNetBindingTable(GraphVariableNetBindingTable* netBindingTable)
|
||||
{
|
||||
m_graphVariableNetBindingTable = netBindingTable;
|
||||
}
|
||||
|
||||
void DatumMarshaler::Marshal(GridMate::WriteBuffer& wb, const Datum* const & property) const
|
||||
{
|
||||
if (!property)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GridMate::Marshaler<Data::eType> typeMarshaler;
|
||||
const Data::eType& datumType = property->GetType().GetType();
|
||||
typeMarshaler.Marshal(wb, datumType);
|
||||
|
||||
VariableId assetVariableId;
|
||||
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& variableIdMap = m_graphVariableNetBindingTable->GetVariableIdMap();
|
||||
|
||||
for (AZStd::pair<VariableId, AZStd::pair<GraphVariable*, int>>& pair : variableIdMap)
|
||||
{
|
||||
AZStd::pair<GraphVariable*, int>& variableIndexPair = pair.second;
|
||||
|
||||
if (variableIndexPair.first->GetDatum() == property)
|
||||
{
|
||||
assetVariableId = m_graphVariableNetBindingTable->FindAssetVariableIdByRuntimeVariableId(pair.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!assetVariableId.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GridMate::Marshaler<AZ::Uuid> uuidMarshaler;
|
||||
uuidMarshaler.Marshal(wb, assetVariableId.GetDatumId());
|
||||
|
||||
AZStd::string uuidString = assetVariableId.m_id.ToString<AZStd::string>();
|
||||
|
||||
switch (datumType)
|
||||
{
|
||||
case Data::eType::AABB:
|
||||
MarshalType<Data::AABBType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Boolean:
|
||||
MarshalType<Data::BooleanType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Color:
|
||||
MarshalType<Data::ColorType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::CRC:
|
||||
MarshalType<Data::CRCType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::EntityID:
|
||||
MarshalType<Data::EntityIDType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Matrix3x3:
|
||||
MarshalType<Data::Matrix3x3Type>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Matrix4x4:
|
||||
MarshalType<Data::Matrix4x4Type>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::NamedEntityID:
|
||||
MarshalType<Data::NamedEntityIDType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Number:
|
||||
MarshalType<Data::NumberType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::OBB:
|
||||
MarshalType<Data::OBBType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Plane:
|
||||
MarshalType<Data::PlaneType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Quaternion:
|
||||
MarshalType<Data::QuaternionType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::String:
|
||||
MarshalType<Data::StringType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Transform:
|
||||
MarshalType<Data::TransformType>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Vector2:
|
||||
MarshalType<Data::Vector2Type>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Vector3:
|
||||
MarshalType<Data::Vector3Type>(wb, property);
|
||||
break;
|
||||
|
||||
case Data::eType::Vector4:
|
||||
MarshalType<Data::Vector4Type>(wb, property);
|
||||
break;
|
||||
|
||||
default:
|
||||
AZ_Warning("ScriptCanvasNetworking", false, "Marshal unsupported data type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DatumMarshaler::UnmarshalToPointer(const Datum*& target, GridMate::ReadBuffer& rb)
|
||||
{
|
||||
// :SCTODO: for some reason, this UnmarshalToPointer can get called before SetNetworkBinding is called
|
||||
// (which is where we set m_graphVariableNetBindingTable). So we check for nullptr here just in case.
|
||||
if (!m_graphVariableNetBindingTable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptCanvas::Data::eType datumType = Data::eType::Invalid;
|
||||
GridMate::Marshaler<Data::eType> typeMarshaler;
|
||||
typeMarshaler.Unmarshal(datumType, rb);
|
||||
|
||||
AZ::Uuid uuid;
|
||||
GridMate::Marshaler<AZ::Uuid> uuidMarshaler;
|
||||
uuidMarshaler.Unmarshal(uuid, rb);
|
||||
|
||||
VariableId runtimeVariableId = m_graphVariableNetBindingTable->FindRuntimeVariableIdByAssetVariableId(VariableId(uuid));
|
||||
|
||||
if (!runtimeVariableId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::string uuidString = runtimeVariableId.m_id.ToString<AZStd::string>();
|
||||
|
||||
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& m_variableIdMap = m_graphVariableNetBindingTable->GetVariableIdMap();
|
||||
AZStd::pair<GraphVariable*, int>& variableIndexPair = m_variableIdMap[runtimeVariableId];
|
||||
GraphVariable* graphVariable = variableIndexPair.first;
|
||||
|
||||
switch (datumType)
|
||||
{
|
||||
case Data::eType::AABB:
|
||||
return UnmarshalType<Data::AABBType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Boolean:
|
||||
return UnmarshalType<Data::BooleanType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Color:
|
||||
return UnmarshalType<Data::ColorType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::CRC:
|
||||
return UnmarshalType<Data::CRCType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::EntityID:
|
||||
return UnmarshalType<Data::EntityIDType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Matrix3x3:
|
||||
return UnmarshalType<Data::Matrix3x3Type>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Matrix4x4:
|
||||
return UnmarshalType<Data::Matrix4x4Type>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::NamedEntityID:
|
||||
return UnmarshalType<Data::NamedEntityIDType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Number:
|
||||
return UnmarshalType<Data::NumberType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::OBB:
|
||||
return UnmarshalType<Data::OBBType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Plane:
|
||||
return UnmarshalType<Data::PlaneType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Quaternion:
|
||||
return UnmarshalType<Data::QuaternionType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::String:
|
||||
return UnmarshalType<Data::StringType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Transform:
|
||||
return UnmarshalType<Data::TransformType>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Vector2:
|
||||
return UnmarshalType<Data::Vector2Type>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Vector3:
|
||||
return UnmarshalType<Data::Vector3Type>(target, rb, graphVariable);
|
||||
|
||||
case Data::eType::Vector4:
|
||||
return UnmarshalType<Data::Vector4Type>(target, rb, graphVariable);
|
||||
|
||||
default:
|
||||
AZ_Warning("ScriptCanvasNetworking", false, "Unmarshal unsupported data type");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DatumThrottler::SignalDirty()
|
||||
{
|
||||
m_isDirty = true;
|
||||
}
|
||||
|
||||
bool DatumThrottler::WithinThreshold(const Datum* newValue) const
|
||||
{
|
||||
return (newValue == nullptr || !m_isDirty);
|
||||
}
|
||||
|
||||
void DatumThrottler::UpdateBaseline([[maybe_unused]] const Datum* baseline)
|
||||
{
|
||||
m_isDirty = false;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +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 <GridMate/Serialize/ContainerMarshal.h>
|
||||
#include <ScriptCanvas/Core/Datum.h>
|
||||
#include <ScriptCanvas/Core/ModifiableDatumView.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariable.h>
|
||||
|
||||
namespace ScriptCanvas
|
||||
{
|
||||
class GraphVariableNetBindingTable;
|
||||
|
||||
class DatumMarshaler
|
||||
{
|
||||
public:
|
||||
void SetNetBindingTable(GraphVariableNetBindingTable* netBindingTable);
|
||||
void Marshal(GridMate::WriteBuffer& wb, const Datum* const & cont) const;
|
||||
bool UnmarshalToPointer(const Datum*& target, GridMate::ReadBuffer& rb);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void MarshalType(GridMate::WriteBuffer& wb, const Datum* const & property) const
|
||||
{
|
||||
GridMate::Marshaler<T> marshaler;
|
||||
const T* value = property->GetAs<T>();
|
||||
marshaler.Marshal(wb, *value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool UnmarshalType(const Datum*& target, GridMate::ReadBuffer& rb, GraphVariable* graphVariable)
|
||||
{
|
||||
bool valueChanged = false;
|
||||
ModifiableDatumView datumView;
|
||||
|
||||
if (graphVariable)
|
||||
{
|
||||
graphVariable->ConfigureDatumView(datumView);
|
||||
|
||||
if (datumView.IsValid())
|
||||
{
|
||||
GridMate::Marshaler<T> marshaler;
|
||||
T value;
|
||||
|
||||
marshaler.Unmarshal(value, rb);
|
||||
datumView.SetAs(value);
|
||||
target = graphVariable->GetDatum();
|
||||
valueChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
return valueChanged;
|
||||
}
|
||||
|
||||
private:
|
||||
//! The network binding table is needed to determine which Datum to update
|
||||
//! when unmarshaling data.
|
||||
// :SCTODO: synced Datums should be tracked via ID
|
||||
//! and that ID should be used to lookup Datums (right now we can assume
|
||||
//! which Datum should be updated, since only one Datum is supported).
|
||||
GraphVariableNetBindingTable* m_graphVariableNetBindingTable = nullptr;
|
||||
};
|
||||
|
||||
//! Simple throttler that simple operates via dirty flag.
|
||||
class DatumThrottler
|
||||
{
|
||||
public:
|
||||
DatumThrottler() = default;
|
||||
|
||||
void SignalDirty();
|
||||
bool WithinThreshold(const Datum* newValue) const;
|
||||
void UpdateBaseline(const Datum* baseline);
|
||||
|
||||
private:
|
||||
bool m_isDirty = false;
|
||||
};
|
||||
}
|
||||
@@ -1,181 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ScriptCanvas/Execution/RuntimeBus.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariable.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
|
||||
#include <ScriptCanvas/Core/Datum.h>
|
||||
#include <GridMate/Replica/DataSet.h>
|
||||
#include <GridMate/Replica/ReplicaFunctions.h>
|
||||
#include <AzFramework/Network/NetworkContext.h>
|
||||
|
||||
namespace ScriptCanvas
|
||||
{
|
||||
const char* DatumDataSet::GetDataSetName()
|
||||
{
|
||||
static size_t s_chunkIndex = 0;
|
||||
static const char* s_nameArray[] = {
|
||||
"DataSet1","DataSet2","DataSet3","DataSet4","DataSet5",
|
||||
"DataSet6","DataSet7","DataSet8","DataSet9","DataSet10",
|
||||
"DataSet11","DataSet12","DataSet13","DataSet14","DataSet15",
|
||||
"DataSet16","DataSet17","DataSet18","DataSet19","DataSet20",
|
||||
"DataSet21","DataSet22","DataSet23","DataSet24","DataSet25",
|
||||
"DataSet26","DataSet27","DataSet28","DataSet29","DataSet30",
|
||||
"DataSet31","DataSet32"
|
||||
};
|
||||
|
||||
if (s_chunkIndex > AZ_ARRAY_SIZE(s_nameArray) && AZ_ARRAY_SIZE(s_nameArray) >= 0)
|
||||
{
|
||||
s_chunkIndex = s_chunkIndex % AZ_ARRAY_SIZE(s_nameArray);
|
||||
}
|
||||
|
||||
return s_nameArray[s_chunkIndex++];
|
||||
}
|
||||
|
||||
DatumDataSet::DatumDataSet()
|
||||
: DatumDataSetType(DatumDataSet::GetDataSetName())
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// GraphVariableReplicaChunk
|
||||
//////////////////////////
|
||||
|
||||
const char* GraphVariableReplicaChunk::GetChunkName()
|
||||
{
|
||||
return "GraphVariableReplicaChunk";
|
||||
}
|
||||
|
||||
bool GraphVariableReplicaChunk::IsReplicaMigratable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// GraphVariableNetBindingTable
|
||||
//////////////////////////
|
||||
|
||||
void GraphVariableNetBindingTable::Reflect([[maybe_unused]] AZ::ReflectContext* reflect)
|
||||
{
|
||||
GridMate::ReplicaChunkDescriptorTable& descriptorTable = GridMate::ReplicaChunkDescriptorTable::Get();
|
||||
AZ::Crc32 hash = GridMate::ReplicaChunkClassId(GraphVariableReplicaChunk::GetChunkName());
|
||||
|
||||
if (!descriptorTable.FindReplicaChunkDescriptor(hash))
|
||||
{
|
||||
descriptorTable.RegisterChunkType<GraphVariableReplicaChunk>();
|
||||
}
|
||||
}
|
||||
|
||||
GridMate::ReplicaChunkPtr GraphVariableNetBindingTable::GetNetworkBinding()
|
||||
{
|
||||
if (!m_replicaChunk)
|
||||
{
|
||||
m_replicaChunk = GridMate::CreateReplicaChunk<GraphVariableReplicaChunk>();
|
||||
m_replicaChunk->SetHandler(this);
|
||||
SetGraphNetBindingTable();
|
||||
}
|
||||
|
||||
return m_replicaChunk;
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::SetNetworkBinding(GridMate::ReplicaChunkPtr chunk)
|
||||
{
|
||||
m_replicaChunk = chunk;
|
||||
m_replicaChunk->SetHandler(this);
|
||||
SetGraphNetBindingTable();
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::UnbindFromNetwork()
|
||||
{
|
||||
if (m_replicaChunk)
|
||||
{
|
||||
m_replicaChunk->SetHandler(nullptr);
|
||||
m_replicaChunk = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::OnPropertyUpdate([[maybe_unused]] const Datum* const & scriptProperty, [[maybe_unused]] const GridMate::TimeContext& tc)
|
||||
{
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::AddDatum(GraphVariable* variable)
|
||||
{
|
||||
size_t index = m_variableIdMap.size();
|
||||
|
||||
m_variableIdMap[variable->GetVariableId()] = AZStd::make_pair(variable, static_cast<int>(index));
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::OnDatumChanged(GraphVariable& variable)
|
||||
{
|
||||
if (m_replicaChunk && m_replicaChunk->IsMaster())
|
||||
{
|
||||
GraphVariableReplicaChunk* graphVarChunk = static_cast<GraphVariableReplicaChunk*>(m_replicaChunk.get());
|
||||
auto iter = m_variableIdMap.find(variable.GetVariableId());
|
||||
|
||||
if (iter == m_variableIdMap.end())
|
||||
{
|
||||
AZ_TracePrintf("ScriptCanvasNetworking", "GraphVariableNetBindingTable::OnDatumChanged: variable not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const AZStd::pair<GraphVariable*, int>& pair = iter->second;
|
||||
DatumDataSet& datumDataSet = graphVarChunk->m_properties[pair.second];
|
||||
datumDataSet.GetThrottler().SignalDirty();
|
||||
datumDataSet.Set(variable.GetDatum());
|
||||
}
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::SetVariableMappings(const AZStd::unordered_map<VariableId, VariableId>& assetToRuntimeVariableMap, const AZStd::unordered_map<VariableId, VariableId>& runtimeToAssetVariableMap)
|
||||
{
|
||||
m_assetToRuntimeVariableMap = assetToRuntimeVariableMap;
|
||||
m_runtimeToAssetVariableMap = runtimeToAssetVariableMap;
|
||||
}
|
||||
|
||||
VariableId GraphVariableNetBindingTable::FindAssetVariableIdByRuntimeVariableId(VariableId runtimeVariableId)
|
||||
{
|
||||
auto iter = m_runtimeToAssetVariableMap.find(runtimeVariableId);
|
||||
|
||||
if (iter != m_runtimeToAssetVariableMap.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return VariableId();
|
||||
}
|
||||
|
||||
VariableId GraphVariableNetBindingTable::FindRuntimeVariableIdByAssetVariableId(VariableId assetVariableId)
|
||||
{
|
||||
auto iter = m_assetToRuntimeVariableMap.find(assetVariableId);
|
||||
|
||||
if (iter != m_assetToRuntimeVariableMap.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return VariableId();
|
||||
}
|
||||
|
||||
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& GraphVariableNetBindingTable::GetVariableIdMap()
|
||||
{
|
||||
return m_variableIdMap;
|
||||
}
|
||||
|
||||
void GraphVariableNetBindingTable::SetGraphNetBindingTable()
|
||||
{
|
||||
GraphVariableReplicaChunk* graphVariableChunk = static_cast<GraphVariableReplicaChunk*>(m_replicaChunk.get());
|
||||
|
||||
for (DatumDataSet& dataSet : graphVariableChunk->m_properties)
|
||||
{
|
||||
dataSet.GetMarshaler().SetNetBindingTable(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +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/RTTI/ReflectContext.h>
|
||||
#include <GridMate/Replica/DataSet.h>
|
||||
#include <GridMate/Replica/ReplicaChunkInterface.h>
|
||||
#include <GridMate/Replica/ReplicaCommon.h>
|
||||
#include <ScriptCanvas/Variable/GraphVariableMarshal.h>
|
||||
|
||||
namespace ScriptCanvas
|
||||
{
|
||||
class GraphVariable;
|
||||
class GraphVariableReplicaChunk;
|
||||
|
||||
//! Core functionality for managing replicated Datums in a script canvas and the
|
||||
//! corresponding GridMate callbacks and data structs (DataSets).
|
||||
class GraphVariableNetBindingTable
|
||||
: public GridMate::ReplicaChunkInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(GraphVariableNetBindingTable, AZ::SystemAllocator, 0);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* reflect);
|
||||
|
||||
GraphVariableNetBindingTable() = default;
|
||||
~GraphVariableNetBindingTable() = default;
|
||||
|
||||
GridMate::ReplicaChunkPtr GetNetworkBinding();
|
||||
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk);
|
||||
void UnbindFromNetwork();
|
||||
|
||||
//! Gets called when the given Datum object is updated with a new value
|
||||
//! that was received over the network.
|
||||
void OnPropertyUpdate(const Datum* const & scriptProperty, const GridMate::TimeContext& tc);
|
||||
|
||||
//! Adds the given Datum to the list of "synced datums" for this instance.
|
||||
void AddDatum(GraphVariable* variable);
|
||||
|
||||
//! Called when local data changes for a Datum whose values should be replicated
|
||||
//! over the network.
|
||||
void OnDatumChanged(GraphVariable& variable);
|
||||
|
||||
void SetVariableMappings(const AZStd::unordered_map<VariableId, VariableId>& assetToRuntimeVariableMap, const AZStd::unordered_map<VariableId, VariableId>& runtimeToAssetVariableMap);
|
||||
VariableId FindAssetVariableIdByRuntimeVariableId(VariableId runtimeVariableId);
|
||||
VariableId FindRuntimeVariableIdByAssetVariableId(VariableId assetVariableId);
|
||||
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& GetVariableIdMap();
|
||||
|
||||
private:
|
||||
void SetGraphNetBindingTable();
|
||||
|
||||
private:
|
||||
AZStd::unordered_map<VariableId, VariableId> m_assetToRuntimeVariableMap;
|
||||
AZStd::unordered_map<VariableId, VariableId> m_runtimeToAssetVariableMap;
|
||||
|
||||
//! Replica chunk used for GridMate networking binding. See GraphVariableReplicaChunk.
|
||||
GridMate::ReplicaChunkPtr m_replicaChunk;
|
||||
|
||||
//! Contains pointers to all replicated variables contained within the runtime component
|
||||
//! of the canvas this net binding is associated with.
|
||||
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>> m_variableIdMap;
|
||||
};
|
||||
|
||||
typedef GridMate::DataSet<const Datum*, DatumMarshaler, DatumThrottler>::BindInterface<GraphVariableNetBindingTable, &GraphVariableNetBindingTable::OnPropertyUpdate> DatumDataSetType;
|
||||
|
||||
class DatumDataSet
|
||||
: public DatumDataSetType
|
||||
{
|
||||
public:
|
||||
DatumDataSet();
|
||||
~DatumDataSet() = default;
|
||||
|
||||
private:
|
||||
const char* GetDataSetName();
|
||||
};
|
||||
|
||||
class GraphVariableReplicaChunk
|
||||
: public GridMate::ReplicaChunkBase
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(GraphVariableReplicaChunk, AZ::SystemAllocator, 0);
|
||||
|
||||
static const char* GetChunkName();
|
||||
|
||||
GraphVariableReplicaChunk() = default;
|
||||
~GraphVariableReplicaChunk() = default;
|
||||
|
||||
bool IsReplicaMigratable() override;
|
||||
|
||||
DatumDataSet m_properties[GM_MAX_DATASETS_IN_CHUNK];
|
||||
};
|
||||
}
|
||||
@@ -597,10 +597,6 @@ set(FILES
|
||||
Include/ScriptCanvas/Variable/GraphVariable.cpp
|
||||
Include/ScriptCanvas/Variable/GraphVariableManagerComponent.h
|
||||
Include/ScriptCanvas/Variable/GraphVariableManagerComponent.cpp
|
||||
Include/ScriptCanvas/Variable/GraphVariableNetBindings.h
|
||||
Include/ScriptCanvas/Variable/GraphVariableNetBindings.cpp
|
||||
Include/ScriptCanvas/Variable/GraphVariableMarshal.h
|
||||
Include/ScriptCanvas/Variable/GraphVariableMarshal.cpp
|
||||
Include/ScriptCanvas/Variable/VariableCore.h
|
||||
Include/ScriptCanvas/Variable/VariableCore.cpp
|
||||
Include/ScriptCanvas/Variable/VariableData.h
|
||||
|
||||
Reference in New Issue
Block a user