This commit is contained in:
Jedd Morgan 2023-08-31 12:08:35 +01:00
Родитель 5d92e12eff
Коммит aa46d49620
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: BBD1F7EA4F833F16
40 изменённых файлов: 1851 добавлений и 1396 удалений

Просмотреть файл

@ -2,11 +2,8 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using Speckle.ConnectorUnity; using Speckle.ConnectorUnity;
using UnityEditor.Experimental;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(Sender)), ExecuteAlways] [RequireComponent(typeof(Sender)), ExecuteAlways]
[Obsolete] [Obsolete]
@ -33,10 +30,16 @@ public class SendChildrenToSpeckle : MonoBehaviour
.ToImmutableHashSet(); .ToImmutableHashSet();
Debug.Log("starting send..."); Debug.Log("starting send...");
sender.Send(streamId, selected, null, branchName, createCommit, sender.Send(
streamId,
selected,
null,
branchName,
createCommit,
onErrorAction: OnError, onErrorAction: OnError,
onProgressAction: OnProgress, onProgressAction: OnProgress,
onDataSentAction: OnSent); onDataSentAction: OnSent
);
} }
private void OnSent(string objectId) private void OnSent(string objectId)
@ -47,14 +50,10 @@ public class SendChildrenToSpeckle : MonoBehaviour
private void OnError(string message, Exception e) private void OnError(string message, Exception e)
{ {
Debug.LogError($"Error while sending {message} \n {e}", this); Debug.LogError($"Error while sending {message} \n {e}", this);
} }
private void OnProgress(ConcurrentDictionary<string, int> dict) private void OnProgress(ConcurrentDictionary<string, int> dict)
{ {
Debug.Log($"progress was made", this); Debug.Log($"progress was made", this);
} }
} }

Просмотреть файл

@ -1,10 +1,8 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Speckle.Core.Api; using Speckle.Core.Api;
using Speckle.Core.Logging;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using Text = UnityEngine.UI.Text; using Text = UnityEngine.UI.Text;
@ -12,209 +10,235 @@ using Text = UnityEngine.UI.Text;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
[Obsolete] [Obsolete]
public class InteractionLogic : MonoBehaviour public class InteractionLogic : MonoBehaviour
{
private Receiver receiver;
public void InitReceiver(Stream stream, bool autoReceive)
{ {
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}"; private Receiver _receiver;
InitRemove();
receiver = gameObject.AddComponent<Receiver>(); public void InitReceiver(Stream stream, bool autoReceive)
receiver.Stream = stream;
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
var branchesDropdown = gameObject.transform.Find("Dropdown").GetComponentInChildren<Dropdown>();
var receiveProgress = btn.GetComponentInChildren<Slider>();
receiveProgress.gameObject.SetActive(false); //hide
//populate branches
branchesDropdown.options.Clear();
List<Branch> branches = receiver.Stream.branches.items;
branches.Reverse();
foreach (Branch branch in branches)
{
branchesDropdown.options.Add(new Dropdown.OptionData(branch.name.Replace(' ', '\u00A0')));
}
//trigger ui refresh, maybe there's a better method
branchesDropdown.value = -1;
branchesDropdown.value = 0;
branchesDropdown.onValueChanged.AddListener(index =>
{
if (index == -1)
return;
receiver.BranchName = branches[index].name;
});
receiver.Init(stream.id, autoReceive, true,
onDataReceivedAction: (go) =>
{ {
statusText.text = $"Received {go.name}"; gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
MakeButtonsInteractable(true); InitRemove();
receiveProgress.value = 0;
receiveProgress.gameObject.SetActive(false);
AddComponents(go); _receiver = gameObject.AddComponent<Receiver>();
}, _receiver.Stream = stream;
onTotalChildrenCountKnown: (count) => { receiver.TotalChildrenCount = count; },
onProgressAction: (dict) =>
{
//Run on a dispatcher as GOs can only be retrieved on the main thread
Dispatcher.Instance().Enqueue(() =>
{
var val = dict.Values.Average() / receiver.TotalChildrenCount;
receiveProgress.gameObject.SetActive(true);
receiveProgress.value = (float) val;
});
});
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
var branchesDropdown = gameObject.transform
.Find("Dropdown")
.GetComponentInChildren<Dropdown>();
var receiveProgress = btn.GetComponentInChildren<Slider>();
receiveProgress.gameObject.SetActive(false); //hide
streamText.text = $"Stream: {stream.name}\nId: {stream.id} - Auto: {autoReceive}"; //populate branches
btn.onClick.AddListener(() => branchesDropdown.options.Clear();
{ List<Branch> branches = _receiver.Stream.branches.items;
statusText.text = "Receiving..."; branches.Reverse();
MakeButtonsInteractable(false); foreach (Branch branch in branches)
receiver.Receive();
});
}
/// <summary>
/// Recursively adds custom components to all children of a GameObject
/// </summary>
/// <param name="go"></param>
private void AddComponents(GameObject go)
{
for (var i = 0; i < go.transform.childCount; i++)
{
var child = go.transform.GetChild(i);
if (child.childCount > 0)
{
AddComponents(child.gameObject);
}
child.gameObject.AddComponent<Selectable>();
//Add extra Components
//var rigidbody = child.gameObject.AddComponent<Rigidbody>();
//rigidbody.mass = 10;
}
}
public void InitSender(Stream stream)
{
gameObject.name = $"sender-{stream.id}-{Guid.NewGuid().ToString()}";
InitRemove();
var sender = gameObject.AddComponent<Sender>();
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
btn.GetComponentInChildren<Text>().text = "Send";
statusText.text = "Ready to send";
var sendProgress = btn.GetComponentInChildren<Slider>();
sendProgress.gameObject.SetActive(false); //hide
streamText.text = $"Stream: {stream.name}\nId: {stream.id}";
btn.onClick.AddListener(() =>
{
var objs = SelectionManager.selectedObjects.Select(s => s.gameObject).ToImmutableHashSet();
if (!objs.Any())
{
statusText.text = $"No objects selected";
return;
}
MakeButtonsInteractable(false);
statusText.text = "Sending...";
try
{
sender.Send(stream.id, objs,
onProgressAction: (dict) =>
{
//Run on a dispatcher as GOs can only be retrieved on the main thread
Dispatcher.Instance().Enqueue(() =>
{
var val = dict.Values.Average() / objs.Count;
sendProgress.gameObject.SetActive(true);
sendProgress.value = (float) val;
});
},
onDataSentAction: (objectId) =>
{
Debug.Log($"Send operation completed, object id: {objectId}", this);
Dispatcher.Instance().Enqueue(() =>
{
MakeButtonsInteractable(true);
statusText.text = $"Sent {objectId}";
sendProgress.gameObject.SetActive(false); //hide
});
},
onErrorAction: (message, e) =>
{
Debug.LogError("Send operation Failed!", this);
Dispatcher.Instance().Enqueue(() =>
{
MakeButtonsInteractable(true);
statusText.text = $"Error {message}";
sendProgress.gameObject.SetActive(false); //hide
Debug.LogError(e, this);
});
});
}
catch(Exception e)
{
Dispatcher.Instance().Enqueue(() =>
{ {
MakeButtonsInteractable(true); branchesDropdown.options.Add(
statusText.text = $"Error {e.Message}"; new Dropdown.OptionData(branch.name.Replace(' ', '\u00A0'))
sendProgress.gameObject.SetActive(false); //hide );
Debug.LogError(e, this); }
//trigger ui refresh, maybe there's a better method
branchesDropdown.value = -1;
branchesDropdown.value = 0;
branchesDropdown.onValueChanged.AddListener(index =>
{
if (index == -1)
return;
_receiver.BranchName = branches[index].name;
});
_receiver.Init(
stream.id,
autoReceive,
onDataReceivedAction: (go) =>
{
statusText.text = $"Received {go.name}";
MakeButtonsInteractable(true);
receiveProgress.value = 0;
receiveProgress.gameObject.SetActive(false);
AddComponents(go);
},
onTotalChildrenCountKnown: (count) =>
{
_receiver.TotalChildrenCount = count;
},
onProgressAction: (dict) =>
{
//Run on a dispatcher as GOs can only be retrieved on the main thread
Dispatcher
.Instance()
.Enqueue(() =>
{
var val = dict.Values.Average() / _receiver.TotalChildrenCount;
receiveProgress.gameObject.SetActive(true);
receiveProgress.value = (float)val;
});
}
);
streamText.text = $"Stream: {stream.name}\nId: {stream.id} - Auto: {autoReceive}";
btn.onClick.AddListener(() =>
{
statusText.text = "Receiving...";
MakeButtonsInteractable(false);
_receiver.Receive();
}); });
}
} }
);
}
private void MakeButtonsInteractable(bool interactable) /// <summary>
{ /// Recursively adds custom components to all children of a GameObject
var selectables = gameObject.transform.GetComponentsInChildren<UnityEngine.UI.Selectable>(); /// </summary>
foreach (var selectable in selectables) /// <param name="go"></param>
{ private void AddComponents(GameObject go)
selectable.interactable = interactable;
}
}
private void InitRemove()
{
var close = gameObject.transform.Find("Close").GetComponentInChildren<Button>();
close.onClick.AddListener(() =>
{
//remove received geometry
if (receiver != null)
{ {
Destroy(receiver.ReceivedData); for (var i = 0; i < go.transform.childCount; i++)
{
var child = go.transform.GetChild(i);
if (child.childCount > 0)
{
AddComponents(child.gameObject);
}
child.gameObject.AddComponent<Selectable>();
//Add extra Components
//var rigidbody = child.gameObject.AddComponent<Rigidbody>();
//rigidbody.mass = 10;
}
} }
//update ui public void InitSender(Stream stream)
GameObject.Find("_SpeckleExamples").GetComponent<SpeckleExamples>().RemoveStreamPrefab(gameObject); {
gameObject.name = $"sender-{stream.id}-{Guid.NewGuid().ToString()}";
InitRemove();
//kill it var sender = gameObject.AddComponent<Sender>();
Destroy(gameObject); var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
});
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
btn.GetComponentInChildren<Text>().text = "Send";
statusText.text = "Ready to send";
var sendProgress = btn.GetComponentInChildren<Slider>();
sendProgress.gameObject.SetActive(false); //hide
streamText.text = $"Stream: {stream.name}\nId: {stream.id}";
btn.onClick.AddListener(() =>
{
var objs = SelectionManager.selectedObjects
.Select(s => s.gameObject)
.ToImmutableHashSet();
if (!objs.Any())
{
statusText.text = $"No objects selected";
return;
}
MakeButtonsInteractable(false);
statusText.text = "Sending...";
try
{
sender.Send(
stream.id,
objs,
onProgressAction: (dict) =>
{
//Run on a dispatcher as GOs can only be retrieved on the main thread
Dispatcher
.Instance()
.Enqueue(() =>
{
var val = dict.Values.Average() / objs.Count;
sendProgress.gameObject.SetActive(true);
sendProgress.value = (float)val;
});
},
onDataSentAction: (objectId) =>
{
Debug.Log($"Send operation completed, object id: {objectId}", this);
Dispatcher
.Instance()
.Enqueue(() =>
{
MakeButtonsInteractable(true);
statusText.text = $"Sent {objectId}";
sendProgress.gameObject.SetActive(false); //hide
});
},
onErrorAction: (message, e) =>
{
Debug.LogError("Send operation Failed!", this);
Dispatcher
.Instance()
.Enqueue(() =>
{
MakeButtonsInteractable(true);
statusText.text = $"Error {message}";
sendProgress.gameObject.SetActive(false); //hide
Debug.LogError(e, this);
});
}
);
}
catch (Exception e)
{
Dispatcher
.Instance()
.Enqueue(() =>
{
MakeButtonsInteractable(true);
statusText.text = $"Error {e.Message}";
sendProgress.gameObject.SetActive(false); //hide
Debug.LogError(e, this);
});
}
});
}
private void MakeButtonsInteractable(bool interactable)
{
var selectables =
gameObject.transform.GetComponentsInChildren<UnityEngine.UI.Selectable>();
foreach (var selectable in selectables)
{
selectable.interactable = interactable;
}
}
private void InitRemove()
{
var close = gameObject.transform.Find("Close").GetComponentInChildren<Button>();
close.onClick.AddListener(() =>
{
//remove received geometry
if (_receiver != null)
{
Destroy(_receiver.ReceivedData);
}
//update ui
GameObject
.Find("_SpeckleExamples")
.GetComponent<SpeckleExamples>()
.RemoveStreamPrefab(gameObject);
//kill it
Destroy(gameObject);
});
}
} }
}
} }

Просмотреть файл

@ -10,125 +10,123 @@ using Stream = Speckle.Core.Api.Stream;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
[Obsolete] [Obsolete]
public class SpeckleExamples : MonoBehaviour public class SpeckleExamples : MonoBehaviour
{
public Text SelectStreamText;
public Text DetailsStreamText;
public Dropdown StreamSelectionDropdown;
public Button AddReceiverBtn;
public Toggle AutoReceiveToggle;
public Button AddSenderBtn;
public GameObject StreamPanel;
public Canvas StreamsCanvas;
private List<Stream> StreamList = null;
private Stream SelectedStream = null;
private List<GameObject> StreamPanels = new List<GameObject>();
async void Start()
{ {
if (SelectStreamText == null || StreamSelectionDropdown == null) public Text SelectStreamText;
{ public Text DetailsStreamText;
Debug.Log("Please set all input fields on _SpeckleExamples"); public Dropdown StreamSelectionDropdown;
return; public Button AddReceiverBtn;
} public Toggle AutoReceiveToggle;
public Button AddSenderBtn;
public GameObject StreamPanel;
public Canvas StreamsCanvas;
var defaultAccount = AccountManager.GetDefaultAccount(); private List<Stream> StreamList = null;
if (defaultAccount == null) private Stream SelectedStream = null;
{ private List<GameObject> StreamPanels = new List<GameObject>();
Debug.Log("Please set a default account in SpeckleManager");
return;
}
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:"; async void Start()
{
if (SelectStreamText == null || StreamSelectionDropdown == null)
{
Debug.Log("Please set all input fields on _SpeckleExamples");
return;
}
StreamList = await Streams.List(30); var defaultAccount = AccountManager.GetDefaultAccount();
if (!StreamList.Any()) if (defaultAccount == null)
{ {
Debug.Log("There are no streams in your account, please create one online."); Debug.Log("Please set a default account in SpeckleManager");
return; return;
} }
StreamSelectionDropdown.options.Clear(); SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
foreach (var stream in StreamList)
{
StreamSelectionDropdown.options.Add(new Dropdown.OptionData(stream.name + " - " + stream.id));
}
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged); StreamList = await Streams.List(30);
//trigger ui refresh, maybe there's a better method if (!StreamList.Any())
StreamSelectionDropdown.value = -1; {
StreamSelectionDropdown.value = 0; Debug.Log("There are no streams in your account, please create one online.");
return;
}
StreamSelectionDropdown.options.Clear();
foreach (var stream in StreamList)
{
StreamSelectionDropdown.options.Add(
new Dropdown.OptionData(stream.name + " - " + stream.id)
);
}
AddReceiverBtn.onClick.AddListener(AddReceiver); StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
AddSenderBtn.onClick.AddListener(AddSender); //trigger ui refresh, maybe there's a better method
StreamSelectionDropdown.value = -1;
StreamSelectionDropdown.value = 0;
AddReceiverBtn.onClick.AddListener(AddReceiver);
AddSenderBtn.onClick.AddListener(AddSender);
}
public void StreamSelectionChanged(int index)
{
if (index == -1)
return;
SelectedStream = StreamList[index];
DetailsStreamText.text =
$"Description: {SelectedStream.description}\n"
+ $"Link sharing on: {SelectedStream.isPublic}\n"
+ $"Role: {SelectedStream.role}\n"
+ $"Collaborators: {SelectedStream.collaborators.Count}\n"
+ $"Id: {SelectedStream.id}";
}
// Shows how to create a new Receiver from code and then pull data manually
// Created receivers are added to a List of Receivers for future use
private async void AddReceiver()
{
var autoReceive = AutoReceiveToggle.isOn;
var stream = await Streams.Get(SelectedStream.id, 30);
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0), Quaternion.identity);
//set position
streamPrefab.transform.SetParent(StreamsCanvas.transform);
var rt = streamPrefab.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
streamPrefab.AddComponent<InteractionLogic>().InitReceiver(stream, autoReceive);
StreamPanels.Add(streamPrefab);
}
private async void AddSender()
{
var stream = await Streams.Get(SelectedStream.id, 10);
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0), Quaternion.identity);
streamPrefab.transform.SetParent(StreamsCanvas.transform);
var rt = streamPrefab.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
streamPrefab.AddComponent<InteractionLogic>().InitSender(stream);
StreamPanels.Add(streamPrefab);
}
public void RemoveStreamPrefab(GameObject streamPrefab)
{
StreamPanels.RemoveAt(StreamPanels.FindIndex(x => x.name == streamPrefab.name));
ReorderStreamPrefabs();
}
private void ReorderStreamPrefabs()
{
for (var i = 0; i < StreamPanels.Count; i++)
{
var rt = StreamPanels[i].GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - i * 110, 0);
}
}
} }
public void StreamSelectionChanged(int index)
{
if (index == -1)
return;
SelectedStream = StreamList[index];
DetailsStreamText.text =
$"Description: {SelectedStream.description}\n" +
$"Link sharing on: {SelectedStream.isPublic}\n" +
$"Role: {SelectedStream.role}\n" +
$"Collaborators: {SelectedStream.collaborators.Count}\n" +
$"Id: {SelectedStream.id}";
}
// Shows how to create a new Receiver from code and then pull data manually
// Created receivers are added to a List of Receivers for future use
private async void AddReceiver()
{
var autoReceive = AutoReceiveToggle.isOn;
var stream = await Streams.Get(SelectedStream.id, 30);
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0),
Quaternion.identity);
//set position
streamPrefab.transform.SetParent(StreamsCanvas.transform);
var rt = streamPrefab.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
streamPrefab.AddComponent<InteractionLogic>().InitReceiver(stream, autoReceive);
StreamPanels.Add(streamPrefab);
}
private async void AddSender()
{
var stream = await Streams.Get(SelectedStream.id, 10);
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0),
Quaternion.identity);
streamPrefab.transform.SetParent(StreamsCanvas.transform);
var rt = streamPrefab.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
streamPrefab.AddComponent<InteractionLogic>().InitSender(stream);
StreamPanels.Add(streamPrefab);
}
public void RemoveStreamPrefab(GameObject streamPrefab)
{
StreamPanels.RemoveAt(StreamPanels.FindIndex(x => x.name == streamPrefab.name));
ReorderStreamPrefabs();
}
private void ReorderStreamPrefabs()
{
for (var i = 0; i < StreamPanels.Count; i++)
{
var rt = StreamPanels[i].GetComponent<RectTransform>();
rt.anchoredPosition = new Vector3(-10, -110 - i * 110, 0);
}
}
}
} }

Просмотреть файл

