1 Commits

Author SHA1 Message Date
Jedd Morgan f406cb8229 wip update for the sample project to use new ui components 2023-06-26 22:13:12 +01:00
95 changed files with 6200 additions and 5011 deletions
@@ -1,62 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Objects.Converter.Unity;
using Speckle.ConnectorUnity.Utils;
using Speckle.ConnectorUnity.Wrappers;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Models;
using Speckle.Core.Transports;
using UnityEngine;
/// <summary>
/// Example script for grabbing speckle properties for a specific object "on-the-fly"
/// </summary>
/// <remarks>
/// see discussion https://speckle.community/t/reloading-assemblies-takes-too-long/6708
/// </remarks>
[AddComponentMenu("Speckle/Extras/" + nameof(AttachSpecklePropertiesExample))]
public class AttachSpecklePropertiesExample : MonoBehaviour
{
public string streamId;
public string objectId;
public virtual void Start()
{
Client speckleClient = new(AccountManager.GetDefaultAccount()!);
StartCoroutine(AttachSpeckleProperties(speckleClient, streamId, objectId));
}
public IEnumerator AttachSpeckleProperties(
Client speckleClient,
string streamId,
string objectId
)
{
//Fetch the object from Speckle
ServerTransport remoteTransport = new(speckleClient.Account, streamId);
Utils.WaitForTask<Base> operation =
new(async () => await Operations.Receive(objectId, remoteTransport));
//yield until task completes
yield return operation;
Base speckleObject = operation.Result;
//Do something with the properties. e.g. attach SpeckleProperties component
DoSomething(speckleObject);
}
protected virtual void DoSomething(Base speckleObject)
{
//GetProperties will filter "useful" properties
Dictionary<string, object> properties = ConverterUnity.GetProperties(
speckleObject,
typeof(SpeckleObject)
);
var sd = this.gameObject.AddComponent<SpeckleProperties>();
sd.Data = properties;
sd.SpeckleType = speckleObject.GetType();
}
}
+27 -23
View File
@@ -1,10 +1,10 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using Speckle.ConnectorUnity;
using Speckle.ConnectorUnity.Components;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Models;
using Speckle.Core.Transports;
using UnityEngine;
@@ -12,56 +12,60 @@ using UnityEngine;
[RequireComponent(typeof(RecursiveConverter))]
public class ManualReceive : MonoBehaviour
{
public string authToken;
public string serverUrl;
public string streamId,
objectId;
public string streamId, objectId;
private RecursiveConverter receiver;
void Awake()
{
receiver = GetComponent<RecursiveConverter>();
}
IEnumerator Start()
{
Debug.developerConsoleVisible = true;
if (Time.timeSinceLevelLoad > 20)
yield return null;
if(Time.timeSinceLevelLoad > 20) yield return null;
Receive();
}
[ContextMenu(nameof(Receive))]
public void Receive()
{
var account = new Account()
{
token = authToken,
serverInfo = new ServerInfo() { url = serverUrl },
serverInfo = new ServerInfo() {url = serverUrl},
};
Task.Run(async () =>
{
using ServerTransport transport = new(account, streamId);
MemoryTransport localTransport = new();
Base speckleObject = await Operations.Receive(
var transport = new ServerTransport(account, streamId);
var localTransport = new MemoryTransport();
var @base = await Operations.Receive(
objectId,
remoteTransport: transport,
localTransport: localTransport
localTransport: localTransport,
onErrorAction: (m, e) => Debug.LogError(m + e),
disposeTransports: true
);
if (@base == null) throw new Exception("received data was null!");
Dispatcher.Instance().Enqueue(() =>
{
var parentObject = new GameObject(name);
receiver.RecursivelyConvertToNative_Sync(@base, parentObject.transform);
Dispatcher
.Instance()
.Enqueue(() =>
{
var parentObject = new GameObject(name);
receiver.RecursivelyConvertToNative_Sync(speckleObject, parentObject.transform);
Debug.Log($"Receive {objectId} completed");
});
Debug.Log($"Receive {objectId} completed");
});
});
}
}
+17 -22
View File
@@ -2,7 +2,6 @@ using System.Threading;
using System.Threading.Tasks;
using Speckle.ConnectorUnity.Components;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Models;
using Speckle.Core.Transports;
using UnityEngine;
@@ -19,25 +18,24 @@ namespace Extra
{
[Range(0, 100)]
public int numberOfIterations = 10;
public Vector3 translation = Vector3.forward * 100;
public GameObject objectToSend;
private SpeckleSender sender;
private void Awake()
{
sender = GetComponent<SpeckleSender>();
}
public async Task SendIterations()
{
GameObject go = new GameObject();
for (int i = 0; i < numberOfIterations; i++)
{
Instantiate(objectToSend, translation * i, Quaternion.identity, go.transform);
Base b = sender.Converter.RecursivelyConvertToSpeckle(go, _ => true);
await Send(b, $"{i}");
}
@@ -46,23 +44,20 @@ namespace Extra
private async Task<string> Send(Base data, string branchName)
{
Client client = sender.Account.Client!;
Stream stream = sender.Stream.Selected;
Account selectedAccount = sender.Account.Selected!;
using ServerTransport transport = new(selectedAccount, stream!.id);
string branchId = await client.BranchCreate(
new BranchCreateInput() { streamId = stream.id, name = branchName }
);
return await SpeckleSender.SendDataAsync(
var client = sender.Account.Client;
var stream = sender.Stream.Selected;
ServerTransport transport = new ServerTransport(sender.Account.Selected, stream!.id);
await client.BranchCreate(new BranchCreateInput(){streamId = stream.id, name = branchName});
return await SpeckleSender.SendDataAsync(CancellationToken.None,
remoteTransport: transport,
data,
client,
branchId,
true
);
data: data,
client: client!,
branchName: branchName,
createCommit: true,
onProgressAction: null,
onErrorAction: (m, e) => throw e);
}
}
@@ -73,7 +68,7 @@ namespace Extra
public override async void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Create and send"))
{
await ((PerformanceTestSender)target).SendIterations();
+153
View File
@@ -0,0 +1,153 @@
using System;
using System.Collections;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Speckle.ConnectorUnity.Components;
using Speckle.ConnectorUnity.Utils;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEngine;
[AddComponentMenu("Speckle/Extras/Receive From Url")]
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
public class ReceiveFromURL : MonoBehaviour
{
[Tooltip("Url of your speckle object/commit/branch/stream")]
public string url;
private RecursiveConverter _converter;
#nullable enable
private CancellationTokenSource? _tokenSource;
void Awake()
{
_converter = GetComponent<RecursiveConverter>();
}
[ContextMenu(nameof(Receive))]
public void Receive()
{
StartCoroutine(Receive_Routine());
}
public IEnumerator Receive_Routine()
{
if (IsBusy()) throw new InvalidOperationException("A receive operation has already started");
_tokenSource = new CancellationTokenSource();
try
{
StreamWrapper sw = new(url);
if (!sw.IsValid)
throw new InvalidOperationException("Speckle url input is not a valid speckle stream/branch/commit");
var accountTask = new Utils.WaitForTask<Account>(async () => await GetAccount(sw));
yield return accountTask;
_tokenSource.Token.ThrowIfCancellationRequested();
using Client c = new(accountTask.Result);
var objectIdTask = new Utils.WaitForTask<(string, Commit?)>(async () => await GetObjectID(sw, c));
yield return objectIdTask;
(string objectId, Commit? commit) = objectIdTask.Result;
Debug.Log($"Receiving from {sw.ServerUrl}...");
var receiveTask = new Utils.WaitForTask<Base>(async () => await SpeckleReceiver.ReceiveAsync(
c,
sw.StreamId,
objectId,
commit,
cancellationToken: _tokenSource.Token));
yield return receiveTask;
Debug.Log("Converting to native...");
_converter.RecursivelyConvertToNative_Sync(receiveTask.Result, transform);
}
finally
{
_tokenSource.Dispose();
_tokenSource = null;
}
}
private async Task<(string objectId, Commit? commit)> GetObjectID(StreamWrapper sw, Client client)
{
string objectId;
Commit? commit = null;
//OBJECT URL
if (!string.IsNullOrEmpty(sw.ObjectId))
{
objectId = sw.ObjectId;
}
//COMMIT URL
else if (!string.IsNullOrEmpty(sw.CommitId))
{
commit = await client.CommitGet(sw.StreamId, sw.CommitId).ConfigureAwait(false);
objectId = commit.referencedObject;
}
//BRANCH URL OR STREAM URL
else
{
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
var branch = await client.BranchGet(sw.StreamId, branchName, 1).ConfigureAwait(false);
if (!branch.commits.items.Any())
throw new SpeckleException("The selected branch has no commits.");
commit = branch.commits.items[0];
objectId = branch.commits.items[0].referencedObject;
}
return (objectId, commit);
}
[ContextMenu(nameof(Cancel))]
public void Cancel()
{
if (IsNotBusy()) throw new InvalidOperationException("There are no pending receive operations to cancel");
_tokenSource!.Cancel();
}
[ContextMenu(nameof(Cancel), true)]
public bool IsBusy()
{
return _tokenSource is not null;
}
[ContextMenu(nameof(Receive), true)]
internal bool IsNotBusy() => !IsBusy();
private void OnDisable()
{
_tokenSource?.Cancel();
}
private async Task<Account> GetAccount(StreamWrapper sw)
{
Account account;
try
{
account = await sw.GetAccount().ConfigureAwait(false);
}
catch (SpeckleException e)
{
if (string.IsNullOrEmpty(sw.StreamId))
throw e;
//Fallback to a non authed account
account = new Account
{
token = "",
serverInfo = new ServerInfo { url = sw.ServerUrl },
userInfo = new UserInfo()
};
}
return account;
}
}
+13 -12
View File
@@ -2,8 +2,11 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using Speckle.ConnectorUnity;
using UnityEditor.Experimental;
using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(Sender)), ExecuteAlways]
[Obsolete]
@@ -20,7 +23,7 @@ public class SendChildrenToSpeckle : MonoBehaviour
{
sender = GetComponent<Sender>();
}
[ContextMenu(nameof(Send))]
public void Send()
{
@@ -28,32 +31,30 @@ public class SendChildrenToSpeckle : MonoBehaviour
.Where(t => t != this.transform)
.Select(o => o.gameObject)
.ToImmutableHashSet();
Debug.Log("starting send...");
sender.Send(
streamId,
selected,
null,
branchName,
createCommit,
sender.Send(streamId, selected, null, branchName, createCommit,
onErrorAction: OnError,
onProgressAction: OnProgress,
onDataSentAction: OnSent
);
onDataSentAction: OnSent);
}
private void OnSent(string objectId)
{
Debug.Log($"Data sent {objectId}", this);
}
private void OnError(string message, Exception e)
{
Debug.LogError($"Error while sending {message} \n {e}", this);
}
private void OnProgress(ConcurrentDictionary<string, int> dict)
{
Debug.Log($"progress was made", this);
}
}
+3 -19
View File
@@ -1,20 +1,4 @@
{
"name": "Speckle.Extra",
"rootNamespace": "",
"references": [
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
"GUID:e6adfdc4e436206479f48eafc82f32b5",
"GUID:d274441ecc3eb3f43b093eec1503d681",
"GUID:50d889142fdf9de4b8501c6eaa4b3225",
"GUID:7383cd71541a2aa48a7baf23f74b4d5f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
"name": "Speckle.Extra",
"references":[ "GUID:eed1b8b83e2c0074d9e5de2348e3ff72", "GUID:e6adfdc4e436206479f48eafc82f32b5", "GUID:d274441ecc3eb3f43b093eec1503d681", "GUID:50d889142fdf9de4b8501c6eaa4b3225" ]
}
+200 -224
View File
@@ -1,244 +1,220 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Speckle.Core.Api;
using Speckle.Core.Logging;
using UnityEngine;
using UnityEngine.UI;
using Text = UnityEngine.UI.Text;
namespace Speckle.ConnectorUnity
{
{
[Obsolete]
public class InteractionLogic : MonoBehaviour
public class InteractionLogic : MonoBehaviour
{
private Receiver receiver;
public void InitReceiver(Stream stream, bool autoReceive)
{
private Receiver _receiver;
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
InitRemove();
public void InitReceiver(Stream stream, bool autoReceive)
receiver = gameObject.AddComponent<Receiver>();
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) =>
{
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
InitRemove();
statusText.text = $"Received {go.name}";
MakeButtonsInteractable(true);
receiveProgress.value = 0;
receiveProgress.gameObject.SetActive(false);
_receiver = gameObject.AddComponent<Receiver>();
_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,
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();
});
}
/// <summary>
/// Recursively adds custom components to all children of a GameObject
/// </summary>
/// <param name="go"></param>
private void AddComponents(GameObject go)
AddComponents(go);
},
onTotalChildrenCountKnown: (count) => { receiver.TotalChildrenCount = count; },
onProgressAction: (dict) =>
{
for (var i = 0; i < go.transform.childCount; i++)
{
var child = go.transform.GetChild(i);
//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;
});
});
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);
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);
});
}
streamText.text = $"Stream: {stream.name}\nId: {stream.id} - Auto: {autoReceive}";
btn.onClick.AddListener(() =>
{
statusText.text = "Receiving...";
MakeButtonsInteractable(false);
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);
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);
});
}
}
}
+113 -110
View File
@@ -3,129 +3,132 @@ using System.Collections.Generic;
using UnityEngine;
using Speckle.Core.Credentials;
using System.Linq;
using UnityEngine.Events;
using UnityEngine.UI;
using Stream = Speckle.Core.Api.Stream;
namespace Speckle.ConnectorUnity
{
[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()
{
public Text SelectStreamText;
public Text DetailsStreamText;
public Dropdown StreamSelectionDropdown;
public Button AddReceiverBtn;
public Toggle AutoReceiveToggle;
public Button AddSenderBtn;
public GameObject StreamPanel;
public Canvas StreamsCanvas;
if (SelectStreamText == null || StreamSelectionDropdown == null)
{
Debug.Log("Please set all input fields on _SpeckleExamples");
return;
}
private List<Stream> StreamList = null;
private Stream SelectedStream = null;
private List<GameObject> StreamPanels = new List<GameObject>();
var defaultAccount = AccountManager.GetDefaultAccount();
if (defaultAccount == null)
{
Debug.Log("Please set a default account in SpeckleManager");
return;
}
async void Start()
{
if (SelectStreamText == null || StreamSelectionDropdown == null)
{
Debug.Log("Please set all input fields on _SpeckleExamples");
return;
}
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
var defaultAccount = AccountManager.GetDefaultAccount();
if (defaultAccount == null)
{
Debug.Log("Please set a default account in SpeckleManager");
return;
}
StreamList = await Streams.List(30);
if (!StreamList.Any())
{
Debug.Log("There are no streams in your account, please create one online.");
return;
}
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
StreamSelectionDropdown.options.Clear();
foreach (var stream in StreamList)
{
StreamSelectionDropdown.options.Add(new Dropdown.OptionData(stream.name + " - " + stream.id));
}
StreamList = await Streams.List(30);
if (!StreamList.Any())
{
Debug.Log("There are no streams in your account, please create one online.");
return;
}
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
//trigger ui refresh, maybe there's a better method
StreamSelectionDropdown.value = -1;
StreamSelectionDropdown.value = 0;
StreamSelectionDropdown.options.Clear();
foreach (var stream in StreamList)
{
StreamSelectionDropdown.options.Add(
new Dropdown.OptionData(stream.name + " - " + stream.id)
);
}
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
//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);
}
}
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);
}
}
}
}
File diff suppressed because one or more lines are too long
+11 -7
View File
@@ -1,3 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using NUnit.Framework;
@@ -7,6 +9,7 @@ using Speckle.ConnectorUnity.Wrappers;
using Speckle.Core.Api;
using Speckle.Core.Models;
using UnityEngine;
using UnityEngine.TestTools;
namespace Speckle.ConnectorUnity.Tests
{
@@ -22,7 +25,7 @@ namespace Speckle.ConnectorUnity.Tests
{
return Task.Run(async () => await Helpers.Receive(stream)).Result;
}
[Test, TestCaseSource(nameof(TestCases))]
public void ToNative_Passes(string stream)
{
@@ -34,13 +37,14 @@ namespace Speckle.ConnectorUnity.Tests
Assert.That(results, HasSomeComponent<SpeckleProperties>());
}
private static Constraint HasSomeComponent<T>()
where T : Component
private static Constraint HasSomeComponent<T>() where T : Component
{
return Has.Some.Matches<ConversionResult>(x =>
{
return x.WasSuccessful(out var success, out _) && success.GetComponent<T>();
});
return Has.Some.Matches<ConversionResult>(
x =>
{
return x.WasSuccessful(out var success, out _)
&& success.GetComponent<T>();
});
}
}
}
+6 -6
View File
@@ -1,14 +1,14 @@
{
"dependencies": {
"com.unity.2d.sprite": "1.0.0",
"com.unity.collab-proxy": "2.0.7",
"com.unity.ide.rider": "3.0.24",
"com.unity.ide.visualstudio": "2.0.18",
"com.unity.collab-proxy": "2.0.1",
"com.unity.ide.rider": "3.0.18",
"com.unity.ide.visualstudio": "2.0.17",
"com.unity.ide.vscode": "1.2.5",
"com.unity.test-framework": "1.1.33",
"com.unity.test-framework": "1.1.31",
"com.unity.textmeshpro": "3.0.6",
"com.unity.timeline": "1.6.5",
"com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.4",
"com.unity.timeline": "1.6.4",
"com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.2",
"com.unity.ugui": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
+11 -11
View File
@@ -7,7 +7,7 @@
"dependencies": {}
},
"com.unity.collab-proxy": {
"version": "2.0.7",
"version": "2.0.1",
"depth": 0,
"source": "registry",
"dependencies": {},
@@ -21,7 +21,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.24",
"version": "3.0.18",
"depth": 0,
"source": "registry",
"dependencies": {
@@ -30,7 +30,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.visualstudio": {
"version": "2.0.18",
"version": "2.0.17",
"depth": 0,
"source": "registry",
"dependencies": {
@@ -46,23 +46,23 @@
"url": "https://packages.unity.com"
},
"com.unity.sysroot": {
"version": "2.0.5",
"version": "2.0.3",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.sysroot.linux-x86_64": {
"version": "2.0.4",
"version": "2.0.2",
"depth": 1,
"source": "registry",
"dependencies": {
"com.unity.sysroot": "2.0.5"
"com.unity.sysroot": "2.0.3"
},
"url": "https://packages.unity.com"
},
"com.unity.test-framework": {
"version": "1.1.33",
"version": "1.1.31",
"depth": 0,
"source": "registry",
"dependencies": {
@@ -82,7 +82,7 @@
"url": "https://packages.unity.com"
},
"com.unity.timeline": {
"version": "1.6.5",
"version": "1.6.4",
"depth": 0,
"source": "registry",
"dependencies": {
@@ -94,12 +94,12 @@
"url": "https://packages.unity.com"
},
"com.unity.toolchain.win-x86_64-linux-x86_64": {
"version": "2.0.4",
"version": "2.0.2",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.sysroot": "2.0.5",
"com.unity.sysroot.linux-x86_64": "2.0.4"
"com.unity.sysroot": "2.0.3",
"com.unity.sysroot.linux-x86_64": "2.0.2"
},
"url": "https://packages.unity.com"
},
@@ -1,34 +0,0 @@
#nullable enable
using UnityEditor;
using UnityEngine;
namespace Speckle.ConnectorUnity.Components.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(ReceiveFromURL))]
public class ReceiveFromURLEditor: UnityEditor.Editor
{
public override void OnInspectorGUI()
{
var speckleReceiver = (ReceiveFromURL)target;
DrawDefaultInspector();
bool isBusy = speckleReceiver.IsBusy();
GUI.enabled = !isBusy;
if (GUILayout.Button("Receive!"))
{
speckleReceiver.Receive();
}
GUI.enabled = isBusy;
if (GUILayout.Button("Cancel!"))
{
speckleReceiver.Cancel();
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1f1a8151dcdc40ac8662b102c2f25277
timeCreated: 1687985346
@@ -6,45 +6,35 @@ using Speckle.Core.Models.GraphTraversal;
using UnityEditor;
using UnityEngine;
#nullable enable
namespace Speckle.ConnectorUnity.Components.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(SpeckleReceiver))]
public class SpeckleReceiverEditor : UnityEditor.Editor
{
private SerializedProperty _accountSelection;
private SerializedProperty _streamSelection;
private SerializedProperty _branchSelection;
private SerializedProperty _commitSelection;
#nullable enable
private static bool _generateAssets;
private bool _foldOutStatus = true;
private Texture2D? _previewImage;
private static bool generateAssets = false;
private bool foldOutStatus = true;
private Texture2D? previewImage;
public override async void OnInspectorGUI()
{
var speckleReceiver = (SpeckleReceiver)target;
//Selection
EditorGUILayout.PropertyField(_accountSelection);
EditorGUILayout.PropertyField(_streamSelection, new GUIContent("Project"));
EditorGUILayout.PropertyField(_branchSelection, new GUIContent("Model"));
EditorGUILayout.PropertyField(_commitSelection, new GUIContent("Version"));
DrawDefaultInspector();
//Preview image
{
_foldOutStatus = EditorGUILayout.Foldout(_foldOutStatus, "Preview Image");
if (_foldOutStatus)
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
if (foldOutStatus)
{
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
if (_previewImage != null)
GUI.DrawTexture(rect, _previewImage);
if (previewImage != null) GUI.DrawTexture(rect, previewImage);
}
}
//TODO: Draw events in a collapsed region
//Receive settings
{
bool prev = GUI.enabled;
@@ -52,10 +42,10 @@ namespace Speckle.ConnectorUnity.Components.Editor
//Receive button
bool userRequestedReceive = GUILayout.Button("Receive!");
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", _generateAssets);
if (_generateAssets != selection)
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets);
if (generateAssets != selection)
{
_generateAssets = selection;
generateAssets = selection;
UpdateGenerateAssets();
}
GUI.enabled = prev;
@@ -64,19 +54,15 @@ namespace Speckle.ConnectorUnity.Components.Editor
{
var value = Progress.globalProgress; //NOTE: this may include non-speckle items...
var percent = Math.Max(0, Mathf.Ceil(value * 100));
var rect = EditorGUILayout.GetControlRect(
false,
EditorGUIUtility.singleLineHeight
);
Debug.Log(value);
var rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
EditorGUI.ProgressBar(rect, value, $"{percent}%");
}
else if (userRequestedReceive)
{
var id = Progress.Start(
"Receiving Speckle Model",
"Fetching data from Speckle",
Progress.Options.Sticky
);
"Receiving Speckle data",
"Fetching commit data", Progress.Options.Sticky);
Progress.ShowDetails();
try
@@ -101,33 +87,21 @@ namespace Speckle.ConnectorUnity.Components.Editor
}
}
}
public void OnEnable()
{
Init();
_accountSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleReceiver.Account)}>k__BackingField"
);
_streamSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleReceiver.Stream)}>k__BackingField"
);
_branchSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleReceiver.Branch)}>k__BackingField"
);
_commitSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleReceiver.Commit)}>k__BackingField"
);
}
public void Reset()
{
Init();
}
private void Init()
{
var speckleReceiver = (SpeckleReceiver)target;
var speckleReceiver = (SpeckleReceiver) target;
UpdatePreviewImage();
speckleReceiver.OnCommitSelectionChange.AddListener(_ => UpdatePreviewImage());
UpdateGenerateAssets();
@@ -135,35 +109,31 @@ namespace Speckle.ConnectorUnity.Components.Editor
private void UpdatePreviewImage()
{
_previewImage = null;
((SpeckleReceiver)target).GetPreviewImage(t => _previewImage = t);
previewImage = null;
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t);
}
private async Task ReceiveSelection(int progressId)
{
var speckleReceiver = (SpeckleReceiver)target;
bool shouldCancel = false;
Progress.RegisterCancelCallback(
progressId,
() =>
{
speckleReceiver.Cancel();
shouldCancel = true;
return true;
}
);
Progress.RegisterCancelCallback(progressId, () =>
{
speckleReceiver.Cancel();
shouldCancel = true;
return true;
});
Base commitObject;
try
{
var token = speckleReceiver.BeginOperation();
commitObject = await Task.Run(
async () => await ReceiveCommit(progressId).ConfigureAwait(false),
token
)
.ConfigureAwait(true);
commitObject = await Task.Run(async () => await ReceiveCommit(progressId).ConfigureAwait(false),
token
)
.ConfigureAwait(true);
}
finally
{
@@ -173,42 +143,32 @@ namespace Speckle.ConnectorUnity.Components.Editor
int childrenConverted = 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;
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)
{
Base b = context.Current;
Base b = context.current;
//NOTE: progress wont reach 100% because not all objects are convertable
float progress = (childrenConverted + childrenFailed) / totalChildrenFloat;
if (shouldCancel)
return false;
if (shouldCancel) return false;
shouldCancel = EditorUtility.DisplayCancelableProgressBar(
"Converting To Native...",
$"{b.speckle_type} - {b.id}",
progress
);
progress);
return !shouldCancel;
}
foreach (
var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
commitObject,
speckleReceiver.transform,
BeforeConvert
)
)
foreach (var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
commitObject,
speckleReceiver.transform,
BeforeConvert))
{
Base speckleObject = conversionResult.SpeckleObject;
if (conversionResult.WasSuccessful(out _, out var ex))
@@ -220,45 +180,31 @@ namespace Speckle.ConnectorUnity.Components.Editor
childrenFailed++;
Debug.LogWarning(
$"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";
if (childrenFailed != 0)
resultString += $", {childrenFailed} objects failed to convert!";
Debug.Log(
shouldCancel
if (childrenFailed != 0) resultString += $", {childrenFailed} objects failed to convert!";
Debug.Log(shouldCancel
? $"Stopped converting to native: The operation has been cancelled - {resultString}\n "
: $"Finished converting to native.\n{resultString}",
speckleReceiver
);
speckleReceiver);
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()
{
var speckleReceiver = (SpeckleReceiver)target;
speckleReceiver.Converter.AssetCache.nativeCaches =
NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
var speckleReceiver = (SpeckleReceiver) target;
speckleReceiver.Converter.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
}
private async Task<Base> ReceiveCommit(int progressId)
@@ -266,19 +212,9 @@ namespace Speckle.ConnectorUnity.Components.Editor
var speckleReceiver = (SpeckleReceiver)target;
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
int transport = Progress.Start(
$"Downloading data from {serverLogName}",
"Waiting...",
Progress.Options.Sticky,
progressId
);
int deserialize = Progress.Start(
"Deserializing data",
"Waiting...",
Progress.Options.Sticky,
progressId
);
int transport = Progress.Start($"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);
var totalObjectCount = 1;
@@ -287,7 +223,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
totalObjectCount = count;
Progress.Report(progressId, 0, totalObjectCount, "Receiving objects");
}
void OnProgress(ConcurrentDictionary<string, int> dict)
{
bool r = dict.TryGetValue("RemoteTransport", out int rtProgress);
@@ -295,24 +231,14 @@ namespace Speckle.ConnectorUnity.Components.Editor
if (r || l)
{
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))
{
tsProgress--; //The root object isn't included, so we add an extra 1
Progress.Report(
deserialize,
tsProgress,
totalObjectCount,
$"{tsProgress}/{totalObjectCount}"
);
Progress.Report(progressId, tsProgress, totalObjectCount);
Progress.Report(deserialize,tsProgress, totalObjectCount, $"{tsProgress}/{totalObjectCount}");
Progress.Report(progressId,tsProgress, totalObjectCount);
}
}
@@ -321,19 +247,18 @@ namespace Speckle.ConnectorUnity.Components.Editor
{
speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown);
speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress);
commitObject = await speckleReceiver
.ReceiveAsync(speckleReceiver.CancellationToken)
commitObject = await speckleReceiver.ReceiveAsync(speckleReceiver.CancellationToken)
.ConfigureAwait(false);
Progress.Finish(transport);
Progress.Finish(deserialize);
}
catch (OperationCanceledException)
catch(OperationCanceledException)
{
Progress.Finish(transport, Progress.Status.Canceled);
Progress.Finish(deserialize, Progress.Status.Canceled);
throw;
}
catch (Exception)
catch(Exception)
{
Progress.Finish(transport, Progress.Status.Failed);
Progress.Finish(deserialize, Progress.Status.Failed);
@@ -352,7 +277,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
static void CreateCustomGameObject(MenuCommand menuCommand)
{
// Create a custom game object
GameObject go = new("Speckle Connector");
GameObject go = new GameObject("Speckle Connector");
// Ensure it gets reparented if this was a context click (otherwise does nothing)
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
// Register the creation in the undo system
@@ -361,15 +286,13 @@ namespace Speckle.ConnectorUnity.Components.Editor
go.AddComponent<RecursiveConverter>();
go.AddComponent<SpeckleReceiver>();
go.AddComponent<ReceiveFromURL>();
go.AddComponent<SpeckleSender>();
#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);
#endif
}
}
}
@@ -1,4 +1,3 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -10,62 +9,39 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using Component = UnityEngine.Component;
#nullable enable
namespace Speckle.ConnectorUnity.Components.Editor
{
public enum SelectionFilter
{
[Tooltip("Convert all children of this GameObject")]
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,
[InspectorName("All (excl. disabled)")]
[Tooltip("Convert all GameObjects (excluding disabled) in the active scene")]
Enabled,
[Tooltip("Convert all GameObjects (including disabled) in the active scene")]
[InspectorName("All (incl. disabled)")]
All,
}
[CustomEditor(typeof(SpeckleSender))]
[CanEditMultipleObjects]
public class SpeckleSendEditor : UnityEditor.Editor
{
private SerializedProperty _accountSelection;
private SerializedProperty _streamSelection;
private SerializedProperty _branchSelection;
#nullable enable
private SelectionFilter _selectedFilter = SelectionFilter.Children;
public void OnEnable()
{
_accountSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleSender.Account)}>k__BackingField"
);
_streamSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleSender.Stream)}>k__BackingField"
);
_branchSelection = serializedObject.FindProperty(
$"<{nameof(SpeckleSender.Branch)}>k__BackingField"
);
}
private SelectionFilter selectedFilter = SelectionFilter.Children;
public override async void OnInspectorGUI()
{
//Selection
EditorGUILayout.PropertyField(_accountSelection);
EditorGUILayout.PropertyField(_streamSelection, new GUIContent("Project"));
EditorGUILayout.PropertyField(_branchSelection, new GUIContent("Model"));
//Draw events in a collapsed region
DrawDefaultInspector();
bool shouldSend = GUILayout.Button("Send!");
_selectedFilter = (SelectionFilter)
EditorGUILayout.EnumPopup("Selection", _selectedFilter);
selectedFilter = (SelectionFilter)EditorGUILayout.EnumPopup("Selection", selectedFilter);
if (shouldSend)
{
await ConvertAndSend();
@@ -74,34 +50,30 @@ namespace Speckle.ConnectorUnity.Components.Editor
public async Task<string?> ConvertAndSend()
{
var speckleSender = (SpeckleSender)target;
var speckleSender = (SpeckleSender) target;
if (!speckleSender.GetSelection(out _, out _, out _, out string? error))
{
Debug.LogWarning($"Not ready to send: {error}", speckleSender);
return null;
}
RecursiveConverter converter = speckleSender.Converter;
Base data = _selectedFilter switch
Base data = selectedFilter switch
{
SelectionFilter.All => ConvertAll(converter),
SelectionFilter.Enabled => ConvertEnabled(converter),
SelectionFilter.Children => ConvertChildren(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?
if (data["@objects"] is IList l && l.Count == 0)
{
Debug.LogWarning("Nothing to send", speckleSender);
Debug.LogWarning($"Nothing to send", speckleSender);
return null;
}
}
return await speckleSender.SendDataAsync(data, true);
}
@@ -109,36 +81,31 @@ namespace Speckle.ConnectorUnity.Components.Editor
private Base ConvertChildren(RecursiveConverter converter)
{
return converter.RecursivelyConvertToSpeckle(
new[] { ((Component)target).gameObject },
_ => true
);
new []{((Component)target).gameObject},
_ => true);
}
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(
SceneManager.GetActiveScene().GetRootGameObjects(),
go => selection.Contains(go)
);
SceneManager.GetActiveScene().GetRootGameObjects(),
go => selection.Contains(go));
}
private Base ConvertAll(RecursiveConverter converter)
{
return converter.RecursivelyConvertToSpeckle(
SceneManager.GetActiveScene().GetRootGameObjects(),
_ => true
);
SceneManager.GetActiveScene().GetRootGameObjects(),
_ => true);
}
private Base ConvertEnabled(RecursiveConverter converter)
{
return converter.RecursivelyConvertToSpeckle(
SceneManager.GetActiveScene().GetRootGameObjects(),
go => go.activeInHierarchy
);
SceneManager.GetActiveScene().GetRootGameObjects(),
go => go.activeInHierarchy);
}
}
}
@@ -20,10 +20,10 @@ namespace Speckle.ConnectorUnity.Components.Editor
public class StreamManagerEditor : UnityEditor.Editor
{
private bool _foldOutAccount;
private int _totalChildrenCount;
private int _totalChildrenCount = 0;
private StreamManager _streamManager;
private static bool _generateAssets;
private static bool generateAssets;
public int StreamsLimit { get; set; } = 30;
public int BranchesLimit { get; set; } = 75;
@@ -98,6 +98,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
private List<Branch> Branches
{
get => _streamManager.Branches;
set => _streamManager.Branches = value;
}
@@ -141,11 +142,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
SelectedStream = Streams[i];
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())
{
SelectedBranchIndex = 0;
@@ -158,6 +155,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorUtility.ClearProgressBar();
}
private async Task Receive()
{
var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
@@ -165,82 +163,53 @@ namespace Speckle.ConnectorUnity.Components.Editor
try
{
Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[
SelectedCommitIndex
];
Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex];
// Receive Speckle Objects
var @base = await Operations.Receive(
selectedCommit.referencedObject,
remoteTransport: transport,
onProgressAction: dict =>
{
EditorApplication.delayCall += () =>
UnityEditor.EditorApplication.delayCall += () =>
{
EditorUtility.DisplayProgressBar(
$"Receiving data from {transport.BaseUri}...",
"",
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount)
);
EditorUtility.DisplayProgressBar($"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();
Analytics.TrackEvent(
SelectedAccount,
Analytics.Events.Receive,
new Dictionary<string, object>()
{
{ "mode", nameof(StreamManagerEditor) },
{
"sourceHostApp",
HostApplications
.GetHostAppFromString(selectedCommit.sourceApplication)
.Slug
},
{ "sourceHostAppVersion", selectedCommit.sourceApplication ?? "" },
{ "hostPlatform", Application.platform.ToString() },
{ "isMultiplayer", selectedCommit.authorId != SelectedAccount.userInfo.id },
}
);
Analytics.TrackEvent(SelectedAccount, Analytics.Events.Receive, new Dictionary<string, object>()
{
{"mode", nameof(StreamManagerEditor)},
{"sourceHostApp", HostApplications.GetHostAppFromString(selectedCommit.sourceApplication).Slug},
{"sourceHostAppVersion", selectedCommit.sourceApplication ?? ""},
{"hostPlatform", Application.platform.ToString()},
{"isMultiplayer", selectedCommit.authorId != SelectedAccount.userInfo.id},
});
//Convert Speckle Objects
int childrenConverted = 0;
void BeforeConvertCallback(Base b)
{
EditorUtility.DisplayProgressBar(
"Converting To Native...",
$"{b.speckle_type} - {b.id}",
Convert.ToSingle(childrenConverted++ / _totalChildrenCount)
);
EditorUtility.DisplayProgressBar("Converting To Native...", $"{b.speckle_type} - {b.id}",
Convert.ToSingle(childrenConverted++ / _totalChildrenCount));
}
_streamManager.ConvertRecursivelyToNative(
@base,
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id,
BeforeConvertCallback
);
var go = _streamManager.ConvertRecursivelyToNative(@base,
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, BeforeConvertCallback);
// Read Receipt
await Client.CommitReceived(
new CommitReceivedInput
{
streamId = SelectedStream.id,
commitId = Branches[SelectedBranchIndex].commits.items[
SelectedCommitIndex
].id,
message = $"received commit from {HostApplications.Unity.Name} Editor",
sourceApplication = HostApplications.Unity.Name
}
);
await Client.CommitReceived(new CommitReceivedInput
{
streamId = SelectedStream.id,
commitId = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id,
message = $"received commit from {HostApplications.Unity.Name} Editor",
sourceApplication = HostApplications.Unity.Name
});
}
catch (Exception e)
{
@@ -254,7 +223,8 @@ namespace Speckle.ConnectorUnity.Components.Editor
public override async void OnInspectorGUI()
{
_streamManager = (StreamManager)target;
_streamManager = (StreamManager) target;
#region Account GUI
@@ -264,15 +234,12 @@ namespace Speckle.ConnectorUnity.Components.Editor
return;
}
EditorGUILayout.BeginHorizontal();
SelectedAccountIndex = EditorGUILayout.Popup(
"Accounts",
SelectedAccountIndex,
SelectedAccountIndex = EditorGUILayout.Popup("Accounts", SelectedAccountIndex,
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)
{
@@ -288,37 +255,26 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.EndHorizontal();
#region Speckle Account Info
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(
_foldOutAccount,
"Account Info"
);
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(_foldOutAccount, "Account Info");
if (_foldOutAccount)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField(
"Name",
SelectedAccount.userInfo.name,
EditorGUILayout.TextField("Name", SelectedAccount.userInfo.name,
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.ExpandWidth(true)
);
GUILayout.ExpandWidth(true));
EditorGUILayout.TextField(
"URL",
SelectedAccount.serverInfo.url,
EditorGUILayout.TextField("URL", SelectedAccount.serverInfo.url,
GUILayout.Height(20),
GUILayout.ExpandWidth(true)
);
GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
}
@@ -336,13 +292,9 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.BeginHorizontal();
SelectedStreamIndex = EditorGUILayout.Popup(
"Streams",
SelectedStreamIndex,
Streams.Select(x => x.name).ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true)
);
SelectedStreamIndex = EditorGUILayout.Popup("Streams",
SelectedStreamIndex, Streams.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
if (OldSelectedStreamIndex != SelectedStreamIndex)
{
@@ -367,29 +319,23 @@ namespace Speckle.ConnectorUnity.Components.Editor
EditorGUILayout.BeginHorizontal();
SelectedBranchIndex = EditorGUILayout.Popup(
"Branches",
SelectedBranchIndex,
Branches.Select(x => x.name).ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true)
);
SelectedBranchIndex = EditorGUILayout.Popup("Branches",
SelectedBranchIndex, Branches.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
if (!Branches[SelectedBranchIndex].commits.items.Any())
return;
EditorGUILayout.BeginHorizontal();
SelectedCommitIndex = EditorGUILayout.Popup(
"Commits",
SelectedCommitIndex = EditorGUILayout.Popup("Commits",
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.ExpandWidth(true)
);
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
@@ -401,12 +347,12 @@ namespace Speckle.ConnectorUnity.Components.Editor
GUILayout.Label("Generate assets");
GUILayout.FlexibleSpace();
bool selection = GUILayout.Toggle(_generateAssets, "");
if (_generateAssets != selection)
bool selection = GUILayout.Toggle(generateAssets, "");
if (generateAssets != selection)
{
_generateAssets = selection;
_streamManager.RC.AssetCache.nativeCaches =
NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
generateAssets = selection;
_streamManager.RC.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
}
EditorGUILayout.EndHorizontal();
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Speckle.Core.Models;
using UnityEditor;
using UnityEngine;
@@ -16,28 +18,23 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
public const string DefaultPath = "Assets/Resources";
public string path = DefaultPath;
private MemoryNativeCache _readCache;
private MemoryNativeCache readCache;
#nullable enable
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;
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);
return nativeObject != null;
}
@@ -46,75 +43,66 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
{
return WriteObject(speckleObject, nativeObject);
}
private bool WriteObject(Base speckleObject, Object nativeObject)
{
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
if (nativeObject is GameObject go)
{
var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(
go,
assetPath,
InteractionMode.AutomatedAction
);
return _readCache.TrySaveObject(speckleObject, prefab);
var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction);
return readCache.TrySaveObject(speckleObject, prefab);
}
// Exit early if there's already an asset
Object? existing = AssetDatabase.LoadAssetAtPath(assetPath, nativeObject.GetType());
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;
}
AssetDatabase.CreateAsset(nativeObject, $"{assetPath}");
return _readCache.TrySaveObject(speckleObject, nativeObject);
return readCache.TrySaveObject(speckleObject, nativeObject);
}
public override void BeginWrite()
{
base.BeginWrite();
//AssetDatabase.StartAssetEditing();
}
public override void FinishWrite()
{
if (!isWriting)
return;
if (!isWriting) return;
//AssetDatabase.StopAssetEditing();
AssetDatabase.SaveAssets();
if (_readCache != null)
_readCache.LoadedAssets.Clear();
if (readCache != null) readCache.LoadedAssets.Clear();
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);
outPath = null;
if (folder == null)
return false;
if (!CreateDirectory(folder))
return false;
if (folder == null) return false;
if (!CreateDirectory(folder)) return false;
string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType);
outPath = $"{folder}/{assetName}";
return true;
}
private static bool CreateDirectory(string directoryPath)
{
if (Directory.Exists(directoryPath))
return true;
var info = Directory.CreateDirectory(directoryPath);
AssetDatabase.Refresh();
return info.Exists;
@@ -123,20 +111,16 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
[ContextMenu("SetPath")]
public void SetPath_Menu()
{
var selection = EditorUtility.OpenFolderPanel(
"Set Assets Path",
"Assets/Resources",
""
);
if (selection.StartsWith(Application.dataPath))
{
var selection = EditorUtility.OpenFolderPanel("Set Assets Path", "Assets/Resources", "");
if (selection.StartsWith(Application.dataPath)) {
path = "Assets" + selection.Substring(Application.dataPath.Length);
}
else
{
Debug.LogError($"Expected selection to be within {Application.dataPath}");
}
}
}
}
@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using UnityEditor;
@@ -14,10 +13,8 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public sealed class AccountSelectionDrawer : OptionSelectionDrawer<Account>
{
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()
{
details = new (string, Func<Account, string>)[]
@@ -32,78 +29,74 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
};
}
}
[CustomPropertyDrawer(typeof(StreamSelection))]
public sealed class StreamSelectionDrawer : OptionSelectionDrawer<Stream>
{
protected override bool DisplayRefresh => true;
protected override string FormatOption(Stream o) => $"{o.name}";
public StreamSelectionDrawer()
{
properties = new[] { $"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField" };
properties = new []{$"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField"};
details = new (string, Func<Stream, string>)[]
{
("Project id", s => s.id),
("Stream id", s => s.id),
("Description", s => s.description),
("Is Public", s => s.isPublic.ToString()),
("Role", s => s.role),
("Created at", s => s.createdAt.ToString(CultureInfo.InvariantCulture)),
("Updated at", s => s.updatedAt.ToString(CultureInfo.InvariantCulture)),
("Created at", s => s.createdAt.ToString()),
("Updated at", s => s.updatedAt.ToString()),
};
}
}
[CustomPropertyDrawer(typeof(BranchSelection))]
public sealed class BranchSelectionDrawer : OptionSelectionDrawer<Branch>
{
protected override bool DisplayRefresh => true;
protected override string FormatOption(Branch o) => $"{o.name}";
public BranchSelectionDrawer()
{
properties = new[]
properties = new []
{
$"<{nameof(BranchSelection.BranchesLimit)}>k__BackingField",
$"<{nameof(BranchSelection.CommitsLimit)}>k__BackingField",
};
details = new (string, Func<Branch, string>)[]
{
("Model Id", s => s.id),
("Description", s => s.description),
};
}
}
[CustomPropertyDrawer(typeof(CommitSelection))]
public sealed class CommitSelectionDrawer : OptionSelectionDrawer<Commit>
{
protected override string FormatOption(Commit o) => $"{o.message} - {o.id}";
public CommitSelectionDrawer()
{
details = new (string, Func<Commit, string>)[]
{
("Version Id", s => s.id),
("Commit Id", s => s.id),
("Author Name", s => s.authorName),
("Created At", s => s.createdAt.ToString(CultureInfo.InvariantCulture)),
("Created At", s => s.createdAt.ToString()),
("Source Application", s => s.sourceApplication),
("Reference Object Id", s => s.referencedObject),
};
}
}
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer
where TOption : class
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer where TOption : class
{
private const float RefreshButtonWidthScale = 0.2f;
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 abstract string FormatOption(TOption o);
@@ -112,10 +105,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
protected string[] properties = { };
protected (string, Func<TOption, string>)[] details = { };
private string[] GetFormattedOptions(IReadOnlyList<TOption> options)
private string[] GetFormattedOptions(TOption[] options)
{
int optionsCount = options.Count;
int optionsCount = options.Length;
string[] choices = new string[optionsCount];
for (int i = 0; i < optionsCount; i++)
{
@@ -125,12 +118,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
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;
@@ -154,106 +142,80 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
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.width -= PrefixIndentation + 5;
TOption? selectedOption = t.Selected;
// Options selection
{
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;
string selectedChoice = selectedOption != null ? FormatOption(selectedOption) : "";
if (GUI.Button(popupSize, selectedChoice, EditorStyles.popup))
{
var windowPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
provider.Title = typeof(TOption).Name;
provider.listItems = GetFormattedOptions(t.Options);
provider.onSetIndexCallback = o =>
{
t.Selected = t.Options[o];
};
provider.listItems = GetFormattedOptions(t.Options);;
provider.onSetIndexCallback = o => { t.SelectedIndex = o;};
SearchWindow.Open(new SearchWindowContext(windowPos), provider);
}
// Optional refresh
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"))
{
EditorApplication.delayCall += t.RefreshOptions;
}
}
}
// Collapsable details
{
{
int visiblePropCount = property.isExpanded ? GUIDetailsPropertyCount : 0;
var detailsHeight = new Vector2(
PrefixIndentation,
DetailsTextHeight + visiblePropCount * DetailsTextHeight
);
var foldoutRect = new Rect(position.position, detailsHeight);
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(
foldoutRect,
property.isExpanded,
label
);
var detailsHeight = new Vector2(PrefixIndentation, DetailsTextHeight + visiblePropCount * DetailsTextHeight);
var foldoutRect = new Rect(position.position, detailsHeight);
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(foldoutRect, property.isExpanded, label);
if (property.isExpanded)
{
OnGUIDetails(position, property, label, selectedOption);
}
EditorGUI.EndFoldoutHeaderGroup();
}
EditorGUI.EndProperty();
//EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var standardHeight = EditorGUIUtility.singleLineHeight;
if (!property.isExpanded) return standardHeight + EditorGUIUtility.standardVerticalSpacing;
if (!property.isExpanded)
return standardHeight + EditorGUIUtility.standardVerticalSpacing;
var detailsHeight =
GUIDetailsPropertyCount
* (standardHeight + EditorGUIUtility.standardVerticalSpacing);
return standardHeight
+ detailsHeight
+ EditorGUIUtility.standardVerticalSpacing
+ EditorGUIUtility.standardVerticalSpacing;
var detailsHeight = GUIDetailsPropertyCount * (standardHeight + EditorGUIUtility.standardVerticalSpacing);
return standardHeight + detailsHeight + EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.standardVerticalSpacing;
}
}
#nullable disable
#nullable disable
public sealed class StringListSearchProvider : ScriptableObject, ISearchWindowProvider
{
@@ -261,13 +223,12 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
public string[] listItems;
public Action<int> onSetIndexCallback;
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
List<SearchTreeEntry> searchList =
new(listItems.Length + 1) { new SearchTreeGroupEntry(new GUIContent(Title)) };
for (int i = 0; i < listItems.Length; i++)
List<SearchTreeEntry> searchList = new(listItems.Length + 1) {new SearchTreeGroupEntry(new GUIContent(Title), 0)};
for(int i = 0; i < listItems.Length; i++)
{
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(listItems[i]))
{
@@ -276,15 +237,18 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
};
searchList.Add(entry);
}
return searchList;
}
public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context)
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
{
onSetIndexCallback?.Invoke((int)searchTreeEntry.userData);
onSetIndexCallback?.Invoke((int)SearchTreeEntry.userData);
return true;
}
}
}
@@ -18,26 +18,25 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
{
private static readonly string[] SpeckleTypeOptionStrings;
private static readonly Type[] SpeckleTypeOptions;
private static HashSet<string> ArrayFoldoutState = new();
private static bool instancePropFoldoutState = true;
private static bool dynamicPropFoldoutState = true;
private static bool isEditMode;
private static bool isEditMode = false;
static SpecklePropertiesEditor()
{
var options = typeof(Mesh).Assembly
.GetTypes()
.Where(x => x.IsSubclassOf(typeof(Base)) && !x.IsAbstract)
.ToList();
.Where(x => x.IsSubclassOf(typeof(Base)) && !x.IsAbstract).ToList();
var strings = options
.Where(x => x.FullName != null)
.Select(x => x.FullName!.Replace('.', '/'));
var manualTypes = new[] { typeof(Base), typeof(Collection) };
var manualStrings = new[] { nameof(Base), nameof(Collection) };
var manualTypes = new [] { typeof(Base), typeof(Collection)};
var manualStrings = new []{ nameof(Base), nameof(Collection)};
//Manually Add `Base`
SpeckleTypeOptions = options.Concat(manualTypes).ToArray();
SpeckleTypeOptionStrings = strings.Concat(manualStrings).ToArray();
@@ -46,82 +45,64 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
}
private static GUILayoutOption[] propLayoutOptions = { GUILayout.ExpandWidth(true) };
public override void OnInspectorGUI()
{
SpeckleProperties properties = (SpeckleProperties)target;
//Edit Mode
isEditMode = EditorGUILayout.ToggleLeft(
"Enable Inspector Edit Mode (experimental)",
isEditMode
);
isEditMode = EditorGUILayout.ToggleLeft("Enable Inspector Edit Mode (experimental)", isEditMode);
if (isEditMode)
{
GUILayout.Label(
"Modifying properties through the inspector is experimental and can lead to invalid objects, proceed at your own risk!",
EditorStyles.helpBox
);
EditorStyles.helpBox);
GUILayout.Space(10);
}
GUI.enabled = isEditMode;
// SpeckleType
GUILayout.Label("Speckle Type: ", EditorStyles.boldLabel);
GUILayout.Label("Speckle Type: ", EditorStyles.boldLabel );
var oldIndex = Array.IndexOf(SpeckleTypeOptions, properties.SpeckleType);
var speckleTypeSelectedIndex = EditorGUILayout.Popup(
oldIndex,
SpeckleTypeOptionStrings
);
if (oldIndex != speckleTypeSelectedIndex && speckleTypeSelectedIndex >= 0)
var speckleTypeSelectedIndex = EditorGUILayout.Popup(oldIndex, SpeckleTypeOptionStrings);
if(oldIndex != speckleTypeSelectedIndex && speckleTypeSelectedIndex >= 0)
{
properties.SpeckleType = SpeckleTypeOptions[speckleTypeSelectedIndex];
}
// Instance Properties
var instancePropertyNames =
(IReadOnlyCollection<string>)
DynamicBase.GetInstanceMembersNames(properties.SpeckleType);
instancePropFoldoutState = EditorGUILayout.Foldout(
instancePropFoldoutState,
"Instance Properties: ",
EditorStyles.foldoutHeader
);
var InstancePropertyNames = DynamicBase.GetInstanceMembersNames(properties.SpeckleType);
instancePropFoldoutState = EditorGUILayout.Foldout(instancePropFoldoutState, "Instance Properties: ", EditorStyles.foldoutHeader);
if (instancePropFoldoutState)
{
foreach (var propName in instancePropertyNames)
foreach (var propName in InstancePropertyNames)
{
if (!properties.Data.TryGetValue(propName, out object? existingValue))
continue;
if (!properties.Data.TryGetValue(propName, out object? existingValue)) continue;
var newValue = CreateField(existingValue, propName, propLayoutOptions);
if (newValue != existingValue)
if(newValue != existingValue)
properties.Data[propName] = newValue;
}
}
GUILayout.Space(10);
dynamicPropFoldoutState = EditorGUILayout.Foldout(
dynamicPropFoldoutState,
"Dynamic Properties:",
EditorStyles.foldoutHeader
);
dynamicPropFoldoutState = EditorGUILayout.Foldout(dynamicPropFoldoutState, "Dynamic Properties:", EditorStyles.foldoutHeader);
if (dynamicPropFoldoutState)
{
var ignoreSet = instancePropertyNames.ToImmutableHashSet();
var ignoreSet = InstancePropertyNames.ToImmutableHashSet();
foreach (var kvp in properties.Data)
{
if (ignoreSet.Contains(kvp.Key))
continue;
if (ignoreSet.Contains(kvp.Key)) continue;
var existingValue = kvp.Value;
var newValue = CreateField(existingValue, kvp.Key, propLayoutOptions);
if (newValue != existingValue)
if(newValue != existingValue)
properties.Data[kvp.Key] = newValue;
GUILayout.Space(10);
}
}
@@ -137,18 +118,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
_ => CreateFieldPrimitive(v, propName, options),
};
if (ret != null)
return ret;
EditorGUILayout.TextField(propName, v == null ? "NULL" : v.ToString());
if (ret != null) return ret;
EditorGUILayout.TextField(propName, v == null? "NULL" : v.ToString());
return v;
}
private static object? CreateFieldPrimitive(
object? v,
string propName,
params GUILayoutOption[] options
)
private static object? CreateFieldPrimitive(object? v, string propName, params GUILayoutOption[] options)
{
return v switch
{
@@ -159,19 +135,11 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
string s => EditorGUILayout.TextField(propName, s, options),
bool b => EditorGUILayout.Toggle(propName, b, options),
Enum e => EditorGUILayout.EnumPopup(propName, e, options),
Point p
=> PointToVector3(
EditorGUILayout.Vector3Field(
propName,
new Vector3((float)p.x, (float)p.z, (float)p.z),
options
),
p
),
Point p => PointToVector3(EditorGUILayout.Vector3Field(propName, new Vector3((float)p.x, (float)p.z, (float)p.z), options), p),
_ => null,
};
}
private static Point PointToVector3(Vector3 vector, Point p)
{
p.x = vector.x;
@@ -179,13 +147,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
p.z = vector.z;
return p;
}
private IList ArrayField(string propName, IList list, params GUILayoutOption[] options)
{
bool isExpanded = EditorGUILayout.Foldout(
ArrayFoldoutState.Contains(propName),
propName
);
bool isExpanded = EditorGUILayout.Foldout(ArrayFoldoutState.Contains(propName), propName);
if (isExpanded)
{
ArrayFoldoutState.Add(propName);
@@ -193,13 +158,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
{
object? item = list[i];
var r = CreateFieldPrimitive(item, i.ToString(), options);
if (r == null)
{
EditorGUILayout.TextField(
i.ToString(),
item == null ? "NULL" : item.ToString()
);
EditorGUILayout.TextField(i.ToString(), item == null? "NULL" : item.ToString());
continue;
}
//Update list item
@@ -213,5 +175,6 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
return list;
}
}
}
@@ -8,6 +8,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Sentry;
using Speckle.ConnectorUnity.Components;
using Speckle.ConnectorUnity.Utils;
@@ -16,201 +17,184 @@ using UnityEngine;
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("See " + nameof(SpeckleReceiver))]
public class Receiver : MonoBehaviour
/// <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()
{
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>
/// Initializes the Receiver manually
/// </summary>
/// <param name="streamId">Id of the stream to receive</param>
/// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param>
/// <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>
/// <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;
AutoReceive = autoReceive;
DeleteOld = deleteOld;
OnDataReceivedAction = onDataReceivedAction;
OnErrorAction = onErrorAction;
OnProgressAction = onProgressAction;
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
Client = new Client(account ?? AccountManager.GetDefaultAccount());
if (AutoReceive)
{
Client.SubscribeCommitCreated(StreamId);
Client.OnCommitCreated += Client_OnCommitCreated;
}
}
/// <summary>
/// Gets and converts the data of the last commit on the Stream
/// </summary>
/// <returns></returns>
public void Receive()
{
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
/// <summary>
/// Fired when a new commit is created on this stream
/// It receives and converts the objects and then executes the user defined _onCommitCreated action.
/// </summary>
/// <param name="sender"></param>
/// <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) },
{
"sourceHostApp",
HostApplications.GetHostAppFromString(sourceApplication).Slug
},
{ "sourceHostAppVersion", sourceApplication ?? "" },
{ "hostPlatform", Application.platform.ToString() },
{
"isMultiplayer",
authorId != null && authorId != Client.Account.userInfo.id
},
}
);
Dispatcher
.Instance()
.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()
{
Client?.CommitCreatedSubscription?.Dispose();
}
#endregion
}
/// <summary>
/// Initializes the Receiver manually
/// </summary>
/// <param name="streamId">Id of the stream to receive</param>
/// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param>
/// <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>
/// <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;
AutoReceive = autoReceive;
DeleteOld = deleteOld;
OnDataReceivedAction = onDataReceivedAction;
OnErrorAction = onErrorAction;
OnProgressAction = onProgressAction;
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
Client = new Client(account ?? AccountManager.GetDefaultAccount());
if (AutoReceive)
{
Client.SubscribeCommitCreated(StreamId);
Client.OnCommitCreated += Client_OnCommitCreated;
}
}
/// <summary>
/// Gets and converts the data of the last commit on the Stream
/// </summary>
/// <returns></returns>
public void Receive()
{
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
/// <summary>
/// Fired when a new commit is created on this stream
/// It receives and converts the objects and then executes the user defined _onCommitCreated action.
/// </summary>
/// <param name="sender"></param>
/// <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)},
{"sourceHostApp", HostApplications.GetHostAppFromString(sourceApplication).Slug},
{"sourceHostAppVersion", sourceApplication ?? ""},
{"hostPlatform", Application.platform.ToString()},
{"isMultiplayer", authorId != null && authorId != Client.Account.userInfo.id},
});
Dispatcher.Instance().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()
{
Client?.CommitCreatedSubscription?.Dispose();
}
#endregion
}
}
@@ -16,150 +16,135 @@ using UnityEngine.SceneManagement;
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("See " + nameof(SpeckleSender))]
public class Sender : MonoBehaviour
/// <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()
{
private ServerTransport transport;
private RecursiveConverter converter;
private CancellationTokenSource cancellationTokenSource;
#nullable enable
private void Awake()
{
converter = GetComponent<RecursiveConverter>();
}
/// <summary>
/// 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
converter = GetComponent<RecursiveConverter>();
}
/// <summary>
/// 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
}
}
@@ -29,18 +29,16 @@ namespace Speckle.ConnectorUnity.Components
public List<Branch> Branches;
public RecursiveConverter RC { get; private set; }
#nullable enable
private void Awake()
{
RC = GetComponent<RecursiveConverter>();
}
public GameObject ConvertRecursivelyToNative(
Base @base,
string rootObjectName,
Action<Base>? beforeConvertCallback
)
public GameObject ConvertRecursivelyToNative(Base @base, string rootObjectName,
Action<Base>? beforeConvertCallback)
{
var rootObject = new GameObject(rootObjectName);
@@ -48,9 +46,10 @@ namespace Speckle.ConnectorUnity.Components
{
beforeConvertCallback?.Invoke(o);
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
// i.e. revit categories
foreach (var prop in @base.GetMembers())
@@ -58,15 +57,13 @@ namespace Speckle.ConnectorUnity.Components
var converted = RC.RecursivelyConvertToNative(prop.Value, null, Predicate);
//Skip empties
if (converted.Count <= 0)
continue;
if (converted.Count <= 0) continue;
var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform);
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);
}
}
@@ -83,4 +80,4 @@ namespace Speckle.ConnectorUnity.Components
}
#endif
}
}
}
@@ -1,177 +0,0 @@
using System;
using System.Collections;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEngine;
namespace Speckle.ConnectorUnity.Components
{
[AddComponentMenu("Speckle/Extras/Receive from URL")]
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
public class ReceiveFromURL : MonoBehaviour
{
[Tooltip("Url of your speckle object/commit/branch/stream")]
public string url;
private RecursiveConverter _converter;
#nullable enable
private CancellationTokenSource? _tokenSource;
void Awake()
{
_converter = GetComponent<RecursiveConverter>();
}
[ContextMenu(nameof(Receive))]
public void Receive()
{
StartCoroutine(Receive_Routine());
}
public IEnumerator Receive_Routine()
{
if (IsBusy())
throw new InvalidOperationException("A receive operation has already started");
_tokenSource = new CancellationTokenSource();
try
{
StreamWrapper sw = new(url);
if (!sw.IsValid)
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),
_tokenSource.Token
);
yield return accountTask;
_tokenSource.Token.ThrowIfCancellationRequested();
using Client c = new(accountTask.Result);
var objectIdTask = new Utils.Utils.WaitForTask<(string, Commit?)>(
async () => await GetObjectID(sw, c),
_tokenSource.Token
);
yield return objectIdTask;
(string objectId, Commit? commit) = objectIdTask.Result;
Debug.Log($"Receiving from {sw.ServerUrl}...");
var receiveTask = new Utils.Utils.WaitForTask<Base>(
async () =>
await SpeckleReceiver.ReceiveAsync(
c,
sw.StreamId,
objectId,
commit,
cancellationToken: _tokenSource.Token
),
_tokenSource.Token
);
yield return receiveTask;
Debug.Log("Converting to native...");
_converter.RecursivelyConvertToNative_Sync(receiveTask.Result, transform);
}
finally
{
_tokenSource.Dispose();
_tokenSource = null;
}
}
private async Task<(string objectId, Commit? commit)> GetObjectID(
StreamWrapper sw,
Client client
)
{
string objectId;
Commit? commit = null;
//OBJECT URL
if (!string.IsNullOrEmpty(sw.ObjectId))
{
objectId = sw.ObjectId;
}
//COMMIT URL
else if (!string.IsNullOrEmpty(sw.CommitId))
{
commit = await client.CommitGet(sw.StreamId, sw.CommitId).ConfigureAwait(false);
objectId = commit.referencedObject;
}
//BRANCH URL OR STREAM URL
else
{
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
var branch = await client
.BranchGet(sw.StreamId, branchName, 1)
.ConfigureAwait(false);
if (!branch.commits.items.Any())
throw new SpeckleException("The selected branch has no commits.");
commit = branch.commits.items[0];
objectId = branch.commits.items[0].referencedObject;
}
return (objectId, commit);
}
[ContextMenu(nameof(Cancel))]
public void Cancel()
{
if (IsNotBusy())
throw new InvalidOperationException(
"There are no pending receive operations to cancel"
);
_tokenSource!.Cancel();
}
[ContextMenu(nameof(Cancel), true)]
public bool IsBusy()
{
return _tokenSource is not null;
}
[ContextMenu(nameof(Receive), true)]
internal bool IsNotBusy() => !IsBusy();
private void OnDisable()
{
_tokenSource?.Cancel();
}
private async Task<Account> GetAccount(StreamWrapper sw)
{
Account account;
try
{
account = await sw.GetAccount().ConfigureAwait(false);
}
catch (SpeckleException)
{
if (string.IsNullOrEmpty(sw.StreamId))
throw;
//Fallback to a non authed account
account = new Account
{
token = "",
serverInfo = new ServerInfo { url = sw.ServerUrl },
userInfo = new UserInfo()
};
}
return account;
}
}
}
@@ -12,6 +12,7 @@ using UnityEngine;
namespace Speckle.ConnectorUnity.Components
{
/// <summary>
/// Struct that encapsulates the result of a <see cref="RecursiveConverter"/> ToNative conversion of a single Speckle Object (<see cref="Base"/>)
/// </summary>
@@ -21,12 +22,12 @@ namespace Speckle.ConnectorUnity.Components
/// The context that was converted ToNative
/// </summary>
public readonly TraversalContext traversalContext;
/// <summary>
/// The result of conversion a successful conversion
/// </summary>
public readonly GameObject? converted;
/// <summary>
/// The result of conversion a failed conversion
/// </summary>
@@ -41,8 +42,7 @@ namespace Speckle.ConnectorUnity.Components
public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted)
: this(traversalContext, converted, null)
{
if (converted is null)
throw new ArgumentNullException(nameof(converted));
if (converted is null) throw new ArgumentNullException(nameof(converted));
}
/// <summary>
@@ -52,22 +52,14 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="exception">The operation halting exception that occured</param>
/// <param name="converted">Optional converted GameObject</param>
/// <exception cref="ArgumentNullException"/>
public ConversionResult(
TraversalContext traversalContext,
[NotNull] Exception? exception,
GameObject? converted = null
)
public ConversionResult(TraversalContext traversalContext, [NotNull] Exception? exception,
GameObject? converted = null)
: 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.converted = converted;
@@ -75,28 +67,28 @@ namespace Speckle.ConnectorUnity.Components
}
/// <summary>
///
///
/// </summary>
/// <param name="converted">The converted <see cref="GameObject"/></param>
/// <param name="exception">The <see cref="exception"/> that occured during conversion</param>
/// <returns>True if the conversion was successful</returns>
public bool WasSuccessful(
[NotNullWhen(true)] out GameObject? converted,
[NotNullWhen(false)] out Exception? exception
)
[NotNullWhen(true)] out GameObject? converted,
[NotNullWhen(false)] out Exception? exception)
{
converted = this.converted;
exception = this.exception;
return WasSuccessful();
}
public bool WasSuccessful() => this.exception == null;
public Base SpeckleObject => traversalContext.Current;
public Base SpeckleObject => traversalContext.current;
}
public partial class RecursiveConverter
{
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
/// <remarks>Calling this function will perform the conversion process synchronously</remarks>
/// <returns>The conversion result</returns>
@@ -108,7 +100,7 @@ namespace Speckle.ConnectorUnity.Components
{
return RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).ToList();
}
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
/// <remarks>Calling this function will start a coroutine to complete later on the coroutine loop</remarks>
/// <returns>The started Coroutine</returns>
@@ -118,14 +110,12 @@ namespace Speckle.ConnectorUnity.Components
Predicate<TraversalContext>? predicate = null
)
{
return StartCoroutine(
RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).GetEnumerator()
);
return StartCoroutine(RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).GetEnumerator());
}
/// <summary>
/// Will recursively traverse the given <paramref name="rootObject"/> and convert convertable child objects
/// where the given <see cref="predicate"/>
/// where the given <see cref="predicate"/>
/// </summary>
/// <param name="rootObject">The Speckle object to traverse and convert all convertable children</param>
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
@@ -134,25 +124,21 @@ namespace Speckle.ConnectorUnity.Components
public IEnumerable<ConversionResult> RecursivelyConvertToNative_Enumerable(
Base rootObject,
Transform? parent,
Predicate<TraversalContext>? predicate = null
)
Predicate<TraversalContext>? predicate = null)
{
var userPredicate = predicate ?? (_ => true);
var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
var objectsToConvert = traversalFunc
.Traverse(rootObject)
.Where(x => ConverterInstance.CanConvertToNative(x.Current))
.Where(x => ConverterInstance.CanConvertToNative(x.current))
.Where(x => userPredicate(x));
Dictionary<Base, GameObject?> created = new();
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;
}
@@ -164,21 +150,17 @@ namespace Speckle.ConnectorUnity.Components
/// or <see langword="null"/> if not.
/// </summary>
/// <remarks>
/// You may enumerate over multiple frames (e.g. coroutine) but you must ensure the output eventually gets fully enumerated (exactly once)
/// You may enumerate over multiple frames (e.g. coroutine) but you must ensure the output eventually gets fully enumerated (exactly once)
/// </remarks>
/// <param name="objectTree"></param>
/// <param name="parent"></param>
/// <param name="outCreatedObjects"></param>
/// <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();
AssetCache.BeginWrite();
foreach (TraversalContext tc in objectTree)
{
ConversionResult result;
@@ -186,9 +168,9 @@ namespace Speckle.ConnectorUnity.Components
{
Transform? currentParent = GetParent(tc, outCreatedObjects) ?? parent;
var converted = ConvertToNative(tc.Current, currentParent);
var converted = ConvertToNative(tc.current, currentParent);
result = new ConversionResult(tc, converted);
outCreatedObjects.TryAdd(tc.Current, result.converted);
outCreatedObjects.TryAdd(tc.current, result.converted);
}
catch (Exception ex)
{
@@ -197,69 +179,49 @@ namespace Speckle.ConnectorUnity.Components
yield return result;
}
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 (createdObjects.TryGetValue(tc.Current, out GameObject? p) && p != null)
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)
return p.transform;
//Go one level up, and repeat!
return GetParent(tc.Parent, createdObjects);
return GetParent(tc.parent, createdObjects);
}
protected GameObject ConvertToNative(Base speckleObject, Transform? parentTransform)
{
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);
//Set some common for all created GameObjects
//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);
if (speckleObject["physicsLayer"] is string layerName)
{
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["isStatic"] is bool isStatic) go.isStatic = isStatic;
return go;
}
#region deprecated conversion functions
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
public IEnumerator ConvertCoroutine(
Base rootObject,
Transform? parent,
List<GameObject> outCreatedObjects
) =>
ConvertCoroutine(
rootObject,
parent,
outCreatedObjects,
b => ConverterInstance.CanConvertToNative(b)
);
public IEnumerator ConvertCoroutine(Base rootObject, Transform? parent, List<GameObject> outCreatedObjects)
=> ConvertCoroutine(rootObject, parent, outCreatedObjects,b => ConverterInstance.CanConvertToNative(b));
[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))
{
@@ -267,7 +229,7 @@ namespace Speckle.ConnectorUnity.Components
yield return null;
}
}
/// <summary>
/// Given <paramref name="o"/>,
/// will recursively convert any objects in the tree
@@ -276,20 +238,16 @@ namespace Speckle.ConnectorUnity.Components
/// <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>
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent) =>
RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b));
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent)
=> RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b));
/// <inheritdoc cref="RecursivelyConvertToNative(object, Transform)"/>
/// <param name="predicate">A function to determine if an object should be converted</param>
[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();
var createdGameObjects = new List<GameObject>();
try
{
@@ -302,9 +260,10 @@ namespace Speckle.ConnectorUnity.Components
}
//TODO track event?
return createdGameObjects;
}
private void InitializeAssetCache()
@@ -318,27 +277,11 @@ namespace Speckle.ConnectorUnity.Components
}
[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;
if (predicate(baseObject))
{
try
{
converted = ConverterInstance.ConvertToNative(baseObject);
}
catch (Exception ex) when (!ex.IsFatal())
{
Debug.LogWarning(
$"Failed to convert {baseObject.speckle_type} - {baseObject.id}\n{ex}"
);
}
}
if(predicate(baseObject))
converted = ConverterInstance.ConvertToNative(baseObject);
// Handle new GameObjects
Transform? nextParent = parent;
@@ -346,23 +289,22 @@ namespace Speckle.ConnectorUnity.Components
{
outCreatedObjects.Add(go);
nextParent = go.transform;
go.transform.SetParent(parent, true);
//Set some common for all created GameObjects
//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);
//if (baseObject["tag"] is string t) go.tag = t;
if (baseObject["physicsLayer"] is string layerName)
{
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;
}
// For geometry, only traverse `elements` prop, otherwise, try and convert everything
IEnumerable<string> potentialChildren = GetPotentialChildren(baseObject);
@@ -371,23 +313,19 @@ namespace Speckle.ConnectorUnity.Components
{
ConvertChild(baseObject[propertyName], nextParent, predicate, outCreatedObjects);
}
}
[Obsolete]
private IEnumerable<string> GetPotentialChildren(Base baseObject)
{
return ConverterInstance.CanConvertToNative(baseObject)
? new[] { "elements" }
? new []{"elements"}
: baseObject.GetMembers().Keys;
}
[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))
{
@@ -8,6 +8,7 @@ using UnityEngine;
namespace Speckle.ConnectorUnity.Components
{
public partial class RecursiveConverter
{
/// <summary>
@@ -30,44 +31,37 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="rootObjects">Root objects of a tree</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>
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>();
foreach (GameObject rootObject in rootObjects)
{
RecurseTreeToSpeckle(rootObject, predicate, convertedRootObjects);
}
return new Base() { ["@objects"] = convertedRootObjects, };
return new Base()
{
["@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
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);
}
if (ConverterInstance.CanConvertToSpeckle(currentObject) && predicate(currentObject))
{
// Convert and output
// Convert and output
Base converted = ConverterInstance.ConvertToSpeckle(currentObject);
converted.SetDetachedPropertyChecked("elements", convertedChildren);
outConverted.Add(converted);
@@ -77,6 +71,7 @@ namespace Speckle.ConnectorUnity.Components
// Skip this object, and output any children
outConverted.AddRange(convertedChildren);
}
}
}
}
}
@@ -14,9 +14,7 @@ namespace Speckle.ConnectorUnity.Components
[ExecuteAlways, DisallowMultipleComponent]
public partial class RecursiveConverter : MonoBehaviour
{
[field: SerializeReference]
public ISpeckleConverter ConverterInstance { get; set; } =
ConverterFactory.GetDefaultConverter();
public ISpeckleConverter ConverterInstance { get; set; } = ConverterFactory.GetDefaultConverter();
[field: SerializeField]
public AggregateNativeCache AssetCache { get; set; }
@@ -28,10 +26,7 @@ namespace Speckle.ConnectorUnity.Components
protected void Init()
{
Setup.Init(
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
HostApplications.Unity.Slug
);
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
if (AssetCache == null)
{
@@ -39,8 +34,6 @@ namespace Speckle.ConnectorUnity.Components
assetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup();
this.AssetCache = assetCache;
}
InitializeAssetCache();
}
}
}
@@ -6,8 +6,7 @@
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
"GUID:13aec21e8e96f864bafd00df49f225fc",
"GUID:d274441ecc3eb3f43b093eec1503d681",
"GUID:50d889142fdf9de4b8501c6eaa4b3225",
"GUID:7383cd71541a2aa48a7baf23f74b4d5f"
"GUID:50d889142fdf9de4b8501c6eaa4b3225"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -19,7 +19,6 @@ using UnityEngine;
using UnityEngine.Events;
[assembly: InternalsVisibleTo("Speckle.ConnectorUnity.Components.Editor")]
namespace Speckle.ConnectorUnity.Components
{
[ExecuteAlways]
@@ -29,13 +28,13 @@ namespace Speckle.ConnectorUnity.Components
{
[field: SerializeReference]
public AccountSelection Account { get; private set; }
[field: SerializeReference]
public StreamSelection Stream { get; private set; }
[field: SerializeReference]
public BranchSelection Branch { get; private set; }
[field: SerializeReference]
public CommitSelection Commit { get; private set; }
@@ -45,16 +44,12 @@ namespace Speckle.ConnectorUnity.Components
[Header("Events")]
[HideInInspector]
public CommitSelectionEvent OnCommitSelectionChange = new();
[HideInInspector]
public OperationProgressEvent OnReceiveProgressAction = new();
[HideInInspector]
public ErrorActionEvent OnErrorAction = new();
[HideInInspector]
public ChildrenCountHandler OnTotalChildrenCountKnown = new();
[HideInInspector]
public ReceiveCompleteHandler OnComplete = new();
@@ -71,34 +66,30 @@ 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>
public bool Cancel()
{
if (CancellationTokenSource == null)
return false;
if (CancellationTokenSource == null) return false;
CancellationTokenSource.Cancel();
return true;
}
/// <inheritdoc cref="ReceiveAndConvert_Async"/>
/// <example>
/// This function is designed to run as a coroutine i.e.
/// <c>StartCoroutine(mySpeckleReceiver.ReceiveAndConvert_Routine());</c>
/// </example>
public IEnumerator ReceiveAndConvert_Routine(
Transform? parent = null,
Predicate<TraversalContext>? predicate = null
)
/// <summary>
/// Receive the selected <see cref="Commit"/> object, and converts ToNative as children of <paramref name="parent"/>
/// </summary>
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
/// <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="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
public IEnumerator ReceiveAndConvert_Routine(Transform? parent, Predicate<TraversalContext>? predicate = null)
{
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;
}
CancellationTokenSource?.Dispose();
CancellationTokenSource = new();
// ReSharper disable once MethodSupportsCancellation
Task<Base> receiveOperation = Task.Run(async () =>
{
@@ -106,7 +97,7 @@ namespace Speckle.ConnectorUnity.Components
CancellationToken.ThrowIfCancellationRequested();
return result;
});
yield return new WaitUntil(() => receiveOperation.IsCompleted);
if (receiveOperation.IsFaulted)
@@ -115,7 +106,7 @@ namespace Speckle.ConnectorUnity.Components
FinishOperation();
yield break;
}
Base b = receiveOperation.Result;
foreach (var _ in Converter.RecursivelyConvertToNative_Enumerable(b, parent, predicate))
@@ -127,18 +118,9 @@ namespace Speckle.ConnectorUnity.Components
FinishOperation();
}
/// <summary>
/// Receive the selected <see cref="Commit"/> object, and converts ToNative as children of <paramref name="parent"/>
/// </summary>
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
/// <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="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
public async void ReceiveAndConvert_Async(
Transform? parent = null,
Predicate<TraversalContext>? predicate = null
)
/// <inheritdoc cref="ReceiveAndConvert_Routine"/>
public async void ReceiveAndConvert_Async(Transform? parent, Predicate<TraversalContext>? predicate = null)
{
try
{
@@ -172,7 +154,7 @@ namespace Speckle.ConnectorUnity.Components
public async Task<Base> ReceiveAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ValidateSelection(out Client? client, out Stream? stream, out Commit? commit);
Base result = await ReceiveAsync(
@@ -185,30 +167,20 @@ namespace Speckle.ConnectorUnity.Components
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
return result;
}
/// <summary>
/// Gets the current selection
/// </summary>
/// <param name="client">The selected Account's Client</param>
/// <param name="stream">The selected <see cref="Stream"/></param>
/// <param name="commit">The selected <see cref="Commit"/></param>
/// <exception cref="InvalidOperationException">Selection was not complete or invalid</exception>
public void ValidateSelection(out Client client, out Stream stream, out Commit commit)
{
Client? selectedClient = Account.Client;
client =
selectedClient ?? throw new InvalidOperationException("Invalid Speckle account selection");
client = selectedClient ?? throw new InvalidOperationException("Invalid account selection");
Stream? selectedStream = Stream.Selected;
stream =
selectedStream ?? throw new InvalidOperationException("Invalid Speckle project selection");
stream = selectedStream ?? throw new InvalidOperationException("Invalid stream selection");
Commit? selectedCommit = Commit.Selected;
commit =
selectedCommit ?? throw new InvalidOperationException("Invalid Speckle version selection");
commit = selectedCommit ?? throw new InvalidOperationException("Invalid commit selection");
}
/// <summary>
@@ -217,26 +189,22 @@ namespace Speckle.ConnectorUnity.Components
/// <exception cref="InvalidOperationException">already receiving</exception>
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 = new();
return CancellationTokenSource.Token;
}
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 = null;
}
/// <summary>
/// Receives the requested <see cref="objectId"/> using async Task
/// </summary>
@@ -248,77 +216,76 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="onTotalChildrenCountKnown"></param>
/// <param name="cancellationToken"></param>
/// <exception cref="Exception">Throws various types of exceptions to indicate faliure</exception>
/// <returns>The requested Speckle object</returns>
/// <returns></returns>
public static async Task<Base> ReceiveAsync(
Client client,
string streamId,
string objectId,
Commit? commit,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<int>? onTotalChildrenCountKnown = null,
CancellationToken cancellationToken = default
)
Action<int>? onTotalChildrenCountKnown = null,
CancellationToken cancellationToken = default)
{
using var transport = new ServerTransport(client.Account, streamId);
using var transport = new ServerTransportV2(client.Account, streamId);
transport.CancellationToken = cancellationToken;
cancellationToken.ThrowIfCancellationRequested();
Base requestedObject = await Operations
.Receive(
objectId,
transport,
null,
onProgressAction,
onTotalChildrenCountKnown,
cancellationToken
)
.ConfigureAwait(false);
Analytics.TrackEvent(
client.Account,
Analytics.Events.Receive,
new Dictionary<string, object>()
Base? requestedObject = await Operations.Receive(
objectId: objectId,
cancellationToken: cancellationToken,
remoteTransport: transport,
onProgressAction: onProgressAction,
onErrorAction: (s, ex) =>
{
{ "mode", nameof(SpeckleReceiver) },
{
"sourceHostApp",
HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug
},
{ "sourceHostAppVersion", commit?.sourceApplication ?? "" },
{ "hostPlatform", Application.platform.ToString() },
{
"isMultiplayer",
commit?.authorId != null && commit?.authorId != client.Account?.userInfo?.id
},
}
);
//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");
cancellationToken.ThrowIfCancellationRequested();
}
//Treat all operation errors as fatal
throw new SpeckleException($"Failed to receive requested object {objectId} from server: {s}", ex);
},
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)
throw new SpeckleException($"Operation {nameof(Operations.Receive)} returned null");
cancellationToken.ThrowIfCancellationRequested();
//Read receipt
try
{
await client
.CommitReceived(
new CommitReceivedInput
{
streamId = streamId,
commitId = commit?.id,
message = $"received commit from {Application.unityVersion}",
sourceApplication = HostApplications.Unity.GetVersion(
CoreUtils.GetHostAppVersion()
)
},
cancellationToken
)
.ConfigureAwait(false);
await client.CommitReceived(cancellationToken, new CommitReceivedInput
{
streamId = streamId,
commitId = commit?.id,
message = $"received commit from {Application.unityVersion}",
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
}).ConfigureAwait(false);
}
catch (Exception ex)
catch (Exception e)
{
// Do nothing!
Debug.LogWarning($"Failed to send read receipt\n{ex}");
Debug.LogWarning($"Failed to send read receipt\n{e}");
}
return requestedObject;
@@ -332,18 +299,9 @@ namespace Speckle.ConnectorUnity.Components
/// <param name="rootObjectName">The name of the parent <see cref="GameObject"/> to create</param>
/// <param name="beforeConvertCallback">Callback for each object converted</param>
/// <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"
)]
public GameObject ConvertToNativeWithCategories(
Base @base,
string rootObjectName,
Action<Base>? beforeConvertCallback
)
[Obsolete("Use " + 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);
@@ -351,9 +309,10 @@ namespace Speckle.ConnectorUnity.Components
{
beforeConvertCallback?.Invoke(o);
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
// i.e. revit categories
foreach (var prop in @base.GetMembers())
@@ -361,23 +320,21 @@ namespace Speckle.ConnectorUnity.Components
var converted = Converter.RecursivelyConvertToNative(prop.Value, null, Predicate);
//Skip empties
if (converted.Count <= 0)
continue;
if (converted.Count <= 0) continue;
var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform);
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);
}
}
return rootObject;
}
/// <summary>
///
///
/// </summary>
/// <param name="client"></param>
/// <param name="stream"></param>
@@ -389,28 +346,27 @@ namespace Speckle.ConnectorUnity.Components
[NotNullWhen(true)] out Client? client,
[NotNullWhen(true)] out Stream? stream,
[NotNullWhen(true)] out Commit? commit,
[NotNullWhen(false)] out string? error
)
[NotNullWhen(false)] out string? error)
{
Account? account = Account.Selected;
stream = Stream.Selected;
commit = Commit.Selected;
if (account == null)
{
error = "Selected Account is null";
client = null;
return false;
}
client = Account.Client ?? new Client(account);
client = Account.Client ?? new Client(account);
if (stream == null)
{
error = "Selected Stream is null";
return false;
}
if (commit == null)
if (commit == null)
{
error = "Selected Commit is null";
return false;
@@ -418,57 +374,52 @@ namespace Speckle.ConnectorUnity.Components
error = null;
return true;
}
/// <summary>
/// Fetches the commit preview for the currently selected commit
/// </summary>
/// <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>
/// <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(Action<Texture2D?> callback)
public Coroutine? GetPreviewImage(/*bool allAngles,*/ Action<Texture2D?> callback)
{
Account? account = Account.Selected;
if (account == null)
return null;
if (account == null) return null;
string? streamId = Stream.Selected?.id;
if (streamId == null)
return null;
if (streamId == null) return null;
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 authToken = account.token;
return StartCoroutine(Utils.Utils.GetImageRoutine(url, authToken, callback));
}
#if UNITY_EDITOR
[ContextMenu("Open Speckle Model in Browser")]
[ContextMenu("Open Speckle Stream in Browser")]
protected void OpenUrlInBrowser()
{
Uri url = GetSelectedUrl();
Application.OpenURL(url.ToString());
string url = GetSelectedUrl();
Application.OpenURL(url);
}
#endif
public Uri GetSelectedUrl()
public string GetSelectedUrl()
{
Account selectedAccount = Account.Selected!;
StreamWrapper sw =
new()
{
ServerUrl = selectedAccount.serverInfo.url,
StreamId = Stream.Selected!.id,
BranchName = Branch.Selected?.id,
CommitId = Commit.Selected?.id
};
sw.SetAccount(selectedAccount);
string serverUrl = Account.Selected!.serverInfo.url;
string? streamId = Stream.Selected?.id;
string? branchName = Branch.Selected?.name;
string? commitId = Commit.Selected?.id;
return sw.ToServerUri();
if (string.IsNullOrEmpty(streamId)) return serverUrl;
if (!string.IsNullOrEmpty(commitId)) return $"{serverUrl}/streams/{streamId}/commits/{commitId}";
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
return $"{serverUrl}/streams/{streamId}";
}
public void Awake()
{
Converter = GetComponent<RecursiveConverter>();
@@ -485,11 +436,12 @@ namespace Speckle.ConnectorUnity.Components
Stream.Initialise();
Branch.Initialise();
Commit.Initialise();
Commit.OnSelectionChange = () => OnCommitSelectionChange?.Invoke(Commit.Selected);
if (Account.Options is not { Count: > 0 } || forceRefresh)
Commit.OnSelectionChange =
() => OnCommitSelectionChange?.Invoke(Commit.Selected);
if(Account.Options is not {Length: > 0} || forceRefresh)
Account.RefreshOptions();
}
public void OnDisable()
{
CancellationTokenSource?.Cancel();
@@ -504,54 +456,36 @@ namespace Speckle.ConnectorUnity.Components
{
//pass
}
public void OnAfterDeserialize()
{
Initialise();
}
#region Deprecated members
[Obsolete("use " + nameof(ReceiveAndConvert_Routine), true)]
public IEnumerator ReceiveAndConvertRoutine(
SpeckleReceiver speckleReceiver,
string rootObjectName,
Action<Base>? beforeConvertCallback = null
)
[Obsolete("use " + nameof(ReceiveAndConvertRoutine), true)]
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action<Base>? beforeConvertCallback = null)
{
// 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);
Base? b = receiveOperation.Result;
if (b == null)
yield break;
if (b == null) yield break;
//NOTE: coroutine doesn't break for each catergory/object
GameObject go = ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
//NOTE: coroutine doesn't break for each catergory/object
ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
}
#endregion
}
[Serializable]
public sealed class CommitSelectionEvent : UnityEvent<Commit?> { }
[Serializable]
public sealed class BranchSelectionEvent : UnityEvent<Branch?> { }
[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> { }
[Serializable] public sealed class CommitSelectionEvent : UnityEvent<Commit?> { }
[Serializable] public sealed class BranchSelectionEvent : UnityEvent<Branch?> { }
[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> { }
}
@@ -23,183 +23,136 @@ namespace Speckle.ConnectorUnity.Components
{
[field: SerializeReference]
public AccountSelection Account { get; private set; }
[field: SerializeReference]
public StreamSelection Stream { get; private set; }
[field: SerializeReference]
public BranchSelection Branch { get; private set; }
public RecursiveConverter Converter { get; private set; }
[Header("Events")]
[HideInInspector]
public BranchSelectionEvent OnBranchSelectionChange;
[Obsolete("No longer used")]
[HideInInspector]
public ErrorActionEvent OnErrorAction;
[HideInInspector]
public OperationProgressEvent OnSendProgressAction;
#nullable enable
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
//TODO runtime sending
public async Task<string> SendDataAsync(Base data, bool createCommit)
{
CancellationTokenSource?.Cancel();
CancellationTokenSource?.Dispose();
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);
using ServerTransport transport = new(client.Account, stream.id);
ServerTransport transport = new ServerTransport(client.Account, stream.id);
transport.CancellationToken = CancellationTokenSource.Token;
return await SendDataAsync(
return await SendDataAsync(CancellationTokenSource.Token,
remoteTransport: transport,
data,
client,
branch.id,
createCommit,
dict => OnSendProgressAction.Invoke(dict),
CancellationTokenSource.Token
data: data,
client: client,
branchName: branch.name,
createCommit: createCommit,
onProgressAction: dict => OnSendProgressAction.Invoke(dict),
onErrorAction: (m, e) => OnErrorAction.Invoke(m, e)
);
}
/// <param name="remoteTransport">The transport to send to</param>
/// <param name="data">The data to send</param>
/// <param name="client">An authenticated Speckle Client</param>
/// <param name="branchId">The branch name or id</param>
/// <param name="createCommit">when <see langword="true"/> will call <see cref="Client.CommitCreate"/>, otherwise only the object data is sent</param>
/// <param name="onProgressAction">Called every progress tick of the <see cref="Operations.Send"/></param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>The id (hash) of the object sent</returns>
public static async Task<string> SendDataAsync(
public static async Task<string> SendDataAsync(CancellationToken cancellationToken,
ServerTransport remoteTransport,
Base data,
Client client,
string branchId,
string branchName,
bool createCommit,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
CancellationToken cancellationToken = default
)
Action<string, Exception>? onErrorAction = null)
{
string res = await Operations.Send(
data,
remoteTransport,
true,
onProgressAction,
cancellationToken
cancellationToken: cancellationToken,
new List<ITransport>{remoteTransport},
useDefaultCache: true,
disposeTransports: true,
onProgressAction: onProgressAction,
onErrorAction: onErrorAction
);
Analytics.TrackEvent(
client.Account,
Analytics.Events.Send,
new Dictionary<string, object>()
{
{ "mode", nameof(SpeckleSender) },
{ "hostPlatform", Application.platform.ToString() },
}
);
Analytics.TrackEvent(client.Account, Analytics.Events.Send, new Dictionary<string, object>()
{
{"mode", nameof(SpeckleSender)},
{"hostPlatform", Application.platform.ToString()},
});
if (createCommit && !cancellationToken.IsCancellationRequested)
{
string streamId = remoteTransport.StreamId;
string unityVer = $"Unity {Application.unityVersion.Substring(0, 6)}";
string unityVer = $"Unity {Application.unityVersion.Substring(0,6)}";
data.totalChildrenCount = data.GetTotalChildrenCount();
string commitMessage = $"Sent {data.totalChildrenCount} objects from {unityVer}";
string commitId = await CreateCommit(
data,
client,
streamId,
branchId,
res,
commitMessage,
cancellationToken
);
StreamWrapper sw =
new()
{
ServerUrl = client.ServerUrl,
StreamId = streamId,
BranchName = branchId,
CommitId = commitId,
};
sw.SetAccount(client.Account);
string url = sw.ToServerUri().GetLeftPart(UriPartial.Path);
string commitId = await CreateCommit(cancellationToken, data, client, streamId, branchName, res, commitMessage);
string url = $"{client.ServerUrl}/streams/{streamId}/commits/{commitId}";
Debug.Log($"Data successfully sent to <a href=\"{url}\">{url}</a>");
}
return res;
}
public static async Task<string> CreateCommit(
public static async Task<string> CreateCommit(CancellationToken cancellationToken,
Base data,
Client client,
string streamId,
string branchName,
string objectId,
string message,
CancellationToken cancellationToken
)
string message)
{
string commitId = await client.CommitCreate(
string commitId = await client.CommitCreate(cancellationToken,
new CommitCreateInput
{
streamId = streamId,
branchName = branchName,
objectId = objectId,
message = message,
sourceApplication = HostApplications.Unity.GetVersion(
CoreUtils.GetHostAppVersion()
),
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
totalChildrenCount = (int)data.totalChildrenCount,
},
cancellationToken
);
});
return commitId;
}
public bool GetSelection(
[NotNullWhen(true)] out Client? client,
[NotNullWhen(true)] out Stream? stream,
[NotNullWhen(true)] out Branch? branch,
[NotNullWhen(false)] out string? error
)
[NotNullWhen(false)] out string? error)
{
Account? account = Account.Selected;
stream = Stream.Selected;
branch = Branch.Selected;
if (account == null)
{
error = "Selected Account is null";
client = null;
return false;
}
client = Account.Client ?? new Client(account);
client = Account.Client ?? new Client(account);
if (stream == null)
{
error = "Selected Stream is null";
return false;
}
if (branch == null)
if (branch == null)
{
error = "Selected Branch is null";
return false;
@@ -207,7 +160,8 @@ namespace Speckle.ConnectorUnity.Components
error = null;
return true;
}
#if UNITY_EDITOR
[ContextMenu("Open Speckle Stream in Browser")]
protected void OpenUrlInBrowser()
@@ -216,26 +170,24 @@ namespace Speckle.ConnectorUnity.Components
Application.OpenURL(url);
}
#endif
public string GetSelectedUrl()
{
string serverUrl = Account.Selected!.serverInfo.url;
string? streamId = Stream.Selected?.id;
string? branchName = Branch.Selected?.name;
if (string.IsNullOrEmpty(streamId))
return serverUrl;
if (!string.IsNullOrEmpty(branchName))
return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
if (string.IsNullOrEmpty(streamId)) return serverUrl;
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
return $"{serverUrl}/streams/{streamId}";
}
public void Awake()
{
Initialise(true);
Converter = GetComponent<RecursiveConverter>();
}
protected void Initialise(bool forceRefresh = false)
{
CoreUtils.SetupInit();
@@ -246,47 +198,24 @@ namespace Speckle.ConnectorUnity.Components
Stream.Initialise();
Branch.Initialise();
Branch.OnSelectionChange = () => OnBranchSelectionChange?.Invoke(Branch.Selected);
if (Account.Options is not { Count: > 0 } || forceRefresh)
if(Account.Options is not {Length: > 0} || forceRefresh)
Account.RefreshOptions();
}
public void OnDestroy()
{
CancellationTokenSource?.Cancel();
CancellationTokenSource?.Dispose();
}
public void OnBeforeSerialize()
{
//pass
}
public void OnAfterDeserialize()
{
Initialise();
}
[Obsolete("use other overload")]
public static async Task<string> SendDataAsync(
CancellationToken cancellationToken,
ServerTransport remoteTransport,
Base data,
Client client,
string branchName,
bool createCommit,
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
Action<string, Exception>? onErrorAction = null
)
{
return await SendDataAsync(
remoteTransport,
data,
client,
branchName,
createCommit,
onProgressAction,
cancellationToken
);
}
}
}
@@ -1,50 +1,29 @@
using Objects.BuiltElements;
using UnityEngine;
namespace Objects.Converter.Unity
{
public partial class ConverterUnity
public partial class ConverterUnity
{
/// <summary>
/// Converts a Speckle View3D to a GameObject
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public GameObject View3DToNative(View3D speckleView)
{
[Tooltip("Enable/Disable the converting of Speckle View objects to Unity Cameras")]
public bool shouldConvertViews;
var go = new GameObject(speckleView.name);
var camera = go.AddComponent<Camera>();
camera.transform.position = VectorByCoordinates(speckleView.origin.x, speckleView.origin.y, speckleView.origin.z,
speckleView.origin.units);
camera.transform.forward = VectorByCoordinates(speckleView.forwardDirection.x, speckleView.forwardDirection.y,
speckleView.forwardDirection.z, speckleView.forwardDirection.units);
camera.transform.up = VectorByCoordinates(speckleView.upDirection.x, speckleView.upDirection.y,
speckleView.upDirection.z, speckleView.upDirection.units);
public GameObject View3DToNative(View3D speckleView)
{
var go = new GameObject(speckleView.name);
go.AddComponent<Camera>();
var matrix = View3DToMatrix(speckleView).transpose;
ApplyMatrixToTransform(go.transform, matrix);
AttachSpeckleProperties(go, speckleView.GetType(), () => speckleView.GetMembers());
return go;
}
protected Matrix4x4 View3DToMatrix(View3D view)
{
var sf = GetConversionFactor(view.units);
var tx = (float)(view.origin.x * sf);
var ty = (float)(view.origin.z * sf); //Y up -> Z up coordinate transformation
var tz = (float)(view.origin.y * sf);
var forward = new Vector3(
(float)view.forwardDirection.x,
(float)view.forwardDirection.z,
(float)view.forwardDirection.y
);
var up = new Vector3(
(float)view.upDirection.x,
(float)view.upDirection.z,
(float)view.upDirection.y
);
var right = Vector3.Cross(forward, up).normalized;
return new Matrix4x4(
new Vector4(right.x, up.x, forward.x, tx),
new Vector4(right.y, up.y, forward.y, ty),
new Vector4(right.z, up.z, forward.z, tz),
new Vector4(0, 0, 0, 1)
);
}
AttachSpeckleProperties(go, speckleView.GetType(),speckleView.GetMembers());
return go;
}
}
}
}
@@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Objects.Other;
using Speckle.ConnectorUnity;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.ConnectorUnity.Utils;
using Speckle.ConnectorUnity.Wrappers;
using Speckle.Core.Logging;
@@ -22,6 +24,7 @@ namespace Objects.Converter.Unity
{
public partial class ConverterUnity
{
#region helper methods
@@ -32,42 +35,32 @@ namespace Objects.Converter.Unity
public Vector3 VectorByCoordinates(double x, double y, double z, double scaleFactor = 1d)
{
// switch y and z //TODO is this correct? LH -> RH
return new Vector3(
(float)(x * scaleFactor),
(float)(z * scaleFactor),
(float)(y * scaleFactor)
);
return new Vector3((float) (x * scaleFactor), (float) (z * scaleFactor), (float) (y * scaleFactor));
}
public Vector3 VectorByCoordinates(double x, double y, double z, string units)
{
var f = GetConversionFactor(units);
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
return VectorByCoordinates(x, y, z, f);
}
public Vector3 VectorFromPoint(Point p) => VectorByCoordinates(p.x, p.y, p.z, p.units);
/// <param name="arr">flat list of x,y,z values</param>
/// <param name="units"></param>
/// <summary>
///
/// </summary>
/// <param name="arr"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">Length of <paramref name="arr"/> must be a multiple of 3</exception>
public Vector3[] ArrayToPoints(IReadOnlyList<double> arr, string units)
public Vector3[] ArrayToPoints(IList<double> arr, string units)
{
if (arr.Count % 3 != 0)
{
throw new ArgumentException(
"Array malformed: length not a multiple of 3",
nameof(arr)
);
}
if (arr.Count % 3 != 0) throw new Exception("Array malformed: length%3 != 0.");
Vector3[] points = new Vector3[arr.Count / 3];
double f = GetConversionFactor(units);
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
for (int i = 2, k = 0; i < arr.Count; i += 3)
{
points[k++] = VectorByCoordinates(arr[i - 2], arr[i - 1], arr[i], f);
}
return points;
}
@@ -76,12 +69,19 @@ namespace Objects.Converter.Unity
#region ToSpeckle
[Obsolete("", true)]
//TODO: more of these
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public virtual Point PointToSpeckle(Vector3 p)
{
//switch y and z
return new Point(p.x, p.z, p.y);
}
#endregion
@@ -90,8 +90,7 @@ namespace Objects.Converter.Unity
protected GameObject? NewPointBasedGameObject(Vector3[] points, string name)
{
if (points.Length == 0)
return null;
if (points.Length == 0) return null;
float pointDiameter = 1; //TODO: figure out how best to change this?
@@ -117,10 +116,11 @@ namespace Objects.Converter.Unity
{
Vector3 newPt = VectorByCoordinates(point.x, point.y, point.z, point.units);
var go = NewPointBasedGameObject(new[] { newPt, newPt }, point.speckle_type);
var go = NewPointBasedGameObject(new Vector3[] {newPt, newPt}, point.speckle_type);
return go;
}
/// <summary>
/// Converts a Speckle <paramref name="line"/> to a <see cref="GameObject"/> with a <see cref="LineRenderer"/>
/// </summary>
@@ -128,11 +128,7 @@ namespace Objects.Converter.Unity
/// <returns></returns>
public GameObject? LineToNative(Line line)
{
var points = new List<Vector3>
{
VectorFromPoint(line.start),
VectorFromPoint(line.end)
};
var points = new List<Vector3> {VectorFromPoint(line.start), VectorFromPoint(line.end)};
var go = NewPointBasedGameObject(points.ToArray(), line.speckle_type);
return go;
@@ -162,63 +158,37 @@ namespace Objects.Converter.Unity
var go = NewPointBasedGameObject(points, curve.speckle_type);
return go;
}
/// <summary>Gets all properties of <paramref name="o"/> except ignored ones. <br/>
/// Ignored properties include properties of <see cref="Base"/>
/// And some hardcoded ones that are likely to be converted (such as material, elements, and name)
/// </summary>
/// <param name="o">The speckle object to grab properties from</param>
/// <returns>The properties</returns>
/// <remarks>If you don't want to filter any properties, simply use <see cref="Base.GetMembers"/></remarks>
public static Dictionary<string, object?> GetProperties(Base o) =>
GetProperties(o, typeof(Base));
public Dictionary<string, object?> GetProperties(Base o) => GetProperties(o, typeof(Base));
/// <summary>
/// Gets all properties of <paramref name="o"/> except ignored ones.<br/>
/// Ignored properties include properties of <paramref name="excludeType"/>
/// And some hardcoded ones that are likely to be converted (such as material, elements, and name)
/// </summary>
/// <inheritdoc cref="GetProperties(Base)"/>
/// <param name="excludeType">A <see cref="Type"/> whose properties should be ignored</param>
public static Dictionary<string, object?> GetProperties(Base o, Type excludeType)
public Dictionary<string, object?> GetProperties(Base o, Type excludeType)
{
var excludeProps = new HashSet<string>(
excludeType
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(x => x.Name)
);
var excludeProps = new HashSet<string>(excludeType
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(x => x.Name));
foreach (string alias in DisplayValuePropertyAliases)
{
excludeProps.Add(alias);
}
excludeProps.Add("@Materials");
excludeProps.Add("@Views");
excludeProps.Add("renderMaterial");
excludeProps.Add("typedDefinition");
excludeProps.Add("definition");
excludeProps.Add("geometry");
excludeProps.Add("elements");
excludeProps.Add("transform");
excludeProps.Add("name");
//excludeProps.Add("tag");
excludeProps.Add("physicsLayer");
return o.GetMembers()
.Where(
x =>
!(
excludeProps.Contains(x.Key)
|| excludeProps.Contains(x.Key.TrimStart('@'))
)
)
.ToDictionary(x => x.Key, x => (object?)x.Value);
.Where(x => !(excludeProps.Contains(x.Key) || excludeProps.Contains(x.Key.TrimStart('@'))))
.ToDictionary(x => x.Key, x => (object?) x.Value);
}
#endregion
private Base CreateSpeckleObjectFromProperties(GameObject go)
{
@@ -226,7 +196,7 @@ namespace Objects.Converter.Unity
if (sd == null || sd.Data == null)
return new Base();
Base sobject = (Base)Activator.CreateInstance(sd.SpeckleType);
Base sobject = (Base) Activator.CreateInstance(sd.SpeckleType);
foreach (var key in sd.Data.Keys)
{
@@ -245,12 +215,11 @@ namespace Objects.Converter.Unity
public GameObject InstanceToNative(Instance instance)
{
if (instance.definition == null)
throw new ArgumentException("Definition was null", nameof(instance));
if (instance.definition == null) throw new ArgumentException("Definition was null", nameof(instance));
var defName = CoreUtils.GenerateObjectName(instance.definition);
// Check for existing converted object
if (LoadedAssets.TryGetObject(instance.definition, out GameObject? existingGo))
if(LoadedAssets.TryGetObject(instance.definition, out GameObject? existingGo))
{
var go = InstantiateCopy(existingGo);
go.name = defName;
@@ -260,33 +229,28 @@ namespace Objects.Converter.Unity
// Convert the block definition
GameObject native = new(defName);
List<SMesh> meshes = new();
List<Base> others = new();
var geometry = instance.definition is BlockDefinition b
? b.geometry
: GraphTraversal.TraverseMember(
new[]
{
instance.definition["elements"] ?? instance.definition["@elements"],
instance.definition["displayValue"] ?? instance.definition["@displayValue"],
}
);
: GraphTraversal.TraverseMember(new[]
{
instance.definition["elements"] ?? instance.definition["@elements"],
instance.definition["displayValue"] ?? instance.definition["@displayValue"],
});
foreach (Base geo in geometry)
{
if (geo is SMesh m)
meshes.Add(m);
else if (geo is IDisplayValue<List<SMesh>> s)
meshes.AddRange(s.displayValue);
else
others.Add(geo);
if (geo is SMesh m) meshes.Add(m);
else if (geo is IDisplayValue<List<SMesh>> s) meshes.AddRange(s.displayValue);
else others.Add(geo);
}
if (meshes.Any())
{
if (!TryGetMeshFromCache(instance.definition, meshes, out Mesh? nativeMesh, out _))
if(!TryGetMeshFromCache(instance.definition, meshes, out Mesh? nativeMesh, out _))
{
MeshToNativeMesh(meshes, out nativeMesh);
string name = CoreUtils.GenerateObjectName(instance.definition);
@@ -300,82 +264,80 @@ namespace Objects.Converter.Unity
foreach (Base child in others)
{
GameObject? c = ConvertToNativeGameObject(child);
if (c == null)
continue;
if (c == null) continue;
c.transform.SetParent(native.transform, false);
}
LoadedAssets.TrySaveObject(instance.definition, native);
TransformToNativeTransform(native.transform, instance.transform);
var instanceName =
CoreUtils.GetFriendlyObjectName(instance) != null
? CoreUtils.GenerateObjectName(instance)
: defName;
var instanceName = CoreUtils.GetFriendlyObjectName(instance) != null
? CoreUtils.GenerateObjectName(instance)
: defName;
native.name = instanceName;
AttachSpeckleProperties(native, instance.GetType(), () => GetProperties(instance));
return native;
}
private static GameObject InstantiateCopy(GameObject existingGo)
{
#if UNITY_EDITOR
GameObject? prefabInstance = null;
bool isPrefab =
PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab;
bool isPrefab = PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab;
if (isPrefab)
{
GameObject? prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(
existingGo
);
if (prefabAsset == null)
prefabAsset = existingGo;
prefabInstance = (GameObject)PrefabUtility.InstantiatePrefab(prefabAsset);
GameObject? prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(existingGo);
if (prefabAsset == null) prefabAsset = existingGo;
prefabInstance = (GameObject) PrefabUtility.InstantiatePrefab(prefabAsset);
}
if (prefabInstance != null)
return prefabInstance;
#endif
return Object.Instantiate(existingGo);
}
/// <summary>
/// Converts a 4x4 transformation matrix from Speckle's <see cref="Other.Transform"/> format,
/// to a Unity <see cref="Matrix4x4"/>. Applying Z -> Y up conversion, and applying units to the translation
/// to a Unity <see cref="Matrix4x4"/>. Applying Z -> Y up conversion, and applying units to the translation
/// </summary>
/// <param name="speckleTransform"></param>
/// <returns>Transformation matrix in Unity's coordinate system</returns>
public Matrix4x4 TransformToNativeMatrix(STransform speckleTransform)
{
var sf = GetConversionFactor(speckleTransform.units);
var sMatrix = speckleTransform.matrix;
var sf = Speckle.Core.Kits.Units.GetConversionFactor(speckleTransform.units, ModelUnits);
var smatrix = speckleTransform.matrix;
return new Matrix4x4
{
// Left (X -> X)
[0, 0] = (float)sMatrix.M11,
[2, 0] = (float)sMatrix.M21,
[1, 0] = (float)sMatrix.M31,
[3, 0] = (float)sMatrix.M41,
[0, 0] = smatrix.M11,
[2, 0] = smatrix.M21,
[1, 0] = smatrix.M31,
[3, 0] = smatrix.M41,
//Up (Z -> Y)
[0, 2] = (float)sMatrix.M12,
[2, 2] = (float)sMatrix.M22,
[1, 2] = (float)sMatrix.M32,
[3, 2] = (float)sMatrix.M42,
[0, 2] = smatrix.M12,
[2, 2] = smatrix.M22,
[1, 2] = smatrix.M32,
[3, 2] = smatrix.M42,
//Forwards (Y -> Z)
[0, 1] = (float)sMatrix.M13,
[2, 1] = (float)sMatrix.M23,
[1, 1] = (float)sMatrix.M33,
[3, 1] = (float)sMatrix.M43,
[0, 1] = smatrix.M13,
[2, 1] = smatrix.M23,
[1, 1] = smatrix.M33,
[3, 1] = smatrix.M43,
//Translation
[0, 3] = (float)(sMatrix.M14 * sf),
[2, 3] = (float)(sMatrix.M24 * sf),
[1, 3] = (float)(sMatrix.M34 * sf),
[3, 3] = (float)sMatrix.M44,
[0, 3] = (float) (smatrix.M14 * sf),
[2, 3] = (float) (smatrix.M24 * sf),
[1, 3] = (float) (smatrix.M34 * sf),
[3, 3] = smatrix.M44,
};
}
@@ -387,11 +349,16 @@ namespace Objects.Converter.Unity
protected static void ApplyMatrixToTransform(Transform transform, Matrix4x4 m)
{
transform.localScale = m.lossyScale; //doesn't work for non TRS, maybe we could fallback to squareSum approach (see TransformVectorized::SetFromMatrix in UE src)
transform.localScale =
m.lossyScale; //doesn't work for non TRS, maybe we could fallback to squareSum approach (see TransformVectorized::SetFromMatrix in UE src)
//We can't use m.rotation, as it gives us incorrect results (perhaps because of RH -> LH? or maybe our MatrixToNative is broken?)
transform.localRotation = Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1));
transform.localRotation = Quaternion.LookRotation(
m.GetColumn(2),
m.GetColumn(1)
);
transform.localPosition = m.GetPosition();
}
}
}
@@ -3,22 +3,32 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Speckle.ConnectorUnity.Utils;
using Objects.Other;
using Objects.Utils;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEngine;
using UnityEngine.Rendering;
using Material = UnityEngine.Material;
using Mesh = UnityEngine.Mesh;
using SMesh = Objects.Geometry.Mesh;
using SColor = System.Drawing.Color;
using Transform = UnityEngine.Transform;
using STransform = Objects.Other.Transform;
#nullable enable
namespace Objects.Converter.Unity
{
public partial class ConverterUnity
{
#region ToSpeckle
protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
#region ToSpeckle
public virtual List<SMesh>? MeshToSpeckle(MeshFilter meshFilter)
{
#if UNITY_EDITOR
@@ -28,8 +38,7 @@ namespace Objects.Converter.Unity
Material[]? materials = meshFilter.GetComponent<Renderer>()?.materials;
var nativeMesh = meshFilter.mesh;
#endif
if (nativeMesh == null)
return null;
if (nativeMesh == null) return null;
List<SMesh> convertedMeshes = new List<SMesh>(nativeMesh.subMeshCount);
for (int i = 0; i < nativeMesh.subMeshCount; i++)
@@ -42,50 +51,31 @@ namespace Objects.Converter.Unity
// //TODO convert as pointcloud
// continue;
case MeshTopology.Triangles:
converted = SubMeshToSpeckle(
nativeMesh,
meshFilter.transform,
subMesh,
i,
3
);
converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 3);
convertedMeshes.Add(converted);
break;
case MeshTopology.Quads:
converted = SubMeshToSpeckle(
nativeMesh,
meshFilter.transform,
subMesh,
i,
4
);
converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 4);
convertedMeshes.Add(converted);
break;
default:
Debug.LogError(
$"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored."
);
$"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored.");
continue;
}
if (materials == null || materials.Length <= i)
continue;
if (materials == null || materials.Length <= i) continue;
Material mat = materials[i];
if (mat != null)
converted["renderMaterial"] = MaterialToSpeckle(mat);
if (mat != null) converted["renderMaterial"] = MaterialToSpeckle(mat);
}
return convertedMeshes;
}
protected virtual SMesh SubMeshToSpeckle(
Mesh nativeMesh,
Transform instanceTransform,
SubMeshDescriptor subMesh,
int subMeshIndex,
int faceN
)
protected virtual SMesh SubMeshToSpeckle(Mesh nativeMesh, Transform instanceTransform,
SubMeshDescriptor subMesh, int subMeshIndex, int faceN)
{
var nFaces = nativeMesh.GetIndices(subMeshIndex, true);
int numFaces = nFaces.Length / faceN;
@@ -135,7 +125,7 @@ namespace Objects.Converter.Unity
}
var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray();
;
List<int> sColors = new List<int>(nColors.Length);
sColors.AddRange(nColors.Select(c => c.ToIntColor()));
@@ -159,6 +149,56 @@ namespace Objects.Converter.Unity
return convertedMesh;
}
/// <summary>
/// List of officially supported shaders. Will attempt to convert shaders not on this list, but will throw warning.
/// </summary>
protected static HashSet<string> SupportedShadersToSpeckle = new()
{
"Legacy Shaders/Transparent/Diffuse", "Standard"
};
public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
{
//Warning message for unknown shaders
if (!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name))
Debug.LogWarning(
$"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect");
var color = nativeMaterial.color;
var opacity = 1f;
if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
{
opacity = color.a;
color.a = 255;
}
var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
? nativeMaterial.GetColor(EmissionColor).ToIntColor()
: SColor.Black.ToArgb();
var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
: $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
var metalness = nativeMaterial.HasProperty(Metallic)
? nativeMaterial.GetFloat(Metallic)
: 0;
var roughness = nativeMaterial.HasProperty(Glossiness)
? 1 - nativeMaterial.GetFloat(Glossiness)
: 1;
return new RenderMaterial
{
name = materialName,
diffuse = color.ToIntColor(),
opacity = opacity,
metalness = metalness,
roughness = roughness,
emissive = emissive,
};
}
#endregion
@@ -172,8 +212,7 @@ namespace Objects.Converter.Unity
/// <returns>A <see cref="GameObject"/> with the converted <see cref="UnityEngine.Mesh"/>, <see cref="MeshFilter"/>, and <see cref="MeshRenderer"/></returns>
public GameObject MeshesToNative(Base element, IReadOnlyCollection<SMesh> meshes)
{
if (!meshes.Any())
throw new ArgumentException("Expected at least one Mesh", nameof(meshes));
if (!meshes.Any()) throw new ArgumentException("Expected at least one Mesh", nameof(meshes));
Material[] nativeMaterials = RenderMaterialsToNative(meshes);
@@ -185,7 +224,7 @@ namespace Objects.Converter.Unity
nativeMesh.name = name;
LoadedAssets.TrySaveObject(element, nativeMesh);
}
var go = new GameObject();
go.transform.position = center;
go.SafeMeshSet(nativeMesh, nativeMaterials);
@@ -193,6 +232,7 @@ namespace Objects.Converter.Unity
return go;
}
/// <summary>
/// Converts <paramref name="speckleMesh"/> to a <see cref="GameObject"/> with a <see cref="MeshRenderer"/>
/// </summary>
@@ -202,8 +242,8 @@ namespace Objects.Converter.Unity
{
if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
throw new ArgumentException("mesh data was empty", nameof(speckleMesh));
GameObject converted = MeshesToNative(speckleMesh, new[] { speckleMesh });
GameObject converted = MeshesToNative(speckleMesh, new[] {speckleMesh});
// Raw meshes shouldn't have dynamic props to attach
//if (converted != null) AttachSpeckleProperties(converted,speckleMesh.GetType(), GetProperties(speckleMesh, typeof(Mesh)));
@@ -211,18 +251,17 @@ namespace Objects.Converter.Unity
return converted;
}
protected bool TryGetMeshFromCache(
Base element,
IReadOnlyCollection<SMesh> meshes,
[NotNullWhen(true)] out Mesh? nativeMesh,
out Vector3 center
)
protected bool TryGetMeshFromCache(Base element, IReadOnlyCollection<SMesh> meshes, [NotNullWhen(true)] out Mesh? nativeMesh, out Vector3 center)
{
if (LoadedAssets.TryGetObject(element, out Mesh? existing))
{
nativeMesh = existing;
//todo This is pretty inefficient, having to convert the mesh data anyway just to get the center... eek
MeshDataToNative(meshes, out List<Vector3> verts, out _, out _, out _);
MeshDataToNative(meshes,
out List<Vector3> verts,
out _,
out _,
out _);
center = CalculateBounds(verts).center;
return true;
}
@@ -231,33 +270,30 @@ namespace Objects.Converter.Unity
center = default;
return false;
}
/// <summary>
/// Converts Speckle <see cref="SMesh"/>s as a native <see cref="Mesh"/> object
/// Converts Speckle <see cref="SMesh"/>s as a native <see cref="Mesh"/> object
/// </summary>
/// <param name="meshes">meshes to be converted as SubMeshes</param>
/// <param name="nativeMesh">The converted native mesh</param>
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes, out Mesh nativeMesh) =>
MeshToNativeMesh(meshes, out nativeMesh, out _, false);
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes, out Mesh nativeMesh)
=> MeshToNativeMesh(meshes, out nativeMesh, out _, false);
/// <inheritdoc cref="MeshToNativeMesh(IReadOnlyCollection{Objects.Geometry.Mesh},out Mesh)"/>
/// <param name="recenterVerts">when true, will recenter vertices</param>
/// <param name="center">Center position for the mesh</param>
public void MeshToNativeMesh(
IReadOnlyCollection<SMesh> meshes,
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes,
out Mesh nativeMesh,
out Vector3 center,
bool recenterVerts = true
)
bool recenterVerts = true)
{
MeshDataToNative(
meshes,
MeshDataToNative(meshes,
out List<Vector3> verts,
out List<Vector2> uvs,
out List<Color> vertexColors,
out List<List<int>> subMeshes
);
out List<List<int>> subMeshes);
Debug.Assert(verts.Count >= 0);
center = recenterVerts ? RecenterVertices(verts) : Vector3.zero;
@@ -268,6 +304,7 @@ namespace Objects.Converter.Unity
nativeMesh.SetUVs(0, uvs);
nativeMesh.SetColors(vertexColors);
int j = 0;
foreach (var subMeshTriangles in subMeshes)
{
@@ -283,14 +320,12 @@ namespace Objects.Converter.Unity
nativeMesh.RecalculateNormals();
nativeMesh.RecalculateTangents();
}
public void MeshDataToNative(
IReadOnlyCollection<SMesh> meshes,
public void MeshDataToNative(IReadOnlyCollection<SMesh> meshes,
out List<Vector3> verts,
out List<Vector2> uvs,
out List<Color> vertexColors,
out List<List<int>> subMeshes
)
out List<List<int>> subMeshes)
{
verts = new List<Vector3>();
uvs = new List<Vector2>();
@@ -299,22 +334,17 @@ namespace Objects.Converter.Unity
foreach (SMesh m in meshes)
{
if (m.vertices.Count == 0 || m.faces.Count == 0)
continue;
if (m.vertices.Count == 0 || m.faces.Count == 0) continue;
List<int> tris = new List<int>();
SubmeshToNative(m, verts, tris, uvs, vertexColors);
subMeshes.Add(tris);
}
}
protected void SubmeshToNative(
SMesh speckleMesh,
List<Vector3> verts,
List<int> tris,
List<Vector2> texCoords,
List<Color> vertexColors
)
protected void SubmeshToNative(SMesh speckleMesh, List<Vector3> verts, List<int> tris, List<Vector2> texCoords,
List<Color> vertexColors)
{
speckleMesh.AlignVerticesWithTexCoordsByIndex();
speckleMesh.TriangulateMesh();
@@ -328,8 +358,7 @@ namespace Objects.Converter.Unity
bool hasValidUVs = speckleMesh.TextureCoordinatesCount == speckleMesh.VerticesCount;
if (speckleMesh.textureCoordinates.Count > 0 && !hasValidUVs)
Debug.LogWarning(
$"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}"
);
$"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}");
if (hasValidUVs)
{
@@ -337,20 +366,14 @@ namespace Objects.Converter.Unity
for (int j = 0; j < speckleMesh.TextureCoordinatesCount; j++)
{
var (u, v) = speckleMesh.GetTextureCoordinate(j);
texCoords.Add(new Vector2((float)u, (float)v));
texCoords.Add(new Vector2((float) u, (float) v));
}
}
else if (speckleMesh.bbox != null)
{
// Attempt to generate some crude UV coordinates using bbox
texCoords.AddRange(
GenerateUV(
indexOffset,
verts,
(float)speckleMesh.bbox.xSize.Length,
(float)speckleMesh.bbox.ySize.Length
)
);
texCoords.AddRange(GenerateUV(indexOffset, verts, (float) speckleMesh.bbox.xSize.Length,
(float) speckleMesh.bbox.ySize.Length));
}
else
{
@@ -368,13 +391,12 @@ namespace Objects.Converter.Unity
{
//TODO what if only some submeshes have colors?
Debug.LogWarning(
$"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}"
);
$"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}");
}
}
// Convert faces
tris.Capacity += (int)(speckleMesh.faces.Count / 4f) * 3;
tris.Capacity += (int) (speckleMesh.faces.Count / 4f) * 3;
for (int i = 0; i < speckleMesh.faces.Count; i += 4)
{
@@ -385,46 +407,89 @@ namespace Objects.Converter.Unity
}
}
protected static IEnumerable<Vector2> GenerateUV(
int indexOffset,
IReadOnlyList<Vector3> verts,
float xSize,
float ySize
)
protected static IEnumerable<Vector2> GenerateUV(int indexOffset, IReadOnlyList<Vector3> verts, float xSize,
float ySize)
{
var uv = new Vector2[verts.Count - indexOffset];
for (int i = 0; i < verts.Count - indexOffset; i++)
{
var vert = verts[i];
uv[i] = new Vector2(vert.x / xSize, vert.y / ySize);
}
return uv;
}
protected static Vector3 RecenterVertices(IList<Vector3> vertices)
{
if (!vertices.Any())
return Vector3.zero;
if (!vertices.Any()) return Vector3.zero;
Bounds meshBounds = CalculateBounds(vertices);
for (int i = 0; i < vertices.Count; i++)
vertices[i] -= meshBounds.center;
return meshBounds.center;
}
protected static Bounds CalculateBounds(IList<Vector3> points)
{
Bounds b = new Bounds { center = points[0] };
Bounds b = new Bounds {center = points[0]};
foreach (var p in points)
b.Encapsulate(p);
return b;
}
public Material[] RenderMaterialsToNative(IEnumerable<SMesh> meshes)
{
return meshes.Select(m => RenderMaterialToNative(m["renderMaterial"] as RenderMaterial)).ToArray();
}
//Just used as cache key for the default (null) material
private static RenderMaterial defaultMaterialPlaceholder = new(){id="defaultMaterial"};
public Material RenderMaterialToNative(RenderMaterial? renderMaterial)
{
//todo support more complex materials
// 1. If no renderMaterial was passed, use default material
if (renderMaterial == null)
{
if (!LoadedAssets.TryGetObject(defaultMaterialPlaceholder, out Material? defaultMaterial))
{
defaultMaterial = new Material(Shader.Find("Standard"));
LoadedAssets.TrySaveObject(defaultMaterialPlaceholder, defaultMaterial);
}
return defaultMaterial;
}
// 2. Try get existing/override material from asset cache
if (LoadedAssets.TryGetObject(renderMaterial, out Material? loadedMaterial)) return loadedMaterial;
// 3. Otherwise, convert fresh!
string shaderName = renderMaterial.opacity < 1 ? "Transparent/Diffuse" : "Standard";
Shader shader = Shader.Find(shaderName);
var mat = new Material(shader);
var c = renderMaterial.diffuse.ToUnityColor();
mat.color = new Color(c.r, c.g, c.b, (float) renderMaterial.opacity);
mat.name = CoreUtils.GenerateObjectName(renderMaterial);
mat.SetFloat(Metallic, (float) renderMaterial.metalness);
mat.SetFloat(Glossiness, 1 - (float) renderMaterial.roughness);
if (renderMaterial.emissive != SColor.Black.ToArgb()) mat.EnableKeyword("_EMISSION");
mat.SetColor(EmissionColor, renderMaterial.emissive.ToUnityColor());
LoadedAssets.TrySaveObject(renderMaterial, mat);
return mat;
}
#endregion
}
}
@@ -1,163 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Speckle.ConnectorUnity.Utils;
using Speckle.Core.Helpers;
using Objects.Other;
using UnityEngine;
using Material = UnityEngine.Material;
using SMesh = Objects.Geometry.Mesh;
using SColor = System.Drawing.Color;
namespace Objects.Converter.Unity
{
public partial class ConverterUnity
{
protected const string DefaultShader = "Standard";
protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
[field: SerializeField]
[field: Tooltip("The shader to use when converting opaque RenderMaterials to native")]
public Shader OpaqueMaterialShader { get; set; }
[field: SerializeField]
[field: Tooltip("The shader to use when converting non-opaque RenderMaterials to native")]
public Shader TranslucentMaterialShader { get; set; }
#nullable enable
/// <summary>
/// List of officially supported shaders. We will attempt to convert shaders not on this list, but will throw warning.
/// </summary>
protected static HashSet<string> SupportedShadersToSpeckle =
new() { "Legacy Shaders/Transparent/Diffuse", "Standard" };
#region ToNative
public Material[] RenderMaterialsToNative(IEnumerable<SMesh> meshes)
{
return meshes
.Select(m => RenderMaterialToNative(m["renderMaterial"] as RenderMaterial))
.ToArray();
}
//Just used as cache key for the default (null) material
private static readonly RenderMaterial DefaultMaterialPlaceholder =
new() { id = "defaultMaterial" };
public virtual Material RenderMaterialToNative(RenderMaterial? renderMaterial)
{
//todo support more complex materials
// 1. If no renderMaterial was passed, use default material
if (renderMaterial == null)
{
if (
!LoadedAssets.TryGetObject(
DefaultMaterialPlaceholder,
out Material? defaultMaterial
)
)
{
defaultMaterial = new Material(OpaqueMaterialShader);
LoadedAssets.TrySaveObject(DefaultMaterialPlaceholder, defaultMaterial);
}
return defaultMaterial;
}
// 2. Try get existing/override material from asset cache
if (LoadedAssets.TryGetObject(renderMaterial, out Material? loadedMaterial))
return loadedMaterial;
// 3. Otherwise, convert fresh!
string name = CoreUtils.GenerateObjectName(renderMaterial);
Color diffuse = renderMaterial.diffuse.ToUnityColor();
bool isOpaque = Math.Abs(renderMaterial.opacity - 1d) < Constants.EPS;
Color color = new(diffuse.r, diffuse.g, diffuse.b, (float)renderMaterial.opacity);
float metalic = (float)renderMaterial.metalness;
float gloss = 1f - (float)renderMaterial.roughness;
bool hasEmission = renderMaterial.emissive != SColor.Black.ToArgb();
Color emission = renderMaterial.emissive.ToUnityColor();
Shader shader =
renderMaterial.opacity < 1 ? TranslucentMaterialShader : OpaqueMaterialShader;
var mat = new Material(shader);
mat.color = color;
mat.name = name;
mat.SetFloat(Metallic, metalic);
mat.SetFloat(Glossiness, gloss);
mat.SetColor(EmissionColor, emission);
if (hasEmission)
mat.EnableKeyword("_EMISSION");
if (!isOpaque)
{
if (shader.name == "Standard")
{
ShaderHelpers.SetupMaterialWithBlendMode_Standard(
mat,
ShaderHelpers.BlendMode.Transparent,
true
);
}
else if (shader.name == "Lit") //URP lit
{ }
}
LoadedAssets.TrySaveObject(renderMaterial, mat);
return mat;
}
#endregion
#region ToSpeckle
public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
{
//Warning message for unknown shaders
if (!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name))
Debug.LogWarning(
$"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect"
);
var color = nativeMaterial.color;
var opacity = 1f;
if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
{
opacity = color.a;
color.a = 255;
}
var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
? nativeMaterial.GetColor(EmissionColor).ToIntColor()
: SColor.Black.ToArgb();
var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
: $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
var metalness = nativeMaterial.HasProperty(Metallic)
? nativeMaterial.GetFloat(Metallic)
: 0;
var roughness = nativeMaterial.HasProperty(Glossiness)
? 1 - nativeMaterial.GetFloat(Glossiness)
: 1;
return new RenderMaterial
{
name = materialName,
diffuse = color.ToIntColor(),
opacity = opacity,
metalness = metalness,
roughness = roughness,
emissive = emissive,
};
}
#endregion
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8f91633e63e64685a6b2a4fd609312a0
timeCreated: 1689085502
@@ -1,22 +1,23 @@
using UnityEngine;
using Objects.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Mesh = Objects.Geometry.Mesh;
namespace Objects.Converter.Unity
{
public partial class ConverterUnity
public partial class ConverterUnity
{
public string ModelUnits = Speckle.Core.Kits.Units.Meters; //the default Unity units are meters
private double ScaleToNative(double value, string units)
{
[field: SerializeField]
[field: Tooltip("The units used by conversion functions")]
public string ModelUnits { get; set; } = Speckle.Core.Kits.Units.Meters; //the default Unity units are meters
private double ScaleToNative(double value, string units)
{
var f = GetConversionFactor(units);
return value * f;
}
private double GetConversionFactor(string units)
{
return Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
}
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
return value * f;
}
}
}
@@ -5,6 +5,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Objects.BuiltElements;
using Objects.Organization;
using Objects.Other;
using Speckle.ConnectorUnity.Utils;
using Speckle.ConnectorUnity.NativeCache;
@@ -15,14 +16,8 @@ using Object = UnityEngine.Object;
namespace Objects.Converter.Unity
{
[Serializable]
public partial class ConverterUnity : ISpeckleConverter
{
[Tooltip(
"Enable/Disable attaching non-converted properties to received objects. Disabling this will lead to lighter weight objects that serialize much faster."
)]
public bool shouldAttachProperties = true;
#region implemented methods
public void SetConverterSettings(object settings) => throw new NotImplementedException();
@@ -32,11 +27,10 @@ namespace Objects.Converter.Unity
public string Author => "Speckle";
public string WebsiteOrEmail => "https://speckle.systems";
public ProgressReport Report => throw new NotImplementedException();
public ProgressReport Report { get; }
public ReceiveMode ReceiveMode { get; set; }
public IEnumerable<string> GetServicedApplications() =>
new[] { HostApplications.Unity.Name };
public IEnumerable<string> GetServicedApplications() => new string[] {HostApplications.Unity.Name};
public AbstractNativeCache LoadedAssets { get; private set; }
@@ -44,23 +38,13 @@ namespace Objects.Converter.Unity
{
if (doc is not AbstractNativeCache context)
throw new ArgumentException(
$"Expected {nameof(doc)} to be of type {typeof(Dictionary<string, Object>)}",
nameof(doc)
);
$"Expected {nameof(doc)} to be of type {typeof(Dictionary<string, Object>)}", nameof(doc));
LoadedAssets = context;
if (OpaqueMaterialShader == null)
OpaqueMaterialShader = Shader.Find(DefaultShader);
if (TranslucentMaterialShader == null)
TranslucentMaterialShader = Shader.Find(DefaultShader);
}
public void SetContextObjects(List<ApplicationObject> objects) =>
throw new NotImplementedException();
public void SetContextObjects(List<ApplicationObject> objects) => throw new NotImplementedException();
public void SetPreviousContextObjects(List<ApplicationObject> objects) =>
throw new NotImplementedException();
public void SetPreviousContextObjects(List<ApplicationObject> objects) => throw new NotImplementedException();
#nullable enable
public object? ConvertToNative(Base @object) => ConvertToNativeGameObject(@object);
@@ -68,9 +52,7 @@ namespace Objects.Converter.Unity
public Base ConvertToSpeckle(object @object)
{
if (!(@object is GameObject go))
throw new NotSupportedException(
$"Cannot convert object of type {@object.GetType()} to Speckle"
);
throw new NotSupportedException($"Cannot convert object of type {@object.GetType()} to Speckle");
return ConvertGameObjectToSpeckle(go);
}
@@ -106,16 +88,14 @@ namespace Objects.Converter.Unity
}
catch (Exception e)
{
Debug.LogError(
$"Failed to convert {component.GetType()} component\n{e}",
component
);
Debug.LogError($"Failed to convert {component.GetType()} component\n{e}", component);
}
}
return speckleObject;
}
public GameObject? ConvertToNativeGameObject(Base speckleObject)
{
switch (speckleObject)
@@ -143,11 +123,7 @@ namespace Objects.Converter.Unity
if (element != null)
{
if (!speckleObject.speckle_type.Contains("Objects.Geometry"))
AttachSpeckleProperties(
element,
speckleObject.GetType(),
() => GetProperties(speckleObject)
);
AttachSpeckleProperties(element, speckleObject.GetType(), GetProperties(speckleObject));
return element;
}
@@ -156,8 +132,9 @@ namespace Objects.Converter.Unity
}
}
public static IList<string> DisplayValuePropertyAliases { get; set; } =
new[] { "displayValue", "@displayValue", "displayMesh", "@displayMesh" };
public IList<string> DisplayValuePropertyAliases { get; set; } =
new[] {"displayValue", "@displayValue", "displayMesh", "@displayMesh"};
public GameObject? DisplayValueToNative(Base @object)
{
@@ -169,7 +146,7 @@ namespace Objects.Converter.Unity
case IList dvCollection:
return MeshesToNative(@object, dvCollection.OfType<Mesh>().ToList());
case Mesh dvMesh:
return MeshesToNative(@object, new[] { dvMesh });
return MeshesToNative(@object, new[] {dvMesh});
case Base dvBase:
return ConvertToNativeGameObject(dvBase);
}
@@ -177,20 +154,16 @@ namespace Objects.Converter.Unity
return null;
}
private SpeckleProperties? AttachSpeckleProperties(
GameObject go,
Type speckleType,
Func<IDictionary<string, object?>> properties
)
private SpeckleProperties AttachSpeckleProperties(GameObject go, Type speckleType,
IDictionary<string, object?> properties)
{
if (!shouldAttachProperties)
return null;
var sd = go.AddComponent<SpeckleProperties>();
sd.Data = properties.Invoke();
sd.Data = properties;
sd.SpeckleType = speckleType;
return sd;
}
public List<Base> ConvertToSpeckle(List<object> objects)
{
@@ -202,18 +175,6 @@ namespace Objects.Converter.Unity
return objects.Select(x => ConvertToNative(x)).ToList();
}
public object ConvertToNativeDisplayable(Base @object)
{
throw new NotImplementedException(
$"{nameof(ConvertToNativeDisplayable)} is not implemented by this converter, use {nameof(ConvertToNative)} instead"
);
}
public bool CanConvertToNativeDisplayable(Base @object)
{
throw new NotImplementedException();
}
public bool CanConvertToSpeckle(object @object)
{
switch (@object)
@@ -227,19 +188,17 @@ namespace Objects.Converter.Unity
public GameObject CollectionToNative(Collection collection)
{
var name =
collection.name
?? $"{collection.collectionType} -- {collection.applicationId ?? collection.id}";
var name = collection.name ?? $"{collection.collectionType} -- {collection.applicationId ?? collection.id}";
var go = new GameObject(name);
AttachSpeckleProperties(go, collection.GetType(), () => GetProperties(collection));
AttachSpeckleProperties(go, collection.GetType(), GetProperties(collection));
if (name == "Rooms")
{
go.SetActive(false);
}
return go;
}
public bool CanConvertToNative(Base @object)
{
switch (@object)
@@ -254,8 +213,10 @@ namespace Objects.Converter.Unity
// return true;
// case View2D:
// return false;
case View3D _:
return shouldConvertViews;
// case View _:
// return true;
case Model:
return false; //This allows us to traverse older commits pre-collections
case Collection:
return true;
case Mesh:
@@ -271,7 +232,7 @@ namespace Objects.Converter.Unity
if (@object[alias] is IList)
return true;
}
return false;
}
}
@@ -23,9 +23,6 @@ PluginImporter:
Exclude WebGL: 0
Exclude Win: 0
Exclude Win64: 0
Exclude WindowsStoreApps: 0
Exclude iOS: 0
Exclude tvOS: 0
- first:
Android: Android
second:
@@ -77,30 +74,9 @@ PluginImporter:
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
enabled: 0
settings:
CPU: AnyCPU
DontProcess: false
PlaceholderPath:
SDK: AnySDK
ScriptingBackend: DotNet
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
- first:
tvOS: tvOS
second:
enabled: 1
settings:
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
@@ -1,33 +0,0 @@
fileFormatVersion: 2
guid: 6faf057a8f503f44287177f8e2094e3e
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -9,69 +9,67 @@
".NETStandard,Version=v2.0/": {
"SpeckleCore2/2.0.999-local": {
"dependencies": {
"GraphQL.Client": "6.0.0",
"GraphQL.Client": "5.1.1",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Data.Sqlite": "7.0.3",
"NETStandard.Library": "2.0.3",
"Polly": "7.2.3",
"Polly.Contrib.WaitAndRetry": "1.1.1",
"Polly.Extensions.Http": "3.0.0",
"Sentry": "3.33.0",
"Sentry.Serilog": "3.33.0",
"Sentry": "3.29.0",
"Sentry.Serilog": "3.29.0",
"Serilog": "2.12.0",
"Serilog.Enrichers.ClientInfo": "1.3.0",
"Serilog.Enrichers.ClientInfo": "1.2.0",
"Serilog.Enrichers.GlobalLogContext": "3.0.0",
"Serilog.Exceptions": "8.4.0",
"Serilog.Sinks.Console": "4.1.0",
"Serilog.Sinks.Seq": "5.2.2",
"SerilogTimings": "3.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"System.DoubleNumerics": "3.1.3"
"Speckle.Newtonsoft.Json": "13.0.2"
},
"runtime": {
"SpeckleCore2.dll": {}
}
},
"GraphQL.Client/6.0.0": {
"GraphQL.Client/5.1.1": {
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0",
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
"GraphQL.Client.Abstractions": "5.1.1",
"GraphQL.Client.Abstractions.Websocket": "5.1.1",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Client.Abstractions/6.0.0": {
"GraphQL.Client.Abstractions/5.1.1": {
"dependencies": {
"GraphQL.Primitives": "6.0.0"
"GraphQL.Primitives": "5.1.1"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0"
"GraphQL.Client.Abstractions": "5.1.1"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.Abstractions.Websocket.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Primitives/6.0.0": {
"GraphQL.Primitives/5.1.1": {
"runtime": {
"lib/netstandard2.0/GraphQL.Primitives.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
@@ -144,20 +142,20 @@
}
}
},
"Microsoft.Data.Sqlite/7.0.5": {
"Microsoft.Data.Sqlite/7.0.3": {
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"Microsoft.Data.Sqlite.Core": "7.0.3",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core/7.0.5": {
"Microsoft.Data.Sqlite.Core/7.0.3": {
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Data.Sqlite.dll": {
"assemblyVersion": "7.0.5.0",
"fileVersion": "7.0.523.16503"
"assemblyVersion": "7.0.3.0",
"fileVersion": "7.0.323.6302"
}
}
},
@@ -247,27 +245,30 @@
}
}
},
"Sentry/3.33.0": {
"Sentry/3.29.0": {
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Buffers": "4.5.1",
"System.Reflection.Metadata": "5.0.0",
"System.Text.Json": "5.0.2"
"System.Text.Json": "5.0.2",
"System.Threading.Tasks.Extensions": "4.5.4"
},
"runtime": {
"lib/netstandard2.0/Sentry.dll": {
"assemblyVersion": "3.33.0.0",
"fileVersion": "3.33.0.0"
"assemblyVersion": "3.29.0.0",
"fileVersion": "3.29.0.0"
}
}
},
"Sentry.Serilog/3.33.0": {
"Sentry.Serilog/3.29.0": {
"dependencies": {
"Sentry": "3.33.0",
"Sentry": "3.29.0",
"Serilog": "2.12.0"
},
"runtime": {
"lib/netstandard2.0/Sentry.Serilog.dll": {
"assemblyVersion": "3.33.0.0",
"fileVersion": "3.33.0.0"
"assemblyVersion": "3.29.0.0",
"fileVersion": "3.29.0.0"
}
}
},
@@ -279,7 +280,7 @@
}
}
},
"Serilog.Enrichers.ClientInfo/1.3.0": {
"Serilog.Enrichers.ClientInfo/1.2.0": {
"dependencies": {
"Microsoft.AspNetCore.Http": "2.1.1",
"Serilog": "2.12.0"
@@ -372,17 +373,6 @@
}
}
},
"SerilogTimings/3.0.1": {
"dependencies": {
"Serilog": "2.12.0"
},
"runtime": {
"lib/netstandard2.0/SerilogTimings.dll": {
"assemblyVersion": "3.0.1.0",
"fileVersion": "3.0.1.0"
}
}
},
"Speckle.Newtonsoft.Json/13.0.2": {
"runtime": {
"lib/netstandard2.0/Speckle.Newtonsoft.Json.dll": {
@@ -445,17 +435,6 @@
}
}
},
"System.DoubleNumerics/3.1.3": {
"dependencies": {
"NETStandard.Library": "2.0.3"
},
"runtime": {
"lib/netstandard1.3/System.DoubleNumerics.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
},
"System.Memory/4.5.4": {
"dependencies": {
"System.Buffers": "4.5.1",
@@ -581,33 +560,33 @@
"serviceable": false,
"sha512": ""
},
"GraphQL.Client/6.0.0": {
"GraphQL.Client/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
"path": "graphql.client/6.0.0",
"hashPath": "graphql.client.6.0.0.nupkg.sha512"
"sha512": "sha512-6bfM9qU4AMcFWm4BHd2M6LE5+rLtK47/+VRtypggwb9appC8sbF58ytVBVOKpqKhKpmZERfPLGJap8O/FH3w5w==",
"path": "graphql.client/5.1.1",
"hashPath": "graphql.client.5.1.1.nupkg.sha512"
},
"GraphQL.Client.Abstractions/6.0.0": {
"GraphQL.Client.Abstractions/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
"path": "graphql.client.abstractions/6.0.0",
"hashPath": "graphql.client.abstractions.6.0.0.nupkg.sha512"
"sha512": "sha512-/znG7Lcz3rzG9VSCq+V2ARb63c/uIs8idGOvXyltZ32Wy570GX/I8HNUIZ1yDThmQRJ5KOGSd9Mzk37lFg49rg==",
"path": "graphql.client.abstractions/5.1.1",
"hashPath": "graphql.client.abstractions.5.1.1.nupkg.sha512"
},
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
"path": "graphql.client.abstractions.websocket/6.0.0",
"hashPath": "graphql.client.abstractions.websocket.6.0.0.nupkg.sha512"
"sha512": "sha512-n1gU3GlqJ0jQceb/VEEr4c0D2vpQc5AtDwthK89+yX7VpzlhJKqE5B4RJwx//Jb33mKybfJioWwDgVfSOPAwdw==",
"path": "graphql.client.abstractions.websocket/5.1.1",
"hashPath": "graphql.client.abstractions.websocket.5.1.1.nupkg.sha512"
},
"GraphQL.Primitives/6.0.0": {
"GraphQL.Primitives/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==",
"path": "graphql.primitives/6.0.0",
"hashPath": "graphql.primitives.6.0.0.nupkg.sha512"
"sha512": "sha512-RlGNsv19gbz6sQwkzif9J6Jd148nuIg1kRQf2AFOLp5K00IA+pKMdJvHF5t5llDR52Rok46ynhJv/wg+ps9ZhQ==",
"path": "graphql.primitives/5.1.1",
"hashPath": "graphql.primitives.5.1.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Http/2.1.1": {
"type": "package",
@@ -651,19 +630,19 @@
"path": "microsoft.csharp/4.7.0",
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
},
"Microsoft.Data.Sqlite/7.0.5": {
"Microsoft.Data.Sqlite/7.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"path": "microsoft.data.sqlite/7.0.5",
"hashPath": "microsoft.data.sqlite.7.0.5.nupkg.sha512"
"sha512": "sha512-uumx0bb4FsN7ApP0ZoQDfSJi9c2Xen0PlXCT2BF27cM+yUMFzDEhqxR7/1/DV8ck4mYtL9yShBoOa7jeJ3736w==",
"path": "microsoft.data.sqlite/7.0.3",
"hashPath": "microsoft.data.sqlite.7.0.3.nupkg.sha512"
},
"Microsoft.Data.Sqlite.Core/7.0.5": {
"Microsoft.Data.Sqlite.Core/7.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"path": "microsoft.data.sqlite.core/7.0.5",
"hashPath": "microsoft.data.sqlite.core.7.0.5.nupkg.sha512"
"sha512": "sha512-pCmzLLWTIrIv94o7JtQ1qcPD0oc1YNY9XvlO6/tOF9YCcUfDZ3Tx9Z//CM7hFnprduHFPekif7jteBc/sXQ31Q==",
"path": "microsoft.data.sqlite.core/7.0.3",
"hashPath": "microsoft.data.sqlite.core.7.0.3.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": {
"type": "package",
@@ -742,19 +721,19 @@
"path": "polly.extensions.http/3.0.0",
"hashPath": "polly.extensions.http.3.0.0.nupkg.sha512"
},
"Sentry/3.33.0": {
"Sentry/3.29.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
"path": "sentry/3.33.0",
"hashPath": "sentry.3.33.0.nupkg.sha512"
"sha512": "sha512-0AQbZte9ETORcrLj+gmzZcjbB3UwLl6KmdRVC9mcFfWTPftnSqVrgWDQHR70t2EYK5w6Y1pM8FAFyLDSJWvyRA==",
"path": "sentry/3.29.0",
"hashPath": "sentry.3.29.0.nupkg.sha512"
},
"Sentry.Serilog/3.33.0": {
"Sentry.Serilog/3.29.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"path": "sentry.serilog/3.33.0",
"hashPath": "sentry.serilog.3.33.0.nupkg.sha512"
"sha512": "sha512-1g+x8fkoYe3/X6qH2F5hJ6eGkfhGqkMcib0P+dGH6lndnVseP+Lgx8HeO8zk3x3bkS2/GUEV7fn/QnFdh32R4A==",
"path": "sentry.serilog/3.29.0",
"hashPath": "sentry.serilog.3.29.0.nupkg.sha512"
},
"Serilog/2.12.0": {
"type": "package",
@@ -763,12 +742,12 @@
"path": "serilog/2.12.0",
"hashPath": "serilog.2.12.0.nupkg.sha512"
},
"Serilog.Enrichers.ClientInfo/1.3.0": {
"Serilog.Enrichers.ClientInfo/1.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"path": "serilog.enrichers.clientinfo/1.3.0",
"hashPath": "serilog.enrichers.clientinfo.1.3.0.nupkg.sha512"
"sha512": "sha512-ZJx2eJQKX6+GxjZM9Y++7DPunR7Nizk9Vdq+BqMs/YPfW3Sv+qDm3PVC88srywJMDKfRaecHFWktBjw5F2pmoQ==",
"path": "serilog.enrichers.clientinfo/1.2.0",
"hashPath": "serilog.enrichers.clientinfo.1.2.0.nupkg.sha512"
},
"Serilog.Enrichers.GlobalLogContext/3.0.0": {
"type": "package",
@@ -819,13 +798,6 @@
"path": "serilog.sinks.seq/5.2.2",
"hashPath": "serilog.sinks.seq.5.2.2.nupkg.sha512"
},
"SerilogTimings/3.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"path": "serilogtimings/3.0.1",
"hashPath": "serilogtimings.3.0.1.nupkg.sha512"
},
"Speckle.Newtonsoft.Json/13.0.2": {
"type": "package",
"serviceable": true,
@@ -875,13 +847,6 @@
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.DoubleNumerics/3.1.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"path": "system.doublenumerics/3.1.3",
"hashPath": "system.doublenumerics.3.1.3.nupkg.sha512"
},
"System.Memory/4.5.4": {
"type": "package",
"serviceable": true,
File diff suppressed because it is too large Load Diff
@@ -1,33 +0,0 @@
fileFormatVersion: 2
guid: a85708f21043b0c458a3a77c0e09e4b8
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -23,9 +23,6 @@ PluginImporter:
Exclude WebGL: 0
Exclude Win: 0
Exclude Win64: 0
Exclude WindowsStoreApps: 0
Exclude iOS: 0
Exclude tvOS: 0
- first:
Android: Android
second:
@@ -50,7 +47,7 @@ PluginImporter:
second:
enabled: 1
settings:
CPU: x86_64
CPU: None
- first:
Standalone: OSXUniversal
second:
@@ -77,30 +74,9 @@ PluginImporter:
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
enabled: 0
settings:
CPU: AnyCPU
DontProcess: false
PlaceholderPath:
SDK: AnySDK
ScriptingBackend: AnyScriptingBackend
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
- first:
tvOS: tvOS
second:
enabled: 1
settings:
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
@@ -22,117 +22,103 @@ using UnityEngine;
namespace Speckle.ConnectorUnity
{
/// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher
/// <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
/// 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>
public class Dispatcher : MonoBehaviour
{
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
/// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher
/// <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
/// 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>
public class Dispatcher : MonoBehaviour {
public void Update()
{
lock (_executionQueue)
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
}
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <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);
});
}
}
public void Update() {
lock(_executionQueue) {
while (_executionQueue.Count > 0) {
_executionQueue.Dequeue().Invoke();
}
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <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>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <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>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <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>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
void WrappedAction()
{
try
{
action();
tcs.TrySetResult(true);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
void WrappedAction() {
try
{
action();
tcs.TrySetResult(true);
} catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
Enqueue(ActionWrapper(WrappedAction));
return tcs.Task;
}
Enqueue(ActionWrapper(WrappedAction));
return tcs.Task;
}
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
private static Dispatcher _instance;
public static bool Exists()
{
return _instance != null;
}
private static Dispatcher _instance = 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;
}
public static bool Exists() {
return _instance != null;
}
void Awake()
{
Setup.Init(
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
HostApplications.Unity.Slug
);
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;
}
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
void OnDestroy()
{
_instance = null;
}
}
}
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,6 +3,7 @@ using Speckle.Core.Kits;
namespace Speckle.ConnectorUnity.Factories
{
public static class ConverterFactory
{
public static ISpeckleConverter GetDefaultConverter()
@@ -14,12 +14,9 @@ namespace Speckle.ConnectorUnity.NativeCache
[ExecuteAlways]
public abstract class AbstractNativeCache : ScriptableObject
{
protected bool isWriting;
public abstract bool TryGetObject<T>(
Base speckleObject,
[NotNullWhen(true)] out T? nativeObject
)
where T : Object;
protected bool isWriting = false;
public abstract bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : Object;
public abstract bool TrySaveObject(Base speckleObject, Object nativeObject);
@@ -44,6 +41,7 @@ namespace Speckle.ConnectorUnity.NativeCache
{
FinishWrite();
}
}
public static class AssetHelpers
@@ -51,12 +49,12 @@ namespace Speckle.ConnectorUnity.NativeCache
public static string? GetAssetFolder(Type nativeType, string path)
{
const string format = "{0}/{1}";
if (nativeType == typeof(Mesh))
{
return string.Format(format, path, "Geometry");
}
if (nativeType == typeof(Material))
if (nativeType == typeof(Material))
{
return string.Format(format, path, "Materials");
}
@@ -66,10 +64,9 @@ namespace Speckle.ConnectorUnity.NativeCache
}
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)
{
string suffix = GetAssetSuffix(nativeType);
@@ -81,18 +78,15 @@ namespace Speckle.ConnectorUnity.NativeCache
public static string GetAssetSuffix(Type nativeType)
{
if (nativeType == typeof(Material))
return ".mat";
if (nativeType == typeof(GameObject))
return ".prefab";
if (nativeType == typeof(Material)) return ".mat";
if (nativeType == typeof(GameObject)) return ".prefab";
return ".asset";
}
[Obsolete("use " + nameof(CoreUtils.GenerateObjectName))]
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}";
}
}
@@ -10,14 +10,12 @@ namespace Speckle.ConnectorUnity.NativeCache
{
[SerializeField, SerializeReference]
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)
{
if (c.TryGetObject(speckleObject, out nativeObject))
return true;
if (c.TryGetObject(speckleObject, out nativeObject)) return true;
}
nativeObject = null;
return false;
@@ -26,13 +24,12 @@ namespace Speckle.ConnectorUnity.NativeCache
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
bool hasSavedSomewhere = false;
foreach (var c in nativeCaches)
{
hasSavedSomewhere =
hasSavedSomewhere || c.TrySaveObject(speckleObject, nativeObject);
hasSavedSomewhere = hasSavedSomewhere || c.TrySaveObject(speckleObject, nativeObject);
}
return hasSavedSomewhere;
}
@@ -60,5 +57,6 @@ namespace Speckle.ConnectorUnity.NativeCache
}
base.FinishWrite();
}
}
}
@@ -6,48 +6,40 @@ using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
#nullable enable
#nullable enable
/// <summary>
/// In memory native object cache
/// </summary>
public sealed class MemoryNativeCache : AbstractNativeCache
{
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 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
{
if (TryGetObject(speckleObject, out List<Object>? e))
{
nativeObject = (T?)e.FirstOrDefault(x => x is T);
return nativeObject != null;
}
nativeObject = null;
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);
}
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
if (LoadedAssets.TryGetValue(speckleObject.id, out List<Object>? assets))
if (LoadedAssets.ContainsKey(speckleObject.id))
{
assets.Add(nativeObject);
LoadedAssets[speckleObject.id].Add(nativeObject);
return true;
}
LoadedAssets.Add(speckleObject.id, new List<Object> { nativeObject });
LoadedAssets.Add(speckleObject.id, new List<Object>{nativeObject});
return true;
}
}
@@ -5,7 +5,7 @@ using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
#nullable enable
#nullable enable
/// <summary>
/// Loads existing assets from <see cref="Resources"/>
/// by friendly id (see <see cref="AssetHelpers.GetAssetName"/>)
@@ -14,25 +14,19 @@ namespace Speckle.ConnectorUnity.NativeCache
public sealed class ResourcesNativeCache : AbstractNativeCache
{
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)
{
string? speckleName =
speckleObject["name"] as string ?? speckleObject["Name"] as string;
string? speckleName = speckleObject["name"] as string ?? speckleObject["Name"] as string;
if (!string.IsNullOrWhiteSpace(speckleName))
{
nativeObject = Resources.Load<T>(speckleName);
if (nativeObject != null)
return true;
if (nativeObject != null) return true;
}
}
}
nativeObject = Resources.Load<T>(AssetHelpers.GetAssetName(speckleObject, typeof(T)));
return nativeObject != null;
}
@@ -8,12 +8,11 @@ using Speckle.ConnectorUnity.NativeCache.Editor;
namespace Speckle.ConnectorUnity
{
#nullable enable
#nullable enable
public static class NativeCacheFactory
{
public static List<AbstractNativeCache> GetDefaultNativeCacheSetup(
bool generateAssets = false
)
public static List<AbstractNativeCache> GetDefaultNativeCacheSetup(bool generateAssets = false)
{
#if UNITY_EDITOR
if (generateAssets)
@@ -22,6 +21,7 @@ namespace Speckle.ConnectorUnity
}
#endif
return GetStandaloneCacheSetup();
}
public static List<AbstractNativeCache> GetStandaloneCacheSetup()
@@ -32,7 +32,7 @@ namespace Speckle.ConnectorUnity
ScriptableObject.CreateInstance<MemoryNativeCache>(),
};
}
#if UNITY_EDITOR
public static List<AbstractNativeCache> GetEditorCacheSetup()
{
@@ -46,3 +46,4 @@ namespace Speckle.ConnectorUnity
#endif
}
}
@@ -16,46 +16,46 @@
"Objects.dll": {}
}
},
"GraphQL.Client/6.0.0": {
"GraphQL.Client/5.1.1": {
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0",
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
"GraphQL.Client.Abstractions": "5.1.1",
"GraphQL.Client.Abstractions.Websocket": "5.1.1",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Client.Abstractions/6.0.0": {
"GraphQL.Client.Abstractions/5.1.1": {
"dependencies": {
"GraphQL.Primitives": "6.0.0"
"GraphQL.Primitives": "5.1.1"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0"
"GraphQL.Client.Abstractions": "5.1.1"
},
"runtime": {
"lib/netstandard2.0/GraphQL.Client.Abstractions.Websocket.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
"GraphQL.Primitives/6.0.0": {
"GraphQL.Primitives/5.1.1": {
"runtime": {
"lib/netstandard2.0/GraphQL.Primitives.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
"assemblyVersion": "5.1.1.0",
"fileVersion": "5.1.1.0"
}
}
},
@@ -128,20 +128,20 @@
}
}
},
"Microsoft.Data.Sqlite/7.0.5": {
"Microsoft.Data.Sqlite/7.0.3": {
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"Microsoft.Data.Sqlite.Core": "7.0.3",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core/7.0.5": {
"Microsoft.Data.Sqlite.Core/7.0.3": {
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
},
"runtime": {
"lib/netstandard2.0/Microsoft.Data.Sqlite.dll": {
"assemblyVersion": "7.0.5.0",
"fileVersion": "7.0.523.16503"
"assemblyVersion": "7.0.3.0",
"fileVersion": "7.0.323.6302"
}
}
},
@@ -231,27 +231,30 @@
}
}
},
"Sentry/3.33.0": {
"Sentry/3.29.0": {
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Buffers": "4.5.1",
"System.Reflection.Metadata": "5.0.0",
"System.Text.Json": "5.0.2"
"System.Text.Json": "5.0.2",
"System.Threading.Tasks.Extensions": "4.5.4"
},
"runtime": {
"lib/netstandard2.0/Sentry.dll": {
"assemblyVersion": "3.33.0.0",
"fileVersion": "3.33.0.0"
"assemblyVersion": "3.29.0.0",
"fileVersion": "3.29.0.0"
}
}
},
"Sentry.Serilog/3.33.0": {
"Sentry.Serilog/3.29.0": {
"dependencies": {
"Sentry": "3.33.0",
"Sentry": "3.29.0",
"Serilog": "2.12.0"
},
"runtime": {
"lib/netstandard2.0/Sentry.Serilog.dll": {
"assemblyVersion": "3.33.0.0",
"fileVersion": "3.33.0.0"
"assemblyVersion": "3.29.0.0",
"fileVersion": "3.29.0.0"
}
}
},
@@ -263,7 +266,7 @@
}
}
},
"Serilog.Enrichers.ClientInfo/1.3.0": {
"Serilog.Enrichers.ClientInfo/1.2.0": {
"dependencies": {
"Microsoft.AspNetCore.Http": "2.1.1",
"Serilog": "2.12.0"
@@ -356,17 +359,6 @@
}
}
},
"SerilogTimings/3.0.1": {
"dependencies": {
"Serilog": "2.12.0"
},
"runtime": {
"lib/netstandard2.0/SerilogTimings.dll": {
"assemblyVersion": "3.0.1.0",
"fileVersion": "3.0.1.0"
}
}
},
"Speckle.Newtonsoft.Json/13.0.2": {
"runtime": {
"lib/netstandard2.0/Speckle.Newtonsoft.Json.dll": {
@@ -429,17 +421,6 @@
}
}
},
"System.DoubleNumerics/3.1.3": {
"dependencies": {
"NETStandard.Library": "2.0.3"
},
"runtime": {
"lib/netstandard1.3/System.DoubleNumerics.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
},
"System.Memory/4.5.4": {
"dependencies": {
"System.Buffers": "4.5.1",
@@ -565,33 +546,33 @@
"serviceable": false,
"sha512": ""
},
"GraphQL.Client/6.0.0": {
"GraphQL.Client/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
"path": "graphql.client/6.0.0",
"hashPath": "graphql.client.6.0.0.nupkg.sha512"
"sha512": "sha512-6bfM9qU4AMcFWm4BHd2M6LE5+rLtK47/+VRtypggwb9appC8sbF58ytVBVOKpqKhKpmZERfPLGJap8O/FH3w5w==",
"path": "graphql.client/5.1.1",
"hashPath": "graphql.client.5.1.1.nupkg.sha512"
},
"GraphQL.Client.Abstractions/6.0.0": {
"GraphQL.Client.Abstractions/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
"path": "graphql.client.abstractions/6.0.0",
"hashPath": "graphql.client.abstractions.6.0.0.nupkg.sha512"
"sha512": "sha512-/znG7Lcz3rzG9VSCq+V2ARb63c/uIs8idGOvXyltZ32Wy570GX/I8HNUIZ1yDThmQRJ5KOGSd9Mzk37lFg49rg==",
"path": "graphql.client.abstractions/5.1.1",
"hashPath": "graphql.client.abstractions.5.1.1.nupkg.sha512"
},
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
"path": "graphql.client.abstractions.websocket/6.0.0",
"hashPath": "graphql.client.abstractions.websocket.6.0.0.nupkg.sha512"
"sha512": "sha512-n1gU3GlqJ0jQceb/VEEr4c0D2vpQc5AtDwthK89+yX7VpzlhJKqE5B4RJwx//Jb33mKybfJioWwDgVfSOPAwdw==",
"path": "graphql.client.abstractions.websocket/5.1.1",
"hashPath": "graphql.client.abstractions.websocket.5.1.1.nupkg.sha512"
},
"GraphQL.Primitives/6.0.0": {
"GraphQL.Primitives/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==",
"path": "graphql.primitives/6.0.0",
"hashPath": "graphql.primitives.6.0.0.nupkg.sha512"
"sha512": "sha512-RlGNsv19gbz6sQwkzif9J6Jd148nuIg1kRQf2AFOLp5K00IA+pKMdJvHF5t5llDR52Rok46ynhJv/wg+ps9ZhQ==",
"path": "graphql.primitives/5.1.1",
"hashPath": "graphql.primitives.5.1.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Http/2.1.1": {
"type": "package",
@@ -635,19 +616,19 @@
"path": "microsoft.csharp/4.7.0",
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
},
"Microsoft.Data.Sqlite/7.0.5": {
"Microsoft.Data.Sqlite/7.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"path": "microsoft.data.sqlite/7.0.5",
"hashPath": "microsoft.data.sqlite.7.0.5.nupkg.sha512"
"sha512": "sha512-uumx0bb4FsN7ApP0ZoQDfSJi9c2Xen0PlXCT2BF27cM+yUMFzDEhqxR7/1/DV8ck4mYtL9yShBoOa7jeJ3736w==",
"path": "microsoft.data.sqlite/7.0.3",
"hashPath": "microsoft.data.sqlite.7.0.3.nupkg.sha512"
},
"Microsoft.Data.Sqlite.Core/7.0.5": {
"Microsoft.Data.Sqlite.Core/7.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"path": "microsoft.data.sqlite.core/7.0.5",
"hashPath": "microsoft.data.sqlite.core.7.0.5.nupkg.sha512"
"sha512": "sha512-pCmzLLWTIrIv94o7JtQ1qcPD0oc1YNY9XvlO6/tOF9YCcUfDZ3Tx9Z//CM7hFnprduHFPekif7jteBc/sXQ31Q==",
"path": "microsoft.data.sqlite.core/7.0.3",
"hashPath": "microsoft.data.sqlite.core.7.0.3.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": {
"type": "package",
@@ -726,19 +707,19 @@
"path": "polly.extensions.http/3.0.0",
"hashPath": "polly.extensions.http.3.0.0.nupkg.sha512"
},
"Sentry/3.33.0": {
"Sentry/3.29.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
"path": "sentry/3.33.0",
"hashPath": "sentry.3.33.0.nupkg.sha512"
"sha512": "sha512-0AQbZte9ETORcrLj+gmzZcjbB3UwLl6KmdRVC9mcFfWTPftnSqVrgWDQHR70t2EYK5w6Y1pM8FAFyLDSJWvyRA==",
"path": "sentry/3.29.0",
"hashPath": "sentry.3.29.0.nupkg.sha512"
},
"Sentry.Serilog/3.33.0": {
"Sentry.Serilog/3.29.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"path": "sentry.serilog/3.33.0",
"hashPath": "sentry.serilog.3.33.0.nupkg.sha512"
"sha512": "sha512-1g+x8fkoYe3/X6qH2F5hJ6eGkfhGqkMcib0P+dGH6lndnVseP+Lgx8HeO8zk3x3bkS2/GUEV7fn/QnFdh32R4A==",
"path": "sentry.serilog/3.29.0",
"hashPath": "sentry.serilog.3.29.0.nupkg.sha512"
},
"Serilog/2.12.0": {
"type": "package",
@@ -747,12 +728,12 @@
"path": "serilog/2.12.0",
"hashPath": "serilog.2.12.0.nupkg.sha512"
},
"Serilog.Enrichers.ClientInfo/1.3.0": {
"Serilog.Enrichers.ClientInfo/1.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"path": "serilog.enrichers.clientinfo/1.3.0",
"hashPath": "serilog.enrichers.clientinfo.1.3.0.nupkg.sha512"
"sha512": "sha512-ZJx2eJQKX6+GxjZM9Y++7DPunR7Nizk9Vdq+BqMs/YPfW3Sv+qDm3PVC88srywJMDKfRaecHFWktBjw5F2pmoQ==",
"path": "serilog.enrichers.clientinfo/1.2.0",
"hashPath": "serilog.enrichers.clientinfo.1.2.0.nupkg.sha512"
},
"Serilog.Enrichers.GlobalLogContext/3.0.0": {
"type": "package",
@@ -803,13 +784,6 @@
"path": "serilog.sinks.seq/5.2.2",
"hashPath": "serilog.sinks.seq.5.2.2.nupkg.sha512"
},
"SerilogTimings/3.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"path": "serilogtimings/3.0.1",
"hashPath": "serilogtimings.3.0.1.nupkg.sha512"
},
"Speckle.Newtonsoft.Json/13.0.2": {
"type": "package",
"serviceable": true,
@@ -859,13 +833,6 @@
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.DoubleNumerics/3.1.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"path": "system.doublenumerics/3.1.3",
"hashPath": "system.doublenumerics.3.1.3.nupkg.sha512"
},
"System.Memory/4.5.4": {
"type": "package",
"serviceable": true,
@@ -14,279 +14,16 @@
Station equation direction for the corresponding station equation should be true for increasing or false for decreasing
</summary>
</member>
<member name="M:Objects.BuiltElements.Archicad.ComponentProperties.ToBase(System.Collections.Generic.List{Objects.BuiltElements.Archicad.ComponentProperties})">
<summary>
Turns a List of ComponentProperties into a Base so that it can be used with the Speckle properties prop
</summary>
<param name="componentPropertiesList"></param>
<returns></returns>
</member>
<member name="M:Objects.BuiltElements.Archicad.Property.ToBase(System.Collections.Generic.List{Objects.BuiltElements.Archicad.Property})">
<summary>
Turns a List of Property into a Base so that it can be used with the Speckle properties prop
</summary>
<param name="properties"></param>
<returns></returns>
</member>
<member name="M:Objects.BuiltElements.Archicad.PropertyGroup.ToBase(System.Collections.Generic.List{Objects.BuiltElements.Archicad.PropertyGroup})">
<summary>
Turns a List of PropertyGroup into a Base so that it can be used with the Speckle properties prop
</summary>
<param name="propertyGroups"></param>
<returns></returns>
</member>
<member name="M:Objects.BuiltElements.Area.#ctor(System.String,System.String,Objects.BuiltElements.Level,Objects.Geometry.Point)">
<summary>
SchemaBuilder constructor for an Area
</summary>
</member>
<member name="P:Objects.BuiltElements.Civil.CivilAlignment.parent">
<summary>
Name of parent alignment if this is an offset alignment
</summary>
</member>
<member name="P:Objects.BuiltElements.Civil.CivilProfile.pvis">
<member name="M:Objects.BuiltElements.Area.#ctor(System.String,System.String,Objects.BuiltElements.Level,Objects.Geometry.Point)">
<summary>
Points of vertical intersection
</summary>
</member>
<member name="P:Objects.BuiltElements.Civil.CivilProfile.parent">
<summary>
Name of parent profile if this is an offset profile
</summary>
</member>
<member name="M:Objects.BuiltElements.Duct.#ctor(Objects.Geometry.Line,System.Double,System.Double,System.Double,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle duct
</summary>
<param name="baseLine"></param>
<param name="width"></param>
<param name="height"></param>
<param name="diameter"></param>
<param name="velocity"></param>
<remarks>Assign units when using this constructor due to <paramref name="width"/>, <paramref name="height"/>, and <paramref name="diameter"/> params</remarks>
</member>
<member name="M:Objects.BuiltElements.Duct.#ctor(Objects.ICurve,System.Double,System.Double,System.Double,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle duct
</summary>
<param name="baseCurve"></param>
<param name="width"></param>
<param name="height"></param>
<param name="diameter"></param>
<param name="velocity"></param>
<remarks>Assign units when using this constructor due to <paramref name="width"/>, <paramref name="height"/>, and <paramref name="diameter"/> params</remarks>
</member>
<member name="P:Objects.BuiltElements.Featureline.curve">
<summary>
The base curve of the featureline
</summary>
</member>
<member name="P:Objects.BuiltElements.Featureline.points">
<summary>
The points constructing the Featureline
</summary>
<remarks>
Can include both intersection and elevation points
</remarks>
</member>
<member name="P:Objects.BuiltElements.Featureline.displayValue">
<summary>
The 3D curves generated from the curve and points of the featureline
</summary>
</member>
<member name="M:Objects.BuiltElements.Level.#ctor(System.String,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle level
</summary>
<param name="name"></param>
<param name="elevation"></param>
<remarks>Assign units when using this constructor due to <paramref name="elevation"/> param</remarks>
</member>
<member name="T:Objects.BuiltElements.Network">
<summary>
Represents graph connections between built elements objects
</summary>
<remarks>
Network <see cref="P:Objects.BuiltElements.Network.elements"/> may need to be created first in native applications before they are linked.
</remarks>
</member>
<member name="P:Objects.BuiltElements.Network.elements">
<summary>
The elements contained in the network
</summary>
</member>
<member name="P:Objects.BuiltElements.Network.links">
<summary>
The connections between <see cref="P:Objects.BuiltElements.Network.elements"/>
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkElement.elements">
<summary>
The Base object representing the element in the network (eg Pipe, Duct, etc)
</summary>
<remarks>
Currently named "elements" to assist with receiving in connector flatten method.
</remarks>
</member>
<member name="P:Objects.BuiltElements.NetworkElement.linkIndices">
<summary>
The index of the links in <see cref="P:Objects.BuiltElements.NetworkElement.network"/> that are connected to this element
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkElement.links">
<summary>
Retrieves the links for this element
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkLink.elementIndices">
<summary>
The index of the elements in <see cref="P:Objects.BuiltElements.NetworkLink.network"/> that are connected by this link
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkLink.elements">
<summary>
Retrieves the elements for this link
</summary>
</member>
<member name="T:Objects.BuiltElements.RebarGroup`1">
<summary>
A reinforcement bar group comprised of reinforcing bars of the same type and shape.
</summary>
<remarks>
This class is not suitable for freeform rebar, which can have multiple shapes.
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.shape">
<summary>
The shape of the rebar group
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.number">
<summary>
The number of rebars in the rebar group
</summary>
<remarks>
Excluded end bars are not included in the count
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.hasFirstBar">
<summary>
Indicates if rebar set includes the first bar
</summary>
<remarks>
Only applicable to stirrup (transverse) rebar
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.hasLastBar">
<summary>
Indicates if rebar set includes the last bar
</summary>
<remarks>
Only applicable to stirrup (transverse) rebar
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.startHook">
<summary>
The start hook of bars in the rebar group
</summary>
<remarks>
Null indicates no start hook
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.endHook">
<summary>
The end hook of bars in the rebar group
</summary>
<remarks>
Null indicates no end hook
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.displayValue">
<summary>
The display representation of the rebar group as centerline curves
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarGroup`1.volume">
<summary>
The total volume of the rebar group.
</summary>
</member>
<member name="T:Objects.BuiltElements.RebarShape">
<summary>
The shape describing the geometry and geometry parameters of a reinforcing bar
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarShape.name">
<summary>
The name of the rebar shape
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarShape.rebarType">
<summary>
The type of the rebar shape
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarShape.curves">
<summary>
The curves of the rebar shape
</summary>
<remarks>
Typically suppresses hooks and bend radius
</remarks>
</member>
<member name="P:Objects.BuiltElements.RebarShape.barDiameter">
<summary>
The diameter of the rebar bar
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarHook.angle">
<summary>
The angle of the hook in radians.
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarHook.length">
<summary>
The length of the hook.
</summary>
</member>
<member name="P:Objects.BuiltElements.RebarHook.radius">
<summary>
The radius of the bend of the hook.
</summary>
</member>
<member name="M:Objects.BuiltElements.Revit.DirectShape.#ctor(System.String,Objects.BuiltElements.Revit.RevitCategory,System.Collections.Generic.List{Speckle.Core.Models.Base},System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
Constructs a new <see cref="T:Objects.BuiltElements.Revit.DirectShape"/> instance given a list of <see cref="T:Speckle.Core.Models.Base"/> objects.
</summary>
<param name="name">The name of the <see cref="T:Objects.BuiltElements.Revit.DirectShape"/></param>
<param name="category">The <see cref="T:Objects.BuiltElements.Revit.RevitCategory"/> of this instance.</param>
<param name="baseGeometries">A list of base classes to represent the direct shape (only mesh and brep are allowed, anything else will be ignored.)</param>
<param name="parameters">Optional Parameters for this instance.</param>
</member>
<member name="T:Objects.BuiltElements.Revit.RevitFamilyCategory">
<summary>
FamilyDocuments can only be assigned these categories
This is a subset of the list above which was manually retrieved from Revit's UI
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.FreeformElement.baseGeometry">
<summary>
DEPRECATED. Sets the geometry contained in the FreeformElement. This field has been deprecated in favor of `baseGeometries`
to align with Revit's API. It remains as a setter-only property for backwards compatibility.
It will set the first item on the baseGeometries list, and instantiate a list if necessary.
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.Parameter.isShared">
<summary>
If True it's a Shared Parameter, in which case the ApplicationId field will contain this parameter GUID,
otherwise it will store its BuiltInParameter name
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.Parameter.isTypeParameter">
<summary>
True = Type Parameter, False = Instance Parameter
SchemaBuilder constructor for a Room
</summary>
<remarks>Assign units when using this constructor due to <paramref name="height"/> param</remarks>
</member>
<member name="M:Objects.BuiltElements.Revit.RevitColumn.#ctor(System.String,System.String,Objects.ICurve,Objects.BuiltElements.Level,Objects.BuiltElements.Level,System.Double,System.Double,System.Boolean,System.Double,System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
@@ -355,27 +92,6 @@
<param name="parameters"></param>
<remarks>Assign units when using this constructor due to <paramref name="width"/>, <paramref name="height"/>, and <paramref name="diameter"/> params</remarks>
</member>
<member name="T:Objects.BuiltElements.Revit.RevitElement">
<summary>
A generic Revit element for which we don't have direct conversions
</summary>
</member>
<member name="T:Objects.BuiltElements.Revit.RevitSymbolElementType">
<summary>
Represents the FamilySymbol subclass of ElementType in Revit
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.RevitSymbolElementType.placementType">
<summary>
The type of placement for this family symbol
</summary>
<remarks> See https://www.revitapidocs.com/2023/2abb8627-1da3-4069-05c9-19e4be5e02ad.htm </remarks>
</member>
<member name="P:Objects.BuiltElements.Revit.RevitSymbolElementType.elements">
<summary>
Subcomponents found in this family symbol
</summary>
</member>
<member name="M:Objects.BuiltElements.Revit.RevitLevel.#ctor(System.String,System.Double,System.Boolean,System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
SchemaBuilder constructor for a Revit level
@@ -445,6 +161,54 @@
<param name="topLevel"></param>
<param name="parameters"></param>
</member>
<member name="M:Objects.BuiltElements.Revit.DirectShape.#ctor(System.String,Objects.BuiltElements.Revit.RevitCategory,System.Collections.Generic.List{Speckle.Core.Models.Base},System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
Constructs a new <see cref="T:Objects.BuiltElements.Revit.DirectShape"/> instance given a list of <see cref="T:Speckle.Core.Models.Base"/> objects.
</summary>
<param name="name">The name of the <see cref="T:Objects.BuiltElements.Revit.DirectShape"/></param>
<param name="category">The <see cref="T:Objects.BuiltElements.Revit.RevitCategory"/> of this instance.</param>
<param name="baseGeometries">A list of base classes to represent the direct shape (only mesh and brep are allowed, anything else will be ignored.)</param>
<param name="parameters">Optional Parameters for this instance.</param>
</member>
<member name="P:Objects.BuiltElements.Revit.FreeformElement.baseGeometry">
<summary>
DEPRECATED. Sets the geometry contained in the FreeformElement. This field has been deprecated in favor of `baseGeometries`
to align with Revit's API. It remains as a setter-only property for backwards compatibility.
It will set the first item on the baseGeometries list, and instantiate a list if necessary.
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.Parameter.isShared">
<summary>
If True it's a Shared Parameter, in which case the ApplicationId field will contain this parameter GUID,
otherwise it will store its BuiltInParameter name
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.Parameter.isTypeParameter">
<summary>
True = Type Parameter, False = Instance Parameter
</summary>
</member>
<member name="T:Objects.BuiltElements.Revit.RevitElement">
<summary>
A generic Revit element for which we don't have direct conversions
</summary>
</member>
<member name="T:Objects.BuiltElements.Revit.RevitSymbolElementType">
<summary>
Represents the FamilySymbol subclass of ElementType in Revit
</summary>
</member>
<member name="P:Objects.BuiltElements.Revit.RevitSymbolElementType.placementType">
<summary>
The type of placement for this family symbol
</summary>
<remarks> See https://www.revitapidocs.com/2023/2abb8627-1da3-4069-05c9-19e4be5e02ad.htm </remarks>
</member>
<member name="P:Objects.BuiltElements.Revit.RevitSymbolElementType.elements">
<summary>
Subcomponents found in this family symbol
</summary>
</member>
<member name="M:Objects.BuiltElements.Revit.RevitRoof.RevitExtrusionRoof.#ctor(System.String,System.String,System.Double,System.Double,Objects.Geometry.Line,Objects.BuiltElements.Level,System.Collections.Generic.List{Speckle.Core.Models.Base},System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
SchemaBuilder constructor for a Revit extrusion roof
@@ -493,17 +257,103 @@
<param name="parameters"></param>
<remarks>Assign units when using this constructor due to <paramref name="height"/>, <paramref name="baseOffset"/>, and <paramref name="topOffset"/> params</remarks>
</member>
<member name="M:Objects.BuiltElements.Duct.#ctor(Objects.Geometry.Line,System.Double,System.Double,System.Double,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle duct
</summary>
<param name="baseLine"></param>
<param name="width"></param>
<param name="height"></param>
<param name="diameter"></param>
<param name="velocity"></param>
<remarks>Assign units when using this constructor due to <paramref name="width"/>, <paramref name="height"/>, and <paramref name="diameter"/> params</remarks>
</member>
<member name="M:Objects.BuiltElements.Duct.#ctor(Objects.ICurve,System.Double,System.Double,System.Double,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle duct
</summary>
<param name="baseCurve"></param>
<param name="width"></param>
<param name="height"></param>
<param name="diameter"></param>
<param name="velocity"></param>
<remarks>Assign units when using this constructor due to <paramref name="width"/>, <paramref name="height"/>, and <paramref name="diameter"/> params</remarks>
</member>
<member name="P:Objects.BuiltElements.Featureline.curve">
<summary>
The base curve of the featureline
</summary>
</member>
<member name="P:Objects.BuiltElements.Featureline.points">
<summary>
The points constructing the Featureline
</summary>
<remarks>
Can include both intersection and elevation points
</remarks>
</member>
<member name="P:Objects.BuiltElements.Featureline.displayValue">
<summary>
The 3D curves generated from the curve and points of the featureline
</summary>
</member>
<member name="M:Objects.BuiltElements.Level.#ctor(System.String,System.Double)">
<summary>
SchemaBuilder constructor for a Speckle level
</summary>
<param name="name"></param>
<param name="elevation"></param>
<remarks>Assign units when using this constructor due to <paramref name="elevation"/> param</remarks>
</member>
<member name="T:Objects.BuiltElements.Network">
<summary>
Represents graph connections between built elements objects
</summary>
<remarks>
Network <see cref="P:Objects.BuiltElements.Network.elements"/> may need to be created first in native applications before they are linked.
</remarks>
</member>
<member name="P:Objects.BuiltElements.Network.elements">
<summary>
The elements contained in the network
</summary>
</member>
<member name="P:Objects.BuiltElements.Network.links">
<summary>
The connections between <see cref="P:Objects.BuiltElements.Network.elements"/>
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkElement.linkIndices">
<summary>
The index of the links in <see cref="P:Objects.BuiltElements.NetworkElement.network"/> that are connected to this element
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkElement.links">
<summary>
Retrieves the links for this element
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkLink.elementIndices">
<summary>
The index of the elements in <see cref="P:Objects.BuiltElements.NetworkLink.network"/> that are connected by this link
</summary>
</member>
<member name="P:Objects.BuiltElements.NetworkLink.elements">
<summary>
Retrieves the elements for this link
</summary>
</member>
<member name="M:Objects.BuiltElements.Room.#ctor(System.String,System.String,Objects.BuiltElements.Level,Objects.Geometry.Point)">
<summary>
SchemaBuilder constructor for a Room
</summary>
<remarks>Assign units when using this constructor due to <see cref="P:Objects.BuiltElements.Room.height"/> prop</remarks>
<remarks>Assign units when using this constructor due to <paramref name="height"/> param</remarks>
</member>
<member name="M:Objects.BuiltElements.Room.#ctor(System.String,System.String,Objects.BuiltElements.Level,Objects.Geometry.Point,System.Collections.Generic.List{Objects.BuiltElements.Revit.Parameter})">
<summary>
SchemaBuilder constructor for a Room
</summary>
<remarks>Assign units when using this constructor due to <see cref="P:Objects.BuiltElements.Room.height"/> prop</remarks>
<remarks>Assign units when using this constructor due to <paramref name="height"/> param</remarks>
</member>
<member name="M:Objects.BuiltElements.Wall.#ctor(System.Double,Objects.ICurve,System.Collections.Generic.List{Speckle.Core.Models.Base})">
<summary>
@@ -967,6 +817,7 @@
<member name="P:Objects.Geometry.ControlPoint.value">
<summary>
OBSOLETE - This is just here for backwards compatibility.
You should not use this for anything. Access coordinates using X,Y,Z and weight fields.
</summary>
</member>
<member name="M:Objects.Geometry.Curve.#ctor">
@@ -1029,7 +880,7 @@
</member>
<member name="M:Objects.Geometry.Curve.ToList">
<summary>
Returns the values of this <see cref="T:Objects.Geometry.Curve"/> as a list of numbers
Returns the vales of this <see cref="T:Objects.Geometry.Curve"/> as a list of numbers
</summary>
<returns>A list of values representing the <see cref="T:Objects.Geometry.Curve"/></returns>
</member>
@@ -1158,10 +1009,10 @@
</member>
<member name="M:Objects.Geometry.Mesh.GetTextureCoordinate(System.Int32)">
<summary>
Gets a texture coordinate as a <see cref="T:System.ValueTuple`2"/> by <paramref name="index"/>
Gets a texture coordinate as a <see cref="!:(T1, T2)"/> by <paramref name="index"/>
</summary>
<param name="index">The index of the texture coordinate</param>
<returns>Texture coordinate as a <see cref="T:System.ValueTuple`2"/></returns>
<returns>Texture coordinate as a <see cref="!:(T1, T2)"/></returns>
</member>
<member name="M:Objects.Geometry.Mesh.AlignVerticesWithTexCoordsByIndex">
<summary>
@@ -1262,7 +1113,7 @@
<param name="x">The x coordinate</param>
<param name="y">The y coordinate</param>
<param name="z">The z coordinate</param>
<param name="units">The units of the point's coordinates. Defaults to Meters. </param>
<param name="units">The units the point's coordinates are in.</param>
<param name="applicationId">The object's unique application ID</param>
</member>
<member name="M:Objects.Geometry.Point.#ctor(Objects.Geometry.Vector)">
@@ -1293,10 +1144,13 @@
</member>
<member name="P:Objects.Geometry.Point.units">
<summary>
The units this <see cref="T:Objects.Geometry.Point"/> is in.
The unit's this <see cref="T:Objects.Geometry.Vector"/> is in.
This should be one of the units specified in <see cref="T:Speckle.Core.Kits.Units"/>
</summary>
</member>
<member name="P:Objects.Geometry.Point.bbox">
<inheritdoc/>
</member>
<member name="M:Objects.Geometry.Point.TransformTo(Objects.Other.Transform,Objects.Geometry.Point@)">
<inheritdoc/>
</member>
@@ -1717,7 +1571,7 @@
Sets the control points of this <see cref="T:Objects.Geometry.Surface"/>.
</summary>
<param name="value">A 2-dimensional array of <see cref="T:Objects.Geometry.ControlPoint"/> instances.</param>
<remarks>The <paramref name="value"/> must be ordered following directions "[u][v]"</remarks>
<remarks>The <see cref="!:value"/> must be ordered following directions "[u][v]"</remarks>
</member>
<member name="M:Objects.Geometry.Surface.ToList">
<summary>
@@ -2010,9 +1864,21 @@
<member name="M:Objects.ObjectsKit.LoadConverter(System.String)">
<inheritdoc/>
</member>
<member name="T:Objects.Organization.Model">
<summary>
Represents a model from an authoring application and can be used as the root commit object when sending.
It contains <see cref="T:Objects.Organization.ModelInfo"/> and <see cref="T:Objects.Organization.Setting"/> objects
</summary>
</member>
<member name="P:Objects.Organization.Model.info">
<summary>
General model-wide information stored in a <see cref="T:Objects.Organization.ModelInfo"/> object.
This may include anything from simply a project / file name to specific location information (eg with <see cref="T:Objects.Organization.BIMModelInfo"/>)
</summary>
</member>
<member name="T:Objects.Organization.ModelInfo">
<summary>
Basic model info class
Basic model info class to be attached to the <see cref="P:Objects.Organization.Model.info"/> field on a <see cref="T:Objects.Organization.Model"/> object.
It contains general information about the model and can be extended or subclassed to include more application-specific
information.
</summary>
@@ -2029,7 +1895,8 @@
</member>
<member name="T:Objects.Organization.BIMModelInfo">
<summary>
Extended <see cref="T:Objects.Organization.ModelInfo"/> to contain additional properties applicable to AEC projects.
Extended <see cref="T:Objects.Organization.ModelInfo"/> to be attached to the <see cref="P:Objects.Organization.Model.info"/> field on a <see cref="T:Objects.Organization.Model"/> object.
This contains additional properties applicable to AEC projects.
</summary>
</member>
<member name="P:Objects.Organization.BIMModelInfo.clientName">
@@ -2072,6 +1939,16 @@
A list of origin locations within this model as a list of <see cref="T:Objects.Other.Transform"/>s
</summary>
</member>
<member name="P:Objects.Organization.Setting.name">
<summary>
The name of the setting
</summary>
</member>
<member name="P:Objects.Organization.Setting.selection">
<summary>
The objects selected in the setting
</summary>
</member>
<member name="T:Objects.Other.BlockDefinition">
<summary>
Block definition class
@@ -2226,6 +2103,18 @@
<remarks>This method will skip scaling. If you need scaling, we recommend using the transform instead.</remarks>
<returns>A Plane on the insertion point of this Block Instance, with the correct 3-axis rotations.</returns>
</member>
<member name="M:Objects.Other.Revit.RevitInstance.GetInsertionPlane">
<summary>
Returns a plane representing the insertion point and orientation of this revit instance.
</summary>
<remarks>This method will skip scaling. If you need scaling, we recommend using the transform instead.</remarks>
<returns>A Plane on the insertion point of this Block Instance, with the correct 3-axis rotations.</returns>
</member>
<member name="T:Objects.Other.Revit.RevitMaterial">
<summary>
Material in Revit defininf all revit properties from Autodesk.Revit.DB.Material
</summary>
</member>
<member name="T:Objects.Other.Material">
<summary>
Generic class for materials containing generic parameters
@@ -2251,18 +2140,6 @@
And: https://blogs.unity3d.com/2014/10/29/physically-based-shading-in-unity-5-a-primer/
</summary>
</member>
<member name="M:Objects.Other.Revit.RevitInstance.GetInsertionPlane">
<summary>
Returns a plane representing the insertion point and orientation of this revit instance.
</summary>
<remarks>This method will skip scaling. If you need scaling, we recommend using the transform instead.</remarks>
<returns>A Plane on the insertion point of this Block Instance, with the correct 3-axis rotations.</returns>
</member>
<member name="T:Objects.Other.Revit.RevitMaterial">
<summary>
Material in Revit defininf all revit properties from Autodesk.Revit.DB.Material
</summary>
</member>
<member name="T:Objects.Other.Text">
<summary>
Text class for Rhino and AutoCAD
@@ -2289,7 +2166,7 @@
<param name="units"></param>
<exception cref="T:Speckle.Core.Logging.SpeckleException"></exception>
</member>
<member name="M:Objects.Other.Transform.#ctor(System.DoubleNumerics.Matrix4x4,System.String)">
<member name="M:Objects.Other.Transform.#ctor(System.Numerics.Matrix4x4,System.String)">
<summary>
Construct a transform from a 4x4 matrix and translation units
</summary>
@@ -2319,7 +2196,7 @@
Units for translation
</summary>
</member>
<member name="M:Objects.Other.Transform.Decompose(System.DoubleNumerics.Vector3@,System.DoubleNumerics.Quaternion@,System.DoubleNumerics.Vector4@)">
<member name="M:Objects.Other.Transform.Decompose(System.Numerics.Vector3@,System.Numerics.Quaternion@,System.Numerics.Vector4@)">
<summary>
Decomposes matrix into its scaling, rotation, and translation components
</summary>
@@ -2390,6 +2267,7 @@
<summary>
SchemaBuilder constructor for a structural model object
</summary>
<param name="modelInfo"></param>
<param name="nodes"></param>
<param name="elements"></param>
<param name="loads"></param>
@@ -2457,6 +2335,18 @@
<param name="orientationNode"></param>
<param name="orientationAngle"></param>
</member>
<member name="M:Objects.Structural.GSA.Geometry.GSANode.#ctor(System.Int32,Objects.Geometry.Point,Objects.Structural.Geometry.Restraint,Objects.Structural.Geometry.Axis,Objects.Structural.Properties.PropertySpring,Objects.Structural.Properties.PropertyMass,Objects.Structural.Properties.PropertyDamper,System.Double,System.String)">
<summary>
SchemaBuilder constructor for a GSA node
</summary>
<param name="basePoint"></param>
<param name="restraint"></param>
<param name="constraintAxis"></param>
<param name="springProperty"></param>
<param name="massProperty"></param>
<param name="damperProperty"></param>
<param name="localElementSize"></param>
</member>
<member name="M:Objects.Structural.Geometry.Element1D.#ctor(Objects.Geometry.Line,Objects.Structural.Properties.Property1D,Objects.Structural.Geometry.ElementType1D,System.String,Objects.Structural.Geometry.Restraint,Objects.Structural.Geometry.Restraint,Objects.Geometry.Vector,Objects.Geometry.Vector,Objects.Geometry.Plane)">
<summary>
SchemaBuilder constructor for structural 1D element (based on local axis)
@@ -2493,18 +2383,6 @@
<param name="name">The name of the storey</param>
<param name="elevation">The elevation of the storey (along the global z-axis, ie. storey exists in the global XY plane)</param>
</member>
<member name="M:Objects.Structural.GSA.Geometry.GSANode.#ctor(System.Int32,Objects.Geometry.Point,Objects.Structural.Geometry.Restraint,Objects.Structural.Geometry.Axis,Objects.Structural.Properties.PropertySpring,Objects.Structural.Properties.PropertyMass,Objects.Structural.Properties.PropertyDamper,System.Double,System.String)">
<summary>
SchemaBuilder constructor for a GSA node
</summary>
<param name="basePoint"></param>
<param name="restraint"></param>
<param name="constraintAxis"></param>
<param name="springProperty"></param>
<param name="massProperty"></param>
<param name="damperProperty"></param>
<param name="localElementSize"></param>
</member>
<member name="M:Objects.Structural.Loading.Load.#ctor(System.String,Objects.Structural.Loading.LoadCase)">
<summary>
A generalised structural load, described by a name and load case
@@ -2642,7 +2520,7 @@
</member>
<member name="M:Objects.Utils.MeshTriangulationHelper.TriangulateMesh(Objects.Geometry.Mesh,System.Boolean)">
<summary>
Triangulates all faces in <paramref name="mesh"/>.
Triangulates all faces in <paramref name="Mesh"/>.
</summary>
<param name="mesh">The mesh to triangulate.</param>
<param name="preserveQuads">If <see langword="true"/>, will not triangulate quad faces.</param>
@@ -2653,13 +2531,13 @@
</member>
<member name="M:Objects.Utils.MeshTriangulationHelper.TriangulateFace(System.Int32,System.Collections.Generic.IReadOnlyList{System.Int32},System.Collections.Generic.IReadOnlyList{System.Double},System.Boolean)">
<summary>
Calculates the triangulation of the face at <paramref name="faceIndex"/> in <paramref name="faces"/> list.
Calculates the triangulation of the face at <paramref name="faceIndex"/> in <paramref name="mesh"/>.
</summary>
<remarks>
This implementation is based the ear clipping method
Proposed by "Christer Ericson (2005) <i>Real-Time Collision Detection</i>".
</remarks>
<param name="faceIndex">The index of the face's cardinality indicator <c>n</c> in <paramref name="faces"/> list</param>.
<param name="faceIndex">The index of the face's cardinality indicator <c>n</c> in <paramref name="mesh"/>.<see cref="P:Objects.Geometry.Mesh.faces"/></param>.
<param name="faces"></param>
<param name="vertices"></param>
<param name="includeIndicators">if <see langword="true"/>, the returned list will include cardinality indicators for each triangle
@@ -196,4 +196,4 @@ namespace System.Collections.Concurrent
}
#endregion
}
}
}
@@ -1,39 +1,42 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Logging;
namespace Speckle.ConnectorUnity
{
public static class Streams
public static class Streams
{
public static async Task<List<Stream>> List(int limit = 10)
{
public static async Task<List<Stream>> List(int limit = 10)
{
var account = AccountManager.GetDefaultAccount();
if (account == null)
return new List<Stream>();
var client = new Client(account);
var account = AccountManager.GetDefaultAccount();
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;
}
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;
}
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,41 +9,37 @@ namespace Speckle.ConnectorUnity.Utils
{
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()
{
#if UNITY_2019
return HostAppVersion.v2019;
#elif UNITY_2020
return HostAppVersion.v2020;
#elif UNITY_2021
return HostAppVersion.v2021;
#elif UNITY_2022
return HostAppVersion.v2022;
#elif UNITY_2023
return HostAppVersion.v2023;
#elif UNITY_2024
return HostAppVersion.v2024;
#elif UNITY_2025
return HostAppVersion.v2025;
#else
#if UNITY_2019
return HostAppVersion.v2019;
#elif UNITY_2020
return HostAppVersion.v2020;
#elif UNITY_2021
return HostAppVersion.v2021;
#elif UNITY_2022
return HostAppVersion.v2022;
#elif UNITY_2023
return HostAppVersion.v2023;
#elif UNITY_2024
return HostAppVersion.v2024;
#elif UNITY_2025
return HostAppVersion.v2025;
#else
return HostAppVersion.v;
#endif
#endif
}
public const string ObjectNameSeparator = " -- ";
/// <param name="speckleObject">The object to be named</param>
/// <returns>A human-readable Object name unique to the given <paramref name="speckleObject"/></returns>
public static string GenerateObjectName(Base speckleObject)
{
var prefix =
GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject);
var prefix = GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject);
return $"{prefix}{ObjectNameSeparator}{speckleObject.id}";
}
@@ -53,12 +49,13 @@ namespace Speckle.ConnectorUnity.Utils
?? speckleObject["Name"] as string
?? speckleObject["family"] as string;
}
/// <param name="speckleObject"></param>
/// <returns>The most significant type in a given <see cref="Base.speckle_type"/></returns>
public static string SimplifiedSpeckleType(Base speckleObject)
{
return speckleObject.speckle_type.Split(':')[^1];
}
}
}
@@ -16,13 +16,9 @@ namespace Speckle.ConnectorUnity.Utils
/// <param name="propertyName"></param>
/// <param name="value"></param>
#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;
else
speckleObject[$"@{propertyName}"] = value;
@@ -1,112 +0,0 @@
using UnityEngine;
namespace Speckle.ConnectorUnity.Utils
{
// see https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/Inspector/StandardShaderGUI.cs
public static class ShaderHelpers
{
private static readonly int SrcBlend = Shader.PropertyToID("_SrcBlend");
private static readonly int DstBlend = Shader.PropertyToID("_DstBlend");
private static readonly int ZWrite = Shader.PropertyToID("_ZWrite");
private static readonly int Mode = Shader.PropertyToID("_Mode");
public enum BlendMode
{
Opaque,
Cutout,
Fade, // Old school alpha-blending mode, fresnel does not affect amount of transparency
Transparent // Physically plausible transparency mode, implemented as alpha pre-multiply
}
public static void SetupMaterialWithBlendMode_Standard(
Material material,
BlendMode blendMode,
bool overrideRenderQueue
)
{
int minRenderQueue = -1;
int maxRenderQueue = 5000;
int defaultRenderQueue = -1;
switch (blendMode)
{
case BlendMode.Opaque:
material.SetOverrideTag("RenderType", "");
material.SetFloat(Mode, 0);
material.SetFloat(SrcBlend, (float)UnityEngine.Rendering.BlendMode.One);
material.SetFloat(DstBlend, (float)UnityEngine.Rendering.BlendMode.Zero);
material.SetFloat(ZWrite, 1.0f);
material.DisableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
minRenderQueue = -1;
maxRenderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest - 1;
defaultRenderQueue = -1;
break;
case BlendMode.Cutout:
material.SetOverrideTag("RenderType", "TransparentCutout");
material.SetFloat(Mode, 1);
material.SetFloat(SrcBlend, (float)UnityEngine.Rendering.BlendMode.One);
material.SetFloat(DstBlend, (float)UnityEngine.Rendering.BlendMode.Zero);
material.SetFloat(ZWrite, 1.0f);
material.EnableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
minRenderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
maxRenderQueue = (int)UnityEngine.Rendering.RenderQueue.GeometryLast;
defaultRenderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
break;
case BlendMode.Fade:
material.SetOverrideTag("RenderType", "Transparent");
material.SetFloat(Mode, 2);
material.SetFloat(SrcBlend, (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetFloat(
DstBlend,
(float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha
);
material.SetFloat(ZWrite, 0.0f);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
minRenderQueue = (int)UnityEngine.Rendering.RenderQueue.GeometryLast + 1;
maxRenderQueue = (int)UnityEngine.Rendering.RenderQueue.Overlay - 1;
defaultRenderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
break;
case BlendMode.Transparent:
material.SetOverrideTag("RenderType", "Transparent");
material.SetFloat(Mode, 3);
material.SetFloat(SrcBlend, (float)UnityEngine.Rendering.BlendMode.One);
material.SetFloat(
DstBlend,
(float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha
);
material.SetFloat(ZWrite, 0.0f);
material.DisableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
minRenderQueue = (int)UnityEngine.Rendering.RenderQueue.GeometryLast + 1;
maxRenderQueue = (int)UnityEngine.Rendering.RenderQueue.Overlay - 1;
defaultRenderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
break;
}
if (
overrideRenderQueue
|| material.renderQueue < minRenderQueue
|| material.renderQueue > maxRenderQueue
)
{
if (!overrideRenderQueue)
Debug.LogFormat(
LogType.Log,
LogOption.NoStacktrace,
null,
"Render queue value outside of the allowed range ({0} - {1}) for selected Blend mode, resetting render queue to default",
minRenderQueue,
maxRenderQueue
);
material.renderQueue = defaultRenderQueue;
}
}
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5f9ac668e8174e13be50ba5a5de9f732
timeCreated: 1689088698
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c62e8ef712f821d418d1a8a248e75df3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
#nullable enable
using Speckle.Core.Kits;
using Speckle.Core.Logging;
using Speckle.Core.Models;
namespace Speckle.ConnectorUnity.Utils
{
public static class CoreUtils
{
public static void SetupInit()
{
Setup.Init(HostApplications.Unity.GetVersion(GetHostAppVersion()), HostApplications.Unity.Slug);
}
public static HostAppVersion GetHostAppVersion()
{
#if UNITY_2019
return HostAppVersion.v2019;
#elif UNITY_2020
return HostAppVersion.v2020;
#elif UNITY_2021
return HostAppVersion.v2021;
#elif UNITY_2022
return HostAppVersion.v2022;
#elif UNITY_2023
return HostAppVersion.v2023;
#elif UNITY_2024
return HostAppVersion.v2024;
#elif UNITY_2025
return HostAppVersion.v2025;
#else
return HostAppVersion.v;
#endif
}
public const string ObjectNameSeparator = " -- ";
/// <param name="speckleObject">The object to be named</param>
/// <returns>A human-readable Object name unique to the given <paramref name="speckleObject"/></returns>
public static string GenerateObjectName(Base speckleObject)
{
var prefix = GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject);
return $"{prefix}{ObjectNameSeparator}{speckleObject.id}";
}
public static string? GetFriendlyObjectName(Base speckleObject)
{
return speckleObject["name"] as string
?? speckleObject["Name"] as string
?? speckleObject["family"] as string;
}
/// <param name="speckleObject"></param>
/// <returns>The most significant type in a given <see cref="Base.speckle_type"/></returns>
public static string SimplifiedSpeckleType(Base speckleObject)
{
return speckleObject.speckle_type.Split(':')[^1];
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b5627857f30c8994c87469d287b2d115
guid: 280d43bfe5dc1d14181295ae5c365183
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,28 @@
#nullable enable
using System.Linq;
using Speckle.Core.Models;
namespace Speckle.ConnectorUnity.SpeckleUtils
{
/// <summary>
/// Extension methods for <see cref="Base"/> object models
/// </summary>
public static class ObjectExtensions
{
/// <summary>
/// Sets a property dynamically, checking if an instance prop with the same name exists
/// </summary>
/// <param name="speckleObject"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
#pragma warning disable CS0618
public static void SetDetachedPropertyChecked(this Base speckleObject, string propertyName, object? value)
{
if(speckleObject.GetInstanceMembersNames().Any(name => name == propertyName))
speckleObject[propertyName] = value;
else
speckleObject[$"@{propertyName}"] = value;
}
#pragma warning restore CS0618
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f88837bf55bd20642a1aed6b2a3b6b81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "Speckle.ConnectorUnity.SpeckleUtils",
"rootNamespace": "Speckle.ConnectorUnity",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e71520b51b4847d45ab0b1dacbf935c5
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d74d43435d909534d9b12f5ed3758603
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,155 @@
#nullable enable
using System;
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Speckle.ConnectorUnity.UnityUtils
{
public static class EngineUtils
{
public static void SafeDestroy(UnityEngine.Object obj)
{
if (Application.isPlaying)
UnityEngine.Object.Destroy(obj);
else
UnityEngine.Object.DestroyImmediate(obj);
}
public static bool Valid(this string name) => !string.IsNullOrEmpty(name);
public static Mesh SafeMeshGet(this MeshFilter mf) => Application.isPlaying ? mf.mesh : mf.sharedMesh;
public static void SafeMeshSet(this GameObject go, Mesh m, Material[] materials, bool addMeshComponentsIfNotFound = true)
{
MeshFilter? mf = go.GetComponent<MeshFilter>();
MeshRenderer? mr = go.GetComponent<MeshRenderer>();
if (addMeshComponentsIfNotFound)
{
if (mf == null)
mf = go.AddComponent<MeshFilter>();
if (mr == null)
mr = go.AddComponent<MeshRenderer>();
}
if (Application.isPlaying)
{
mf.mesh = m;
mr.materials = materials;
}
else
{
mf.sharedMesh = m;
mr.sharedMaterials = materials;
}
}
/// <summary>
/// Converts a Unity color to an ARBG int
/// </summary>
public static int ToIntColor(this Color c)
{
return
System.Drawing.Color
.FromArgb(Convert.ToInt32(c.r * 255), Convert.ToInt32(c.g * 255), Convert.ToInt32(c.b * 255))
.ToArgb();
}
/// <summary>
/// Converts a ARGB formatted int to a Unity Color
/// </summary>
public static Color ToUnityColor(this int c)
{
var argb = System.Drawing.Color.FromArgb(c);
return new Color(argb.R / 255f, argb.G / 255f, argb.B / 255f);
}
public static IEnumerator GetImageRoutine(string url, string authToken, Action<Texture2D?> callback)
{
using UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
www.SetRequestHeader("Authorization", $"Bearer {authToken}");
UnityWebRequestAsyncOperation request = www.SendWebRequest();
yield return request;
if(www.result != UnityWebRequest.Result.Success )
{
bool isDataError = www.result == UnityWebRequest.Result.DataProcessingError;
string error = isDataError
? $"{www.result}: {www.downloadHandler.error}"
: www.error;
Debug.LogWarning( $"Error fetching image from {www.url}: {error}" );
yield break;
}
Texture2D? texture = DownloadHandlerTexture.GetContent(www);
callback.Invoke(texture);
}
/// <summary>
/// Coroutine <see cref="CustomYieldInstruction"/> that starts and waits for an async <see cref="System.Threading.Tasks.Task"/>
/// to complete.
/// </summary>
/// <remarks>Useful for running async tasks from coroutines</remarks>
public class WaitForTask : CustomYieldInstruction
{
public readonly Task Task;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(Func<Task> function)
{
Task = Task.Run(function);
}
}
/// <inheritdoc cref="WaitForTask"/>
public sealed class WaitForTask<TResult> : CustomYieldInstruction
{
public readonly Task<TResult> Task;
public TResult Result => Task.Result;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(Func<Task<TResult>> function)
{
this.Task = System.Threading.Tasks.Task.Run(function);
}
}
public sealed class PerformanceThrottle : IEnumerator
{
private readonly IEnumerator _routine;
public PerformanceThrottle(IEnumerator routine)
{
_routine = routine;
}
public bool MoveNext()
{
while (_routine.MoveNext())
{
_ = 0;
}
return false;
}
public void Reset()
{
_routine.Reset();
}
public object? Current => _routine.Current;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98317fb9e5c090c46a7802a177a3c691
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "Speckle.ConnectorUnity.Utils.Unity",
"rootNamespace": "Speckle.ConnectorUnity.Utils.Unity",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 13c1dc1fefb5ca44aa3d755118da5f6c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,137 +1,127 @@
#nullable enable
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Speckle.ConnectorUnity.Utils
{
public static class Utils
public static class Utils
{
public static void SafeDestroy(UnityEngine.Object obj)
{
public static void SafeDestroy(UnityEngine.Object obj)
if (Application.isPlaying)
UnityEngine.Object.Destroy(obj);
else
UnityEngine.Object.DestroyImmediate(obj);
}
public static bool Valid(this string name) => !string.IsNullOrEmpty(name);
public static Mesh SafeMeshGet(this MeshFilter mf) => Application.isPlaying ? mf.mesh : mf.sharedMesh;
public static void SafeMeshSet(this GameObject go, Mesh m, Material[] materials, bool addMeshComponentsIfNotFound = true)
{
MeshFilter? mf = go.GetComponent<MeshFilter>();
MeshRenderer? mr = go.GetComponent<MeshRenderer>();
if (addMeshComponentsIfNotFound)
{
if (mf == null)
mf = go.AddComponent<MeshFilter>();
if (mr == null)
mr = go.AddComponent<MeshRenderer>();
}
if (Application.isPlaying)
{
mf.mesh = m;
mr.materials = materials;
}
else
{
mf.sharedMesh = m;
mr.sharedMaterials = materials;
}
}
/// <summary>
/// Converts a Unity color to an ARBG int
/// </summary>
public static int ToIntColor(this Color c)
{
return
System.Drawing.Color
.FromArgb(Convert.ToInt32(c.r * 255), Convert.ToInt32(c.g * 255), Convert.ToInt32(c.b * 255))
.ToArgb();
}
/// <summary>
/// Converts a ARGB formatted int to a Unity Color
/// </summary>
public static Color ToUnityColor(this int c)
{
var argb = System.Drawing.Color.FromArgb(c);
return new Color(argb.R / 255f, argb.G / 255f, argb.B / 255f);
}
public static IEnumerator GetImageRoutine(string url, string authToken, Action<Texture2D?> callback)
{
using UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
www.SetRequestHeader("Authorization", $"Bearer {authToken}");
UnityWebRequestAsyncOperation request = www.SendWebRequest();
yield return request;
if(www.result != UnityWebRequest.Result.Success )
{
bool isDataError = www.result == UnityWebRequest.Result.DataProcessingError;
string error = isDataError
? $"{www.result}: {www.downloadHandler.error}"
: www.error;
Debug.LogWarning( $"Error fetching image from {www.url}: {error}" );
yield break;
}
Texture2D? texture = DownloadHandlerTexture.GetContent(www);
callback.Invoke(texture);
}
/// <summary>
/// Coroutine <see cref="CustomYieldInstruction"/> that starts and waits for an async <see cref="System.Threading.Tasks.Task"/>
/// to complete.
/// </summary>
/// <remarks>Useful for running async tasks from coroutines</remarks>
public class WaitForTask : CustomYieldInstruction
{
public readonly Task Task;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(Func<Task> function)
{
if (Application.isPlaying)
UnityEngine.Object.Destroy(obj);
else
UnityEngine.Object.DestroyImmediate(obj);
}
public static bool Valid(this string name) => !string.IsNullOrEmpty(name);
public static Mesh SafeMeshGet(this MeshFilter mf) =>
Application.isPlaying ? mf.mesh : mf.sharedMesh;
public static void SafeMeshSet(
this GameObject go,
Mesh m,
Material[] materials,
bool addMeshComponentsIfNotFound = true
)
{
MeshFilter? mf = go.GetComponent<MeshFilter>();
MeshRenderer? mr = go.GetComponent<MeshRenderer>();
if (addMeshComponentsIfNotFound)
{
if (mf == null)
mf = go.AddComponent<MeshFilter>();
if (mr == null)
mr = go.AddComponent<MeshRenderer>();
}
if (Application.isPlaying)
{
mf.mesh = m;
mr.materials = materials;
}
else
{
mf.sharedMesh = m;
mr.sharedMaterials = materials;
}
}
/// <summary>
/// Converts a Unity color to an ARBG int
/// </summary>
public static int ToIntColor(this Color c)
{
return System.Drawing.Color
.FromArgb(
Convert.ToInt32(c.r * 255),
Convert.ToInt32(c.g * 255),
Convert.ToInt32(c.b * 255)
)
.ToArgb();
}
/// <summary>
/// Converts a ARGB formatted int to a Unity Color
/// </summary>
public static Color ToUnityColor(this int c)
{
var argb = System.Drawing.Color.FromArgb(c);
return new Color(argb.R / 255f, argb.G / 255f, argb.B / 255f);
}
public static IEnumerator GetImageRoutine(
string url,
string authToken,
Action<Texture2D?> callback
)
{
using UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
www.SetRequestHeader("Authorization", $"Bearer {authToken}");
UnityWebRequestAsyncOperation request = www.SendWebRequest();
yield return request;
if (www.result != UnityWebRequest.Result.Success)
{
bool isDataError = www.result == UnityWebRequest.Result.DataProcessingError;
string error = isDataError
? $"{www.result}: {www.downloadHandler.error}"
: www.error;
Debug.LogWarning($"Error fetching image from {www.url}: {error}");
yield break;
}
Texture2D? texture = DownloadHandlerTexture.GetContent(www);
callback.Invoke(texture);
}
/// <summary>
/// Coroutine <see cref="CustomYieldInstruction"/> that starts and waits for an async <see cref="System.Threading.Tasks.Task"/>
/// to complete.
/// </summary>
/// <remarks>Useful for running async tasks from coroutines</remarks>
public class WaitForTask : CustomYieldInstruction
{
public readonly Task Task;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(Func<Task> function, CancellationToken cancellationToken = default)
{
Task = Task.Run(function, cancellationToken);
}
}
/// <inheritdoc cref="WaitForTask"/>
public sealed class WaitForTask<TResult> : CustomYieldInstruction
{
public readonly Task<TResult> Task;
public TResult Result => Task.Result;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(
Func<Task<TResult>> function,
CancellationToken cancellationToken = default
)
{
this.Task = System.Threading.Tasks.Task.Run(function, cancellationToken);
}
Task = Task.Run(function);
}
}
/// <inheritdoc cref="WaitForTask"/>
public sealed class WaitForTask<TResult> : CustomYieldInstruction
{
public readonly Task<TResult> Task;
public TResult Result => Task.Result;
public override bool keepWaiting => !Task.IsCompleted;
public WaitForTask(Func<Task<TResult>> function)
{
this.Task = System.Threading.Tasks.Task.Run(function);
}
}
}
}
@@ -1,8 +1,8 @@
using System;
using System.Collections;
using System.Linq;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Helpers;
using UnityEngine;
#nullable enable
@@ -11,48 +11,40 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
[Serializable]
public sealed class AccountSelection : OptionSelection<Account>, IDisposable
{
private Client? _client;
private Client? client;
public override Client? Client
{
get
{
Account? account = Selected;
if (account == null)
return _client = null;
if (_client == null || !_client.Account.Equals(account))
return _client = new Client(account);
return _client;
if (account == null) return client = null;
if (client == null || !client.Account.Equals(account)) return client = new Client(account);
return client;
}
}
protected override string? KeyFunction(Account? value)
{
if (value is null)
return null;
return value.id + Crypt.Md5(value.serverInfo.url ?? "", "X2");
}
protected override string? KeyFunction(Account? value) => value?.id;
public override void RefreshOptions()
{
Account[] accounts;
try
{
accounts = AccountManager.GetAccounts().ToArray();
if (accounts.Length == 0)
if(accounts.Length == 0)
Debug.LogWarning("No Accounts found, please login in Manager");
}
catch (Exception e)
catch(Exception e)
{
accounts = Array.Empty<Account>();
Debug.LogWarning($"Unable to refresh {this}\n{e}");
}
GenerateOptions(accounts, isDefault: (a, i) => a.isDefault || i == 0);
}
public void Dispose()
{
_client?.Dispose();
client?.Dispose();
}
}
}
@@ -9,47 +9,38 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
[Serializable]
public sealed class BranchSelection : OptionSelection<Branch>
{
[field:
SerializeField,
Range(1, ServerLimits.BRANCH_GET_LIMIT),
Tooltip("Number of branches to request")
]
public int BranchesLimit { get; set; } = ServerLimits.OLD_BRANCH_GET_LIMIT;
[field: SerializeField, Range(1, 100), Tooltip("Number of commits to request")]
[field: SerializeField, Range(1,100), Tooltip("Number of branches to request")]
public int BranchesLimit { get; set; } = 100;
[field: SerializeField, Range(1,100), Tooltip("Number of commits to request")]
public int CommitsLimit { get; set; } = 25;
[field: SerializeReference]
public StreamSelection StreamSelection { get; private set; }
public override Client? Client => StreamSelection.Client;
public BranchSelection(StreamSelection streamSelection)
{
StreamSelection = streamSelection;
Initialise();
}
public void Initialise()
{
StreamSelection.OnSelectionChange = RefreshOptions;
}
protected override string? KeyFunction(Branch? value) => value?.id;
protected override string? KeyFunction(Branch? value) => value?.name;
public override void RefreshOptions()
{
Stream? stream = StreamSelection.Selected;
if (stream == null)
return;
IReadOnlyList<Branch> branches;
if (stream == null) return;
IList<Branch> branches;
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}");
branches = Array.Empty<Branch>();
@@ -6,32 +6,35 @@ using UnityEngine;
#nullable enable
namespace Speckle.ConnectorUnity.Wrappers.Selection
{
[Serializable]
public sealed class CommitSelection : OptionSelection<Commit>
{
[field: SerializeReference]
public BranchSelection BranchSelection { get; private set; }
public override Client? Client => BranchSelection.Client;
public CommitSelection(BranchSelection branchSelection)
{
BranchSelection = branchSelection;
Initialise();
}
public void Initialise()
{
BranchSelection.OnSelectionChange = RefreshOptions;
}
protected override string? KeyFunction(Commit? value) => value?.id;
protected override string? KeyFunction(Commit? value) => value?.id;
public override void RefreshOptions()
{
Branch? branch = BranchSelection.Selected;
if (branch == null)
return;
Branch? branch = BranchSelection!.Selected;
if (branch == null) return;
List<Commit> commits = branch.commits.items;
GenerateOptions(commits, (_, i) => i == 0);
}
@@ -10,7 +10,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
/// <summary>
/// Reusable <see langword="abstract"/> serializable type that abstracts
/// the fetching of <typeparamref name="TOption"/> objects.
/// And exposes an list of <see cref="Options"/>
/// And exposes an <see cref="Array"/> of <see cref="Options"/>
/// with serialised selection.
/// </summary>
/// <typeparam name="TOption"></typeparam>
@@ -18,42 +18,29 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public abstract class OptionSelection<TOption>
where TOption : class
{
public IReadOnlyList<TOption> Options { get; protected set; } = Array.Empty<TOption>();
[SerializeField] private int selectedIndex = -1;
private Dictionary<string, int>? _indexMap;
[SerializeField]
private string? selectedId;
public int SelectedIndex
{
get => selectedIndex;
set
{
selectedIndex = value;
OnSelectionChange?.Invoke();
}
}
public TOption? Selected
{
get
{
if (selectedId == null)
return null;
TryGetOption(selectedId, out var value);
return value;
}
set
{
selectedId = KeyFunction(value);
OnSelectionChange?.Invoke();
if (Options is null) return null;
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null;
return Options[SelectedIndex];
}
}
public bool TryGetOption(string key, [NotNullWhen(true)] out TOption? value)
{
if (_indexMap is not null && _indexMap.TryGetValue(key, out int index))
{
value = Options[index];
return true;
}
value = null;
return false;
}
public TOption[] Options { get; protected set; } = Array.Empty<TOption>();
public Action? OnSelectionChange { get; set; }
public abstract Client? Client { get; }
@@ -63,38 +50,30 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public abstract void RefreshOptions();
protected void GenerateOptions(
IReadOnlyCollection<TOption?> source,
Func<TOption, int, bool> isDefault
)
protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault)
{
List<TOption> optionsToAdd = new(source.Count);
Dictionary<string, int> indexMap = new(source.Count);
string? defaultOption = null;
List<TOption> optionsToAdd = new (source.Count);
int defaultOption = -1;
int index = 0;
foreach (TOption? a in source)
{
if (a == null)
continue;
var key = KeyFunction(a);
if (a == null) continue;
optionsToAdd.Add(a);
indexMap.Add(key, index);
if (isDefault(a, index))
defaultOption = key;
if (isDefault(a, index)) defaultOption = index;
index++;
}
string? currentSelected = selectedId;
if (currentSelected is null || !indexMap.ContainsKey(currentSelected))
TOption? currentSelected = Selected;
bool selectionOutOfRange = SelectedIndex < 0 || SelectedIndex >= optionsToAdd.Count;
if (selectionOutOfRange
|| (currentSelected != null
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex])))
{
selectedId = defaultOption;
selectedIndex = defaultOption;
}
Options = optionsToAdd;
_indexMap = indexMap;
Options = optionsToAdd.ToArray();
//Debug.Log($"{this.GetType()} updated");
OnSelectionChange?.Invoke();
}
}
@@ -9,20 +9,17 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
[Serializable]
public sealed class StreamSelection : OptionSelection<Stream>
{
private const int DefaultRequestLimit = 50;
[field: SerializeField, Range(1, 100), Tooltip("Number of streams to request")]
public int StreamsLimit { get; set; } = DefaultRequestLimit;
private const int DEFAULT_REQUEST_LIMIT = 50;
[field: SerializeField, Range(1,100), Tooltip("Number of streams to request")]
public int StreamsLimit { get; set; } = DEFAULT_REQUEST_LIMIT;
[field: SerializeReference]
public AccountSelection AccountSelection { get; private set; }
public StreamSelection(AccountSelection accountSelection)
{
AccountSelection = accountSelection;
Initialise();
}
public void Initialise()
{
AccountSelection.OnSelectionChange = RefreshOptions;
@@ -31,17 +28,15 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
public override Client? Client => AccountSelection.Client;
protected override string? KeyFunction(Stream? value) => value?.id;
public override void RefreshOptions()
{
if (Client == null)
return;
IReadOnlyList<Stream> streams;
if (Client == null) return;
IList<Stream> streams;
try
{
streams = Client.StreamsGet(StreamsLimit).GetAwaiter().GetResult();
}
catch (Exception e)
catch(Exception e)
{
Debug.LogWarning($"Unable to refresh {this}\n{e}");
streams = Array.Empty<Stream>();
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
using Speckle.Core.Api;
using Speckle.Core.Models;
using Speckle.Core.Serialisation;
using Speckle.Newtonsoft.Json;
using UnityEngine;
namespace Speckle.ConnectorUnity.Wrappers
@@ -16,20 +17,21 @@ namespace Speckle.ConnectorUnity.Wrappers
[Serializable, DisallowMultipleComponent]
public class SpeckleProperties : MonoBehaviour, ISerializationCallbackReceiver
{
[SerializeField, HideInInspector]
private string _serializedData = "";
[SerializeField, HideInInspector]
private bool _hasChanged;
private ObservableConcurrentDictionary<string, object> _data;
public IDictionary<string, object> Data
{
get => _data;
set
{
((ICollection<KeyValuePair<string, object>>)_data).Clear();
((ICollection<KeyValuePair<string, object>>) _data).Clear();
foreach (var kvp in value)
{
@@ -41,17 +43,18 @@ namespace Speckle.ConnectorUnity.Wrappers
[SerializeField, HideInInspector]
private string _serializedSpeckleType;
private Type _speckleType = typeof(Base);
public Type SpeckleType
{
public Type SpeckleType {
get => _speckleType ??= typeof(Base);
set
{
Debug.Assert(typeof(Base).IsAssignableFrom(value));
Debug.Assert(!value.IsAbstract);
_speckleType = value;
_hasChanged = true;
}
}
public SpeckleProperties()
@@ -61,7 +64,7 @@ namespace Speckle.ConnectorUnity.Wrappers
_hasChanged = true;
SpeckleType = typeof(Base);
}
private void CollectionChangeHandler(object sender, NotifyCollectionChangedEventArgs e)
{
_hasChanged = true;
@@ -69,22 +72,21 @@ namespace Speckle.ConnectorUnity.Wrappers
public void OnBeforeSerialize()
{
if (!_hasChanged)
return;
if (!_hasChanged) return;
_serializedData = Operations.Serialize(new SpeckleData(Data));
_hasChanged = false;
_serializedSpeckleType = SpeckleType.AssemblyQualifiedName;
}
public void OnAfterDeserialize()
{
var deserializer = new BaseObjectDeserializerV2();
Base speckleData = deserializer.Deserialize(_serializedData);
Data = speckleData.GetMembers();
_hasChanged = false;
try
{
_speckleType = Type.GetType(_serializedSpeckleType);
@@ -1,9 +1,9 @@
{
"name": "systems.speckle.speckle-unity",
"version": "2.18.1",
"version": "2.14.2",
"displayName": "Speckle Unity Connector",
"description": "AEC Interoperability for Unity through Speckle",
"unity": "2021.1",
"unity": "2018.4",
"documentationUrl": "https://speckle.guide/user/unity.html",
"changelogUrl": "https://speckle.systems/blog/",
"license": "Apache-2.0",
+2 -2
View File
@@ -1,2 +1,2 @@
m_EditorVersion: 2021.3.30f1
m_EditorVersionWithRevision: 2021.3.30f1 (b4360d7cdac4)
m_EditorVersion: 2021.3.22f1
m_EditorVersionWithRevision: 2021.3.22f1 (b6c551784ba3)
+16 -20
View File
@@ -5,14 +5,12 @@
[![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fdiscourse.speckle.works&style=flat-square&logo=discourse&logoColor=white)](https://discourse.speckle.works) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/user/unity.html)
## Introduction
This repo holds Speckle's Unity Connector + a sample project (Speckle playground). This connector is currently in an Alpha stage.
This repo holds Speckle's Unity Connector package + a sample project (Speckle playground).
The package offers several Unity Components to send and receive data from Speckle, and allows developers to easily develop their own components and features.
It has a simple UI, and is missing some of the comforts present in other connectors.
The connector uses our [Speckle .NET SDK](https://github.com/specklesystems/speckle-sharp).
This connector is meant to be used by developers, it doesn't have an elaborated UI but it offers convenience methods to send and receive data. The connector uses our [Speckle .NET SDK](https://github.com/specklesystems/speckle-sharp).
![unity](https://user-images.githubusercontent.com/2679513/108543628-3a83ff00-72dd-11eb-8792-3d43ce54e6af.gif)
@@ -22,28 +20,26 @@ If you are enjoying using Speckle, don't forget to ⭐ our [GitHub repositories]
and [join our community forum](https://speckle.community/) where you can post any questions, suggestions, and discuss exciting projects!
## Notice
We officially support Unity 2021.3 or newer.
We support Unity 2020 and 2021 (newer versions likely work, but aren't currently part of our test pipeline).
Features:
- Receive Speckle Objects at Editor or Runtime
- Send Speckle Objects at Editor or Runtime
- Send Speckle Objects at Runtime (editor support in the works!)
- Material override/substitution
- Automatic receiving changes
Currently tested on Windows, Linux, and MacOS.
Currently tested on Windows and MacOS. Experimental support for Android [in the works](https://github.com/specklesystems/speckle-unity/issues/68).
Android will work [with some signficant limitations](https://github.com/specklesystems/speckle-unity/issues/68), and other platforms likly work with similar limitations.
## Sample Project
This repo holds a simple sample project (Speckle Playground), containing an example GUI (UnityUI) for fetching stream/branch data, and sending/receiving geometry to/from Speckle.
Simply [download this repo](https://github.com/specklesystems/speckle-unity/archive/refs/heads/main.zip)
or clone with git, and open in Unity 2021.3 or newer.
## Sample project
This repo holds a simple sample project (Speckle Playground). Simply [download this repo](https://github.com/specklesystems/speckle-unity/archive/refs/heads/main.zip)
or clone with git, and open in Unity 2020.3.
```
git clone https://github.com/specklesystems/speckle-unity.git
```
The sample project contains an example GUI (UnityUI) for fetching stream/branch data, and receiving/sending geometry to Speckle.
## Installation (Package)
## Installation
To install the connector into your own Unity project (rather than using the sample project), open the Package Manager (`Windows -> Package Manager`)
and select **Add Package from git URL**. (requires [git](https://git-scm.com/downloads) installed)
@@ -63,18 +59,18 @@ We encourage everyone interested to hack / contribute / debug / give feedback to
### Requirements
- Unity 2021 or greater
- Have created an account on [app.speckle.systems](https://app.speckle.systems) (or your own server)
- Unity 2020.3+
- Have created an account on [speckle.xyz](https://speckle.xyz) (or your own server)
- Installed [Speckle Manager](https://speckle.guide/user/manager.html) (recommended, otherwise you'll need to implement your own authentication system in Unity)
### Dependencies
All dependencies to Speckle Core have been included; compiled in `systems.speckle.speckle-unity` package.
All dependencies to Speckle Core have been included; compiled in the Asset folder until we figure out how to best reference Core.
## Contributing
Please make sure you read the [contribution guidelines](https://github.com/specklesystems/speckle-sharp/blob/main/.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
## License