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
@@ -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