@ -13,9 +13,9 @@ namespace Speckle.ConnectorUnity.Components.Editor
[CustomEditor(typeof(SpeckleReceiver))] [CustomEditor(typeof(SpeckleReceiver))]
public class SpeckleReceiverEditor : UnityEditor.Editor public class SpeckleReceiverEditor : UnityEditor.Editor
{ {
private static bool generateAssets = false; private static bool _generateAssets = false;
private bool foldOutStatus = true; private bool _foldOutStatus = true;
private Texture2D? previewImage; private Texture2D? _previewImage;
public override async void OnInspectorGUI() public override async void OnInspectorGUI()
{ {
@ -25,11 +25,12 @@ namespace Speckle.ConnectorUnity.Components.Editor
//Preview image //Preview image
{ {
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image"); _foldOutStatus = EditorGUILayout.Foldout(_foldOutStatus, "Preview Image");
if (foldOutStatus) if (_foldOutStatus)
{ {
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f); Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
if (previewImage != null) GUI.DrawTexture(rect, previewImage); if (_previewImage != null)
GUI.DrawTexture(rect, _previewImage);
} }
} }
@ -42,10 +43,10 @@ namespace Speckle.ConnectorUnity.Components.Editor
//Receive button //Receive button
bool userRequestedReceive = GUILayout.Button("Receive!"); bool userRequestedReceive = GUILayout.Button("Receive!");
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets); bool selection = EditorGUILayout.ToggleLeft("Generate Assets", _generateAssets);
if (generateAssets != selection) if (_generateAssets != selection)
{ {
generateAssets = selection; _generateAssets = selection;
UpdateGenerateAssets(); UpdateGenerateAssets();
} }
GUI.enabled = prev; GUI.enabled = prev;
@ -54,14 +55,19 @@ namespace Speckle.ConnectorUnity.Components.Editor
{ {
var value = Progress.globalProgress; //NOTE: this may include non-speckle items... var value = Progress.globalProgress; //NOTE: this may include non-speckle items...
var percent = Math.Max(0, Mathf.Ceil(value * 100)); var percent = Math.Max(0, Mathf.Ceil(value * 100));
var rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight); var rect = EditorGUILayout.GetControlRect(
false,
EditorGUIUtility.singleLineHeight
);
EditorGUI.ProgressBar(rect, value, $"{percent}%"); EditorGUI.ProgressBar(rect, value, $"{percent}%");
} }
else if (userRequestedReceive) else if (userRequestedReceive)
{ {
var id = Progress.Start( var id = Progress.Start(
"Receiving Speckle data", "Receiving Speckle data",
"Fetching commit data", Progress.Options.Sticky); "Fetching commit data",
Progress.Options.Sticky
);
Progress.ShowDetails(); Progress.ShowDetails();
try try
@ -97,10 +103,9 @@ namespace Speckle.ConnectorUnity.Components.Editor
Init(); Init();
} }
private void Init() private void Init()
{ {
var speckleReceiver = (SpeckleReceiver) target; var speckleReceiver = (SpeckleReceiver)target;
UpdatePreviewImage(); UpdatePreviewImage();
speckleReceiver.OnCommitSelectionChange.AddListener(_ => UpdatePreviewImage()); speckleReceiver.OnCommitSelectionChange.AddListener(_ => UpdatePreviewImage());
UpdateGenerateAssets(); UpdateGenerateAssets();
@ -108,8 +113,8 @@ namespace Speckle.ConnectorUnity.Components.Editor
private void UpdatePreviewImage() private void UpdatePreviewImage()
{ {
previewImage = null; _previewImage = null;
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t); ((SpeckleReceiver)target).GetPreviewImage(t => _previewImage = t);
} }
private async Task ReceiveSelection(int progressId) private async Task ReceiveSelection(int progressId)
@ -118,21 +123,25 @@ namespace Speckle.ConnectorUnity.Components.Editor
bool shouldCancel = false; bool shouldCancel = false;
Progress.RegisterCancelCallback(progressId, () => Progress.RegisterCancelCallback(
{ progressId,
speckleReceiver.Cancel(); () =>
shouldCancel = true; {
return true; speckleReceiver.Cancel();
}); shouldCancel = true;
return true;
}
);
Base commitObject; Base commitObject;
try try
{ {
var token = speckleReceiver.BeginOperation(); var token = speckleReceiver.BeginOperation();
commitObject = await Task.Run(async () => await ReceiveCommit(progressId).ConfigureAwait(false), commitObject = await Task.Run(
token async () => await ReceiveCommit(progressId).ConfigureAwait(false),
) token
.ConfigureAwait(true); )
.ConfigureAwait(true);
} }
finally finally
{ {
@ -142,10 +151,15 @@ namespace Speckle.ConnectorUnity.Components.Editor
int childrenConverted = 0; int childrenConverted = 0;
int childrenFailed = 0; int childrenFailed = 0;
int totalChildren = (int) Math.Min(commitObject.totalChildrenCount, int.MaxValue); int totalChildren = (int)Math.Min(commitObject.totalChildrenCount, int.MaxValue);
float totalChildrenFloat = commitObject.totalChildrenCount; float totalChildrenFloat = commitObject.totalChildrenCount;
var convertProgress = Progress.Start("Converting To Native", "Preparing...", Progress.Options.Indefinite | Progress.Options.Sticky, progressId); var convertProgress = Progress.Start(
"Converting To Native",
"Preparing...",
Progress.Options.Indefinite | Progress.Options.Sticky,
progressId
);
bool BeforeConvert(TraversalContext context) bool BeforeConvert(TraversalContext context)
{ {
@ -154,20 +168,25 @@ namespace Speckle.ConnectorUnity.Components.Editor
//NOTE: progress wont reach 100% because not all objects are convertable //NOTE: progress wont reach 100% because not all objects are convertable
float progress = (childrenConverted + childrenFailed) / totalChildrenFloat; float progress = (childrenConverted + childrenFailed) / totalChildrenFloat;
if (shouldCancel) return false; if (shouldCancel)
return false;
shouldCancel = EditorUtility.DisplayCancelableProgressBar( shouldCancel = EditorUtility.DisplayCancelableProgressBar(
"Converting To Native...", "Converting To Native...",
$"{b.speckle_type} - {b.id}", $"{b.speckle_type} - {b.id}",
progress); progress
);
return !shouldCancel; return !shouldCancel;
} }
foreach (var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable( foreach (
commitObject, var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
speckleReceiver.transform, commitObject,
BeforeConvert)) speckleReceiver.transform,
BeforeConvert
)
)
{ {
Base speckleObject = conversionResult.SpeckleObject; Base speckleObject = conversionResult.SpeckleObject;
if (conversionResult.WasSuccessful(out _, out var ex)) if (conversionResult.WasSuccessful(out _, out var ex))
@ -179,31 +198,45 @@ namespace Speckle.ConnectorUnity.Components.Editor
childrenFailed++; childrenFailed++;
Debug.LogWarning( Debug.LogWarning(
$"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}", $"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}",
this); this
);
} }
Progress.Report(progressId, childrenConverted + childrenFailed, totalChildren, "Receiving objects"); Progress.Report(
progressId,
childrenConverted + childrenFailed,
totalChildren,
"Receiving objects"
);
if (shouldCancel) break; if (shouldCancel)
break;
} }
var resultString = $"{childrenConverted} {nameof(GameObject)}s created"; var resultString = $"{childrenConverted} {nameof(GameObject)}s created";
if (childrenFailed != 0) resultString += $", {childrenFailed} objects failed to convert!"; if (childrenFailed != 0)
resultString += $", {childrenFailed} objects failed to convert!";
Debug.Log(shouldCancel Debug.Log(
shouldCancel
? $"Stopped converting to native: The operation has been cancelled - {resultString}\n " ? $"Stopped converting to native: The operation has been cancelled - {resultString}\n "
: $"Finished converting to native.\n{resultString}", : $"Finished converting to native.\n{resultString}",
speckleReceiver); speckleReceiver
);
Progress.Finish(convertProgress); Progress.Finish(convertProgress);
if (shouldCancel) throw new OperationCanceledException("Conversion operation canceled through editor dialogue"); if (shouldCancel)
throw new OperationCanceledException(
"Conversion operation canceled through editor dialogue"
);
} }
private void UpdateGenerateAssets() private void UpdateGenerateAssets()
{ {
var speckleReceiver = (SpeckleReceiver) target; var speckleReceiver = (SpeckleReceiver)target;
speckleReceiver.Converter.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets); speckleReceiver.Converter.AssetCache.nativeCaches =
NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
} }
private async Task<Base> ReceiveCommit(int progressId) private async Task<Base> ReceiveCommit(int progressId)
@ -212,8 +245,18 @@ namespace Speckle.ConnectorUnity.Components.Editor
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle"; string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
int transport = Progress.Start($"Downloading data from {serverLogName}", "Waiting...", Progress.Options.Sticky, progressId); int transport = Progress.Start(
int deserialize = Progress.Start("Deserializing data", "Waiting...", Progress.Options.Sticky, progressId); $"Downloading data from {serverLogName}",
"Waiting...",
Progress.Options.Sticky,
progressId
);
int deserialize = Progress.Start(
"Deserializing data",
"Waiting...",
Progress.Options.Sticky,
progressId
);
Progress.SetPriority(transport, Progress.Priority.High); Progress.SetPriority(transport, Progress.Priority.High);
var totalObjectCount = 1; var totalObjectCount = 1;
@ -230,14 +273,24 @@ namespace Speckle.ConnectorUnity.Components.Editor
if (r || l) if (r || l)
{ {
var fetched = (rtProgress + ltProgress); var fetched = (rtProgress + ltProgress);
Progress.Report(transport, fetched, totalObjectCount, $"{fetched}/{totalObjectCount}"); Progress.Report(
transport,
fetched,
totalObjectCount,
$"{fetched}/{totalObjectCount}"
);
} }
if (dict.TryGetValue("DS", out int tsProgress)) if (dict.TryGetValue("DS", out int tsProgress))
{ {
tsProgress--; //The root object isn't included, so we add an extra 1 tsProgress--; //The root object isn't included, so we add an extra 1
Progress.Report(deserialize,tsProgress, totalObjectCount, $"{tsProgress}/{totalObjectCount}"); Progress.Report(
Progress.Report(progressId,tsProgress, totalObjectCount); deserialize,
tsProgress,
totalObjectCount,
$"{tsProgress}/{totalObjectCount}"
);
Progress.Report(progressId, tsProgress, totalObjectCount);
} }
} }
@ -246,18 +299,19 @@ namespace Speckle.ConnectorUnity.Components.Editor
{ {
speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown); speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown);
speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress); speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress);
commitObject = await speckleReceiver.ReceiveAsync(speckleReceiver.CancellationToken) commitObject = await speckleReceiver
.ReceiveAsync(speckleReceiver.CancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
Progress.Finish(transport); Progress.Finish(transport);
Progress.Finish(deserialize); Progress.Finish(deserialize);
} }
catch(OperationCanceledException) catch (OperationCanceledException)
{ {
Progress.Finish(transport, Progress.Status.Canceled); Progress.Finish(transport, Progress.Status.Canceled);
Progress.Finish(deserialize, Progress.Status.Canceled); Progress.Finish(deserialize, Progress.Status.Canceled);
throw; throw;
} }
catch(Exception) catch (Exception)
{ {
Progress.Finish(transport, Progress.Status.Failed); Progress.Finish(transport, Progress.Status.Failed);
Progress.Finish(deserialize, Progress.Status.Failed); Progress.Finish(deserialize, Progress.Status.Failed);
@ -289,10 +343,11 @@ namespace Speckle.ConnectorUnity.Components.Editor
go.AddComponent<SpeckleSender>(); go.AddComponent<SpeckleSender>();
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/systems.speckle.speckle-unity/Editor/Gizmos/logo128.png"); var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(
"Packages/systems.speckle.speckle-unity/Editor/Gizmos/logo128.png"
);
EditorGUIUtility.SetIconForObject(go, icon); EditorGUIUtility.SetIconForObject(go, icon);
#endif #endif
} }
} }
} }

Просмотреть файл

