Skip to content

Commit

Permalink
additional changes toward an extreme visual graph editor appropach
Browse files Browse the repository at this point in the history
  • Loading branch information
tbg10101 committed Jul 16, 2023
1 parent e79a17c commit 17a4324
Show file tree
Hide file tree
Showing 57 changed files with 1,002 additions and 1,109 deletions.
3 changes: 3 additions & 0 deletions Editor.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Editor/Scripts.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions Editor/Scripts/10101Software.DOTS.HybridSimulation.Editor.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "10101Software.DOTS.HybridSimulation.Editor",
"rootNamespace": "Software10101.DOTS.Editor",
"references": [
"GUID:852fdcd981c08e249a237d901a75e290",
"GUID:e0cd26848372d4e5c891c569017e11f1"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Editor/Scripts/Drawers.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions Editor/Scripts/Drawers/GraphSystemGroupDataDrawer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Software10101.DOTS.Editor.GraphEditor;
using Software10101.DOTS.MonoBehaviours;
using UnityEditor;
using UnityEngine.UIElements;

namespace Software10101.DOTS.Editor.Drawers {
[CustomPropertyDrawer(typeof(WorldBehaviour.GraphSystemGroupData))]
public class GraphSystemGroupDataDrawer : PropertyDrawer {
public override VisualElement CreatePropertyGUI(SerializedProperty property) {
// Create property container element.
VisualElement container = new();

// Create property fields.
Button editButton = new(() => {
GraphSystemGroupEditorWindow window = EditorWindow.GetWindow<GraphSystemGroupEditorWindow>();

WorldBehaviour wb = (WorldBehaviour)property.serializedObject.targetObject;
window.Initialize(wb, property.name);
}) {
text = $"Edit {property.displayName}"
};

// Add fields to the container.
container.Add(editButton);

return container;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Editor/Scripts/GraphEditor.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

292 changes: 292 additions & 0 deletions Editor/Scripts/GraphEditor/GraphSystemGroupEditorWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Software10101.DOTS.MonoBehaviours;
using Unity.Collections;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;

namespace Software10101.DOTS.Editor.GraphEditor {
public class GraphSystemGroupEditorWindow : EditorWindow {
private Object _serializedObjectPrev;
private bool _worldBehaviourPrevExist;
private string _propertyNamePrev;

private Object _serializedObject;
private string _propertyName;

private Toolbar _toolbar;
private Label _loadedLabel;

private GraphSystemGroupGraphView _graphView;

private readonly List<string> _addNodeChoices = new();

private bool _populateNodeCreationChoices = false;

private void CreateGUI() {
titleContent = new GUIContent("Graph System Group");

_graphView = new GraphSystemGroupGraphView {
name = "World Name > System Name"
};
_graphView.StretchToParentSize();
_graphView.graphViewChanged += change => {
if (change.elementsToRemove != null) {
_populateNodeCreationChoices = true;
}

return change;
};
rootVisualElement.Add(_graphView);

Node selectGraphNode = new() {
title = "Open a graph system group to use the editor."
};

selectGraphNode.SetPosition(new Rect(0, 20, 150, 100));
selectGraphNode.RefreshExpandedState();
selectGraphNode.RefreshPorts();

_graphView.AddElement(selectGraphNode);
_graphView.UpdateViewTransform(Vector3.zero, Vector3.one);
_graphView.SetEnabled(false);

_toolbar = new();

PopulateNodeCreationChoices();
DropdownField nodeCreateDropDown = new("Create Node", _addNodeChoices, -1);
nodeCreateDropDown.RegisterValueChangedCallback(evt => {
if (string.IsNullOrEmpty(evt.newValue)) {
return;
}

SystemTypeReference systemReference = AssetDatabase.LoadAssetAtPath<SystemTypeReference>(evt.newValue);
_graphView.AddSystemNode(systemReference.GetInstanceID());
nodeCreateDropDown.index = -1;
PopulateNodeCreationChoices();
});
_toolbar.Add(nodeCreateDropDown);

_loadedLabel = new Label("");
_toolbar.Add(_loadedLabel);

Button saveButton = new(Save) {
text = "Save"
};
_toolbar.Add(saveButton);

rootVisualElement.Add(_toolbar);
_toolbar.SetEnabled(false);
}

private void OnFocus() {
PopulateNodeCreationChoices();
}

private void PopulateNodeCreationChoices() {
if (_graphView == null) {
return;
}

_addNodeChoices.Clear();

int[] existingInstanceIds = _graphView.nodes
.Where(node => node is SystemNode systemNode && systemNode.InstanceId.HasValue)
// ReSharper disable once PossibleInvalidOperationException // HasValue check is on the line above
.Select(node => ((SystemNode)node).InstanceId.Value)
.ToArray();
NativeArray<int> existingInstanceIdsNative = new(existingInstanceIds.Length, Allocator.Persistent);
for (int i = 0; i < existingInstanceIds.Length; i++) {
int existingInstanceId = existingInstanceIds[i];
existingInstanceIdsNative[i] = existingInstanceId;
}

NativeArray<GUID> existingNodeGuidsNative = new(existingInstanceIds.Length, Allocator.Persistent);

AssetDatabase.InstanceIDsToGUIDs(existingInstanceIdsNative, existingNodeGuidsNative);

existingInstanceIdsNative.Dispose();

HashSet<string> existingNodeGuids = new();
for (int i = 0; i < existingNodeGuidsNative.Length; i++) {
GUID existingGuid = existingNodeGuidsNative[i];
existingNodeGuids.Add(existingGuid.ToString());
}

_addNodeChoices.AddRange(
AssetDatabase.FindAssets($"t:{nameof(SystemTypeReference)}")
.Where(guid => !existingNodeGuids.Contains(guid))
.Select(AssetDatabase.GUIDToAssetPath)
);

existingNodeGuidsNative.Dispose();
}

private void OnGUI() {
if (_graphView == null) {
return;
}

if (_populateNodeCreationChoices) {
_populateNodeCreationChoices = false;
PopulateNodeCreationChoices();
}

if (EditorPrefs.HasKey("graph_editing_world_instance")) {
int instanceId = EditorPrefs.GetInt("graph_editing_world_instance");
_serializedObject = (WorldBehaviour)EditorUtility.InstanceIDToObject(instanceId);

if (_serializedObject) {
if (EditorPrefs.HasKey("graph_editing_save_callback")) {
_propertyName = EditorPrefs.GetString("graph_editing_save_callback");
}
}
}

if (_serializedObjectPrev != _serializedObject || _propertyNamePrev != _propertyName || _worldBehaviourPrevExist != _serializedObject) {
_graphView.DeleteElements(_graphView.graphElements);

if (!_serializedObject) {
_toolbar.SetEnabled(false);

Node selectGraphNode = new() {
title = "Open a graph system group to use the editor."
};

selectGraphNode.SetPosition(new Rect(0, 20, 150, 100));
selectGraphNode.RefreshExpandedState();
selectGraphNode.RefreshPorts();

_graphView.AddElement(selectGraphNode);
_graphView.UpdateViewTransform(Vector3.zero, Vector3.one);
_graphView.SetEnabled(false);
} else {
_toolbar.SetEnabled(true);
_graphView.SetEnabled(true);

Load();
}
} else if (_serializedObject && _graphView.enabledSelf == false) {
_graphView.DeleteElements(_graphView.graphElements);

_toolbar.SetEnabled(true);
_graphView.SetEnabled(true);

Load();
}

if (_serializedObject) {
string dirtyFlag = _graphView.Dirty ? " *" : "";
_loadedLabel.text = $"{_serializedObject.name} - {_propertyName}{dirtyFlag}";
} else {
_loadedLabel.text = "";
}

_worldBehaviourPrevExist = _serializedObject;
_serializedObjectPrev = _serializedObject;
_propertyNamePrev = _propertyName;
}

public void Initialize(Object serializedObject, string propertyName) {
_serializedObject = serializedObject;
_propertyName = propertyName;

EditorPrefs.SetInt("graph_editing_world_instance", serializedObject.GetInstanceID());
EditorPrefs.SetString("graph_editing_save_callback", propertyName);
}

private void OnDisable() {
rootVisualElement.Clear();
_serializedObject = null;
_propertyName = null;
}

private void Save() {
SerializedObject so = new(_serializedObject);

WorldBehaviour.GraphSystemGroupData graphData = WorldBehaviour.GraphSystemGroupData.CreateEmpty();

HashSet<int> rootDependencies = new();
Dictionary<int, HashSet<int>> dependencies = new();

_graphView.edges.ForEach(edge => {
int? dependent = ((SystemNode)edge.input.node).InstanceId;
// ReSharper disable once PossibleInvalidOperationException // not possible - root is never a dependency
int dependency = ((SystemNode)edge.output.node).InstanceId.Value;

HashSet<int> existingDependencies;

if (!dependent.HasValue) {
existingDependencies = rootDependencies;
} else if (!dependencies.TryGetValue(dependent.Value, out existingDependencies)) {
existingDependencies = new HashSet<int>();
dependencies[dependent.Value] = existingDependencies;
}

existingDependencies.Add(dependency);
});

graphData.Nodes = _graphView.nodes
.OfType<SystemNode>()
.Select(systemNode => new WorldBehaviour.GraphSystemGroupData.SystemNodeData(
systemNode.InstanceId.HasValue
? (SystemTypeReference)EditorUtility.InstanceIDToObject(systemNode.InstanceId.Value)
: null,
systemNode.GetPosition().position,
systemNode.InstanceId.HasValue
? dependencies.ContainsKey(systemNode.InstanceId.Value)
? dependencies[systemNode.InstanceId.Value]?
.Select(instanceId => (SystemTypeReference)EditorUtility.InstanceIDToObject(instanceId))
.ToArray() ?? Array.Empty<SystemTypeReference>()
: Array.Empty<SystemTypeReference>()
: rootDependencies
.Select(instanceId => (SystemTypeReference)EditorUtility.InstanceIDToObject(instanceId))
.ToArray()))
.ToArray();

so.FindProperty(_propertyName).boxedValue = graphData;
so.ApplyModifiedProperties();

_graphView.Dirty = false;
}

private void Load() {
SerializedObject so = new(_serializedObject);
object graphDataRaw = so.FindProperty(_propertyName).boxedValue;
WorldBehaviour.GraphSystemGroupData graphData = (WorldBehaviour.GraphSystemGroupData)graphDataRaw;

SystemNode rootNode = null;
Dictionary<int, SystemNode> nodesByInstanceId = new();

foreach (WorldBehaviour.GraphSystemGroupData.SystemNodeData systemNodeData in graphData.Nodes) {
if (systemNodeData.SystemReference) {
int instanceId = systemNodeData.SystemReference.GetInstanceID();

SystemNode node = _graphView.AddSystemNode(instanceId, systemNodeData.NodePosition);
nodesByInstanceId[instanceId] = node;
} else {
rootNode = _graphView.AddSystemNode(null, systemNodeData.NodePosition);
}
}

foreach (WorldBehaviour.GraphSystemGroupData.SystemNodeData systemNodeData in graphData.Nodes) {
if (systemNodeData.SystemReference) {
SystemNode dependent = nodesByInstanceId[systemNodeData.SystemReference.GetInstanceID()];

foreach (SystemTypeReference systemTypeReference in systemNodeData.Dependencies) {
_graphView.AddSystemDependency(dependent, nodesByInstanceId[systemTypeReference.GetInstanceID()]);
}
} else {
foreach (SystemTypeReference systemTypeReference in systemNodeData.Dependencies) {
_graphView.AddSystemDependency(rootNode, nodesByInstanceId[systemTypeReference.GetInstanceID()]);
}
}
}
}
}
}
Loading

0 comments on commit 17a4324

Please sign in to comment.