3605 lines
97 KiB
C#
3605 lines
97 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BlockSpace.Voxels;
|
|
using BlockSpace.Worlds;
|
|
using UnityEngine;
|
|
using UnityEngine.XR;
|
|
|
|
[DisallowMultipleComponent]
|
|
public sealed class NodeGraphManager : MonoBehaviour
|
|
{
|
|
private sealed class CircuitUndoStep
|
|
{
|
|
public readonly List<ICircuitMutation> undo = new List<ICircuitMutation>();
|
|
|
|
public readonly List<ICircuitMutation> redo = new List<ICircuitMutation>();
|
|
}
|
|
|
|
private interface ICircuitMutation
|
|
{
|
|
void Apply(NodeGraphManager manager, bool broadcast);
|
|
}
|
|
|
|
private sealed class UpsertNodeMutation : ICircuitMutation
|
|
{
|
|
private readonly WorldGroupsCodec.WorldNodeRecord record;
|
|
|
|
public UpsertNodeMutation(WorldGroupsCodec.WorldNodeRecord record)
|
|
{
|
|
this.record = record;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
manager.ApplyNodeRecord(record, broadcast);
|
|
}
|
|
}
|
|
|
|
private sealed class DeleteNodeMutation : ICircuitMutation
|
|
{
|
|
private readonly int nodeId;
|
|
|
|
public DeleteNodeMutation(int nodeId)
|
|
{
|
|
this.nodeId = nodeId;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
if (manager.nodesById.TryGetValue(nodeId, out var value) && value != null)
|
|
{
|
|
manager.DeleteNodeInternal(value, broadcast, recordUndo: false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class ConnectWireMutation : ICircuitMutation
|
|
{
|
|
private readonly WorldGroupsCodec.WorldWireRecord record;
|
|
|
|
public ConnectWireMutation(WorldGroupsCodec.WorldWireRecord record)
|
|
{
|
|
this.record = record;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
manager.ApplyWireRecord(record, broadcast);
|
|
}
|
|
}
|
|
|
|
private sealed class DisconnectWireMutation : ICircuitMutation
|
|
{
|
|
private readonly WorldGroupsCodec.WorldWireRecord record;
|
|
|
|
public DisconnectWireMutation(WorldGroupsCodec.WorldWireRecord record)
|
|
{
|
|
this.record = record;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
Node value;
|
|
Node value2;
|
|
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId) || WorldGroupsCodec.TryGetGroupSavedWireIndex(record, out groupId))
|
|
{
|
|
new DisconnectInputMutation(record.inputNodeId, record.inputPortName).Apply(manager, broadcast);
|
|
}
|
|
else if (manager.nodesById.TryGetValue(record.outputNodeId, out value) && manager.nodesById.TryGetValue(record.inputNodeId, out value2) && value != null && value2 != null)
|
|
{
|
|
manager.Disconnect(value, record.outputPortName, value2, record.inputPortName, broadcast);
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class DisconnectInputMutation : ICircuitMutation
|
|
{
|
|
private readonly int inputNodeId;
|
|
|
|
private readonly string inputPortName;
|
|
|
|
public DisconnectInputMutation(int nodeId, string portName)
|
|
{
|
|
inputNodeId = nodeId;
|
|
inputPortName = portName;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
if (!manager.nodesById.TryGetValue(inputNodeId, out var value) || !(value != null) || !value.TryGetPort(inputPortName, PortDirection.Input, out var port))
|
|
{
|
|
return;
|
|
}
|
|
bool num = port.SourceGroupId > 0;
|
|
manager.DisconnectInputPortInternal(port, broadcast);
|
|
if (num)
|
|
{
|
|
manager.ClearGroupInputSourceInternal(port, clearValue: true, pulsePort: false);
|
|
if (broadcast && !manager.suppressNetworkBroadcast)
|
|
{
|
|
manager.ResolveNetworkSync()?.BroadcastDisconnectInput(inputNodeId, inputPortName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class DisconnectOutputMutation : ICircuitMutation
|
|
{
|
|
private readonly int outputNodeId;
|
|
|
|
private readonly string outputPortName;
|
|
|
|
public DisconnectOutputMutation(int nodeId, string portName)
|
|
{
|
|
outputNodeId = nodeId;
|
|
outputPortName = portName;
|
|
}
|
|
|
|
public void Apply(NodeGraphManager manager, bool broadcast)
|
|
{
|
|
if (manager.nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, out var port))
|
|
{
|
|
manager.DisconnectOutputPortInternal(port, broadcast);
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class WireVisual
|
|
{
|
|
public string key;
|
|
|
|
public int sourceGroupId;
|
|
|
|
public NodePort output;
|
|
|
|
public NodePort input;
|
|
|
|
public PortKind Kind;
|
|
|
|
public LineRenderer renderer;
|
|
|
|
public float flashUntil;
|
|
}
|
|
|
|
private sealed class NodeGrabState
|
|
{
|
|
public Node node;
|
|
|
|
public Vector3 startPosition;
|
|
|
|
public Quaternion startRotation;
|
|
|
|
public float lastPreviewSentAt = float.NegativeInfinity;
|
|
}
|
|
|
|
private sealed class PendingWirePress
|
|
{
|
|
public NodePort startPort;
|
|
|
|
public VoxelGroup startGroup;
|
|
|
|
public Vector3 startWorldPosition;
|
|
|
|
public float pressedAt;
|
|
}
|
|
|
|
private sealed class ActiveValueEditContext
|
|
{
|
|
public NodePort port;
|
|
|
|
public string buffer;
|
|
|
|
public string originalBuffer;
|
|
|
|
public float startedAt;
|
|
}
|
|
|
|
private sealed class ActiveWireContext
|
|
{
|
|
public NodePort startPort;
|
|
|
|
public VoxelGroup startGroup;
|
|
|
|
public Vector3 startWorldPosition;
|
|
|
|
public PortKind Kind;
|
|
}
|
|
|
|
private const string NodePrefabResourcePath = "Node";
|
|
|
|
private static readonly Color NoneWireColor = NodeWirePalette.None;
|
|
|
|
private static readonly Color BoolWireColor = NodeWirePalette.Bool;
|
|
|
|
private static readonly Color NumberWireColor = NodeWirePalette.Number;
|
|
|
|
private static readonly Color TextWireColor = NodeWirePalette.Text;
|
|
|
|
private static readonly Color PlayerWireColor = NodeWirePalette.Player;
|
|
|
|
private static readonly Color GroupWireColor = NodeWirePalette.Group;
|
|
|
|
private static readonly Color RunWireColor = NodeWirePalette.Run;
|
|
|
|
private static readonly Color WireFlashColor = new Color(1f, 1f, 0.7f, 1f);
|
|
|
|
private static readonly Color AimRayColor = new Color(0.55f, 0.9f, 1f, 0.72f);
|
|
|
|
private static readonly Color PreviewRunColor = new Color(1f, 0.55f, 0.28f, 0.92f);
|
|
|
|
[SerializeField]
|
|
private VoxelGroupManager groupManager;
|
|
|
|
[SerializeField]
|
|
private VRVoxelBrush brush;
|
|
|
|
[SerializeField]
|
|
private Transform trackingOrigin;
|
|
|
|
[SerializeField]
|
|
private Transform leftHandAnchor;
|
|
|
|
[SerializeField]
|
|
private Transform rightHandAnchor;
|
|
|
|
[SerializeField]
|
|
private List<NodeDefinition> preloadedDefinitions = new List<NodeDefinition>();
|
|
|
|
[SerializeField]
|
|
private float portPickRadius = 0.08f;
|
|
|
|
[SerializeField]
|
|
private float nodePickRadius = 0.16f;
|
|
|
|
[SerializeField]
|
|
private float wireRayDistance = 2f;
|
|
|
|
[SerializeField]
|
|
private float wireTriggerThreshold = 0.7f;
|
|
|
|
[SerializeField]
|
|
private float rightGripThreshold = 0.85f;
|
|
|
|
[SerializeField]
|
|
private float quickTapThreshold = 0.16f;
|
|
|
|
[SerializeField]
|
|
private float movePreviewSendInterval = 0.05f;
|
|
|
|
[SerializeField]
|
|
private float nodeMovePositionSnap = 0.05f;
|
|
|
|
[SerializeField]
|
|
private float nodeMoveRotationSnapDegrees = 90f;
|
|
|
|
[SerializeField]
|
|
private float undoChordWindow = 0.12f;
|
|
|
|
[SerializeField]
|
|
private float undoTriggerThreshold = 0.7f;
|
|
|
|
[SerializeField]
|
|
private LayerMask wireRaycastMask = -5;
|
|
|
|
[SerializeField]
|
|
private KeyCode keyboardSelectKey = KeyCode.G;
|
|
|
|
[SerializeField]
|
|
private KeyCode keyboardDuplicateKey = KeyCode.D;
|
|
|
|
[SerializeField]
|
|
private KeyCode keyboardDeleteKey = KeyCode.Delete;
|
|
|
|
[SerializeField]
|
|
private KeyCode keyboardWireKey = KeyCode.Space;
|
|
|
|
[SerializeField]
|
|
private int maxInlineValueChars = 64;
|
|
|
|
private readonly Dictionary<int, Node> nodesById = new Dictionary<int, Node>();
|
|
|
|
private readonly Dictionary<string, NodeDefinition> definitionsByName = new Dictionary<string, NodeDefinition>(StringComparer.Ordinal);
|
|
|
|
private readonly HashSet<Node> selectedNodes = new HashSet<Node>();
|
|
|
|
private readonly List<Node> selectionBuffer = new List<Node>();
|
|
|
|
private readonly List<NodeGrabState> grabbedNodes = new List<NodeGrabState>();
|
|
|
|
private readonly Dictionary<string, WireVisual> wireVisuals = new Dictionary<string, WireVisual>(StringComparer.Ordinal);
|
|
|
|
private readonly List<string> wireCleanupBuffer = new List<string>();
|
|
|
|
private readonly List<WorldGroupsCodec.WorldWireRecord> pendingWireRecords = new List<WorldGroupsCodec.WorldWireRecord>();
|
|
|
|
private readonly List<VoxelGroup> groupPickBuffer = new List<VoxelGroup>();
|
|
|
|
private readonly Stack<CircuitUndoStep> undoStack = new Stack<CircuitUndoStep>();
|
|
|
|
private readonly Stack<CircuitUndoStep> redoStack = new Stack<CircuitUndoStep>();
|
|
|
|
[SerializeField]
|
|
private int maxUndoSteps = 64;
|
|
|
|
private NodeGraphPhotonSync networkSync;
|
|
|
|
private InputDevice leftHandDevice;
|
|
|
|
private InputDevice rightHandDevice;
|
|
|
|
private GameObject nodePrefab;
|
|
|
|
private Material lineMaterial;
|
|
|
|
private LineRenderer previewWireRenderer;
|
|
|
|
private LineRenderer aimRayRenderer;
|
|
|
|
private NodePort hoveredPort;
|
|
|
|
private Node hoveredNode;
|
|
|
|
private VoxelGroup hoveredGroup;
|
|
|
|
private PendingWirePress pendingWirePress;
|
|
|
|
private ActiveWireContext activeWire;
|
|
|
|
private ActiveValueEditContext activeValueEdit;
|
|
|
|
private bool wireInputPrevious;
|
|
|
|
private bool selectInputPrevious;
|
|
|
|
private bool duplicateInputPrevious;
|
|
|
|
private bool nodeGrabActive;
|
|
|
|
private bool nodeSelectLatchedThisHold;
|
|
|
|
private bool editVisibilityApplied = true;
|
|
|
|
private bool suppressNetworkBroadcast;
|
|
|
|
private bool suppressUndoRecording;
|
|
|
|
private bool undoChordActive;
|
|
|
|
private bool suppressUndoUntilNeutral;
|
|
|
|
private bool undoGripHeldPrevious;
|
|
|
|
private bool undoTriggerHeldPrevious;
|
|
|
|
private float lastUndoGripPressTime = float.NegativeInfinity;
|
|
|
|
private float lastUndoTriggerPressTime = float.NegativeInfinity;
|
|
|
|
private int nextNodeId = 1;
|
|
|
|
private Vector3 grabStartReferencePosition;
|
|
|
|
private Quaternion grabStartReferenceRotation;
|
|
|
|
private Vector3 grabStartSelectionCenter;
|
|
|
|
private Vector3 grabStartSelectionOffset;
|
|
|
|
public static NodeGraphManager Instance { get; private set; }
|
|
|
|
public bool HasSelection => selectedNodes.Count > 0;
|
|
|
|
public bool BlocksRightHandActions => nodeGrabActive;
|
|
|
|
public bool CanUndo => undoStack.Count > 0;
|
|
|
|
public bool CanRedo => redoStack.Count > 0;
|
|
|
|
public event Action<int> PlayerJoined;
|
|
|
|
public event Action<int> PlayerLeft;
|
|
|
|
public bool UndoLastCircuitEdit()
|
|
{
|
|
if (!CanEdit() || undoStack.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
CircuitUndoStep circuitUndoStep = undoStack.Pop();
|
|
redoStack.Push(circuitUndoStep);
|
|
bool flag = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
for (int i = 0; i < circuitUndoStep.undo.Count; i++)
|
|
{
|
|
circuitUndoStep.undo[i]?.Apply(this, broadcast: true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressUndoRecording = flag;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool RedoLastCircuitEdit()
|
|
{
|
|
if (!CanEdit() || redoStack.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
CircuitUndoStep circuitUndoStep = redoStack.Pop();
|
|
undoStack.Push(circuitUndoStep);
|
|
bool flag = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
for (int i = 0; i < circuitUndoStep.redo.Count; i++)
|
|
{
|
|
circuitUndoStep.redo[i]?.Apply(this, broadcast: true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressUndoRecording = flag;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void PushUndoStep(CircuitUndoStep step)
|
|
{
|
|
if (step == null || suppressUndoRecording)
|
|
{
|
|
return;
|
|
}
|
|
undoStack.Push(step);
|
|
redoStack.Clear();
|
|
int num = Mathf.Clamp(maxUndoSteps, 0, 256);
|
|
if (num <= 0)
|
|
{
|
|
undoStack.Clear();
|
|
redoStack.Clear();
|
|
}
|
|
else if (undoStack.Count > num)
|
|
{
|
|
CircuitUndoStep[] array = undoStack.ToArray();
|
|
undoStack.Clear();
|
|
for (int num2 = Mathf.Min(num - 1, array.Length - 1); num2 >= 0; num2--)
|
|
{
|
|
undoStack.Push(array[num2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SelectNodesWithinSphere(Vector3 worldCenter, float radius, bool additive)
|
|
{
|
|
if (!CanEdit())
|
|
{
|
|
return 0;
|
|
}
|
|
if (!additive)
|
|
{
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
}
|
|
float num = Mathf.Max(0.0001f, radius) * Mathf.Max(0.0001f, radius);
|
|
int num2 = 0;
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (!(value == null) && !((value.transform.position - worldCenter).sqrMagnitude > num))
|
|
{
|
|
bool num3 = selectedNodes.Contains(value);
|
|
AddSelection(value);
|
|
if (!num3)
|
|
{
|
|
num2++;
|
|
}
|
|
}
|
|
}
|
|
return num2;
|
|
}
|
|
|
|
public int DeleteNodesWithinSphere(Vector3 worldCenter, float radius, bool broadcast)
|
|
{
|
|
if (!CanEdit())
|
|
{
|
|
return 0;
|
|
}
|
|
float num = Mathf.Max(0.0001f, radius) * Mathf.Max(0.0001f, radius);
|
|
selectionBuffer.Clear();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (!(value == null) && (value.transform.position - worldCenter).sqrMagnitude <= num)
|
|
{
|
|
selectionBuffer.Add(value);
|
|
}
|
|
}
|
|
int num2 = 0;
|
|
CircuitUndoStep circuitUndoStep = null;
|
|
if (!suppressUndoRecording)
|
|
{
|
|
circuitUndoStep = new CircuitUndoStep();
|
|
}
|
|
for (int i = 0; i < selectionBuffer.Count; i++)
|
|
{
|
|
Node node = selectionBuffer[i];
|
|
if (node == null || !nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
continue;
|
|
}
|
|
if (circuitUndoStep != null)
|
|
{
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
|
|
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
|
|
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
|
|
for (int j = 0; j < wiresTouchingNode.Count; j++)
|
|
{
|
|
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[j]));
|
|
}
|
|
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
|
|
}
|
|
DeleteNodeInternal(node, broadcast, recordUndo: false);
|
|
num2++;
|
|
}
|
|
if (circuitUndoStep != null && num2 > 0)
|
|
{
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
selectionBuffer.Clear();
|
|
return num2;
|
|
}
|
|
|
|
private List<WorldGroupsCodec.WorldWireRecord> GetWiresTouchingNode(int nodeId)
|
|
{
|
|
List<WorldGroupsCodec.WorldWireRecord> list = BuildWorldWireRecords();
|
|
List<WorldGroupsCodec.WorldWireRecord> list2 = new List<WorldGroupsCodec.WorldWireRecord>();
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
WorldGroupsCodec.WorldWireRecord item = list[i];
|
|
if (item.inputNodeId == nodeId || item.outputNodeId == nodeId)
|
|
{
|
|
list2.Add(item);
|
|
}
|
|
}
|
|
return list2;
|
|
}
|
|
|
|
private static List<WorldGroupsCodec.WorldWireRecord> GetExistingWiresForPort(NodePort port)
|
|
{
|
|
List<WorldGroupsCodec.WorldWireRecord> list = new List<WorldGroupsCodec.WorldWireRecord>();
|
|
if (port == null || port.Owner == null)
|
|
{
|
|
return list;
|
|
}
|
|
int nodeId = port.Owner.NodeId;
|
|
if (port.Direction == PortDirection.Input)
|
|
{
|
|
if (port.Kind == PortKind.Run)
|
|
{
|
|
for (int i = 0; i < port.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = port.Connections[i];
|
|
if (!(nodePort?.Owner == null))
|
|
{
|
|
list.Add(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = nodePort.Owner.NodeId,
|
|
outputPortName = nodePort.Name,
|
|
inputNodeId = nodeId,
|
|
inputPortName = port.Name
|
|
});
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
if (port.SourceGroupId > 0)
|
|
{
|
|
list.Add(WorldGroupsCodec.CreateGroupRuntimeWireRecord(port.SourceGroupId, nodeId, port.Name));
|
|
}
|
|
else if (port.IncomingConnection != null && port.IncomingConnection.Owner != null)
|
|
{
|
|
list.Add(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = port.IncomingConnection.Owner.NodeId,
|
|
outputPortName = port.IncomingConnection.Name,
|
|
inputNodeId = nodeId,
|
|
inputPortName = port.Name
|
|
});
|
|
}
|
|
return list;
|
|
}
|
|
for (int j = 0; j < port.Connections.Count; j++)
|
|
{
|
|
NodePort nodePort2 = port.Connections[j];
|
|
if (!(nodePort2?.Owner == null))
|
|
{
|
|
list.Add(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = nodeId,
|
|
outputPortName = port.Name,
|
|
inputNodeId = nodePort2.Owner.NodeId,
|
|
inputPortName = nodePort2.Name
|
|
});
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
internal void NotifyPlayerJoined(int actorNumber)
|
|
{
|
|
if (actorNumber > 0)
|
|
{
|
|
this.PlayerJoined?.Invoke(actorNumber);
|
|
}
|
|
}
|
|
|
|
internal void NotifyPlayerLeft(int actorNumber)
|
|
{
|
|
if (actorNumber > 0)
|
|
{
|
|
this.PlayerLeft?.Invoke(actorNumber);
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null && Instance != this)
|
|
{
|
|
Debug.LogError("Multiple NodeGraphManager instances found.", this);
|
|
return;
|
|
}
|
|
Instance = this;
|
|
ResolveReferences();
|
|
CachePreloadedDefinitions();
|
|
CacheLoadedDefinitions();
|
|
EnsureLineResources();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
RefreshDevices();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
RefreshSceneNodesFromHierarchy();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (Instance == this)
|
|
{
|
|
Instance = null;
|
|
}
|
|
if (lineMaterial != null)
|
|
{
|
|
UnityEngine.Object.Destroy(lineMaterial);
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
ResolveReferences();
|
|
RefreshDevices();
|
|
CleanupDestroyedNodes();
|
|
TickNodeBehaviors();
|
|
bool flag = CanEdit();
|
|
ApplyEditVisibility(flag);
|
|
if (!flag)
|
|
{
|
|
CancelEditingInteractions(clearSelection: true);
|
|
return;
|
|
}
|
|
UpdateHoverTargets();
|
|
if (!HandleUndoChord() && !HandleInlineValueEdit())
|
|
{
|
|
HandleSelectionInput();
|
|
HandleDuplicateInput();
|
|
HandleDeleteInput();
|
|
HandleGrabInput();
|
|
HandleWireInput();
|
|
}
|
|
}
|
|
|
|
private bool HandleInlineValueEdit()
|
|
{
|
|
if (UseXRInput())
|
|
{
|
|
activeValueEdit = null;
|
|
return false;
|
|
}
|
|
if (activeValueEdit != null)
|
|
{
|
|
if (activeValueEdit.port == null || activeValueEdit.port.Owner == null)
|
|
{
|
|
activeValueEdit = null;
|
|
return false;
|
|
}
|
|
if (Input.GetKeyDown(KeyCode.Escape))
|
|
{
|
|
RestoreInlineEditLabel(activeValueEdit.port);
|
|
activeValueEdit = null;
|
|
return true;
|
|
}
|
|
string inputString = Input.inputString;
|
|
if (!string.IsNullOrEmpty(inputString))
|
|
{
|
|
for (int i = 0; i < inputString.Length; i++)
|
|
{
|
|
char c = inputString[i];
|
|
switch (c)
|
|
{
|
|
case '\b':
|
|
if (!string.IsNullOrEmpty(activeValueEdit.buffer))
|
|
{
|
|
activeValueEdit.buffer = activeValueEdit.buffer.Substring(0, activeValueEdit.buffer.Length - 1);
|
|
}
|
|
continue;
|
|
case '\n':
|
|
case '\r':
|
|
CommitInlineValueEdit();
|
|
return true;
|
|
}
|
|
if (!char.IsControl(c))
|
|
{
|
|
if (activeValueEdit.buffer == null)
|
|
{
|
|
activeValueEdit.buffer = string.Empty;
|
|
}
|
|
if (activeValueEdit.buffer.Length < Mathf.Clamp(maxInlineValueChars, 1, 256))
|
|
{
|
|
activeValueEdit.buffer += c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UpdateInlineEditLabel(activeValueEdit.port, activeValueEdit.buffer);
|
|
return true;
|
|
}
|
|
if ((!Input.GetKeyDown(KeyCode.Return) && !Input.GetKeyDown(KeyCode.KeypadEnter)) || hoveredPort == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (hoveredPort.Direction != PortDirection.Input || hoveredPort.Kind != PortKind.Value)
|
|
{
|
|
return false;
|
|
}
|
|
if (hoveredPort.IncomingConnection != null || hoveredPort.SourceGroupId > 0)
|
|
{
|
|
return false;
|
|
}
|
|
activeValueEdit = new ActiveValueEditContext
|
|
{
|
|
port = hoveredPort,
|
|
originalBuffer = hoveredPort.Value.AsText(),
|
|
buffer = hoveredPort.Value.AsText(),
|
|
startedAt = Time.unscaledTime
|
|
};
|
|
UpdateInlineEditLabel(hoveredPort, activeValueEdit.buffer);
|
|
return true;
|
|
}
|
|
|
|
private void UpdateInlineEditLabel(NodePort port, string buffer)
|
|
{
|
|
if (port != null && !(port.LabelText == null))
|
|
{
|
|
port.LabelText.text = port.Name + ": " + buffer + "_";
|
|
}
|
|
}
|
|
|
|
private void RestoreInlineEditLabel(NodePort port)
|
|
{
|
|
if (port != null && !(port.Owner == null))
|
|
{
|
|
port.Owner.PulsePort(port);
|
|
}
|
|
}
|
|
|
|
private void CommitInlineValueEdit()
|
|
{
|
|
if (activeValueEdit == null || activeValueEdit.port == null || activeValueEdit.port.Owner == null)
|
|
{
|
|
activeValueEdit = null;
|
|
return;
|
|
}
|
|
NodePort port = activeValueEdit.port;
|
|
Node owner = port.Owner;
|
|
string value = activeValueEdit.buffer ?? string.Empty;
|
|
activeValueEdit = null;
|
|
RestoreInlineEditLabel(port);
|
|
AssignInputValue(owner, port, BSValue.FromText(value), broadcast: true);
|
|
}
|
|
|
|
private bool HandleUndoChord()
|
|
{
|
|
if (!UseXRInput())
|
|
{
|
|
undoChordActive = false;
|
|
suppressUndoUntilNeutral = false;
|
|
undoGripHeldPrevious = false;
|
|
undoTriggerHeldPrevious = false;
|
|
return false;
|
|
}
|
|
bool flag = ReadGrabHeld(rightHandDevice);
|
|
bool flag2 = ReadFloat(rightHandDevice, CommonUsages.trigger) >= undoTriggerThreshold;
|
|
if (!flag && undoGripHeldPrevious)
|
|
{
|
|
lastUndoGripPressTime = float.NegativeInfinity;
|
|
}
|
|
if (!flag2 && undoTriggerHeldPrevious)
|
|
{
|
|
lastUndoTriggerPressTime = float.NegativeInfinity;
|
|
}
|
|
if (flag && !undoGripHeldPrevious)
|
|
{
|
|
lastUndoGripPressTime = Time.unscaledTime;
|
|
}
|
|
if (flag2 && !undoTriggerHeldPrevious)
|
|
{
|
|
lastUndoTriggerPressTime = Time.unscaledTime;
|
|
}
|
|
if (suppressUndoUntilNeutral)
|
|
{
|
|
if (!flag && !flag2)
|
|
{
|
|
suppressUndoUntilNeutral = false;
|
|
undoChordActive = false;
|
|
}
|
|
undoGripHeldPrevious = flag;
|
|
undoTriggerHeldPrevious = flag2;
|
|
return suppressUndoUntilNeutral;
|
|
}
|
|
if (!undoChordActive)
|
|
{
|
|
float num = Mathf.Max(lastUndoGripPressTime, lastUndoTriggerPressTime);
|
|
if (flag && flag2 && num > 0f && Math.Abs(lastUndoGripPressTime - lastUndoTriggerPressTime) <= undoChordWindow && Time.unscaledTime - num <= undoChordWindow)
|
|
{
|
|
if (nodeGrabActive)
|
|
{
|
|
EndNodeGrab(broadcastFinal: true);
|
|
}
|
|
UndoLastCircuitEdit();
|
|
undoChordActive = true;
|
|
suppressUndoUntilNeutral = true;
|
|
}
|
|
}
|
|
else if (!flag || !flag2)
|
|
{
|
|
undoChordActive = false;
|
|
}
|
|
undoGripHeldPrevious = flag;
|
|
undoTriggerHeldPrevious = flag2;
|
|
return suppressUndoUntilNeutral;
|
|
}
|
|
|
|
private void TickNodeBehaviors()
|
|
{
|
|
if (nodesById.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
selectionBuffer.Clear();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value != null)
|
|
{
|
|
selectionBuffer.Add(item.Value);
|
|
}
|
|
}
|
|
for (int i = 0; i < selectionBuffer.Count; i++)
|
|
{
|
|
Node node = selectionBuffer[i];
|
|
if (!(node == null))
|
|
{
|
|
node.TickBehavior();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
UpdateWireVisuals();
|
|
UpdateTransientLines();
|
|
}
|
|
|
|
public void Configure(VoxelGroupManager manager, VRVoxelBrush voxelBrush, Transform origin, Transform leftAnchor, Transform rightAnchor)
|
|
{
|
|
if (manager != null)
|
|
{
|
|
groupManager = manager;
|
|
}
|
|
if (voxelBrush != null)
|
|
{
|
|
brush = voxelBrush;
|
|
}
|
|
if (origin != null)
|
|
{
|
|
trackingOrigin = origin;
|
|
}
|
|
if (leftAnchor != null)
|
|
{
|
|
leftHandAnchor = leftAnchor;
|
|
}
|
|
if (rightAnchor != null)
|
|
{
|
|
rightHandAnchor = rightAnchor;
|
|
}
|
|
}
|
|
|
|
public void RegisterNetworkSync(NodeGraphPhotonSync sync)
|
|
{
|
|
networkSync = sync;
|
|
}
|
|
|
|
public void RegisterNode(Node node)
|
|
{
|
|
if (!(node == null))
|
|
{
|
|
CacheDefinition(node.Definition);
|
|
if (node.NodeId <= 0 || (nodesById.TryGetValue(node.NodeId, out var value) && value != null && value != node))
|
|
{
|
|
node.SetNodeId(AllocateNodeId());
|
|
}
|
|
else
|
|
{
|
|
nextNodeId = Mathf.Max(nextNodeId, node.NodeId + 1);
|
|
}
|
|
node.AssignGraph(this);
|
|
nodesById[node.NodeId] = node;
|
|
node.SetVisibleInEditMode(CanEdit());
|
|
ResolvePendingWireRecords();
|
|
}
|
|
}
|
|
|
|
public List<Node> GetSelectedNodesSnapshot()
|
|
{
|
|
selectionBuffer.Clear();
|
|
foreach (Node selectedNode in selectedNodes)
|
|
{
|
|
if (selectedNode != null)
|
|
{
|
|
selectionBuffer.Add(selectedNode);
|
|
}
|
|
}
|
|
return new List<Node>(selectionBuffer);
|
|
}
|
|
|
|
public List<Node> GetAllNodesSnapshot()
|
|
{
|
|
selectionBuffer.Clear();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value != null)
|
|
{
|
|
selectionBuffer.Add(item.Value);
|
|
}
|
|
}
|
|
return new List<Node>(selectionBuffer);
|
|
}
|
|
|
|
public List<NodeDefinition> GetAllDefinitionsSnapshot()
|
|
{
|
|
CacheLoadedDefinitions();
|
|
List<NodeDefinition> list = new List<NodeDefinition>(definitionsByName.Count);
|
|
foreach (KeyValuePair<string, NodeDefinition> item in definitionsByName)
|
|
{
|
|
if (item.Value != null)
|
|
{
|
|
list.Add(item.Value);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public void ClearSelectionForToolUse()
|
|
{
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
}
|
|
|
|
public void CancelEditingInteractions(bool clearSelection)
|
|
{
|
|
CancelPendingWirePress();
|
|
ClearActiveWire();
|
|
EndNodeGrab(broadcastFinal: true);
|
|
ClearHoverState();
|
|
if (clearSelection)
|
|
{
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
}
|
|
}
|
|
|
|
public bool SelectNode(Node node, bool additive)
|
|
{
|
|
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
return false;
|
|
}
|
|
if (!additive)
|
|
{
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
}
|
|
AddSelection(node);
|
|
return true;
|
|
}
|
|
|
|
public bool MoveNode(Node node, Vector3 position, Quaternion rotation, bool broadcast)
|
|
{
|
|
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
return false;
|
|
}
|
|
node.transform.SetPositionAndRotation(position, rotation);
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool DeleteNode(Node node, bool broadcast)
|
|
{
|
|
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
return false;
|
|
}
|
|
DeleteNodeInternal(node, broadcast, recordUndo: true);
|
|
return true;
|
|
}
|
|
|
|
public Node SpawnNode(NodeDefinition definition, Vector3 position, Quaternion rotation, bool broadcast)
|
|
{
|
|
if (!CanEdit() || definition == null)
|
|
{
|
|
return null;
|
|
}
|
|
Node node = SpawnNodeInternal(definition, position, rotation, 0);
|
|
if (node != null && broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
if (node != null && !suppressUndoRecording)
|
|
{
|
|
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
|
|
circuitUndoStep.undo.Add(new DeleteNodeMutation(node.NodeId));
|
|
circuitUndoStep.redo.Add(new UpsertNodeMutation(record));
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
public Node SpawnNodeSystem(NodeDefinition definition, Vector3 position, Quaternion rotation, bool broadcast)
|
|
{
|
|
if (definition == null)
|
|
{
|
|
return null;
|
|
}
|
|
Node node = SpawnNodeInternal(definition, position, rotation, 0);
|
|
if (node != null && broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
public Node SpawnNode(string definitionName, Vector3 position, Quaternion rotation, bool broadcast)
|
|
{
|
|
NodeDefinition definition = ResolveDefinition(definitionName);
|
|
return SpawnNode(definition, position, rotation, broadcast);
|
|
}
|
|
|
|
public void AssignInputValue(Node node, NodePort inputPort, BSValue value, bool broadcast)
|
|
{
|
|
if (node == null || inputPort == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
|
|
{
|
|
return;
|
|
}
|
|
bool flag = ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
|
|
DisconnectInputPortInternal(inputPort, broadcast: false);
|
|
node.SetInputValueLocal(inputPort, value, pulsePort: true);
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
if (flag)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDisconnectInput(node.NodeId, inputPort.Name);
|
|
}
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
}
|
|
|
|
public void ApplyOutputValue(Node node, NodePort outputPort, BSValue value, bool broadcast)
|
|
{
|
|
if (!(node == null) && outputPort != null && outputPort.Direction == PortDirection.Output && outputPort.Kind == PortKind.Value)
|
|
{
|
|
value = value.Normalize();
|
|
outputPort.Value = value;
|
|
node.PulsePort(outputPort);
|
|
PropagateOutputValue(outputPort, value, flashWire: true, pulseInputPorts: true);
|
|
}
|
|
}
|
|
|
|
public void TriggerOutputRun(Node node, NodePort outputPort, bool broadcast)
|
|
{
|
|
if (node == null || outputPort == null || outputPort.Direction != PortDirection.Output || outputPort.Kind != PortKind.Run)
|
|
{
|
|
return;
|
|
}
|
|
node.PulsePort(outputPort);
|
|
for (int i = 0; i < outputPort.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = outputPort.Connections[i];
|
|
if (nodePort != null && !(nodePort.Owner == null))
|
|
{
|
|
nodePort.Owner.PulsePort(nodePort);
|
|
FlashWire(outputPort, nodePort);
|
|
nodePort.Owner.ReceiveRun(nodePort.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Connect(Node outputNode, string outputPortName, Node inputNode, string inputPortName, bool broadcast)
|
|
{
|
|
if (outputNode == null || inputNode == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!outputNode.TryGetPort(outputPortName, PortDirection.Output, out var port) || !inputNode.TryGetPort(inputPortName, PortDirection.Input, out var port2))
|
|
{
|
|
return false;
|
|
}
|
|
return ConnectPorts(port, port2, broadcast);
|
|
}
|
|
|
|
public bool Disconnect(Node outputNode, string outputPortName, Node inputNode, string inputPortName, bool broadcast)
|
|
{
|
|
if (outputNode == null || inputNode == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!outputNode.TryGetPort(outputPortName, PortDirection.Output, out var port) || !inputNode.TryGetPort(inputPortName, PortDirection.Input, out var port2))
|
|
{
|
|
return false;
|
|
}
|
|
return DisconnectPortsInternal(port, port2, broadcast);
|
|
}
|
|
|
|
public void ClearAllNodesForNetworkSync()
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
try
|
|
{
|
|
CancelEditingInteractions(clearSelection: true);
|
|
selectionBuffer.Clear();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value != null)
|
|
{
|
|
selectionBuffer.Add(item.Value);
|
|
}
|
|
}
|
|
for (int i = 0; i < selectionBuffer.Count; i++)
|
|
{
|
|
DeleteNodeInternal(selectionBuffer[i], broadcast: false, recordUndo: false);
|
|
}
|
|
nodesById.Clear();
|
|
pendingWireRecords.Clear();
|
|
nextNodeId = 1;
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
}
|
|
}
|
|
|
|
public List<WorldGroupsCodec.WorldNodeRecord> BuildWorldNodeRecords(HashSet<int> filterNodeIds = null)
|
|
{
|
|
List<WorldGroupsCodec.WorldNodeRecord> list = new List<WorldGroupsCodec.WorldNodeRecord>();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (!(value == null) && !(value.Definition == null) && (filterNodeIds == null || filterNodeIds.Contains(value.NodeId)))
|
|
{
|
|
list.Add(BuildNodeRecord(value));
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public WorldGroupsCodec.WorldNodeRecord BuildWorldNodeRecord(Node node)
|
|
{
|
|
return BuildNodeRecord(node);
|
|
}
|
|
|
|
public WorldGroupsCodec.WorldNodeRecord BuildNetworkNodeRecord(Node node)
|
|
{
|
|
if (node == null)
|
|
{
|
|
return default(WorldGroupsCodec.WorldNodeRecord);
|
|
}
|
|
return new WorldGroupsCodec.WorldNodeRecord
|
|
{
|
|
nodeId = node.NodeId,
|
|
definitionName = ((node.Definition != null) ? node.Definition.name : string.Empty),
|
|
position = node.transform.position,
|
|
rotation = node.transform.rotation,
|
|
inputValues = BuildNetworkInputValueRecords(node.InputPorts),
|
|
outputValues = null
|
|
};
|
|
}
|
|
|
|
private static WorldGroupsCodec.NodePortValueRecord[] BuildNetworkInputValueRecords(List<NodePort> ports)
|
|
{
|
|
List<WorldGroupsCodec.NodePortValueRecord> list = new List<WorldGroupsCodec.NodePortValueRecord>();
|
|
if (ports == null)
|
|
{
|
|
return list.ToArray();
|
|
}
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort != null && nodePort.Kind == PortKind.Value && nodePort.Direction == PortDirection.Input && nodePort.IncomingConnection == null && nodePort.SourceGroupId <= 0)
|
|
{
|
|
list.Add(new WorldGroupsCodec.NodePortValueRecord
|
|
{
|
|
portName = nodePort.Name,
|
|
value = nodePort.Value.Normalize()
|
|
});
|
|
}
|
|
}
|
|
return list.ToArray();
|
|
}
|
|
|
|
public List<WorldGroupsCodec.WorldWireRecord> BuildWorldWireRecords(HashSet<int> filterNodeIds = null, Dictionary<int, int> sourceGroupSaveIndices = null)
|
|
{
|
|
List<WorldGroupsCodec.WorldWireRecord> list = new List<WorldGroupsCodec.WorldWireRecord>();
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (value == null)
|
|
{
|
|
continue;
|
|
}
|
|
for (int i = 0; i < value.OutputPorts.Count; i++)
|
|
{
|
|
NodePort nodePort = value.OutputPorts[i];
|
|
if (nodePort == null)
|
|
{
|
|
continue;
|
|
}
|
|
for (int j = 0; j < nodePort.Connections.Count; j++)
|
|
{
|
|
NodePort nodePort2 = nodePort.Connections[j];
|
|
if (nodePort2 != null && !(nodePort2.Owner == null) && (filterNodeIds == null || (filterNodeIds.Contains(value.NodeId) && filterNodeIds.Contains(nodePort2.Owner.NodeId))))
|
|
{
|
|
list.Add(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = value.NodeId,
|
|
outputPortName = nodePort.Name,
|
|
inputNodeId = nodePort2.Owner.NodeId,
|
|
inputPortName = nodePort2.Name
|
|
});
|
|
}
|
|
}
|
|
}
|
|
for (int k = 0; k < value.InputPorts.Count; k++)
|
|
{
|
|
NodePort nodePort3 = value.InputPorts[k];
|
|
if (nodePort3 == null || nodePort3.Kind != PortKind.Value || nodePort3.Direction != PortDirection.Input || nodePort3.SourceGroupId <= 0 || (filterNodeIds != null && !filterNodeIds.Contains(value.NodeId)))
|
|
{
|
|
continue;
|
|
}
|
|
if (sourceGroupSaveIndices != null)
|
|
{
|
|
if (sourceGroupSaveIndices.TryGetValue(nodePort3.SourceGroupId, out var value2))
|
|
{
|
|
list.Add(WorldGroupsCodec.CreateGroupSavedWireRecord(value2, value.NodeId, nodePort3.Name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
list.Add(WorldGroupsCodec.CreateGroupRuntimeWireRecord(nodePort3.SourceGroupId, value.NodeId, nodePort3.Name));
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public void ApplySavedGraph(List<WorldGroupsCodec.WorldNodeRecord> nodeRecords, List<WorldGroupsCodec.WorldWireRecord> wireRecords, bool clearExisting, bool broadcast, Vector3 positionOffset, Dictionary<int, int> savedGroupIndexToRuntimeId = null)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = suppressNetworkBroadcast || !broadcast;
|
|
List<int> list = ((broadcast && clearExisting && !flag) ? new List<int>(nodesById.Keys) : null);
|
|
List<WorldGroupsCodec.WorldNodeRecord> list2 = ((broadcast && nodeRecords != null) ? new List<WorldGroupsCodec.WorldNodeRecord>(nodeRecords.Count) : null);
|
|
List<WorldGroupsCodec.WorldWireRecord> list3 = ((broadcast && wireRecords != null) ? new List<WorldGroupsCodec.WorldWireRecord>(wireRecords.Count) : null);
|
|
try
|
|
{
|
|
if (clearExisting)
|
|
{
|
|
ClearAllNodesForNetworkSync();
|
|
}
|
|
if (nodeRecords != null)
|
|
{
|
|
for (int i = 0; i < nodeRecords.Count; i++)
|
|
{
|
|
WorldGroupsCodec.WorldNodeRecord worldNodeRecord = nodeRecords[i];
|
|
worldNodeRecord.position += positionOffset;
|
|
list2?.Add(worldNodeRecord);
|
|
ApplyNodeRecord(worldNodeRecord, broadcast: false);
|
|
}
|
|
}
|
|
if (wireRecords != null)
|
|
{
|
|
for (int j = 0; j < wireRecords.Count; j++)
|
|
{
|
|
if (TryResolveSavedGroupWireRecord(wireRecords[j], savedGroupIndexToRuntimeId, out var resolvedRecord))
|
|
{
|
|
list3?.Add(resolvedRecord);
|
|
ApplyWireRecord(resolvedRecord, broadcast: false);
|
|
}
|
|
}
|
|
}
|
|
ResolvePendingWireRecords();
|
|
if (!broadcast || flag)
|
|
{
|
|
return;
|
|
}
|
|
if (list != null)
|
|
{
|
|
for (int k = 0; k < list.Count; k++)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDeleteNode(list[k]);
|
|
}
|
|
}
|
|
if (list2 != null)
|
|
{
|
|
for (int l = 0; l < list2.Count; l++)
|
|
{
|
|
if (nodesById.TryGetValue(list2[l].nodeId, out var value) && value != null)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(value);
|
|
}
|
|
}
|
|
}
|
|
if (list3 != null)
|
|
{
|
|
for (int m = 0; m < list3.Count; m++)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastConnectWire(list3[m]);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkNodeRecord(WorldGroupsCodec.WorldNodeRecord record)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
bool flag2 = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
ApplyNodeRecord(record, broadcast: false);
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
suppressUndoRecording = flag2;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkDelete(int nodeId)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
bool flag2 = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
if (nodesById.TryGetValue(nodeId, out var value))
|
|
{
|
|
DeleteNodeInternal(value, broadcast: false, recordUndo: false);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
suppressUndoRecording = flag2;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkWireRecord(WorldGroupsCodec.WorldWireRecord record)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
bool flag2 = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
ApplyWireRecord(record, broadcast: false);
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
suppressUndoRecording = flag2;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkDisconnectInput(int inputNodeId, string inputPortName)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
bool flag2 = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
if (nodesById.TryGetValue(inputNodeId, out var value) && value != null && value.TryGetPort(inputPortName, PortDirection.Input, out var port))
|
|
{
|
|
bool num = port.SourceGroupId > 0;
|
|
DisconnectInputPortInternal(port, broadcast: false);
|
|
if (num)
|
|
{
|
|
ClearGroupInputSourceInternal(port, clearValue: true, pulsePort: false);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
suppressUndoRecording = flag2;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkDisconnectOutput(int outputNodeId, string outputPortName)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
bool flag2 = suppressUndoRecording;
|
|
suppressUndoRecording = true;
|
|
try
|
|
{
|
|
if (nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, out var port))
|
|
{
|
|
DisconnectOutputPortInternal(port, broadcast: false);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
suppressUndoRecording = flag2;
|
|
}
|
|
}
|
|
|
|
public void ApplyNetworkTriggerRun(int outputNodeId, string outputPortName)
|
|
{
|
|
bool flag = suppressNetworkBroadcast;
|
|
suppressNetworkBroadcast = true;
|
|
try
|
|
{
|
|
if (nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, PortKind.Run, out var port))
|
|
{
|
|
TriggerOutputRun(value, port, broadcast: false);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNetworkBroadcast = flag;
|
|
}
|
|
}
|
|
|
|
private void ResolveReferences()
|
|
{
|
|
if (groupManager == null)
|
|
{
|
|
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindAnyObjectByType<VoxelGroupManager>());
|
|
}
|
|
if (brush == null)
|
|
{
|
|
brush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : GetComponent<VRVoxelBrush>());
|
|
}
|
|
if (trackingOrigin == null && brush != null)
|
|
{
|
|
trackingOrigin = brush.TrackingOrigin;
|
|
}
|
|
if (leftHandAnchor == null && brush != null)
|
|
{
|
|
leftHandAnchor = brush.LeftHandAnchor;
|
|
}
|
|
if (rightHandAnchor == null && brush != null)
|
|
{
|
|
rightHandAnchor = brush.RightHandAnchor;
|
|
}
|
|
if (networkSync == null)
|
|
{
|
|
networkSync = GetComponent<NodeGraphPhotonSync>();
|
|
}
|
|
}
|
|
|
|
private void RefreshDevices()
|
|
{
|
|
if (!leftHandDevice.isValid)
|
|
{
|
|
leftHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
|
|
}
|
|
if (!rightHandDevice.isValid)
|
|
{
|
|
rightHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
|
|
}
|
|
}
|
|
|
|
private void RefreshSceneNodesFromHierarchy()
|
|
{
|
|
Node[] array = UnityEngine.Object.FindObjectsByType<Node>(FindObjectsInactive.Include);
|
|
Array.Sort(array, CompareNodesByHierarchyPath);
|
|
nodesById.Clear();
|
|
nextNodeId = 1;
|
|
foreach (Node node in array)
|
|
{
|
|
if (!(node == null))
|
|
{
|
|
CacheDefinition(node.Definition);
|
|
if (node.NodeId <= 0 || nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
node.SetNodeId(nextNodeId++);
|
|
}
|
|
else
|
|
{
|
|
nextNodeId = Mathf.Max(nextNodeId, node.NodeId + 1);
|
|
}
|
|
node.AssignGraph(this);
|
|
node.SetVisibleInEditMode(CanEdit());
|
|
nodesById[node.NodeId] = node;
|
|
}
|
|
}
|
|
foreach (Node node2 in array)
|
|
{
|
|
if (node2 == null)
|
|
{
|
|
continue;
|
|
}
|
|
List<NodeSerializedConnection> list = node2.ConsumePendingSerializedConnections();
|
|
for (int k = 0; k < list.Count; k++)
|
|
{
|
|
NodeSerializedConnection nodeSerializedConnection = list[k];
|
|
if (nodeSerializedConnection != null && !(nodeSerializedConnection.inputNode == null) && node2.TryGetPort(nodeSerializedConnection.outputPortName, PortDirection.Output, out var port) && nodeSerializedConnection.inputNode.TryGetPort(nodeSerializedConnection.inputPortName, PortDirection.Input, out var port2))
|
|
{
|
|
ConnectPorts(port, port2, broadcast: false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CleanupDestroyedNodes()
|
|
{
|
|
bool flag = false;
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value == null)
|
|
{
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!flag)
|
|
{
|
|
return;
|
|
}
|
|
List<int> list = new List<int>();
|
|
foreach (KeyValuePair<int, Node> item2 in nodesById)
|
|
{
|
|
if (item2.Value == null)
|
|
{
|
|
list.Add(item2.Key);
|
|
}
|
|
}
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
nodesById.Remove(list[i]);
|
|
}
|
|
selectedNodes.RemoveWhere((Node node) => node == null);
|
|
grabbedNodes.RemoveAll((NodeGrabState state) => state == null || state.node == null);
|
|
}
|
|
|
|
private void ApplyEditVisibility(bool visible)
|
|
{
|
|
if (editVisibilityApplied == visible)
|
|
{
|
|
return;
|
|
}
|
|
editVisibilityApplied = visible;
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value != null)
|
|
{
|
|
item.Value.SetVisibleInEditMode(visible);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateHoverTargets()
|
|
{
|
|
ClearHoverState();
|
|
if (TryGetLeftRay(out var ray))
|
|
{
|
|
hoveredPort = TryPickPort(ray, wireRayDistance, GetExpectedWireTargetKind(), GetExpectedWireTargetDirection());
|
|
if (hoveredPort != null)
|
|
{
|
|
hoveredPort.Owner?.SetPortHighlight(hoveredPort, highlighted: true);
|
|
}
|
|
if (hoveredPort == null && activeWire == null)
|
|
{
|
|
hoveredGroup = TryPickGroup(ray);
|
|
}
|
|
}
|
|
if (TryGetRightRay(out var ray2))
|
|
{
|
|
hoveredNode = TryPickNode(ray2);
|
|
}
|
|
}
|
|
|
|
private void ClearHoverState()
|
|
{
|
|
if (hoveredPort != null && hoveredPort.Owner != null)
|
|
{
|
|
hoveredPort.Owner.SetPortHighlight(hoveredPort, highlighted: false);
|
|
}
|
|
hoveredPort = null;
|
|
hoveredNode = null;
|
|
hoveredGroup = null;
|
|
}
|
|
|
|
private void HandleSelectionInput()
|
|
{
|
|
bool flag = ((!UseXRInput()) ? (Input.GetMouseButton(2) || Input.GetKey(keyboardSelectKey)) : (ReadBool(rightHandDevice, CommonUsages.primary2DAxisClick) || Input.GetKey(keyboardSelectKey)));
|
|
if (flag && !selectInputPrevious)
|
|
{
|
|
nodeSelectLatchedThisHold = false;
|
|
}
|
|
if (!flag && selectInputPrevious)
|
|
{
|
|
nodeSelectLatchedThisHold = false;
|
|
}
|
|
if (flag && hoveredNode != null)
|
|
{
|
|
nodeSelectLatchedThisHold = true;
|
|
}
|
|
if (flag && nodeSelectLatchedThisHold && hoveredNode != null)
|
|
{
|
|
AddSelection(hoveredNode);
|
|
}
|
|
selectInputPrevious = flag;
|
|
}
|
|
|
|
private void HandleDuplicateInput()
|
|
{
|
|
bool flag = UseXRInput() && ReadBool(leftHandDevice, CommonUsages.primaryButton);
|
|
bool flag2 = Input.GetKeyDown(keyboardDuplicateKey) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
|
|
if (!HasSelection)
|
|
{
|
|
duplicateInputPrevious = flag;
|
|
return;
|
|
}
|
|
if ((flag && !duplicateInputPrevious) || flag2)
|
|
{
|
|
DuplicateSelectedNodes();
|
|
}
|
|
duplicateInputPrevious = flag;
|
|
}
|
|
|
|
private void HandleDeleteInput()
|
|
{
|
|
if (HasSelection && Input.GetKeyDown(keyboardDeleteKey))
|
|
{
|
|
DeleteSelectedNodes();
|
|
}
|
|
}
|
|
|
|
private void HandleGrabInput()
|
|
{
|
|
bool flag = (UseXRInput() ? ReadGrabHeld(rightHandDevice) : Input.GetMouseButton(1));
|
|
if (HasSelection && flag)
|
|
{
|
|
if (!nodeGrabActive)
|
|
{
|
|
BeginNodeGrab();
|
|
}
|
|
UpdateNodeGrab();
|
|
}
|
|
else if (nodeGrabActive)
|
|
{
|
|
EndNodeGrab(broadcastFinal: true);
|
|
}
|
|
}
|
|
|
|
private void HandleWireInput()
|
|
{
|
|
bool num = UseXRInput();
|
|
bool flag = num && ReadGrabHeld(leftHandDevice);
|
|
bool flag2 = (num ? (ReadFloat(leftHandDevice, CommonUsages.trigger) >= wireTriggerThreshold) : Input.GetKey(keyboardWireKey));
|
|
bool flag3 = flag2 && !wireInputPrevious;
|
|
bool flag4 = !flag2 && wireInputPrevious;
|
|
if (flag)
|
|
{
|
|
wireInputPrevious = flag2;
|
|
return;
|
|
}
|
|
if (activeWire != null && (flag3 || flag4))
|
|
{
|
|
if (hoveredPort == null || !IsWireTargetCompatible(hoveredPort))
|
|
{
|
|
ClearActiveWire();
|
|
}
|
|
else
|
|
{
|
|
CompleteActiveWire(hoveredPort);
|
|
}
|
|
wireInputPrevious = flag2;
|
|
return;
|
|
}
|
|
if (pendingWirePress != null)
|
|
{
|
|
if (!flag2)
|
|
{
|
|
if (Time.unscaledTime - pendingWirePress.pressedAt <= quickTapThreshold)
|
|
{
|
|
HandleQuickWireTap(pendingWirePress);
|
|
}
|
|
else
|
|
{
|
|
StartActiveWire(pendingWirePress.startPort, pendingWirePress.startGroup, pendingWirePress.startWorldPosition);
|
|
}
|
|
pendingWirePress = null;
|
|
}
|
|
else if (Time.unscaledTime - pendingWirePress.pressedAt > quickTapThreshold)
|
|
{
|
|
StartActiveWire(pendingWirePress.startPort, pendingWirePress.startGroup, pendingWirePress.startWorldPosition);
|
|
pendingWirePress = null;
|
|
}
|
|
}
|
|
else if (flag3)
|
|
{
|
|
if (hoveredPort != null)
|
|
{
|
|
pendingWirePress = new PendingWirePress
|
|
{
|
|
startPort = hoveredPort,
|
|
startWorldPosition = ((hoveredPort.Owner != null) ? hoveredPort.Owner.GetPortWorldPosition(hoveredPort) : base.transform.position),
|
|
pressedAt = Time.unscaledTime
|
|
};
|
|
}
|
|
else if (hoveredGroup != null)
|
|
{
|
|
pendingWirePress = new PendingWirePress
|
|
{
|
|
startGroup = hoveredGroup,
|
|
startWorldPosition = hoveredGroup.GetWorldBounds().center,
|
|
pressedAt = Time.unscaledTime
|
|
};
|
|
}
|
|
}
|
|
if (flag4 && activeWire == null)
|
|
{
|
|
CancelPendingWirePress();
|
|
}
|
|
wireInputPrevious = flag2;
|
|
}
|
|
|
|
private void BeginNodeGrab()
|
|
{
|
|
if (!TryGetGrabReferencePose(out grabStartReferencePosition, out grabStartReferenceRotation) || !TryGetSelectionBounds(out var bounds))
|
|
{
|
|
return;
|
|
}
|
|
grabbedNodes.Clear();
|
|
foreach (Node selectedNode in selectedNodes)
|
|
{
|
|
if (!(selectedNode == null))
|
|
{
|
|
grabbedNodes.Add(new NodeGrabState
|
|
{
|
|
node = selectedNode,
|
|
startPosition = selectedNode.transform.position,
|
|
startRotation = selectedNode.transform.rotation
|
|
});
|
|
}
|
|
}
|
|
if (grabbedNodes.Count != 0)
|
|
{
|
|
nodeGrabActive = true;
|
|
grabStartSelectionCenter = bounds.center;
|
|
grabStartSelectionOffset = bounds.center - grabStartReferencePosition;
|
|
}
|
|
}
|
|
|
|
private void UpdateNodeGrab()
|
|
{
|
|
if (!nodeGrabActive || !TryGetGrabReferencePose(out var worldPosition, out var worldRotation))
|
|
{
|
|
return;
|
|
}
|
|
Quaternion quaternion = SnapRotation(worldRotation * Quaternion.Inverse(grabStartReferenceRotation), nodeMoveRotationSnapDegrees);
|
|
Vector3 vector = SnapVector(worldPosition + grabStartSelectionOffset, nodeMovePositionSnap);
|
|
for (int num = grabbedNodes.Count - 1; num >= 0; num--)
|
|
{
|
|
NodeGrabState nodeGrabState = grabbedNodes[num];
|
|
if (nodeGrabState == null || nodeGrabState.node == null || !selectedNodes.Contains(nodeGrabState.node))
|
|
{
|
|
grabbedNodes.RemoveAt(num);
|
|
}
|
|
else
|
|
{
|
|
Vector3 vector2 = nodeGrabState.startPosition - grabStartSelectionCenter;
|
|
Vector3 position = vector + quaternion * vector2;
|
|
Quaternion rotation = quaternion * nodeGrabState.startRotation;
|
|
nodeGrabState.node.transform.SetPositionAndRotation(position, rotation);
|
|
nodeGrabState.node.SetSelected(isSelected: true);
|
|
if (!suppressNetworkBroadcast && Time.unscaledTime - nodeGrabState.lastPreviewSentAt >= Mathf.Max(0.01f, movePreviewSendInterval))
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(nodeGrabState.node);
|
|
nodeGrabState.lastPreviewSentAt = Time.unscaledTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EndNodeGrab(bool broadcastFinal)
|
|
{
|
|
if (!nodeGrabActive)
|
|
{
|
|
grabbedNodes.Clear();
|
|
return;
|
|
}
|
|
nodeGrabActive = false;
|
|
if (!suppressUndoRecording && grabbedNodes.Count > 0)
|
|
{
|
|
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
|
|
int num = 0;
|
|
for (int i = 0; i < grabbedNodes.Count; i++)
|
|
{
|
|
NodeGrabState nodeGrabState = grabbedNodes[i];
|
|
if (nodeGrabState != null && !(nodeGrabState.node == null) && nodesById.ContainsKey(nodeGrabState.node.NodeId))
|
|
{
|
|
Vector3 position = nodeGrabState.node.transform.position;
|
|
Quaternion rotation = nodeGrabState.node.transform.rotation;
|
|
if (!((position - nodeGrabState.startPosition).sqrMagnitude <= 1E-06f) || !(Quaternion.Angle(rotation, nodeGrabState.startRotation) <= 0.01f))
|
|
{
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(nodeGrabState.node);
|
|
record.position = nodeGrabState.startPosition;
|
|
record.rotation = nodeGrabState.startRotation;
|
|
WorldGroupsCodec.WorldNodeRecord record2 = BuildNetworkNodeRecord(nodeGrabState.node);
|
|
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
|
|
circuitUndoStep.redo.Add(new UpsertNodeMutation(record2));
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
}
|
|
if (broadcastFinal && !suppressNetworkBroadcast)
|
|
{
|
|
for (int j = 0; j < grabbedNodes.Count; j++)
|
|
{
|
|
if (grabbedNodes[j] != null && grabbedNodes[j].node != null)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(grabbedNodes[j].node);
|
|
}
|
|
}
|
|
}
|
|
grabbedNodes.Clear();
|
|
}
|
|
|
|
private void StartActiveWire(NodePort startPort, VoxelGroup startGroup, Vector3 startWorldPosition)
|
|
{
|
|
PortKind kind = startPort?.Kind ?? PortKind.Value;
|
|
activeWire = new ActiveWireContext
|
|
{
|
|
startPort = startPort,
|
|
startGroup = startGroup,
|
|
startWorldPosition = startWorldPosition,
|
|
Kind = kind
|
|
};
|
|
}
|
|
|
|
private void CompleteActiveWire(NodePort targetPort)
|
|
{
|
|
if (activeWire == null || targetPort == null)
|
|
{
|
|
ClearActiveWire();
|
|
return;
|
|
}
|
|
if (activeWire.startGroup != null)
|
|
{
|
|
if (targetPort.Direction == PortDirection.Input && targetPort.Kind == PortKind.Value)
|
|
{
|
|
List<WorldGroupsCodec.WorldWireRecord> list = (suppressUndoRecording ? null : GetExistingWiresForPort(targetPort));
|
|
if (ConnectGroupToInputPort(activeWire.startGroup.id, targetPort, broadcast: true) && !suppressUndoRecording)
|
|
{
|
|
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
|
|
WorldGroupsCodec.WorldWireRecord record = WorldGroupsCodec.CreateGroupRuntimeWireRecord(activeWire.startGroup.id, (targetPort.Owner != null) ? targetPort.Owner.NodeId : 0, targetPort.Name);
|
|
circuitUndoStep.undo.Add(new DisconnectWireMutation(record));
|
|
if (list != null)
|
|
{
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
circuitUndoStep.undo.Add(new ConnectWireMutation(list[i]));
|
|
}
|
|
}
|
|
circuitUndoStep.redo.Add(new ConnectWireMutation(record));
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
}
|
|
ClearActiveWire();
|
|
return;
|
|
}
|
|
if (activeWire.startPort == null)
|
|
{
|
|
ClearActiveWire();
|
|
return;
|
|
}
|
|
if (activeWire.startPort == targetPort)
|
|
{
|
|
ClearActiveWire();
|
|
return;
|
|
}
|
|
NodePort startPort = activeWire.startPort;
|
|
List<WorldGroupsCodec.WorldWireRecord> list2 = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
|
|
List<WorldGroupsCodec.WorldWireRecord> list3 = (suppressUndoRecording ? null : GetExistingWiresForPort(targetPort));
|
|
if (ConnectPortsDirectional(startPort, targetPort, broadcast: true) && !suppressUndoRecording)
|
|
{
|
|
NodePort nodePort = ((startPort.Direction == PortDirection.Output) ? startPort : targetPort);
|
|
NodePort nodePort2 = ((startPort.Direction == PortDirection.Input) ? startPort : targetPort);
|
|
WorldGroupsCodec.WorldWireRecord record2 = new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = ((nodePort.Owner != null) ? nodePort.Owner.NodeId : 0),
|
|
outputPortName = nodePort.Name,
|
|
inputNodeId = ((nodePort2.Owner != null) ? nodePort2.Owner.NodeId : 0),
|
|
inputPortName = nodePort2.Name
|
|
};
|
|
CircuitUndoStep circuitUndoStep2 = new CircuitUndoStep();
|
|
circuitUndoStep2.undo.Add(new DisconnectWireMutation(record2));
|
|
if (list2 != null)
|
|
{
|
|
for (int j = 0; j < list2.Count; j++)
|
|
{
|
|
circuitUndoStep2.undo.Add(new ConnectWireMutation(list2[j]));
|
|
}
|
|
}
|
|
if (list3 != null)
|
|
{
|
|
for (int k = 0; k < list3.Count; k++)
|
|
{
|
|
circuitUndoStep2.undo.Add(new ConnectWireMutation(list3[k]));
|
|
}
|
|
}
|
|
circuitUndoStep2.redo.Add(new ConnectWireMutation(record2));
|
|
PushUndoStep(circuitUndoStep2);
|
|
}
|
|
ClearActiveWire();
|
|
}
|
|
|
|
private void HandleQuickWireTap(PendingWirePress pending)
|
|
{
|
|
if (pending == null || pending.startGroup != null)
|
|
{
|
|
return;
|
|
}
|
|
NodePort startPort = pending.startPort;
|
|
if (startPort == null)
|
|
{
|
|
return;
|
|
}
|
|
if (startPort.Direction == PortDirection.Input)
|
|
{
|
|
bool num = startPort.Kind == PortKind.Run && startPort.Connections.Count > 0;
|
|
bool flag = startPort.Kind != PortKind.Run && (startPort.IncomingConnection != null || startPort.SourceGroupId > 0);
|
|
if (num || flag)
|
|
{
|
|
List<WorldGroupsCodec.WorldWireRecord> list = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
|
|
bool num2 = startPort.SourceGroupId > 0;
|
|
DisconnectInputPortInternal(startPort, broadcast: true);
|
|
if (num2)
|
|
{
|
|
ClearGroupInputSourceInternal(startPort, clearValue: true, pulsePort: false);
|
|
if (!suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDisconnectInput((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name);
|
|
}
|
|
}
|
|
if (!suppressUndoRecording && list != null && list.Count > 0)
|
|
{
|
|
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
|
|
circuitUndoStep.redo.Add(new DisconnectInputMutation((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name));
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
circuitUndoStep.undo.Add(new ConnectWireMutation(list[i]));
|
|
}
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (startPort.Kind != PortKind.Value || !(startPort.Owner != null))
|
|
{
|
|
return;
|
|
}
|
|
if (!suppressUndoRecording)
|
|
{
|
|
BSValue a = startPort.Value.Normalize();
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(startPort.Owner);
|
|
AssignInputValue(startPort.Owner, startPort, BSValue.None, broadcast: true);
|
|
WorldGroupsCodec.WorldNodeRecord record2 = BuildNetworkNodeRecord(startPort.Owner);
|
|
BSValue b = startPort.Value.Normalize();
|
|
if (!BSValueUtility.Equals(a, b))
|
|
{
|
|
CircuitUndoStep circuitUndoStep2 = new CircuitUndoStep();
|
|
circuitUndoStep2.undo.Add(new UpsertNodeMutation(record));
|
|
circuitUndoStep2.redo.Add(new UpsertNodeMutation(record2));
|
|
PushUndoStep(circuitUndoStep2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssignInputValue(startPort.Owner, startPort, BSValue.None, broadcast: true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
List<WorldGroupsCodec.WorldWireRecord> list2 = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
|
|
DisconnectOutputPortInternal(startPort, broadcast: true);
|
|
if (!suppressUndoRecording && list2 != null && list2.Count > 0)
|
|
{
|
|
CircuitUndoStep circuitUndoStep3 = new CircuitUndoStep();
|
|
circuitUndoStep3.redo.Add(new DisconnectOutputMutation((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name));
|
|
for (int j = 0; j < list2.Count; j++)
|
|
{
|
|
circuitUndoStep3.undo.Add(new ConnectWireMutation(list2[j]));
|
|
}
|
|
PushUndoStep(circuitUndoStep3);
|
|
}
|
|
}
|
|
|
|
private void CancelPendingWirePress()
|
|
{
|
|
pendingWirePress = null;
|
|
}
|
|
|
|
private void ClearActiveWire()
|
|
{
|
|
activeWire = null;
|
|
}
|
|
|
|
private void AddSelection(Node node)
|
|
{
|
|
if (!(node == null) && selectedNodes.Add(node))
|
|
{
|
|
node.SetSelected(isSelected: true);
|
|
}
|
|
}
|
|
|
|
private void ClearSelectionInternal(bool keepSuppression)
|
|
{
|
|
selectionBuffer.Clear();
|
|
foreach (Node selectedNode in selectedNodes)
|
|
{
|
|
if (selectedNode != null)
|
|
{
|
|
selectionBuffer.Add(selectedNode);
|
|
}
|
|
}
|
|
for (int i = 0; i < selectionBuffer.Count; i++)
|
|
{
|
|
selectionBuffer[i].SetSelected(isSelected: false);
|
|
}
|
|
selectedNodes.Clear();
|
|
if (!keepSuppression)
|
|
{
|
|
grabbedNodes.Clear();
|
|
}
|
|
}
|
|
|
|
private void DeleteSelectedNodes()
|
|
{
|
|
CircuitUndoStep circuitUndoStep = (suppressUndoRecording ? null : new CircuitUndoStep());
|
|
selectionBuffer.Clear();
|
|
foreach (Node selectedNode in selectedNodes)
|
|
{
|
|
if (selectedNode != null)
|
|
{
|
|
selectionBuffer.Add(selectedNode);
|
|
}
|
|
}
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
for (int i = 0; i < selectionBuffer.Count; i++)
|
|
{
|
|
Node node = selectionBuffer[i];
|
|
if (node == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (circuitUndoStep != null && nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
|
|
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
|
|
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
|
|
for (int j = 0; j < wiresTouchingNode.Count; j++)
|
|
{
|
|
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[j]));
|
|
}
|
|
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
|
|
}
|
|
DeleteNodeInternal(node, broadcast: true, recordUndo: false);
|
|
}
|
|
if (circuitUndoStep != null && circuitUndoStep.redo.Count > 0)
|
|
{
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
}
|
|
|
|
private void DuplicateSelectedNodes()
|
|
{
|
|
if (selectedNodes.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
CircuitUndoStep circuitUndoStep = (suppressUndoRecording ? null : new CircuitUndoStep());
|
|
Dictionary<int, Node> dictionary = new Dictionary<int, Node>();
|
|
List<WorldGroupsCodec.WorldWireRecord> list = BuildWorldWireRecords();
|
|
List<Node> selectedNodesSnapshot = GetSelectedNodesSnapshot();
|
|
HashSet<int> hashSet = new HashSet<int>();
|
|
for (int i = 0; i < selectedNodesSnapshot.Count; i++)
|
|
{
|
|
if (selectedNodesSnapshot[i] != null)
|
|
{
|
|
hashSet.Add(selectedNodesSnapshot[i].NodeId);
|
|
}
|
|
}
|
|
ClearSelectionInternal(keepSuppression: false);
|
|
Vector3 vector = new Vector3(0.25f, 0.1f, 0.25f);
|
|
for (int j = 0; j < selectedNodesSnapshot.Count; j++)
|
|
{
|
|
Node node = selectedNodesSnapshot[j];
|
|
if (node == null || node.Definition == null)
|
|
{
|
|
continue;
|
|
}
|
|
Node node2 = SpawnNodeInternal(node.Definition, node.transform.position + vector, node.transform.rotation, 0);
|
|
if (!(node2 == null))
|
|
{
|
|
CopyNodeValues(node, node2);
|
|
dictionary[node.NodeId] = node2;
|
|
AddSelection(node2);
|
|
if (!suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node2);
|
|
}
|
|
}
|
|
}
|
|
for (int k = 0; k < list.Count; k++)
|
|
{
|
|
WorldGroupsCodec.WorldWireRecord record = list[k];
|
|
Node value2;
|
|
Node value3;
|
|
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
|
|
{
|
|
if (hashSet.Contains(record.inputNodeId) && dictionary.TryGetValue(record.inputNodeId, out var value))
|
|
{
|
|
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast: true);
|
|
}
|
|
}
|
|
else if (hashSet.Contains(record.outputNodeId) && hashSet.Contains(record.inputNodeId) && dictionary.TryGetValue(record.outputNodeId, out value2) && dictionary.TryGetValue(record.inputNodeId, out value3))
|
|
{
|
|
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast: true);
|
|
}
|
|
}
|
|
if (circuitUndoStep == null || dictionary.Count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
HashSet<int> hashSet2 = new HashSet<int>();
|
|
foreach (KeyValuePair<int, Node> item2 in dictionary)
|
|
{
|
|
if (item2.Value != null)
|
|
{
|
|
hashSet2.Add(item2.Value.NodeId);
|
|
}
|
|
}
|
|
List<WorldGroupsCodec.WorldNodeRecord> list2 = new List<WorldGroupsCodec.WorldNodeRecord>();
|
|
foreach (int item3 in hashSet2)
|
|
{
|
|
if (nodesById.TryGetValue(item3, out var value4) && value4 != null)
|
|
{
|
|
list2.Add(BuildNetworkNodeRecord(value4));
|
|
}
|
|
}
|
|
List<WorldGroupsCodec.WorldWireRecord> list3 = BuildWorldWireRecords();
|
|
List<WorldGroupsCodec.WorldWireRecord> list4 = new List<WorldGroupsCodec.WorldWireRecord>();
|
|
for (int l = 0; l < list3.Count; l++)
|
|
{
|
|
WorldGroupsCodec.WorldWireRecord item = list3[l];
|
|
if (hashSet2.Contains(item.inputNodeId) || hashSet2.Contains(item.outputNodeId))
|
|
{
|
|
list4.Add(item);
|
|
}
|
|
}
|
|
foreach (int item4 in hashSet2)
|
|
{
|
|
circuitUndoStep.undo.Add(new DeleteNodeMutation(item4));
|
|
}
|
|
for (int m = 0; m < list2.Count; m++)
|
|
{
|
|
circuitUndoStep.redo.Add(new UpsertNodeMutation(list2[m]));
|
|
}
|
|
for (int n = 0; n < list4.Count; n++)
|
|
{
|
|
circuitUndoStep.redo.Add(new ConnectWireMutation(list4[n]));
|
|
}
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
|
|
private void CopyNodeValues(Node source, Node target)
|
|
{
|
|
if (source == null || target == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < source.InputPorts.Count; i++)
|
|
{
|
|
NodePort nodePort = source.InputPorts[i];
|
|
if (nodePort != null && nodePort.Kind == PortKind.Value && target.TryGetPort(nodePort.Name, PortDirection.Input, PortKind.Value, out var port))
|
|
{
|
|
target.SetInputValueLocal(port, nodePort.Value, pulsePort: false);
|
|
}
|
|
}
|
|
for (int j = 0; j < source.OutputPorts.Count; j++)
|
|
{
|
|
NodePort nodePort2 = source.OutputPorts[j];
|
|
if (nodePort2 != null && nodePort2.Kind == PortKind.Value && target.TryGetPort(nodePort2.Name, PortDirection.Output, PortKind.Value, out var port2))
|
|
{
|
|
port2.Value = nodePort2.Value.Normalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DeleteNodeInternal(Node node, bool broadcast, bool recordUndo)
|
|
{
|
|
if (node == null)
|
|
{
|
|
return;
|
|
}
|
|
if (recordUndo && !suppressUndoRecording && nodesById.ContainsKey(node.NodeId))
|
|
{
|
|
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
|
|
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
|
|
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
|
|
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
|
|
for (int i = 0; i < wiresTouchingNode.Count; i++)
|
|
{
|
|
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[i]));
|
|
}
|
|
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
|
|
PushUndoStep(circuitUndoStep);
|
|
}
|
|
DisconnectAllForNode(node, broadcast: false);
|
|
selectedNodes.Remove(node);
|
|
grabbedNodes.RemoveAll((NodeGrabState state) => state == null || state.node == node);
|
|
nodesById.Remove(node.NodeId);
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDeleteNode(node.NodeId);
|
|
}
|
|
if (Application.isPlaying)
|
|
{
|
|
UnityEngine.Object.Destroy(node.gameObject);
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(node.gameObject);
|
|
}
|
|
}
|
|
|
|
private void DisconnectAllForNode(Node node, bool broadcast)
|
|
{
|
|
if (!(node == null))
|
|
{
|
|
for (int i = 0; i < node.InputPorts.Count; i++)
|
|
{
|
|
DisconnectInputPortInternal(node.InputPorts[i], broadcast: false);
|
|
ClearGroupInputSourceInternal(node.InputPorts[i], clearValue: false, pulsePort: false);
|
|
}
|
|
for (int j = 0; j < node.OutputPorts.Count; j++)
|
|
{
|
|
DisconnectOutputPortInternal(node.OutputPorts[j], broadcast: false);
|
|
}
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool ConnectPortsDirectional(NodePort firstPort, NodePort secondPort, bool broadcast)
|
|
{
|
|
if (!TryResolveDirectionalPorts(firstPort, secondPort, out var outputPort, out var inputPort))
|
|
{
|
|
return false;
|
|
}
|
|
return ConnectPorts(outputPort, inputPort, broadcast);
|
|
}
|
|
|
|
private bool ConnectPorts(NodePort outputPort, NodePort inputPort, bool broadcast)
|
|
{
|
|
if (outputPort == null || inputPort == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (outputPort.Direction != PortDirection.Output || inputPort.Direction != PortDirection.Input || outputPort.Kind != inputPort.Kind)
|
|
{
|
|
return false;
|
|
}
|
|
if (outputPort.Kind == PortKind.Run)
|
|
{
|
|
if (outputPort.Connections.Count > 0 && !outputPort.Connections.Contains(inputPort))
|
|
{
|
|
DisconnectOutputPortInternal(outputPort, broadcast: false);
|
|
}
|
|
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
|
|
inputPort.IncomingConnection = null;
|
|
if (!outputPort.Connections.Contains(inputPort))
|
|
{
|
|
outputPort.Connections.Add(inputPort);
|
|
}
|
|
if (!inputPort.Connections.Contains(outputPort))
|
|
{
|
|
inputPort.Connections.Add(outputPort);
|
|
}
|
|
EnsureWireVisual(outputPort, inputPort);
|
|
outputPort.Owner?.PulsePort(outputPort);
|
|
inputPort.Owner?.PulsePort(inputPort);
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastConnectWire(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = ((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0),
|
|
outputPortName = outputPort.Name,
|
|
inputNodeId = ((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0),
|
|
inputPortName = inputPort.Name
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
if (inputPort.IncomingConnection != null && inputPort.IncomingConnection != outputPort)
|
|
{
|
|
DisconnectInputPortInternal(inputPort, broadcast: false);
|
|
}
|
|
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
|
|
if (!outputPort.Connections.Contains(inputPort))
|
|
{
|
|
outputPort.Connections.Add(inputPort);
|
|
}
|
|
inputPort.IncomingConnection = outputPort;
|
|
EnsureWireVisual(outputPort, inputPort);
|
|
outputPort.Owner?.PulsePort(outputPort);
|
|
inputPort.Owner?.PulsePort(inputPort);
|
|
if (outputPort.Kind == PortKind.Value)
|
|
{
|
|
inputPort.Owner?.SetInputValueLocal(inputPort, outputPort.Value, pulsePort: true);
|
|
FlashWire(outputPort, inputPort);
|
|
}
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastConnectWire(new WorldGroupsCodec.WorldWireRecord
|
|
{
|
|
outputNodeId = ((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0),
|
|
outputPortName = outputPort.Name,
|
|
inputNodeId = ((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0),
|
|
inputPortName = inputPort.Name
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool DisconnectPortsInternal(NodePort outputPort, NodePort inputPort, bool broadcast)
|
|
{
|
|
if (outputPort == null || inputPort == null)
|
|
{
|
|
return false;
|
|
}
|
|
bool num = outputPort.Connections.Remove(inputPort);
|
|
if (outputPort.Kind == PortKind.Run)
|
|
{
|
|
inputPort.Connections.Remove(outputPort);
|
|
if (inputPort.IncomingConnection == outputPort)
|
|
{
|
|
inputPort.IncomingConnection = null;
|
|
}
|
|
}
|
|
else if (inputPort.IncomingConnection == outputPort)
|
|
{
|
|
inputPort.IncomingConnection = null;
|
|
}
|
|
RemoveWireVisual(outputPort, inputPort);
|
|
if (num && broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
NodeGraphPhotonSync nodeGraphPhotonSync = ResolveNetworkSync();
|
|
if ((object)nodeGraphPhotonSync == null)
|
|
{
|
|
return num;
|
|
}
|
|
nodeGraphPhotonSync.BroadcastDisconnectInput((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0, inputPort.Name);
|
|
}
|
|
return num;
|
|
}
|
|
|
|
private void DisconnectInputPortInternal(NodePort inputPort, bool broadcast)
|
|
{
|
|
if (inputPort == null)
|
|
{
|
|
return;
|
|
}
|
|
if (inputPort.Kind == PortKind.Run)
|
|
{
|
|
if (inputPort.Connections.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
List<NodePort> list = new List<NodePort>(inputPort.Connections);
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
if (list[i] != null)
|
|
{
|
|
DisconnectPortsInternal(list[i], inputPort, broadcast: false);
|
|
}
|
|
}
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDisconnectInput((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0, inputPort.Name);
|
|
}
|
|
}
|
|
else if (inputPort.IncomingConnection != null)
|
|
{
|
|
NodePort incomingConnection = inputPort.IncomingConnection;
|
|
DisconnectPortsInternal(incomingConnection, inputPort, broadcast);
|
|
}
|
|
}
|
|
|
|
private void DisconnectOutputPortInternal(NodePort outputPort, bool broadcast)
|
|
{
|
|
if (outputPort != null && outputPort.Connections.Count != 0)
|
|
{
|
|
List<NodePort> list = new List<NodePort>(outputPort.Connections);
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
DisconnectPortsInternal(outputPort, list[i], broadcast: false);
|
|
}
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastDisconnectOutput((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0, outputPort.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PropagateOutputValue(NodePort outputPort, BSValue value, bool flashWire, bool pulseInputPorts)
|
|
{
|
|
for (int i = 0; i < outputPort.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = outputPort.Connections[i];
|
|
if (nodePort != null && !(nodePort.Owner == null))
|
|
{
|
|
nodePort.Owner.SetInputValueLocal(nodePort, value, pulseInputPorts);
|
|
if (flashWire)
|
|
{
|
|
FlashWire(outputPort, nodePort);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyNodeRecord(WorldGroupsCodec.WorldNodeRecord record, bool broadcast)
|
|
{
|
|
if (string.IsNullOrEmpty(record.definitionName))
|
|
{
|
|
return;
|
|
}
|
|
NodeDefinition nodeDefinition = ResolveDefinition(record.definitionName);
|
|
if (nodeDefinition == null)
|
|
{
|
|
Debug.LogWarning("NodeGraphManager could not resolve node definition '" + record.definitionName + "'.");
|
|
return;
|
|
}
|
|
Node value;
|
|
Node node = (nodesById.TryGetValue(record.nodeId, out value) ? value : null);
|
|
bool flag = node != null && node.Definition != nodeDefinition;
|
|
if (node == null)
|
|
{
|
|
node = SpawnNodeInternal(nodeDefinition, record.position, record.rotation, record.nodeId);
|
|
}
|
|
else
|
|
{
|
|
if (flag)
|
|
{
|
|
DisconnectAllForNode(node, broadcast: false);
|
|
node.Definition = nodeDefinition;
|
|
node.ReinitializeFromDefinition();
|
|
node.AssignGraph(this);
|
|
}
|
|
node.SetNodeId(record.nodeId);
|
|
node.transform.SetPositionAndRotation(record.position, record.rotation);
|
|
}
|
|
if (node == null)
|
|
{
|
|
return;
|
|
}
|
|
List<NodePort> list = new List<NodePort>();
|
|
ApplyPortValues(node, record.inputValues, PortDirection.Input, null);
|
|
ApplyPortValues(node, record.outputValues, PortDirection.Output, list);
|
|
for (int i = 0; i < node.OutputPorts.Count; i++)
|
|
{
|
|
NodePort nodePort = node.OutputPorts[i];
|
|
if (nodePort != null && nodePort.Kind == PortKind.Value)
|
|
{
|
|
PropagateOutputValue(nodePort, nodePort.Value, flashWire: false, pulseInputPorts: false);
|
|
if (list.Contains(nodePort))
|
|
{
|
|
FlashAllConnections(nodePort);
|
|
}
|
|
}
|
|
}
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastUpsertNode(node);
|
|
}
|
|
}
|
|
|
|
private void ApplyPortValues(Node node, WorldGroupsCodec.NodePortValueRecord[] values, PortDirection direction, List<NodePort> changedOutputPorts)
|
|
{
|
|
if (node == null || values == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
WorldGroupsCodec.NodePortValueRecord nodePortValueRecord = values[i];
|
|
if (!node.TryGetPort(nodePortValueRecord.portName, direction, PortKind.Value, out var port))
|
|
{
|
|
continue;
|
|
}
|
|
if (direction == PortDirection.Input)
|
|
{
|
|
node.SetInputValueLocal(port, nodePortValueRecord.value, pulsePort: false);
|
|
continue;
|
|
}
|
|
if (!BSValueEquals(port.Value, nodePortValueRecord.value))
|
|
{
|
|
changedOutputPorts?.Add(port);
|
|
}
|
|
port.Value = nodePortValueRecord.value.Normalize();
|
|
}
|
|
}
|
|
|
|
private void FlashAllConnections(NodePort outputPort)
|
|
{
|
|
if (outputPort == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < outputPort.Connections.Count; i++)
|
|
{
|
|
NodePort nodePort = outputPort.Connections[i];
|
|
if (nodePort != null)
|
|
{
|
|
FlashWire(outputPort, nodePort);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool ConnectGroupToInput(int groupId, Node inputNode, string inputPortName, bool broadcast)
|
|
{
|
|
if (groupId <= 0 || inputNode == null || string.IsNullOrEmpty(inputPortName))
|
|
{
|
|
return false;
|
|
}
|
|
if (inputNode.TryGetPort(inputPortName, PortDirection.Input, PortKind.Value, out var port))
|
|
{
|
|
return ConnectGroupToInputPort(groupId, port, broadcast);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool ConnectGroupToInputPort(int groupId, NodePort inputPort, bool broadcast)
|
|
{
|
|
if (groupId <= 0 || inputPort == null || inputPort.Owner == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
|
|
{
|
|
return false;
|
|
}
|
|
DisconnectInputPortInternal(inputPort, broadcast: false);
|
|
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
|
|
inputPort.SourceGroupId = groupId;
|
|
EnsureGroupWireVisual(groupId, inputPort);
|
|
inputPort.Owner.SetInputValueLocal(inputPort, BSValue.FromGroup(groupId), pulsePort: true);
|
|
FlashGroupWire(groupId, inputPort);
|
|
if (broadcast && !suppressNetworkBroadcast)
|
|
{
|
|
ResolveNetworkSync()?.BroadcastConnectWire(WorldGroupsCodec.CreateGroupRuntimeWireRecord(groupId, inputPort.Owner.NodeId, inputPort.Name));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ClearGroupInputSourceInternal(NodePort inputPort, bool clearValue, bool pulsePort)
|
|
{
|
|
if (inputPort == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
|
|
{
|
|
return false;
|
|
}
|
|
int sourceGroupId = inputPort.SourceGroupId;
|
|
if (sourceGroupId <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
inputPort.SourceGroupId = 0;
|
|
RemoveGroupWireVisual(sourceGroupId, inputPort);
|
|
if (clearValue && inputPort.Owner != null)
|
|
{
|
|
inputPort.Owner.SetInputValueLocal(inputPort, BSValue.None, pulsePort);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void ApplyWireRecord(WorldGroupsCodec.WorldWireRecord record, bool broadcast)
|
|
{
|
|
Node value2;
|
|
Node value3;
|
|
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
|
|
{
|
|
if (!nodesById.TryGetValue(record.inputNodeId, out var value) || value == null)
|
|
{
|
|
pendingWireRecords.Add(record);
|
|
}
|
|
else
|
|
{
|
|
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast);
|
|
}
|
|
}
|
|
else if (!nodesById.TryGetValue(record.outputNodeId, out value2) || !nodesById.TryGetValue(record.inputNodeId, out value3) || value2 == null || value3 == null)
|
|
{
|
|
pendingWireRecords.Add(record);
|
|
}
|
|
else
|
|
{
|
|
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast);
|
|
}
|
|
}
|
|
|
|
private void ResolvePendingWireRecords()
|
|
{
|
|
if (pendingWireRecords.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
for (int num = pendingWireRecords.Count - 1; num >= 0; num--)
|
|
{
|
|
WorldGroupsCodec.WorldWireRecord record = pendingWireRecords[num];
|
|
Node value2;
|
|
Node value3;
|
|
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
|
|
{
|
|
if (nodesById.TryGetValue(record.inputNodeId, out var value) && value != null)
|
|
{
|
|
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast: false);
|
|
pendingWireRecords.RemoveAt(num);
|
|
}
|
|
}
|
|
else if (nodesById.TryGetValue(record.outputNodeId, out value2) && nodesById.TryGetValue(record.inputNodeId, out value3) && value2 != null && value3 != null)
|
|
{
|
|
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast: false);
|
|
pendingWireRecords.RemoveAt(num);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool TryResolveSavedGroupWireRecord(WorldGroupsCodec.WorldWireRecord record, Dictionary<int, int> savedGroupIndexToRuntimeId, out WorldGroupsCodec.WorldWireRecord resolvedRecord)
|
|
{
|
|
resolvedRecord = record;
|
|
if (!WorldGroupsCodec.TryGetGroupSavedWireIndex(record, out var savedGroupIndex))
|
|
{
|
|
return true;
|
|
}
|
|
if (savedGroupIndexToRuntimeId == null || !savedGroupIndexToRuntimeId.TryGetValue(savedGroupIndex, out var value) || value <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
resolvedRecord = WorldGroupsCodec.CreateGroupRuntimeWireRecord(value, record.inputNodeId, record.inputPortName);
|
|
return true;
|
|
}
|
|
|
|
private WorldGroupsCodec.WorldNodeRecord BuildNodeRecord(Node node)
|
|
{
|
|
return new WorldGroupsCodec.WorldNodeRecord
|
|
{
|
|
nodeId = node.NodeId,
|
|
definitionName = ((node.Definition != null) ? node.Definition.name : string.Empty),
|
|
position = node.transform.position,
|
|
rotation = node.transform.rotation,
|
|
inputValues = BuildPortValueRecords(node.InputPorts),
|
|
outputValues = BuildPortValueRecords(node.OutputPorts)
|
|
};
|
|
}
|
|
|
|
private static WorldGroupsCodec.NodePortValueRecord[] BuildPortValueRecords(List<NodePort> ports)
|
|
{
|
|
List<WorldGroupsCodec.NodePortValueRecord> list = new List<WorldGroupsCodec.NodePortValueRecord>();
|
|
if (ports == null)
|
|
{
|
|
return list.ToArray();
|
|
}
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort != null && nodePort.Kind == PortKind.Value)
|
|
{
|
|
list.Add(new WorldGroupsCodec.NodePortValueRecord
|
|
{
|
|
portName = nodePort.Name,
|
|
value = nodePort.Value.Normalize()
|
|
});
|
|
}
|
|
}
|
|
return list.ToArray();
|
|
}
|
|
|
|
private Node SpawnNodeInternal(NodeDefinition definition, Vector3 position, Quaternion rotation, int forcedNodeId)
|
|
{
|
|
if (definition == null)
|
|
{
|
|
return null;
|
|
}
|
|
EnsureNodePrefabLoaded();
|
|
if (nodePrefab == null)
|
|
{
|
|
return null;
|
|
}
|
|
GameObject gameObject = UnityEngine.Object.Instantiate(nodePrefab, position, rotation);
|
|
Node component = gameObject.GetComponent<Node>();
|
|
if (component == null)
|
|
{
|
|
UnityEngine.Object.Destroy(gameObject);
|
|
return null;
|
|
}
|
|
component.Definition = definition;
|
|
component.ReinitializeFromDefinition();
|
|
component.SetNodeId((forcedNodeId > 0) ? forcedNodeId : AllocateNodeId());
|
|
component.AssignGraph(this);
|
|
component.SetVisibleInEditMode(CanEdit());
|
|
CacheDefinition(definition);
|
|
nodesById[component.NodeId] = component;
|
|
nextNodeId = Mathf.Max(nextNodeId, component.NodeId + 1);
|
|
return component;
|
|
}
|
|
|
|
private void EnsureNodePrefabLoaded()
|
|
{
|
|
if (nodePrefab == null)
|
|
{
|
|
nodePrefab = Resources.Load<GameObject>("Node");
|
|
}
|
|
}
|
|
|
|
private void CacheDefinition(NodeDefinition definition)
|
|
{
|
|
if (definition != null && !definitionsByName.ContainsKey(definition.name))
|
|
{
|
|
definitionsByName.Add(definition.name, definition);
|
|
}
|
|
}
|
|
|
|
private void CachePreloadedDefinitions()
|
|
{
|
|
if (preloadedDefinitions != null && preloadedDefinitions.Count != 0)
|
|
{
|
|
for (int i = 0; i < preloadedDefinitions.Count; i++)
|
|
{
|
|
CacheDefinition(preloadedDefinitions[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CacheLoadedDefinitions()
|
|
{
|
|
CachePreloadedDefinitions();
|
|
NodeDefinitionCatalog nodeDefinitionCatalog = Resources.Load<NodeDefinitionCatalog>("NodeDefinitionCatalog");
|
|
if (nodeDefinitionCatalog != null && nodeDefinitionCatalog.Definitions != null)
|
|
{
|
|
for (int i = 0; i < nodeDefinitionCatalog.Definitions.Count; i++)
|
|
{
|
|
CacheDefinition(nodeDefinitionCatalog.Definitions[i]);
|
|
}
|
|
}
|
|
NodeDefinition[] array = Resources.FindObjectsOfTypeAll<NodeDefinition>();
|
|
for (int j = 0; j < array.Length; j++)
|
|
{
|
|
CacheDefinition(array[j]);
|
|
}
|
|
}
|
|
|
|
private NodeDefinition ResolveDefinition(string definitionName)
|
|
{
|
|
if (string.IsNullOrEmpty(definitionName))
|
|
{
|
|
return null;
|
|
}
|
|
if (definitionsByName.TryGetValue(definitionName, out var value) && value != null)
|
|
{
|
|
return value;
|
|
}
|
|
CacheLoadedDefinitions();
|
|
if (definitionsByName.TryGetValue(definitionName, out value) && value != null)
|
|
{
|
|
return value;
|
|
}
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
if (item.Value != null && item.Value.Definition != null && item.Value.Definition.name == definitionName)
|
|
{
|
|
CacheDefinition(item.Value.Definition);
|
|
return item.Value.Definition;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private int AllocateNodeId()
|
|
{
|
|
while (nodesById.ContainsKey(nextNodeId))
|
|
{
|
|
nextNodeId++;
|
|
}
|
|
return nextNodeId++;
|
|
}
|
|
|
|
private void EnsureLineResources()
|
|
{
|
|
if (lineMaterial == null)
|
|
{
|
|
Shader shader = Shader.Find("Sprites/Default");
|
|
if (shader != null)
|
|
{
|
|
lineMaterial = new Material(shader);
|
|
}
|
|
}
|
|
if (previewWireRenderer == null)
|
|
{
|
|
previewWireRenderer = CreateLineRenderer("Node Preview Wire", 0.018f);
|
|
}
|
|
if (aimRayRenderer == null)
|
|
{
|
|
aimRayRenderer = CreateLineRenderer("Node Aim Ray", 0.0085f);
|
|
}
|
|
}
|
|
|
|
private LineRenderer CreateLineRenderer(string name, float width)
|
|
{
|
|
GameObject obj = new GameObject(name);
|
|
obj.transform.SetParent(base.transform, worldPositionStays: false);
|
|
LineRenderer lineRenderer = obj.AddComponent<LineRenderer>();
|
|
lineRenderer.positionCount = 5;
|
|
lineRenderer.loop = false;
|
|
lineRenderer.useWorldSpace = true;
|
|
lineRenderer.textureMode = LineTextureMode.Stretch;
|
|
lineRenderer.numCapVertices = 4;
|
|
lineRenderer.alignment = LineAlignment.View;
|
|
lineRenderer.widthMultiplier = width;
|
|
lineRenderer.material = lineMaterial;
|
|
lineRenderer.enabled = false;
|
|
return lineRenderer;
|
|
}
|
|
|
|
private void EnsureWireVisual(NodePort outputPort, NodePort inputPort)
|
|
{
|
|
string wireKey = GetWireKey(outputPort, inputPort);
|
|
if (!wireVisuals.ContainsKey(wireKey))
|
|
{
|
|
WireVisual wireVisual = new WireVisual
|
|
{
|
|
key = wireKey,
|
|
output = outputPort,
|
|
input = inputPort,
|
|
Kind = (outputPort?.Kind ?? PortKind.Value),
|
|
renderer = CreateLineRenderer("Wire " + wireKey, 0.015f)
|
|
};
|
|
wireVisuals.Add(wireKey, wireVisual);
|
|
UpdateWireVisual(wireVisual);
|
|
}
|
|
}
|
|
|
|
private void EnsureGroupWireVisual(int groupId, NodePort inputPort)
|
|
{
|
|
if (groupId > 0 && inputPort != null)
|
|
{
|
|
string groupWireKey = GetGroupWireKey(groupId, inputPort);
|
|
if (!wireVisuals.ContainsKey(groupWireKey))
|
|
{
|
|
WireVisual wireVisual = new WireVisual
|
|
{
|
|
key = groupWireKey,
|
|
sourceGroupId = groupId,
|
|
input = inputPort,
|
|
Kind = PortKind.Value,
|
|
renderer = CreateLineRenderer("Wire " + groupWireKey, 0.015f)
|
|
};
|
|
wireVisuals.Add(groupWireKey, wireVisual);
|
|
UpdateWireVisual(wireVisual);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RemoveWireVisual(NodePort outputPort, NodePort inputPort)
|
|
{
|
|
string wireKey = GetWireKey(outputPort, inputPort);
|
|
if (wireVisuals.TryGetValue(wireKey, out var value))
|
|
{
|
|
DestroyWireVisualRenderer(value);
|
|
wireVisuals.Remove(wireKey);
|
|
}
|
|
}
|
|
|
|
private void RemoveGroupWireVisual(int groupId, NodePort inputPort)
|
|
{
|
|
string groupWireKey = GetGroupWireKey(groupId, inputPort);
|
|
if (wireVisuals.TryGetValue(groupWireKey, out var value))
|
|
{
|
|
DestroyWireVisualRenderer(value);
|
|
wireVisuals.Remove(groupWireKey);
|
|
}
|
|
}
|
|
|
|
private void FlashWire(NodePort outputPort, NodePort inputPort)
|
|
{
|
|
string wireKey = GetWireKey(outputPort, inputPort);
|
|
if (wireVisuals.TryGetValue(wireKey, out var value))
|
|
{
|
|
value.flashUntil = Time.unscaledTime + 0.18f;
|
|
}
|
|
}
|
|
|
|
private void FlashGroupWire(int groupId, NodePort inputPort)
|
|
{
|
|
string groupWireKey = GetGroupWireKey(groupId, inputPort);
|
|
if (wireVisuals.TryGetValue(groupWireKey, out var value))
|
|
{
|
|
value.flashUntil = Time.unscaledTime + 0.18f;
|
|
}
|
|
}
|
|
|
|
private void UpdateWireVisuals()
|
|
{
|
|
wireCleanupBuffer.Clear();
|
|
foreach (KeyValuePair<string, WireVisual> wireVisual in wireVisuals)
|
|
{
|
|
WireVisual value = wireVisual.Value;
|
|
bool flag = value != null && value.sourceGroupId > 0;
|
|
if (value == null || value.renderer == null || value.input == null || value.input.Owner == null || (!flag && (value.output == null || value.output.Owner == null)))
|
|
{
|
|
wireCleanupBuffer.Add(wireVisual.Key);
|
|
}
|
|
else
|
|
{
|
|
UpdateWireVisual(value);
|
|
}
|
|
}
|
|
for (int i = 0; i < wireCleanupBuffer.Count; i++)
|
|
{
|
|
if (wireVisuals.TryGetValue(wireCleanupBuffer[i], out var value2))
|
|
{
|
|
DestroyWireVisualRenderer(value2);
|
|
wireVisuals.Remove(wireCleanupBuffer[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateWireVisual(WireVisual visual)
|
|
{
|
|
if (visual == null || visual.renderer == null || visual.input == null || visual.input.Owner == null)
|
|
{
|
|
return;
|
|
}
|
|
Vector3 portWorldPosition = visual.input.Owner.GetPortWorldPosition(visual.input);
|
|
Vector3 normalized = visual.input.Owner.GetPortWorldDirection(visual.input).normalized;
|
|
Color color;
|
|
if (visual.sourceGroupId > 0)
|
|
{
|
|
if (!TryResolveGroupSource(visual.sourceGroupId, out var group) || group == null)
|
|
{
|
|
visual.renderer.enabled = false;
|
|
return;
|
|
}
|
|
Bounds worldBounds = group.GetWorldBounds();
|
|
Vector3 vector = worldBounds.ClosestPoint(portWorldPosition);
|
|
if ((vector - portWorldPosition).sqrMagnitude < 0.0001f)
|
|
{
|
|
vector = worldBounds.center;
|
|
}
|
|
Vector3 startDirection = (((portWorldPosition - vector).sqrMagnitude > 0.0001f) ? (portWorldPosition - vector).normalized : Vector3.up);
|
|
SetCurvedLinePositions(visual.renderer, vector, startDirection, portWorldPosition, normalized, 12);
|
|
color = GroupWireColor;
|
|
}
|
|
else
|
|
{
|
|
if (visual.output == null || visual.output.Owner == null)
|
|
{
|
|
visual.renderer.enabled = false;
|
|
return;
|
|
}
|
|
Vector3 portWorldPosition2 = visual.output.Owner.GetPortWorldPosition(visual.output);
|
|
Vector3 normalized2 = visual.output.Owner.GetPortWorldDirection(visual.output).normalized;
|
|
SetCurvedLinePositions(visual.renderer, portWorldPosition2, normalized2, portWorldPosition, normalized, 12);
|
|
color = ((visual.Kind != PortKind.Run) ? (visual.output.Value.Normalize().Kind switch
|
|
{
|
|
BSValueKind.Bool => BoolWireColor,
|
|
BSValueKind.Number => NumberWireColor,
|
|
BSValueKind.Text => TextWireColor,
|
|
BSValueKind.Player => PlayerWireColor,
|
|
BSValueKind.Group => GroupWireColor,
|
|
_ => NoneWireColor,
|
|
}) : RunWireColor);
|
|
}
|
|
if (visual.flashUntil > Time.unscaledTime)
|
|
{
|
|
float t = Mathf.Clamp01((visual.flashUntil - Time.unscaledTime) / 0.18f);
|
|
color = Color.Lerp(color, WireFlashColor, t);
|
|
visual.renderer.widthMultiplier = Mathf.Lerp(0.015f, 0.024f, t);
|
|
}
|
|
else
|
|
{
|
|
visual.renderer.widthMultiplier = 0.015f;
|
|
}
|
|
visual.renderer.startColor = color;
|
|
visual.renderer.endColor = color;
|
|
visual.renderer.enabled = editVisibilityApplied;
|
|
}
|
|
|
|
private void DestroyWireVisualRenderer(WireVisual visual)
|
|
{
|
|
if (visual != null && !(visual.renderer == null))
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
UnityEngine.Object.Destroy(visual.renderer.gameObject);
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(visual.renderer.gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateTransientLines()
|
|
{
|
|
bool num = CanEdit();
|
|
bool flag = UseXRInput();
|
|
Ray ray;
|
|
if (!num)
|
|
{
|
|
if (previewWireRenderer != null)
|
|
{
|
|
previewWireRenderer.enabled = false;
|
|
}
|
|
if (aimRayRenderer != null)
|
|
{
|
|
aimRayRenderer.enabled = false;
|
|
}
|
|
}
|
|
else if (TryGetLeftRay(out ray))
|
|
{
|
|
Vector3 vector = GetPreviewRayEndpoint(ray);
|
|
if (hoveredPort != null && hoveredPort.Owner != null)
|
|
{
|
|
vector = hoveredPort.Owner.GetPortWorldPosition(hoveredPort);
|
|
}
|
|
if (aimRayRenderer != null)
|
|
{
|
|
aimRayRenderer.enabled = flag;
|
|
if (flag)
|
|
{
|
|
aimRayRenderer.positionCount = 2;
|
|
aimRayRenderer.SetPosition(0, ray.origin);
|
|
aimRayRenderer.SetPosition(1, vector);
|
|
aimRayRenderer.startColor = AimRayColor;
|
|
aimRayRenderer.endColor = AimRayColor;
|
|
}
|
|
}
|
|
if (activeWire != null && previewWireRenderer != null)
|
|
{
|
|
previewWireRenderer.enabled = true;
|
|
Vector3 end = ((hoveredPort != null && IsWireTargetCompatible(hoveredPort)) ? hoveredPort.Owner.GetPortWorldPosition(hoveredPort) : vector);
|
|
Color color = ((activeWire.Kind == PortKind.Run) ? RunWireColor : ((activeWire.startGroup != null) ? GroupWireColor : ((activeWire.startPort == null) ? NoneWireColor : (activeWire.startPort.Value.Normalize().Kind switch
|
|
{
|
|
BSValueKind.Bool => BoolWireColor,
|
|
BSValueKind.Number => NumberWireColor,
|
|
BSValueKind.Text => TextWireColor,
|
|
BSValueKind.Player => PlayerWireColor,
|
|
BSValueKind.Group => GroupWireColor,
|
|
_ => NoneWireColor,
|
|
}))));
|
|
previewWireRenderer.startColor = color;
|
|
previewWireRenderer.endColor = color;
|
|
SetCurvedLinePositions(previewWireRenderer, activeWire.startWorldPosition, ray.direction, end, -ray.direction, 30);
|
|
}
|
|
else if (previewWireRenderer != null)
|
|
{
|
|
previewWireRenderer.enabled = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (previewWireRenderer != null)
|
|
{
|
|
previewWireRenderer.enabled = false;
|
|
}
|
|
if (aimRayRenderer != null)
|
|
{
|
|
aimRayRenderer.enabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private Vector3 GetPreviewRayEndpoint(Ray ray)
|
|
{
|
|
if (Physics.Raycast(ray, out var hitInfo, wireRayDistance, wireRaycastMask, QueryTriggerInteraction.Ignore))
|
|
{
|
|
return hitInfo.point;
|
|
}
|
|
return ray.origin + ray.direction.normalized * wireRayDistance;
|
|
}
|
|
|
|
private void SetCurvedLinePositions(LineRenderer renderer, Vector3 start, Vector3 startDirection, Vector3 end, Vector3 endDirection, int vertexCount)
|
|
{
|
|
if (!(renderer == null))
|
|
{
|
|
float magnitude = (end - start).magnitude;
|
|
Vector3 vector = Vector3.up * Mathf.Clamp(magnitude * 0.12f, 0.04f, 0.18f);
|
|
float num = Mathf.Clamp(magnitude * 0.22f, 0.06f, 0.28f);
|
|
Vector3 p = start + startDirection.normalized * num + vector;
|
|
Vector3 p2 = end + endDirection.normalized * num + vector;
|
|
int num2 = (renderer.positionCount = Mathf.Max(2, vertexCount));
|
|
for (int i = 0; i < num2; i++)
|
|
{
|
|
float t = ((num2 == 1) ? 1f : ((float)i / ((float)num2 - 1f)));
|
|
renderer.SetPosition(i, EvaluateCubicBezier(start, p, p2, end, t));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Vector3 EvaluateCubicBezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
|
{
|
|
float num = 1f - t;
|
|
float num2 = num * num;
|
|
float num3 = t * t;
|
|
return num2 * num * p0 + 3f * num2 * t * p1 + 3f * num * num3 * p2 + num3 * t * p3;
|
|
}
|
|
|
|
private bool TryGetLeftRay(out Ray ray)
|
|
{
|
|
ray = default(Ray);
|
|
if (UseXRInput())
|
|
{
|
|
if (leftHandAnchor == null)
|
|
{
|
|
return false;
|
|
}
|
|
ray = new Ray(leftHandAnchor.position, leftHandAnchor.forward);
|
|
return true;
|
|
}
|
|
Camera main = Camera.main;
|
|
if (main == null)
|
|
{
|
|
return false;
|
|
}
|
|
ray = new Ray(main.transform.position, main.transform.forward);
|
|
return true;
|
|
}
|
|
|
|
private bool TryGetRightRay(out Ray ray)
|
|
{
|
|
ray = default(Ray);
|
|
if (UseXRInput())
|
|
{
|
|
if (rightHandAnchor == null)
|
|
{
|
|
return false;
|
|
}
|
|
ray = new Ray(rightHandAnchor.position, rightHandAnchor.forward);
|
|
return true;
|
|
}
|
|
Camera main = Camera.main;
|
|
if (main == null)
|
|
{
|
|
return false;
|
|
}
|
|
ray = new Ray(main.transform.position, main.transform.forward);
|
|
return true;
|
|
}
|
|
|
|
private Node TryPickNode(Ray ray)
|
|
{
|
|
Node result = null;
|
|
float num = float.PositiveInfinity;
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (value == null)
|
|
{
|
|
continue;
|
|
}
|
|
Vector3 position = value.transform.position;
|
|
float num2 = Vector3.Dot(position - ray.origin, ray.direction);
|
|
if (!(num2 < 0f) && !(num2 > wireRayDistance))
|
|
{
|
|
Vector3 vector = ray.origin + ray.direction * num2;
|
|
if (!((position - vector).sqrMagnitude > nodePickRadius * nodePickRadius) && num2 < num)
|
|
{
|
|
num = num2;
|
|
result = value;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private NodePort TryPickPort(Ray ray, float maxDistance, PortKind? requiredKind, PortDirection? requiredDirection)
|
|
{
|
|
NodePort nodePort = null;
|
|
float bestDistance = float.PositiveInfinity;
|
|
foreach (KeyValuePair<int, Node> item in nodesById)
|
|
{
|
|
Node value = item.Value;
|
|
if (!(value == null))
|
|
{
|
|
nodePort = EvaluatePorts(value.InputPorts, ray, maxDistance, requiredKind, requiredDirection, nodePort, ref bestDistance);
|
|
nodePort = EvaluatePorts(value.OutputPorts, ray, maxDistance, requiredKind, requiredDirection, nodePort, ref bestDistance);
|
|
}
|
|
}
|
|
return nodePort;
|
|
}
|
|
|
|
private NodePort EvaluatePorts(List<NodePort> ports, Ray ray, float maxDistance, PortKind? requiredKind, PortDirection? requiredDirection, NodePort bestPort, ref float bestDistance)
|
|
{
|
|
for (int i = 0; i < ports.Count; i++)
|
|
{
|
|
NodePort nodePort = ports[i];
|
|
if (nodePort == null || nodePort.Owner == null || nodePort.VisualTransform == null || (requiredKind.HasValue && nodePort.Kind != requiredKind.Value) || (requiredDirection.HasValue && nodePort.Direction != requiredDirection.Value) || !IsWireTargetCompatible(nodePort))
|
|
{
|
|
continue;
|
|
}
|
|
Vector3 portWorldPosition = nodePort.Owner.GetPortWorldPosition(nodePort);
|
|
float num = Vector3.Dot(portWorldPosition - ray.origin, ray.direction);
|
|
if (!(num < 0f) && !(num > maxDistance) && !(num > bestDistance))
|
|
{
|
|
Vector3 vector = ray.origin + ray.direction * num;
|
|
if (!((portWorldPosition - vector).sqrMagnitude > portPickRadius * portPickRadius))
|
|
{
|
|
bestDistance = num;
|
|
bestPort = nodePort;
|
|
}
|
|
}
|
|
}
|
|
return bestPort;
|
|
}
|
|
|
|
private VoxelGroup TryPickGroup(Ray ray)
|
|
{
|
|
VoxelGroup voxelGroup = TryPickGroupFromList(ray, (VRGroupSelector.Instance != null && VRGroupSelector.Instance.HasSelection) ? VRGroupSelector.Instance.GetSelectedGroupsSnapshot() : null);
|
|
if (voxelGroup != null)
|
|
{
|
|
return voxelGroup;
|
|
}
|
|
if (groupManager == null)
|
|
{
|
|
return null;
|
|
}
|
|
groupManager.GetAllGroups(groupPickBuffer);
|
|
return TryPickGroupFromList(ray, groupPickBuffer);
|
|
}
|
|
|
|
private VoxelGroup TryPickGroupFromList(Ray ray, List<VoxelGroup> groups)
|
|
{
|
|
if (groups == null || groups.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
VoxelGroup result = null;
|
|
float num = float.PositiveInfinity;
|
|
for (int i = 0; i < groups.Count; i++)
|
|
{
|
|
VoxelGroup voxelGroup = groups[i];
|
|
if (!(voxelGroup == null) && voxelGroup.GetWorldBounds().IntersectRay(ray, out var distance) && distance < num && distance <= wireRayDistance)
|
|
{
|
|
num = distance;
|
|
result = voxelGroup;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private bool TryResolveDirectionalPorts(NodePort firstPort, NodePort secondPort, out NodePort outputPort, out NodePort inputPort)
|
|
{
|
|
outputPort = null;
|
|
inputPort = null;
|
|
if (firstPort == null || secondPort == null || firstPort.Kind != secondPort.Kind)
|
|
{
|
|
return false;
|
|
}
|
|
if (firstPort.Direction == PortDirection.Output && secondPort.Direction == PortDirection.Input)
|
|
{
|
|
outputPort = firstPort;
|
|
inputPort = secondPort;
|
|
return true;
|
|
}
|
|
if (firstPort.Direction == PortDirection.Input && secondPort.Direction == PortDirection.Output)
|
|
{
|
|
outputPort = secondPort;
|
|
inputPort = firstPort;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsWireTargetCompatible(NodePort port)
|
|
{
|
|
if (port == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (activeWire == null)
|
|
{
|
|
return true;
|
|
}
|
|
if (activeWire.startGroup != null)
|
|
{
|
|
if (port.Direction == PortDirection.Input)
|
|
{
|
|
return port.Kind == PortKind.Value;
|
|
}
|
|
return false;
|
|
}
|
|
if (activeWire.startPort == null)
|
|
{
|
|
return false;
|
|
}
|
|
NodePort outputPort;
|
|
NodePort inputPort;
|
|
return TryResolveDirectionalPorts(activeWire.startPort, port, out outputPort, out inputPort);
|
|
}
|
|
|
|
private PortKind? GetExpectedWireTargetKind()
|
|
{
|
|
if (activeWire == null)
|
|
{
|
|
return null;
|
|
}
|
|
return activeWire.Kind;
|
|
}
|
|
|
|
private PortDirection? GetExpectedWireTargetDirection()
|
|
{
|
|
if (activeWire == null)
|
|
{
|
|
return null;
|
|
}
|
|
if (activeWire.startGroup != null)
|
|
{
|
|
return PortDirection.Input;
|
|
}
|
|
if (activeWire.startPort == null)
|
|
{
|
|
return null;
|
|
}
|
|
return (activeWire.startPort.Direction != PortDirection.Output) ? PortDirection.Output : PortDirection.Input;
|
|
}
|
|
|
|
private bool TryGetSelectionBounds(out Bounds bounds)
|
|
{
|
|
bounds = default(Bounds);
|
|
bool flag = false;
|
|
foreach (Node selectedNode in selectedNodes)
|
|
{
|
|
if (!(selectedNode == null))
|
|
{
|
|
Bounds worldBounds = selectedNode.GetWorldBounds();
|
|
if (!flag)
|
|
{
|
|
bounds = worldBounds;
|
|
flag = true;
|
|
}
|
|
else
|
|
{
|
|
bounds.Encapsulate(worldBounds);
|
|
}
|
|
}
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
private bool TryGetGrabReferencePose(out Vector3 worldPosition, out Quaternion worldRotation)
|
|
{
|
|
if (UseXRInput())
|
|
{
|
|
if (rightHandAnchor != null)
|
|
{
|
|
worldPosition = rightHandAnchor.position;
|
|
worldRotation = rightHandAnchor.rotation;
|
|
return true;
|
|
}
|
|
if (trackingOrigin != null && rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out var value) && rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out var value2))
|
|
{
|
|
worldPosition = trackingOrigin.TransformPoint(value);
|
|
worldRotation = trackingOrigin.rotation * value2;
|
|
return true;
|
|
}
|
|
}
|
|
Camera main = Camera.main;
|
|
if (main != null)
|
|
{
|
|
worldPosition = main.transform.position;
|
|
worldRotation = main.transform.rotation;
|
|
return true;
|
|
}
|
|
worldPosition = base.transform.position;
|
|
worldRotation = base.transform.rotation;
|
|
return false;
|
|
}
|
|
|
|
private static float ProjectBoundsAlongRay(Bounds bounds, Ray ray)
|
|
{
|
|
return Vector3.Dot(bounds.center - ray.origin, ray.direction);
|
|
}
|
|
|
|
private static Vector3 SnapVector(Vector3 value, float snap)
|
|
{
|
|
if (snap <= 0.0001f)
|
|
{
|
|
return value;
|
|
}
|
|
return new Vector3(Mathf.Round(value.x / snap) * snap, Mathf.Round(value.y / snap) * snap, Mathf.Round(value.z / snap) * snap);
|
|
}
|
|
|
|
private static Quaternion SnapRotation(Quaternion rotation, float snapDegrees)
|
|
{
|
|
if (snapDegrees <= 0.001f)
|
|
{
|
|
return rotation;
|
|
}
|
|
Vector3 eulerAngles = rotation.eulerAngles;
|
|
eulerAngles.x = Mathf.Round(eulerAngles.x / snapDegrees) * snapDegrees;
|
|
eulerAngles.y = Mathf.Round(eulerAngles.y / snapDegrees) * snapDegrees;
|
|
eulerAngles.z = Mathf.Round(eulerAngles.z / snapDegrees) * snapDegrees;
|
|
return Quaternion.Euler(eulerAngles);
|
|
}
|
|
|
|
private string GetWireKey(NodePort outputPort, NodePort inputPort)
|
|
{
|
|
int num = ((outputPort != null && outputPort.Owner != null) ? outputPort.Owner.NodeId : 0);
|
|
int num2 = ((inputPort != null && inputPort.Owner != null) ? inputPort.Owner.NodeId : 0);
|
|
return $"{num}:{outputPort?.Name}->{num2}:{inputPort?.Name}";
|
|
}
|
|
|
|
private string GetGroupWireKey(int groupId, NodePort inputPort)
|
|
{
|
|
int num = ((inputPort != null && inputPort.Owner != null) ? inputPort.Owner.NodeId : 0);
|
|
return $"group:{groupId}->{num}:{inputPort?.Name}";
|
|
}
|
|
|
|
private bool TryResolveGroupSource(int groupId, out VoxelGroup group)
|
|
{
|
|
group = null;
|
|
if (groupManager == null || groupId <= 0 || groupId > 65535)
|
|
{
|
|
return false;
|
|
}
|
|
return groupManager.TryGetGroup((ushort)groupId, out group);
|
|
}
|
|
|
|
private bool CanEdit()
|
|
{
|
|
if (groupManager != null && groupManager.CreatorMode)
|
|
{
|
|
return !groupManager.EditingLocked;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool UseXRInput()
|
|
{
|
|
if (brush != null)
|
|
{
|
|
return brush.UsingXRInput;
|
|
}
|
|
if (!InputDevices.GetDeviceAtXRNode(XRNode.Head).isValid && !InputDevices.GetDeviceAtXRNode(XRNode.LeftHand).isValid)
|
|
{
|
|
return InputDevices.GetDeviceAtXRNode(XRNode.RightHand).isValid;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private NodeGraphPhotonSync ResolveNetworkSync()
|
|
{
|
|
if (networkSync == null)
|
|
{
|
|
networkSync = GetComponent<NodeGraphPhotonSync>();
|
|
}
|
|
return networkSync;
|
|
}
|
|
|
|
private static int CompareNodesByHierarchyPath(Node a, Node b)
|
|
{
|
|
if ((object)a == b)
|
|
{
|
|
return 0;
|
|
}
|
|
if (a == null)
|
|
{
|
|
return -1;
|
|
}
|
|
if (b == null)
|
|
{
|
|
return 1;
|
|
}
|
|
return string.CompareOrdinal(GetHierarchyPath(a.transform), GetHierarchyPath(b.transform));
|
|
}
|
|
|
|
private static string GetHierarchyPath(Transform target)
|
|
{
|
|
if (target == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
List<string> list = new List<string>();
|
|
Transform transform = target;
|
|
while (transform != null)
|
|
{
|
|
list.Add($"{transform.GetSiblingIndex():D4}_{transform.name}");
|
|
transform = transform.parent;
|
|
}
|
|
list.Reverse();
|
|
return string.Join("/", list);
|
|
}
|
|
|
|
private static bool ReadBool(InputDevice device, InputFeatureUsage<bool> usage)
|
|
{
|
|
bool value = default(bool);
|
|
return device.isValid && device.TryGetFeatureValue(usage, out value) && value;
|
|
}
|
|
|
|
private static float ReadFloat(InputDevice device, InputFeatureUsage<float> usage)
|
|
{
|
|
if (!device.isValid || !device.TryGetFeatureValue(usage, out var value))
|
|
{
|
|
return 0f;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private bool ReadGrabHeld(InputDevice device)
|
|
{
|
|
return ReadFloat(device, CommonUsages.grip) >= rightGripThreshold;
|
|
}
|
|
|
|
private static bool BSValueEquals(BSValue a, BSValue b)
|
|
{
|
|
a = a.Normalize();
|
|
b = b.Normalize();
|
|
if (a.Kind == b.Kind && Math.Abs(a.Number - b.Number) <= 0.0001 && string.Equals(a.Text ?? string.Empty, b.Text ?? string.Empty, StringComparison.Ordinal) && a.PlayerId == b.PlayerId)
|
|
{
|
|
return a.GroupId == b.GroupId;
|
|
}
|
|
return false;
|
|
}
|
|
}
|