@ -12,16 +12,20 @@ using Component = UnityEngine.Component;
#nullable enable #nullable enable
namespace Speckle.ConnectorUnity.Components.Editor namespace Speckle.ConnectorUnity.Components.Editor
{ {
public enum SelectionFilter public enum SelectionFilter
{ {
[Tooltip("Convert all children of this GameObject")] [Tooltip("Convert all children of this GameObject")]
Children, Children,
[Tooltip("Convert GameObjects currently selected in the hierarchy (consider padlocking this inspector)")]
[Tooltip(
"Convert GameObjects currently selected in the hierarchy (consider padlocking this inspector)"
)]
Selection, Selection,
[InspectorName("All (excl. disabled)")] [InspectorName("All (excl. disabled)")]
[Tooltip("Convert all GameObjects (excluding disabled) in the active scene")] [Tooltip("Convert all GameObjects (excluding disabled) in the active scene")]
Enabled, Enabled,
[Tooltip("Convert all GameObjects (including disabled) in the active scene")] [Tooltip("Convert all GameObjects (including disabled) in the active scene")]
[InspectorName("All (incl. disabled)")] [InspectorName("All (incl. disabled)")]
All, All,
@ -31,8 +35,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
[CanEditMultipleObjects] [CanEditMultipleObjects]
public class SpeckleSendEditor : UnityEditor.Editor public class SpeckleSendEditor : UnityEditor.Editor
{ {
private SelectionFilter _selectedFilter = SelectionFilter.Children;
private SelectionFilter selectedFilter = SelectionFilter.Children;
public override async void OnInspectorGUI() public override async void OnInspectorGUI()
{ {
@ -40,7 +43,8 @@ namespace Speckle.ConnectorUnity.Components.Editor
DrawDefaultInspector(); DrawDefaultInspector();
bool shouldSend = GUILayout.Button("Send!"); bool shouldSend = GUILayout.Button("Send!");
selectedFilter = (SelectionFilter)EditorGUILayout.EnumPopup("Selection", selectedFilter); _selectedFilter = (SelectionFilter)
EditorGUILayout.EnumPopup("Selection", _selectedFilter);
if (shouldSend) if (shouldSend)
{ {
@ -50,7 +54,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
public async Task<string?> ConvertAndSend() public async Task<string?> ConvertAndSend()
{ {
var speckleSender = (SpeckleSender) target; var speckleSender = (SpeckleSender)target;
if (!speckleSender.GetSelection(out _, out _, out _, out string? error)) if (!speckleSender.GetSelection(out _, out _, out _, out string? error))
{ {
@ -59,13 +63,18 @@ namespace Speckle.ConnectorUnity.Components.Editor
} }
RecursiveConverter converter = speckleSender.Converter; RecursiveConverter converter = speckleSender.Converter;
Base data = selectedFilter switch Base data = _selectedFilter switch
{ {
SelectionFilter.All => ConvertAll(converter), SelectionFilter.All => ConvertAll(converter),
SelectionFilter.Enabled => ConvertEnabled(converter), SelectionFilter.Enabled => ConvertEnabled(converter),
SelectionFilter.Children => ConvertChildren(converter), SelectionFilter.Children => ConvertChildren(converter),
SelectionFilter.Selection => ConvertSelection(converter), SelectionFilter.Selection => ConvertSelection(converter),
_ => throw new InvalidEnumArgumentException(nameof(selectedFilter), (int) selectedFilter, selectedFilter.GetType()), _
=> throw new InvalidEnumArgumentException(
nameof(_selectedFilter),
(int)_selectedFilter,
_selectedFilter.GetType()
),
}; };
//TODO onError action? //TODO onError action?
@ -81,31 +90,36 @@ namespace Speckle.ConnectorUnity.Components.Editor
private Base ConvertChildren(RecursiveConverter converter) private Base ConvertChildren(RecursiveConverter converter)
{ {
return converter.RecursivelyConvertToSpeckle( return converter.RecursivelyConvertToSpeckle(
new []{((Component)target).gameObject}, new[] { ((Component)target).gameObject },
_ => true); _ => true
);
} }
private Base ConvertSelection(RecursiveConverter converter) private Base ConvertSelection(RecursiveConverter converter)
{ {
ISet<GameObject> selection = Selection.GetFiltered<GameObject>(SelectionMode.Deep).ToImmutableHashSet(); ISet<GameObject> selection = Selection
.GetFiltered<GameObject>(SelectionMode.Deep)
.ToImmutableHashSet();
return converter.RecursivelyConvertToSpeckle( return converter.RecursivelyConvertToSpeckle(
SceneManager.GetActiveScene().GetRootGameObjects(), SceneManager.GetActiveScene().GetRootGameObjects(),
go => selection.Contains(go)); go => selection.Contains(go)
);
} }
private Base ConvertAll(RecursiveConverter converter) private Base ConvertAll(RecursiveConverter converter)
{ {
return converter.RecursivelyConvertToSpeckle( return converter.RecursivelyConvertToSpeckle(
SceneManager.GetActiveScene().GetRootGameObjects(), SceneManager.GetActiveScene().GetRootGameObjects(),
_ => true); _ => true
);
} }
private Base ConvertEnabled(RecursiveConverter converter) private Base ConvertEnabled(RecursiveConverter converter)
{ {
return converter.RecursivelyConvertToSpeckle( return converter.RecursivelyConvertToSpeckle(
SceneManager.GetActiveScene().GetRootGameObjects(), SceneManager.GetActiveScene().GetRootGameObjects(),
go => go.activeInHierarchy); go => go.activeInHierarchy
);
} }
} }
} }

Просмотреть файл

@ -20,10 +20,10 @@ namespace Speckle.ConnectorUnity.Components.Editor
public class StreamManagerEditor : UnityEditor.Editor public class StreamManagerEditor : UnityEditor.Editor
{ {
private bool _foldOutAccount; private bool _foldOutAccount;
private int _totalChildrenCount = 0; private int _totalChildrenCount;
private StreamManager _streamManager; private StreamManager _streamManager;
private static bool generateAssets; private static bool _generateAssets;
public int StreamsLimit { get; set; } = 30; public int StreamsLimit { get; set; } = 30;
public int BranchesLimit { get; set; } = 75; public int BranchesLimit { get; set; } = 75;
@ -98,7 +98,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
private List<Branch> Branches private List<Branch> Branches
{ {
get => _streamManager.Branches; get => _streamManager.Branches;
set => _streamManager.Branches = value; set => _streamManager.Branches = value;
} }
@ -142,7 +141,11 @@ namespace Speckle.ConnectorUnity.Components.Editor
SelectedStream = Streams[i]; SelectedStream = Streams[i];
EditorUtility.DisplayProgressBar("Loading stream details...", "", 0); EditorUtility.DisplayProgressBar("Loading stream details...", "", 0);
Branches = await Client.StreamGetBranches(SelectedStream.id, BranchesLimit, CommitsLimit); Branches = await Client.StreamGetBranches(
SelectedStream.id,
BranchesLimit,
CommitsLimit
);
if (Branches.Any()) if (Branches.Any())
{ {
SelectedBranchIndex = 0; SelectedBranchIndex = 0;
@ -155,7 +158,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorUtility.ClearProgressBar(); EditorUtility.ClearProgressBar();
} }
private async Task Receive() private async Task Receive()
{ {
var transport = new ServerTransport(SelectedAccount, SelectedStream.id); var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
@ -163,53 +165,82 @@ namespace Speckle.ConnectorUnity.Components.Editor
try try
{ {
Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex]; Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[
SelectedCommitIndex
];
// Receive Speckle Objects // Receive Speckle Objects
var @base = await Operations.Receive( var @base = await Operations.Receive(
selectedCommit.referencedObject, selectedCommit.referencedObject,
remoteTransport: transport, remoteTransport: transport,
onProgressAction: dict => onProgressAction: dict =>
{ {
UnityEditor.EditorApplication.delayCall += () => EditorApplication.delayCall += () =>
{ {
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "", EditorUtility.DisplayProgressBar(
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount)); $"Receiving data from {transport.BaseUri}...",
"",
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount)
);
}; };
}, },
onTotalChildrenCountKnown: count => { _totalChildrenCount = count; } onTotalChildrenCountKnown: count =>
{
_totalChildrenCount = count;
}
); );
if (@base is null)
throw new InvalidOperationException("Received object was null");
EditorUtility.ClearProgressBar(); EditorUtility.ClearProgressBar();
Analytics.TrackEvent(SelectedAccount, Analytics.Events.Receive, new Dictionary<string, object>() Analytics.TrackEvent(
{ SelectedAccount,
{"mode", nameof(StreamManagerEditor)}, Analytics.Events.Receive,
{"sourceHostApp", HostApplications.GetHostAppFromString(selectedCommit.sourceApplication).Slug}, new Dictionary<string, object>()
{"sourceHostAppVersion", selectedCommit.sourceApplication ?? ""}, {
{"hostPlatform", Application.platform.ToString()}, { "mode", nameof(StreamManagerEditor) },
{"isMultiplayer", selectedCommit.authorId != SelectedAccount.userInfo.id}, {
}); "sourceHostApp",
HostApplications
.GetHostAppFromString(selectedCommit.sourceApplication)
.Slug
},
{ "sourceHostAppVersion", selectedCommit.sourceApplication ?? "" },
{ "hostPlatform", Application.platform.ToString() },
{ "isMultiplayer", selectedCommit.authorId != SelectedAccount.userInfo.id },
}
);
//Convert Speckle Objects //Convert Speckle Objects
int childrenConverted = 0; int childrenConverted = 0;
void BeforeConvertCallback(Base b) void BeforeConvertCallback(Base b)
{ {
EditorUtility.DisplayProgressBar("Converting To Native...", $"{b.speckle_type} - {b.id}", EditorUtility.DisplayProgressBar(
Convert.ToSingle(childrenConverted++ / _totalChildrenCount)); "Converting To Native...",
$"{b.speckle_type} - {b.id}",
Convert.ToSingle(childrenConverted++ / _totalChildrenCount)
);
} }
var go = _streamManager.ConvertRecursivelyToNative(@base, _streamManager.ConvertRecursivelyToNative(
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, BeforeConvertCallback); @base,
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id,
BeforeConvertCallback
);
// Read Receipt // Read Receipt
await Client.CommitReceived(new CommitReceivedInput await Client.CommitReceived(
{ new CommitReceivedInput
streamId = SelectedStream.id, {
commitId = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, streamId = SelectedStream.id,
message = $"received commit from {HostApplications.Unity.Name} Editor", commitId = Branches[SelectedBranchIndex].commits.items[
sourceApplication = HostApplications.Unity.Name SelectedCommitIndex
}); ].id,
message = $"received commit from {HostApplications.Unity.Name} Editor",
sourceApplication = HostApplications.Unity.Name
}
);
} }
catch (Exception e) catch (Exception e)
{ {
@ -223,8 +254,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
public override async void OnInspectorGUI() public override async void OnInspectorGUI()
{ {
_streamManager = (StreamManager) target; _streamManager = (StreamManager)target;
#region Account GUI #region Account GUI
@ -234,12 +264,15 @@ namespace Speckle.ConnectorUnity.Components.Editor
return; return;
} }
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
SelectedAccountIndex = EditorGUILayout.Popup("Accounts", SelectedAccountIndex, SelectedAccountIndex = EditorGUILayout.Popup(
"Accounts",
SelectedAccountIndex,
Accounts.Select(x => x.userInfo.email + " | " + x.serverInfo.name).ToArray(), Accounts.Select(x => x.userInfo.email + " | " + x.serverInfo.name).ToArray(),
GUILayout.ExpandWidth(true), GUILayout.Height(20)); GUILayout.ExpandWidth(true),
GUILayout.Height(20)
);
if (OldSelectedAccountIndex != SelectedAccountIndex) if (OldSelectedAccountIndex != SelectedAccountIndex)
{ {
@ -255,26 +288,37 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
#region Speckle Account Info #region Speckle Account Info
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(_foldOutAccount, "Account Info"); _foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(
_foldOutAccount,
"Account Info"
);
if (_foldOutAccount) if (_foldOutAccount)
{ {
EditorGUI.BeginDisabledGroup(true); EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField("Name", SelectedAccount.userInfo.name, EditorGUILayout.TextField(
"Name",
SelectedAccount.userInfo.name,
GUILayout.Height(20), GUILayout.Height(20),
GUILayout.ExpandWidth(true)); GUILayout.ExpandWidth(true)
);
EditorGUILayout.TextField("Server", SelectedAccount.serverInfo.name, EditorGUILayout.TextField(
"Server",
SelectedAccount.serverInfo.name,
GUILayout.Height(20), GUILayout.Height(20),
GUILayout.ExpandWidth(true)); GUILayout.ExpandWidth(true)
);
EditorGUILayout.TextField("URL", SelectedAccount.serverInfo.url, EditorGUILayout.TextField(
"URL",
SelectedAccount.serverInfo.url,
GUILayout.Height(20), GUILayout.Height(20),
GUILayout.ExpandWidth(true)); GUILayout.ExpandWidth(true)
);
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
} }
@ -292,9 +336,13 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
SelectedStreamIndex = EditorGUILayout.Popup("Streams", SelectedStreamIndex = EditorGUILayout.Popup(
SelectedStreamIndex, Streams.Select(x => x.name).ToArray(), GUILayout.Height(20), "Streams",
GUILayout.ExpandWidth(true)); SelectedStreamIndex,
Streams.Select(x => x.name).ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true)
);
if (OldSelectedStreamIndex != SelectedStreamIndex) if (OldSelectedStreamIndex != SelectedStreamIndex)
{ {
@ -319,23 +367,29 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
SelectedBranchIndex = EditorGUILayout.Popup("Branches", SelectedBranchIndex = EditorGUILayout.Popup(
SelectedBranchIndex, Branches.Select(x => x.name).ToArray(), GUILayout.Height(20), "Branches",
GUILayout.ExpandWidth(true)); SelectedBranchIndex,
Branches.Select(x => x.name).ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true)
);
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
if (!Branches[SelectedBranchIndex].commits.items.Any()) if (!Branches[SelectedBranchIndex].commits.items.Any())
return; return;
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
SelectedCommitIndex = EditorGUILayout.Popup("Commits", SelectedCommitIndex = EditorGUILayout.Popup(
"Commits",
SelectedCommitIndex, SelectedCommitIndex,
Branches[SelectedBranchIndex].commits.items.Select(x => $"{x.message} - {x.id}").ToArray(), Branches[SelectedBranchIndex].commits.items
.Select(x => $"{x.message} - {x.id}")
.ToArray(),
GUILayout.Height(20), GUILayout.Height(20),
GUILayout.ExpandWidth(true)); GUILayout.ExpandWidth(true)
);
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
@ -347,12 +401,12 @@ namespace Speckle.ConnectorUnity.Components.Editor
GUILayout.Label("Generate assets"); GUILayout.Label("Generate assets");
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
bool selection = GUILayout.Toggle(generateAssets, ""); bool selection = GUILayout.Toggle(_generateAssets, "");
if (generateAssets != selection) if (_generateAssets != selection)
{ {
_generateAssets = selection;
generateAssets = selection; _streamManager.RC.AssetCache.nativeCaches =
_streamManager.RC.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets); NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
} }
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();

Просмотреть файл

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq;
using Speckle.Core.Models; using Speckle.Core.Models;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -18,22 +16,27 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
public const string DefaultPath = "Assets/Resources"; public const string DefaultPath = "Assets/Resources";
public string path = DefaultPath; public string path = DefaultPath;
private MemoryNativeCache readCache; private MemoryNativeCache _readCache;
#nullable enable #nullable enable
void Awake() void Awake()
{ {
readCache = CreateInstance<MemoryNativeCache>(); _readCache = CreateInstance<MemoryNativeCache>();
} }
public override bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class public override bool TryGetObject<T>(
Base speckleObject,
[NotNullWhen(true)] out T? nativeObject
)
where T : class
{ {
if(readCache.TryGetObject(speckleObject, out nativeObject)) if (_readCache.TryGetObject(speckleObject, out nativeObject))
return true; return true;
Type nativeType = typeof(T); Type nativeType = typeof(T);
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false; if (!GetAssetPath(nativeType, speckleObject, out string? assetPath))
return false;
nativeObject = AssetDatabase.LoadAssetAtPath<T>(assetPath); nativeObject = AssetDatabase.LoadAssetAtPath<T>(assetPath);
return nativeObject != null; return nativeObject != null;
@ -47,28 +50,35 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
private bool WriteObject(Base speckleObject, Object nativeObject) private bool WriteObject(Base speckleObject, Object nativeObject)
{ {
Type nativeType = nativeObject.GetType(); Type nativeType = nativeObject.GetType();
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false; if (!GetAssetPath(nativeType, speckleObject, out string? assetPath))
return false;
// Special case for GameObjects, we want to use PrefabUtility // Special case for GameObjects, we want to use PrefabUtility
if (nativeObject is GameObject go) if (nativeObject is GameObject go)
{ {
var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction); var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(
return readCache.TrySaveObject(speckleObject, prefab); go,
assetPath,
InteractionMode.AutomatedAction
);
return _readCache.TrySaveObject(speckleObject, prefab);
} }
// Exit early if there's already an asset // Exit early if there's already an asset
Object? existing = AssetDatabase.LoadAssetAtPath(assetPath, nativeObject.GetType()); Object? existing = AssetDatabase.LoadAssetAtPath(assetPath, nativeObject.GetType());
if (existing != null) if (existing != null)
{ {
Debug.LogWarning($"Failed to write asset as one already existed at path: {assetPath}", this); Debug.LogWarning(
$"Failed to write asset as one already existed at path: {assetPath}",
this
);
return false; return false;
} }
AssetDatabase.CreateAsset(nativeObject, $"{assetPath}"); AssetDatabase.CreateAsset(nativeObject, $"{assetPath}");
return readCache.TrySaveObject(speckleObject, nativeObject); return _readCache.TrySaveObject(speckleObject, nativeObject);
} }
public override void BeginWrite() public override void BeginWrite()
{ {
base.BeginWrite(); base.BeginWrite();
@ -77,21 +87,29 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
public override void FinishWrite() public override void FinishWrite()
{ {
if (!isWriting) return; if (!isWriting)
return;
//AssetDatabase.StopAssetEditing(); //AssetDatabase.StopAssetEditing();
AssetDatabase.SaveAssets(); AssetDatabase.SaveAssets();
if (readCache != null) readCache.LoadedAssets.Clear(); if (_readCache != null)
_readCache.LoadedAssets.Clear();
base.FinishWrite(); base.FinishWrite();
} }
private bool GetAssetPath(Type nativeType, Base speckleObject, [NotNullWhen(true)] out string? outPath) private bool GetAssetPath(
Type nativeType,
Base speckleObject,
[NotNullWhen(true)] out string? outPath
)
{ {
string? folder = AssetHelpers.GetAssetFolder(nativeType, path); string? folder = AssetHelpers.GetAssetFolder(nativeType, path);
outPath = null; outPath = null;
if (folder == null) return false; if (folder == null)
if (!CreateDirectory(folder)) return false; return false;
if (!CreateDirectory(folder))
return false;
string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType); string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType);
outPath = $"{folder}/{assetName}"; outPath = $"{folder}/{assetName}";
@ -111,16 +129,20 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
[ContextMenu("SetPath")] [ContextMenu("SetPath")]
public void SetPath_Menu() public void SetPath_Menu()
{ {
var selection = EditorUtility.OpenFolderPanel("Set Assets Path", "Assets/Resources", ""); var selection = EditorUtility.OpenFolderPanel(
"Set Assets Path",
"Assets/Resources",
""
);
if (selection.StartsWith(Application.dataPath)) { if (selection.StartsWith(Application.dataPath))
{
path = "Assets" + selection.Substring(Application.dataPath.Length); path = "Assets" + selection.Substring(Application.dataPath.Length);
} }
else else
{ {
Debug.LogError($"Expected selection to be within {Application.dataPath}"); Debug.LogError($"Expected selection to be within {Application.dataPath}");
} }
} }
} }
} }

Просмотреть файл

@ -13,7 +13,9 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public sealed class AccountSelectionDrawer : OptionSelectionDrawer<Account> public sealed class AccountSelectionDrawer : OptionSelectionDrawer<Account>
{ {
protected override bool DisplayRefresh => true; protected override bool DisplayRefresh => true;
protected override string FormatOption(Account o) => $"{o.userInfo.email} | {o.serverInfo.name}";
protected override string FormatOption(Account o) =>
$"{o.userInfo.email} | {o.serverInfo.name}";
public AccountSelectionDrawer() public AccountSelectionDrawer()
{ {
@ -34,11 +36,12 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public sealed class StreamSelectionDrawer : OptionSelectionDrawer<Stream> public sealed class StreamSelectionDrawer : OptionSelectionDrawer<Stream>
{ {
protected override bool DisplayRefresh => true; protected override bool DisplayRefresh => true;
protected override string FormatOption(Stream o) => $"{o.name}"; protected override string FormatOption(Stream o) => $"{o.name}";
public StreamSelectionDrawer() public StreamSelectionDrawer()
{ {
properties = new []{$"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField"}; properties = new[] { $"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField" };
details = new (string, Func<Stream, string>)[] details = new (string, Func<Stream, string>)[]
{ {
@ -56,20 +59,18 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public sealed class BranchSelectionDrawer : OptionSelectionDrawer<Branch> public sealed class BranchSelectionDrawer : OptionSelectionDrawer<Branch>
{ {
protected override bool DisplayRefresh => true; protected override bool DisplayRefresh => true;
protected override string FormatOption(Branch o) => $"{o.name}"; protected override string FormatOption(Branch o) => $"{o.name}";
public BranchSelectionDrawer() public BranchSelectionDrawer()
{ {
properties = new [] properties = new[]
{ {
$"<{nameof(BranchSelection.BranchesLimit)}>k__BackingField", $"<{nameof(BranchSelection.BranchesLimit)}>k__BackingField",
$"<{nameof(BranchSelection.CommitsLimit)}>k__BackingField", $"<{nameof(BranchSelection.CommitsLimit)}>k__BackingField",
}; };
details = new (string, Func<Branch, string>)[] details = new (string, Func<Branch, string>)[] { ("Description", s => s.description), };
{
("Description", s => s.description),
};
} }
} }
@ -91,12 +92,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
} }
} }
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer where TOption : class where TOption : class
{ {
private const float RefreshButtonWidthScale = 0.2f; private const float RefreshButtonWidthScale = 0.2f;
private const float PrefixIndentation = 100f; private const float PrefixIndentation = 100f;
protected readonly float DetailsTextHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; protected readonly float DetailsTextHeight =
EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
protected virtual bool DisplayRefresh => false; protected virtual bool DisplayRefresh => false;
protected abstract string FormatOption(TOption o); protected abstract string FormatOption(TOption o);
@ -118,7 +120,12 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
return choices; return choices;
} }
protected virtual void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, TOption? selection) protected virtual void OnGUIDetails(
Rect position,
SerializedProperty property,
GUIContent label,
TOption? selection
)
{ {
position.height = DetailsTextHeight; position.height = DetailsTextHeight;
@ -146,7 +153,9 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{ {
EditorGUI.BeginProperty(position, label, property); EditorGUI.BeginProperty(position, label, property);
var t = (OptionSelection<TOption>)fieldInfo.GetValue(property.serializedObject.targetObject); var t =
(OptionSelection<TOption>)
fieldInfo.GetValue(property.serializedObject.targetObject);
var selectionRect = position; var selectionRect = position;
selectionRect.x += PrefixIndentation + 5; selectionRect.x += PrefixIndentation + 5;
@ -156,9 +165,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
// Options selection // Options selection
{ {
var popupSize = DisplayRefresh var popupSize = DisplayRefresh
? new Rect(selectionRect.x, selectionRect.y, selectionRect.width * (1-RefreshButtonWidthScale), DetailsTextHeight) ? new Rect(
selectionRect.x,
selectionRect.y,
selectionRect.width * (1 - RefreshButtonWidthScale),
DetailsTextHeight
)
: selectionRect; : selectionRect;
string selectedChoice = selectedOption != null ? FormatOption(selectedOption) : ""; string selectedChoice = selectedOption != null ? FormatOption(selectedOption) : "";
@ -168,15 +181,24 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
var windowPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); var windowPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
var provider = ScriptableObject.CreateInstance<StringListSearchProvider>(); var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
provider.Title = typeof(TOption).Name; provider.Title = typeof(TOption).Name;
provider.listItems = GetFormattedOptions(t.Options);; provider.listItems = GetFormattedOptions(t.Options);
provider.onSetIndexCallback = o => { t.SelectedIndex = o;}; ;
provider.onSetIndexCallback = o =>
{
t.SelectedIndex = o;
};
SearchWindow.Open(new SearchWindowContext(windowPos), provider); SearchWindow.Open(new SearchWindowContext(windowPos), provider);
} }
// Optional refresh // Optional refresh
if (DisplayRefresh) if (DisplayRefresh)
{ {
var buttonSize = new Rect(selectionRect.x + popupSize.width , selectionRect.y, selectionRect.width * RefreshButtonWidthScale, DetailsTextHeight); var buttonSize = new Rect(
selectionRect.x + popupSize.width,
selectionRect.y,
selectionRect.width * RefreshButtonWidthScale,
DetailsTextHeight
);
if (GUI.Button(buttonSize, "Refresh")) if (GUI.Button(buttonSize, "Refresh"))
{ {
EditorApplication.delayCall += t.RefreshOptions; EditorApplication.delayCall += t.RefreshOptions;
@ -187,9 +209,16 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
// Collapsable details // Collapsable details
{ {
int visiblePropCount = property.isExpanded ? GUIDetailsPropertyCount : 0; int visiblePropCount = property.isExpanded ? GUIDetailsPropertyCount : 0;
var detailsHeight = new Vector2(PrefixIndentation, DetailsTextHeight + visiblePropCount * DetailsTextHeight); var detailsHeight = new Vector2(
var foldoutRect = new Rect(position.position, detailsHeight); PrefixIndentation,
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(foldoutRect, property.isExpanded, label); DetailsTextHeight + visiblePropCount * DetailsTextHeight
);
var foldoutRect = new Rect(position.position, detailsHeight);
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(
foldoutRect,
property.isExpanded,
label
);
if (property.isExpanded) if (property.isExpanded)
{ {
OnGUIDetails(position, property, label, selectedOption); OnGUIDetails(position, property, label, selectedOption);
@ -197,25 +226,29 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
EditorGUI.EndFoldoutHeaderGroup(); EditorGUI.EndFoldoutHeaderGroup();
} }
EditorGUI.EndProperty(); EditorGUI.EndProperty();
//EditorUtility.SetDirty(property.serializedObject.targetObject); //EditorUtility.SetDirty(property.serializedObject.targetObject);
} }
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{ {
var standardHeight = EditorGUIUtility.singleLineHeight; var standardHeight = EditorGUIUtility.singleLineHeight;
if (!property.isExpanded) return standardHeight + EditorGUIUtility.standardVerticalSpacing; if (!property.isExpanded)
return standardHeight + EditorGUIUtility.standardVerticalSpacing;
var detailsHeight = GUIDetailsPropertyCount * (standardHeight + EditorGUIUtility.standardVerticalSpacing); var detailsHeight =
GUIDetailsPropertyCount
* (standardHeight + EditorGUIUtility.standardVerticalSpacing);
return standardHeight + detailsHeight + EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.standardVerticalSpacing; return standardHeight
+ detailsHeight
+ EditorGUIUtility.standardVerticalSpacing
+ EditorGUIUtility.standardVerticalSpacing;
} }
} }
#nullable disable #nullable disable
public sealed class StringListSearchProvider : ScriptableObject, ISearchWindowProvider public sealed class StringListSearchProvider : ScriptableObject, ISearchWindowProvider
{ {
@ -226,9 +259,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context) public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{ {
List<SearchTreeEntry> searchList = new(listItems.Length + 1) {new SearchTreeGroupEntry(new GUIContent(Title), 0)}; List<SearchTreeEntry> searchList =
new(listItems.Length + 1) { new SearchTreeGroupEntry(new GUIContent(Title), 0) };
for(int i = 0; i < listItems.Length; i++) for (int i = 0; i < listItems.Length; i++)
{ {
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(listItems[i])) SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(listItems[i]))
{ {
@ -248,7 +282,4 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
return true; return true;
} }
} }
} }

Просмотреть файл

@ -8,7 +8,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Sentry; using Sentry;
using Speckle.ConnectorUnity.Components; using Speckle.ConnectorUnity.Components;
using Speckle.ConnectorUnity.Utils; using Speckle.ConnectorUnity.Utils;
@ -17,184 +16,201 @@ using UnityEngine;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
/// <summary>
/// A Speckle Receiver, it's a wrapper around a basic Speckle Client
/// that handles conversions and subscriptions for you
/// </summary>
[RequireComponent(typeof(RecursiveConverter))]
[Obsolete]
public class Receiver : MonoBehaviour
{
public string StreamId;
public string BranchName = "main";
public Stream Stream;
public int TotalChildrenCount = 0;
public GameObject ReceivedData;
private bool AutoReceive;
private bool DeleteOld;
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
private Action<string, Exception> OnErrorAction;
private Action<int> OnTotalChildrenCountKnown;
private Action<GameObject> OnDataReceivedAction;
private Client Client { get; set; }
public Receiver()
{
}
/// <summary> /// <summary>
/// Initializes the Receiver manually /// A Speckle Receiver, it's a wrapper around a basic Speckle Client
/// that handles conversions and subscriptions for you
/// </summary> /// </summary>
/// <param name="streamId">Id of the stream to receive</param> [RequireComponent(typeof(RecursiveConverter))]
/// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param> [Obsolete]
/// <param name="deleteOld">If true, it will delete previously received objects when new one are received</param> public class Receiver : MonoBehaviour
/// <param name="account">Account to use, if null the default account will be used</param>
/// <param name="onDataReceivedAction">Action to run after new data has been received and converted</param>
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
/// <param name="onErrorAction">Action to run on error</param>
/// <param name="onTotalChildrenCountKnown">Action to run when the TotalChildrenCount is known</param>
public void Init(string streamId, bool autoReceive = false, bool deleteOld = true, Account account = null,
Action<GameObject> onDataReceivedAction = null, Action<ConcurrentDictionary<string, int>> onProgressAction = null,
Action<string, Exception> onErrorAction = null, Action<int> onTotalChildrenCountKnown = null)
{ {
StreamId = streamId; public string StreamId;
AutoReceive = autoReceive; public string BranchName = "main";
DeleteOld = deleteOld; public Stream Stream;
OnDataReceivedAction = onDataReceivedAction; public int TotalChildrenCount = 0;
OnErrorAction = onErrorAction; public GameObject ReceivedData;
OnProgressAction = onProgressAction;
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
Client = new Client(account ?? AccountManager.GetDefaultAccount()); private bool AutoReceive;
private bool DeleteOld;
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
private Action<string, Exception> OnErrorAction;
private Action<int> OnTotalChildrenCountKnown;
private Action<GameObject> OnDataReceivedAction;
private Client Client { get; set; }
if (AutoReceive) public Receiver() { }
{
Client.SubscribeCommitCreated(StreamId);
Client.OnCommitCreated += Client_OnCommitCreated;
}
}
/// <summary>
/// <summary> /// Initializes the Receiver manually
/// Gets and converts the data of the last commit on the Stream /// </summary>
/// </summary> /// <param name="streamId">Id of the stream to receive</param>
/// <returns></returns> /// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param>
public void Receive() /// <param name="deleteOld">If true, it will delete previously received objects when new one are received</param>
{ /// <param name="account">Account to use, if null the default account will be used</param>
if (Client == null || string.IsNullOrEmpty(StreamId)) /// <param name="onDataReceivedAction">Action to run after new data has been received and converted</param>
throw new Exception("Receiver has not been initialized. Please call Init()."); /// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
/// <param name="onErrorAction">Action to run on error</param>
Task.Run(async () => /// <param name="onTotalChildrenCountKnown">Action to run when the TotalChildrenCount is known</param>
{ public void Init(
try string streamId,
bool autoReceive = false,
bool deleteOld = true,
Account account = null,
Action<GameObject> onDataReceivedAction = null,
Action<ConcurrentDictionary<string, int>> onProgressAction = null,
Action<string, Exception> onErrorAction = null,
Action<int> onTotalChildrenCountKnown = null
)
{ {
var mainBranch = await Client.BranchGet(StreamId, BranchName, 1); StreamId = streamId;
if (!mainBranch.commits.items.Any()) AutoReceive = autoReceive;
throw new Exception("This branch has no commits"); DeleteOld = deleteOld;
var commit = mainBranch.commits.items[0]; OnDataReceivedAction = onDataReceivedAction;
GetAndConvertObject(commit.referencedObject, commit.id, commit.sourceApplication, commit.authorId); OnErrorAction = onErrorAction;
OnProgressAction = onProgressAction;
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
Client = new Client(account ?? AccountManager.GetDefaultAccount());
if (AutoReceive)
{
Client.SubscribeCommitCreated(StreamId);
Client.OnCommitCreated += Client_OnCommitCreated;
}
} }
catch (Exception e)
/// <summary>
/// Gets and converts the data of the last commit on the Stream
/// </summary>
/// <returns></returns>
public void Receive()
{ {
throw new SpeckleException(e.Message, e, true, SentryLevel.Error); if (Client == null || string.IsNullOrEmpty(StreamId))
throw new Exception("Receiver has not been initialized. Please call Init().");
Task.Run(async () =>
{
try
{
var mainBranch = await Client.BranchGet(StreamId, BranchName, 1);
if (!mainBranch.commits.items.Any())
throw new Exception("This branch has no commits");
var commit = mainBranch.commits.items[0];
GetAndConvertObject(
commit.referencedObject,
commit.id,
commit.sourceApplication,
commit.authorId
);
}
catch (Exception e)
{
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
}
});
} }
});
}
#region private methods
#region private methods /// <summary>
/// Fired when a new commit is created on this stream
/// <summary> /// It receives and converts the objects and then executes the user defined _onCommitCreated action.
/// Fired when a new commit is created on this stream /// </summary>
/// It receives and converts the objects and then executes the user defined _onCommitCreated action. /// <param name="sender"></param>
/// </summary> /// <param name="e"></param>
/// <param name="sender"></param> protected virtual void Client_OnCommitCreated(object sender, CommitInfo e)
/// <param name="e"></param>
protected virtual void Client_OnCommitCreated(object sender, CommitInfo e)
{
if (e.branchName == BranchName)
{
Debug.Log("New commit created");
GetAndConvertObject(e.objectId, e.id, e.sourceApplication, e.authorId);
}
}
private async void GetAndConvertObject(string objectId, string commitId, string sourceApplication, string authorId)
{
try
{
var transport = new ServerTransport(Client.Account, StreamId);
var @base = await Operations.Receive(
objectId,
remoteTransport: transport,
onErrorAction: OnErrorAction,
onProgressAction: OnProgressAction,
onTotalChildrenCountKnown: OnTotalChildrenCountKnown,
disposeTransports: true
);
Analytics.TrackEvent(Client.Account, Analytics.Events.Receive, new Dictionary<string, object>()
{ {
{"mode", nameof(Receiver)}, if (e.branchName == BranchName)
{"sourceHostApp", HostApplications.GetHostAppFromString(sourceApplication).Slug}, {
{"sourceHostAppVersion", sourceApplication ?? ""}, Debug.Log("New commit created");
{"hostPlatform", Application.platform.ToString()}, GetAndConvertObject(e.objectId, e.id, e.sourceApplication, e.authorId);
{"isMultiplayer", authorId != null && authorId != Client.Account.userInfo.id}, }
}); }
Dispatcher.Instance().Enqueue(() => private async void GetAndConvertObject(
string objectId,
string commitId,
string sourceApplication,
string authorId
)
{ {
var root = new GameObject() try
{ {
name = commitId, var transport = new ServerTransport(Client.Account, StreamId);
}; var @base = await Operations.Receive(
objectId,
remoteTransport: transport,
onErrorAction: OnErrorAction,
onProgressAction: OnProgressAction,
onTotalChildrenCountKnown: OnTotalChildrenCountKnown,
disposeTransports: true
);
var rc = GetComponent<RecursiveConverter>(); Analytics.TrackEvent(
var go = rc.RecursivelyConvertToNative(@base, root.transform); Client.Account,
//remove previously received object Analytics.Events.Receive,
if (DeleteOld && ReceivedData != null) new Dictionary<string, object>()
Destroy(ReceivedData); {
ReceivedData = root; { "mode", nameof(Receiver) },
OnDataReceivedAction?.Invoke(root); {
}); "sourceHostApp",
} HostApplications.GetHostAppFromString(sourceApplication).Slug
catch (Exception e) },
{ { "sourceHostAppVersion", sourceApplication ?? "" },
throw new SpeckleException(e.Message, e, true, SentryLevel.Error); { "hostPlatform", Application.platform.ToString() },
} {
"isMultiplayer",
authorId != null && authorId != Client.Account.userInfo.id
},
}
);
try Dispatcher
{ .Instance()
await Client.CommitReceived(new CommitReceivedInput .Enqueue(() =>
{
var root = new GameObject() { name = commitId, };
var rc = GetComponent<RecursiveConverter>();
var go = rc.RecursivelyConvertToNative(@base, root.transform);
//remove previously received object
if (DeleteOld && ReceivedData != null)
Destroy(ReceivedData);
ReceivedData = root;
OnDataReceivedAction?.Invoke(root);
});
}
catch (Exception e)
{
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
}
try
{
await Client.CommitReceived(
new CommitReceivedInput
{
streamId = StreamId,
commitId = commitId,
message = $"received commit from {Application.unityVersion}",
sourceApplication = HostApplications.Unity.GetVersion(
CoreUtils.GetHostAppVersion()
)
}
);
}
catch
{
// Do nothing!
}
}
private void OnDestroy()
{ {
streamId = StreamId, Client?.CommitCreatedSubscription?.Dispose();
commitId = commitId, }
message = $"received commit from {Application.unityVersion}",
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
});
}
catch
{
// Do nothing!
}
#endregion
} }
private void OnDestroy()
{
Client?.CommitCreatedSubscription?.Dispose();
}
#endregion
}
} }

Просмотреть файл

@ -16,135 +16,150 @@ using UnityEngine.SceneManagement;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
/// <summary>
/// A Speckle Sender, it's a wrapper around a basic Speckle Client
/// that handles conversions for you
/// </summary>
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
[Obsolete]
public class Sender : MonoBehaviour
{
private ServerTransport transport;
private RecursiveConverter converter;
private CancellationTokenSource cancellationTokenSource;
#nullable enable
private void Awake()
{
converter = GetComponent<RecursiveConverter>();
}
/// <summary> /// <summary>
/// Converts and sends the data of the last commit on the Stream /// A Speckle Sender, it's a wrapper around a basic Speckle Client
/// that handles conversions for you
/// </summary> /// </summary>
/// <param name="streamId">ID of the stream to send to</param> [RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
/// <param name="gameObjects">List of gameObjects to convert and send</param> [Obsolete]
/// <param name="account">Account to use. If not provided the default account will be used</param> public class Sender : MonoBehaviour
/// <param name="branchName">Name of branch to send to</param>
/// <param name="createCommit">When true, will create a commit using the root object</param>
/// <param name="onDataSentAction">Action to run after the data has been sent</param>
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
/// <param name="onErrorAction">Action to run on error</param>
/// <exception cref="SpeckleException"></exception>
public void Send(string streamId,
ISet<GameObject> gameObjects,
Account? account = null,
string branchName = "main",
bool createCommit = true,
Action<string>? onDataSentAction = null,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null)
{ {
try private ServerTransport transport;
{ private RecursiveConverter converter;
CancelOperations(); private CancellationTokenSource cancellationTokenSource;
cancellationTokenSource = new CancellationTokenSource(); #nullable enable
var client = new Client(account ?? AccountManager.GetDefaultAccount()); private void Awake()
transport = new ServerTransport(client.Account, streamId);
transport.CancellationToken = cancellationTokenSource.Token;
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
var data = converter.RecursivelyConvertToSpeckle(rootObjects,
o => gameObjects.Contains(o));
SendData(transport, data, client, branchName, createCommit, cancellationTokenSource.Token, onDataSentAction, onProgressAction, onErrorAction);
}
catch (Exception e)
{
throw new SpeckleException(e.ToString(), e, true, SentryLevel.Error);
}
}
public static void SendData(ServerTransport remoteTransport,
Base data,
Client client,
string branchName,
bool createCommit,
CancellationToken cancellationToken,
Action<string>? onDataSentAction = null,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null)
{
Task.Run(async () =>
{
var res = await Operations.Send(
data,
cancellationToken: cancellationToken,
new List<ITransport>() {remoteTransport},
useDefaultCache: true,
disposeTransports: true,
onProgressAction: onProgressAction,
onErrorAction: onErrorAction
);
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
if (createCommit && !cancellationToken.IsCancellationRequested)
{ {
long count = data.GetTotalChildrenCount(); converter = GetComponent<RecursiveConverter>();
await client.CommitCreate(cancellationToken,
new CommitCreateInput
{
streamId = remoteTransport.StreamId,
branchName = branchName,
objectId = res,
message = $"Sent {count} objects from Unity",
sourceApplication = HostApplications.Unity.Name,
totalChildrenCount = (int)count,
});
} }
onDataSentAction?.Invoke(res); /// <summary>
}, cancellationToken); /// Converts and sends the data of the last commit on the Stream
/// </summary>
/// <param name="streamId">ID of the stream to send to</param>
/// <param name="gameObjects">List of gameObjects to convert and send</param>
/// <param name="account">Account to use. If not provided the default account will be used</param>
/// <param name="branchName">Name of branch to send to</param>
/// <param name="createCommit">When true, will create a commit using the root object</param>
/// <param name="onDataSentAction">Action to run after the data has been sent</param>
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
/// <param name="onErrorAction">Action to run on error</param>
/// <exception cref="SpeckleException"></exception>
public void Send(
string streamId,
ISet<GameObject> gameObjects,
Account? account = null,
string branchName = "main",
bool createCommit = true,
Action<string>? onDataSentAction = null,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null
)
{
try
{
CancelOperations();
cancellationTokenSource = new CancellationTokenSource();
var client = new Client(account ?? AccountManager.GetDefaultAccount());
transport = new ServerTransport(client.Account, streamId);
transport.CancellationToken = cancellationTokenSource.Token;
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
var data = converter.RecursivelyConvertToSpeckle(
rootObjects,
o => gameObjects.Contains(o)
);
SendData(
transport,
data,
client,
branchName,
createCommit,
cancellationTokenSource.Token,
onDataSentAction,
onProgressAction,
onErrorAction
);
}
catch (Exception e)
{
throw new SpeckleException(e.ToString(), e, true, SentryLevel.Error);
}
}
public static void SendData(
ServerTransport remoteTransport,
Base data,
Client client,
string branchName,
bool createCommit,
CancellationToken cancellationToken,
Action<string>? onDataSentAction = null,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null
)
{
Task.Run(
async () =>
{
var res = await Operations.Send(
data,
cancellationToken: cancellationToken,
new List<ITransport>() { remoteTransport },
useDefaultCache: true,
disposeTransports: true,
onProgressAction: onProgressAction,
onErrorAction: onErrorAction
);
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
if (createCommit && !cancellationToken.IsCancellationRequested)
{
long count = data.GetTotalChildrenCount();
await client.CommitCreate(
cancellationToken,
new CommitCreateInput
{
streamId = remoteTransport.StreamId,
branchName = branchName,
objectId = res,
message = $"Sent {count} objects from Unity",
sourceApplication = HostApplications.Unity.Name,
totalChildrenCount = (int)count,
}
);
}
onDataSentAction?.Invoke(res);
},
cancellationToken
);
}
private void OnDestroy()
{
CancelOperations();
}
public void CancelOperations()
{
cancellationTokenSource?.Cancel();
transport?.Dispose();
cancellationTokenSource?.Dispose();
}
#region private methods
#endregion
} }
private void OnDestroy()
{
CancelOperations();
}
public void CancelOperations()
{
cancellationTokenSource?.Cancel();
transport?.Dispose();
cancellationTokenSource?.Dispose();
}
#region private methods
#endregion
}
} }

Просмотреть файл

@ -36,9 +36,11 @@ namespace Speckle.ConnectorUnity.Components
RC = GetComponent<RecursiveConverter>(); RC = GetComponent<RecursiveConverter>();
} }
public GameObject ConvertRecursivelyToNative(
public GameObject ConvertRecursivelyToNative(Base @base, string rootObjectName, Base @base,
Action<Base>? beforeConvertCallback) string rootObjectName,
Action<Base>? beforeConvertCallback
)
{ {
var rootObject = new GameObject(rootObjectName); var rootObject = new GameObject(rootObjectName);
@ -46,10 +48,9 @@ namespace Speckle.ConnectorUnity.Components
{ {
beforeConvertCallback?.Invoke(o); beforeConvertCallback?.Invoke(o);
return RC.ConverterInstance.CanConvertToNative(o) //Accept geometry return RC.ConverterInstance.CanConvertToNative(o) //Accept geometry
|| o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children || o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children
} }
// For the rootObject only, we will create property GameObjects // For the rootObject only, we will create property GameObjects
// i.e. revit categories // i.e. revit categories
foreach (var prop in @base.GetMembers()) foreach (var prop in @base.GetMembers())
@ -57,13 +58,15 @@ namespace Speckle.ConnectorUnity.Components
var converted = RC.RecursivelyConvertToNative(prop.Value, null, Predicate); var converted = RC.RecursivelyConvertToNative(prop.Value, null, Predicate);
//Skip empties //Skip empties
if (converted.Count <= 0) continue; if (converted.Count <= 0)
continue;
var propertyObject = new GameObject(prop.Key); var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform); propertyObject.transform.SetParent(rootObject.transform);
foreach (var o in converted) foreach (var o in converted)
{ {
if (o.transform.parent == null) o.transform.SetParent(propertyObject.transform); if (o.transform.parent == null)
o.transform.SetParent(propertyObject.transform);
} }
} }

Просмотреть файл

@ -22,6 +22,7 @@ namespace Speckle.ConnectorUnity.Components
#nullable enable #nullable enable
private CancellationTokenSource? _tokenSource; private CancellationTokenSource? _tokenSource;
void Awake() void Awake()
{ {
_converter = GetComponent<RecursiveConverter>(); _converter = GetComponent<RecursiveConverter>();
@ -35,33 +36,44 @@ namespace Speckle.ConnectorUnity.Components
public IEnumerator Receive_Routine() public IEnumerator Receive_Routine()
{ {
if (IsBusy()) throw new InvalidOperationException("A receive operation has already started"); if (IsBusy())
throw new InvalidOperationException("A receive operation has already started");
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
try try
{ {
StreamWrapper sw = new(url); StreamWrapper sw = new(url);
if (!sw.IsValid) if (!sw.IsValid)
throw new InvalidOperationException("Speckle url input is not a valid speckle stream/branch/commit"); throw new InvalidOperationException(
"Speckle url input is not a valid speckle stream/branch/commit"
);
var accountTask = new Utils.Utils.WaitForTask<Account>(async () => await GetAccount(sw)); var accountTask = new Utils.Utils.WaitForTask<Account>(
async () => await GetAccount(sw)
);
yield return accountTask; yield return accountTask;
_tokenSource.Token.ThrowIfCancellationRequested(); _tokenSource.Token.ThrowIfCancellationRequested();
using Client c = new(accountTask.Result); using Client c = new(accountTask.Result);
var objectIdTask = new Utils.Utils.WaitForTask<(string, Commit?)>(async () => await GetObjectID(sw, c)); var objectIdTask = new Utils.Utils.WaitForTask<(string, Commit?)>(
async () => await GetObjectID(sw, c)
);
yield return objectIdTask; yield return objectIdTask;
(string objectId, Commit? commit) = objectIdTask.Result; (string objectId, Commit? commit) = objectIdTask.Result;
Debug.Log($"Receiving from {sw.ServerUrl}..."); Debug.Log($"Receiving from {sw.ServerUrl}...");
var receiveTask = new Utils.Utils.WaitForTask<Base>(async () => await SpeckleReceiver.ReceiveAsync( var receiveTask = new Utils.Utils.WaitForTask<Base>(
c, async () =>
sw.StreamId, await SpeckleReceiver.ReceiveAsync(
objectId, c,
commit, sw.StreamId,
cancellationToken: _tokenSource.Token)); objectId,
commit,
cancellationToken: _tokenSource.Token
)
);
yield return receiveTask; yield return receiveTask;
Debug.Log("Converting to native..."); Debug.Log("Converting to native...");
@ -74,8 +86,10 @@ namespace Speckle.ConnectorUnity.Components
} }
} }
private async Task<(string objectId, Commit? commit)> GetObjectID(
private async Task<(string objectId, Commit? commit)> GetObjectID(StreamWrapper sw, Client client) StreamWrapper sw,
Client client
)
{ {
string objectId; string objectId;
Commit? commit = null; Commit? commit = null;
@ -95,7 +109,9 @@ namespace Speckle.ConnectorUnity.Components
{ {
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName; var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
var branch = await client.BranchGet(sw.StreamId, branchName, 1).ConfigureAwait(false); var branch = await client
.BranchGet(sw.StreamId, branchName, 1)
.ConfigureAwait(false);
if (!branch.commits.items.Any()) if (!branch.commits.items.Any())
throw new SpeckleException("The selected branch has no commits."); throw new SpeckleException("The selected branch has no commits.");
@ -105,10 +121,14 @@ namespace Speckle.ConnectorUnity.Components
return (objectId, commit); return (objectId, commit);
} }
[ContextMenu(nameof(Cancel))] [ContextMenu(nameof(Cancel))]
public void Cancel() public void Cancel()
{ {
if (IsNotBusy()) throw new InvalidOperationException("There are no pending receive operations to cancel"); if (IsNotBusy())
throw new InvalidOperationException(
"There are no pending receive operations to cancel"
);
_tokenSource!.Cancel(); _tokenSource!.Cancel();
} }
@ -126,7 +146,6 @@ namespace Speckle.ConnectorUnity.Components
_tokenSource?.Cancel(); _tokenSource?.Cancel();
} }
private async Task<Account> GetAccount(StreamWrapper sw) private async Task<Account> GetAccount(StreamWrapper sw)
{ {
Account account; Account account;
@ -134,10 +153,10 @@ namespace Speckle.ConnectorUnity.Components
{ {
account = await sw.GetAccount().ConfigureAwait(false); account = await sw.GetAccount().ConfigureAwait(false);
} }
catch (SpeckleException e) catch (SpeckleException)
{ {
if (string.IsNullOrEmpty(sw.StreamId)) if (string.IsNullOrEmpty(sw.StreamId))
throw e; throw;
//Fallback to a non authed account //Fallback to a non authed account
account = new Account account = new Account

Просмотреть файл

@ -12,7 +12,6 @@ using UnityEngine;
namespace Speckle.ConnectorUnity.Components namespace Speckle.ConnectorUnity.Components
{ {
/// <summary> /// <summary>
/// Struct that encapsulates the result of a <see cref="RecursiveConverter"/> ToNative conversion of a single Speckle Object (<see cref="Base"/>) /// Struct that encapsulates the result of a <see cref="RecursiveConverter"/> ToNative conversion of a single Speckle Object (<see cref="Base"/>)
/// </summary> /// </summary>
@ -42,7 +41,8 @@ namespace Speckle.ConnectorUnity.Components
public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted) public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted)
: this(traversalContext, converted, null) : this(traversalContext, converted, null)
{ {
if (converted is null) throw new ArgumentNullException(nameof(converted)); if (converted is null)
throw new ArgumentNullException(nameof(converted));
} }
/// <summary> /// <summary>
@ -52,14 +52,22 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="exception">The operation halting exception that occured</param> /// <param name="exception">The operation halting exception that occured</param>
/// <param name="converted">Optional converted GameObject</param> /// <param name="converted">Optional converted GameObject</param>
/// <exception cref="ArgumentNullException"/> /// <exception cref="ArgumentNullException"/>
public ConversionResult(TraversalContext traversalContext, [NotNull] Exception? exception, public ConversionResult(
GameObject? converted = null) TraversalContext traversalContext,
[NotNull] Exception? exception,
GameObject? converted = null
)
: this(traversalContext, converted, exception) : this(traversalContext, converted, exception)
{ {
if (exception is null) throw new ArgumentNullException(nameof(exception)); if (exception is null)
throw new ArgumentNullException(nameof(exception));
} }
private ConversionResult(TraversalContext traversalContext, GameObject? converted, Exception? exception) private ConversionResult(
TraversalContext traversalContext,
GameObject? converted,
Exception? exception
)
{ {
this.traversalContext = traversalContext; this.traversalContext = traversalContext;
this.converted = converted; this.converted = converted;
@ -74,7 +82,8 @@ namespace Speckle.ConnectorUnity.Components
/// <returns>True if the conversion was successful</returns> /// <returns>True if the conversion was successful</returns>
public bool WasSuccessful( public bool WasSuccessful(
[NotNullWhen(true)] out GameObject? converted, [NotNullWhen(true)] out GameObject? converted,
[NotNullWhen(false)] out Exception? exception) [NotNullWhen(false)] out Exception? exception
)
{ {
converted = this.converted; converted = this.converted;
exception = this.exception; exception = this.exception;
@ -88,7 +97,6 @@ namespace Speckle.ConnectorUnity.Components
public partial class RecursiveConverter public partial class RecursiveConverter
{ {
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/> /// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
/// <remarks>Calling this function will perform the conversion process synchronously</remarks> /// <remarks>Calling this function will perform the conversion process synchronously</remarks>
/// <returns>The conversion result</returns> /// <returns>The conversion result</returns>
@ -110,7 +118,9 @@ namespace Speckle.ConnectorUnity.Components
Predicate<TraversalContext>? predicate = null Predicate<TraversalContext>? predicate = null
) )
{ {
return StartCoroutine(RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).GetEnumerator()); return StartCoroutine(
RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).GetEnumerator()
);
} }
/// <summary> /// <summary>
@ -124,7 +134,8 @@ namespace Speckle.ConnectorUnity.Components
public IEnumerable<ConversionResult> RecursivelyConvertToNative_Enumerable( public IEnumerable<ConversionResult> RecursivelyConvertToNative_Enumerable(
Base rootObject, Base rootObject,
Transform? parent, Transform? parent,
Predicate<TraversalContext>? predicate = null) Predicate<TraversalContext>? predicate = null
)
{ {
var userPredicate = predicate ?? (_ => true); var userPredicate = predicate ?? (_ => true);
@ -138,7 +149,10 @@ namespace Speckle.ConnectorUnity.Components
Dictionary<Base, GameObject?> created = new(); Dictionary<Base, GameObject?> created = new();
foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created)) foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
{ {
if (!isActiveAndEnabled) throw new InvalidOperationException($"Cannot convert objects while {GetType()} is disabled"); if (!isActiveAndEnabled)
throw new InvalidOperationException(
$"Cannot convert objects while {GetType()} is disabled"
);
yield return conversionResult; yield return conversionResult;
} }
@ -156,7 +170,11 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="parent"></param> /// <param name="parent"></param>
/// <param name="outCreatedObjects"></param> /// <param name="outCreatedObjects"></param>
/// <returns></returns> /// <returns></returns>
protected IEnumerable<ConversionResult> ConvertTree(IEnumerable<TraversalContext> objectTree, Transform? parent, IDictionary<Base, GameObject?> outCreatedObjects) protected IEnumerable<ConversionResult> ConvertTree(
IEnumerable<TraversalContext> objectTree,
Transform? parent,
IDictionary<Base, GameObject?> outCreatedObjects
)
{ {
InitializeAssetCache(); InitializeAssetCache();
AssetCache.BeginWrite(); AssetCache.BeginWrite();
@ -183,11 +201,15 @@ namespace Speckle.ConnectorUnity.Components
AssetCache.FinishWrite(); AssetCache.FinishWrite();
} }
protected static Transform? GetParent(TraversalContext? tc, IDictionary<Base, GameObject?> createdObjects) protected static Transform? GetParent(
TraversalContext? tc,
IDictionary<Base, GameObject?> createdObjects
)
{ {
if (tc == null) return null; //We've reached the root object, and still not found a converted parent if (tc == null)
return null; //We've reached the root object, and still not found a converted parent
if(createdObjects.TryGetValue(tc.current, out GameObject? p) && p != null) if (createdObjects.TryGetValue(tc.current, out GameObject? p) && p != null)
return p.transform; return p.transform;
//Go one level up, and repeat! //Go one level up, and repeat!
@ -197,18 +219,20 @@ namespace Speckle.ConnectorUnity.Components
protected GameObject ConvertToNative(Base speckleObject, Transform? parentTransform) protected GameObject ConvertToNative(Base speckleObject, Transform? parentTransform)
{ {
GameObject? go = ConverterInstance.ConvertToNative(speckleObject) as GameObject; GameObject? go = ConverterInstance.ConvertToNative(speckleObject) as GameObject;
if (go == null) throw new SpeckleException("Conversion Returned Null"); if (go == null)
throw new SpeckleException("Conversion Returned Null");
go.transform.SetParent(parentTransform, true); go.transform.SetParent(parentTransform, true);
//Set some common for all created GameObjects //Set some common for all created GameObjects
//TODO add support for more unity specific props //TODO add support for more unity specific props
if(go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name)) if (go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name))
go.name = CoreUtils.GenerateObjectName(speckleObject); go.name = CoreUtils.GenerateObjectName(speckleObject);
if (speckleObject["physicsLayer"] is string layerName) if (speckleObject["physicsLayer"] is string layerName)
{ {
int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender
if (layer > -1) go.layer = layer; if (layer > -1)
go.layer = layer;
} }
//if (baseObject["tag"] is string t) go.tag = t; //if (baseObject["tag"] is string t) go.tag = t;
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic; //if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
@ -217,11 +241,25 @@ namespace Speckle.ConnectorUnity.Components
#region deprecated conversion functions #region deprecated conversion functions
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))] [Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
public IEnumerator ConvertCoroutine(Base rootObject, Transform? parent, List<GameObject> outCreatedObjects) public IEnumerator ConvertCoroutine(
=> ConvertCoroutine(rootObject, parent, outCreatedObjects,b => ConverterInstance.CanConvertToNative(b)); Base rootObject,
Transform? parent,
List<GameObject> outCreatedObjects
) =>
ConvertCoroutine(
rootObject,
parent,
outCreatedObjects,
b => ConverterInstance.CanConvertToNative(b)
);
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))] [Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
public IEnumerator ConvertCoroutine(Base rootObject, Transform? parent, List<GameObject> outCreatedObjects, Func<Base, bool> predicate) public IEnumerator ConvertCoroutine(
Base rootObject,
Transform? parent,
List<GameObject> outCreatedObjects,
Func<Base, bool> predicate
)
{ {
foreach (string propertyName in GetPotentialChildren(rootObject)) foreach (string propertyName in GetPotentialChildren(rootObject))
{ {
@ -238,13 +276,17 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param> /// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param>
/// <returns> A list of all created <see cref="GameObject"/>s</returns> /// <returns> A list of all created <see cref="GameObject"/>s</returns>
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))] [Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent) public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent) =>
=> RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b)); RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b));
/// <inheritdoc cref="RecursivelyConvertToNative(object, Transform)"/> /// <inheritdoc cref="RecursivelyConvertToNative(object, Transform)"/>
/// <param name="predicate">A function to determine if an object should be converted</param> /// <param name="predicate">A function to determine if an object should be converted</param>
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))] [Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent, Func<Base, bool> predicate) public virtual List<GameObject> RecursivelyConvertToNative(
object? o,
Transform? parent,
Func<Base, bool> predicate
)
{ {
InitializeAssetCache(); InitializeAssetCache();
@ -263,7 +305,6 @@ namespace Speckle.ConnectorUnity.Components
return createdGameObjects; return createdGameObjects;
} }
private void InitializeAssetCache() private void InitializeAssetCache()
@ -277,10 +318,15 @@ namespace Speckle.ConnectorUnity.Components
} }
[Obsolete] [Obsolete]
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects) public virtual void RecurseTreeToNative(
Base baseObject,
Transform? parent,
Func<Base, bool> predicate,
IList<GameObject> outCreatedObjects
)
{ {
object? converted = null; object? converted = null;
if(predicate(baseObject)) if (predicate(baseObject))
converted = ConverterInstance.ConvertToNative(baseObject); converted = ConverterInstance.ConvertToNative(baseObject);
// Handle new GameObjects // Handle new GameObjects
@ -294,13 +340,14 @@ namespace Speckle.ConnectorUnity.Components
//Set some common for all created GameObjects //Set some common for all created GameObjects
//TODO add support for more unity specific props //TODO add support for more unity specific props
if(go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name)) if (go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name))
go.name = CoreUtils.GenerateObjectName(baseObject); go.name = CoreUtils.GenerateObjectName(baseObject);
//if (baseObject["tag"] is string t) go.tag = t; //if (baseObject["tag"] is string t) go.tag = t;
if (baseObject["physicsLayer"] is string layerName) if (baseObject["physicsLayer"] is string layerName)
{ {
int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender
if (layer > -1) go.layer = layer; if (layer > -1)
go.layer = layer;
} }
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic; //if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
} }
@ -313,19 +360,23 @@ namespace Speckle.ConnectorUnity.Components
{ {
ConvertChild(baseObject[propertyName], nextParent, predicate, outCreatedObjects); ConvertChild(baseObject[propertyName], nextParent, predicate, outCreatedObjects);
} }
} }
[Obsolete] [Obsolete]
private IEnumerable<string> GetPotentialChildren(Base baseObject) private IEnumerable<string> GetPotentialChildren(Base baseObject)
{ {
return ConverterInstance.CanConvertToNative(baseObject) return ConverterInstance.CanConvertToNative(baseObject)
? new []{"elements"} ? new[] { "elements" }
: baseObject.GetMembers().Keys; : baseObject.GetMembers().Keys;
} }
[Obsolete] [Obsolete]
protected virtual void ConvertChild(object? value, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects) protected virtual void ConvertChild(
object? value,
Transform? parent,
Func<Base, bool> predicate,
IList<GameObject> outCreatedObjects
)
{ {
foreach (Base b in GraphTraversal.TraverseMember(value)) foreach (Base b in GraphTraversal.TraverseMember(value))
{ {

Просмотреть файл

@ -8,7 +8,6 @@ using UnityEngine;
namespace Speckle.ConnectorUnity.Components namespace Speckle.ConnectorUnity.Components
{ {
public partial class RecursiveConverter public partial class RecursiveConverter
{ {
/// <summary> /// <summary>
@ -31,7 +30,10 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="rootObjects">Root objects of a tree</param> /// <param name="rootObjects">Root objects of a tree</param>
/// <param name="predicate">A function to determine if an object should be converted</param> /// <param name="predicate">A function to determine if an object should be converted</param>
/// <returns>A simple <see cref="Base"/> wrapping converted objects</returns> /// <returns>A simple <see cref="Base"/> wrapping converted objects</returns>
public virtual Base RecursivelyConvertToSpeckle(IEnumerable<GameObject> rootObjects, Func<GameObject, bool> predicate) public virtual Base RecursivelyConvertToSpeckle(
IEnumerable<GameObject> rootObjects,
Func<GameObject, bool> predicate
)
{ {
List<Base> convertedRootObjects = new List<Base>(); List<Base> convertedRootObjects = new List<Base>();
foreach (GameObject rootObject in rootObjects) foreach (GameObject rootObject in rootObjects)
@ -39,22 +41,26 @@ namespace Speckle.ConnectorUnity.Components
RecurseTreeToSpeckle(rootObject, predicate, convertedRootObjects); RecurseTreeToSpeckle(rootObject, predicate, convertedRootObjects);
} }
return new Base() return new Base() { ["@objects"] = convertedRootObjects, };
{
["@objects"] = convertedRootObjects,
};
} }
public virtual Base RecursivelyConvertToSpeckle(GameObject rootObject, Func<GameObject, bool> predicate) public virtual Base RecursivelyConvertToSpeckle(
GameObject rootObject,
Func<GameObject, bool> predicate
)
{ {
return RecursivelyConvertToSpeckle(new[] {rootObject}, predicate); return RecursivelyConvertToSpeckle(new[] { rootObject }, predicate);
} }
public virtual void RecurseTreeToSpeckle(GameObject currentObject, Func<GameObject, bool> predicate, List<Base> outConverted) public virtual void RecurseTreeToSpeckle(
GameObject currentObject,
Func<GameObject, bool> predicate,
List<Base> outConverted
)
{ {
// Convert children first // Convert children first
var convertedChildren = new List<Base>(currentObject.transform.childCount); var convertedChildren = new List<Base>(currentObject.transform.childCount);
foreach(Transform child in currentObject.transform) foreach (Transform child in currentObject.transform)
{ {
RecurseTreeToSpeckle(child.gameObject, predicate, convertedChildren); RecurseTreeToSpeckle(child.gameObject, predicate, convertedChildren);
} }
@ -71,7 +77,6 @@ namespace Speckle.ConnectorUnity.Components
// Skip this object, and output any children // Skip this object, and output any children
outConverted.AddRange(convertedChildren); outConverted.AddRange(convertedChildren);
} }
} }
} }
} }

Просмотреть файл

@ -19,6 +19,7 @@ using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
[assembly: InternalsVisibleTo("Speckle.ConnectorUnity.Components.Editor")] [assembly: InternalsVisibleTo("Speckle.ConnectorUnity.Components.Editor")]
namespace Speckle.ConnectorUnity.Components namespace Speckle.ConnectorUnity.Components
{ {
[ExecuteAlways] [ExecuteAlways]
@ -44,12 +45,16 @@ namespace Speckle.ConnectorUnity.Components
[Header("Events")] [Header("Events")]
[HideInInspector] [HideInInspector]
public CommitSelectionEvent OnCommitSelectionChange = new(); public CommitSelectionEvent OnCommitSelectionChange = new();
[HideInInspector] [HideInInspector]
public OperationProgressEvent OnReceiveProgressAction = new(); public OperationProgressEvent OnReceiveProgressAction = new();
[HideInInspector] [HideInInspector]
public ErrorActionEvent OnErrorAction = new(); public ErrorActionEvent OnErrorAction = new();
[HideInInspector] [HideInInspector]
public ChildrenCountHandler OnTotalChildrenCountKnown = new(); public ChildrenCountHandler OnTotalChildrenCountKnown = new();
[HideInInspector] [HideInInspector]
public ReceiveCompleteHandler OnComplete = new(); public ReceiveCompleteHandler OnComplete = new();
@ -66,7 +71,8 @@ namespace Speckle.ConnectorUnity.Components
/// <returns><see langword="true"/> if the cancellation request was made. <see langword="false"/> if there was no pending operation to cancel (see <see cref="IsReceiving"/>)</returns> /// <returns><see langword="true"/> if the cancellation request was made. <see langword="false"/> if there was no pending operation to cancel (see <see cref="IsReceiving"/>)</returns>
public bool Cancel() public bool Cancel()
{ {
if (CancellationTokenSource == null) return false; if (CancellationTokenSource == null)
return false;
CancellationTokenSource.Cancel(); CancellationTokenSource.Cancel();
return true; return true;
} }
@ -79,11 +85,17 @@ namespace Speckle.ConnectorUnity.Components
/// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> upon completion</remarks> /// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> upon completion</remarks>
/// <seealso cref="ReceiveAsync(System.Threading.CancellationToken)"/> /// <seealso cref="ReceiveAsync(System.Threading.CancellationToken)"/>
/// <seealso cref="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/> /// <seealso cref="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
public IEnumerator ReceiveAndConvert_Routine(Transform? parent, Predicate<TraversalContext>? predicate = null) public IEnumerator ReceiveAndConvert_Routine(
Transform? parent,
Predicate<TraversalContext>? predicate = null
)
{ {
if (IsReceiving) if (IsReceiving)
{ {
OnErrorAction.Invoke("Failed to receive", new InvalidOperationException("A pending receive operation has already started")); OnErrorAction.Invoke(
"Failed to receive",
new InvalidOperationException("A pending receive operation has already started")
);
yield break; yield break;
} }
@ -118,9 +130,11 @@ namespace Speckle.ConnectorUnity.Components
FinishOperation(); FinishOperation();
} }
/// <inheritdoc cref="ReceiveAndConvert_Routine"/> /// <inheritdoc cref="ReceiveAndConvert_Routine"/>
public async void ReceiveAndConvert_Async(Transform? parent, Predicate<TraversalContext>? predicate = null) public async void ReceiveAndConvert_Async(
Transform? parent,
Predicate<TraversalContext>? predicate = null
)
{ {
try try
{ {
@ -174,13 +188,16 @@ namespace Speckle.ConnectorUnity.Components
public void ValidateSelection(out Client client, out Stream stream, out Commit commit) public void ValidateSelection(out Client client, out Stream stream, out Commit commit)
{ {
Client? selectedClient = Account.Client; Client? selectedClient = Account.Client;
client = selectedClient ?? throw new InvalidOperationException("Invalid account selection"); client =
selectedClient ?? throw new InvalidOperationException("Invalid account selection");
Stream? selectedStream = Stream.Selected; Stream? selectedStream = Stream.Selected;
stream = selectedStream ?? throw new InvalidOperationException("Invalid stream selection"); stream =
selectedStream ?? throw new InvalidOperationException("Invalid stream selection");
Commit? selectedCommit = Commit.Selected; Commit? selectedCommit = Commit.Selected;
commit = selectedCommit ?? throw new InvalidOperationException("Invalid commit selection"); commit =
selectedCommit ?? throw new InvalidOperationException("Invalid commit selection");
} }
/// <summary> /// <summary>
@ -189,7 +206,10 @@ namespace Speckle.ConnectorUnity.Components
/// <exception cref="InvalidOperationException">already receiving</exception> /// <exception cref="InvalidOperationException">already receiving</exception>
protected internal CancellationToken BeginOperation() protected internal CancellationToken BeginOperation()
{ {
if (IsReceiving) throw new InvalidOperationException("A pending receive operation has already started"); if (IsReceiving)
throw new InvalidOperationException(
"A pending receive operation has already started"
);
CancellationTokenSource?.Dispose(); CancellationTokenSource?.Dispose();
CancellationTokenSource = new(); CancellationTokenSource = new();
@ -199,7 +219,8 @@ namespace Speckle.ConnectorUnity.Components
protected internal void FinishOperation() protected internal void FinishOperation()
{ {
if (!IsReceiving) throw new InvalidOperationException("No pending operations to finish"); if (!IsReceiving)
throw new InvalidOperationException("No pending operations to finish");
CancellationTokenSource!.Dispose(); CancellationTokenSource!.Dispose();
CancellationTokenSource = null; CancellationTokenSource = null;
@ -224,7 +245,8 @@ namespace Speckle.ConnectorUnity.Components
Commit? commit, Commit? commit,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null, Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<int>? onTotalChildrenCountKnown = null, Action<int>? onTotalChildrenCountKnown = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default
)
{ {
using var transport = new ServerTransportV2(client.Account, streamId); using var transport = new ServerTransportV2(client.Account, streamId);
@ -232,39 +254,57 @@ namespace Speckle.ConnectorUnity.Components
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
Base? requestedObject = await Operations.Receive( Base? requestedObject = await Operations
objectId: objectId, .Receive(
cancellationToken: cancellationToken, objectId: objectId,
remoteTransport: transport, cancellationToken: cancellationToken,
onProgressAction: onProgressAction, remoteTransport: transport,
onErrorAction: (s, ex) => onProgressAction: onProgressAction,
{ onErrorAction: (s, ex) =>
//Don't wrap cancellation exceptions!
if (ex is OperationCanceledException)
throw ex;
//HACK: Sometimes, the task was cancelled, and Operations.Receive doesn't fail in a reliable way. In this case, the exception is often simply a symptom of a cancel.
if (cancellationToken.IsCancellationRequested)
{ {
SpeckleLog.Logger.Warning(ex, "A task was cancelled, ignoring potentially symptomatic exception"); //Don't wrap cancellation exceptions!
cancellationToken.ThrowIfCancellationRequested(); if (ex is OperationCanceledException)
} throw ex;
//Treat all operation errors as fatal //HACK: Sometimes, the task was cancelled, and Operations.Receive doesn't fail in a reliable way. In this case, the exception is often simply a symptom of a cancel.
throw new SpeckleException($"Failed to receive requested object {objectId} from server: {s}", ex); if (cancellationToken.IsCancellationRequested)
}, {
onTotalChildrenCountKnown: onTotalChildrenCountKnown, SpeckleLog.Logger.Warning(
disposeTransports: false ex,
).ConfigureAwait(false); "A task was cancelled, ignoring potentially symptomatic exception"
);
cancellationToken.ThrowIfCancellationRequested();
}
Analytics.TrackEvent(client.Account, Analytics.Events.Receive, new Dictionary<string, object>() //Treat all operation errors as fatal
{ throw new SpeckleException(
{"mode", nameof(SpeckleReceiver)}, $"Failed to receive requested object {objectId} from server: {s}",
{"sourceHostApp", HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug}, ex
{"sourceHostAppVersion", commit?.sourceApplication ?? ""}, );
{"hostPlatform", Application.platform.ToString()}, },
{"isMultiplayer", commit != null && commit.authorId != client.Account.userInfo.id}, onTotalChildrenCountKnown: onTotalChildrenCountKnown,
}); disposeTransports: false
)
.ConfigureAwait(false);
Analytics.TrackEvent(
client.Account,
Analytics.Events.Receive,
new Dictionary<string, object>()
{
{ "mode", nameof(SpeckleReceiver) },
{
"sourceHostApp",
HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug
},
{ "sourceHostAppVersion", commit?.sourceApplication ?? "" },
{ "hostPlatform", Application.platform.ToString() },
{
"isMultiplayer",
commit != null && commit.authorId != client.Account.userInfo.id
},
}
);
if (requestedObject == null) if (requestedObject == null)
throw new SpeckleException($"Operation {nameof(Operations.Receive)} returned null"); throw new SpeckleException($"Operation {nameof(Operations.Receive)} returned null");
@ -274,13 +314,20 @@ namespace Speckle.ConnectorUnity.Components
//Read receipt //Read receipt
try try
{ {
await client.CommitReceived(cancellationToken, new CommitReceivedInput await client
{ .CommitReceived(
streamId = streamId, cancellationToken,
commitId = commit?.id, new CommitReceivedInput
message = $"received commit from {Application.unityVersion}", {
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()) streamId = streamId,
}).ConfigureAwait(false); commitId = commit?.id,
message = $"received commit from {Application.unityVersion}",
sourceApplication = HostApplications.Unity.GetVersion(
CoreUtils.GetHostAppVersion()
)
}
)
.ConfigureAwait(false);
} }
catch (Exception e) catch (Exception e)
{ {
@ -299,9 +346,18 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="rootObjectName">The name of the parent <see cref="GameObject"/> to create</param> /// <param name="rootObjectName">The name of the parent <see cref="GameObject"/> to create</param>
/// <param name="beforeConvertCallback">Callback for each object converted</param> /// <param name="beforeConvertCallback">Callback for each object converted</param>
/// <returns>The created parent <see cref="GameObject"/></returns> /// <returns>The created parent <see cref="GameObject"/></returns>
[Obsolete("Use " + nameof(RecursiveConverter) + " Now we have implemented support for " + nameof(Collection) + "s, receiving any collection is now the default behaviour")] [Obsolete(
public GameObject ConvertToNativeWithCategories(Base @base, string rootObjectName, "Use "
Action<Base>? beforeConvertCallback) + nameof(RecursiveConverter)
+ " Now we have implemented support for "
+ nameof(Collection)
+ "s, receiving any collection is now the default behaviour"
)]
public GameObject ConvertToNativeWithCategories(
Base @base,
string rootObjectName,
Action<Base>? beforeConvertCallback
)
{ {
var rootObject = new GameObject(rootObjectName); var rootObject = new GameObject(rootObjectName);
@ -309,10 +365,9 @@ namespace Speckle.ConnectorUnity.Components
{ {
beforeConvertCallback?.Invoke(o); beforeConvertCallback?.Invoke(o);
return Converter.ConverterInstance.CanConvertToNative(o) //Accept geometry return Converter.ConverterInstance.CanConvertToNative(o) //Accept geometry
|| o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children || o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children
} }
// For the rootObject only, we will create property GameObjects // For the rootObject only, we will create property GameObjects
// i.e. revit categories // i.e. revit categories
foreach (var prop in @base.GetMembers()) foreach (var prop in @base.GetMembers())
@ -320,13 +375,15 @@ namespace Speckle.ConnectorUnity.Components
var converted = Converter.RecursivelyConvertToNative(prop.Value, null, Predicate); var converted = Converter.RecursivelyConvertToNative(prop.Value, null, Predicate);
//Skip empties //Skip empties
if (converted.Count <= 0) continue; if (converted.Count <= 0)
continue;
var propertyObject = new GameObject(prop.Key); var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform); propertyObject.transform.SetParent(rootObject.transform);
foreach (var o in converted) foreach (var o in converted)
{ {
if (o.transform.parent == null) o.transform.SetParent(propertyObject.transform); if (o.transform.parent == null)
o.transform.SetParent(propertyObject.transform);
} }
} }
@ -346,7 +403,8 @@ namespace Speckle.ConnectorUnity.Components
[NotNullWhen(true)] out Client? client, [NotNullWhen(true)] out Client? client,
[NotNullWhen(true)] out Stream? stream, [NotNullWhen(true)] out Stream? stream,
[NotNullWhen(true)] out Commit? commit, [NotNullWhen(true)] out Commit? commit,
[NotNullWhen(false)] out string? error) [NotNullWhen(false)] out string? error
)
{ {
Account? account = Account.Selected; Account? account = Account.Selected;
stream = Stream.Selected; stream = Stream.Selected;
@ -381,16 +439,22 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="allAngles">when <see langword="true"/>, will fetch 360 degree preview image</param> /// <param name="allAngles">when <see langword="true"/>, will fetch 360 degree preview image</param>
/// <param name="callback">Callback function to be called when the web request completes</param> /// <param name="callback">Callback function to be called when the web request completes</param>
/// <returns>The executing <see cref="Coroutine"/> or <see langword="null"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns> /// <returns>The executing <see cref="Coroutine"/> or <see langword="null"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns>
public Coroutine? GetPreviewImage(/*bool allAngles,*/ Action<Texture2D?> callback) public Coroutine? GetPreviewImage( /*bool allAngles,*/
Action<Texture2D?> callback
)
{ {
Account? account = Account.Selected; Account? account = Account.Selected;
if (account == null) return null; if (account == null)
return null;
string? streamId = Stream.Selected?.id; string? streamId = Stream.Selected?.id;
if (streamId == null) return null; if (streamId == null)
return null;
string? commitId = Commit.Selected?.id; string? commitId = Commit.Selected?.id;
if (commitId == null) return null; if (commitId == null)
return null;
string angles = /*allAngles ? "all" :*/ ""; string angles = /*allAngles ? "all" :*/
"";
string url = $"{account.serverInfo.url}/preview/{streamId}/commits/{commitId}/{angles}"; string url = $"{account.serverInfo.url}/preview/{streamId}/commits/{commitId}/{angles}";
string authToken = account.token; string authToken = account.token;
@ -406,7 +470,6 @@ namespace Speckle.ConnectorUnity.Components
} }
#endif #endif
public string GetSelectedUrl() public string GetSelectedUrl()
{ {
string serverUrl = Account.Selected!.serverInfo.url; string serverUrl = Account.Selected!.serverInfo.url;
@ -414,9 +477,12 @@ namespace Speckle.ConnectorUnity.Components
string? branchName = Branch.Selected?.name; string? branchName = Branch.Selected?.name;
string? commitId = Commit.Selected?.id; string? commitId = Commit.Selected?.id;
if (string.IsNullOrEmpty(streamId)) return serverUrl; if (string.IsNullOrEmpty(streamId))
if (!string.IsNullOrEmpty(commitId)) return $"{serverUrl}/streams/{streamId}/commits/{commitId}"; return serverUrl;
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}"; if (!string.IsNullOrEmpty(commitId))
return $"{serverUrl}/streams/{streamId}/commits/{commitId}";
if (!string.IsNullOrEmpty(branchName))
return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
return $"{serverUrl}/streams/{streamId}"; return $"{serverUrl}/streams/{streamId}";
} }
@ -436,9 +502,8 @@ namespace Speckle.ConnectorUnity.Components
Stream.Initialise(); Stream.Initialise();
Branch.Initialise(); Branch.Initialise();
Commit.Initialise(); Commit.Initialise();
Commit.OnSelectionChange = Commit.OnSelectionChange = () => OnCommitSelectionChange?.Invoke(Commit.Selected);
() => OnCommitSelectionChange?.Invoke(Commit.Selected); if (Account.Options is not { Length: > 0 } || forceRefresh)
if(Account.Options is not {Length: > 0} || forceRefresh)
Account.RefreshOptions(); Account.RefreshOptions();
} }
@ -456,6 +521,7 @@ namespace Speckle.ConnectorUnity.Components
{ {
//pass //pass
} }
public void OnAfterDeserialize() public void OnAfterDeserialize()
{ {
Initialise(); Initialise();
@ -464,28 +530,45 @@ namespace Speckle.ConnectorUnity.Components
#region Deprecated members #region Deprecated members
[Obsolete("use " + nameof(ReceiveAndConvertRoutine), true)] [Obsolete("use " + nameof(ReceiveAndConvertRoutine), true)]
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action<Base>? beforeConvertCallback = null) public IEnumerator ReceiveAndConvertRoutine(
SpeckleReceiver speckleReceiver,
string rootObjectName,
Action<Base>? beforeConvertCallback = null
)
{ {
// ReSharper disable once MethodSupportsCancellation // ReSharper disable once MethodSupportsCancellation
Task<Base> receiveOperation = Task.Run(async () => await ReceiveAsync(CancellationToken)); Task<Base> receiveOperation = Task.Run(
async () => await ReceiveAsync(CancellationToken)
);
yield return new WaitUntil(() => receiveOperation.IsCompleted); yield return new WaitUntil(() => receiveOperation.IsCompleted);
Base? b = receiveOperation.Result; Base? b = receiveOperation.Result;
if (b == null) yield break; if (b == null)
yield break;
//NOTE: coroutine doesn't break for each catergory/object //NOTE: coroutine doesn't break for each catergory/object
GameObject go = ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback); ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
} }
#endregion #endregion
} }
[Serializable] public sealed class CommitSelectionEvent : UnityEvent<Commit?> { } [Serializable]
[Serializable] public sealed class BranchSelectionEvent : UnityEvent<Branch?> { } public sealed class CommitSelectionEvent : UnityEvent<Commit?> { }
[Serializable] public sealed class ErrorActionEvent : UnityEvent<string, Exception> { }
[Serializable] public sealed class OperationProgressEvent : UnityEvent<ConcurrentDictionary<string, int>> { } [Serializable]
[Serializable] public sealed class ReceiveCompleteHandler : UnityEvent<Transform?> { } public sealed class BranchSelectionEvent : UnityEvent<Branch?> { }
[Serializable] public sealed class ChildrenCountHandler : UnityEvent<int> { }
[Serializable]
public sealed class ErrorActionEvent : UnityEvent<string, Exception> { }
[Serializable]
public sealed class OperationProgressEvent : UnityEvent<ConcurrentDictionary<string, int>> { }
[Serializable]
public sealed class ReceiveCompleteHandler : UnityEvent<Transform?> { }
[Serializable]
public sealed class ChildrenCountHandler : UnityEvent<int> { }
} }

Просмотреть файл

@ -35,8 +35,10 @@ namespace Speckle.ConnectorUnity.Components
[Header("Events")] [Header("Events")]
[HideInInspector] [HideInInspector]
public BranchSelectionEvent OnBranchSelectionChange; public BranchSelectionEvent OnBranchSelectionChange;
[HideInInspector] [HideInInspector]
public ErrorActionEvent OnErrorAction; public ErrorActionEvent OnErrorAction;
[HideInInspector] [HideInInspector]
public OperationProgressEvent OnSendProgressAction; public OperationProgressEvent OnSendProgressAction;
#nullable enable #nullable enable
@ -49,13 +51,21 @@ namespace Speckle.ConnectorUnity.Components
CancellationTokenSource?.Cancel(); CancellationTokenSource?.Cancel();
CancellationTokenSource?.Dispose(); CancellationTokenSource?.Dispose();
CancellationTokenSource = new CancellationTokenSource(); CancellationTokenSource = new CancellationTokenSource();
if(!GetSelection(out Client? client, out Stream? stream, out Branch? branch, out string? error)) if (
!GetSelection(
out Client? client,
out Stream? stream,
out Branch? branch,
out string? error
)
)
throw new SpeckleException(error); throw new SpeckleException(error);
ServerTransport transport = new ServerTransport(client.Account, stream.id); ServerTransport transport = new ServerTransport(client.Account, stream.id);
transport.CancellationToken = CancellationTokenSource.Token; transport.CancellationToken = CancellationTokenSource.Token;
return await SendDataAsync(CancellationTokenSource.Token, return await SendDataAsync(
CancellationTokenSource.Token,
remoteTransport: transport, remoteTransport: transport,
data: data, data: data,
client: client, client: client,
@ -66,39 +76,53 @@ namespace Speckle.ConnectorUnity.Components
); );
} }
public static async Task<string> SendDataAsync(CancellationToken cancellationToken, public static async Task<string> SendDataAsync(
CancellationToken cancellationToken,
ServerTransport remoteTransport, ServerTransport remoteTransport,
Base data, Base data,
Client client, Client client,
string branchName, string branchName,
bool createCommit, bool createCommit,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null, Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null) Action<string, Exception>? onErrorAction = null
)
{ {
string res = await Operations.Send( string res = await Operations.Send(
data, data,
cancellationToken: cancellationToken, cancellationToken: cancellationToken,
new List<ITransport>{remoteTransport}, new List<ITransport> { remoteTransport },
useDefaultCache: true, useDefaultCache: true,
disposeTransports: true, disposeTransports: true,
onProgressAction: onProgressAction, onProgressAction: onProgressAction,
onErrorAction: onErrorAction onErrorAction: onErrorAction
); );
Analytics.TrackEvent(client.Account, Analytics.Events.Send, new Dictionary<string, object>() Analytics.TrackEvent(
{ client.Account,
{"mode", nameof(SpeckleSender)}, Analytics.Events.Send,
{"hostPlatform", Application.platform.ToString()}, new Dictionary<string, object>()
}); {
{ "mode", nameof(SpeckleSender) },
{ "hostPlatform", Application.platform.ToString() },
}
);
if (createCommit && !cancellationToken.IsCancellationRequested) if (createCommit && !cancellationToken.IsCancellationRequested)
{ {
string streamId = remoteTransport.StreamId; string streamId = remoteTransport.StreamId;
string unityVer = $"Unity {Application.unityVersion.Substring(0,6)}"; string unityVer = $"Unity {Application.unityVersion.Substring(0, 6)}";
data.totalChildrenCount = data.GetTotalChildrenCount(); data.totalChildrenCount = data.GetTotalChildrenCount();
string commitMessage = $"Sent {data.totalChildrenCount} objects from {unityVer}"; string commitMessage = $"Sent {data.totalChildrenCount} objects from {unityVer}";
string commitId = await CreateCommit(cancellationToken, data, client, streamId, branchName, res, commitMessage); string commitId = await CreateCommit(
cancellationToken,
data,
client,
streamId,
branchName,
res,
commitMessage
);
string url = $"{client.ServerUrl}/streams/{streamId}/commits/{commitId}"; string url = $"{client.ServerUrl}/streams/{streamId}/commits/{commitId}";
Debug.Log($"Data successfully sent to <a href=\"{url}\">{url}</a>"); Debug.Log($"Data successfully sent to <a href=\"{url}\">{url}</a>");
} }
@ -106,24 +130,30 @@ namespace Speckle.ConnectorUnity.Components
return res; return res;
} }
public static async Task<string> CreateCommit(CancellationToken cancellationToken, public static async Task<string> CreateCommit(
CancellationToken cancellationToken,
Base data, Base data,
Client client, Client client,
string streamId, string streamId,
string branchName, string branchName,
string objectId, string objectId,
string message) string message
)
{ {
string commitId = await client.CommitCreate(cancellationToken, string commitId = await client.CommitCreate(
cancellationToken,
new CommitCreateInput new CommitCreateInput
{ {
streamId = streamId, streamId = streamId,
branchName = branchName, branchName = branchName,
objectId = objectId, objectId = objectId,
message = message, message = message,
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), sourceApplication = HostApplications.Unity.GetVersion(
CoreUtils.GetHostAppVersion()
),
totalChildrenCount = (int)data.totalChildrenCount, totalChildrenCount = (int)data.totalChildrenCount,
}); }
);
return commitId; return commitId;
} }
@ -132,7 +162,8 @@ namespace Speckle.ConnectorUnity.Components
[NotNullWhen(true)] out Client? client, [NotNullWhen(true)] out Client? client,
[NotNullWhen(true)] out Stream? stream, [NotNullWhen(true)] out Stream? stream,
[NotNullWhen(true)] out Branch? branch, [NotNullWhen(true)] out Branch? branch,
[NotNullWhen(false)] out string? error) [NotNullWhen(false)] out string? error
)
{ {
Account? account = Account.Selected; Account? account = Account.Selected;
stream = Stream.Selected; stream = Stream.Selected;
@ -161,7 +192,6 @@ namespace Speckle.ConnectorUnity.Components
return true; return true;
} }
#if UNITY_EDITOR #if UNITY_EDITOR
[ContextMenu("Open Speckle Stream in Browser")] [ContextMenu("Open Speckle Stream in Browser")]
protected void OpenUrlInBrowser() protected void OpenUrlInBrowser()
@ -170,18 +200,20 @@ namespace Speckle.ConnectorUnity.Components
Application.OpenURL(url); Application.OpenURL(url);
} }
#endif #endif
public string GetSelectedUrl() public string GetSelectedUrl()
{ {
string serverUrl = Account.Selected!.serverInfo.url; string serverUrl = Account.Selected!.serverInfo.url;
string? streamId = Stream.Selected?.id; string? streamId = Stream.Selected?.id;
string? branchName = Branch.Selected?.name; string? branchName = Branch.Selected?.name;
if (string.IsNullOrEmpty(streamId)) return serverUrl; if (string.IsNullOrEmpty(streamId))
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}"; return serverUrl;
if (!string.IsNullOrEmpty(branchName))
return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
return $"{serverUrl}/streams/{streamId}"; return $"{serverUrl}/streams/{streamId}";
} }
public void Awake() public void Awake()
{ {
Initialise(true); Initialise(true);
@ -198,7 +230,7 @@ namespace Speckle.ConnectorUnity.Components
Stream.Initialise(); Stream.Initialise();
Branch.Initialise(); Branch.Initialise();
Branch.OnSelectionChange = () => OnBranchSelectionChange?.Invoke(Branch.Selected); Branch.OnSelectionChange = () => OnBranchSelectionChange?.Invoke(Branch.Selected);
if(Account.Options is not {Length: > 0} || forceRefresh) if (Account.Options is not { Length: > 0 } || forceRefresh)
Account.RefreshOptions(); Account.RefreshOptions();
} }
@ -212,10 +244,10 @@ namespace Speckle.ConnectorUnity.Components
{ {
//pass //pass
} }
public void OnAfterDeserialize() public void OnAfterDeserialize()
{ {
Initialise(); Initialise();
} }
} }
} }

