Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
#
# 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.
#
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME GraphModel.Editor.Static STATIC
NAMESPACE Gem
FILES_CMAKE
graphmodel_editor_static_files.cmake
COMPILE_DEFINITIONS
PRIVATE
GRAPHMODEL_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
AZ::AzToolsFramework
Gem::GraphCanvasWidgets
)
ly_add_target(
NAME GraphModel.Editor MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.GraphModel.Editor.0844f64a3acf4f5abf3a535dc9b63bc9.v0.1.0
FILES_CMAKE
graphmodel_editor_files.cmake
COMPILE_DEFINITIONS
PRIVATE
GRAPHMODEL_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
AZ::AzToolsFramework
Gem::GraphCanvasWidgets
Gem::GraphModel.Editor.Static
)
endif()
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME GraphModel.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
graphmodel_tests_editor_files.cmake
COMPILE_DEFINITIONS
PRIVATE
GRAPHMODEL_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
.
Tests
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AzFramework
AZ::AzToolsFramework
Gem::GraphCanvasWidgets
Gem::GraphModel.Editor.Static
)
ly_add_googletest(
NAME Gem::GraphModel.Editor.Tests
)
endif()
endif()
@@ -0,0 +1,223 @@
/*
* 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
// AZ
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Component/Entity.h>
// Graph Canvas
#include <GraphCanvas/Editor/EditorTypes.h>
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/Slot.h>
class QPixmap;
class QPoint;
class QPointF;
class QRect;
namespace GraphModelIntegration
{
class ThumbnailItem;
struct GraphModelSerialization
{
AZ_TYPE_INFO(GraphModelSerialization, "{0D4D420B-5D9E-429C-A567-DF8596439F5F}");
using SerializedSlotMapping = AZStd::unordered_map<GraphModel::SlotId, GraphCanvas::SlotId>;
//! Keep track of any nodes and their slots that have been serialized
AZStd::unordered_map<GraphCanvas::NodeId, GraphModel::NodePtr> m_serializedNodes;
AZStd::unordered_map<GraphCanvas::NodeId, SerializedSlotMapping> m_serializedSlotMappings;
//! Mapping of serialized nodeIds to their wrapper (parent) nodeId and layout order so they can be restored after deserialization
using SerializedNodeWrappingMap = AZStd::unordered_map<GraphCanvas::NodeId, AZStd::pair<GraphCanvas::NodeId, AZ::u32>>;
SerializedNodeWrappingMap m_serializedNodeWrappings;
};
//! GraphManagerRequests
//! Create/delete for handling our Graph Controllers
class GraphManagerRequests : public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
//! Create a scene and a corresponding graph controller
virtual AZ::Entity* CreateScene(GraphModel::GraphPtr graph, const GraphCanvas::EditorId editorId) = 0;
//! Remove the graph controller for the scene
virtual void RemoveScene(const GraphCanvas::GraphId& sceneId) = 0;
//! Create a new Graph Controller for the given scene
virtual void CreateGraphController(const GraphCanvas::GraphId& sceneId, GraphModel::GraphPtr graph) = 0;
//! Delete the Graph Controller for the given scene
virtual void DeleteGraphController(const GraphCanvas::GraphId& sceneId) = 0;
//! Retrieve a reference to the Graph object for the specified Graph Controller (if it exists)
virtual GraphModel::GraphPtr GetGraph(const GraphCanvas::GraphId& sceneId) = 0;
//! Get/set our serialized mappings of the GraphCanvas nodes/slots that correspond to
//! GraphModel nodes/slots
virtual const GraphModelSerialization& GetSerializedMappings() = 0;
virtual void SetSerializedMappings(const GraphModelSerialization& serialization) = 0;
};
using GraphManagerRequestBus = AZ::EBus<GraphManagerRequests>;
//! GraphControllerRequests
//! Used to invoke functionality on specific Graph Controllers
class GraphControllerRequests : public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::EntityId;
//////////////////////////////////////////////////////////////////////////
//! Add a new node at the specified position
virtual GraphCanvas::NodeId AddNode(GraphModel::NodePtr node, AZ::Vector2& sceneDropPosition) = 0;
//! Remove the specified node
virtual bool RemoveNode(GraphModel::NodePtr node) = 0;
//! Retrieve the position for the specified node
virtual AZ::Vector2 GetPosition(GraphModel::NodePtr node) const = 0;
//! Embed a node on a wrapper node
virtual void WrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node) = 0;
//! Embed a node on a wrapper node with a layout order configured
virtual void WrapNodeOrdered(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder) = 0;
//! Unwrap a node from a wrapper node
//! This results in a no-op if node isn't actually wrapped on the wrapperNode
virtual void UnwrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node) = 0;
//! Set the action string for the specified node (used by wrapper nodes for
//! setting the action widget label)
virtual void SetWrapperNodeActionString(GraphModel::NodePtr node, const char* actionString) = 0;
//! Add a new connection between the specified source and target
virtual GraphModel::ConnectionPtr AddConnection(GraphModel::SlotPtr sourceSlot, GraphModel::SlotPtr targetSlot) = 0;
//! Create a new connection between the specified source and target specified slots
virtual GraphModel::ConnectionPtr AddConnectionBySlotId(GraphModel::NodePtr sourceNode, GraphModel::SlotId sourceSlotId, GraphModel::NodePtr targetNode, GraphModel::SlotId targetSlotId) = 0;
//! Remove the specified connection
virtual bool RemoveConnection(GraphModel::ConnectionPtr connection) = 0;
//! Extend the given Slot on the specified node
virtual GraphModel::SlotId ExtendSlot(GraphModel::NodePtr node, GraphModel::SlotName slotName) = 0;
//! Returns a GraphModel::Node that corresponds to the Graph Canvas Node Id
virtual GraphModel::NodePtr GetNodeById(const GraphCanvas::NodeId& nodeId) = 0;
//! Retrieve the list of GraphModel::Nodes for the specified GraphCanvas node IDs
virtual GraphModel::NodePtrList GetNodesFromGraphNodeIds(const AZStd::vector<GraphCanvas::NodeId>& nodeIds) = 0;
//! Returns the GraphCanvas::NodeId that corresponds to the specified GraphModel::Node
virtual GraphCanvas::NodeId GetNodeIdByNode(GraphModel::NodePtr node) const = 0;
//! Returns the GraphCanvas::SlotId that corresponds to the specified GraphModel::Slot
virtual GraphCanvas::SlotId GetSlotIdBySlot(GraphModel::SlotPtr slot) const = 0;
//! Retrieve all of the nodes in our graph
virtual GraphModel::NodePtrList GetNodes() = 0;
//! Retrieve the selected nodes in our graph
virtual GraphModel::NodePtrList GetSelectedNodes() = 0;
//! Set the selected property on the specified Nodes
virtual void SetSelected(GraphModel::NodePtrList nodes, bool selected) = 0;
//! Clears the selection in the scene
virtual void ClearSelection() = 0;
//! Enable the specified node in the graph
virtual void EnableNode(GraphModel::NodePtr node) = 0;
//! Disable the specified node in the graph
virtual void DisableNode(GraphModel::NodePtr node) = 0;
//! Move the view to be centered on the given Nodes
virtual void CenterOnNodes(GraphModel::NodePtrList nodes) = 0;
//! Retrieve the major pitch of the grid for this scene graph
virtual AZ::Vector2 GetMajorPitch() const = 0;
//! Embed a thumbnail image on a specified node. This is the most straightforward use-case
//! where the client just wants to show a static image. The thumnbnail image can be updated
//! after being set using this same API.
//! \param node Node to add the thumbnail on
//! \param image Pixmap for the image of the thumbnail
virtual void SetThumbnailImageOnNode(GraphModel::NodePtr node, const QPixmap& image) = 0;
//! Embed a custom thumbnail item on a specified node. This allows the client to
//! implement their own ThumbnailItem to display anything they want by overriding
//! paint() method. Ownership of the ThumbnailItem is passed to the node layout.
//! \param node Node to add the thumbnail on
//! \param item Custom item for the thumbnail
virtual void SetThumbnailOnNode(GraphModel::NodePtr node, ThumbnailItem* item) = 0;
//! Remove the thumbnail from a specified node. If you created your own custom ThumbnailItem
//! and set it using SetThumbnailOnNode, then ownership is passed back to whoever calls this
//! method so they are in charge of deleting it.
//! \param node Node to remove the thumbnail from
virtual void RemoveThumbnailFromNode(GraphModel::NodePtr node) = 0;
};
using GraphControllerRequestBus = AZ::EBus<GraphControllerRequests>;
//! GraphControllerNotifications
//! Notifications about changes to the state of scene graphs.
class GraphControllerNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::EntityId;
//! A node has been added to the scene.
virtual void OnGraphModelNodeAdded(GraphModel::NodePtr /*node*/) {}
//! A node has been removed from the scene.
virtual void OnGraphModelNodeRemoved(GraphModel::NodePtr /*node*/) {}
//! Invoked prior to a node being removed from the scene.
virtual void PreOnGraphModelNodeRemoved(GraphModel::NodePtr /*node*/) {}
//! A connection has been added to the scene.
virtual void OnGraphModelConnectionAdded(GraphModel::ConnectionPtr /*connection*/) {}
//! A connection has been removed from the scene.
virtual void OnGraphModelConnectionRemoved(GraphModel::ConnectionPtr /*connection*/) {}
//! The specified node has been wrapped (embedded) onto the wrapperNode
virtual void OnGraphModelNodeWrapped(GraphModel::NodePtr /*wrapperNode*/, GraphModel::NodePtr /*node*/) {}
//! The specified node has been unwrapped (removed) from the wrapperNode
virtual void OnGraphModelNodeUnwrapped(GraphModel::NodePtr /*wrapperNode*/, GraphModel::NodePtr /*node*/) {}
//! Something in the graph has been modified
//! \param node The node that was modified in the graph. If this is nullptr, some metadata on the graph itself was modified
virtual void OnGraphModelGraphModified(GraphModel::NodePtr node) {}
};
using GraphControllerNotificationBus = AZ::EBus<GraphControllerNotifications>;
}
@@ -0,0 +1,39 @@
/*
* 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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/BooleanDataInterface.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
//! Satisfies GraphCanvas API requirements for showing bool property widgets in nodes.
class BooleanDataInterface
: public GraphCanvas::BooleanDataInterface
{
public:
AZ_CLASS_ALLOCATOR(BooleanDataInterface, AZ::SystemAllocator, 0);
BooleanDataInterface(GraphModel::SlotPtr slot);
~BooleanDataInterface() = default;
bool GetBool() const override;
void SetBool(bool enabled) override;
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,62 @@
/*
* 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
// AZ ...
#include <AzCore/std/containers/unordered_map.h>
// GraphCanvas ...
#include <GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasAssetEditorMainWindow.h>
// GraphModel ...
#include <GraphModel/GraphModelBus.h>
namespace GraphModelIntegration
{
/**
* This class extends the base GraphCanvas windowing framework to integrate
* GraphModel functionality into the generic windowing framework.
*/
class EditorMainWindow
: public GraphCanvas::AssetEditorMainWindow
, protected GraphControllerNotificationBus::MultiHandler
{
public:
explicit EditorMainWindow(GraphCanvas::AssetEditorWindowConfig* config, QWidget* parent = nullptr);
~EditorMainWindow() override;
protected:
/// Subclasses must implement this method so that this class can
/// create graphs on their behalf.
virtual GraphModel::IGraphContextPtr GetGraphContext() const = 0;
/// Helper method for retrieving the graph associated with a graphId.
GraphModel::GraphPtr GetGraphById(GraphCanvas::GraphId graphId) const;
/// Helper method for retrieving the graphId associated with a graph.
GraphCanvas::GraphId GetGraphId(GraphModel::GraphPtr graph) const;
// GraphCanvas::AssetEditorMainWindow overrides ...
void OnEditorOpened(GraphCanvas::EditorDockWidget* dockWidget) override;
void OnEditorClosing(GraphCanvas::EditorDockWidget* dockWidget) override;
void OnWrapperNodeActionWidgetClicked(const AZ::EntityId& wrapperNode, const QRect& actionWidgetBoundingRect, const QPointF& scenePoint, const QPoint& screenPoint) override;
/// Client can override this to handle click events on a wrapper node's action widget
/// using a GraphModel::NodePtr instead of the lower-level GraphCanvas::NodeId
virtual void HandleWrapperNodeActionWidgetClicked(GraphModel::NodePtr wrapperNode, [[maybe_unused]] const QRect& actionWidgetBoundingRect, [[maybe_unused]] const QPointF& scenePoint, [[maybe_unused]] const QPoint& screenPoint) {}
/// Keep track of the graphs we create on behalf of the client when
/// new editor dock widgets are created.
AZStd::unordered_map<GraphCanvas::GraphId, GraphModel::GraphPtr> m_graphs;
};
}
@@ -0,0 +1,45 @@
/*
* 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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/DoubleDataInterface.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
//! Satisfies GraphCanvas API requirements for showing float property widgets in nodes.
class FloatDataInterface
: public GraphCanvas::NumericDataInterface
{
public:
AZ_CLASS_ALLOCATOR(FloatDataInterface, AZ::SystemAllocator, 0);
FloatDataInterface(GraphModel::SlotPtr slot);
~FloatDataInterface() = default;
double GetNumber() const override;
void SetNumber(double value) override;
int GetDecimalPlaces() const override;
int GetDisplayDecimalPlaces() const override;
double GetMin() const override;
double GetMax() const override;
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,64 @@
/*
* 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
// AZ
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/map.h>
// Graph Model
#include <GraphModel/Model/Common.h>
namespace GraphCanvas
{
class EntitySaveDataContainer;
}
namespace GraphModelIntegration
{
//! This class provides a way to bundle metadata from Graph Canvas for storage
//! in a GraphModel::Graph. The Graph class has a single AZStd::any for storing
//! UI-specific metadata, where the node canvas stores one of these GraphCanvasMetadata.
//! This allows the Graph's file on disk to include information about where nodes
//! are located in the scene, bookmarks, comment blocks, node groupings, etc.
class GraphCanvasMetadata
{
public:
AZ_RTTI(GraphCanvasMetadata, "{BD95C3EB-CD09-4F82-9724-032BD1827B95}");
virtual ~GraphCanvasMetadata() = default;
static void Reflect(AZ::ReflectContext* reflectContext);
private:
friend class GraphController;
// I tried using a unique_ptr but SerializeContext didn't like it
typedef AZStd::shared_ptr<GraphCanvas::EntitySaveDataContainer> EntitySaveDataContainerPtr;
// Using a map instead of unordered_map for simpler xml diffs
typedef AZStd::map<GraphModel::NodeId, EntitySaveDataContainerPtr> NodeMetadataMap;
typedef AZStd::map<AZ::EntityId, EntitySaveDataContainerPtr> OtherMetadataMap;
//! Graph Canvas metadata that pertains to the entire scene
EntitySaveDataContainerPtr m_sceneMetadata;
//! Graph Canvas metadata that pertains to each node in our data model. For example,
//! the position of each node.
NodeMetadataMap m_nodeMetadata;
//! Graph Canvas metadata that is not related to our data model. For example,
//! Comment nodes and Group Box nodes.
OtherMetadataMap m_otherMetadata;
};
}
@@ -0,0 +1,327 @@
/*
* 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
// AZ
#include <AzCore/std/containers/unordered_map.h>
// Qt
#include <QPixmap>
// Graph Canvas
#include <GraphCanvas/Editor/GraphModelBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/SceneBus.h>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/Slot.h>
class QGraphicsLinearLayout;
namespace GraphModelIntegration
{
class GraphCanvasMetadata;
class ThumbnailItem;
//! This is the main class for binding the node graph data to the UI provided by Graph Canvas
class GraphController
: private GraphCanvas::GraphModelRequestBus::Handler
, private GraphCanvas::SceneNotificationBus::Handler
, public GraphControllerRequestBus::Handler
{
public:
AZ_RTTI(GraphController, "{E8433794-4BAE-4B63-B5A5-6EE69DFF0793}");
GraphController(GraphModel::GraphPtr graph, AZ::EntityId graphCanvasSceneId);
~GraphController() override;
GraphController(const GraphController&) = delete;
GraphModel::GraphPtr GetGraph() { return m_graph; }
const GraphModel::GraphPtr GetGraph() const { return m_graph; }
const AZ::EntityId GetGraphCanvasSceneId() const { return m_graphCanvasSceneId; }
////////////////////////////////////////////////////////////////////////////////////
// GraphModel::GraphControllerRequestBus, connections
//! Adds a node to the Graph and creates the corresponding Graph Canvas UI elements
//! \param node The node to add. This should be a freshly created Node that hasn't been added to the Graph yet.
//! \param sceneDropPosition The position in the Graph Cavnas scene where the Node was dropped
GraphCanvas::NodeId AddNode(GraphModel::NodePtr node, AZ::Vector2& sceneDropPosition) override;
bool RemoveNode(GraphModel::NodePtr node) override;
AZ::Vector2 GetPosition(GraphModel::NodePtr node) const override;
void WrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node) override;
void WrapNodeOrdered(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder) override;
void UnwrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node) override;
void SetWrapperNodeActionString(GraphModel::NodePtr node, const char* actionString) override;
GraphModel::ConnectionPtr AddConnection(GraphModel::SlotPtr sourceSlot, GraphModel::SlotPtr targetSlot) override;
GraphModel::ConnectionPtr AddConnectionBySlotId(GraphModel::NodePtr sourceNode, GraphModel::SlotId sourceSlotId, GraphModel::NodePtr targetNode, GraphModel::SlotId targetSlotId) override;
bool RemoveConnection(GraphModel::ConnectionPtr connection) override;
GraphModel::SlotId ExtendSlot(GraphModel::NodePtr node, GraphModel::SlotName slotName) override;
GraphModel::NodePtr GetNodeById(const GraphCanvas::NodeId& nodeId) override;
GraphModel::NodePtrList GetNodesFromGraphNodeIds(const AZStd::vector<GraphCanvas::NodeId>& nodeIds) override;
GraphCanvas::NodeId GetNodeIdByNode(GraphModel::NodePtr node) const override;
GraphCanvas::SlotId GetSlotIdBySlot(GraphModel::SlotPtr slot) const override;
GraphModel::NodePtrList GetNodes() override;
GraphModel::NodePtrList GetSelectedNodes() override;
void SetSelected(GraphModel::NodePtrList nodes, bool selected) override;
void ClearSelection() override;
void EnableNode(GraphModel::NodePtr node);
void DisableNode(GraphModel::NodePtr node);
void CenterOnNodes(GraphModel::NodePtrList nodes) override;
AZ::Vector2 GetMajorPitch() const override;
void SetThumbnailImageOnNode(GraphModel::NodePtr node, const QPixmap& image) override;
void SetThumbnailOnNode(GraphModel::NodePtr node, ThumbnailItem* item) override;
void RemoveThumbnailFromNode(GraphModel::NodePtr node) override;
////////////////////////////////////////////////////////////////////////////////////
private:
//! Helper method for retrieving the UI layout for a given node
QGraphicsLinearLayout* GetLayoutFromNode(GraphModel::NodePtr node);
//! Saves metadata for a Graph Canvas element into the Graph data model so it's ready
//! to be serialized out with the data model. graphCanvasElement could be any number
//! of entities including a node, comment, group, or the scene itself.
void SaveMetadata(const AZ::EntityId& graphCanvasElement);
//! Utility function for getting the GraphCanvasMetadata from the Graph data model
GraphCanvasMetadata* GetGraphMetadata();
////////////////////////////////////////////////////////////////////////////////////
// Functions for building Graph Canvas UI from our data model
//! Creates the all Graph Canvas elements necessary for representing the Graph. This will be called once
//! to instrument a Graph that was recently loaded.
void CreateFullGraphUi();
//! Creates the GraphCanvas slot UI representing a given Slot
AZ::Entity* CreateSlotUi(GraphModel::SlotPtr slot, AZ::EntityId nodeUiId);
//! Creates the GraphCanvas node UI represeting a given Node
//! \param scenePosition Pass in a lambda function that provides the node's position given it's GraphCanvas node EntityId.
AZ::EntityId CreateNodeUi(GraphModel::NodeId nodeId, GraphModel::NodePtr node, AZStd::function<AZ::Vector2(AZ::EntityId/*nodeUiId*/)> getScenePosition);
//! Utility function for adding a Graph Canvas node to a Graph Canvas scene
void AddNodeUiToScene(AZ::EntityId graphCanvasNodeId, const AZ::Vector2& scenePosition);
//! Creates the GraphCanvas UI represeting a given Connection
void CreateConnectionUi(GraphModel::ConnectionPtr connection);
//! Create a new GraphModel::Connection using the given source and target slots. This will also remove any existing connections on the target slot.
GraphModel::ConnectionPtr CreateConnection(GraphModel::SlotPtr sourceSlot, GraphModel::SlotPtr targetSlot);
//! Check if creating a connection between the specified target and source node would
//! cause a connection loopback.
bool CheckForLoopback(GraphModel::NodePtr sourceNode, GraphModel::NodePtr targetNode) const;
void WrapNodeUi(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder = GraphModel::DefaultWrappedNodeLayoutOrder);
void WrapNodeInternal(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder = GraphModel::DefaultWrappedNodeLayoutOrder);
////////////////////////////////////////////////////////////////////////////////////
// GraphCanvas::SceneNotificationBus, connections
void OnNodeAdded(const AZ::EntityId& nodeUiId) override;
void OnNodeRemoved(const AZ::EntityId& nodeUiId) override;
void PreOnNodeRemoved(const AZ::EntityId& nodeUiId) override;
void OnConnectionRemoved(const AZ::EntityId& connectionUiId) override;
void OnEntitiesSerialized(GraphCanvas::GraphSerialization& serializationTarget) override;
void OnEntitiesDeserialized(const GraphCanvas::GraphSerialization& serializationSource) override;
void OnEntitiesDeserializationComplete(const GraphCanvas::GraphSerialization& serializationSource) override;
////////////////////////////////////////////////////////////////////////////////////
// GraphCanvas::GraphModelRequestBus, connections
void DisconnectConnection([[maybe_unused]] const AZ::EntityId& connectionUiId) override {}
bool CreateConnection(const AZ::EntityId& connectionUiId, const GraphCanvas::Endpoint& sourcePoint, const GraphCanvas::Endpoint& targetPoint) override;
bool IsValidConnection(const GraphCanvas::Endpoint& sourcePoint, const GraphCanvas::Endpoint& targetPoint) const override;
bool IsValidVariableAssignment(const AZ::EntityId& variableId, const GraphCanvas::Endpoint& targetPoint) const override;
////////////////////////////////////////////////////////////////////////////////////
// GraphCanvas::GraphModelRequestBus, undo
// CJS TODO: I put this stuff in to handle making the file as dirty, but it looks like I might be able to get OnSaveDataDirtied to do that instead.
void RequestUndoPoint() override;
void RequestPushPreventUndoStateUpdate() override;
void RequestPopPreventUndoStateUpdate() override;
void TriggerUndo() override {}
void TriggerRedo() override {}
////////////////////////////////////////////////////////////////////////////////////
// GraphCanvas::GraphModelRequestBus, other
bool EnableNodes(const AZStd::unordered_set<GraphCanvas::NodeId>& nodeIds) override;
bool DisableNodes(const AZStd::unordered_set<GraphCanvas::NodeId>& nodeIds) override;
AZStd::string GetDataTypeString(const AZ::Uuid& typeId);
//! This is where we find all of the graph metadata (like node positions, comments, etc) and store it in the node graph for serialization
// CJS TODO: Use this instead of the above undo functions
void OnSaveDataDirtied(const AZ::EntityId& savedElement) override;
void OnRemoveUnusedNodes() override {}
void OnRemoveUnusedElements() override {}
void ResetSlotToDefaultValue(const GraphCanvas::Endpoint& endpoint) override;
/// Extendable slot handlers
void RemoveSlot(const GraphCanvas::Endpoint& endpoint) override;
bool IsSlotRemovable(const GraphCanvas::Endpoint& endpoint) const override;
GraphCanvas::SlotId RequestExtension(const GraphCanvas::NodeId& nodeId, const GraphCanvas::ExtenderId& extenderId) override;
bool ShouldWrapperAcceptDrop(const GraphCanvas::NodeId& wrapperNode, const QMimeData* mimeData) const override;
void AddWrapperDropTarget(const GraphCanvas::NodeId& wrapperNode) override;
void RemoveWrapperDropTarget(const GraphCanvas::NodeId& wrapperNode) override;
////////////////////////////////////////////////////////////////////////////////////
// GraphCanvas::GraphModelRequestBus, node properties
//! Creates a GraphCanvas::NodePropertyDisplay and a data interface for editing input values
GraphCanvas::NodePropertyDisplay* CreateDataSlotPropertyDisplay(const AZ::Uuid& dataTypeUuid, const GraphCanvas::NodeId& nodeUiId, const GraphCanvas::SlotId& slotUiId) const override;
GraphCanvas::NodePropertyDisplay* CreatePropertySlotPropertyDisplay(const AZ::Crc32& propertyId, const GraphCanvas::NodeId& nodeUiId, const GraphCanvas::SlotId& slotUiId) const override;
//! Common implementation for CreateDataSlotPropertyDisplay and CreatePropertySlotPropertyDisplay
GraphCanvas::NodePropertyDisplay* CreateSlotPropertyDisplay(GraphModel::SlotPtr inputSlot) const;
////////////////////////////////////////////////////////////////////////////////////
//! This class maps the association between our data model's GraphElements and Graph Camvas's UI elements
class GraphElementMap
{
public:
//! Adds a 1:1 mapping between a Graph Canvas UI element and a GraphElement
void Add(AZ::EntityId graphCanvasId, GraphModel::GraphElementPtr graphElement);
//! Removes the Graph Canvas EntityId and its associated GraphElement from the map
void Remove(AZ::EntityId graphCanvasId);
//! Removes the GraphElement and its associated Graph Canvas EntityId from the map
void Remove(GraphModel::ConstGraphElementPtr graphElement);
//! Find the GraphElement that corresponds to the given Graph Canvas EntityId.
//! Returns nullptr if the mapping doesn't exist.
GraphModel::GraphElementPtr Find(AZ::EntityId graphCanvasId);
GraphModel::ConstGraphElementPtr Find(AZ::EntityId graphCanvasId) const;
//! Find the Graph Canvas EntityId that corresponds to the given GraphElement.
//! Returns an invalid EntityId if the mapping doesn't exist.
AZ::EntityId Find(GraphModel::ConstGraphElementPtr graphElement) const;
private:
// shared_ptr can't be a key in a map so we use a raw pointer. This is fine because the two maps
// are kept in sync, and the other map has a shared_ptr.
typedef AZStd::unordered_map<const GraphModel::GraphElement*, AZ::EntityId /*graphCanvasId*/> GraphElementToUiMap;
GraphElementToUiMap m_graphElementToUi;
typedef AZStd::unordered_map<AZ::EntityId /*graphCanvasId*/, GraphModel::GraphElementPtr> UiToGraphElementMap;
UiToGraphElementMap m_uiToGraphElement;
};
//! This class provides a collection of GraphElementMaps for the various types of elements. We could
//! put all the elements in one GraphElementMap, but splitting them out makes debugging a lot easier.
class GraphElementMapCollection
{
public:
GraphElementMapCollection() = default;
//! Adds a 1:1 mapping between a Graph Canvas UI element and a GraphElement.
//! Automatically determines which GraphElementMap is appropriate.
void Add(AZ::EntityId graphCanvasId, GraphModel::GraphElementPtr graphElement);
void Remove(AZ::EntityId graphCanvasId);
void Remove(GraphModel::ConstGraphElementPtr graphElement);
//! Find the GraphElement that corresponds to the given Graph Canvas EntityId.
//! Returns nullptr if the mapping doesn't exist, or the ElementType is wrong.
template<typename ElementType>
AZStd::shared_ptr<ElementType> Find(AZ::EntityId graphCanvasId);
template<typename ElementType>
AZStd::shared_ptr<const ElementType> Find(AZ::EntityId graphCanvasId) const;
//! Find the Graph Canvas EntityId that corresponds to the given GraphElement.
//! Returns an invalid EntityId if the mapping doesn't exist.
AZ::EntityId Find(GraphModel::ConstGraphElementPtr graphElement) const;
private:
GraphElementMap m_nodeMap;
GraphElementMap m_slotMap;
GraphElementMap m_connectionMap;
//! Returns which is the right GraphElementMap for graphElement based on its type
GraphElementMap* GetMapFor(GraphModel::ConstGraphElementPtr graphElement);
const GraphElementMap* GetMapFor(GraphModel::ConstGraphElementPtr graphElement) const;
// This list allows for easy iteration over all the GraphElementMaps.
AZStd::vector<GraphElementMap*> m_allMaps = { &m_nodeMap, &m_slotMap, &m_connectionMap };
} m_elementMap;
AZ::SerializeContext* m_serializeContext = nullptr;
GraphModel::GraphPtr m_graph;
AZ::EntityId m_graphCanvasSceneId;
AZStd::unordered_map<GraphModel::NodeId, GraphModelIntegration::ThumbnailItem*> m_nodeThumbnails;
AZStd::unordered_map<GraphCanvas::NodeId, AZStd::unordered_map<GraphCanvas::ExtenderId, GraphModel::SlotName>> m_nodeExtenderIds;
bool m_isCreatingConnectionUi = false;
};
template<typename ElementType>
AZStd::shared_ptr<ElementType> GraphController::GraphElementMapCollection::Find(AZ::EntityId graphCanvasId)
{
GraphModel::GraphElementPtr graphElement;
for (GraphElementMap* map : m_allMaps)
{
graphElement = map->Find(graphCanvasId);
if (graphElement)
{
break;
}
}
return azrtti_cast<ElementType*>(graphElement);
}
template<typename ElementType>
AZStd::shared_ptr<const ElementType> GraphController::GraphElementMapCollection::Find(AZ::EntityId graphCanvasId) const
{
GraphModel::ConstGraphElementPtr graphElement;
for (GraphElementMap* map : m_allMaps)
{
graphElement = map->Find(graphCanvasId);
if (graphElement)
{
break;
}
}
return azrtti_cast<const ElementType*>(graphElement);
}
}
@@ -0,0 +1,54 @@
/*
* 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
// AZ
#include <AzCore/base.h>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Integration/GraphController.h>
namespace GraphModelIntegration
{
//! This is the main class for managing the Graph Controllers for Graph Canvas scenes
class GraphControllerManager
: private GraphManagerRequestBus::Handler
{
public:
AZ_RTTI(GraphControllerManager, "{DA358B3E-46EF-411B-B84B-0397F5CD3539}");
GraphControllerManager() = default;
////////////////////////////////////////////////////////////////////////////////////
// GraphModelIntegration::GraphManagerRequestBus overrides
AZ::Entity* CreateScene(GraphModel::GraphPtr graph, const GraphCanvas::EditorId editorId) override;
void RemoveScene(const GraphCanvas::GraphId& sceneId) override;
void CreateGraphController(const GraphCanvas::GraphId& sceneId, GraphModel::GraphPtr graph) override;
void DeleteGraphController(const GraphCanvas::GraphId& sceneId) override;
GraphModel::GraphPtr GetGraph(const GraphCanvas::GraphId& sceneId) override;
const GraphModelSerialization& GetSerializedMappings() override;
void SetSerializedMappings(const GraphModelSerialization& serialization) override;
////////////////////////////////////////////////////////////////////////////////////
void Activate();
void Deactivate();
private:
AZ_DISABLE_COPY_MOVE(GraphControllerManager);
AZStd::unordered_map<GraphCanvas::GraphId, AZStd::shared_ptr<GraphController>> m_graphControllers;
GraphModelSerialization m_serialization;
};
}
@@ -0,0 +1,89 @@
/*
* 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/ComponentApplicationBus.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/string/string.h>
namespace GraphModelIntegration
{
namespace Attributes
{
const static AZ::Crc32 TitlePaletteOverride = AZ_CRC("TitlePaletteOverride", 0x2faad537);
}
class Helpers
{
public:
//! Helper method to retrieve the TitlePaletteOverride attribute (if exists) set on
//! a given AZ type class, that will also check any base class that it is derived from
static AZStd::string GetTitlePaletteOverride(const AZ::TypeId& typeId)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
AZStd::string paletteOverride;
const AZ::SerializeContext::ClassData* derivedClassData = serializeContext->FindClassData(typeId);
if (!derivedClassData)
{
return paletteOverride;
}
// Use the EnumHierarchy API to retrive a list of TypeIds that this class derives from,
// starting with the actual type and going backwards
AZStd::vector<AZ::TypeId> typeIds;
if (derivedClassData->m_azRtti)
{
derivedClassData->m_azRtti->EnumHierarchy(&RttiEnumHeirarchyHelper, &typeIds);
}
// Look through all the derived TypeIds to see if the TitlePaletteOverride attribute
// was set in the EditContext at any level
for (auto currentTypeId : typeIds)
{
auto classData = serializeContext->FindClassData(currentTypeId);
if (classData)
{
if (classData->m_editData)
{
const AZ::Edit::ElementData* elementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
if (elementData)
{
if (auto titlePaletteAttribute = elementData->FindAttribute(Attributes::TitlePaletteOverride))
{
AZ::AttributeReader nameReader(nullptr, titlePaletteAttribute);
nameReader.Read<AZStd::string>(paletteOverride);
}
}
}
}
}
return paletteOverride;
}
private:
//! Callback method needed for IRttiHelper::EnumHierarchy that gets invoked at every level
//! allowing us to build a list of each TypeId it encounters
static void RttiEnumHeirarchyHelper(const AZ::TypeId& typeId, void* userData)
{
AZStd::vector<AZ::TypeId>* typeIds = reinterpret_cast<AZStd::vector<AZ::TypeId>*>(userData);
typeIds->push_back(typeId);
}
};
}
@@ -0,0 +1,44 @@
/*
* 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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/NumericDataInterface.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
//! Satisfies GraphCanvas API requirements for showing int property widgets in nodes.
class IntegerDataInterface
: public GraphCanvas::NumericDataInterface
{
public:
AZ_CLASS_ALLOCATOR(IntegerDataInterface, AZ::SystemAllocator, 0);
IntegerDataInterface(GraphModel::SlotPtr slot);
~IntegerDataInterface() = default;
double GetNumber() const override;
void SetNumber(double value) override;
int GetDecimalPlaces() const override;
int GetDisplayDecimalPlaces() const override;
double GetMin() const override;
double GetMax() const override;
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,39 @@
/*
* 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
// AZ
#include <AzCore/EBus/EBus.h>
// Graph Canvas
#include <GraphCanvas/Editor/EditorTypes.h>
namespace GraphModelIntegration
{
class GraphController;
//! Bus functions that allow the GraphModel Integration system to callback to the client system.
class IntegrationBusInterface : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! Return the Graph Canvas EntityId for whichever Graph Canvas scene is active in the Editor
virtual GraphCanvas::GraphId GetActiveGraphCanvasSceneId() const = 0;
//! Notifies the client node graph system that the graph data has changed
virtual void SignalSceneDirty(GraphCanvas::GraphId graphCanvasSceneId) = 0;
};
using IntegrationBus = AZ::EBus<IntegrationBusInterface>;
}
@@ -0,0 +1,179 @@
/*
* 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
// AZ
#include <AzCore/Serialization/SerializeContext.h>
// Graph Canvas
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/GraphCanvasBus.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
namespace GraphModelIntegration
{
//! Provides a common interface for instantiating Graph Canvas support nodes like comments through the Node Palette
class CreateGraphCanvasNodeMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI(CreateGraphCanvasNodeMimeEvent, "{7171A847-7405-459F-A031-CC9AE50745B6}", GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(CreateGraphCanvasNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateGraphCanvasNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
CreateGraphCanvasNodeMimeEvent() = default;
~CreateGraphCanvasNodeMimeEvent() = default;
bool ExecuteEvent([[maybe_unused]] const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasSceneId) override final
{
AZ::Entity* graphCanvasNode = CreateNode();
if (graphCanvasNode)
{
AZ::EntityId graphCanvasNodeId = graphCanvasNode->GetId();
GraphCanvas::SceneRequestBus::Event(graphCanvasSceneId, &GraphCanvas::SceneRequests::AddNode, graphCanvasNodeId, dropPosition);
GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasNodeId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasSceneId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
dropPosition += offset;
return true;
}
return false;
}
protected:
virtual AZ::Entity* CreateNode() const = 0;
};
////////////////////////////////////////////////////////////////////////////////////
// Comment Node
class CreateCommentNodeMimeEvent
: public CreateGraphCanvasNodeMimeEvent
{
public:
AZ_RTTI(CreateCommentNodeMimeEvent, "{1060EE7B-DBC2-4B7F-BC4C-4AB4651A3812}", CreateGraphCanvasNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateCommentNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateCommentNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
CreateCommentNodeMimeEvent() = default;
~CreateCommentNodeMimeEvent() = default;
virtual AZ::Entity* CreateNode() const override
{
AZ::Entity* graphCanvasNode = nullptr;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(graphCanvasNode, &GraphCanvas::GraphCanvasRequests::CreateCommentNodeAndActivate);
return graphCanvasNode;
}
};
class CommentNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(CommentNodePaletteTreeItem, AZ::SystemAllocator, 0);
CommentNodePaletteTreeItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId)
: DraggableNodePaletteTreeItem(nodeName, editorId)
{
SetToolTip("Comment box for notes. Does not affect script execution or data.");
}
~CommentNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override
{
return aznew CreateCommentNodeMimeEvent();
}
};
////////////////////////////////////////////////////////////////////////////////////
// Node Group Node
class CreateNodeGroupNodeMimeEvent
: public CreateGraphCanvasNodeMimeEvent
{
public:
AZ_RTTI(CreateNodeGroupNodeMimeEvent, "{1451A2F2-640B-4CB3-BF48-DD77E97EC900}", CreateGraphCanvasNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateNodeGroupNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateNodeGroupNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
CreateNodeGroupNodeMimeEvent() = default;
~CreateNodeGroupNodeMimeEvent() = default;
virtual AZ::Entity* CreateNode() const override
{
AZ::Entity* graphCanvasNode = nullptr;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(graphCanvasNode, &GraphCanvas::GraphCanvasRequests::CreateNodeGroupAndActivate);
return graphCanvasNode;
}
};
class NodeGroupNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodeGroupNodePaletteTreeItem, AZ::SystemAllocator, 0);
NodeGroupNodePaletteTreeItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId)
: DraggableNodePaletteTreeItem(nodeName, editorId)
{}
~NodeGroupNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override
{
return aznew CreateNodeGroupNodeMimeEvent();
}
};
/// Add common utilities to a specific Node Palette tree.
void AddCommonNodePaletteUtilities(GraphCanvas::GraphCanvasTreeItem* rootItem, const GraphCanvas::EditorId& editorId);
}
@@ -0,0 +1,110 @@
/*
* 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
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
// Graph Canvas
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Model/Common.h>
namespace GraphModelIntegration
{
template<typename NodeType>
class CreateInputOutputNodeMimeEvent;
//! Provides a common interface for instantiating InputGraphNode and OutputGraphNode through the Node Palette
template<typename NodeType>
class InputOutputNodePaletteItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(InputOutputNodePaletteItem, AZ::SystemAllocator, 0);
//! Constructor
//! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
//! \param dataType The type of data that the InputGraphNode or OutputGraphNode will represent
InputOutputNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId, GraphModel::DataTypePtr dataType)
: DraggableNodePaletteTreeItem(nodeName, editorId)
, m_dataType(dataType)
{}
~InputOutputNodePaletteItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override
{
return aznew CreateInputOutputNodeMimeEvent<NodeType>(m_dataType);
}
protected:
GraphModel::DataTypePtr m_dataType;
};
template<typename NodeType>
class CreateInputOutputNodeMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI(((CreateInputOutputNodeMimeEvent<NodeType>), "{16BED069-A386-4E5C-8A5A-0827121991E7}", NodeType), GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(CreateInputOutputNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateInputOutputNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("m_dataType", &CreateInputOutputNodeMimeEvent::m_dataType)
;
}
}
CreateInputOutputNodeMimeEvent() = default; // Required by SerializeContext
explicit CreateInputOutputNodeMimeEvent(GraphModel::DataTypePtr dataType)
{
// Copy because m_dataType has to be non-const for use with SerializeContext, and dataType is const
m_dataType = AZStd::make_shared<GraphModel::DataType>(*dataType);
}
bool ExecuteEvent([[maybe_unused]] const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasSceneId) override
{
GraphModel::GraphPtr graph = nullptr;
GraphManagerRequestBus::BroadcastResult(graph, &GraphManagerRequests::GetGraph, graphCanvasSceneId);
if (!graph)
{
return false;
}
AZStd::shared_ptr<GraphModel::Node> node = AZStd::make_shared<NodeType>(graph, m_dataType);
if (!node)
{
return false;
}
GraphControllerRequestBus::Event(graphCanvasSceneId, &GraphControllerRequests::AddNode, node, dropPosition);
return true;
}
protected:
AZStd::shared_ptr<GraphModel::DataType> m_dataType;
};
}
@@ -0,0 +1,121 @@
/*
* 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
// AZ
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
// Graph Canvas
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/Module/ModuleNode.h>
namespace GraphModelIntegration
{
AZStd::string GetNodeName(AZStd::string_view sourceFileName)
{
AZStd::string name = "Unnamed";
if (!AzFramework::StringFunc::Path::GetFileName(sourceFileName.data(), name))
{
AZ_Assert(false, "Could not get node name from module file path [%s]", sourceFileName.data());
}
return name;
}
class CreateModuleNodeMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI(CreateModuleNodeMimeEvent, "{914F9D88-7B60-408D-A16F-BCCE4CA41EFB}", GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(CreateModuleNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateModuleNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("m_sourceFileName", &CreateModuleNodeMimeEvent::m_sourceFileName)
->Field("m_sourceFileId", &CreateModuleNodeMimeEvent::m_sourceFileId)
;
}
}
CreateModuleNodeMimeEvent() = default; // required by SerializeContext
CreateModuleNodeMimeEvent(AZStd::string_view sourceFileName, AZ::Uuid sourceFileId)
: m_sourceFileName(sourceFileName)
, m_sourceFileId(sourceFileId)
{
}
bool ExecuteEvent([[maybe_unused]] const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasSceneId) override
{
GraphModel::GraphPtr graph = nullptr;
GraphManagerRequestBus::BroadcastResult(graph, &GraphManagerRequests::GetGraph, graphCanvasSceneId);
if (!graph)
{
return false;
}
AZStd::shared_ptr<GraphModel::Node> node = AZStd::make_shared<GraphModel::ModuleNode>(graph, m_sourceFileId, m_sourceFileName);
if (!node)
{
return false;
}
GraphControllerRequestBus::Event(graphCanvasSceneId, &GraphControllerRequests::AddNode, node, dropPosition);
return true;
}
protected:
AZStd::string m_sourceFileName;
AZ::Uuid m_sourceFileId;
};
//! Provides the interface for instantiating ModuleNodes through the Node Palette. The ModuleNode is based on a
//! module node graph file that defines the inputs, outputs, and behavior of the node.
class ModuleNodePaletteItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(ModuleNodePaletteItem, AZ::SystemAllocator, 0);
//! Constructor
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
//! \param sourceFileId The unique id for the module node graph source file.
//! \param sourceFilePath The path to the module node graph source file. This will be used for node naming and debug output.
ModuleNodePaletteItem(GraphCanvas::EditorId editorId, AZ::Uuid sourceFileId, AZStd::string_view sourceFilePath)
: DraggableNodePaletteTreeItem(GetNodeName(sourceFilePath).data(), editorId)
, m_sourceFileName(sourceFilePath)
, m_sourceFileId(sourceFileId)
{}
~ModuleNodePaletteItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override
{
return aznew CreateModuleNodeMimeEvent(m_sourceFileName, m_sourceFileId);
}
protected:
AZStd::string m_sourceFileName;
AZ::Uuid m_sourceFileId;
};
}
@@ -0,0 +1,108 @@
/*
* 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
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
// Graph Canvas
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Integration/Helpers.h>
namespace GraphModelIntegration
{
template<typename NodeType>
class CreateStandardNodeMimeEvent;
//! Provides a common interface for instantiating GraphModel::Node subclasses through the Node Palette
template<typename NodeType>
class StandardNodePaletteItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(StandardNodePaletteItem, AZ::SystemAllocator, 0);
//! Constructor
//! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a))
StandardNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId)
: DraggableNodePaletteTreeItem(nodeName, editorId)
{
// Setting the palette override (if specified) is mainly used to set the icon color for this
// node palette item, but it can also be used to override other styling aspects as well
AZStd::string paletteOverride = Helpers::GetTitlePaletteOverride(azrtti_typeid<NodeType>());
if (!paletteOverride.empty())
{
SetTitlePalette(paletteOverride);
}
}
~StandardNodePaletteItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override
{
return aznew CreateStandardNodeMimeEvent<NodeType>();
}
};
template<typename NodeType>
class CreateStandardNodeMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI( ( (CreateStandardNodeMimeEvent<NodeType>), "{DF6213A0-5C60-4C22-88F1-4CEA6D8A17EF}", NodeType), GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(CreateStandardNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateStandardNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
bool ExecuteEvent([[maybe_unused]] const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasSceneId) override
{
GraphModel::GraphPtr graph = nullptr;
GraphManagerRequestBus::BroadcastResult(graph, &GraphManagerRequests::GetGraph, graphCanvasSceneId);
if (!graph)
{
return false;
}
AZStd::shared_ptr<GraphModel::Node> node = AZStd::make_shared<NodeType>(graph);
if (!node)
{
return false;
}
GraphControllerRequestBus::EventResult(m_createdNodeId, graphCanvasSceneId, &GraphControllerRequests::AddNode, node, dropPosition);
return true;
}
};
template<typename NodeType>
void ReflectAndCreateNodeMimeEvent(AZ::ReflectContext* context)
{
NodeType::Reflect(context);
GraphModelIntegration::CreateStandardNodeMimeEvent<NodeType>::Reflect(context);
}
}
@@ -0,0 +1,39 @@
/*
* 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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/ReadOnlyDataInterface.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
//! Satisfies GraphCanvas API requirements for showing read only property widgets in nodes.
class ReadOnlyDataInterface
: public GraphCanvas::ReadOnlyDataInterface
{
public:
AZ_CLASS_ALLOCATOR(ReadOnlyDataInterface, AZ::SystemAllocator, 0);
ReadOnlyDataInterface(GraphModel::SlotPtr slot);
~ReadOnlyDataInterface() = default;
// GraphCanvas::ReadOnlyDataInterface overrides ...
AZStd::string GetString() const override;
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,39 @@
/*
* 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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/StringDataInterface.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
//! Satisfies GraphCanvas API requirements for showing string property widgets in nodes.
class StringDataInterface
: public GraphCanvas::StringDataInterface
{
public:
AZ_CLASS_ALLOCATOR(StringDataInterface, AZ::SystemAllocator, 0);
StringDataInterface(GraphModel::SlotPtr slot);
~StringDataInterface() = default;
AZStd::string GetString() const;
void SetString(const AZStd::string& value);
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,46 @@
/*
* 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
// Qt
#include <QPixmap>
// GraphModel
#include <GraphModel/Integration/ThumbnailItem.h>
namespace GraphModelIntegration
{
/**
* Default image implementation of our ThumbnailItem class to draw a
* simple QPixmap as the thumbnail.
*/
class ThumbnailImageItem
: public ThumbnailItem
{
public:
AZ_RTTI(ThumbnailImageItem, "{DB2F488F-95CF-49BC-8DD4-806969A71A16}", ThumbnailItem);
ThumbnailImageItem(const QPixmap& image, QGraphicsItem* parent = nullptr);
void UpdateImage(const QPixmap& image);
//! Override from QGraphicsLayoutItem
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override;
//! Override from QGraphicsItem
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
protected:
QPixmap m_pixmap;
};
}
@@ -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
// AZ
#include <AzCore/RTTI/RTTI.h>
// Qt
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
namespace GraphModelIntegration
{
/**
* Base layout item class for embedding thumbnails inside a Node. The paint()
* method can be overriden to implement any custom rendering desired.
*/
class ThumbnailItem
: public QGraphicsLayoutItem
, public QGraphicsItem
{
public:
AZ_RTTI(ThumbnailItem, "{4248ADDE-4DFF-4A02-A8FD-B992E3CFF94B}");
ThumbnailItem(QGraphicsItem* parent = nullptr);
//! Override from QGraphicsLayoutItem
void setGeometry(const QRectF &geom) override;
//! Override from QGraphicsItem
QRectF boundingRect() const override;
};
}
@@ -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
// Graph Canvas
#include <GraphCanvas/Components/NodePropertyDisplay/VectorDataInterface.h>
// Graph Model
#include <GraphModel/Integration/IntegrationBus.h>
#include <GraphModel/Model/Slot.h>
namespace GraphModelIntegration
{
template<class Type, int ElementCount>
class VectorDataInterface
: public GraphCanvas::VectorDataInterface
{
public:
AZ_CLASS_ALLOCATOR(VectorDataInterface, AZ::SystemAllocator, 0);
VectorDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
~VectorDataInterface() = default;
const char* GetLabel(int index) const override
{
if (index == 0)
{
return "X";
}
else if (index == 1)
{
return "Y";
}
else if (index == 2)
{
return "Z";
}
else if (index == 3)
{
return "W";
}
return "???";
}
AZStd::string GetStyle() const override
{
return "vectorized";
}
AZStd::string GetElementStyle(int index) const override
{
return AZStd::string::format("vector_%i", index);
}
int GetElementCount() const override
{
return ElementCount;
}
double GetValue(int index) const override
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<Type>().GetElement(index);
}
else
{
return 0.0;
}
}
void SetValue(int index, double value) override
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
Type vector = slot->GetValue<Type>();
if (value != vector.GetElement(index))
{
vector.SetElement(index, aznumeric_cast<float>(value));
slot->SetValue(vector);
GraphCanvas::GraphId graphCanvasSceneId;
IntegrationBus::BroadcastResult(graphCanvasSceneId, &IntegrationBusInterface::GetActiveGraphCanvasSceneId);
IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, graphCanvasSceneId);
}
}
}
private:
AZStd::weak_ptr<GraphModel::Slot> m_slot;
};
}
@@ -0,0 +1,75 @@
/*
* 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
// AZ
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/tuple.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
// Node ID that is unique within the context of a Graph
using NodeId = int;
// Slot ID that is unique within the context of a Node
struct SlotIdData;
using SlotId = SlotIdData;
// An Endpoint is a specific Slot within a specific Node.
// It's basically a Slot ID that is unique within the context of an entire Graph.
using Endpoint = AZStd::pair<NodeId, SlotId>;
class DataType;
using DataTypePtr = AZStd::shared_ptr<const DataType>; //!< All pointers are const since this data is immutable anyway
using DataTypeList = AZStd::vector<DataTypePtr>;
class IGraphContext;
using IGraphContextPtr = AZStd::shared_ptr<IGraphContext>;
using ConstIGraphContextPtr = AZStd::shared_ptr<const IGraphContext>;
class Graph;
using GraphPtr = AZStd::shared_ptr<Graph>;
using ConstGraphPtr = AZStd::shared_ptr<const Graph>;
class GraphElement;
using GraphElementPtr = AZStd::shared_ptr<GraphElement>;
using ConstGraphElementPtr = AZStd::shared_ptr<const GraphElement>;
class Node;
using NodePtr = AZStd::shared_ptr<Node>;
using ConstNodePtr = AZStd::shared_ptr<const Node>;
using NodePtrList = AZStd::vector<NodePtr>;
class SlotDefinition;
using SlotDefinitionPtr = AZStd::shared_ptr<const SlotDefinition>; //!< All pointers are const since this data is immutable anyway
class Slot;
using SlotPtr = AZStd::shared_ptr<Slot>;
using ConstSlotPtr = AZStd::shared_ptr<const Slot>;
using SlotPtrList = AZStd::vector<SlotPtr>;
class Connection;
using ConnectionPtr = AZStd::shared_ptr<Connection>;
using ConstConnectionPtr = AZStd::shared_ptr<const Connection>;
class ModuleGraphManager;
using ModuleGraphManagerPtr = AZStd::shared_ptr<ModuleGraphManager>;
using ConstModuleGraphManagerPtr = AZStd::shared_ptr<const ModuleGraphManager>;
static const AZ::u32 DefaultWrappedNodeLayoutOrder = -1;
} // namespace GraphModel
@@ -0,0 +1,64 @@
/*
* 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
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/GraphElement.h>
#include <GraphModel/Model/Slot.h>
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
//! Defines the connection between an Output Slot and an Input Slot.
//! Usually a Connection instance will be created by the Graph class
//! rather than directly.
class Connection : public GraphElement
{
public:
AZ_CLASS_ALLOCATOR(Connection, AZ::SystemAllocator, 0);
AZ_RTTI(Connection, "{B4301AE1-98F4-474E-B0A1-18F27EEDB059}", GraphElement);
static void Reflect(AZ::ReflectContext* context);
Connection() = default; // Needed by SerializeContext
~Connection() override = default;
//! Create a Connection for a specific Graph, though this doesn't actually
//! add it to the Graph.
Connection(GraphPtr graph, SlotPtr sourceSlot, SlotPtr targetSlot);
//! Initializion after the Connection has been serialized in.
//! This must be called whenever the default constructor is used.
//! Sets the m_graph pointer and caches pointers to other GraphElements.
void PostLoadSetup(GraphPtr graph);
NodePtr GetSourceNode() const;
NodePtr GetTargetNode() const;
SlotPtr GetSourceSlot() const;
SlotPtr GetTargetSlot() const;
const Endpoint& GetSourceEndpoint() const;
const Endpoint& GetTargetEndpoint() const;
private:
AZStd::weak_ptr<Slot> m_sourceSlot;
AZStd::weak_ptr<Slot> m_targetSlot;
Endpoint m_sourceEndpoint;
Endpoint m_targetEndpoint;
};
} // namespace GraphModel
@@ -0,0 +1,98 @@
/*
* 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
// AZ
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
// GraphModel
#include <GraphModel/Model/Common.h>
namespace AZ
{
class ReflectContext;
}
namespace GraphModel
{
//! Provides a way for client systems to describe each data type that they support, including a
//! unique enum value, the AZ type Uuid, and a user-friendly display name. Client systems may
//! subclass DataType if desired, for example to provide additional name formats.
class DataType
{
public:
AZ_CLASS_ALLOCATOR(DataType, AZ::SystemAllocator, 0);
AZ_RTTI(DataType, "{B8CBD17E-B8F7-4090-99A7-E9E9970D3EF3}");
static void Reflect(AZ::ReflectContext* reflection);
//! Data types can be described by a simple enum value. Client systems can
//! use whatever value they want as long as each type has a unique value.
using Enum = uint32_t;
static const Enum ENUM_INVALID = uint32_t(-1);
DataType();
//! Constructs a new DataType object.
//! @param typeEnum - The main unique ID used by the GraphModel framework for this DataType object.Every DataType in the IGraphContext must have a unique enum value.
//! @param typeUuid - An alternate unique ID that is used by the node graph UI system. (This is not necessarily the same thing as an RTTI TypeId.The only requirement is that it maps 1:1 with the typeEnum).
//! @param defaultValue - The default value assigned to any slot that uses this data type upon creation.
//! @param typeDisplayName - Used for tooltips or other UI elements as well as debug messages.This should be unique, and similar to typeEnum.
//! @param cppTypeName - The name of the c++ class that the DataType maps to.This is only used for debug messages.
DataType(Enum typeEnum, AZ::Uuid typeUuid, AZStd::any defaultValue, AZStd::string_view typeDisplayName, AZStd::string_view cppTypeName);
DataType(const DataType& other);
virtual ~DataType() = default;
// Because the DataType class is supposed to be immutable
bool operator=(const DataType& other) = delete;
bool operator==(const DataType& other) const;
bool operator!=(const DataType& other) const;
bool IsValid() const;
//! Return the enum value that identifies this DataType
Enum GetTypeEnum() const { return m_typeEnum; }
//! Return the type Uuid that corresponds to this DataType
AZ::Uuid GetTypeUuid() const;
//! Returns GetTypeUuid() as a string (for convenience)
AZStd::string GetTypeUuidString() const;
//! Returns a default value for data of this type
AZStd::any GetDefaultValue() const;
//! Returns the C++ type name
AZStd::string GetCppName() const;
//! Returns a user friently type name, for UI display
AZStd::string GetDisplayName() const;
private:
Enum m_typeEnum;
AZ::Uuid m_typeUuid;
AZStd::any m_defaultValue;
AZStd::string m_cppName;
AZStd::string m_displayName;
};
} // namespace GraphModel
@@ -0,0 +1,168 @@
/*
* 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
// AZ
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/any.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/GraphElement.h>
namespace AZ
{
class ReflectContext;
}
namespace GraphModel
{
//! This is the main class for the GraphModel framework. It provides a generic node graph data model
//! that is originally intended for use with Graph Canvas providing the UI, but in theory it could
//! be used with any node graph widget system. It is also designed with primarily editor processing
//! in mind, rather than runtime processing, so if this were to be used at runtime we may need some
//! improvements.
//!
//! Data Model Goals
//! * Keep it simple
//! (For example, avoid using the component entity system even though that's an established pattern
//! in Script Canvas's data model)
//! * It shouldn't know anything about GraphCanvas or any other UI representation; it's purely a data model
//! * Make it suitible for multiple contexts
//! (For example, even though this is built for Shader Canvas initially, it should be generic enough
//! to use for the Particle Editor or other contexts)
//! * It shouldn't know anything about how the nodes will be used.
//! (For example, any functionality specific to Shader Canvas, Particle Editors, or any other context
//! should be added to some external subclasses of GraphModel classes)
//!
//! Key elements of a Graph include
//! * Node - The main building block of a Graph. Contains multiple input slots and output slots.
//! * Slot - Every node contains input slots and/or output slots that can be connected together
//! * Endpoint - A specific Slot on a specific Node; basically a {NodeID,SlotID} pair
//! * Connection - A link from an output Endpoint to an input Endpoint (we could also say this is a link
//! from an output Slot to an input Slot)
//! * Metadata - Every graph can contain generic metadata like comments and node groupings for example.
//! But this is specific to the node graph UI system, and the Graph class just stores this
//! in an abstract way to bundle the data together.
//!
//! For continued reading, see Node.h next.
class Graph : public AZStd::enable_shared_from_this<Graph>
{
public:
AZ_CLASS_ALLOCATOR(Graph, AZ::SystemAllocator, 0);
AZ_RTTI(Graph, "{CBF5DC3C-A0A7-45F5-A207-06433A9A10C5}");
static void Reflect(AZ::ReflectContext* context);
typedef AZStd::unordered_map<NodeId, NodePtr> NodeMap;
typedef AZStd::unordered_map<NodeId, ConstNodePtr> ConstNodeMap;
// Used to store the mappings for our wrapped nodes, where the key is the NodeId of
// the wrapped node, and the value is a pair of the NodeId for the parent WrapperNode
// and the layout order for the wrapped node
typedef AZStd::unordered_map<NodeId, AZStd::pair<NodeId, AZ::u32>> NodeWrappingMap;
// We use a vector instead of set to maintain a consistent order in the serialized data, to reduce diffs
typedef AZStd::vector<ConnectionPtr> ConnectionList;
Graph() = default; // Needed by SerializeContext
Graph(const Graph&) = delete;
//! Constructor
//! \param graphContext interface to client system specific data and functionality
explicit Graph(IGraphContextPtr graphContext);
virtual ~Graph() = default;
//! Initializion after the Graph has been serialized in.
//! This must be called after building a Graph from serialized data
//! in order to connect internal pointers between elements of the Graph
//! and perform any other precedural setup that isn't stored in the
//! serialized data.
//! \param graphContext interface to client system specific data and functionality
void PostLoadSetup(IGraphContextPtr graphContext);
//! Add a node that has been deserialized to the graph
//! This should only be necessary for cases like copy/paste where we
//! need to load a deserialized node, but don't actually know the nodeId before-hand
NodeId PostLoadSetup(NodePtr node);
//! Returns the interface to client system specific data and functionality
IGraphContextPtr GetContext() const;
//! This name is used for debug messages in GraphModel classes, to provide appropriate context for the user.
//! It's a convenience function for GetContext()->GetSystemName()
const char* GetSystemName() const;
//! Adds a Node to the graph and gives it a unique ID
NodeId AddNode(NodePtr node);
//! Removes a Node and all connections between it and other Nodes in the graph
bool RemoveNode(ConstNodePtr node);
//! Wrap (embed) the node onto the specified wrapperNode
//! The wrapperNode and node must already exist in the graph before being wrapped
void WrapNode(NodePtr wrapperNode, NodePtr node, AZ::u32 layoutOrder = DefaultWrappedNodeLayoutOrder);
//! Remove the wrapping from the specified node
void UnwrapNode(ConstNodePtr node);
//! Return our full map of node wrappings
const NodeWrappingMap& GetNodeWrappings();
NodePtr GetNode(NodeId nodeId);
const NodeMap& GetNodes();
ConstNodeMap GetNodes() const;
//! Adds a new connection between sourceSlot and targetSlot and returns the
//! new Connection, or returns the existing Connection if one already exists.
ConnectionPtr AddConnection(SlotPtr sourceSlot, SlotPtr targetSlot);
//! Removes a connection from the Graph, and returns whether it was found and removed
bool RemoveConnection(ConstConnectionPtr connection);
const ConnectionList& GetConnections();
//! Set/gets a bundle of generic metadata that is provided by the node graph UI
//! system. This may include node positions, comment blocks, node groupings, and
//! bookmarks, for example.
void SetUiMetadata(const AZStd::any& uiMetadata);
const AZStd::any& GetUiMetadata() const;
AZStd::any& GetUiMetadata();
AZStd::shared_ptr<Slot> FindSlot(const Endpoint& endpoint);
protected:
bool Contains(SlotPtr slot) const;
ConnectionPtr FindConnection(ConstSlotPtr sourceSlot, ConstSlotPtr targetSlot);
//! Common implementation for removing a specific connection from m_connections
bool RemoveConnection(ConnectionList::iterator iter);
private:
NodeMap m_nodes;
int m_nextNodeId = 1; //!< NodeIds are unique within each Graph. This is a simple counter for generating the next ID.
ConnectionList m_connections;
//! Used to store and serialize metadata from the graph UI, like node positions, comments, group boxes, etc.
AZStd::any m_uiMetadata;
//! Used to store all of our node <-> wrapper node mappings
NodeWrappingMap m_nodeWrappings;
IGraphContextPtr m_graphContext; //!< interface to client system specific data and functionality
};
} // namespace GraphModel
@@ -0,0 +1,49 @@
/*
* 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
// AZ
#include <AzCore/Memory/SystemAllocator.h>
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/Graph.h>
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
//! The common base class for every element in a Graph, like Node, Slot, and Connection.
class GraphElement
{
public:
AZ_CLASS_ALLOCATOR(GraphElement, AZ::SystemAllocator, 0);
AZ_RTTI(GraphElement, "{FD83C7CA-556B-49F1-BACE-6E9C7A4D6347}");
GraphElement() = default; // Needed by SerializeContext
virtual ~GraphElement() = default;
GraphElement(GraphPtr graph);
//! Returns the Graph that owns this GraphElement
GraphPtr GetGraph() const;
//! Returns the IGraphContext for this GraphElement
IGraphContextPtr GetGraphContext() const;
protected:
AZStd::weak_ptr<Graph> m_graph; // Every GraphElement will at least need a pointer to the Graph, so it can convert IDs into actual element pointers.
};
} // namespace GraphModel
@@ -0,0 +1,70 @@
/*
* 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
// AZ
#include <AzCore/std/containers/vector.h>
// GraphModel
#include <GraphModel/Model/DataType.h>
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
//! IGraphContext provides an interface to client system specific features for the GraphModel framework to use.
//! All systems that use GraphModel must provide an implementation of this interface, passed to the main Graph object.
class IGraphContext
{
public:
using DataTypeList = AZStd::vector<DataTypePtr>;
virtual ~IGraphContext() = default;
//! Returns the name of the system that is using the GraphModel framework, mostly for debug messages.
virtual const char* GetSystemName() const = 0;
//! Returns the file extension used for module files
virtual const char* GetModuleFileExtension() const = 0;
//! Returns a ModuleGraphManager to support creating ModuleNodes. Subclasses can just return nullptr if this isn't needed.
virtual ModuleGraphManagerPtr GetModuleGraphManager() const = 0;
//! Returns all available data types.
virtual const DataTypeList& GetAllDataTypes() const = 0;
//! Returns a DataType object representing the given TypeId, or Invalid if it doesn't exist.
virtual DataTypePtr GetDataType(AZ::Uuid typeId) const = 0;
//! Returns a DataType object representing the given AZStd::any value, or Invalid if it doesn't exist.
//! This data type method has a different name because if the GraphContext implementation doesn't override
//! this, there will be a compile error for a hidden function because of subclasses implementing
//! the templated version below
virtual DataTypePtr GetDataTypeForValue(const AZStd::any& value) const { return GetDataType(value.type()); }
//! Returns a DataType object representing the given TypeId, or Invalid if it doesn't exist.
virtual DataTypePtr GetDataType(DataType::Enum typeEnum) const = 0;
//! Utility function to returns a DataType object representing the given template type T, or Invalid if it doesn't exist.
//! Subclasses may need to implement this function too, and just call IGraphContext::GetDataType<T>()
//! in order to avoid "error C2275: 'Type': illegal use of this type as an expression"
template<typename T>
DataTypePtr GetDataType() const { return GetDataType(azrtti_typeid<T>()); }
};
} // namespace GraphModel
@@ -0,0 +1,110 @@
/*
* 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
// AZ
#include <AzCore/std/string/string_view.h>
// Graph Model
#include <GraphModel/Model/Node.h>
namespace AZ
{
class ReflectContext;
}
namespace GraphModel
{
//! Common base class for GraphInput/OutputNodes
class BaseInputOutputNode : public Node
{
public:
AZ_RTTI(BaseInputOutputNode, "{C54F11AE-3151-44D7-B206-9206FA888963}", Node);
static void Reflect(AZ::ReflectContext* context);
BaseInputOutputNode() = default; // Needed by SerializeContext
//! Constructor
//! \param graph The graph that owns this node
//! \param dataType The type of data represented by this node
BaseInputOutputNode(GraphPtr graph, DataTypePtr dataType);
const char* GetTitle() const override;
GraphModel::DataTypePtr GetNodeDataType() const;
AZStd::string GetName() const;
AZStd::string GetDisplayName() const;
AZStd::string GetDescription() const;
protected:
//! Registers metadata slots that are common for inputs and outputs, like name, displayName, and description.
void RegisterCommonSlots(AZStd::string_view directionName);
AZStd::string m_title;
AZStd::shared_ptr<DataType> m_dataType;
};
//! Provides a node that serves as a data input into a node graph.
class GraphInputNode : public BaseInputOutputNode
{
public:
AZ_RTTI(GraphInputNode, "{4CDE10B9-14C1-4B5A-896C-C3E15EDAC665}", BaseInputOutputNode);
static void Reflect(AZ::ReflectContext* context);
GraphInputNode() = default; // Needed by SerializeContext
//! Constructor
//! \param graph The graph that owns this node
//! \param dataType The type of data represented by this node
GraphInputNode(GraphModel::GraphPtr graph, DataTypePtr dataType);
void PostLoadSetup(GraphPtr graph, NodeId id) override;
//! Returns the value of the DefaultValue slot, which indicates the default value for this input. This
//! is the value that will be used when this node's graph is used as a ModuleNode, but no data is
//! connected to this graph input.
AZStd::any GetDefaultValue() const;
protected:
//! Registers SlotDescriptors for each of this node's slots
void RegisterSlots() override;
};
//! Provides an node that serves as a data output from a node graph.
class GraphOutputNode : public BaseInputOutputNode
{
public:
AZ_RTTI(GraphOutputNode, "{5E5188E1-7F79-41D4-965F-248EECE7A735}", BaseInputOutputNode);
static void Reflect(AZ::ReflectContext* context);
GraphOutputNode() = default; // Needed by SerializeContext
//! Constructor
//! \param graph The graph that owns this node
//! \param dataType The type of data represented by this node
GraphOutputNode(GraphModel::GraphPtr graph, DataTypePtr dataType);
void PostLoadSetup(GraphPtr graph, NodeId id) override;
protected:
//! Registers SlotDescriptors for each of this node's slots
void RegisterSlots() override;
};
}
@@ -0,0 +1,82 @@
/*
* 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
// AZ
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
// Graph Model
#include <GraphModel/Model/Common.h>
namespace AZ
{
namespace IO
{
class FileIOStream;
}
}
namespace GraphModel
{
//! This is a manager that exists to support ModuleNode. A ModuleNode is a node that contains
//! another node graph to be reused as a single node. If there are multiple ModuleNode instances
//! that all use the same graph, we should only need one copy of the referenced graph in memory.
//! The collection of available modules graphs will be managed here.
//! The graphs stored here are const/immutable, and used only for instancing ModuleNodes, which
//! do not make any changes to the underlying module graph.
class ModuleGraphManager
: public AzToolsFramework::AssetSystemBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ModuleGraphManager, AZ::SystemAllocator, 0);
AZ_RTTI(Graph, "{68476353-C672-4408-9B34-A409CC63858E}");
explicit ModuleGraphManager(IGraphContextPtr graphContext, AZ::SerializeContext* serializeContext = nullptr);
virtual ~ModuleGraphManager();
//! Returns the Graph loaded from a module source file. If the file has already been loaded,
//! it simply returns the Graph. If it has not been loaded yet, this function will first load
//! the Graph from the source file.
//! \param sourceFileId Unique Id of the sourfe file that contains a module graph
AZ::Outcome<ConstGraphPtr, AZStd::string> GetModuleGraph(AZ::Uuid sourceFileId);
protected:
//! Loads a module graph from the given stream
virtual ConstGraphPtr LoadGraph(AZ::IO::FileIOStream& stream);
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetSystemBus
//! WHen a module graph source file is added or changed, this will cause the Graph to be reloaded
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
//////////////////////////////////////////////////////////////////////////
private:
//! Loads a Graph from a module source file
//! \param sourceFileId Unique Id of the sourfe file that contains a module graph
AZ::Outcome<ConstGraphPtr, AZStd::string> LoadGraph(AZ::Uuid sourceFileId);
// We use a weak_ptr to allow the graphs to go out of scope and be deleted when not used
using ModuleGraphMap = AZStd::unordered_map<AZ::Uuid /*Source File ID*/, AZStd::weak_ptr<const Graph>>;
AZStd::weak_ptr<IGraphContext> m_graphContext; //!< interface to client system specific data and functionality. Uses a weak_ptr so the IGraphContext can hold this ModuleGraphManager.
AZStd::string m_moduleFileExtension;
AZ::SerializeContext* m_serializeContext;
ModuleGraphMap m_graphs;
};
} // namespace GraphModel
@@ -0,0 +1,58 @@
/*
* 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
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/Node.h>
namespace GraphModel
{
//! Encapsulates an entire node graph as a node to be used in another graph.
//! The graph that defines this node is called a Module Graph, which has
//! GraphInputNodes and/or GraphOutputNodes to define inputs and outputs
//! for the graph. These input/output nodes become input/output Slots in
//! the ModuleNode.
class ModuleNode : public Node
{
public:
AZ_CLASS_ALLOCATOR(ModuleNode, AZ::SystemAllocator, 0);
AZ_RTTI(ModuleNode, "{C7D57EFE-462D-48A0-B46F-6E927D504BA5}", Node);
static void Reflect(AZ::ReflectContext* context);
ModuleNode() = default; // Needed by SerializeContext
//! Constructor
//! \param ownerGraph The graph that owns this node
//! \param sourceFileId The unique id for the module node graph source file, which is the module graph that defines this ModuleNode.
//! \param sourceFilePath The path to the module node graph source file. This will be used for node naming and debug output.
ModuleNode(GraphPtr ownerGraph, AZ::Uuid moduleGraphFileId, AZStd::string_view moduleGraphFileName);
const char* GetTitle() const override;
void PostLoadSetup(GraphPtr ownerGraph, NodeId id) override;
protected:
//! Gets the module graph that defines this ModuleNode
void LoadModuleGraph(ModuleGraphManagerPtr moduleGraphManager);
//! Registers input and output SlotDescriptions based on the contents of the module graph
void RegisterSlots() override;
ConstGraphPtr m_moduleGraph; //!< The module graph that defines the inputs, outputs, and behavior of this node
AZStd::string m_nodeTitle; //!< Node title indicates the name of the module file
AZ::Uuid m_moduleGraphFileId; //!< Unique identifier of the source file that contains the module graph
};
}
@@ -0,0 +1,258 @@
/*
* 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
// AZ
#include <AzCore/Math/Crc.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/GraphElement.h>
#include <GraphModel/Model/Slot.h>
namespace AZ
{
class ReflectContext;
}
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
enum class NodeType
{
GeneralNode = 0,
WrapperNode
};
//! The abstract base class for every type of node in a Graph. It consists primarily of a set of
//! Slots. There is no functionality here beyond managing the Slots and finding Connections. Any
//! useful functionality must be provided by subclasses in the client context where the GraphModel
//! framework is used.
//!
//! Slots are divided into two main objects: a SlotDefinition and the actual Slot.
//! The SlotDefinition contains the predefined description of each slot that the Node contains. This
//! information is not saved with the Node data in the Graph because it is provided by the Node subclass
//! itself (either hard-coded or reflected from some other source). The Slot is the functional part,
//! and contains any data related to specific instance of the Node (for example, the default value
//! of an Input Data Slot). This data is serialized with the Node. Whenever a Node is created, either
//! directly or by serializing in, the Node class ensures that Slots are created for each SlotDefinition
//! defined by the Node subclass.
//!
//! Every Slot in the Node has a SlotId, which is unique within the context of the Node.
//! A specific Slot in a spectific Node is called an Endpoint and is identified by a pairing of
//! NodeId and SlotId.
//!
//! Subclasses must call RegisterSlot() to define the Node's inputs and outputs, but it shouldn't need
//! to serialize any of its own data. This base class's Slot lists are reflected for serialization, and
//! that's all that should be needed in most cases.
class Node : public GraphElement, public AZStd::enable_shared_from_this<Node>
{
friend class Graph; // Because the Graph needs to set the ID, but no one else should be able to.
public:
AZ_CLASS_ALLOCATOR(Node, AZ::SystemAllocator, 0);
AZ_RTTI(Node, "{274B4495-FDBF-45A9-9BAD-9E90269F2B73}", GraphElement);
static void Reflect(AZ::ReflectContext* context);
static const int INVALID_NODE_ID = 0;
using SlotDefinitionList = AZStd::vector<SlotDefinitionPtr>;
// We use a map instead of unordered_map to get consistent order in XML to make diffs more readable. Performance
// isn't really a concern because these maps will be rather small.
// But ConstSlotMap uses unordered_map because it isn't serialized.
using SlotMap = AZStd::map<SlotId, SlotPtr>;
using ConstSlotMap = AZStd::unordered_map<SlotId, const SlotPtr>;
// Special case mapping for holding the extendable slots so we can keep track of their indexed
// order as well.
struct SortSlotsBySubId
{
AZ_TYPE_INFO(SortSlotsBySubId, "{01ED3FF5-0DE4-4B25-84FA-8763EB05FAFE}");
SortSlotsBySubId() = default;
bool operator()(const SlotPtr& left, const SlotPtr& right) const
{
return left->GetSlotSubId() < right->GetSlotSubId();
}
};
using ExtendableSlotSet = AZStd::set<SlotPtr, SortSlotsBySubId>;
using ExtendableSlotMap = AZStd::map<SlotName, ExtendableSlotSet>;
Node() = default; // Needed by SerializeContext
//! Constructor
//! \param graph The Graph that will own this Node (though constructing the Node doesn't actually add it to this graph yet).
explicit Node(GraphPtr graph);
//! Initializion after the Node has been serialized in.
//! This must be called whenever the default constructor is used.
//! Sets the m_graph pointer and caches pointers to other GraphElements.
//! It also ensures the loaded Slot data aligns with the defined SlotDefinitions.
virtual void PostLoadSetup(GraphPtr graph, NodeId id);
//! An alternative to the above PostLoadSetup when the
//! nodeId isn't already known (e.g. a deserialized node that has been copy/pasted)
virtual void PostLoadSetup();
//! Returns the name that will be displayed as the title of the Node in the UI
virtual const char* GetTitle() const = 0;
//! Returns the name that will be displayed as the sub-title of the Node in the UI
virtual const char* GetSubTitle() const
{
return "";
};
//! Returns node type (general by default) which can be overriden for
//! other types, such as wrapper nodes
virtual NodeType GetNodeType() const
{
return NodeType::GeneralNode;
}
NodeId GetId() const;
bool Contains(ConstSlotPtr slot) const;
//! Returns SlotDefinitions for all available Slots
const SlotDefinitionList& GetSlotDefinitions() const;
//! Returns the map of all available Slots.
//! For the generic case, there will be one Slot for every SlotDefinition in GetSlotDefinitions().
//! Additionally, for extendable slots there could be 0 or more Slots per SlotDefinition
const SlotMap& GetSlots();
ConstSlotMap GetSlots() const;
//! Returns the slot with the given slotId, or nullptr if it doesn't exist
SlotPtr GetSlot(const SlotId& slotId);
ConstSlotPtr GetSlot(const SlotId& slotId) const;
//! Returns the slot with the given SlotName, or nullptr if it doesn't exist.
//! This is a simplified version for normal (non-extendable) slots. It is equivalent to calling GetSlot
//! with the given SlotName and a subId of 0. If the slot is actually extendable, it
//! will return the first indexed slot if it exists.
SlotPtr GetSlot(const SlotName& name);
ConstSlotPtr GetSlot(const SlotName& name) const;
//! Returns an ordered set of the extendable slots for a given SlotName, or an empty set if there are none
const ExtendableSlotSet& GetExtendableSlots(const SlotName& name);
//! Returns the number of extendable slots for a given SlotName.
//! Will return -1 if the specified slot is not extendable.
int GetExtendableSlotCount(const SlotName& name);
//! Returns the DataType for the given slot, which can be overriden for individual nodes to extend
virtual DataTypePtr GetDataType(ConstSlotPtr slot) const;
//! Delete the specified slot, which is only allowed on extendable slots.
//! This method does nothing if the slot is not extendable.
void DeleteSlot(SlotPtr slot);
//! Check if the specified slot can be deleted, which is restricted by
//! - The slot must be extendable
//! - Deleting the slot can't reduce the number of extendable slots below the configured minimum
//! number of allowed slots for this definition
//! This method is also virtual so the client can impose any custom limitations as needed.
//! This method does nothing if the slot is not extendable.
virtual bool CanDeleteSlot(ConstSlotPtr slot) const;
//! Append a new slot to an extendable slot list.
//! This is restricted such that
//! - The slot definition must be extendable
//! - Creating a new slot can't increase the number of extendable slots above the configured maximum
//! number of allowed slots for this definition
//! This method does nothing if the slot is not extendable.
virtual SlotPtr AddExtendedSlot(const SlotName& slotName);
protected:
//! Default implementation will prevent slots from being extended past the
//! maximum allowed configuration, but the client could override this to impose
//! additional restrictions
virtual bool CanExtendSlot(SlotDefinitionPtr slotDefinition) const;
//! Subclasses should call this function during construction to define its slots.
//! The slot's name must be unique among all slots in this node.
void RegisterSlot(SlotDefinitionPtr slotDefinition);
//! Overridden by specific nodes to register their necessary slots
//! This is called automatically by this Node base class when PostLoadSetup is called,
//! which occurs after a Node has been deserialized. The derived classes are stil in charge
//! of calling it in their Node(GraphPtr graph) constructor, since their overrides aren't
//! accessible from the base class during construction.
virtual void RegisterSlots() {}
//! Once a subclass is done calling RegisterSlot(), it can call this function to
//! instantiate all the slot data. This should only be done when creating a new
//! Node, not when loading a Node from serialize data (in that case Slot creation
//! will be handled automatically by PostLoadSetup()).
void CreateSlotData();
private:
//! Common implementation for RegisterSlot() to a specific SlotDefinitionList
void RegisterSlot(SlotDefinitionPtr slotDefinition, SlotDefinitionList& slotDefinitionList);
//! Common implementation for CreateSlotData() to a specific SlotMap
void CreateSlotData(SlotMap& slotMap, const SlotDefinitionList& slotDefinitionList);
//! Specific implementation for creating slot data for extendable slots
void CreateExtendableSlotData();
//! This is a substep in the PostLoadSetup() process. It ensures that the set of loaded Slot
//! data aligns with the Node's pre-established SlotDefinitions, and calls PostLoadSetup()
//! on each Slot.
//! \param slotData The collection of loaded Slot object data (m_inputDataSlots, m_outputDataSlots, etc)
//! \param slotDefinitions The set of estalished SlotDefinitions that defines the Node's set of available slots
void SyncAndSetupSlots(SlotMap& slotData, Node::SlotDefinitionList& slotDefinitions);
//! Special case of above method for the extendable slots, which are stored in a different mapping
void SyncAndSetupExtendableSlots();
//! Assertions for use during slot registration, to prevent duplicates
void AssertPointerIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const;
void AssertNameIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const;
void AssertDisplayNameIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const;
NodeId m_id = INVALID_NODE_ID;
// These are what will actually be serialized. That way we don't have to make every node sub-type
// provide its own reflection; it just puts the slots in these lists via RegisterSlot() and CreateSlotData().
// These are stored in separate maps rather than just one because some of these slot types don't need to be
// reflected to SerializeContext.
SlotMap m_propertySlots; //!< For slots with configuration = SlotDirection::Input SlotType::Property
SlotMap m_inputDataSlots; //!< For slots with configuration = SlotDirection::Input SlotType::Data
SlotMap m_outputDataSlots; //!< For slots with configuration = SlotDirection::Output SlotType::Data
SlotMap m_inputEventSlots; //!< For slots with configuration = SlotDirection::Input SlotType::Event
SlotMap m_outputEventSlots; //!< For slots with configuration = SlotDirection::Output SlotType::Event
ExtendableSlotMap m_extendableSlots; //!< For all extendable slots, regardless of configuration, since we need to serialize all of them
SlotMap m_allSlots; //!< Provies a single list of all of the above SlotMaps for convenient looping over them all
// These are not serialized; they're definitions of the slots that are part of the Node type
// definition so saving this data is unnecessary.
SlotDefinitionList m_propertySlotDefinitions; //!< For slots with configuration = SlotDirection::Input SlotType::Property
SlotDefinitionList m_inputDataSlotDefinitions; //!< For slots with configuration = SlotDirection::Input SlotType::Data
SlotDefinitionList m_outputDataSlotDefinitions; //!< For slots with configuration = SlotDirection::Output SlotType::Data
SlotDefinitionList m_inputEventSlotDefinitions; //!< For slots with configuration = SlotDirection::Input SlotType::Event
SlotDefinitionList m_outputEventSlotDefinitions; //!< For slots with configuration = SlotDirection::Output SlotType::Event
SlotDefinitionList m_extendableSlotDefinitions; //!< For all extendable slot configurations
SlotDefinitionList m_allSlotDefinitions; //!< Provies a single list of all of the above SlotDefinitionLists for convenient looping over them all
};
} // namespace GraphModel
@@ -0,0 +1,353 @@
/*
* 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
// AZ
#include <AzCore/std/any.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
// Graph Model
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/GraphElement.h>
#include <GraphModel/Model/IGraphContext.h>
namespace GraphModel
{
//!!! Start in Graph.h for high level GraphModel documentation !!!
//! All slots have a SlotType and a SlotDirection. This combination determines the slot's available features.
enum class SlotType
{
Invalid,
Data, //!< Has a DataType and can be connected to other Data slots. Input Data slot has a default value for when it is not connected.
Event, //!< Has no DataType. Represents an event sent or received. Can be connected to other Event slots.
Property //!< Has a DataType and a value. Cannot be connected to other slots. SlotDirection must be Input.
};
//! All slots have a SlotType and a SlotDirection. This combination determines the slot's available features.
enum class SlotDirection
{
Invalid,
Input, //!< Represents information consumed by the node, usually appearing on the left side.
Output //!< Represents information produced by the node, usually appearing on the right side.
};
//! The sub ID is only used for extendable slots that can support
//! multiple slots of the same definition, where the sub ID is a
//! counter, not an index of the current slots
using SlotName = AZStd::string;
using SlotSubId = int;
struct SlotIdData
{
public:
AZ_TYPE_INFO(SlotIdData, "{D24130B9-89C4-4EAA-9A5D-3469B05C5065}");
static void Reflect(AZ::ReflectContext* context);
SlotIdData() = default;
explicit SlotIdData(const SlotName& name);
explicit SlotIdData(const SlotName& name, SlotSubId subId);
~SlotIdData() = default;
bool IsValid() const;
bool operator==(const SlotIdData& rhs) const;
bool operator!=(const SlotIdData& rhs) const;
bool operator<(const SlotIdData& rhs) const;
bool operator>(const SlotIdData& rhs) const;
AZStd::size_t GetHash() const;
SlotName m_name;
SlotSubId m_subId = 0;
};
struct ExtendableSlotConfiguration
{
public:
AZ_TYPE_INFO(ExtendableSlotConfiguration, "{ACD18AD2-AD90-408C-9C11-920C2A8D77EC}");
AZ_CLASS_ALLOCATOR(ExtendableSlotConfiguration, AZ::SystemAllocator, 0);
ExtendableSlotConfiguration() = default;
~ExtendableSlotConfiguration() = default;
bool m_isValid = false; //!< Flag to determine if this extendable slot configuration is valid
AZStd::string m_addButtonLabel; //!< Label for the button for adding new extendable slots
AZStd::string m_addButtonTooltip; //!< Tooltip for the button for adding new extendable slots
int m_minimumSlots = 1;
int m_maximumSlots = 100;
};
//! Provides static information about a Slot, like its name and data type.
//! The set of features provided by this slot is determined by the combination
//! of SlotDirection and SlotType, which is set depending on which Create* function
//! is used to create the SlotDefinition.
//!
//! This information will either be hard-coded for each Node type, or
//! reflected from some other source, so it does not need to be saved with
//! the Node data (i.e. it isn't added to a SerializeContext).
//!
//! See the Node class documentation for more.
//!
//! (We take the approach of using a single class with some features unused
//! in specific configurations because it ends up being cleaner than a complex
//! class hierarchy).
class SlotDefinition
{
public:
AZ_CLASS_ALLOCATOR(SlotDefinition, AZ::SystemAllocator, 0);
AZ_RTTI(SlotDefinition, "{917F9C1A-1513-4694-B25A-D6404A4991ED}");
SlotDefinition() = default;
virtual ~SlotDefinition() = default;
//! This set of factory functions create a SlotDefinition for each of the valid SlotDirection/SlotType combinations
static SlotDefinitionPtr CreateInputData(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
static SlotDefinitionPtr CreateInputData(AZStd::string_view name, AZStd::string_view displayName, DataTypeList supportedDataTypes, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
static SlotDefinitionPtr CreateOutputData(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
static SlotDefinitionPtr CreateInputEvent(AZStd::string_view name, AZStd::string_view displayName, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
static SlotDefinitionPtr CreateOutputEvent(AZStd::string_view name, AZStd::string_view displayName, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
static SlotDefinitionPtr CreateProperty(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration = nullptr);
SlotDirection GetSlotDirection() const;
SlotType GetSlotType() const;
//! Returns whether slot value is relevent to this slot's configuration
bool SupportsValue() const;
//! Returns whether slot data type is relevent to this slot's configuration
bool SupportsDataType() const;
//! Returns whether this slot's configuration allows connections to other slots
bool SupportsConnections() const;
//! Returns whether this slot matches the given configuration
bool Is(SlotDirection slotDirection, SlotType slotType) const;
//! Returns whether or not this slot is configured to be extendable
bool SupportsExtendability() const;
const SlotName& GetName() const; //!< Valid for all slot configurations
const AZStd::string& GetDisplayName() const; //!< Valid for all slot configurations
const AZStd::string& GetDescription() const; //!< Valid for all slot configurations
const DataTypeList& GetSupportedDataTypes() const; //!< Valid for Data and Property slots. Otherwise returns an empty DataTypeList.
AZStd::any GetDefaultValue() const; //!< Valid for Input Data and Property slots. Otherwise returns an empty AZStd::any.
//! These methods are only pertinent for extendable slots
const int GetMinimumSlots() const; //!< Retrieve the minimum configured number of extendable slots (returns a default value if not configured)
const int GetMaximumSlots() const; //!< Retrieve the maximum configured number of extendable slots (returns a default value if not configured)
const AZStd::string& GetExtensionLabel() const; //!< Retrieve the text for the label with the '+' sign for adding extendable slots
const AZStd::string& GetExtensionTooltip() const; //!< Retrieve the hover tooltip for the label with the '+' sign for adding extendable slots
private:
//! Helper method for handling the assignment/registration of the extendable slot configuration for a definition.
static void HandleExtendableSlotRegistration(AZStd::shared_ptr<SlotDefinition> slotDefinition, ExtendableSlotConfiguration* extendableSlotConfiguration);
SlotDirection m_slotDirection = SlotDirection::Invalid;
SlotType m_slotType = SlotType::Invalid;
SlotName m_name;
AZStd::string m_displayName;
AZStd::string m_description;
DataTypeList m_supportedDataTypes;
AZStd::any m_defaultValue;
ExtendableSlotConfiguration m_extendableSlotConfiguration;
};
//!!! Start in Graph.h for high level GraphModel documentation !!!
//! Represents the instance of a slot, based on a specific SlotDefinition.
//! If you think of the SlotDefinition as a class declaration, then a Slot is like an
//! instance of that class. Slots may contain data like default values and connections
//! to other Slots. The specific set of supported features is determined by the
//! SlotDefinition's combination of SlotType and SlotDirection.
//!
//! (We take the approach of using a single class with some features unused
//! in specific configurations because it ends up being cleaner than a complex
//! class hierarchy).
class Slot : public GraphElement, public AZStd::enable_shared_from_this<Slot>
{
friend class Graph; // So the Graph can update the Slot's cache of Connection pointers
public:
AZ_CLASS_ALLOCATOR(Slot, AZ::SystemAllocator, 0);
AZ_RTTI(Slot, "{50494867-04F1-4785-BB9C-9D6C96DCBFC9}", GraphElement);
static void Reflect(AZ::ReflectContext* context);
using ConnectionList = AZStd::set<AZStd::shared_ptr<Connection>>; // AZStd::unordered_set doesn't work with shared_ptr so use set
using WeakConnectionList = AZStd::list<AZStd::weak_ptr<Connection>>; // AZStd::set doesn't work with weak_ptr so use list
Slot() = default; // Needed by SerializeContext
~Slot() override = default;
//! Constructor
//! \param graph The Graph that will own this Slot
//! \param slotDefinition The descriptor that defines this Slot
//! \param subId The subId that is used to identify extendable slots
Slot(GraphPtr graph, SlotDefinitionPtr slotDefinition, SlotSubId subId = 0);
//! Initializion after the Slot has been serialized in.
//! This must be called whenever the default constructor is used.
//! Sets the m_graph pointer and caches pointers to other GraphElements.
virtual void PostLoadSetup(GraphPtr graph, SlotDefinitionPtr slotDefinition);
//! Return the SlotDefinition that defines this Slot
SlotDefinitionPtr GetDefinition() const;
//! Convenience functions that wrap SlotDefinition accessors
bool Is(SlotDirection slotDirection, SlotType slotType) const;
SlotDirection GetSlotDirection() const;
SlotType GetSlotType() const;
bool SupportsValue() const;
bool SupportsDataType() const;
bool SupportsConnections() const;
bool SupportsExtendability() const;
const SlotName& GetName() const; //!< Valid for all slot configurations
const AZStd::string& GetDisplayName() const; //!< Valid for all slot configurations
const AZStd::string& GetDescription() const; //!< Valid for all slot configurations
DataTypePtr GetDataType() const; //!< Valid for Data and Property slots. Otherwise returns null.
AZStd::any GetDefaultValue() const; //!< Valid for Input Data and Property slots. Otherwise returns an empty AZStd::any.
//! Valid for Data and Property slots. Otherwise returns an empty DataTypeList.
//! If valid, this will return the full list of all data types this slot could support.
const DataTypeList& GetSupportedDataTypes() const;
//! Valid for Data and Property slots. Otherwise returns an empty DataTypeList.
//! If valid, this will return the subset of data types that this slot can currently accept
//! based on the configuration of the node this slot belongs to and/or connections to other slots on the node.
const DataTypeList& GetPossibleDataTypes() const;
//! Convenience functions that wrap SlotDefinition accessors (specific to definitions that support extendable slots)
const int GetMinimumSlots() const;
const int GetMaximumSlots() const;
SlotId GetSlotId() const;
SlotSubId GetSlotSubId() const;
//! Get the Node that contains this Slot.
//! This function cannot be called until this Slot is added to a Node and
//! that Node is added to the Graph.
NodePtr GetParentNode() const;
//! Return the slot's value, which will be used if there are no input connections.
//! Valid for Input Data and Property slots.
AZStd::any GetValue() const;
//! Return the slot's value, which will be used if there are no input connections. Returns 0 if the type doesn't match.
//! Type template type T must match the slot's data type.
//! Valid for Input Data and Property slots.
template<typename T>
const T& GetValue() const;
//! Sets the slot's value, which will be used if there are no input connections.
//! Type template type T must match the slot's data type.
//! Valid for Input Data and Property slots.
template<typename T>
void SetValue(const T& value);
//! Sets the slot's value, which will be used if there are no input connections.
//! AZStd::any type must match the slot's data type.
//! Valid for Input Data and Property slots.
void SetValue(const AZStd::any& value);
// CJS TODO: More functions to add a bit later...
// CJS TODO: Also cache connection information here so Slot doesn't have to search the Graph for connections
//! Whether it's connected to other Slots in the Graph
//! (Property slots will never have connections)
// bool IsConnected() const;
//! Returns the list of connections to this Slot.
//! (Property slots will never have connections)
ConnectionList GetConnections() const;
//! Returns the list of other Slots that this Slot is connected to
//! (Property slots will never have connections)
// AZStd::vector<SlotPtr> GetConnectedSlots();
//! Returns the list of all Nodes that this Slot is connected to
//! (Property slots will never have connections)
//AZStd::vector<NodePtr> GetConnectedNodes();
//! Returns the list of IDs for other Slots that this Slot is connected to
//! (Property slots will never have connections)
// AZStd::vector<Endpoint> GetConnectedEndpoints();
//! Returns the list of IDs for all Nodes that this Slot is connected to
//! (Property slots will never have connections)
// AZStd::vector<NodeId> GetConnectedNodeIds();
protected:
#if defined(AZ_ENABLE_TRACING)
void AssertWithTypeInfo(bool expression, DataTypePtr dataTypeUsed, const char* message) const;
void AssertTypeMatch(DataTypePtr dataTypeUsed, const char* message) const;
#endif
private:
mutable AZStd::weak_ptr<Node> m_parentNode; //!< This is a mutable because it is just-in-time initialized in a const accessor function. This is okay because it's just a cache.
SlotDefinitionPtr m_slotDefinition; //!< Pointer to the SlotDefinition in the parent Node, that defines this slot.
AZStd::any m_value; //!< This is the value that gets used for a Property slot or an Input Data slot that doesn't have any connection.
WeakConnectionList m_connections; //!< List of connections to this Slot. Not reflected/serialized because this is just a cache of information owned by the Graph.
SlotSubId m_subId = 0; //!< SubId to uniquely identify extendable slots of the same name (regular slots will always have a SubId of 0)
};
template<typename T>
const T& Slot::GetValue() const
{
const T* pValue = AZStd::any_cast<T>(&m_value);
#if defined(AZ_ENABLE_TRACING)
DataTypePtr dataTypeUsed = GetGraphContext()->GetDataType<T>();
AssertWithTypeInfo(SupportsValue(), dataTypeUsed, "This slot type does not support Value");
AssertTypeMatch(dataTypeUsed, "Slot::GetValue used with the wrong type");
AssertWithTypeInfo(nullptr != pValue, dataTypeUsed, "m_value does not hold data of the appropriate type");
#endif
return *pValue;
}
template<typename T>
void Slot::SetValue(const T& value)
{
#if defined(AZ_ENABLE_TRACING)
DataTypePtr dataTypeUsed = GetGraphContext()->GetDataType<T>();
AssertWithTypeInfo(SupportsValue(), dataTypeUsed, "This slot type does not support Value");
AssertTypeMatch(dataTypeUsed, "Slot::SetValue used with the wrong type");
#endif
m_value = value;
}
} // namespace GraphModel
namespace AZStd
{
// This must be defined so that our custom data type SlotId can be used as a key
// in AZStd::unordered_map
template <>
struct hash<GraphModel::SlotId>
{
typedef GraphModel::SlotId argument_type;
typedef size_t result_type;
AZ_FORCE_INLINE size_t operator()(const argument_type& id) const
{
return id.GetHash();
}
};
}
@@ -0,0 +1,56 @@
/*
* 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/Memory/SystemAllocator.h>
#include <AzCore/Module/Module.h>
#if defined(GRAPHMODEL_EDITOR)
#include <GraphModelSystemComponent.h>
#endif
namespace GraphModel
{
class GraphModelModule
: public AZ::Module
{
public:
AZ_RTTI(GraphModelModule, "{217B9E5D-C0FC-4D9D-AD75-AA3B23566A96}", AZ::Module);
AZ_CLASS_ALLOCATOR(GraphModelModule, AZ::SystemAllocator, 0);
GraphModelModule()
: AZ::Module()
{
m_descriptors.insert(m_descriptors.end(), {
#if defined(GRAPHMODEL_EDITOR)
GraphModelSystemComponent::CreateDescriptor(),
#endif
});
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList{
#if defined(GRAPHMODEL_EDITOR)
azrtti_typeid<GraphModelSystemComponent>(),
#endif
};
}
};
}
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Gem_GraphModel, GraphModel::GraphModelModule)
@@ -0,0 +1,143 @@
/*
* 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 <GraphModelSystemComponent.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
// Graph Model
#include <GraphModel/GraphModelBus.h>
#include <GraphModel/Model/Connection.h>
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/DataType.h>
#include <GraphModel/Model/Node.h>
#include <GraphModel/Model/Slot.h>
#include <GraphModel/Model/Module/InputOutputNodes.h>
#include <GraphModel/Model/Module/ModuleNode.h>
#include <GraphModel/Integration/GraphCanvasMetadata.h>
#include <GraphModel/Integration/NodePalette/InputOutputNodePaletteItem.h>
#include <GraphModel/Integration/NodePalette/GraphCanvasNodePaletteItems.h>
#include <GraphModel/Integration/NodePalette/ModuleNodePaletteItem.h>
namespace GraphModel
{
void GraphModelSystemComponent::Reflect(AZ::ReflectContext* context)
{
// Reflect core graph classes
GraphModel::Graph::Reflect(context);
GraphModel::DataType::Reflect(context);
GraphModelIntegration::GraphCanvasMetadata::Reflect(context);
// Mime Events for Graph Canvas nodes
GraphModelIntegration::CreateGraphCanvasNodeMimeEvent::Reflect(context);
GraphModelIntegration::CreateNodeGroupNodeMimeEvent::Reflect(context);
GraphModelIntegration::CreateCommentNodeMimeEvent::Reflect(context);
// Reflect all the nodes needed to support ModuleNode
GraphModel::BaseInputOutputNode::Reflect(context);
GraphModel::GraphInputNode::Reflect(context);
GraphModel::GraphOutputNode::Reflect(context);
GraphModel::ModuleNode::Reflect(context);
GraphModelIntegration::CreateInputOutputNodeMimeEvent<GraphModel::GraphInputNode>::Reflect(context);
GraphModelIntegration::CreateInputOutputNodeMimeEvent<GraphModel::GraphOutputNode>::Reflect(context);
GraphModelIntegration::CreateModuleNodeMimeEvent::Reflect(context);
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<GraphModelSystemComponent, AZ::Component>()
->Version(0)
;
serialize->RegisterGenericType<GraphModel::NodePtrList>();
serialize->RegisterGenericType<GraphModel::SlotPtrList>();
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<GraphModelSystemComponent>("GraphModel", "A generic node graph data model")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<GraphModelIntegration::GraphManagerRequestBus>("GraphManagerRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, GraphCanvas::EditorGraphModuleName)
->Event("GetGraph", &GraphModelIntegration::GraphManagerRequests::GetGraph)
;
behaviorContext->EBus<GraphModelIntegration::GraphControllerRequestBus>("GraphControllerRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, GraphCanvas::EditorGraphModuleName)
->Event("AddNode", &GraphModelIntegration::GraphControllerRequests::AddNode)
->Event("RemoveNode", &GraphModelIntegration::GraphControllerRequests::RemoveNode)
->Event("WrapNode", &GraphModelIntegration::GraphControllerRequests::WrapNode)
->Event("AddConnection", &GraphModelIntegration::GraphControllerRequests::AddConnection)
->Event("AddConnectionBySlotId", &GraphModelIntegration::GraphControllerRequests::AddConnectionBySlotId)
->Event("RemoveConnection", &GraphModelIntegration::GraphControllerRequests::RemoveConnection)
->Event("ExtendSlot", &GraphModelIntegration::GraphControllerRequests::ExtendSlot)
;
behaviorContext->Class<GraphModel::SlotId>("GraphModelSlotId")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, GraphCanvas::EditorGraphModuleName)
->Constructor<const GraphModel::SlotName&>()
->Constructor<const GraphModel::SlotName&, GraphModel::SlotSubId>()
->Property("name", BehaviorValueProperty(&GraphModel::SlotId::m_name))
->Property("subId", BehaviorValueProperty(&GraphModel::SlotId::m_subId))
;
}
}
void GraphModelSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("GraphModelService", 0xc798f75e));
}
void GraphModelSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("GraphModelService", 0xc798f75e));
}
void GraphModelSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
}
void GraphModelSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
}
void GraphModelSystemComponent::Init()
{
}
void GraphModelSystemComponent::Activate()
{
m_graphControllerManager.Activate();
}
void GraphModelSystemComponent::Deactivate()
{
m_graphControllerManager.Deactivate();
}
}
@@ -0,0 +1,46 @@
/*
* 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>
// Graph Model
#include <GraphModel/Integration/GraphControllerManager.h>
namespace GraphModel
{
class GraphModelSystemComponent
: public AZ::Component
{
public:
AZ_COMPONENT(GraphModelSystemComponent, "{58CE2D43-2DDC-4CEB-BB9F-61B77C50C35D}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
private:
GraphModelIntegration::GraphControllerManager m_graphControllerManager;
};
}
@@ -0,0 +1,49 @@
/*
* 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 <GraphModel/Integration/BooleanDataInterface.h>
#include <GraphModel/Integration/IntegrationBus.h>
namespace GraphModelIntegration
{
BooleanDataInterface::BooleanDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
bool BooleanDataInterface::GetBool() const
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<bool>();
}
else
{
return false;
}
}
void BooleanDataInterface::SetBool(bool enabled)
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
if (enabled != slot->GetValue<bool>())
{
slot->SetValue(enabled);
GraphCanvas::GraphId graphCanvasSceneId;
IntegrationBus::BroadcastResult(graphCanvasSceneId, &IntegrationBusInterface::GetActiveGraphCanvasSceneId);
IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, graphCanvasSceneId);
}
}
}
}
@@ -0,0 +1,105 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
// GraphCanvas
#include <GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasEditorDockWidget.h>
// GraphModel
#include <GraphModel/Integration/EditorMainWindow.h>
#include <GraphModel/Model/Graph.h>
namespace GraphModelIntegration
{
EditorMainWindow::EditorMainWindow(GraphCanvas::AssetEditorWindowConfig* config, QWidget* parent)
: GraphCanvas::AssetEditorMainWindow(config, parent)
{
}
EditorMainWindow::~EditorMainWindow()
{
GraphControllerNotificationBus::MultiHandler::BusDisconnect();
}
GraphModel::GraphPtr EditorMainWindow::GetGraphById(GraphCanvas::GraphId graphId) const
{
auto it = m_graphs.find(graphId);
if (it != m_graphs.end())
{
return it->second;
}
return nullptr;
}
GraphCanvas::GraphId EditorMainWindow::GetGraphId(GraphModel::GraphPtr graph) const
{
auto it = AZStd::find_if(m_graphs.begin(), m_graphs.end(),
[graph](decltype(m_graphs)::const_reference pair)
{
return graph == pair.second;
});
if (it != m_graphs.end())
{
return it->first;
}
return GraphCanvas::GraphId();
}
void EditorMainWindow::OnEditorOpened(GraphCanvas::EditorDockWidget* dockWidget)
{
GraphCanvas::AssetEditorMainWindow::OnEditorOpened(dockWidget);
GraphCanvas::GraphId graphId = dockWidget->GetGraphId();
// Create the new graph.
GraphModel::GraphPtr graph = AZStd::make_shared<GraphModel::Graph>(GetGraphContext());
m_graphs[graphId] = graph;
// Create the controller for the new graph.
GraphModelIntegration::GraphManagerRequestBus::Broadcast(&GraphModelIntegration::GraphManagerRequests::CreateGraphController, graphId, graph);
// Listen for GraphController notifications on the new graph.
GraphModelIntegration::GraphControllerNotificationBus::MultiHandler::BusConnect(graphId);
}
void EditorMainWindow::OnEditorClosing(GraphCanvas::EditorDockWidget* dockWidget)
{
GraphCanvas::AssetEditorMainWindow::OnEditorClosing(dockWidget);
GraphCanvas::GraphId graphId = dockWidget->GetGraphId();
// Stop listening for GraphController notifications for this graph.
GraphModelIntegration::GraphControllerNotificationBus::MultiHandler::BusDisconnect(graphId);
// Remove the controller for this graph.
GraphModelIntegration::GraphManagerRequestBus::Broadcast(&GraphModelIntegration::GraphManagerRequests::DeleteGraphController, graphId);
// Delete the graph that was created.
m_graphs.erase(graphId);
}
void EditorMainWindow::OnWrapperNodeActionWidgetClicked(const AZ::EntityId& wrapperNode, const QRect& actionWidgetBoundingRect, const QPointF& scenePoint, const QPoint& screenPoint)
{
// Find the GraphModel::NodePtr whose action widget was clicked.
GraphCanvas::GraphId graphId = GetActiveGraphCanvasGraphId();
GraphModel::NodePtr node;
GraphControllerRequestBus::EventResult(node, graphId, &GraphControllerRequests::GetNodeById, wrapperNode);
AZ_Assert(node, "Unable to find NodePtr for the given NodeId");
// Invoke the handler so that the client can handle this event if necessary.
HandleWrapperNodeActionWidgetClicked(node, actionWidgetBoundingRect, scenePoint, screenPoint);
}
}
@@ -0,0 +1,66 @@
/*
* 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 <GraphModel/Integration/FloatDataInterface.h>
#include <GraphModel/Integration/IntegrationBus.h>
namespace GraphModelIntegration
{
FloatDataInterface::FloatDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
double FloatDataInterface::GetNumber() const
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<float>();
}
else
{
return 0.0;
}
}
void FloatDataInterface::SetNumber(double value)
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
if (static_cast<float>(value) != slot->GetValue<float>())
{
slot->SetValue(static_cast<float>(value));
GraphCanvas::GraphId graphCanvasSceneId;
IntegrationBus::BroadcastResult(graphCanvasSceneId, &IntegrationBusInterface::GetActiveGraphCanvasSceneId);
IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, graphCanvasSceneId);
}
}
}
int FloatDataInterface::GetDecimalPlaces() const
{
return 7;
}
int FloatDataInterface::GetDisplayDecimalPlaces() const
{
return 4;
}
double FloatDataInterface::GetMin() const
{
return std::numeric_limits<float>::lowest();
}
double FloatDataInterface::GetMax() const
{
return std::numeric_limits<float>::max();
}
}
@@ -0,0 +1,37 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/Serialization/SerializeContext.h>
// Graph Canvas
#include <GraphCanvas/Types/EntitySaveData.h>
// Graph Model
#include <GraphModel/Integration/GraphCanvasMetadata.h>
namespace GraphModelIntegration
{
void GraphCanvasMetadata::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<GraphCanvasMetadata>()
->Version(0)
->Field("m_sceneMetadata", &GraphCanvasMetadata::m_sceneMetadata)
->Field("m_nodeMetadata", &GraphCanvasMetadata::m_nodeMetadata)
->Field("m_otherMetadata", &GraphCanvasMetadata::m_otherMetadata)
;
}
}
} // namespace ShaderCanvas
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
// Graph Canvas
#include <GraphCanvas/GraphCanvasBus.h>
// Graph Model
#include <GraphModel/Integration/GraphControllerManager.h>
namespace GraphModelIntegration
{
AZ::Entity* GraphControllerManager::CreateScene(GraphModel::GraphPtr graph, const GraphCanvas::EditorId editorId)
{
AZ::Entity* scene = nullptr;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(scene, &GraphCanvas::GraphCanvasRequests::CreateSceneAndActivate);
// Set the EditorId for the new scene
const AZ::EntityId& sceneId = scene->GetId();
GraphCanvas::SceneRequestBus::Event(sceneId, &GraphCanvas::SceneRequests::SetEditorId, editorId);
// Create a graph controller for the new scene
CreateGraphController(sceneId, graph);
return scene;
}
void GraphControllerManager::RemoveScene(const GraphCanvas::GraphId& sceneId)
{
DeleteGraphController(sceneId);
}
void GraphControllerManager::CreateGraphController(const GraphCanvas::GraphId& sceneId, GraphModel::GraphPtr graph)
{
m_graphControllers[sceneId] = AZStd::make_shared<GraphController>(graph, sceneId);
}
void GraphControllerManager::DeleteGraphController(const GraphCanvas::GraphId& sceneId)
{
m_graphControllers.erase(sceneId);
}
GraphModel::GraphPtr GraphControllerManager::GetGraph(const GraphCanvas::GraphId& sceneId)
{
auto it = m_graphControllers.find(sceneId);
if (it != m_graphControllers.end())
{
return it->second->GetGraph();
}
return nullptr;
}
const GraphModelSerialization& GraphControllerManager::GetSerializedMappings()
{
return m_serialization;
}
void GraphControllerManager::SetSerializedMappings(const GraphModelSerialization& serialization)
{
m_serialization = serialization;
}
void GraphControllerManager::Activate()
{
GraphManagerRequestBus::Handler::BusConnect();
}
void GraphControllerManager::Deactivate()
{
GraphManagerRequestBus::Handler::BusDisconnect();
}
} // namespace GraphModelIntegration
@@ -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.
*
*/
#include <GraphModel/Integration/IntegerDataInterface.h>
#include <GraphModel/Integration/IntegrationBus.h>
namespace GraphModelIntegration
{
IntegerDataInterface::IntegerDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
double IntegerDataInterface::GetNumber() const
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<int>();
}
else
{
return 0.0;
}
}
void IntegerDataInterface::SetNumber(double value)
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
if (static_cast<int>(value) != slot->GetValue<int>())
{
slot->SetValue(static_cast<int>(value));
GraphCanvas::GraphId graphCanvasSceneId;
IntegrationBus::BroadcastResult(graphCanvasSceneId, &IntegrationBusInterface::GetActiveGraphCanvasSceneId);
IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, graphCanvasSceneId);
}
}
}
int IntegerDataInterface::GetDecimalPlaces() const
{
return 0;
}
int IntegerDataInterface::GetDisplayDecimalPlaces() const
{
return 0;
}
double IntegerDataInterface::GetMin() const
{
return std::numeric_limits<int>::min();
}
double IntegerDataInterface::GetMax() const
{
return std::numeric_limits<int>::max();
}
}
@@ -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.
*
*/
#include <GraphModel/Integration/NodePalette/GraphCanvasNodePaletteItems.h>
namespace GraphModelIntegration
{
/// Add common utilities to a specific Node Palette tree.
void AddCommonNodePaletteUtilities(GraphCanvas::GraphCanvasTreeItem* rootItem, const GraphCanvas::EditorId& editorId)
{
GraphCanvas::IconDecoratedNodePaletteTreeItem* utilitiesCategory = rootItem->CreateChildNode<GraphCanvas::IconDecoratedNodePaletteTreeItem>("Utilities", editorId);
utilitiesCategory->SetTitlePalette("UtilityNodeTitlePalette");
utilitiesCategory->CreateChildNode<CommentNodePaletteTreeItem>("Comment", editorId);
utilitiesCategory->CreateChildNode<NodeGroupNodePaletteTreeItem>("Node Group", editorId);
}
}
@@ -0,0 +1,35 @@
/*
* 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.
*
*/
// Graph Model
#include <GraphModel/Integration/ReadOnlyDataInterface.h>
#include <GraphModel/Integration/IntegrationBus.h>
namespace GraphModelIntegration
{
ReadOnlyDataInterface::ReadOnlyDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
AZStd::string ReadOnlyDataInterface::GetString() const
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<AZStd::string>();
}
else
{
return "";
}
}
}
@@ -0,0 +1,55 @@
/*
* 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.
*
*/
// AZ
#include <AzFramework/StringFunc/StringFunc.h>
// Graph Model
#include <GraphModel/Integration/StringDataInterface.h>
#include <GraphModel/Integration/IntegrationBus.h>
namespace GraphModelIntegration
{
StringDataInterface::StringDataInterface(GraphModel::SlotPtr slot)
: m_slot(slot)
{
}
AZStd::string StringDataInterface::GetString() const
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
return slot->GetValue<AZStd::string>();
}
else
{
return "";
}
}
void StringDataInterface::SetString(const AZStd::string& value)
{
if (GraphModel::SlotPtr slot = m_slot.lock())
{
AZStd::string trimValue = value;
AzFramework::StringFunc::TrimWhiteSpace(trimValue, true, true);
if (trimValue != slot->GetValue<AZStd::string>())
{
slot->SetValue(trimValue);
GraphCanvas::GraphId graphCanvasSceneId;
IntegrationBus::BroadcastResult(graphCanvasSceneId, &IntegrationBusInterface::GetActiveGraphCanvasSceneId);
IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, graphCanvasSceneId);
}
}
}
}
@@ -0,0 +1,64 @@
/*
* 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.
*
*/
// Qt
#include <QPainter>
#include <QWidget>
// GraphModel
#include <GraphModel/Integration/ThumbnailImageItem.h>
namespace GraphModelIntegration
{
static const QSize IMAGE_MARGIN = QSize(10, 10);
ThumbnailImageItem::ThumbnailImageItem(const QPixmap& image, QGraphicsItem* parent)
: ThumbnailItem(parent)
, m_pixmap(image)
{
}
void ThumbnailImageItem::UpdateImage(const QPixmap& image)
{
m_pixmap = image;
// Schedule a new paint request since we changed the pixmap
update();
}
QSizeF ThumbnailImageItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
switch (which)
{
case Qt::MinimumSize:
case Qt::PreferredSize:
return m_pixmap.size() + IMAGE_MARGIN;
case Qt::MaximumSize:
return QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
default:
break;
}
return constraint;
}
void ThumbnailImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
(void)option;
(void)widget;
// Draw the pixmap centered in our given frame
QRectF frame(QPointF(0, 0), geometry().size());
QPointF topLeft = frame.center() - (QPointF(m_pixmap.width(), m_pixmap.height()) / 2);
painter->drawPixmap(topLeft, m_pixmap);
}
}
@@ -0,0 +1,35 @@
/*
* 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 <GraphModel/Integration/ThumbnailItem.h>
namespace GraphModelIntegration
{
ThumbnailItem::ThumbnailItem(QGraphicsItem* parent)
: QGraphicsLayoutItem()
, QGraphicsItem(parent)
{
setGraphicsItem(this);
}
void ThumbnailItem::setGeometry(const QRectF &geom)
{
prepareGeometryChange();
QGraphicsLayoutItem::setGeometry(geom);
setPos(geom.topLeft());
}
QRectF ThumbnailItem::boundingRect() const
{
return QRectF(QPointF(0, 0), geometry().size());
}
}
@@ -0,0 +1,100 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/PlatformDef.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
// Graph Model
#include <GraphModel/Model/Connection.h>
#include <GraphModel/Model/Node.h>
#include <GraphModel/Model/Graph.h>
namespace GraphModel
{
Connection::Connection(GraphPtr graph, SlotPtr sourceSlot, SlotPtr targetSlot)
: GraphElement(graph)
, m_sourceSlot(sourceSlot)
, m_targetSlot(targetSlot)
{
AZ_Assert(sourceSlot->SupportsConnections(), "sourceSlot type does not support connections to other slots");
AZ_Assert(targetSlot->SupportsConnections(), "targetSlot type does not support connections to other slots");
const NodeId sourceNodeId = sourceSlot->GetParentNode()->GetId();
const NodeId targetNodeId = targetSlot->GetParentNode()->GetId();
m_sourceEndpoint = AZStd::make_pair(sourceNodeId, sourceSlot->GetSlotId());
m_targetEndpoint = AZStd::make_pair(targetNodeId, targetSlot->GetSlotId());
}
void Connection::PostLoadSetup(GraphPtr graph)
{
m_graph = graph;
m_sourceSlot = azrtti_cast<Slot*>(graph->FindSlot(m_sourceEndpoint));
m_targetSlot = azrtti_cast<Slot*>(graph->FindSlot(m_targetEndpoint));
}
void Connection::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<Connection>()
->Version(0)
->Field("m_sourceEndpoint", &Connection::m_sourceEndpoint)
->Field("m_targetEndpoint", &Connection::m_targetEndpoint)
;
}
}
NodePtr Connection::GetSourceNode() const
{
if (GetSourceSlot())
{
return GetSourceSlot()->GetParentNode();
}
return nullptr;
}
NodePtr Connection::GetTargetNode() const
{
if (GetTargetSlot())
{
return GetTargetSlot()->GetParentNode();
}
return nullptr;
}
SlotPtr Connection::GetSourceSlot() const
{
return m_sourceSlot.lock();
}
SlotPtr Connection::GetTargetSlot() const
{
return m_targetSlot.lock();
}
const Endpoint& Connection::GetSourceEndpoint() const
{
return m_sourceEndpoint;
}
const Endpoint& Connection::GetTargetEndpoint() const
{
return m_targetEndpoint;
}
} // namespace GraphModel
@@ -0,0 +1,100 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/Serialization/SerializeContext.h>
// GraphModel
#include <GraphModel/Model/DataType.h>
namespace GraphModel
{
void DataType::Reflect(AZ::ReflectContext* reflection)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
{
serializeContext->Class<DataType>()
->Version(0)
->Field("m_typeEnum", &DataType::m_typeEnum)
->Field("m_typeUuid", &DataType::m_typeUuid)
->Field("m_defaultValue", &DataType::m_defaultValue)
->Field("m_cppName", &DataType::m_cppName)
->Field("m_displayName", &DataType::m_displayName)
;
}
}
DataType::DataType()
: m_typeEnum(ENUM_INVALID)
, m_displayName("INVALID")
, m_cppName("INVALID")
{}
DataType::DataType(Enum typeEnum, AZ::Uuid typeUuid, AZStd::any defaultValue, AZStd::string_view typeDisplayName, AZStd::string_view cppTypeName)
: m_typeEnum(typeEnum)
, m_typeUuid(typeUuid)
, m_defaultValue(defaultValue)
, m_displayName(typeDisplayName)
, m_cppName(cppTypeName)
{}
DataType::DataType(const DataType& other)
: m_typeEnum(other.m_typeEnum)
, m_typeUuid(other.m_typeUuid)
, m_defaultValue(other.m_defaultValue)
, m_displayName(other.m_displayName)
, m_cppName(other.m_cppName)
{}
bool DataType::IsValid() const
{
return ENUM_INVALID != m_typeEnum && !m_typeUuid.IsNull();
}
AZ::Uuid DataType::GetTypeUuid() const
{
return m_typeUuid;
}
AZStd::string DataType::GetTypeUuidString() const
{
return GetTypeUuid().ToString<AZStd::string>();
}
bool DataType::operator==(const DataType& other) const
{
return m_typeEnum != ENUM_INVALID &&
other.m_typeEnum != ENUM_INVALID &&
m_typeEnum == other.m_typeEnum;
}
bool DataType::operator!=(const DataType& other) const
{
return !(*this == other);
}
AZStd::any DataType::GetDefaultValue() const
{
return m_defaultValue;
}
AZStd::string DataType::GetDisplayName() const
{
return m_displayName;
}
AZStd::string DataType::GetCppName() const
{
return m_cppName;
}
} // namespace GraphModel
+332
View File
@@ -0,0 +1,332 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
// Graph Model
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/IGraphContext.h>
#include <GraphModel/Model/Node.h>
#include <GraphModel/Model/Slot.h>
#include <GraphModel/Model/Connection.h>
namespace GraphModel
{
void Graph::Reflect(AZ::ReflectContext* context)
{
Node::Reflect(context);
SlotIdData::Reflect(context);
Slot::Reflect(context);
Connection::Reflect(context);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<Graph>()
->Version(1)
->Field("m_nodes", &Graph::m_nodes)
->Field("m_connections", &Graph::m_connections)
->Field("m_uiMetadata", &Graph::m_uiMetadata)
->Field("m_nodeWrappings", &Graph::m_nodeWrappings)
;
}
}
Graph::Graph(IGraphContextPtr graphContext)
: m_graphContext(graphContext)
{
}
void Graph::PostLoadSetup(IGraphContextPtr graphContext)
{
AZ_Assert(m_nextNodeId == 1, "This graph has been set up before");
m_graphContext = graphContext;
for (auto& pair : m_nodes)
{
const NodeId nodeId = pair.first;
pair.second->PostLoadSetup(shared_from_this(), nodeId);
// Find the highest NodeId in the graph so we can figure out
// what the next one should be
m_nextNodeId = AZ::GetMax(m_nextNodeId, nodeId + 1);
}
for (auto it = m_connections.begin(); it != m_connections.end();)
{
ConnectionPtr connection = *it;
connection->PostLoadSetup(shared_from_this());
if (!connection->GetSourceSlot() || !connection->GetTargetSlot())
{
// Discard any cached connections if the source or target slot no longer exists
m_connections.erase(it);
}
else
{
// Valid slots, so update each slot's local cache of its connections
connection->GetSourceSlot()->m_connections.push_back(connection);
connection->GetTargetSlot()->m_connections.push_back(connection);
++it;
}
}
}
NodeId Graph::PostLoadSetup(NodePtr node)
{
node->m_graph = shared_from_this();
NodeId nodeId = AddNode(node);
node->PostLoadSetup();
return nodeId;
}
IGraphContextPtr Graph::GetContext() const
{
AZ_Assert(m_graphContext, "Graph::m_graphContext is not set");
return m_graphContext;
}
const char* Graph::GetSystemName() const
{
return GetContext()->GetSystemName();
}
ConnectionPtr Graph::FindConnection(ConstSlotPtr sourceSlot, ConstSlotPtr targetSlot)
{
if (!sourceSlot || !targetSlot)
{
return nullptr;
}
for (ConnectionPtr searchConnection : m_connections)
{
if (searchConnection->GetSourceSlot() == sourceSlot && searchConnection->GetTargetSlot() == targetSlot)
{
return searchConnection;
}
}
return nullptr;
}
bool Graph::Contains(SlotPtr slot) const
{
if (!slot)
{
return false;
}
for (auto pair : m_nodes)
{
if (pair.second->Contains(slot))
{
return true;
}
}
return false;
}
NodePtr Graph::GetNode(NodeId nodeId)
{
auto nodeIter = m_nodes.find(nodeId);
if (nodeIter != m_nodes.end())
{
return nodeIter->second;
}
return nullptr;
}
const Graph::NodeMap& Graph::GetNodes()
{
return m_nodes;
}
Graph::ConstNodeMap Graph::GetNodes() const
{
Graph::ConstNodeMap constNodes;
AZStd::for_each(m_nodes.begin(), m_nodes.end(), [&](auto pair) { constNodes.insert(pair); });
return constNodes;
}
NodeId Graph::AddNode(NodePtr node)
{
AZ_Assert(Node::INVALID_NODE_ID == node->GetId(), "It appears this node already exists in a Graph");
AZ_Assert(this == node->GetGraph().get(), "The Node was not created for this Graph");
node->m_id = m_nextNodeId++;
m_nodes.insert(AZStd::make_pair(node->m_id, node));
return node->m_id;
}
bool Graph::RemoveNode(ConstNodePtr node)
{
// First delete any connections that are attached to the node.
// It looks like this code is never run because the connections are always
// deleted individually first. But still have this hear for completeness.
for (int i = static_cast<int>(m_connections.size()) - 1; i >= 0; --i)
{
ConnectionPtr connection = m_connections[i];
if (connection->GetSourceNode() == node || connection->GetTargetNode() == node)
{
RemoveConnection(&m_connections[i]);
}
}
// Also, remove any node wrapping stored for this node
UnwrapNode(node);
return m_nodes.erase(node->GetId()) != 0;
}
void Graph::WrapNode(NodePtr wrapperNode, NodePtr node, AZ::u32 layoutOrder)
{
AZ_Assert(m_nodes.find(wrapperNode->GetId()) != m_nodes.end(), "The wrapperNode must be in the graph before having a node wrapped on it");
AZ_Assert(m_nodes.find(node->GetId()) != m_nodes.end(), "The node must be in the graph before being wrapped");
AZ_Assert(wrapperNode->GetNodeType() == NodeType::WrapperNode, "The node containing the wrapped node must be of node type WrapperNode");
AZ_Assert(node->GetNodeType() != NodeType::WrapperNode, "Nested WrapperNodes are not allowed");
AZ_Assert(m_nodeWrappings.find(node->GetId()) == m_nodeWrappings.end(), "The specified node is already wrapped on another WrapperNode");
m_nodeWrappings[node->GetId()] = AZStd::make_pair(wrapperNode->GetId(), layoutOrder);
}
void Graph::UnwrapNode(ConstNodePtr node)
{
auto it = m_nodeWrappings.find(node->GetId());
if (it != m_nodeWrappings.end())
{
m_nodeWrappings.erase(it);
}
}
const Graph::NodeWrappingMap& Graph::GetNodeWrappings()
{
return m_nodeWrappings;
}
const Graph::ConnectionList& Graph::GetConnections()
{
return m_connections;
}
ConnectionPtr Graph::AddConnection(SlotPtr sourceSlot, SlotPtr targetSlot)
{
if (ConnectionPtr existingConnection = FindConnection(sourceSlot, targetSlot))
{
return existingConnection;
}
else if (Contains(sourceSlot) && Contains(targetSlot))
{
ConnectionPtr newConnection = AZStd::make_shared<Connection>(shared_from_this(), sourceSlot, targetSlot);
m_connections.push_back(newConnection);
sourceSlot->m_connections.push_back(newConnection);
targetSlot->m_connections.push_back(newConnection);
return newConnection;
}
else
{
AZ_Error(GetSystemName(), false, "Tried to add a connection between slots that don't exist in this Graph.");
return nullptr;
}
}
bool Graph::RemoveConnection(ConnectionList::iterator iter)
{
if (iter != m_connections.end())
{
ConnectionPtr connection = *iter;
// Remove the cached connection pointers from the slots
auto shouldRemove = [&connection](auto entry) {
ConstConnectionPtr entryPtr = entry.lock();
return !entryPtr || entryPtr == connection;
};
(*iter)->GetSourceSlot()->m_connections.remove_if(shouldRemove);
(*iter)->GetTargetSlot()->m_connections.remove_if(shouldRemove);
// Remove the actual connection
m_connections.erase(iter);
#if defined(AZ_ENABLE_TRACING)
auto iter = AZStd::find(m_connections.begin(), m_connections.end(), connection);
AZ_Assert(iter == m_connections.end(), "Graph is broken. The same connection object was found multiple times.");
#endif
return true;
}
else
{
return false;
}
}
bool Graph::RemoveConnection(ConstConnectionPtr connection)
{
auto iter = AZStd::find(m_connections.begin(), m_connections.end(), connection);
return RemoveConnection(iter);
}
AZStd::shared_ptr<Slot> Graph::FindSlot(const Endpoint& endpoint)
{
AZStd::shared_ptr<Slot> slot;
auto nodeIter = m_nodes.find(endpoint.first);
if (nodeIter != m_nodes.end())
{
slot = nodeIter->second->GetSlot(endpoint.second);
}
return slot;
}
void Graph::SetUiMetadata(const AZStd::any& uiMetadata)
{
m_uiMetadata = uiMetadata;
}
const AZStd::any& Graph::GetUiMetadata() const
{
return m_uiMetadata;
}
AZStd::any& Graph::GetUiMetadata()
{
return m_uiMetadata;
}
} // namespace GraphModel
@@ -0,0 +1,35 @@
/*
* 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.
*
*/
// Graph Model
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/GraphElement.h>
namespace GraphModel
{
GraphElement::GraphElement(GraphPtr graph) : m_graph(graph)
{
}
GraphPtr GraphElement::GetGraph() const
{
return m_graph.lock();
}
IGraphContextPtr GraphElement::GetGraphContext() const
{
GraphPtr graph = m_graph.lock();
return graph ? graph->GetContext() : nullptr;
}
} // namespace GraphModel
@@ -0,0 +1,173 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
// Graph Model
#include <GraphModel/Model/Module/InputOutputNodes.h>
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/Slot.h>
#include <GraphModel/Model/DataType.h>
namespace GraphModel
{
//////////////////////////////////////////////////////////////////////////////
// BaseInputOutputNode
void BaseInputOutputNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<BaseInputOutputNode, Node>()
->Version(0)
->Field("m_dataType", &BaseInputOutputNode::m_dataType)
;
}
}
BaseInputOutputNode::BaseInputOutputNode(GraphPtr graph, DataTypePtr dataType)
: Node(graph)
{
// Copy because m_dataType has to be non-const for use with SerializeContext, and dataType is const
m_dataType = AZStd::make_shared<DataType>(*dataType);
}
const char* BaseInputOutputNode::GetTitle() const
{
return m_title.c_str();
}
GraphModel::DataTypePtr BaseInputOutputNode::GetNodeDataType() const
{
return m_dataType;
}
AZStd::string BaseInputOutputNode::GetName() const
{
return GetSlot("name")->GetValue<AZStd::string>();
}
AZStd::string BaseInputOutputNode::GetDisplayName() const
{
return GetSlot("displayName")->GetValue<AZStd::string>();
}
AZStd::string BaseInputOutputNode::GetDescription() const
{
return GetSlot("description")->GetValue<AZStd::string>();
}
void BaseInputOutputNode::RegisterCommonSlots(AZStd::string_view directionName)
{
GraphModel::DataTypePtr stringDataType = GetGraphContext()->GetDataType<AZStd::string>();
RegisterSlot(GraphModel::SlotDefinition::CreateProperty("name", "Name", stringDataType, stringDataType->GetDefaultValue(),
AZStd::string::format("The official name for this %s", directionName.data())));
RegisterSlot(GraphModel::SlotDefinition::CreateProperty("displayName", "Display Name", stringDataType, stringDataType->GetDefaultValue(),
AZStd::string::format("The name for this %s, displayed to the user. Will use the above Name if left blank.", directionName.data())));
RegisterSlot(GraphModel::SlotDefinition::CreateProperty("description", "Description", stringDataType, stringDataType->GetDefaultValue(),
AZStd::string::format("A description of this %s, used for tooltips", directionName.data())));
}
//////////////////////////////////////////////////////////////////////////////
// GraphInputNode
void GraphInputNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<GraphInputNode, BaseInputOutputNode>()
->Version(0)
;
}
}
GraphInputNode::GraphInputNode(GraphModel::GraphPtr graph, DataTypePtr dataType)
: BaseInputOutputNode(graph, dataType)
{
m_title = m_dataType->GetDisplayName() + " Input";
RegisterSlots();
CreateSlotData();
}
void GraphInputNode::PostLoadSetup(GraphPtr graph, NodeId id)
{
m_title = m_dataType->GetDisplayName() + " Input";
Node::PostLoadSetup(graph, id);
}
AZStd::any GraphInputNode::GetDefaultValue() const
{
return GetSlot("defaultValue")->GetValue();
}
void GraphInputNode::RegisterSlots()
{
// Register just a single output slot for the data that is input through this node
RegisterSlot(GraphModel::SlotDefinition::CreateOutputData("value", "Value", m_dataType, "An external value provided as input to this graph"));
// Register meta-data properties
RegisterCommonSlots("input");
RegisterSlot(GraphModel::SlotDefinition::CreateProperty("defaultValue", "Default Value", m_dataType, m_dataType->GetDefaultValue(),
"The default value for this input when no data is provided externally"));
}
//////////////////////////////////////////////////////////////////////////////
// GraphOutputNode
void GraphOutputNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<GraphOutputNode, BaseInputOutputNode>()
->Version(0)
;
}
}
GraphOutputNode::GraphOutputNode(GraphModel::GraphPtr graph, DataTypePtr dataType)
: BaseInputOutputNode(graph, dataType)
{
m_title = m_dataType->GetDisplayName() + " Output";
RegisterSlots();
CreateSlotData();
}
void GraphOutputNode::PostLoadSetup(GraphPtr graph, NodeId id)
{
m_title = m_dataType->GetDisplayName() + " Output";
Node::PostLoadSetup(graph, id);
}
void GraphOutputNode::RegisterSlots()
{
// Register just a single input slot for the data that is output through this node
RegisterSlot(GraphModel::SlotDefinition::CreateInputData("value", "Value", m_dataType, m_dataType->GetDefaultValue(), "A value output by this graph for external use"));
// Register meta-data properties
RegisterCommonSlots("output");
}
}
@@ -0,0 +1,155 @@
/*
* 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.
*
*/
// AZ
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Component/ComponentApplicationBus.h>
// Graph Model
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/IGraphContext.h>
#include <GraphModel/Model/Module/ModuleGraphManager.h>
namespace GraphModel
{
ModuleGraphManager::ModuleGraphManager(IGraphContextPtr graphContext, AZ::SerializeContext* serializeContext)
: m_graphContext(graphContext)
, m_moduleFileExtension(graphContext->GetModuleFileExtension())
, m_serializeContext(serializeContext)
{
if (m_serializeContext == nullptr)
{
// use the default app serialize context
AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
if (!m_serializeContext)
{
AZ_Error(graphContext->GetSystemName(), false, "ModuleGraphManager: No serialize context provided! We will not be able to load module files.");
}
}
AzToolsFramework::AssetSystemBus::Handler::BusConnect();
}
ModuleGraphManager::~ModuleGraphManager()
{
AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
}
void ModuleGraphManager::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID)
{
AZStd::string extension;
if (AzFramework::StringFunc::Path::GetExtension(relativePath.data(), extension) && extension == m_moduleFileExtension)
{
// Force the manager to reload the graph next time GetModuleGraph() is called.
m_graphs.erase(sourceUUID);
}
}
ConstGraphPtr ModuleGraphManager::LoadGraph(AZ::IO::FileIOStream& stream)
{
GraphPtr graph = AZStd::make_shared<Graph>();
bool loadSuccess = AZ::Utils::LoadObjectFromStreamInPlace(stream, *graph, m_serializeContext);
if (loadSuccess)
{
graph->PostLoadSetup(m_graphContext.lock());
return graph;
}
else
{
return nullptr;
}
}
AZ::Outcome<ConstGraphPtr, AZStd::string> ModuleGraphManager::LoadGraph(AZ::Uuid sourceFileId)
{
bool gotSourceInfo = false;
AZ::Data::AssetInfo assetInfo;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gotSourceInfo, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourceUUID, sourceFileId, assetInfo, watchFolder);
if (!gotSourceInfo)
{
return AZ::Failure<AZStd::string>("Could not get source file info for [" + sourceFileId.ToString<AZStd::string>() + "]");
}
AZStd::string extension;
if (!AzFramework::StringFunc::Path::GetExtension(assetInfo.m_relativePath.data(), extension) || extension != m_moduleFileExtension)
{
return AZ::Failure<AZStd::string>("Incorrect extension [" + assetInfo.m_relativePath + "]. Must be [" + m_moduleFileExtension + "]");
}
AZStd::string fullAssetPath;
AzFramework::StringFunc::Path::Join(watchFolder.data(), assetInfo.m_relativePath.data(), fullAssetPath);
AZ::IO::FileIOStream stream;
stream.Open(fullAssetPath.data(), AZ::IO::OpenMode::ModeRead);
if (!stream.IsOpen())
{
return AZ::Failure<AZStd::string>("Could not open [" + fullAssetPath + "]");
}
ConstGraphPtr graph = LoadGraph(stream);
if (!graph)
{
return AZ::Failure<AZStd::string>("Could not load [" + fullAssetPath + "]");
}
return AZ::Success(graph);
}
AZ::Outcome<ConstGraphPtr, AZStd::string> ModuleGraphManager::GetModuleGraph(AZ::Uuid sourceFileId)
{
auto iter = m_graphs.find(sourceFileId);
// If the soure file has never been loaded, go ahead and load it now
if (iter == m_graphs.end())
{
AZ::Outcome<ConstGraphPtr, AZStd::string> graphOutcome = LoadGraph(sourceFileId);
if (!graphOutcome.IsSuccess())
{
return graphOutcome;
}
m_graphs[sourceFileId] = graphOutcome.GetValue();
return graphOutcome;
}
else
{
// If the Graph has been loaded and is still in memory, we can just return it
ConstGraphPtr graph = iter->second.lock();
if (graph)
{
return AZ::Success(graph);
}
// The Graph has been released at some point and needs to be loaded again
else
{
AZ::Outcome<ConstGraphPtr, AZStd::string> graphOutcome = LoadGraph(sourceFileId);
if (!graphOutcome.IsSuccess())
{
m_graphs.erase(iter);
return graphOutcome;
}
m_graphs[sourceFileId] = graphOutcome.GetValue();
return graphOutcome;
}
}
}
} // namespace GraphModel
@@ -0,0 +1,109 @@
/*
* 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.
*
*/
// AZ
#include <AzFramework/StringFunc/StringFunc.h>
// Graph Model
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/Slot.h>
#include <GraphModel/Model/Module/ModuleNode.h>
#include <GraphModel/Model/Module/ModuleGraphManager.h>
#include <GraphModel/Model/Module/InputOutputNodes.h>
#include <GraphModel/Model/IGraphContext.h>
namespace GraphModel
{
void ModuleNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ModuleNode, Node>()
->Version(0)
->Field("m_moduleGraphFileId", &ModuleNode::m_moduleGraphFileId)
->Field("m_nodeTitle", &ModuleNode::m_nodeTitle)
;
}
}
ModuleNode::ModuleNode(GraphPtr ownerGraph, AZ::Uuid moduleGraphFileId, AZStd::string_view moduleGraphFileName)
: Node(ownerGraph)
, m_moduleGraphFileId(moduleGraphFileId)
{
// The module file name (without extension) is the node title
if (!AzFramework::StringFunc::Path::GetFileName(moduleGraphFileName.data(), m_nodeTitle))
{
AZ_Error(ownerGraph->GetSystemName(), false, "Could not get node name from file string [%s]", moduleGraphFileName.data());
}
LoadModuleGraph(ownerGraph->GetContext()->GetModuleGraphManager());
RegisterSlots();
CreateSlotData();
}
void ModuleNode::PostLoadSetup(GraphPtr ownerGraph, NodeId id)
{
LoadModuleGraph(ownerGraph->GetContext()->GetModuleGraphManager());
Node::PostLoadSetup(ownerGraph, id);
}
const char* ModuleNode::GetTitle() const
{
return m_nodeTitle.c_str();
}
void ModuleNode::LoadModuleGraph(ModuleGraphManagerPtr moduleGraphManager)
{
auto result = moduleGraphManager->GetModuleGraph(m_moduleGraphFileId);
if (result.IsSuccess())
{
m_moduleGraph = result.GetValue();
}
else
{
AZ_Warning(GetGraph()->GetSystemName(), false, "%s (Module Node [%s])", result.GetError().data(), m_nodeTitle.data());
}
}
void ModuleNode::RegisterSlots()
{
if (m_moduleGraph)
{
for (auto iter : m_moduleGraph->GetNodes())
{
ConstNodePtr node = iter.second;
if (AZStd::shared_ptr<const GraphInputNode> inputNode = azrtti_cast<const GraphInputNode*>(node))
{
RegisterSlot(GraphModel::SlotDefinition::CreateInputData(
inputNode->GetName(),
inputNode->GetDisplayName(),
inputNode->GetNodeDataType(),
inputNode->GetDefaultValue(),
inputNode->GetDescription()));
}
else if (AZStd::shared_ptr<const GraphOutputNode> outputNode = azrtti_cast<const GraphOutputNode*>(node))
{
RegisterSlot(GraphModel::SlotDefinition::CreateOutputData(
outputNode->GetName(),
outputNode->GetDisplayName(),
outputNode->GetNodeDataType(),
outputNode->GetDescription()));
}
}
}
}
}
+524
View File
@@ -0,0 +1,524 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
// Graph Model
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/Node.h>
namespace GraphModel
{
static constexpr int InvalidExtendableSlot = -1;
void Node::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<Node>()
->Version(0)
// m_id isn't reflected because this information is already stored in the Graph's node map
// m_outputDataSlots isn't reflected because its Slot::m_value field is unused
// m_inputEventSlots isn't reflected because its Slot::m_value field is unused
// m_outputEventSlots isn't reflected because its Slot::m_value field is unused
->Field("m_propertySlots", &Node::m_propertySlots)
->Field("m_inputDataSlots", &Node::m_inputDataSlots)
->Field("m_extendableSlots", &Node::m_extendableSlots)
;
}
}
Node::Node(GraphPtr graph)
: GraphElement(graph)
{
}
void Node::PostLoadSetup(GraphPtr graph, NodeId id)
{
AZ_Assert(nullptr == GetGraph(), "Node isn't freshly loaded.");
AZ_Assert(m_id == INVALID_NODE_ID, "Node isn't freshly loaded.");
m_graph = graph;
m_id = id;
PostLoadSetup();
}
void Node::PostLoadSetup()
{
RegisterSlots();
// Make sure the loaded Slot data aligns with the Node's input slot descriptions
SyncAndSetupSlots(m_propertySlots, m_propertySlotDefinitions);
SyncAndSetupSlots(m_inputDataSlots, m_inputDataSlotDefinitions);
SyncAndSetupExtendableSlots();
// These slots types are a bit different, because they don't actually have any data to be serialized, so instead of SyncAndSetupSlots we need to create them.
CreateSlotData(m_outputDataSlots, m_outputDataSlotDefinitions);
CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions);
CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions);
int numExtendableSlots = 0;
for (auto it = m_extendableSlots.begin(); it != m_extendableSlots.end(); it++)
{
numExtendableSlots += aznumeric_cast<int>(it->second.size());
}
AZ_Assert(m_allSlots.size() == m_propertySlots.size() + m_inputDataSlots.size() + m_outputDataSlots.size() + m_inputEventSlots.size() + m_outputEventSlots.size() + numExtendableSlots, "Slot counts don't match");
AZ_Assert(m_allSlotDefinitions.size() == m_propertySlotDefinitions.size() + m_inputDataSlotDefinitions.size() + m_outputDataSlotDefinitions.size() + m_inputEventSlotDefinitions.size() + m_outputEventSlotDefinitions.size() + m_extendableSlotDefinitions.size(), "SlotDefinition counts don't match");
}
void Node::CreateSlotData()
{
AZ_Assert(m_allSlots.empty(), "CreateSlotData() should only be called once after creating a new node.");
CreateSlotData(m_propertySlots, m_propertySlotDefinitions);
CreateSlotData(m_inputDataSlots, m_inputDataSlotDefinitions);
CreateSlotData(m_outputDataSlots, m_outputDataSlotDefinitions);
CreateSlotData(m_inputEventSlots, m_inputEventSlotDefinitions);
CreateSlotData(m_outputEventSlots, m_outputEventSlotDefinitions);
CreateExtendableSlotData();
}
void Node::CreateSlotData(SlotMap& slotMap, const SlotDefinitionList& slotDefinitionList)
{
AZ_Assert(slotMap.empty(), "This node isn't freshly initialized");
for (SlotDefinitionPtr slotDefinition : slotDefinitionList)
{
SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition);
slot->SetValue(slotDefinition->GetDefaultValue());
SlotId slotId(slotDefinition->GetName());
auto newEntry = AZStd::make_pair(slotId, slot);
slotMap.insert(newEntry);
m_allSlots.insert(newEntry);
}
}
void Node::CreateExtendableSlotData()
{
for (SlotDefinitionPtr slotDefinition : m_extendableSlotDefinitions)
{
// Skip creating slots for this definition if a set already exists in the map, since we
// use this method to populate slots that have been loaded on existing nodes where
// new slots have been added in addition to creating slots on nodes for the first time
const SlotName& slotName = slotDefinition->GetName();
if (m_extendableSlots.find(slotName) != m_extendableSlots.end())
{
continue;
}
ExtendableSlotSet extendableSet;
// When creating a new node, we need to populate enough extendable slots to satisfy
// the minimum requirement of the definition.
int minimumSlots = slotDefinition->GetMinimumSlots();
for (int i = 0; i < minimumSlots; ++i)
{
SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition, i);
slot->SetValue(slotDefinition->GetDefaultValue());
auto newEntry = AZStd::make_pair(slot->GetSlotId(), slot);
m_allSlots.insert(newEntry);
extendableSet.insert(slot);
}
m_extendableSlots.insert(AZStd::make_pair(slotName, extendableSet));
}
}
void Node::SyncAndSetupSlots(SlotMap& slotData, Node::SlotDefinitionList& slotDefinitions)
{
// Do PostLoadSetup to attach each Slot to its SlotDefinition
// Also remove any Slot that doesn't have a corresponding SlotDefinition
for (auto slotDataIter = slotData.begin(); slotDataIter != slotData.end(); /* increment in loop */)
{
const SlotId& slotId = slotDataIter->first;
const SlotName& slotName = slotId.m_name;
SlotPtr slot = slotDataIter->second;
auto slotDefinitionIter = AZStd::find_if(slotDefinitions.begin(), slotDefinitions.end(), [&slotName](SlotDefinitionPtr slotDefinition) { return slotDefinition->GetName() == slotName; });
if (slotDefinitionIter == slotDefinitions.end())
{
AZ_Warning(GetGraph()->GetSystemName(), false, "Found data for unrecognized slot [%s]. It will be ignored.", slotName.c_str());
slotDataIter = slotData.erase(slotDataIter);
}
else
{
// CJS TODO: Consider using AZ::Outcome for better error reporting, and if PostLoadSetup fails (could be due to type mismatch)
slot->PostLoadSetup(GetGraph(), *slotDefinitionIter);
++slotDataIter;
}
}
// Make sure all SlotDefinitions have slot data. This would normally happen when the code for a Node class has been changed to add a new slot.
for (SlotDefinitionPtr slotDefinition : slotDefinitions)
{
SlotId slotId(slotDefinition->GetName());
if (slotData.find(slotId) == slotData.end())
{
AZ_Warning(GetGraph()->GetSystemName(), false, "No data found for slot [%s]. It will be filled with default values.", slotDefinition->GetName().c_str());
SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition);
slotData[slotId] = slot;
}
}
m_allSlots.insert(slotData.begin(), slotData.end());
}
void Node::SyncAndSetupExtendableSlots()
{
// Do PostLoadSetup to attach each Slot to its SlotDefinition
// Also remove any Slot that doesn't have a corresponding SlotDefinition
for (auto slotDataIter = m_extendableSlots.begin(); slotDataIter != m_extendableSlots.end(); /* increment in loop */)
{
const SlotName& slotName = slotDataIter->first;
auto slotDefinitionIter = AZStd::find_if(m_extendableSlotDefinitions.begin(), m_extendableSlotDefinitions.end(), [&slotName](SlotDefinitionPtr slotDefinition) { return slotDefinition->GetName() == slotName; });
if (slotDefinitionIter == m_extendableSlotDefinitions.end())
{
AZ_Warning(GetGraph()->GetSystemName(), false, "Found data for unrecognized slot [%s]. It will be ignored.", slotName.c_str());
slotDataIter = m_extendableSlots.erase(slotDataIter);
}
else
{
for (SlotPtr slot : slotDataIter->second)
{
// CJS TODO: Consider using AZ::Outcome for better error reporting, and if PostLoadSetup fails (could be due to type mismatch)
slot->PostLoadSetup(GetGraph(), *slotDefinitionIter);
auto newEntry = AZStd::make_pair(slot->GetSlotId(), slot);
m_allSlots.insert(newEntry);
}
++slotDataIter;
}
}
// Make sure all SlotDefinitions have slot data. This would normally happen when the code for a Node class has been changed to add a new slot.
CreateExtendableSlotData();
}
NodeId Node::GetId() const
{
return m_id;
}
bool Node::Contains(ConstSlotPtr slot) const
{
if (!slot)
{
return false;
}
auto iter = m_allSlots.find(slot->GetSlotId());
if (iter != m_allSlots.end() && iter->second == slot)
{
return true;
}
return false;
}
const Node::SlotDefinitionList& Node::GetSlotDefinitions() const
{
return m_allSlotDefinitions;
}
const Node::SlotMap& Node::GetSlots()
{
return m_allSlots;
}
Node::ConstSlotMap Node::GetSlots() const
{
Node::ConstSlotMap constSlots;
AZStd::for_each(m_allSlots.begin(), m_allSlots.end(), [&](auto pair) { constSlots.insert(pair); });
return constSlots;
}
ConstSlotPtr Node::GetSlot(const SlotId& slotId) const
{
auto slot = m_allSlots.find(slotId);
if (slot != m_allSlots.end())
{
return slot->second;
}
return nullptr;
}
SlotPtr Node::GetSlot(const SlotId& slotId)
{
// Shared const/non-const overload implementation
return AZStd::const_pointer_cast<Slot>(static_cast<const Node*>(this)->GetSlot(slotId));
}
SlotPtr Node::GetSlot(const SlotName& name)
{
SlotId slotId(name);
return GetSlot(slotId);
}
ConstSlotPtr Node::GetSlot(const SlotName& name) const
{
SlotId slotId(name);
return GetSlot(slotId);
}
const Node::ExtendableSlotSet& Node::GetExtendableSlots(const SlotName& name)
{
auto it = m_extendableSlots.find(name);
if (it != m_extendableSlots.end())
{
return it->second;
}
else
{
static Node::ExtendableSlotSet defaultSet;
return defaultSet;
}
}
int Node::GetExtendableSlotCount(const SlotName& name)
{
auto it = m_extendableSlots.find(name);
if (it != m_extendableSlots.end())
{
return aznumeric_cast<int>(it->second.size());
}
return InvalidExtendableSlot;
}
DataTypePtr Node::GetDataType(ConstSlotPtr slot) const
{
if (slot)
{
// TODO: This method allows Nodes to introduce extra logic when slots
// ask what their data type should be depending on existing connections.
// This has been partially implemented, so for now just return the first
// possible data type.
auto possibleDataTypes = slot->GetPossibleDataTypes();
if (possibleDataTypes.size())
{
return possibleDataTypes[0];
}
}
return nullptr;
}
void Node::DeleteSlot(SlotPtr slot)
{
if (CanDeleteSlot(slot))
{
SlotId slotId = slot->GetSlotId();
// Remove this slot from our map tracking all slots on the node, as well as the extendable slots
auto allSlotsIt = m_allSlots.find(slotId);
if (allSlotsIt != m_allSlots.end())
{
m_allSlots.erase(allSlotsIt);
}
auto extendableSlotsIt = m_extendableSlots.find(slot->GetName());
if (extendableSlotsIt != m_extendableSlots.end())
{
auto slotIt = extendableSlotsIt->second.find(slot);
if (slotIt != extendableSlotsIt->second.end())
{
extendableSlotsIt->second.erase(slotIt);
}
}
}
}
bool Node::CanDeleteSlot(ConstSlotPtr slot) const
{
// Only extendable slots can be removed
if (slot->SupportsExtendability())
{
int currentNumSlots = 0;
auto it = m_extendableSlots.find(slot->GetName());
if (it != m_extendableSlots.end())
{
currentNumSlots = aznumeric_cast<int>(it->second.size());
}
// Only allow this slot to be deleted if there are more than the required minimum
int minimumSlots = slot->GetMinimumSlots();
if (currentNumSlots > minimumSlots)
{
return true;
}
}
return false;
}
bool Node::CanExtendSlot(SlotDefinitionPtr slotDefinition) const
{
if (slotDefinition->SupportsExtendability())
{
int currentNumSlots = 0;
auto it = m_extendableSlots.find(slotDefinition->GetName());
if (it != m_extendableSlots.end())
{
currentNumSlots = aznumeric_cast<int>(it->second.size());
}
// Only allow this slot to extended if we haven't reached the maximum
int maximumSlots = slotDefinition->GetMaximumSlots();
if (currentNumSlots < maximumSlots)
{
return true;
}
}
return false;
}
SlotPtr Node::AddExtendedSlot(const SlotName& slotName)
{
SlotDefinitionPtr slotDefinition;
for (auto definition : m_extendableSlotDefinitions)
{
if (definition->GetName() == slotName)
{
slotDefinition = definition;
break;
}
}
if (!slotDefinition)
{
AZ_Assert(false, "No slot definitions with registered slotName");
return nullptr;
}
if (!CanExtendSlot(slotDefinition))
{
return nullptr;
}
// Find the existing slots (if any) for this definition, so that we can set the subId
// for the newly created slot
auto extendableIt = m_extendableSlots.find(slotName);
AZ_Assert(extendableIt != m_extendableSlots.end(), "Extendable slot definition name should always exist in the mapping.");
int newSubId = 0;
ExtendableSlotSet& currentSlots = extendableIt->second;
if (!currentSlots.empty())
{
newSubId = (*currentSlots.rbegin())->GetSlotId().m_subId + 1;
}
SlotPtr slot = AZStd::make_shared<Slot>(GetGraph(), slotDefinition, newSubId);
slot->SetValue(slotDefinition->GetDefaultValue());
auto newEntry = AZStd::make_pair(slot->GetSlotId(), slot);
m_allSlots.insert(newEntry);
currentSlots.insert(slot);
return slot;
}
void Node::RegisterSlot(SlotDefinitionPtr slotDefinition, SlotDefinitionList& slotDefinitionList)
{
AssertPointerIsNew(slotDefinition, m_allSlotDefinitions);
AssertPointerIsNew(slotDefinition, m_propertySlotDefinitions);
AssertPointerIsNew(slotDefinition, m_inputDataSlotDefinitions);
AssertPointerIsNew(slotDefinition, m_outputDataSlotDefinitions);
AssertPointerIsNew(slotDefinition, m_extendableSlotDefinitions);
AssertNameIsNew(slotDefinition, m_allSlotDefinitions);
AssertNameIsNew(slotDefinition, m_propertySlotDefinitions);
AssertNameIsNew(slotDefinition, m_inputDataSlotDefinitions);
AssertNameIsNew(slotDefinition, m_outputDataSlotDefinitions);
AssertNameIsNew(slotDefinition, m_extendableSlotDefinitions);
// Only check the target list because we could allow the same display name for an input and an output.
// Only check if DisplayName is not empty, because Name will be used for display in that case.
if (!slotDefinition->GetDisplayName().empty())
{
AssertDisplayNameIsNew(slotDefinition, slotDefinitionList);
}
slotDefinitionList.push_back(slotDefinition);
m_allSlotDefinitions.push_back(slotDefinition);
}
void Node::RegisterSlot(SlotDefinitionPtr slotDefinition)
{
// [GFX TODO] CJS Consider merging SlotDirection and SlotType into a single enum so we can use switch statements.
if (slotDefinition->SupportsExtendability())
{
RegisterSlot(slotDefinition, m_extendableSlotDefinitions);
}
else if (slotDefinition->Is(SlotDirection::Input, SlotType::Data))
{
RegisterSlot(slotDefinition, m_inputDataSlotDefinitions);
}
else if (slotDefinition->Is(SlotDirection::Output, SlotType::Data))
{
RegisterSlot(slotDefinition, m_outputDataSlotDefinitions);
}
else if (slotDefinition->Is(SlotDirection::Input, SlotType::Property))
{
RegisterSlot(slotDefinition, m_propertySlotDefinitions);
}
else if (slotDefinition->Is(SlotDirection::Input, SlotType::Event))
{
RegisterSlot(slotDefinition, m_inputEventSlotDefinitions);
}
else if (slotDefinition->Is(SlotDirection::Output, SlotType::Event))
{
RegisterSlot(slotDefinition, m_outputEventSlotDefinitions);
}
else
{
AZ_Assert(false, "Unsupported slot configuration");
}
}
void Node::AssertPointerIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const
{
auto iter = AZStd::find(existingSlotDefinitions.begin(), existingSlotDefinitions.end(), newSlotDefinition);
AZ_Assert(iter == existingSlotDefinitions.end(), "This slot has already been registered");
}
void Node::AssertNameIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const
{
auto iter = AZStd::find_if(existingSlotDefinitions.begin(), existingSlotDefinitions.end(),
[newSlotDefinition](SlotDefinitionPtr existingSlotDefinition) { return newSlotDefinition->GetName() == existingSlotDefinition->GetName(); });
AZ_Assert(iter == existingSlotDefinitions.end(), "Another slot with name [%s] already exists", newSlotDefinition->GetName().c_str());
}
void Node::AssertDisplayNameIsNew(SlotDefinitionPtr newSlotDefinition, const SlotDefinitionList& existingSlotDefinitions) const
{
auto iter = AZStd::find_if(existingSlotDefinitions.begin(), existingSlotDefinitions.end(),
[newSlotDefinition](SlotDefinitionPtr existingSlotDefinition) { return newSlotDefinition->GetDisplayName() == existingSlotDefinition->GetDisplayName(); });
AZ_Assert(iter == existingSlotDefinitions.end(), "Another slot with display name [%s] already exists", newSlotDefinition->GetDisplayName().c_str());
}
} // namespace GraphModel
+516
View File
@@ -0,0 +1,516 @@
/*
* 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.
*
*/
// AZ
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
// Graph Model
#include <GraphModel/Model/Slot.h>
#include <GraphModel/Model/Node.h>
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/IGraphContext.h>
namespace GraphModel
{
void SlotIdData::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SlotIdData>()
->Version(0)
->Field("m_name", &SlotIdData::m_name)
->Field("m_subId", &SlotIdData::m_subId)
;
}
}
SlotIdData::SlotIdData(const SlotName& name)
: m_name(name)
{
}
SlotIdData::SlotIdData(const SlotName& name, SlotSubId subId)
: m_name(name)
, m_subId(subId)
{
}
bool SlotIdData::IsValid() const
{
return !m_name.empty() && (m_subId >= 0);
}
bool SlotIdData::operator==(const SlotIdData& rhs) const
{
return (m_name == rhs.m_name) && (m_subId == rhs.m_subId);
}
bool SlotIdData::operator!=(const SlotIdData& rhs) const
{
return (m_name != rhs.m_name) || (m_subId != rhs.m_subId);
}
bool SlotIdData::operator<(const SlotIdData& rhs) const
{
if (m_name < rhs.m_name)
{
return true;
}
else if (m_name == rhs.m_name)
{
return m_subId < rhs.m_subId;
}
return false;
}
bool SlotIdData::operator>(const SlotIdData& rhs) const
{
if (m_name > rhs.m_name)
{
return true;
}
else if (m_name == rhs.m_name)
{
return m_subId > rhs.m_subId;
}
return false;
}
AZStd::size_t SlotIdData::GetHash() const
{
AZStd::size_t result = 0;
AZStd::hash_combine(result, m_name);
AZStd::hash_combine(result, m_subId);
return result;
}
/////////////////////////////////////////////////////////
// SlotDefinition
SlotDefinitionPtr SlotDefinition::CreateInputData(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Input;
slotDefinition->m_slotType = SlotType::Data;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_supportedDataTypes = { dataType };
slotDefinition->m_defaultValue = defaultValue;
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
SlotDefinitionPtr SlotDefinition::CreateInputData(AZStd::string_view name, AZStd::string_view displayName, DataTypeList supportedDataTypes, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Input;
slotDefinition->m_slotType = SlotType::Data;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_supportedDataTypes = supportedDataTypes;
slotDefinition->m_defaultValue = defaultValue;
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
SlotDefinitionPtr SlotDefinition::CreateOutputData(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Output;
slotDefinition->m_slotType = SlotType::Data;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_supportedDataTypes = { dataType };
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
SlotDefinitionPtr SlotDefinition::CreateInputEvent(AZStd::string_view name, AZStd::string_view displayName, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Input;
slotDefinition->m_slotType = SlotType::Event;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
SlotDefinitionPtr SlotDefinition::CreateOutputEvent(AZStd::string_view name, AZStd::string_view displayName, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Output;
slotDefinition->m_slotType = SlotType::Event;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
SlotDefinitionPtr SlotDefinition::CreateProperty(AZStd::string_view name, AZStd::string_view displayName, DataTypePtr dataType, AZStd::any defaultValue, AZStd::string_view description, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
AZStd::shared_ptr<SlotDefinition> slotDefinition = AZStd::make_shared<SlotDefinition>();
slotDefinition->m_slotDirection = SlotDirection::Input;
slotDefinition->m_slotType = SlotType::Property;
slotDefinition->m_name = name;
slotDefinition->m_displayName = displayName;
slotDefinition->m_supportedDataTypes = { dataType };
slotDefinition->m_defaultValue = defaultValue;
slotDefinition->m_description = description;
HandleExtendableSlotRegistration(slotDefinition, extendableSlotConfiguration);
return slotDefinition;
}
void SlotDefinition::HandleExtendableSlotRegistration(AZStd::shared_ptr<SlotDefinition> slotDefinition, ExtendableSlotConfiguration* extendableSlotConfiguration)
{
if (extendableSlotConfiguration)
{
slotDefinition->m_extendableSlotConfiguration = *extendableSlotConfiguration;
if (slotDefinition->m_extendableSlotConfiguration.m_minimumSlots > slotDefinition->m_extendableSlotConfiguration.m_maximumSlots)
{
AZ_Assert(false, "Invalid extendable slot configuration for %s, minimum slots greater than maximum slots", slotDefinition->GetName().c_str());
return;
}
slotDefinition->m_extendableSlotConfiguration.m_isValid = true;
}
}
SlotDirection SlotDefinition::GetSlotDirection() const
{
return m_slotDirection;
}
SlotType SlotDefinition::GetSlotType() const
{
return m_slotType;
}
bool SlotDefinition::SupportsValue() const
{
return (GetSlotType() == SlotType::Data && GetSlotDirection() == SlotDirection::Input) ||
(GetSlotType() == SlotType::Property);
}
bool SlotDefinition::SupportsDataType() const
{
return GetSlotType() == SlotType::Data || GetSlotType() == SlotType::Property;
}
bool SlotDefinition::SupportsConnections() const
{
return GetSlotType() == SlotType::Data || GetSlotType() == SlotType::Event;
}
bool SlotDefinition::Is(SlotDirection slotDirection, SlotType slotType) const
{
return GetSlotDirection() == slotDirection && GetSlotType() == slotType;
}
bool SlotDefinition::SupportsExtendability() const
{
return m_extendableSlotConfiguration.m_isValid;
}
const DataTypeList& SlotDefinition::GetSupportedDataTypes() const
{
return m_supportedDataTypes;
}
const SlotName& SlotDefinition::GetName() const
{
return m_name;
}
const AZStd::string& SlotDefinition::GetDisplayName() const
{
return m_displayName;
}
const AZStd::string& SlotDefinition::GetDescription() const
{
return m_description;
}
AZStd::any SlotDefinition::GetDefaultValue() const
{
return m_defaultValue;
}
const int SlotDefinition::GetMinimumSlots() const
{
return m_extendableSlotConfiguration.m_minimumSlots;
}
const int SlotDefinition::GetMaximumSlots() const
{
return m_extendableSlotConfiguration.m_maximumSlots;
}
const AZStd::string& SlotDefinition::GetExtensionLabel() const
{
return m_extendableSlotConfiguration.m_addButtonLabel;
}
const AZStd::string& SlotDefinition::GetExtensionTooltip() const
{
return m_extendableSlotConfiguration.m_addButtonTooltip;
}
/////////////////////////////////////////////////////////
// Slot
void Slot::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<Slot>()
->Version(0)
->Field("m_value", &Slot::m_value)
->Field("m_subId", &Slot::m_subId)
// m_slotDescription is not reflected because that data is populated procedurally by each node
// m_connections is not reflected because they are actually owned by the Graph and reflected there
;
}
}
Slot::Slot(GraphPtr graph, SlotDefinitionPtr slotDefinition, SlotSubId subId)
: GraphElement(graph)
, m_slotDefinition(slotDefinition)
, m_subId(subId)
{
if (SupportsValue())
{
// The m_value must be initialized with an object of the appropriate type, or
// GetValue() will fail the first time its called.
SetValue(m_slotDefinition->GetDefaultValue());
}
}
void Slot::PostLoadSetup(GraphPtr graph, SlotDefinitionPtr slotDefinition)
{
AZ_Assert(nullptr == GetGraph(), "This slot is not freshly loaded");
AZ_Assert(m_parentNode._empty(), "This slot is not freshly loaded");
m_graph = graph;
m_slotDefinition = slotDefinition;
if (SupportsValue())
{
// CJS TODO: Consider using AZ::Outcome for better error reporting
// Check the serialized value type against the supported types for this slot
// instead of just using Slot::GetDataType(), because for slots with
// multiple supported types, Slot::GetDataType() will call GetParentNode()
// to try and resolve its type, which will be a nullptr at this point
// because the parent won't be valid yet
bool valueTypeSupported = false;
DataTypePtr valueDataType = GetGraphContext()->GetDataTypeForValue(m_value);
for (DataTypePtr dataType : GetSupportedDataTypes())
{
if (valueDataType == dataType)
{
valueTypeSupported = true;
break;
}
}
AZ_Error(GetGraph()->GetSystemName(),
valueTypeSupported,
"Possible data corruption. Slot [%s] data type [%s] does not match any supported data type.",
GetDisplayName().c_str(),
valueDataType->GetDisplayName().c_str());
}
}
NodePtr Slot::GetParentNode() const
{
// Originally the parent node was passed to the Slot constructor, but this was before
// using shared_ptr for Nodes. Because the Node constructor is what creates the Slots,
// and shared_from_this() doesn't work in constructors, we can't initialize m_parentNode
// until after the Node is created. So we search for and cache the pointer here the first
// time it is requested.
if (m_parentNode._empty())
{
for (auto nodeIter : GetGraph()->GetNodes())
{
if (nodeIter.second->Contains(shared_from_this()))
{
m_parentNode = nodeIter.second;
break;
}
}
}
return m_parentNode.lock();
}
AZStd::any Slot::GetValue() const
{
return m_value;
}
Slot::ConnectionList Slot::GetConnections() const
{
ConnectionList connections;
for (auto iter : m_connections)
{
if (ConnectionPtr connection = iter.lock())
{
connections.insert(connection);
}
else
{
AZ_Assert(false , "Slot's connection cache is out of date");
}
}
return connections;
}
SlotDefinitionPtr Slot::GetDefinition() const
{
return m_slotDefinition;
}
bool Slot::Is(SlotDirection slotDirection, SlotType slotType) const
{
return m_slotDefinition->Is(slotDirection, slotType);
}
SlotDirection Slot::GetSlotDirection() const { return m_slotDefinition->GetSlotDirection(); }
SlotType Slot::GetSlotType() const { return m_slotDefinition->GetSlotType(); }
bool Slot::SupportsValue() const { return m_slotDefinition->SupportsValue(); }
bool Slot::SupportsDataType() const { return m_slotDefinition->SupportsDataType(); }
bool Slot::SupportsConnections() const { return m_slotDefinition->SupportsConnections(); }
bool Slot::SupportsExtendability() const { return m_slotDefinition->SupportsExtendability(); }
const SlotName& Slot::GetName() const { return m_slotDefinition->GetName(); }
const AZStd::string& Slot::GetDisplayName() const { return m_slotDefinition->GetDisplayName(); }
const AZStd::string& Slot::GetDescription() const { return m_slotDefinition->GetDescription(); }
AZStd::any Slot::GetDefaultValue() const { return m_slotDefinition->GetDefaultValue(); }
const DataTypeList& Slot::GetSupportedDataTypes() const { return m_slotDefinition->GetSupportedDataTypes(); }
const int Slot::GetMinimumSlots() const
{
return m_slotDefinition->GetMinimumSlots();
}
const int Slot::GetMaximumSlots() const
{
return m_slotDefinition->GetMaximumSlots();
}
SlotId Slot::GetSlotId() const
{
return SlotId(GetName(), m_subId);
}
SlotSubId Slot::GetSlotSubId() const
{
return m_subId;
}
const DataTypeList& Slot::GetPossibleDataTypes() const
{
// TODO: For now this will just return all the supported types, but eventually
// it return the subset of possible data types given the current configuration
// of the node.
return GetSupportedDataTypes();
}
DataTypePtr Slot::GetDataType() const
{
// If the slot definition only has a single data type, then that is returned.
// Otherwise, we can ask our parent node to find out what the active type is.
DataTypeList possibleDataTypes = GetPossibleDataTypes();
size_t numPossibleDataTypes = possibleDataTypes.size();
if (numPossibleDataTypes == 1)
{
return possibleDataTypes[0];
}
else if (numPossibleDataTypes > 1)
{
return GetParentNode()->GetDataType(shared_from_this());
}
return nullptr;
}
void Slot::SetValue(const AZStd::any& value)
{
if (SupportsValue())
{
#if defined(AZ_ENABLE_TRACING)
DataTypePtr dataType = GetGraphContext()->GetDataTypeForValue(value);
AssertTypeMatch(dataType, "Slot::SetValue used with the wrong type");
#endif
m_value = value;
}
}
#if defined(AZ_ENABLE_TRACING)
void Slot::AssertWithTypeInfo(bool expression, DataTypePtr dataTypeUsed, const char* message) const
{
AZ_Assert(expression, "%s (Slot DataType=['%s', '%s', %s]. Used DataType=['%s', '%s', %s]). m_value TypeId=%s.",
message,
GetDataType()->GetDisplayName().c_str(),
GetDataType()->GetCppName().c_str(),
GetDataType()->GetTypeUuidString().c_str(),
dataTypeUsed->GetDisplayName().c_str(),
dataTypeUsed->GetCppName().c_str(),
dataTypeUsed->GetTypeUuidString().c_str(),
m_value.type().ToString<AZStd::string>().c_str()
);
}
void Slot::AssertTypeMatch(DataTypePtr dataTypeUsed, const char* message) const
{
// Check if any of the possible data types for this slot match
bool expression = false;
for (auto iter : GetPossibleDataTypes())
{
expression |= (*dataTypeUsed == *iter);
}
AssertWithTypeInfo(expression, dataTypeUsed, message);
}
#endif // AZ_ENABLE_TRACING
} // namespace GraphModel
@@ -0,0 +1,369 @@
/**
* 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.
*
*/
// AZ
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
// Graph Canvas
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/Slots/Data/DataSlotBus.h>
#include <GraphCanvas/Editor/EditorTypes.h>
#include <GraphCanvas/GraphCanvasBus.h>
// GraphModel
#include <GraphModel/GraphModelBus.h>
// GraphModel test harness and minimum setup for a custom GraphModel instance
#include <Tests/TestEnvironment.h>
// These tests rely on this test environment to setup the necessary
// mocked GraphCanvas buses and system components
AZ_UNIT_TEST_HOOK(new GraphModelIntegrationTest::GraphModelTestEnvironment);
namespace GraphModelIntegrationTest
{
/**
* Used for testing the GraphModelIntegration namespace, which relies on GraphCanvasWidgets and Qt.
* NOTE: These tests rely on being run in the GraphModelTestEnvironment, which sets up the
* necessary mocked GraphCanvas buses and system components.
*/
class GraphModelIntegrationTests
: public ::testing::Test
, public UnitTest::TraceBusRedirector
{
protected:
void SetUp() override
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
// Create our test graph context
m_graphContext = AZStd::make_shared<TestGraphContext>();
// Create a new node graph
m_graph = AZStd::make_shared<GraphModel::Graph>(m_graphContext);
// Create a new scene for the graph
AZ::Entity* scene = nullptr;
GraphModelIntegration::GraphManagerRequestBus::BroadcastResult(scene, &GraphModelIntegration::GraphManagerRequests::CreateScene, m_graph, NODE_GRAPH_TEST_EDITOR_ID);
m_scene.reset(scene);
m_sceneId = m_scene->GetId();
}
void TearDown() override
{
// Release shared pointers that were setup
m_graphContext.reset();
m_graph.reset();
m_scene.reset();
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
AZStd::shared_ptr<TestGraphContext> m_graphContext = nullptr;
GraphModel::GraphPtr m_graph = nullptr;
AZStd::unique_ptr<AZ::Entity> m_scene = nullptr;
AZ::EntityId m_sceneId;
};
TEST_F(GraphModelIntegrationTests, NodeAddedToScene)
{
// Create our test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Make sure the correct node was added to the scene
GraphModel::NodePtrList nodeList;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeList, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodes);
EXPECT_EQ(nodeList.size(), 1);
EXPECT_EQ(nodeList[0], testNode);
}
/**
* Make sure the data type of the slot on a node is set properly. There was a new DataSlotConfiguration added
* in GraphCanvas, which the GraphModel implementation hadn't previously accounted for, resulting in all data slots on
* GraphModel nodes having an invalid data type.
*/
TEST_F(GraphModelIntegrationTests, NodeWithDataSlotHasProperDataType)
{
// Create our test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Retrieve the data type (string) for the string input slot on our test node
GraphModel::SlotPtr testNodeStringInputSlot = testNode->GetSlot(TEST_STRING_INPUT_ID);
EXPECT_TRUE(testNodeStringInputSlot != nullptr);
GraphModel::DataTypePtr stringDataType = testNodeStringInputSlot->GetDataType();
AZ::Uuid stringDataTypeId = stringDataType->GetTypeUuid();
// Make sure our node has the expected slots
AZStd::vector<AZ::EntityId> slotIds;
GraphCanvas::NodeRequestBus::EventResult(slotIds, nodeId, &GraphCanvas::NodeRequests::GetSlotIds);
EXPECT_EQ(slotIds.size(), 4);
// Make sure the data type of the input string slot on our test node matches the expected data type
AZ::EntityId slotId = slotIds[0];
AZ::Uuid slotDataTypeId;
GraphCanvas::DataSlotRequestBus::EventResult(slotDataTypeId, slotId, &GraphCanvas::DataSlotRequests::GetDataTypeId);
EXPECT_EQ(stringDataTypeId, slotDataTypeId);
}
TEST_F(GraphModelIntegrationTests, GetNodeById)
{
// Create our test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Test that we can retrieve the expected node by NodeId
GraphModel::NodePtr retrievedNode = nullptr;
GraphModelIntegration::GraphControllerRequestBus::EventResult(retrievedNode, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodeById, nodeId);
EXPECT_EQ(retrievedNode, testNode);
// Test requesting an invalid NodeId returns nullptr
retrievedNode = nullptr;
GraphModelIntegration::GraphControllerRequestBus::EventResult(retrievedNode, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodeById, GraphCanvas::NodeId());
EXPECT_EQ(retrievedNode, nullptr);
// Test requesting a valid NodeId but one that doesn't exist in the scene returns nullptr
retrievedNode = nullptr;
GraphModelIntegration::GraphControllerRequestBus::EventResult(retrievedNode, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodeById, GraphCanvas::NodeId(1234));
EXPECT_EQ(retrievedNode, nullptr);
}
TEST_F(GraphModelIntegrationTests, GetNodesFromGraphNodeIds)
{
// Create a test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Retrieve Nodes by their NodeId
AZStd::vector<GraphCanvas::NodeId> nodeIds = {
nodeId, // Valid NodeId for a node in the scene
GraphCanvas::NodeId(), // Invalid NodeId
GraphCanvas::NodeId(1234) // Valid NodeId but not in the scene
};
GraphModel::NodePtrList retrievedNodes;
GraphModelIntegration::GraphControllerRequestBus::EventResult(retrievedNodes, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodesFromGraphNodeIds, nodeIds);
EXPECT_EQ(nodeIds.size(), retrievedNodes.size());
// Test the first node in the list should be our valid test node
EXPECT_EQ(retrievedNodes[0], testNode);
// Test the second node should be a nullptr since it was an invalid NodeId
EXPECT_EQ(retrievedNodes[1], nullptr);
// Test the third node should also be a nullptr since it was a valid NodeId but one that doesn't exist in the scene
EXPECT_EQ(retrievedNodes[2], nullptr);
}
TEST_F(GraphModelIntegrationTests, ExtendableSlotsWithDifferentMinimumValues)
{
// Create a node with extendable slots
GraphModel::NodePtr testNode = AZStd::make_shared<ExtendableSlotsNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// The input string extendable slot has a minimum of 0 slots, so there should be none
auto extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
int numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 0);
EXPECT_EQ(numExtendableSlots, 0);
// The output string and input event extendable slots both use the default minimum (1)
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_OUTPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
extendableSlots = testNode->GetExtendableSlots(TEST_EVENT_INPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_EVENT_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
// The output event extendable slot has a minimum of 3 slots
extendableSlots = testNode->GetExtendableSlots(TEST_EVENT_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 3);
}
TEST_F(GraphModelIntegrationTests, ExtendableSlotWithInvalidConfiguration)
{
// Create a node with extendable slots
AZ_TEST_START_TRACE_SUPPRESSION;
GraphModel::NodePtr testNode = AZStd::make_shared<BadNode>(m_graph, m_graphContext);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// The input string extendable slot has a minimum of 5 and a maximum of 1,
// which is an invalid configuration, so there will be no extendable
// slots created for this slot
auto extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
int numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 0);
EXPECT_EQ(numExtendableSlots, -1);
}
TEST_F(GraphModelIntegrationTests, AddingExtendableSlotsPastMaximum)
{
// Create a node with extendable slots
GraphModel::NodePtr testNode = AZStd::make_shared<ExtendableSlotsNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// The input string extendable slot has a minimum of 0 slots, so it starts with 0
auto extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
int numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 0);
EXPECT_EQ(numExtendableSlots, 0);
// The input string extendable slot has a maximum of 2 slots, so the first add should succeed
GraphModel::SlotPtr firstSlot = testNode->AddExtendedSlot(TEST_STRING_INPUT_ID);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_TRUE(firstSlot != nullptr);
EXPECT_EQ(firstSlot->GetName(), TEST_STRING_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
// The input string extendable slot has a maximum of 2 slots, so the second add should also succeed
GraphModel::SlotPtr secondSlot = testNode->AddExtendedSlot(TEST_STRING_INPUT_ID);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_TRUE(secondSlot != nullptr);
EXPECT_EQ(secondSlot->GetName(), TEST_STRING_INPUT_ID);
EXPECT_EQ(extendableSlots.size(), 2);
EXPECT_EQ(numExtendableSlots, 2);
// The input string extendable slot has a maximum of 2 slots, so the third add should fail
GraphModel::SlotPtr thirdSlot = testNode->AddExtendedSlot(TEST_STRING_INPUT_ID);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_INPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_INPUT_ID);
EXPECT_TRUE(thirdSlot == nullptr);
EXPECT_EQ(extendableSlots.size(), 2);
EXPECT_EQ(numExtendableSlots, 2);
}
TEST_F(GraphModelIntegrationTests, RemovingExtendableSlotsBelowMinimum)
{
// Create a node with extendable slots
GraphModel::NodePtr testNode = AZStd::make_shared<ExtendableSlotsNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// The output string extendable slot has a minimum of 1 slot, so it starts with 1
auto extendableSlots = testNode->GetExtendableSlots(TEST_STRING_OUTPUT_ID);
int numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
// The output string extendable slot has the default maximum (100), so we can add one
GraphModel::SlotPtr firstSlot = testNode->AddExtendedSlot(TEST_STRING_OUTPUT_ID);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_OUTPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_OUTPUT_ID);
EXPECT_TRUE(firstSlot != nullptr);
EXPECT_EQ(firstSlot->GetName(), TEST_STRING_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 2);
EXPECT_EQ(numExtendableSlots, 2);
// The output string extednable slot has a minimum of 1, so we can remove 1
testNode->DeleteSlot(firstSlot);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_OUTPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
// The output string extednable slot has a minimum of 1, so attempting to remove one
// when there is only one left will fail
GraphModel::SlotPtr lastSlot = *extendableSlots.begin();
testNode->DeleteSlot(lastSlot);
extendableSlots = testNode->GetExtendableSlots(TEST_STRING_OUTPUT_ID);
numExtendableSlots = testNode->GetExtendableSlotCount(TEST_STRING_OUTPUT_ID);
EXPECT_EQ(extendableSlots.size(), 1);
EXPECT_EQ(numExtendableSlots, 1);
}
TEST_F(GraphModelIntegrationTests, CannotAddNonExtendableSlot)
{
// Create a test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Can't add a non-extendable slot, so adding a non-extendable slot will fail
AZ_TEST_START_TRACE_SUPPRESSION;
GraphModel::SlotPtr newSlot = testNode->AddExtendedSlot(TEST_STRING_INPUT_ID);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_EQ(newSlot, nullptr);
}
TEST_F(GraphModelIntegrationTests, CannotDeleteNonExtendableSlot)
{
// Create a test node
GraphModel::NodePtr testNode = AZStd::make_shared<TestNode>(m_graph, m_graphContext);
EXPECT_TRUE(testNode != nullptr);
// Add our test node to the scene
AZ::Vector2 offset;
GraphCanvas::NodeId nodeId;
GraphModelIntegration::GraphControllerRequestBus::EventResult(nodeId, m_sceneId, &GraphModelIntegration::GraphControllerRequests::AddNode, testNode, offset);
// Can't delete a non-extendable slot, so deleting a non-extendable slot will fail
auto beforeSlots = testNode->GetSlots();
GraphModel::SlotPtr inputSlot = testNode->GetSlot(TEST_STRING_INPUT_ID);
testNode->DeleteSlot(inputSlot);
auto afterSlots = testNode->GetSlots();
EXPECT_EQ(beforeSlots.size(), afterSlots.size());
}
}
@@ -0,0 +1,66 @@
/*
* 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/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
class GraphModelPythonBindingsFixture
: public ::testing::Test
{
};
TEST_F(GraphModelPythonBindingsFixture, GraphModelGraphManagerRequests_ApiExists)
{
AZ::BehaviorContext* behaviorContext(nullptr);
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
ASSERT_TRUE(behaviorContext);
auto graphManagerRequestBus = behaviorContext->m_ebuses.find("GraphManagerRequestBus");
EXPECT_TRUE(behaviorContext->m_ebuses.end() != graphManagerRequestBus);
auto* behaviorBus = graphManagerRequestBus->second;
auto eventIt = behaviorBus->m_events.find("GetGraph");
EXPECT_TRUE(behaviorBus->m_events.end() != eventIt);
}
TEST_F(GraphModelPythonBindingsFixture, GraphModelGraphControllerRequests_ApiExists)
{
AZ::BehaviorContext* behaviorContext(nullptr);
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
ASSERT_TRUE(behaviorContext);
auto graphControllerRequestBus = behaviorContext->m_ebuses.find("GraphControllerRequestBus");
EXPECT_TRUE(behaviorContext->m_ebuses.end() != graphControllerRequestBus);
const AZStd::vector<AZStd::string> eventNames = {
"AddNode",
"RemoveNode",
"AddConnection",
"AddConnectionBySlotId",
"RemoveConnection"
};
auto* behaviorBus = graphControllerRequestBus->second;
for (const AZStd::string& name : eventNames)
{
auto eventIt = behaviorBus->m_events.find(name);
EXPECT_TRUE(behaviorBus->m_events.end() != eventIt);
}
}
}
@@ -0,0 +1,567 @@
/**
* 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 <Tests/MockGraphCanvas.h>
namespace MockGraphCanvasServices
{
// MockSlotComponent
void MockSlotComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockSlotComponent, AZ::Component>()
->Version(0)
;
}
}
AZ::Entity* MockSlotComponent::CreateCoreSlotEntity()
{
AZ::Entity* entity = aznew AZ::Entity("Slot");
return entity;
}
MockSlotComponent::MockSlotComponent(const GraphCanvas::SlotType& slotType)
: m_slotType(slotType)
{
}
MockSlotComponent::MockSlotComponent(const GraphCanvas::SlotType& slotType, const GraphCanvas::SlotConfiguration& configuration)
: m_slotType(slotType)
, m_slotConfiguration(configuration)
{
}
void MockSlotComponent::Activate()
{
}
void MockSlotComponent::Deactivate()
{
}
// MockDataSlotComponent
void MockDataSlotComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockDataSlotComponent, MockSlotComponent>()
->Version(0)
;
}
}
AZ::Entity* MockDataSlotComponent::CreateDataSlot(const GraphCanvas::DataSlotConfiguration& dataSlotConfiguration)
{
AZ::Entity* entity = MockSlotComponent::CreateCoreSlotEntity();
MockDataSlotComponent* dataSlot = aznew MockDataSlotComponent(dataSlotConfiguration);
if (!entity->AddComponent(dataSlot))
{
delete dataSlot;
delete entity;
return nullptr;
}
return entity;
}
MockDataSlotComponent::MockDataSlotComponent()
: MockSlotComponent(GraphCanvas::SlotTypes::DataSlot)
{
}
MockDataSlotComponent::MockDataSlotComponent(const GraphCanvas::DataSlotConfiguration& dataSlotConfiguration)
: MockSlotComponent(GraphCanvas::SlotTypes::DataSlot, dataSlotConfiguration)
, m_dataSlotConfiguration(dataSlotConfiguration)
{
}
void MockDataSlotComponent::Activate()
{
GraphCanvas::DataSlotRequestBus::Handler::BusConnect(GetEntityId());
}
void MockDataSlotComponent::Deactivate()
{
GraphCanvas::DataSlotRequestBus::Handler::BusDisconnect();
}
bool MockDataSlotComponent::ConvertToReference()
{
return false;
}
bool MockDataSlotComponent::CanConvertToReference() const
{
return false;
}
bool MockDataSlotComponent::ConvertToValue()
{
return false;
}
bool MockDataSlotComponent::CanConvertToValue() const
{
return false;
}
GraphCanvas::DataSlotType MockDataSlotComponent::GetDataSlotType() const
{
return m_dataSlotConfiguration.m_dataSlotType;
}
GraphCanvas::DataValueType MockDataSlotComponent::GetDataValueType() const
{
return m_dataSlotConfiguration.m_dataValueType;
}
AZ::Uuid MockDataSlotComponent::GetDataTypeId() const
{
return m_dataSlotConfiguration.m_typeId;
}
void MockDataSlotComponent::SetDataTypeId(AZ::Uuid typeId)
{
m_dataSlotConfiguration.m_typeId = typeId;
}
const GraphCanvas::Styling::StyleHelper* MockDataSlotComponent::GetDataColorPalette() const
{
return nullptr;
}
size_t MockDataSlotComponent::GetContainedTypesCount() const
{
return m_dataSlotConfiguration.m_containerTypeIds.size();
}
AZ::Uuid MockDataSlotComponent::GetContainedTypeId(size_t index) const
{
return m_dataSlotConfiguration.m_containerTypeIds[index];
}
const GraphCanvas::Styling::StyleHelper* MockDataSlotComponent::GetContainedTypeColorPalette([[maybe_unused]] size_t index) const
{
return nullptr;
}
void MockDataSlotComponent::SetDataAndContainedTypeIds(AZ::Uuid typeId, const AZStd::vector<AZ::Uuid>& typeIds, GraphCanvas::DataValueType valueType)
{
AZ_UNUSED(typeId);
AZ_UNUSED(typeIds);
AZ_UNUSED(valueType);
}
// MockExecutionSlotComponent
void MockExecutionSlotComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockExecutionSlotComponent, MockSlotComponent>()
->Version(0)
;
}
}
AZ::Entity* MockExecutionSlotComponent::CreateExecutionSlot(const AZ::EntityId& nodeId, const GraphCanvas::SlotConfiguration& slotConfiguration)
{
AZ_UNUSED(nodeId);
AZ::Entity* entity = MockSlotComponent::CreateCoreSlotEntity();
MockExecutionSlotComponent* executionSlot = aznew MockExecutionSlotComponent(slotConfiguration);
if (!entity->AddComponent(executionSlot))
{
delete executionSlot;
delete entity;
return nullptr;
}
return entity;
}
MockExecutionSlotComponent::MockExecutionSlotComponent()
: MockSlotComponent(GraphCanvas::SlotTypes::ExecutionSlot)
{
}
MockExecutionSlotComponent::MockExecutionSlotComponent(const GraphCanvas::SlotConfiguration& slotConfiguration)
: MockSlotComponent(GraphCanvas::SlotTypes::ExecutionSlot, slotConfiguration)
, m_executionSlotConfiguration(slotConfiguration)
{
}
// MockExtenderSlotComponent
void MockExtenderSlotComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockExtenderSlotComponent, MockSlotComponent>()
->Version(0)
;
}
}
AZ::Entity* MockExtenderSlotComponent::CreateExtenderSlot(const AZ::EntityId& nodeId, const GraphCanvas::ExtenderSlotConfiguration& slotConfiguration)
{
AZ_UNUSED(nodeId);
AZ::Entity* entity = MockSlotComponent::CreateCoreSlotEntity();
MockExtenderSlotComponent* extenderSlot = aznew MockExtenderSlotComponent(slotConfiguration);
if (!entity->AddComponent(extenderSlot))
{
delete extenderSlot;
delete entity;
return nullptr;
}
return entity;
}
MockExtenderSlotComponent::MockExtenderSlotComponent()
: MockSlotComponent(GraphCanvas::SlotTypes::ExtenderSlot)
{
}
MockExtenderSlotComponent::MockExtenderSlotComponent(const GraphCanvas::ExtenderSlotConfiguration& slotConfiguration)
: MockSlotComponent(GraphCanvas::SlotTypes::ExtenderSlot, slotConfiguration)
, m_extenderSlotConfiguration(slotConfiguration)
{
}
void MockExtenderSlotComponent::Activate()
{
GraphCanvas::ExtenderSlotRequestBus::Handler::BusConnect(GetEntityId());
}
void MockExtenderSlotComponent::Deactivate()
{
GraphCanvas::ExtenderSlotRequestBus::Handler::BusDisconnect();
}
void MockExtenderSlotComponent::TriggerExtension()
{
}
GraphCanvas::Endpoint MockExtenderSlotComponent::ExtendForConnectionProposal(const GraphCanvas::ConnectionId& connectionId, const GraphCanvas::Endpoint& endpoint)
{
AZ_UNUSED(connectionId);
AZ_UNUSED(endpoint);
return GraphCanvas::Endpoint();
}
// MockNodeComponent
void MockNodeComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockNodeComponent, AZ::Component>()
->Version(0)
;
}
}
AZ::Entity* MockNodeComponent::CreateCoreNodeEntity(const GraphCanvas::NodeConfiguration& config)
{
// Create this Node's entity.
AZ::Entity* entity = aznew AZ::Entity();
entity->CreateComponent<MockNodeComponent>(config);
return entity;
}
MockNodeComponent::MockNodeComponent(const GraphCanvas::NodeConfiguration& config)
: m_configuration(config)
{
}
void MockNodeComponent::Activate()
{
GraphCanvas::NodeRequestBus::Handler::BusConnect(GetEntityId());
}
void MockNodeComponent::Deactivate()
{
GraphCanvas::NodeRequestBus::Handler::BusDisconnect();
}
void MockNodeComponent::SetTooltip(const AZStd::string& tooltip)
{
m_configuration.SetTooltip(tooltip);
}
void MockNodeComponent::SetTranslationKeyedTooltip(const GraphCanvas::TranslationKeyedString& tooltip)
{
m_configuration.SetTooltip(tooltip.GetDisplayString());
}
const AZStd::string MockNodeComponent::GetTooltip() const
{
return m_configuration.GetTooltip();
}
void MockNodeComponent::SetShowInOutliner(bool showInOutliner)
{
m_configuration.SetShowInOutliner(showInOutliner);
}
bool MockNodeComponent::ShowInOutliner() const
{
return m_configuration.GetShowInOutliner();
}
void MockNodeComponent::AddSlot(const AZ::EntityId& slotId)
{
AZ_Assert(slotId.IsValid(), "Slot entity (ID: %s) is not valid!", slotId.ToString().data());
m_slotIds.emplace_back(slotId);
}
void MockNodeComponent::RemoveSlot(const AZ::EntityId& slotId)
{
AZ_Assert(slotId.IsValid(), "Slot (ID: %s) is not valid!", slotId.ToString().data());
auto entry = AZStd::find(m_slotIds.begin(), m_slotIds.end(), slotId);
AZ_Assert(entry != m_slotIds.end(), "Slot (ID: %s) is unknown", slotId.ToString().data());
if (entry != m_slotIds.end())
{
m_slotIds.erase(entry);
}
}
AZStd::vector<AZ::EntityId> MockNodeComponent::GetSlotIds() const
{
return m_slotIds;
}
AZStd::vector<GraphCanvas::SlotId> MockNodeComponent::GetVisibleSlotIds() const
{
return m_slotIds;
}
AZStd::vector<GraphCanvas::SlotId> MockNodeComponent::FindVisibleSlotIdsByType([[maybe_unused]] const GraphCanvas::ConnectionType& connectionType, [[maybe_unused]] const GraphCanvas::SlotType& slotType) const
{
AZStd::vector<GraphCanvas::SlotId> empty;
return empty;
}
bool MockNodeComponent::HasConnections() const
{
bool hasConnections = false;
for (auto slotId : m_slotIds)
{
GraphCanvas::SlotRequestBus::EventResult(hasConnections, slotId, &GraphCanvas::SlotRequests::HasConnections);
if (hasConnections)
{
break;
}
}
return hasConnections;
}
AZStd::any* MockNodeComponent::GetUserData()
{
return &m_userData;
}
bool MockNodeComponent::IsWrapped() const
{
return false;
}
void MockNodeComponent::SetWrappingNode([[maybe_unused]] const AZ::EntityId& wrappingNode)
{
}
AZ::EntityId MockNodeComponent::GetWrappingNode() const
{
return AZ::EntityId();
}
void MockNodeComponent::SignalBatchedConnectionManipulationBegin()
{
}
void MockNodeComponent::SignalBatchedConnectionManipulationEnd()
{
}
GraphCanvas::RootGraphicsItemEnabledState MockNodeComponent::UpdateEnabledState()
{
return GraphCanvas::RootGraphicsItemEnabledState::ES_Enabled;
}
bool MockNodeComponent::IsHidingUnusedSlots() const
{
return false;
}
void MockNodeComponent::ShowAllSlots()
{
}
void MockNodeComponent::HideUnusedSlots()
{
}
bool MockNodeComponent::HasHideableSlots() const
{
return false;
}
void MockNodeComponent::SignalConnectionMoveBegin([[maybe_unused]] const GraphCanvas::ConnectionId& connectionId)
{
}
// MockGraphCanvasSystemComponent
void MockGraphCanvasSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MockGraphCanvasSystemComponent, AZ::Component>()
->Version(0)
;
}
}
void MockGraphCanvasSystemComponent::Activate()
{
GraphCanvas::GraphCanvasRequestBus::Handler::BusConnect();
}
void MockGraphCanvasSystemComponent::Deactivate()
{
GraphCanvas::GraphCanvasRequestBus::Handler::BusDisconnect();
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateBookmarkAnchor() const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateScene() const
{
AZ::Entity* entity = aznew AZ::Entity("GraphCanvasScene");
return entity;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateCoreNode() const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateGeneralNode([[maybe_unused]] const char* nodeType) const
{
// Create this Node's entity.
AZ::Entity* entity = MockNodeComponent::CreateCoreNodeEntity();
return entity;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateCommentNode() const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateWrapperNode([[maybe_unused]] const char* nodeType) const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateNodeGroup() const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateCollapsedNodeGroup([[maybe_unused]] const GraphCanvas::CollapsedNodeGroupConfiguration& groupedNodeConfiguration) const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreateSlot(const AZ::EntityId& nodeId, const GraphCanvas::SlotConfiguration& slotConfiguration) const
{
if (const GraphCanvas::DataSlotConfiguration* dataSlotConfiguration = azrtti_cast<const GraphCanvas::DataSlotConfiguration*>(&slotConfiguration))
{
return MockDataSlotComponent::CreateDataSlot((*dataSlotConfiguration));
}
else if (const GraphCanvas::ExecutionSlotConfiguration* executionSlotConfiguration = azrtti_cast<const GraphCanvas::ExecutionSlotConfiguration*>(&slotConfiguration))
{
return MockExecutionSlotComponent::CreateExecutionSlot(nodeId, (*executionSlotConfiguration));
}
else if (const GraphCanvas::ExtenderSlotConfiguration* extenderSlotConfiguration = azrtti_cast<const GraphCanvas::ExtenderSlotConfiguration*>(&slotConfiguration))
{
return MockExtenderSlotComponent::CreateExtenderSlot(nodeId, (*extenderSlotConfiguration));
}
else
{
AZ_Error("GraphCanvas", false, "Trying to create using an unknown Slot Configuration");
}
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateBooleanNodePropertyDisplay([[maybe_unused]] GraphCanvas::BooleanDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateNumericNodePropertyDisplay([[maybe_unused]] GraphCanvas::NumericDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateComboBoxNodePropertyDisplay([[maybe_unused]] GraphCanvas::ComboBoxDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateEntityIdNodePropertyDisplay([[maybe_unused]] GraphCanvas::EntityIdDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateReadOnlyNodePropertyDisplay([[maybe_unused]] GraphCanvas::ReadOnlyDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateStringNodePropertyDisplay([[maybe_unused]] GraphCanvas::StringDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateVectorNodePropertyDisplay([[maybe_unused]] GraphCanvas::VectorDataInterface* dataInterface) const
{
return nullptr;
}
GraphCanvas::NodePropertyDisplay* MockGraphCanvasSystemComponent::CreateAssetIdNodePropertyDisplay([[maybe_unused]] GraphCanvas::AssetIdDataInterface* dataInterface) const
{
return nullptr;
}
AZ::Entity* MockGraphCanvasSystemComponent::CreatePropertySlot([[maybe_unused]] const AZ::EntityId& nodeId, [[maybe_unused]] const AZ::Crc32& propertyId, [[maybe_unused]] const GraphCanvas::SlotConfiguration& slotConfiguration) const
{
return nullptr;
}
}
@@ -0,0 +1,255 @@
/**
* 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
// AZ ...
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Serialization/SerializeContext.h>
// Graph Canvas ...
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/Slots/Data/DataSlotBus.h>
#include <GraphCanvas/Components/Slots/Extender/ExtenderSlotBus.h>
#include <GraphCanvas/Editor/EditorTypes.h>
#include <GraphCanvas/GraphCanvasBus.h>
namespace MockGraphCanvasServices
{
//! This mocks the GraphCanvas::SlotComponent component.
//! This component is added to a SlotEntity that is created when a Slot is added to a Node.
class MockSlotComponent
: public AZ::Component
{
public:
AZ_COMPONENT(MockSlotComponent, "{030690A4-6D16-4770-89B8-20A2EDF48D87}");
static void Reflect(AZ::ReflectContext* context);
static AZ::Entity* CreateCoreSlotEntity();
MockSlotComponent() = default;
explicit MockSlotComponent(const GraphCanvas::SlotType& slotType);
MockSlotComponent(const GraphCanvas::SlotType& slotType, const GraphCanvas::SlotConfiguration& configuration);
~MockSlotComponent() override = default;
// Component overrides ...
void Activate() override;
void Deactivate() override;
protected:
GraphCanvas::SlotType m_slotType;
GraphCanvas::SlotConfiguration m_slotConfiguration;
};
//! This mocks the GraphCanvas::DataSlotComponent component.
//! This component is the specific instance of a SlotComponent that is
//! added to a SlotEntity when a DataSlot is added to a Node.
//! Implements the GraphCanvas::DataSlotRequestBus for tests which involve data slots.
class MockDataSlotComponent
: public MockSlotComponent
, public GraphCanvas::DataSlotRequestBus::Handler
{
public:
AZ_COMPONENT(MockDataSlotComponent, "{0E2E8F38-3B7B-427D-ABD6-38C68FDEFE88}", MockSlotComponent);
static void Reflect(AZ::ReflectContext* context);
static AZ::Entity* CreateDataSlot(const GraphCanvas::DataSlotConfiguration& dataSlotConfiguration);
MockDataSlotComponent();
MockDataSlotComponent(const GraphCanvas::DataSlotConfiguration& dataSlotConfiguration);
~MockDataSlotComponent() = default;
// Component overrides ...
void Activate() override;
void Deactivate() override;
// GraphCanvas::DataSlotRequestBus overrides ...
bool ConvertToReference() override;
bool CanConvertToReference() const override;
bool ConvertToValue() override;
bool CanConvertToValue() const override;
GraphCanvas::DataSlotType GetDataSlotType() const override;
GraphCanvas::DataValueType GetDataValueType() const override;
AZ::Uuid GetDataTypeId() const override;
void SetDataTypeId(AZ::Uuid typeId) override;
const GraphCanvas::Styling::StyleHelper* GetDataColorPalette() const override;
size_t GetContainedTypesCount() const override;
AZ::Uuid GetContainedTypeId(size_t index) const override;
const GraphCanvas::Styling::StyleHelper* GetContainedTypeColorPalette(size_t index) const override;
void SetDataAndContainedTypeIds(AZ::Uuid typeId, const AZStd::vector<AZ::Uuid>& typeIds, GraphCanvas::DataValueType valueType) override;
private:
MockDataSlotComponent(const MockDataSlotComponent&) = delete;
MockDataSlotComponent& operator=(const MockDataSlotComponent&) = delete;
GraphCanvas::DataSlotConfiguration m_dataSlotConfiguration;
};
//! This mocks the GraphCanvas::ExecutionSlotComponent component.
//! This component is the specific instance of a SlotComponent that is
//! added to a SlotEntity when an ExecutionSlot is added to a Node.
class MockExecutionSlotComponent
: public MockSlotComponent
{
public:
AZ_COMPONENT(MockExecutionSlotComponent, "{3E12451C-65EB-45A6-AC98-437F06021359}", MockSlotComponent);
static void Reflect(AZ::ReflectContext* reflectContext);
static AZ::Entity* CreateExecutionSlot(const AZ::EntityId& nodeId, const GraphCanvas::SlotConfiguration& slotConfiguration);
MockExecutionSlotComponent();
explicit MockExecutionSlotComponent(const GraphCanvas::SlotConfiguration& slotConfiguration);
~MockExecutionSlotComponent() = default;
protected:
MockExecutionSlotComponent(const MockExecutionSlotComponent&) = delete;
MockExecutionSlotComponent& operator=(const MockExecutionSlotComponent&) = delete;
GraphCanvas::SlotConfiguration m_executionSlotConfiguration;
};
//! This mocks the GraphCanvas::ExtenderSlotComponent component.
//! This component is the specific instance of a SlotComponent that is
//! added to a SlotEntity when an ExtenderSlot is added to a Node.
//! Implements the GraphCanvas::ExtenderSlotRequestBus for tests which involve extender slots.
class MockExtenderSlotComponent
: public MockSlotComponent
, public GraphCanvas::ExtenderSlotRequestBus::Handler
{
public:
AZ_COMPONENT(MockExtenderSlotComponent, "{0CAE942E-5E4E-42EC-8F63-809A4DE317C0}", MockSlotComponent);
static void Reflect(AZ::ReflectContext* reflectContext);
static AZ::Entity* CreateExtenderSlot(const AZ::EntityId& nodeId, const GraphCanvas::ExtenderSlotConfiguration& slotConfiguration);
MockExtenderSlotComponent();
explicit MockExtenderSlotComponent(const GraphCanvas::ExtenderSlotConfiguration& slotConfiguration);
~MockExtenderSlotComponent() = default;
// Component overrides ...
void Activate();
void Deactivate();
////
// ExtenderSlotComponent overrides ...
void TriggerExtension() override;
GraphCanvas::Endpoint ExtendForConnectionProposal(const GraphCanvas::ConnectionId& connectionId, const GraphCanvas::Endpoint& endpoint) override;
protected:
MockExtenderSlotComponent(const MockExtenderSlotComponent&) = delete;
MockExtenderSlotComponent& operator=(const MockExtenderSlotComponent&) = delete;
GraphCanvas::ExtenderSlotConfiguration m_extenderSlotConfiguration;
};
//! This mocks the GraphCanvas::NodeComponent component.
//! This component is added to a Node entity when a Node is added to the graph.
//! Implements the GraphCanvas::NodeRequestBus for tests that invole nodes.
class MockNodeComponent
: public AZ::Component
, public GraphCanvas::NodeRequestBus::Handler
{
public:
AZ_COMPONENT(MockNodeComponent, "{886E7216-FD58-442B-AF1E-1AC7174885F8}", AZ::Component);
static void Reflect(AZ::ReflectContext* context);
static AZ::Entity* CreateCoreNodeEntity(const GraphCanvas::NodeConfiguration& config = GraphCanvas::NodeConfiguration());
MockNodeComponent() = default;
MockNodeComponent(const GraphCanvas::NodeConfiguration& config);
~MockNodeComponent() override = default;
// Component overrides ...
void Activate() override;
void Deactivate() override;
// GraphCanvas::NodeRequestBus overrides ...
void SetTooltip(const AZStd::string& tooltip) override;
void SetTranslationKeyedTooltip(const GraphCanvas::TranslationKeyedString& tooltip) override;
const AZStd::string GetTooltip() const override;
void SetShowInOutliner(bool showInOutliner);
bool ShowInOutliner() const override;
void AddSlot(const AZ::EntityId& slotId) override;
void RemoveSlot(const AZ::EntityId& slotId) override;
AZStd::vector<AZ::EntityId> GetSlotIds() const override;
AZStd::vector<GraphCanvas::SlotId> GetVisibleSlotIds() const override;
AZStd::vector<GraphCanvas::SlotId> FindVisibleSlotIdsByType(const GraphCanvas::ConnectionType& connectionType, const GraphCanvas::SlotType& slotType) const override;
bool HasConnections() const override;
AZStd::any* GetUserData() override;
bool IsWrapped() const override;
void SetWrappingNode(const AZ::EntityId& wrappingNode) override;
AZ::EntityId GetWrappingNode() const override;
void SignalBatchedConnectionManipulationBegin() override;
void SignalBatchedConnectionManipulationEnd() override;
GraphCanvas::RootGraphicsItemEnabledState UpdateEnabledState() override;
bool IsHidingUnusedSlots() const override;
void ShowAllSlots() override;
void HideUnusedSlots() override;
bool HasHideableSlots() const override;
void SignalConnectionMoveBegin(const GraphCanvas::ConnectionId& connectionId) override;
protected:
/// This node's slots
AZStd::vector<AZ::EntityId> m_slotIds;
/// Serialized configuration settings
GraphCanvas::NodeConfiguration m_configuration;
/// Stores custom user data for this node
AZStd::any m_userData;
};
//! This mocks the GraphCanvas::GraphCanvasSystemComponent component.
//! This component is created and added to the system entity created in our GraphModelIntegrationTest::TestEnvironment
//! because this component implements the GraphCanvas::GraphCanvasRequestBus that is
//! the entry point bus for performing basic GraphCanvas operations such as creating
//! a new scene, creating nodes, creating slots, etc...
class MockGraphCanvasSystemComponent
: public AZ::Component
, private GraphCanvas::GraphCanvasRequestBus::Handler
{
public:
AZ_COMPONENT(MockGraphCanvasSystemComponent, "{03D5474F-5FF3-4D7B-B578-2C3EC132E921}");
static void Reflect(AZ::ReflectContext* context);
MockGraphCanvasSystemComponent() = default;
~MockGraphCanvasSystemComponent() override = default;
private:
// Component overrides ...
void Activate() override;
void Deactivate() override;
// GraphCanvas::GraphCanvasRequestBus overrides ...
AZ::Entity* CreateBookmarkAnchor() const override;
AZ::Entity* CreateScene() const override;
AZ::Entity* CreateCoreNode() const override;
AZ::Entity* CreateGeneralNode(const char* nodeType) const override;
AZ::Entity* CreateCommentNode() const override;
AZ::Entity* CreateWrapperNode(const char* nodeType) const override;
AZ::Entity* CreateNodeGroup() const override;
AZ::Entity* CreateCollapsedNodeGroup(const GraphCanvas::CollapsedNodeGroupConfiguration& groupedNodeConfiguration) const override;
AZ::Entity* CreateSlot(const AZ::EntityId& nodeId, const GraphCanvas::SlotConfiguration& slotConfiguration) const override;
GraphCanvas::NodePropertyDisplay* CreateBooleanNodePropertyDisplay(GraphCanvas::BooleanDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateNumericNodePropertyDisplay(GraphCanvas::NumericDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateComboBoxNodePropertyDisplay(GraphCanvas::ComboBoxDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateEntityIdNodePropertyDisplay(GraphCanvas::EntityIdDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateReadOnlyNodePropertyDisplay(GraphCanvas::ReadOnlyDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateStringNodePropertyDisplay(GraphCanvas::StringDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateVectorNodePropertyDisplay(GraphCanvas::VectorDataInterface* dataInterface) const override;
GraphCanvas::NodePropertyDisplay* CreateAssetIdNodePropertyDisplay(GraphCanvas::AssetIdDataInterface* dataInterface) const override;
AZ::Entity* CreatePropertySlot(const AZ::EntityId& nodeId, const AZ::Crc32& propertyId, const GraphCanvas::SlotConfiguration& slotConfiguration) const override;
};
}
@@ -0,0 +1,287 @@
/**
* 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 <Tests/TestEnvironment.h>
#include <Source/GraphModelSystemComponent.h>
namespace GraphModelIntegrationTest
{
// TestGraphContext
TestGraphContext::TestGraphContext()
{
// Construct basic data types
const AZ::Uuid stringTypeUuid = azrtti_typeid<AZStd::string>();
const AZ::Uuid entityIdTypeUuid = azrtti_typeid<AZ::EntityId>();
m_dataTypes.push_back(AZStd::make_shared<GraphModel::DataType>(TestDataTypeEnum::TestDataTypeEnum_String, stringTypeUuid, AZStd::any(AZStd::string("")), "String", "AZStd::string"));
m_dataTypes.push_back(AZStd::make_shared<GraphModel::DataType>(TestDataTypeEnum::TestDataTypeEnum_EntityId, entityIdTypeUuid, AZStd::any(AZ::EntityId()), "EntityId", "AZ::EntityId"));
}
const char* TestGraphContext::GetSystemName() const
{
return "GraphModelIntegrationTest";
}
const char* TestGraphContext::GetModuleFileExtension() const
{
return ".nodeTest";
}
const GraphModel::DataTypeList& TestGraphContext::GetAllDataTypes() const
{
return m_dataTypes;
}
GraphModel::DataTypePtr TestGraphContext::GetDataType(AZ::Uuid typeId) const
{
for (GraphModel::DataTypePtr dataType : m_dataTypes)
{
if (dataType->GetTypeUuid() == typeId)
{
return dataType;
}
}
return AZStd::make_shared<GraphModel::DataType>();
}
GraphModel::DataTypePtr TestGraphContext::GetDataType(GraphModel::DataType::Enum typeEnum) const
{
if (0 <= typeEnum && typeEnum < m_dataTypes.size())
{
return m_dataTypes[typeEnum];
}
else
{
return AZStd::make_shared<GraphModel::DataType>();
}
}
GraphModel::ModuleGraphManagerPtr TestGraphContext::GetModuleGraphManager() const
{
return nullptr;
}
// TestNode
void TestNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<TestNode, GraphModel::Node>()
->Version(0)
;
}
}
TestNode::TestNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext)
: GraphModel::Node(graph)
, m_graphContext(graphContext)
{
RegisterSlots();
CreateSlotData();
}
const char* TestNode::GetTitle() const
{
return "TestNode";
}
void TestNode::RegisterSlots()
{
GraphModel::DataTypePtr stringDataType = m_graphContext->GetDataType(TestDataTypeEnum::TestDataTypeEnum_String);
RegisterSlot(GraphModel::SlotDefinition::CreateInputData(
TEST_STRING_INPUT_ID,
"Test Input",
stringDataType,
stringDataType->GetDefaultValue(),
"A test input slot for String data type"));
RegisterSlot(GraphModel::SlotDefinition::CreateOutputData(
TEST_STRING_OUTPUT_ID,
"Test Output",
stringDataType,
"A test output slot for String data type"));
RegisterSlot(GraphModel::SlotDefinition::CreateInputEvent(
TEST_EVENT_INPUT_ID,
"Event In",
"A test input event slot"));
RegisterSlot(GraphModel::SlotDefinition::CreateOutputEvent(
TEST_EVENT_OUTPUT_ID,
"Event Out",
"A test output event slot"));
}
// ExtendableSlotsNode
void ExtendableSlotsNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ExtendableSlotsNode, GraphModel::Node>()
->Version(0)
;
}
}
ExtendableSlotsNode::ExtendableSlotsNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext)
: GraphModel::Node(graph)
, m_graphContext(graphContext)
{
RegisterSlots();
CreateSlotData();
}
const char* ExtendableSlotsNode::GetTitle() const
{
return "ExtendableSlotsNode";
}
void ExtendableSlotsNode::RegisterSlots()
{
GraphModel::DataTypePtr stringDataType = m_graphContext->GetDataType(TestDataTypeEnum::TestDataTypeEnum_String);
GraphModel::ExtendableSlotConfiguration inputDataSlotConfig;
inputDataSlotConfig.m_minimumSlots = 0;
inputDataSlotConfig.m_maximumSlots = 2;
inputDataSlotConfig.m_addButtonLabel = "Add String Input";
inputDataSlotConfig.m_addButtonTooltip = "Add a test string input";
RegisterSlot(GraphModel::SlotDefinition::CreateInputData(
TEST_STRING_INPUT_ID,
"Test Input",
stringDataType,
stringDataType->GetDefaultValue(),
"An extendable input slot for String data type"
, &inputDataSlotConfig));
GraphModel::ExtendableSlotConfiguration outputDataSlotConfig;
outputDataSlotConfig.m_addButtonLabel = "Add String Output";
outputDataSlotConfig.m_addButtonTooltip = "Add a test string output";
RegisterSlot(GraphModel::SlotDefinition::CreateOutputData(
TEST_STRING_OUTPUT_ID,
"Test Output",
stringDataType,
"An extendable output slot for String data type",
&outputDataSlotConfig));
GraphModel::ExtendableSlotConfiguration inputEventSlotConfig;
inputEventSlotConfig.m_addButtonLabel = "Add Input Event";
inputEventSlotConfig.m_addButtonTooltip = "Add a test event input";
RegisterSlot(GraphModel::SlotDefinition::CreateInputEvent(
TEST_EVENT_INPUT_ID,
"Test Input Event",
"An extendable input event"
, &inputEventSlotConfig));
GraphModel::ExtendableSlotConfiguration outputEventSlotConfig;
outputEventSlotConfig.m_addButtonLabel = "Add Output Event";
outputEventSlotConfig.m_addButtonTooltip = "Add a test event output";
outputEventSlotConfig.m_minimumSlots = 3;
outputEventSlotConfig.m_maximumSlots = 4;
RegisterSlot(GraphModel::SlotDefinition::CreateOutputEvent(
TEST_EVENT_OUTPUT_ID,
"Test Output Event",
"An extendable output event"
, &outputEventSlotConfig));
}
// BadNode
void BadNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<BadNode, GraphModel::Node>()
->Version(0)
;
}
}
BadNode::BadNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext)
: GraphModel::Node(graph)
, m_graphContext(graphContext)
{
RegisterSlots();
CreateSlotData();
}
const char* BadNode::GetTitle() const
{
return "BadNode";
}
void BadNode::RegisterSlots()
{
GraphModel::DataTypePtr stringDataType = m_graphContext->GetDataType(TestDataTypeEnum::TestDataTypeEnum_String);
// This will result in an invalid configuration since the minimum is greater than the maximum
GraphModel::ExtendableSlotConfiguration inputDataSlotConfig;
inputDataSlotConfig.m_minimumSlots = 5;
inputDataSlotConfig.m_maximumSlots = 1;
inputDataSlotConfig.m_addButtonLabel = "Add String Input";
inputDataSlotConfig.m_addButtonTooltip = "Add a test string input";
RegisterSlot(GraphModel::SlotDefinition::CreateInputData(
TEST_STRING_INPUT_ID,
"Test Input",
stringDataType,
stringDataType->GetDefaultValue(),
"An extendable input slot for String data type"
, &inputDataSlotConfig));
}
// GraphModelTestEnvironment
void GraphModelTestEnvironment::SetupEnvironment()
{
// Setup a system allocator
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
// Create application and descriptor
m_application = aznew AZ::ComponentApplication;
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_useExistingAllocator = true;
// Create basic system entity
AZ::ComponentApplication::StartupParameters startupParams;
m_systemEntity = m_application->Create(appDesc, startupParams);
m_systemEntity->AddComponent(aznew AZ::MemoryComponent());
m_systemEntity->AddComponent(aznew AZ::AssetManagerComponent());
m_systemEntity->AddComponent(aznew AZ::JobManagerComponent());
m_systemEntity->AddComponent(aznew AZ::StreamerComponent());
m_systemEntity->AddComponent(aznew GraphModel::GraphModelSystemComponent());
// Register descriptor for the GraphModelSystemComponent
m_application->RegisterComponentDescriptor(GraphModel::GraphModelSystemComponent::CreateDescriptor());
// Register descriptors for our mock components
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockNodeComponent::CreateDescriptor());
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockSlotComponent::CreateDescriptor());
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockDataSlotComponent::CreateDescriptor());
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockExecutionSlotComponent::CreateDescriptor());
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockExtenderSlotComponent::CreateDescriptor());
m_application->RegisterComponentDescriptor(MockGraphCanvasServices::MockGraphCanvasSystemComponent::CreateDescriptor());
// Register our mock GraphCanvasSystemComponent
m_systemEntity->AddComponent(aznew MockGraphCanvasServices::MockGraphCanvasSystemComponent());
m_systemEntity->Init();
m_systemEntity->Activate();
}
void GraphModelTestEnvironment::TeardownEnvironment()
{
delete m_application;
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
}
@@ -0,0 +1,151 @@
/**
* 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
// AZ ...
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzTest/AzTest.h>
// GraphModel ...
#include <GraphModel/Model/DataType.h>
#include <GraphModel/Model/IGraphContext.h>
#include <GraphModel/Model/Graph.h>
#include <GraphModel/Model/Node.h>
#include <GraphModel/Model/Slot.h>
// Mock GraphCanvas buses ...
#include <Tests/MockGraphCanvas.h>
namespace GraphModelIntegrationTest
{
static const GraphCanvas::EditorId NODE_GRAPH_TEST_EDITOR_ID = AZ_CRC("GraphModelIntegrationTestEditor", 0x56953df8);
static const char* TEST_STRING_INPUT_ID = "inputString";
static const char* TEST_STRING_OUTPUT_ID = "outputString";
static const char* TEST_EVENT_INPUT_ID = "inputEvent";
static const char* TEST_EVENT_OUTPUT_ID = "outputEvent";
enum TestDataTypeEnum : GraphModel::DataType::Enum
{
TestDataTypeEnum_String,
TestDataTypeEnum_EntityId,
TestDataTypeEnum_Count
};
class TestGraphContext
: public GraphModel::IGraphContext
, public AZStd::enable_shared_from_this<TestGraphContext>
{
public:
TestGraphContext();
virtual ~TestGraphContext() = default;
const char* GetSystemName() const override;
const char* GetModuleFileExtension() const override;
const GraphModel::DataTypeList& GetAllDataTypes() const override;
GraphModel::DataTypePtr GetDataType(AZ::Uuid typeId) const override;
GraphModel::DataTypePtr GetDataType(GraphModel::DataType::Enum typeEnum) const override;
template<typename T>
GraphModel::DataTypePtr GetDataType() const
{
return IGraphContext::GetDataType<T>();
}
GraphModel::ModuleGraphManagerPtr GetModuleGraphManager() const override;
private:
GraphModel::DataTypeList m_dataTypes;
};
class TestNode
: public GraphModel::Node
{
public:
AZ_RTTI(TestNode, "{C51A8CE2-229A-4807-9173-96CF730C6C2B}", Node);
using TestNodePtr = AZStd::shared_ptr<TestNode>;
static void Reflect(AZ::ReflectContext* context);
TestNode() = default;
explicit TestNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext);
const char* GetTitle() const override;
protected:
void RegisterSlots();
AZStd::shared_ptr<TestGraphContext> m_graphContext = nullptr;
};
class ExtendableSlotsNode
: public GraphModel::Node
{
public:
AZ_RTTI(ExtendableSlotsNode, "{5670CFB9-EE42-456D-B1AE-CACC55EC0967}", Node);
using ExtendableSlotsNodePtr = AZStd::shared_ptr<ExtendableSlotsNode>;
static void Reflect(AZ::ReflectContext* context);
ExtendableSlotsNode() = default;
explicit ExtendableSlotsNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext);
const char* GetTitle() const override;
protected:
void RegisterSlots();
AZStd::shared_ptr<TestGraphContext> m_graphContext = nullptr;
};
class BadNode
: public GraphModel::Node
{
public:
AZ_RTTI(BadNode, "{8ECC580C-1AFC-4D66-863D-72AC9971CEC8}", Node);
using BadNodePtr = AZStd::shared_ptr<BadNode>;
static void Reflect(AZ::ReflectContext* context);
BadNode() = default;
explicit BadNode(GraphModel::GraphPtr graph, AZStd::shared_ptr<TestGraphContext> graphContext);
const char* GetTitle() const override;
protected:
void RegisterSlots();
AZStd::shared_ptr<TestGraphContext> m_graphContext = nullptr;
};
class GraphModelTestEnvironment
: public AZ::Test::ITestEnvironment
{
protected:
void SetupEnvironment() override;
void TeardownEnvironment() override;
AZ::ComponentApplication* m_application;
AZ::Entity* m_systemEntity;
};
}
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
Source/GraphModelModule.cpp
)
@@ -0,0 +1,66 @@
#
# 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.
#
set(FILES
Include/GraphModel/GraphModelBus.h
Include/GraphModel/Model/Common.h
Include/GraphModel/Model/Connection.h
Include/GraphModel/Model/DataType.h
Include/GraphModel/Model/Graph.h
Include/GraphModel/Model/GraphElement.h
Include/GraphModel/Model/IGraphContext.h
Include/GraphModel/Model/Node.h
Include/GraphModel/Model/Slot.h
Include/GraphModel/Model/Module/InputOutputNodes.h
Include/GraphModel/Model/Module/ModuleGraphManager.h
Include/GraphModel/Model/Module/ModuleNode.h
Include/GraphModel/Integration/EditorMainWindow.h
Include/GraphModel/Integration/ReadOnlyDataInterface.h
Include/GraphModel/Integration/ThumbnailItem.h
Include/GraphModel/Integration/ThumbnailImageItem.h
Include/GraphModel/Integration/GraphCanvasMetadata.h
Include/GraphModel/Integration/GraphController.h
Include/GraphModel/Integration/GraphControllerManager.h
Include/GraphModel/Integration/Helpers.h
Include/GraphModel/Integration/BooleanDataInterface.h
Include/GraphModel/Integration/FloatDataInterface.h
Include/GraphModel/Integration/IntegerDataInterface.h
Include/GraphModel/Integration/StringDataInterface.h
Include/GraphModel/Integration/VectorDataInterface.inl
Include/GraphModel/Integration/IntegrationBus.h
Include/GraphModel/Integration/NodePalette/InputOutputNodePaletteItem.h
Include/GraphModel/Integration/NodePalette/ModuleNodePaletteItem.h
Include/GraphModel/Integration/NodePalette/StandardNodePaletteItem.h
Include/GraphModel/Integration/NodePalette/GraphCanvasNodePaletteItems.h
Source/GraphModelSystemComponent.cpp
Source/GraphModelSystemComponent.h
Source/Model/Connection.cpp
Source/Model/DataType.cpp
Source/Model/Graph.cpp
Source/Model/GraphElement.cpp
Source/Model/Node.cpp
Source/Model/Slot.cpp
Source/Model/Module/InputOutputNodes.cpp
Source/Model/Module/ModuleGraphManager.cpp
Source/Model/Module/ModuleNode.cpp
Source/Integration/EditorMainWindow.cpp
Source/Integration/ReadOnlyDataInterface.cpp
Source/Integration/ThumbnailItem.cpp
Source/Integration/ThumbnailImageItem.cpp
Source/Integration/GraphCanvasMetadata.cpp
Source/Integration/GraphController.cpp
Source/Integration/GraphControllerManager.cpp
Source/Integration/BooleanDataInterface.cpp
Source/Integration/FloatDataInterface.cpp
Source/Integration/IntegerDataInterface.cpp
Source/Integration/StringDataInterface.cpp
Source/Integration/NodePalette/GraphCanvasNodePaletteItems.cpp
)
@@ -0,0 +1,19 @@
#
# 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.
#
set(FILES
Tests/MockGraphCanvas.cpp
Tests/MockGraphCanvas.h
Tests/TestEnvironment.cpp
Tests/TestEnvironment.h
Tests/GraphModelIntegrationTest.cpp
Tests/GraphModelPythonBindingsTest.cpp
)