Просмотреть файл

@ -1,6 +1,4 @@
using Objects.BuiltElements; using Objects.BuiltElements;
using Speckle.Core.Kits;
using UnityEditor;
using UnityEngine; using UnityEngine;
namespace Objects.Converter.Unity namespace Objects.Converter.Unity

Просмотреть файл

@ -137,7 +137,7 @@ namespace Objects.Converter.Unity
} }
var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray(); var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray();
;
List<int> sColors = new List<int>(nColors.Length); List<int> sColors = new List<int>(nColors.Length);
sColors.AddRange(nColors.Select(c => c.ToIntColor())); sColors.AddRange(nColors.Select(c => c.ToIntColor()));

Просмотреть файл

@ -46,7 +46,8 @@ namespace Objects.Converter.Unity
} }
//Just used as cache key for the default (null) material //Just used as cache key for the default (null) material
private static RenderMaterial defaultMaterialPlaceholder = new() { id = "defaultMaterial" }; private static readonly RenderMaterial DefaultMaterialPlaceholder =
new() { id = "defaultMaterial" };
public virtual Material RenderMaterialToNative(RenderMaterial? renderMaterial) public virtual Material RenderMaterialToNative(RenderMaterial? renderMaterial)
{ {
@ -57,13 +58,13 @@ namespace Objects.Converter.Unity
{ {
if ( if (
!LoadedAssets.TryGetObject( !LoadedAssets.TryGetObject(
defaultMaterialPlaceholder, DefaultMaterialPlaceholder,
out Material? defaultMaterial out Material? defaultMaterial
) )
) )
{ {
defaultMaterial = new Material(OpaqueMaterialShader); defaultMaterial = new Material(OpaqueMaterialShader);
LoadedAssets.TrySaveObject(defaultMaterialPlaceholder, defaultMaterial); LoadedAssets.TrySaveObject(DefaultMaterialPlaceholder, defaultMaterial);
} }
return defaultMaterial; return defaultMaterial;
} }
@ -104,9 +105,7 @@ namespace Objects.Converter.Unity
); );
} }
else if (shader.name == "Lit") //URP lit else if (shader.name == "Lit") //URP lit
{ { }
ShaderHelpers.SetupMaterialWithBlendMode_URP(mat, true, 1);
}
} }
LoadedAssets.TrySaveObject(renderMaterial, mat); LoadedAssets.TrySaveObject(renderMaterial, mat);

Просмотреть файл

@ -1,12 +1,4 @@
using Objects.Geometry; using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Serialization;
using Mesh = Objects.Geometry.Mesh;
namespace Objects.Converter.Unity namespace Objects.Converter.Unity
{ {

Просмотреть файл

@ -5,7 +5,6 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Objects.BuiltElements; using Objects.BuiltElements;
using Objects.Organization;
using Objects.Other; using Objects.Other;
using Speckle.ConnectorUnity.Utils; using Speckle.ConnectorUnity.Utils;
using Speckle.ConnectorUnity.NativeCache; using Speckle.ConnectorUnity.NativeCache;
@ -28,11 +27,11 @@ namespace Objects.Converter.Unity
public string Author => "Speckle"; public string Author => "Speckle";
public string WebsiteOrEmail => "https://speckle.systems"; public string WebsiteOrEmail => "https://speckle.systems";
public ProgressReport Report { get; } public ProgressReport Report => throw new NotImplementedException();
public ReceiveMode ReceiveMode { get; set; } public ReceiveMode ReceiveMode { get; set; }
public IEnumerable<string> GetServicedApplications() => public IEnumerable<string> GetServicedApplications() =>
new string[] { HostApplications.Unity.Name }; new[] { HostApplications.Unity.Name };
public AbstractNativeCache LoadedAssets { get; private set; } public AbstractNativeCache LoadedAssets { get; private set; }
@ -238,8 +237,6 @@ namespace Objects.Converter.Unity
// return false; // return false;
case View3D _: case View3D _:
return shouldConvertViews; return shouldConvertViews;
case Model:
return false; //This allows us to traverse older commits pre-collections
case Collection: case Collection:
return true; return true;
case Mesh: case Mesh:

Просмотреть файл

@ -22,103 +22,117 @@ using UnityEngine;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
/// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher /// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher
/// <summary> /// <summary>
/// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for /// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for
/// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling /// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling
/// </summary> /// </summary>
public class Dispatcher : MonoBehaviour { public class Dispatcher : MonoBehaviour
{
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
private static readonly Queue<Action> _executionQueue = new Queue<Action>(); public void Update()
{
lock (_executionQueue)
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
}
public void Update() { /// <summary>
lock(_executionQueue) { /// Locks the queue and adds the IEnumerator to the queue
while (_executionQueue.Count > 0) { /// </summary>
_executionQueue.Dequeue().Invoke(); /// <param name="action">IEnumerator function that will be executed from the main thread.</param>
} public void Enqueue(IEnumerator action)
} {
} lock (_executionQueue)
{
_executionQueue.Enqueue(() =>
{
StartCoroutine(action);
});
}
}
/// <summary> /// <summary>
/// Locks the queue and adds the IEnumerator to the queue /// Locks the queue and adds the Action to the queue
/// </summary> /// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param> /// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action) { public void Enqueue(Action action)
lock (_executionQueue) { {
_executionQueue.Enqueue (() => { Enqueue(ActionWrapper(action));
StartCoroutine (action); }
});
}
}
/// <summary> /// <summary>
/// Locks the queue and adds the Action to the queue /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary> /// </summary>
/// <param name="action">function that will be executed from the main thread.</param> /// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action) /// <returns>A Task that can be awaited until the action completes</returns>
{ public Task EnqueueAsync(Action action)
Enqueue(ActionWrapper(action)); {
} var tcs = new TaskCompletionSource<bool>();
/// <summary> void WrappedAction()
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes {
/// </summary> try
/// <param name="action">function that will be executed from the main thread.</param> {
/// <returns>A Task that can be awaited until the action completes</returns> action();
public Task EnqueueAsync(Action action) tcs.TrySetResult(true);
{ }
var tcs = new TaskCompletionSource<bool>(); catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
void WrappedAction() { Enqueue(ActionWrapper(WrappedAction));
try return tcs.Task;
{ }
action();
tcs.TrySetResult(true);
} catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
Enqueue(ActionWrapper(WrappedAction)); IEnumerator ActionWrapper(Action a)
return tcs.Task; {
} a();
yield return null;
}
private static Dispatcher _instance = null;
IEnumerator ActionWrapper(Action a) public static bool Exists()
{ {
a(); return _instance != null;
yield return null; }
}
public static Dispatcher Instance()
{
if (!Exists())
{
throw new Exception(
"Could not find the Dispatcher object. Please ensure you have added a Dispatcher object with this script to your scene."
);
}
return _instance;
}
private static Dispatcher _instance = null; void Awake()
{
Setup.Init(
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
HostApplications.Unity.Slug
);
public static bool Exists() { if (_instance == null)
return _instance != null; {
} _instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
public static Dispatcher Instance() { void OnDestroy()
if (!Exists ()) { {
throw new Exception ("Could not find the Dispatcher object. Please ensure you have added a Dispatcher object with this script to your scene."); _instance = null;
} }
return _instance; }
}
void Awake() {
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
if (_instance == null) {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
void OnDestroy() {
_instance = null;
}
}
} }

Просмотреть файл

@ -3,7 +3,6 @@ using Speckle.Core.Kits;
namespace Speckle.ConnectorUnity.Factories namespace Speckle.ConnectorUnity.Factories
{ {
public static class ConverterFactory public static class ConverterFactory
{ {
public static ISpeckleConverter GetDefaultConverter() public static ISpeckleConverter GetDefaultConverter()

Просмотреть файл

@ -14,9 +14,12 @@ namespace Speckle.ConnectorUnity.NativeCache
[ExecuteAlways] [ExecuteAlways]
public abstract class AbstractNativeCache : ScriptableObject public abstract class AbstractNativeCache : ScriptableObject
{ {
protected bool isWriting = false; protected bool isWriting = false;
public abstract bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : Object; public abstract bool TryGetObject<T>(
Base speckleObject,
[NotNullWhen(true)] out T? nativeObject
)
where T : Object;
public abstract bool TrySaveObject(Base speckleObject, Object nativeObject); public abstract bool TrySaveObject(Base speckleObject, Object nativeObject);
@ -41,7 +44,6 @@ namespace Speckle.ConnectorUnity.NativeCache
{ {
FinishWrite(); FinishWrite();
} }
} }
public static class AssetHelpers public static class AssetHelpers
@ -54,7 +56,7 @@ namespace Speckle.ConnectorUnity.NativeCache
{ {
return string.Format(format, path, "Geometry"); return string.Format(format, path, "Geometry");
} }
if (nativeType == typeof(Material)) if (nativeType == typeof(Material))
{ {
return string.Format(format, path, "Materials"); return string.Format(format, path, "Materials");
} }
@ -65,8 +67,9 @@ namespace Speckle.ConnectorUnity.NativeCache
return null; return null;
} }
private static readonly HashSet<char> InvalidChars = Path.GetInvalidFileNameChars()
.ToHashSet();
private static readonly HashSet<char> InvalidChars = Path.GetInvalidFileNameChars().ToHashSet();
public static string GetAssetName(Base speckleObject, Type nativeType) public static string GetAssetName(Base speckleObject, Type nativeType)
{ {
string suffix = GetAssetSuffix(nativeType); string suffix = GetAssetSuffix(nativeType);
@ -78,15 +81,18 @@ namespace Speckle.ConnectorUnity.NativeCache
public static string GetAssetSuffix(Type nativeType) public static string GetAssetSuffix(Type nativeType)
{ {
if (nativeType == typeof(Material)) return ".mat"; if (nativeType == typeof(Material))
if (nativeType == typeof(GameObject)) return ".prefab"; return ".mat";
if (nativeType == typeof(GameObject))
return ".prefab";
return ".asset"; return ".asset";
} }
[Obsolete("use " + nameof(CoreUtils.GenerateObjectName))] [Obsolete("use " + nameof(CoreUtils.GenerateObjectName))]
public static string GetObjectName(Base speckleObject) public static string GetObjectName(Base speckleObject)
{ {
string objectName = speckleObject["name"] as string ?? speckleObject.speckle_type.Split(':').Last(); string objectName =
speckleObject["name"] as string ?? speckleObject.speckle_type.Split(':').Last();
return $"{objectName} - {speckleObject.id}"; return $"{objectName} - {speckleObject.id}";
} }
} }

Просмотреть файл

@ -11,11 +11,13 @@ namespace Speckle.ConnectorUnity.NativeCache
[SerializeField, SerializeReference] [SerializeField, SerializeReference]
public List<AbstractNativeCache> nativeCaches; public List<AbstractNativeCache> nativeCaches;
public override bool TryGetObject<T>(Base speckleObject, out T nativeObject) where T : class public override bool TryGetObject<T>(Base speckleObject, out T nativeObject)
where T : class
{ {
foreach (var c in nativeCaches) foreach (var c in nativeCaches)
{ {
if (c.TryGetObject(speckleObject, out nativeObject)) return true; if (c.TryGetObject(speckleObject, out nativeObject))
return true;
} }
nativeObject = null; nativeObject = null;
return false; return false;
@ -27,7 +29,8 @@ namespace Speckle.ConnectorUnity.NativeCache
foreach (var c in nativeCaches) foreach (var c in nativeCaches)
{ {
hasSavedSomewhere = hasSavedSomewhere || c.TrySaveObject(speckleObject, nativeObject); hasSavedSomewhere =
hasSavedSomewhere || c.TrySaveObject(speckleObject, nativeObject);
} }
return hasSavedSomewhere; return hasSavedSomewhere;
@ -57,6 +60,5 @@ namespace Speckle.ConnectorUnity.NativeCache
} }
base.FinishWrite(); base.FinishWrite();
} }
} }
} }

Просмотреть файл

@ -6,15 +6,20 @@ using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache namespace Speckle.ConnectorUnity.NativeCache
{ {
#nullable enable #nullable enable
/// <summary> /// <summary>
/// In memory native object cache /// In memory native object cache
/// </summary> /// </summary>
public sealed class MemoryNativeCache : AbstractNativeCache public sealed class MemoryNativeCache : AbstractNativeCache
{ {
public IDictionary<string, List<Object>> LoadedAssets { get; set; } = new Dictionary<string, List<Object>>(); public IDictionary<string, List<Object>> LoadedAssets { get; set; } =
new Dictionary<string, List<Object>>();
public override bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class public override bool TryGetObject<T>(
Base speckleObject,
[NotNullWhen(true)] out T? nativeObject
)
where T : class
{ {
if (TryGetObject(speckleObject, out List<Object>? e)) if (TryGetObject(speckleObject, out List<Object>? e))
{ {
@ -26,20 +31,23 @@ namespace Speckle.ConnectorUnity.NativeCache
return false; return false;
} }
public bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out List<Object>? nativeObject) public bool TryGetObject(
Base speckleObject,
[NotNullWhen(true)] out List<Object>? nativeObject
)
{ {
return LoadedAssets.TryGetValue(speckleObject.id, out nativeObject); return LoadedAssets.TryGetValue(speckleObject.id, out nativeObject);
} }
public override bool TrySaveObject(Base speckleObject, Object nativeObject) public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{ {
if (LoadedAssets.ContainsKey(speckleObject.id)) if (LoadedAssets.TryGetValue(speckleObject.id, out List<Object>? assets))
{ {
LoadedAssets[speckleObject.id].Add(nativeObject); assets.Add(nativeObject);
return true; return true;
} }
LoadedAssets.Add(speckleObject.id, new List<Object>{nativeObject}); LoadedAssets.Add(speckleObject.id, new List<Object> { nativeObject });
return true; return true;
} }
} }

Просмотреть файл

@ -5,7 +5,7 @@ using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache namespace Speckle.ConnectorUnity.NativeCache
{ {
#nullable enable #nullable enable
/// <summary> /// <summary>
/// Loads existing assets from <see cref="Resources"/> /// Loads existing assets from <see cref="Resources"/>
/// by friendly id (see <see cref="AssetHelpers.GetAssetName"/>) /// by friendly id (see <see cref="AssetHelpers.GetAssetName"/>)
@ -15,15 +15,21 @@ namespace Speckle.ConnectorUnity.NativeCache
{ {
public bool matchByName = true; public bool matchByName = true;
public override bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class public override bool TryGetObject<T>(
Base speckleObject,
[NotNullWhen(true)] out T? nativeObject
)
where T : class
{ {
if (matchByName) if (matchByName)
{ {
string? speckleName = speckleObject["name"] as string ?? speckleObject["Name"] as string; string? speckleName =
speckleObject["name"] as string ?? speckleObject["Name"] as string;
if (!string.IsNullOrWhiteSpace(speckleName)) if (!string.IsNullOrWhiteSpace(speckleName))
{ {
nativeObject = Resources.Load<T>(speckleName); nativeObject = Resources.Load<T>(speckleName);
if (nativeObject != null) return true; if (nativeObject != null)
return true;
} }
} }

Просмотреть файл

@ -8,11 +8,12 @@ using Speckle.ConnectorUnity.NativeCache.Editor;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
#nullable enable #nullable enable
public static class NativeCacheFactory public static class NativeCacheFactory
{ {
public static List<AbstractNativeCache> GetDefaultNativeCacheSetup(
public static List<AbstractNativeCache> GetDefaultNativeCacheSetup(bool generateAssets = false) bool generateAssets = false
)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
if (generateAssets) if (generateAssets)
@ -21,7 +22,6 @@ namespace Speckle.ConnectorUnity
} }
#endif #endif
return GetStandaloneCacheSetup(); return GetStandaloneCacheSetup();
} }
public static List<AbstractNativeCache> GetStandaloneCacheSetup() public static List<AbstractNativeCache> GetStandaloneCacheSetup()
@ -46,4 +46,3 @@ namespace Speckle.ConnectorUnity
#endif #endif
} }
} }

Просмотреть файл

@ -8,35 +8,35 @@ using Speckle.Core.Logging;
namespace Speckle.ConnectorUnity namespace Speckle.ConnectorUnity
{ {
public static class Streams public static class Streams
{
public static async Task<List<Stream>> List(int limit = 10)
{ {
var account = AccountManager.GetDefaultAccount(); public static async Task<List<Stream>> List(int limit = 10)
if (account == null) {
return new List<Stream>(); var account = AccountManager.GetDefaultAccount();
var client = new Client(account); if (account == null)
return new List<Stream>();
var client = new Client(account);
var res = await client.StreamsGet(limit); var res = await client.StreamsGet(limit);
return res; return res;
}
public static async Task<Stream> Get(string streamId, int limit = 10)
{
var account = AccountManager.GetDefaultAccount();
if (account == null)
return null;
var client = new Client(account);
var res = await client.StreamGet(streamId, limit);
if (res.branches.items != null)
{
res.branches.items.Reverse();
}
return res;
}
} }
public static async Task<Stream> Get(string streamId, int limit = 10)
{
var account = AccountManager.GetDefaultAccount();
if (account == null)
return null;
var client = new Client(account);
var res = await client.StreamGet(streamId, limit);
if (res.branches.items != null)
{
res.branches.items.Reverse();
}
return res;
}
}
} }

Просмотреть файл

@ -9,28 +9,31 @@ namespace Speckle.ConnectorUnity.Utils
{ {
public static void SetupInit() public static void SetupInit()
{ {
Setup.Init(HostApplications.Unity.GetVersion(GetHostAppVersion()), HostApplications.Unity.Slug); Setup.Init(
HostApplications.Unity.GetVersion(GetHostAppVersion()),
HostApplications.Unity.Slug
);
} }
public static HostAppVersion GetHostAppVersion() public static HostAppVersion GetHostAppVersion()
{ {
#if UNITY_2019 #if UNITY_2019
return HostAppVersion.v2019; return HostAppVersion.v2019;
#elif UNITY_2020 #elif UNITY_2020
return HostAppVersion.v2020; return HostAppVersion.v2020;
#elif UNITY_2021 #elif UNITY_2021
return HostAppVersion.v2021; return HostAppVersion.v2021;
#elif UNITY_2022 #elif UNITY_2022
return HostAppVersion.v2022; return HostAppVersion.v2022;
#elif UNITY_2023 #elif UNITY_2023
return HostAppVersion.v2023; return HostAppVersion.v2023;
#elif UNITY_2024 #elif UNITY_2024
return HostAppVersion.v2024; return HostAppVersion.v2024;
#elif UNITY_2025 #elif UNITY_2025
return HostAppVersion.v2025; return HostAppVersion.v2025;
#else #else
return HostAppVersion.v; return HostAppVersion.v;
#endif #endif
} }
public const string ObjectNameSeparator = " -- "; public const string ObjectNameSeparator = " -- ";
@ -39,7 +42,8 @@ namespace Speckle.ConnectorUnity.Utils
/// <returns>A human-readable Object name unique to the given <paramref name="speckleObject"/></returns> /// <returns>A human-readable Object name unique to the given <paramref name="speckleObject"/></returns>
public static string GenerateObjectName(Base speckleObject) public static string GenerateObjectName(Base speckleObject)
{ {
var prefix = GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject); var prefix =
GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject);
return $"{prefix}{ObjectNameSeparator}{speckleObject.id}"; return $"{prefix}{ObjectNameSeparator}{speckleObject.id}";
} }
@ -56,6 +60,5 @@ namespace Speckle.ConnectorUnity.Utils
{ {
return speckleObject.speckle_type.Split(':')[^1]; return speckleObject.speckle_type.Split(':')[^1];
} }
} }
} }

Просмотреть файл

@ -16,9 +16,13 @@ namespace Speckle.ConnectorUnity.Utils
/// <param name="propertyName"></param> /// <param name="propertyName"></param>
/// <param name="value"></param> /// <param name="value"></param>
#pragma warning disable CS0618 #pragma warning disable CS0618
public static void SetDetachedPropertyChecked(this Base speckleObject, string propertyName, object? value) public static void SetDetachedPropertyChecked(
this Base speckleObject,
string propertyName,
object? value
)
{ {
if(speckleObject.GetInstanceMembersNames().Any(name => name == propertyName)) if (speckleObject.GetInstanceMembersNames().Any(name => name == propertyName))
speckleObject[propertyName] = value; speckleObject[propertyName] = value;
else else
speckleObject[$"@{propertyName}"] = value; speckleObject[$"@{propertyName}"] = value;

Просмотреть файл

@ -108,14 +108,5 @@ namespace Speckle.ConnectorUnity.Utils
} }
} }
public static void SetupMaterialWithBlendMode_URP(
Material material,
bool transparent,
int blendMode
)
{
material.SetFloat("__surface", 1);
material.SetFloat("__blend", blendMode);
}
} }
} }

Просмотреть файл

@ -4,7 +4,6 @@ using System.Collections;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.Rendering;
namespace Speckle.ConnectorUnity.Utils namespace Speckle.ConnectorUnity.Utils
{ {

Просмотреть файл

@ -17,8 +17,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
get get
{ {
Account? account = Selected; Account? account = Selected;
if (account == null) return client = null; if (account == null)
if (client == null || !client.Account.Equals(account)) return client = new Client(account); return client = null;
if (client == null || !client.Account.Equals(account))
return client = new Client(account);
return client; return client;
} }
} }
@ -31,10 +33,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
try try
{ {
accounts = AccountManager.GetAccounts().ToArray(); accounts = AccountManager.GetAccounts().ToArray();
if(accounts.Length == 0) if (accounts.Length == 0)
Debug.LogWarning("No Accounts found, please login in Manager"); Debug.LogWarning("No Accounts found, please login in Manager");
} }
catch(Exception e) catch (Exception e)
{ {
accounts = Array.Empty<Account>(); accounts = Array.Empty<Account>();
Debug.LogWarning($"Unable to refresh {this}\n{e}"); Debug.LogWarning($"Unable to refresh {this}\n{e}");

Просмотреть файл

@ -9,9 +9,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
[Serializable] [Serializable]
public sealed class BranchSelection : OptionSelection<Branch> public sealed class BranchSelection : OptionSelection<Branch>
{ {
[field: SerializeField, Range(1,100), Tooltip("Number of branches to request")] [field: SerializeField, Range(1, 100), Tooltip("Number of branches to request")]
public int BranchesLimit { get; set; } = 100; public int BranchesLimit { get; set; } = 100;
[field: SerializeField, Range(1,100), Tooltip("Number of commits to request")]
[field: SerializeField, Range(1, 100), Tooltip("Number of commits to request")]
public int CommitsLimit { get; set; } = 25; public int CommitsLimit { get; set; } = 25;
[field: SerializeReference] [field: SerializeReference]
@ -34,13 +35,17 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public override void RefreshOptions() public override void RefreshOptions()
{ {
Stream? stream = StreamSelection.Selected; Stream? stream = StreamSelection.Selected;
if (stream == null) return; if (stream == null)
return;
IList<Branch> branches; IList<Branch> branches;
try try
{ {
branches = Client!.StreamGetBranches(stream.id, BranchesLimit, CommitsLimit).GetAwaiter().GetResult(); branches = Client!
.StreamGetBranches(stream.id, BranchesLimit, CommitsLimit)
.GetAwaiter()
.GetResult();
} }
catch(Exception e) catch (Exception e)
{ {
Debug.LogWarning($"Unable to refresh {this}\n{e}"); Debug.LogWarning($"Unable to refresh {this}\n{e}");
branches = Array.Empty<Branch>(); branches = Array.Empty<Branch>();

Просмотреть файл

@ -6,11 +6,9 @@ using UnityEngine;
#nullable enable #nullable enable
namespace Speckle.ConnectorUnity.Wrappers.Selection namespace Speckle.ConnectorUnity.Wrappers.Selection
{ {
[Serializable] [Serializable]
public sealed class CommitSelection : OptionSelection<Commit> public sealed class CommitSelection : OptionSelection<Commit>
{ {
[field: SerializeReference] [field: SerializeReference]
public BranchSelection BranchSelection { get; private set; } public BranchSelection BranchSelection { get; private set; }
@ -20,7 +18,6 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
{ {
BranchSelection = branchSelection; BranchSelection = branchSelection;
Initialise(); Initialise();
} }
public void Initialise() public void Initialise()
@ -28,13 +25,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
BranchSelection.OnSelectionChange = RefreshOptions; BranchSelection.OnSelectionChange = RefreshOptions;
} }
protected override string? KeyFunction(Commit? value) => value?.id; protected override string? KeyFunction(Commit? value) => value?.id;
public override void RefreshOptions() public override void RefreshOptions()
{ {
Branch? branch = BranchSelection!.Selected; Branch? branch = BranchSelection!.Selected;
if (branch == null) return; if (branch == null)
return;
List<Commit> commits = branch.commits.items; List<Commit> commits = branch.commits.items;
GenerateOptions(commits, (_, i) => i == 0); GenerateOptions(commits, (_, i) => i == 0);
} }

Просмотреть файл

@ -18,7 +18,8 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public abstract class OptionSelection<TOption> public abstract class OptionSelection<TOption>
where TOption : class where TOption : class
{ {
[SerializeField] private int selectedIndex = -1; [SerializeField]
private int selectedIndex = -1;
public int SelectedIndex public int SelectedIndex
{ {
@ -34,8 +35,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
{ {
get get
{ {
if (Options is null) return null; if (Options is null)
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null; return null;
if (SelectedIndex < 0 || SelectedIndex >= Options.Length)
return null;
return Options[SelectedIndex]; return Options[SelectedIndex];
} }
} }
@ -52,22 +55,28 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault) protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault)
{ {
List<TOption> optionsToAdd = new (source.Count); List<TOption> optionsToAdd = new(source.Count);
int defaultOption = -1; int defaultOption = -1;
int index = 0; int index = 0;
foreach (TOption? a in source) foreach (TOption? a in source)
{ {
if (a == null) continue; if (a == null)
continue;
optionsToAdd.Add(a); optionsToAdd.Add(a);
if (isDefault(a, index)) defaultOption = index; if (isDefault(a, index))
defaultOption = index;
index++; index++;
} }
TOption? currentSelected = Selected; TOption? currentSelected = Selected;
bool selectionOutOfRange = SelectedIndex < 0 || SelectedIndex >= optionsToAdd.Count; bool selectionOutOfRange = SelectedIndex < 0 || SelectedIndex >= optionsToAdd.Count;
if (selectionOutOfRange if (
|| (currentSelected != null selectionOutOfRange
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex]))) || (
currentSelected != null
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex])
)
)
{ {
selectedIndex = defaultOption; selectedIndex = defaultOption;
} }

Просмотреть файл

@ -10,8 +10,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public sealed class StreamSelection : OptionSelection<Stream> public sealed class StreamSelection : OptionSelection<Stream>
{ {
private const int DEFAULT_REQUEST_LIMIT = 50; private const int DEFAULT_REQUEST_LIMIT = 50;
[field: SerializeField, Range(1,100), Tooltip("Number of streams to request")]
[field: SerializeField, Range(1, 100), Tooltip("Number of streams to request")]
public int StreamsLimit { get; set; } = DEFAULT_REQUEST_LIMIT; public int StreamsLimit { get; set; } = DEFAULT_REQUEST_LIMIT;
[field: SerializeReference] [field: SerializeReference]
public AccountSelection AccountSelection { get; private set; } public AccountSelection AccountSelection { get; private set; }
@ -20,6 +22,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
AccountSelection = accountSelection; AccountSelection = accountSelection;
Initialise(); Initialise();
} }
public void Initialise() public void Initialise()
{ {
AccountSelection.OnSelectionChange = RefreshOptions; AccountSelection.OnSelectionChange = RefreshOptions;
@ -28,15 +31,17 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public override Client? Client => AccountSelection.Client; public override Client? Client => AccountSelection.Client;
protected override string? KeyFunction(Stream? value) => value?.id; protected override string? KeyFunction(Stream? value) => value?.id;
public override void RefreshOptions() public override void RefreshOptions()
{ {
if (Client == null) return; if (Client == null)
return;
IList<Stream> streams; IList<Stream> streams;
try try
{ {
streams = Client.StreamsGet(StreamsLimit).GetAwaiter().GetResult(); streams = Client.StreamsGet(StreamsLimit).GetAwaiter().GetResult();
} }
catch(Exception e) catch (Exception e)
{ {
Debug.LogWarning($"Unable to refresh {this}\n{e}"); Debug.LogWarning($"Unable to refresh {this}\n{e}");
streams = Array.Empty<Stream>(); streams = Array.Empty<Stream>();

Просмотреть файл

@ -17,7 +17,6 @@ namespace Speckle.ConnectorUnity.Wrappers
[Serializable, DisallowMultipleComponent] [Serializable, DisallowMultipleComponent]
public class SpeckleProperties : MonoBehaviour, ISerializationCallbackReceiver public class SpeckleProperties : MonoBehaviour, ISerializationCallbackReceiver
{ {
[SerializeField, HideInInspector] [SerializeField, HideInInspector]
private string _serializedData = ""; private string _serializedData = "";
@ -31,7 +30,7 @@ namespace Speckle.ConnectorUnity.Wrappers
get => _data; get => _data;
set set
{ {
((ICollection<KeyValuePair<string, object>>) _data).Clear(); ((ICollection<KeyValuePair<string, object>>)_data).Clear();
foreach (var kvp in value) foreach (var kvp in value)
{ {
@ -43,18 +42,17 @@ namespace Speckle.ConnectorUnity.Wrappers
[SerializeField, HideInInspector] [SerializeField, HideInInspector]
private string _serializedSpeckleType; private string _serializedSpeckleType;
private Type _speckleType = typeof(Base); private Type _speckleType = typeof(Base);
public Type SpeckleType { public Type SpeckleType
{
get => _speckleType ??= typeof(Base); get => _speckleType ??= typeof(Base);
set set
{ {
Debug.Assert(typeof(Base).IsAssignableFrom(value)); Debug.Assert(typeof(Base).IsAssignableFrom(value));
Debug.Assert(!value.IsAbstract); Debug.Assert(!value.IsAbstract);
_speckleType = value; _speckleType = value;
_hasChanged = true; _hasChanged = true;
} }
} }
public SpeckleProperties() public SpeckleProperties()
@ -72,7 +70,8 @@ namespace Speckle.ConnectorUnity.Wrappers
public void OnBeforeSerialize() public void OnBeforeSerialize()
{ {
if (!_hasChanged) return; if (!_hasChanged)
return;
_serializedData = Operations.Serialize(new SpeckleData(Data)); _serializedData = Operations.Serialize(new SpeckleData(Data));
_hasChanged = false; _hasChanged = false;