Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce33e7c454 | |||
| fb1e458970 | |||
| 2755a9abd7 | |||
| ee9795e39f | |||
| 999e6ae4ea | |||
| 8df96eeca4 | |||
| 6aa92d4c57 | |||
| 030cb277b8 | |||
| 5ee498afce | |||
| 0bb1591624 | |||
| 5dd889c898 | |||
| 9c7d1deb0a | |||
| 106d4c8e73 | |||
| 7c8f70c0c0 | |||
| f0be78888f | |||
| 892e77e0ff | |||
| 8a9c21f979 | |||
| aa46d49620 | |||
| 5d92e12eff | |||
| e1a3ae6b9c | |||
| d44ead55db | |||
| a3fb10570e | |||
| 57154a6fb8 | |||
| 5db1f450af | |||
| 62875c0a27 | |||
| b3c6b59721 | |||
| d9f7895b3f | |||
| dc58f6b0b0 | |||
| 556a7eaddf | |||
| 39a79be700 | |||
| 73ef71f4fd | |||
| 6a07ecfd5b | |||
| e512df9c82 | |||
| 59221e89ba | |||
| 32533ddb21 | |||
| a89e4cdfe7 |
@@ -0,0 +1,62 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dd598fed5008c44a815ba09e81a2d19
|
||||
guid: b5627857f30c8994c87469d287b2d115
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity;
|
||||
@@ -7,6 +8,7 @@ using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Speckle/Extras/Manual Receiver")]
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
public class ManualReceive : MonoBehaviour
|
||||
{
|
||||
@@ -30,7 +32,8 @@ public class ManualReceive : MonoBehaviour
|
||||
if(Time.timeSinceLevelLoad > 20) yield return null;
|
||||
Receive();
|
||||
}
|
||||
|
||||
|
||||
[ContextMenu(nameof(Receive))]
|
||||
public void Receive()
|
||||
{
|
||||
var account = new Account()
|
||||
@@ -48,14 +51,17 @@ public class ManualReceive : MonoBehaviour
|
||||
objectId,
|
||||
remoteTransport: transport,
|
||||
localTransport: localTransport,
|
||||
onErrorAction: (m, e)=> Debug.LogError(m + e),
|
||||
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(@base, parentObject.transform);
|
||||
|
||||
receiver.RecursivelyConvertToNative_Sync(@base, parentObject.transform);
|
||||
|
||||
Debug.Log($"Receive {objectId} completed");
|
||||
});
|
||||
@@ -2,13 +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]
|
||||
public class SendChildrenToSpeckle : MonoBehaviour
|
||||
{
|
||||
public LayerMask layerMask;
|
||||
@@ -22,7 +20,7 @@ public class SendChildrenToSpeckle : MonoBehaviour
|
||||
{
|
||||
sender = GetComponent<Sender>();
|
||||
}
|
||||
|
||||
|
||||
[ContextMenu(nameof(Send))]
|
||||
public void Send()
|
||||
{
|
||||
@@ -30,30 +28,32 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
{
|
||||
"name": "Speckle.Extra",
|
||||
"references":[ "GUID:eed1b8b83e2c0074d9e5de2348e3ff72", "GUID:e6adfdc4e436206479f48eafc82f32b5", "GUID:d274441ecc3eb3f43b093eec1503d681" ]
|
||||
}
|
||||
"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
|
||||
}
|
||||
+218
-193
@@ -1,219 +1,244 @@
|
||||
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
|
||||
{
|
||||
public class InteractionLogic : MonoBehaviour
|
||||
{
|
||||
private Receiver receiver;
|
||||
|
||||
public void InitReceiver(Stream stream, bool autoReceive)
|
||||
[Obsolete]
|
||||
public class InteractionLogic : MonoBehaviour
|
||||
{
|
||||
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
private Receiver _receiver;
|
||||
|
||||
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) =>
|
||||
public void InitReceiver(Stream stream, bool autoReceive)
|
||||
{
|
||||
statusText.text = $"Received {go.name}";
|
||||
MakeButtonsInteractable(true);
|
||||
receiveProgress.value = 0;
|
||||
receiveProgress.gameObject.SetActive(false);
|
||||
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
_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
|
||||
|
||||
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(() =>
|
||||
//populate branches
|
||||
branchesDropdown.options.Clear();
|
||||
List<Branch> branches = _receiver.Stream.branches.items;
|
||||
branches.Reverse();
|
||||
foreach (Branch branch in branches)
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Error {e.Message}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
Debug.LogError(e, this);
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// Recursively adds custom components to all children of a GameObject
|
||||
/// </summary>
|
||||
/// <param name="go"></param>
|
||||
private void AddComponents(GameObject go)
|
||||
{
|
||||
Destroy(receiver.ReceivedData);
|
||||
for (var i = 0; i < go.transform.childCount; i++)
|
||||
{
|
||||
var child = go.transform.GetChild(i);
|
||||
|
||||
if (child.childCount > 0)
|
||||
{
|
||||
AddComponents(child.gameObject);
|
||||
}
|
||||
|
||||
child.gameObject.AddComponent<Selectable>();
|
||||
|
||||
//Add extra Components
|
||||
//var rigidbody = child.gameObject.AddComponent<Rigidbody>();
|
||||
//rigidbody.mass = 10;
|
||||
}
|
||||
}
|
||||
|
||||
//update ui
|
||||
GameObject.Find("_SpeckleExamples").GetComponent<SpeckleExamples>().RemoveStreamPrefab(gameObject);
|
||||
public void InitSender(Stream stream)
|
||||
{
|
||||
gameObject.name = $"sender-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
|
||||
//kill it
|
||||
Destroy(gameObject);
|
||||
});
|
||||
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
-113
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Speckle.Core.Credentials;
|
||||
@@ -8,125 +9,124 @@ using Stream = Speckle.Core.Api.Stream;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
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()
|
||||
[Obsolete]
|
||||
public class SpeckleExamples : MonoBehaviour
|
||||
{
|
||||
if (SelectStreamText == null || StreamSelectionDropdown == null)
|
||||
{
|
||||
Debug.Log("Please set all input fields on _SpeckleExamples");
|
||||
return;
|
||||
}
|
||||
public Text SelectStreamText;
|
||||
public Text DetailsStreamText;
|
||||
public Dropdown StreamSelectionDropdown;
|
||||
public Button AddReceiverBtn;
|
||||
public Toggle AutoReceiveToggle;
|
||||
public Button AddSenderBtn;
|
||||
public GameObject StreamPanel;
|
||||
public Canvas StreamsCanvas;
|
||||
|
||||
var defaultAccount = AccountManager.GetDefaultAccount();
|
||||
if (defaultAccount == null)
|
||||
{
|
||||
Debug.Log("Please set a default account in SpeckleManager");
|
||||
return;
|
||||
}
|
||||
private List<Stream> StreamList = null;
|
||||
private Stream SelectedStream = null;
|
||||
private List<GameObject> StreamPanels = new List<GameObject>();
|
||||
|
||||
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
|
||||
async void Start()
|
||||
{
|
||||
if (SelectStreamText == null || StreamSelectionDropdown == null)
|
||||
{
|
||||
Debug.Log("Please set all input fields on _SpeckleExamples");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamList = await Streams.List(30);
|
||||
if (!StreamList.Any())
|
||||
{
|
||||
Debug.Log("There are no streams in your account, please create one online.");
|
||||
return;
|
||||
}
|
||||
var defaultAccount = AccountManager.GetDefaultAccount();
|
||||
if (defaultAccount == null)
|
||||
{
|
||||
Debug.Log("Please set a default account in SpeckleManager");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamSelectionDropdown.options.Clear();
|
||||
foreach (var stream in StreamList)
|
||||
{
|
||||
StreamSelectionDropdown.options.Add(new Dropdown.OptionData(stream.name + " - " + stream.id));
|
||||
}
|
||||
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
|
||||
|
||||
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
|
||||
//trigger ui refresh, maybe there's a better method
|
||||
StreamSelectionDropdown.value = -1;
|
||||
StreamSelectionDropdown.value = 0;
|
||||
StreamList = await Streams.List(30);
|
||||
if (!StreamList.Any())
|
||||
{
|
||||
Debug.Log("There are no streams in your account, please create one online.");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamSelectionDropdown.options.Clear();
|
||||
foreach (var stream in StreamList)
|
||||
{
|
||||
StreamSelectionDropdown.options.Add(
|
||||
new Dropdown.OptionData(stream.name + " - " + stream.id)
|
||||
);
|
||||
}
|
||||
|
||||
AddReceiverBtn.onClick.AddListener(AddReceiver);
|
||||
AddSenderBtn.onClick.AddListener(AddSender);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+226
-199
@@ -849,6 +849,192 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 160171836}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &161249242
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 161249247}
|
||||
- component: {fileID: 161249246}
|
||||
- component: {fileID: 161249245}
|
||||
- component: {fileID: 161249244}
|
||||
- component: {fileID: 161249243}
|
||||
m_Layer: 0
|
||||
m_Name: Speckle Connector
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 2800000, guid: ee2ed9d8fff3a1d4db5590491978062e, type: 3}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &161249243
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b95e704835cc48444b81e33c978f6f7f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 1485638386691080198
|
||||
<Stream>k__BackingField:
|
||||
rid: 1485638386691080199
|
||||
<Branch>k__BackingField:
|
||||
rid: 1485638386691080200
|
||||
OnBranchSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnSendProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080198
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 1
|
||||
- rid: 1485638386691080199
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 1485638386691080198
|
||||
- rid: 1485638386691080200
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 100
|
||||
<CommitsLimit>k__BackingField: 0
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 1485638386691080199
|
||||
--- !u!114 &161249244
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 942bf0cb27c5c5045bc4cbb7fc0fad71, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
url:
|
||||
--- !u!114 &161249245
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 1485638386691080194
|
||||
<Stream>k__BackingField:
|
||||
rid: 1485638386691080195
|
||||
<Branch>k__BackingField:
|
||||
rid: 1485638386691080196
|
||||
<Commit>k__BackingField:
|
||||
rid: 1485638386691080197
|
||||
OnCommitSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnReceiveProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnTotalChildrenCountKnown:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnComplete:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080194
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
- rid: 1485638386691080195
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 1485638386691080194
|
||||
- rid: 1485638386691080196
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 100
|
||||
<CommitsLimit>k__BackingField: 25
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 1485638386691080195
|
||||
- rid: 1485638386691080197
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 1485638386691080196
|
||||
--- !u!114 &161249246
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ed6cbf9ce4dca0349997d163ec9bce7e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<ConverterInstance>k__BackingField:
|
||||
rid: 1485638386691080193
|
||||
<AssetCache>k__BackingField: {fileID: 1438814813}
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080193
|
||||
type: {class: ConverterUnity, ns: Objects.Converter.Unity, asm: Objects.Converter}
|
||||
data:
|
||||
shouldConvertViews: 0
|
||||
shouldAttachProperties: 1
|
||||
<OpaqueMaterialShader>k__BackingField: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
<TranslucentMaterialShader>k__BackingField: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
<ModelUnits>k__BackingField: m
|
||||
--- !u!4 &161249247
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 9
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &194696812
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -928,165 +1114,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 194696812}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &218987857
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 218987861}
|
||||
- component: {fileID: 218987860}
|
||||
- component: {fileID: 218987859}
|
||||
- component: {fileID: 218987858}
|
||||
m_Layer: 0
|
||||
m_Name: Speckle Connector
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 2800000, guid: ee2ed9d8fff3a1d4db5590491978062e, type: 3}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &218987858
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b95e704835cc48444b81e33c978f6f7f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 5855987529328361546
|
||||
<Stream>k__BackingField:
|
||||
rid: 5855987529328361547
|
||||
<Branch>k__BackingField:
|
||||
rid: 5855987529328361548
|
||||
OnBranchSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnSendProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 5855987529328361546
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
- rid: 5855987529328361547
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 5855987529328361546
|
||||
- rid: 5855987529328361548
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 30
|
||||
<CommitsLimit>k__BackingField: 0
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 5855987529328361547
|
||||
--- !u!114 &218987859
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 5855987529328361542
|
||||
<Stream>k__BackingField:
|
||||
rid: 5855987529328361543
|
||||
<Branch>k__BackingField:
|
||||
rid: 5855987529328361544
|
||||
<Commit>k__BackingField:
|
||||
rid: 5855987529328361545
|
||||
OnCommitSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnReceiveProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnTotalChildrenCountKnown:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnComplete:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 5855987529328361542
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
- rid: 5855987529328361543
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 5855987529328361542
|
||||
- rid: 5855987529328361544
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 30
|
||||
<CommitsLimit>k__BackingField: 15
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 5855987529328361543
|
||||
- rid: 5855987529328361545
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 5855987529328361544
|
||||
--- !u!114 &218987860
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ed6cbf9ce4dca0349997d163ec9bce7e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<AssetCache>k__BackingField: {fileID: 1710028308}
|
||||
--- !u!4 &218987861
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 9
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &234733581
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1616,18 +1643,6 @@ Rigidbody:
|
||||
m_Interpolate: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 0
|
||||
--- !u!114 &540478226
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b3354e8208862c341940152f5340d41a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &641375517
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1755,6 +1770,18 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 641375517}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &701880765
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b3354e8208862c341940152f5340d41a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &712628247
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3019,6 +3046,21 @@ MonoBehaviour:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 1
|
||||
--- !u!114 &1438814813
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 88d6b4f2f80eaa14f9f07505f7e44ec2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
nativeCaches:
|
||||
- {fileID: 1923150226}
|
||||
- {fileID: 701880765}
|
||||
--- !u!1 &1464556211
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3599,21 +3641,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1707872729}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1710028308
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 88d6b4f2f80eaa14f9f07505f7e44ec2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
nativeCaches:
|
||||
- {fileID: 1771830985}
|
||||
- {fileID: 540478226}
|
||||
--- !u!1 &1729237655
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3726,19 +3753,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1762991479}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1771830985
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2a4a29c776298714c88f406ad39c6095, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
matchByName: 1
|
||||
--- !u!1 &1885647142
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -4043,6 +4057,19 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1903798475}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1923150226
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2a4a29c776298714c88f406ad39c6095, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
matchByName: 1
|
||||
--- !u!1 &2014586909
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Editor",
|
||||
"rootNamespace": "",
|
||||
"name": "EditorTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Objects.Utils;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
|
||||
public class PerformanceTest
|
||||
{
|
||||
private static readonly string[] dataSource = new[]
|
||||
{
|
||||
"https://latest.speckle.dev/streams/24c3741255/commits/0925840e09"
|
||||
};
|
||||
|
||||
|
||||
//This method is much faster
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_GetAwaiterResult(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Helpers.Receive(stream).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_TaskRunAsync(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Helpers.Receive(stream);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
// [UnityTest, TestCaseSource(nameof(dataSource))]
|
||||
// public IEnumerable Receive_Coroutine(string stream)
|
||||
// {
|
||||
// var stopwatch = Stopwatch.StartNew();
|
||||
//
|
||||
// Task t = Helpers.Receive(stream);
|
||||
// t.Start();
|
||||
//
|
||||
// yield return new WaitUntil(() => !t.IsCompleted || stopwatch.ElapsedMilliseconds >= 100000);
|
||||
//
|
||||
// stopwatch.Stop();
|
||||
// Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
// Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
// Assert.True(t.IsCompletedSuccessfully);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test]
|
||||
public void TestTriangulate()
|
||||
{
|
||||
|
||||
|
||||
Base b = Task.Run(async () =>
|
||||
{
|
||||
return await Helpers.Receive("https://speckle.xyz/streams/4a8fd0c6b6/commits/067bf723b1");
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
|
||||
foreach (Base child in b.Traverse(b => b is Objects.Geometry.Mesh))
|
||||
{
|
||||
if(child is not Objects.Geometry.Mesh m) continue;
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
m.TriangulateMesh();
|
||||
|
||||
Console.WriteLine($"took {stopwatch.ElapsedMilliseconds:ms} to triangulate {child.id}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a4f4baa829261d438b740c7d3028756
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
public abstract class ComponentTest<T> where T : Component
|
||||
{
|
||||
protected T sut;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
GameObject go = new();
|
||||
sut = go.AddComponent<T>();
|
||||
Assert.That(sut, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e2fe277dd9c47ad998138dcdbb024ae
|
||||
timeCreated: 1686757093
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(RecursiveConverter))]
|
||||
public class ConvertToNativeTests : ComponentTest<RecursiveConverter>
|
||||
{
|
||||
private static IEnumerable<string> TestCases()
|
||||
{
|
||||
yield return @"https://latest.speckle.dev/streams/c1faab5c62/commits/704984e22d";
|
||||
}
|
||||
|
||||
private static Base Receive(string stream)
|
||||
{
|
||||
return Task.Run(async () => await Helpers.Receive(stream)).Result;
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void ToNative_Passes(string stream)
|
||||
{
|
||||
Base testCase = Receive(stream);
|
||||
var results = sut.RecursivelyConvertToNative_Sync(testCase, null);
|
||||
Assert.That(results, Has.Count.GreaterThan(0));
|
||||
Assert.That(results, HasSomeComponent<Transform>());
|
||||
Assert.That(results, HasSomeComponent<MeshRenderer>());
|
||||
Assert.That(results, HasSomeComponent<SpeckleProperties>());
|
||||
}
|
||||
|
||||
private static Constraint HasSomeComponent<T>() where T : Component
|
||||
{
|
||||
return Has.Some.Matches<ConversionResult>(
|
||||
x =>
|
||||
{
|
||||
return x.WasSuccessful(out var success, out _)
|
||||
&& success.GetComponent<T>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d9b0fc7baaf51a4a8e2bcefad8bd7b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "PlayModeTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
"Speckle.ConnectorUnity.Components",
|
||||
"Utils",
|
||||
"Speckle.ConnectorUnity.Wrappers"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll",
|
||||
"SpeckleCore2.dll",
|
||||
"Objects.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79301723eb79d2745ab1e1a9360f6f2d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(SpeckleReceiver))]
|
||||
public sealed class SpeckleReceiverTests : ComponentTest<SpeckleReceiver>
|
||||
{
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAsync_Succeeds()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
var task = new Utils.Utils.WaitForTask<Base>(async () => await sut.ReceiveAsync(default));
|
||||
yield return task;
|
||||
Base myBase = task.Result;
|
||||
Assert.That(myBase, Is.Not.Null);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Async_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
sut.ReceiveAndConvert_Async(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Routine_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
yield return sut.ReceiveAndConvert_Routine(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1756c50dd28a4e341a70866daa68a8d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.unity.2d.sprite": "1.0.0",
|
||||
"com.unity.collab-proxy": "2.0.1",
|
||||
"com.unity.ide.rider": "3.0.18",
|
||||
"com.unity.ide.visualstudio": "2.0.17",
|
||||
"com.unity.collab-proxy": "2.0.5",
|
||||
"com.unity.ide.rider": "3.0.24",
|
||||
"com.unity.ide.visualstudio": "2.0.18",
|
||||
"com.unity.ide.vscode": "1.2.5",
|
||||
"com.unity.test-framework": "1.1.31",
|
||||
"com.unity.test-framework": "1.1.33",
|
||||
"com.unity.textmeshpro": "3.0.6",
|
||||
"com.unity.timeline": "1.6.4",
|
||||
"com.unity.timeline": "1.6.5",
|
||||
"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",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"com.unity.collab-proxy": {
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.5",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
@@ -21,7 +21,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.rider": {
|
||||
"version": "3.0.18",
|
||||
"version": "3.0.24",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.visualstudio": {
|
||||
"version": "2.0.17",
|
||||
"version": "2.0.18",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -62,7 +62,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.test-framework": {
|
||||
"version": "1.1.31",
|
||||
"version": "1.1.33",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -82,7 +82,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.timeline": {
|
||||
"version": "1.6.4",
|
||||
"version": "1.6.5",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f1a8151dcdc40ac8662b102c2f25277
|
||||
timeCreated: 1687985346
|
||||
+262
-121
@@ -1,28 +1,103 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
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 static bool generateAssets = false;
|
||||
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;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
//Preview image
|
||||
{
|
||||
_foldOutStatus = EditorGUILayout.Foldout(_foldOutStatus, "Preview Image");
|
||||
if (_foldOutStatus)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
|
||||
if (_previewImage != null)
|
||||
GUI.DrawTexture(rect, _previewImage);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
|
||||
//Receive settings
|
||||
{
|
||||
bool prev = GUI.enabled;
|
||||
GUI.enabled = !speckleReceiver.IsReceiving;
|
||||
//Receive button
|
||||
bool userRequestedReceive = GUILayout.Button("Receive!");
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", _generateAssets);
|
||||
if (_generateAssets != selection)
|
||||
{
|
||||
_generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
GUI.enabled = prev;
|
||||
|
||||
if (speckleReceiver.IsReceiving)
|
||||
{
|
||||
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
|
||||
);
|
||||
EditorGUI.ProgressBar(rect, value, $"{percent}%");
|
||||
}
|
||||
else if (userRequestedReceive)
|
||||
{
|
||||
var id = Progress.Start(
|
||||
"Receiving Speckle data",
|
||||
"Fetching commit data",
|
||||
Progress.Options.Sticky
|
||||
);
|
||||
Progress.ShowDetails();
|
||||
|
||||
try
|
||||
{
|
||||
await ReceiveSelection(id).ConfigureAwait(true);
|
||||
Progress.Finish(id);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Canceled);
|
||||
Debug.Log($"Receive operation cancelled\n{ex}", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Failed);
|
||||
Debug.LogError($"Receive operation failed {ex}", this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Init();
|
||||
@@ -30,7 +105,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
private void Init()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
UpdatePreviewImage();
|
||||
speckleReceiver.OnCommitSelectionChange.AddListener(_ => UpdatePreviewImage());
|
||||
UpdateGenerateAssets();
|
||||
@@ -38,161 +113,224 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
private void UpdatePreviewImage()
|
||||
{
|
||||
previewImage = null;
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t);
|
||||
_previewImage = null;
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => _previewImage = t);
|
||||
}
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
|
||||
private async Task ReceiveSelection(int progressId)
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
//Preview image
|
||||
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
|
||||
if (foldOutStatus)
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
bool shouldCancel = false;
|
||||
|
||||
Progress.RegisterCancelCallback(
|
||||
progressId,
|
||||
() =>
|
||||
{
|
||||
speckleReceiver.Cancel();
|
||||
shouldCancel = true;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f/4f);
|
||||
if(previewImage != null) GUI.DrawTexture(rect, previewImage);
|
||||
var token = speckleReceiver.BeginOperation();
|
||||
commitObject = await Task.Run(
|
||||
async () => await ReceiveCommit(progressId).ConfigureAwait(false),
|
||||
token
|
||||
)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.FinishOperation();
|
||||
}
|
||||
|
||||
|
||||
//Receive button
|
||||
bool receive = GUILayout.Button("Receive!");
|
||||
int childrenConverted = 0;
|
||||
int childrenFailed = 0;
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets);
|
||||
if (generateAssets != selection)
|
||||
{
|
||||
generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
int totalChildren = (int)Math.Min(commitObject.totalChildrenCount, int.MaxValue);
|
||||
float totalChildrenFloat = commitObject.totalChildrenCount;
|
||||
|
||||
|
||||
if (receive)
|
||||
var convertProgress = Progress.Start(
|
||||
"Converting To Native",
|
||||
"Preparing...",
|
||||
Progress.Options.Indefinite | Progress.Options.Sticky,
|
||||
progressId
|
||||
);
|
||||
|
||||
bool BeforeConvert(TraversalContext context)
|
||||
{
|
||||
await ReceiveAndConvert(speckleReceiver).ConfigureAwait(false);;
|
||||
Base b = context.current;
|
||||
|
||||
//NOTE: progress wont reach 100% because not all objects are convertable
|
||||
float progress = (childrenConverted + childrenFailed) / totalChildrenFloat;
|
||||
|
||||
if (shouldCancel)
|
||||
return false;
|
||||
|
||||
shouldCancel = EditorUtility.DisplayCancelableProgressBar(
|
||||
"Converting To Native...",
|
||||
$"{b.speckle_type} - {b.id}",
|
||||
progress
|
||||
);
|
||||
|
||||
return !shouldCancel;
|
||||
}
|
||||
|
||||
foreach (
|
||||
var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
|
||||
commitObject,
|
||||
speckleReceiver.transform,
|
||||
BeforeConvert
|
||||
)
|
||||
)
|
||||
{
|
||||
Base speckleObject = conversionResult.SpeckleObject;
|
||||
if (conversionResult.WasSuccessful(out _, out var ex))
|
||||
{
|
||||
childrenConverted++;
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenFailed++;
|
||||
Debug.LogWarning(
|
||||
$"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}",
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
Progress.Report(
|
||||
progressId,
|
||||
childrenConverted + childrenFailed,
|
||||
totalChildren,
|
||||
"Receiving objects"
|
||||
);
|
||||
|
||||
if (shouldCancel)
|
||||
break;
|
||||
}
|
||||
|
||||
var resultString = $"{childrenConverted} {nameof(GameObject)}s created";
|
||||
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
|
||||
);
|
||||
|
||||
Progress.Finish(convertProgress);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task<GameObject?> ReceiveAndConvert(SpeckleReceiver speckleReceiver)
|
||||
private async Task<Base> ReceiveCommit(int progressId)
|
||||
{
|
||||
speckleReceiver.CancellationTokenSource?.Cancel();
|
||||
if (!speckleReceiver.GetSelection(out Client? client, out _, out Commit? commit, out string? error))
|
||||
{
|
||||
Debug.LogWarning($"Not ready to receive: {error}", speckleReceiver);
|
||||
return null;
|
||||
}
|
||||
|
||||
Base? commitObject = await ReceiveCommit(speckleReceiver, client.ServerUrl).ConfigureAwait(true);;
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
if (commitObject == null) return null;
|
||||
|
||||
var gameObject = Convert(speckleReceiver, commitObject, commit.id);
|
||||
Debug.Log($"Successfully received and converted commit: {commit.id}", target);
|
||||
return gameObject;
|
||||
}
|
||||
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
|
||||
|
||||
private GameObject Convert(SpeckleReceiver receiver, Base commitObject, string name)
|
||||
{
|
||||
//Convert Speckle Objects
|
||||
int childrenConverted = 0;
|
||||
float totalChildren = commitObject.totalChildrenCount;
|
||||
|
||||
void BeforeConvertCallback(Base b)
|
||||
{
|
||||
//TODO: this is an incorrect way of measuring progress, as totalChildren != total convertable children
|
||||
float progress = childrenConverted++ / totalChildren;
|
||||
|
||||
EditorUtility.DisplayProgressBar("Converting To Native...",
|
||||
$"{b.speckle_type} - {b.id}",
|
||||
progress);
|
||||
}
|
||||
|
||||
var go = receiver.ConvertToNativeWithCategories(commitObject,
|
||||
name, BeforeConvertCallback);
|
||||
go.transform.SetParent(receiver.transform);
|
||||
return go;
|
||||
}
|
||||
|
||||
private async Task<Base?> ReceiveCommit(SpeckleReceiver speckleReceiver, string serverLogName)
|
||||
{
|
||||
string message = $"Receiving data from {serverLogName}...";
|
||||
EditorUtility.DisplayProgressBar(message, "", 0);
|
||||
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;
|
||||
void OnTotalChildrenKnown(int count)
|
||||
{
|
||||
totalObjectCount = count;
|
||||
};
|
||||
|
||||
Progress.Report(progressId, 0, totalObjectCount, "Receiving objects");
|
||||
}
|
||||
|
||||
void OnProgress(ConcurrentDictionary<string, int> dict)
|
||||
{
|
||||
var currentProgress = dict.Values.Average();
|
||||
var progress = (float) currentProgress / totalObjectCount;
|
||||
EditorApplication.delayCall += () =>
|
||||
bool r = dict.TryGetValue("RemoteTransport", out int rtProgress);
|
||||
bool l = dict.TryGetValue("SQLite", out int ltProgress);
|
||||
if (r || l)
|
||||
{
|
||||
bool shouldCancel = EditorUtility.DisplayCancelableProgressBar(message,
|
||||
$"{currentProgress}/{totalObjectCount}",
|
||||
progress);
|
||||
|
||||
if (shouldCancel)
|
||||
{
|
||||
CancelReceive();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void OnError(string message, Exception e)
|
||||
{
|
||||
if (e is not OperationCanceledException)
|
||||
{
|
||||
Debug.LogError($"Receive failed: {message}\n{e}", speckleReceiver);
|
||||
var fetched = (rtProgress + ltProgress);
|
||||
Progress.Report(
|
||||
transport,
|
||||
fetched,
|
||||
totalObjectCount,
|
||||
$"{fetched}/{totalObjectCount}"
|
||||
);
|
||||
}
|
||||
CancelReceive();
|
||||
};
|
||||
|
||||
Base? commitObject = null;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress);
|
||||
speckleReceiver.OnErrorAction.AddListener(OnError);
|
||||
commitObject = await speckleReceiver.ReceiveAsync().ConfigureAwait(false);;
|
||||
if (commitObject == null)
|
||||
{
|
||||
Debug.LogWarning($"Receive warning: Receive operation returned null", speckleReceiver);
|
||||
}
|
||||
commitObject = await speckleReceiver
|
||||
.ReceiveAsync(speckleReceiver.CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Progress.Finish(transport);
|
||||
Progress.Finish(deserialize);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Progress.Finish(transport, Progress.Status.Canceled);
|
||||
Progress.Finish(deserialize, Progress.Status.Canceled);
|
||||
throw;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Progress.Finish(transport, Progress.Status.Failed);
|
||||
Progress.Finish(deserialize, Progress.Status.Failed);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.RemoveListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.RemoveListener(OnProgress);
|
||||
speckleReceiver.OnErrorAction.RemoveListener(OnError);
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
return commitObject;
|
||||
}
|
||||
|
||||
private void CancelReceive()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Speckle/Speckle Connector", false, 10)]
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand) {
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand)
|
||||
{
|
||||
// Create a custom game object
|
||||
GameObject go = new GameObject("Speckle Connector");
|
||||
GameObject go = new("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
|
||||
@@ -201,10 +339,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
|
||||
}
|
||||
|
||||
@@ -12,36 +12,40 @@ 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 SelectionFilter selectedFilter = SelectionFilter.Children;
|
||||
|
||||
private SelectionFilter _selectedFilter = SelectionFilter.Children;
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
//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();
|
||||
@@ -50,30 +54,35 @@ 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);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return await speckleSender.SendDataAsync(data, true);
|
||||
}
|
||||
@@ -81,37 +90,36 @@ 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
|
||||
);
|
||||
}
|
||||
|
||||
private void CancelSend()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
public class StreamManagerEditor : UnityEditor.Editor
|
||||
{
|
||||
private bool _foldOutAccount;
|
||||
private int _totalChildrenCount = 0;
|
||||
private int _totalChildrenCount;
|
||||
private StreamManager _streamManager;
|
||||
|
||||
private static bool generateAssets;
|
||||
private static bool _generateAssets;
|
||||
|
||||
public int StreamsLimit { get; set; } = 30;
|
||||
public int BranchesLimit { get; set; } = 75;
|
||||
@@ -98,7 +98,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
private List<Branch> Branches
|
||||
{
|
||||
get => _streamManager.Branches;
|
||||
|
||||
set => _streamManager.Branches = value;
|
||||
}
|
||||
|
||||
@@ -142,7 +141,11 @@ 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;
|
||||
@@ -155,7 +158,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
|
||||
private async Task Receive()
|
||||
{
|
||||
var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
|
||||
@@ -163,45 +165,82 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
try
|
||||
{
|
||||
Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[
|
||||
SelectedCommitIndex
|
||||
];
|
||||
// Receive Speckle Objects
|
||||
var @base = await Operations.Receive(
|
||||
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].referencedObject,
|
||||
selectedCommit.referencedObject,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: dict =>
|
||||
{
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
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);
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
var go = _streamManager.ConvertRecursivelyToNative(@base,
|
||||
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, BeforeConvertCallback);
|
||||
_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)
|
||||
{
|
||||
@@ -215,8 +254,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
_streamManager = (StreamManager) target;
|
||||
|
||||
_streamManager = (StreamManager)target;
|
||||
|
||||
#region Account GUI
|
||||
|
||||
@@ -226,12 +264,15 @@ 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)
|
||||
{
|
||||
@@ -247,26 +288,37 @@ 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();
|
||||
}
|
||||
@@ -284,9 +336,13 @@ 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)
|
||||
{
|
||||
@@ -311,23 +367,29 @@ 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();
|
||||
|
||||
@@ -339,12 +401,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,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@@ -18,23 +16,28 @@ 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;
|
||||
}
|
||||
@@ -43,32 +46,39 @@ 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();
|
||||
@@ -77,32 +87,40 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
|
||||
|
||||
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;
|
||||
@@ -111,16 +129,20 @@ 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}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+88
-57
@@ -13,8 +13,10 @@ 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>)[]
|
||||
@@ -29,74 +31,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>)[]
|
||||
{
|
||||
("Stream id", s => s.id),
|
||||
("Description", s => s.description),
|
||||
("Is Public", s => s.isPublic.ToString()),
|
||||
("Role", s => s.role),
|
||||
("Created at", s => s.createdAt),
|
||||
("Updated at", s => s.updatedAt),
|
||||
("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>)[]
|
||||
{
|
||||
("Description", s => s.description),
|
||||
};
|
||||
|
||||
details = new (string, Func<Branch, string>)[] { ("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>)[]
|
||||
{
|
||||
("Commit Id", s => s.id),
|
||||
("Author Name", s => s.authorName),
|
||||
("Created At", s => s.createdAt),
|
||||
("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);
|
||||
@@ -105,7 +107,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
protected string[] properties = { };
|
||||
|
||||
protected (string, Func<TOption, string>)[] details = { };
|
||||
|
||||
|
||||
private string[] GetFormattedOptions(TOption[] options)
|
||||
{
|
||||
int optionsCount = options.Length;
|
||||
@@ -118,7 +120,12 @@ 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;
|
||||
|
||||
@@ -142,80 +149,106 @@ 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.SelectedIndex = 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;
|
||||
|
||||
var detailsHeight = GUIDetailsPropertyCount * (standardHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
|
||||
return standardHeight + detailsHeight + EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.standardVerticalSpacing;
|
||||
if (!property.isExpanded)
|
||||
return standardHeight + 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
|
||||
{
|
||||
@@ -223,12 +256,13 @@ 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), 0)};
|
||||
|
||||
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]))
|
||||
{
|
||||
@@ -237,18 +271,15 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
};
|
||||
searchList.Add(entry);
|
||||
}
|
||||
|
||||
|
||||
return searchList;
|
||||
}
|
||||
|
||||
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
|
||||
{
|
||||
onSetIndexCallback?.Invoke((int)SearchTreeEntry.userData);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -34,12 +34,16 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
.Where(x => x.FullName != null)
|
||||
.Select(x => x.FullName!.Replace('.', '/'));
|
||||
|
||||
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();
|
||||
|
||||
SpeckleTypeOptionStrings = strings.Append(nameof(Base)).ToArray();
|
||||
SpeckleTypeOptions = options.Append(typeof(Base)).ToArray();
|
||||
Debug.Assert(SpeckleTypeOptions.Length == SpeckleTypeOptionStrings.Length);
|
||||
}
|
||||
|
||||
|
||||
private static GUILayoutOption[] propLayoutOptions = { GUILayout.ExpandWidth(true) };
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
|
||||
+183
-156
@@ -5,185 +5,212 @@ using Speckle.Core.Logging;
|
||||
using Speckle.Core.Transports;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Sentry;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
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 ) )]
|
||||
public class Receiver : MonoBehaviour
|
||||
{
|
||||
public string StreamId;
|
||||
public string BranchName = "main";
|
||||
public Stream Stream;
|
||||
public int TotalChildrenCount = 0;
|
||||
public GameObject ReceivedData;
|
||||
|
||||
private bool AutoReceive;
|
||||
private bool DeleteOld;
|
||||
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
|
||||
private Action<string, Exception> OnErrorAction;
|
||||
private Action<int> OnTotalChildrenCountKnown;
|
||||
private Action<GameObject> OnDataReceivedAction;
|
||||
|
||||
|
||||
private Client Client { get; set; }
|
||||
|
||||
|
||||
public Receiver()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Receiver manually
|
||||
/// A Speckle Receiver, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions and subscriptions for you
|
||||
/// </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)
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
[Obsolete("See " + nameof(SpeckleReceiver))]
|
||||
public class Receiver : MonoBehaviour
|
||||
{
|
||||
StreamId = streamId;
|
||||
AutoReceive = autoReceive;
|
||||
DeleteOld = deleteOld;
|
||||
OnDataReceivedAction = onDataReceivedAction;
|
||||
OnErrorAction = onErrorAction;
|
||||
OnProgressAction = onProgressAction;
|
||||
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
|
||||
public string StreamId;
|
||||
public string BranchName = "main";
|
||||
public Stream Stream;
|
||||
public int TotalChildrenCount = 0;
|
||||
public GameObject ReceivedData;
|
||||
|
||||
Client = new Client(account ?? AccountManager.GetDefaultAccount());
|
||||
private bool AutoReceive;
|
||||
private bool DeleteOld;
|
||||
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
|
||||
private Action<string, Exception> OnErrorAction;
|
||||
private Action<int> OnTotalChildrenCountKnown;
|
||||
private Action<GameObject> OnDataReceivedAction;
|
||||
|
||||
private Client Client { get; set; }
|
||||
|
||||
if (AutoReceive)
|
||||
{
|
||||
Client.SubscribeCommitCreated(StreamId);
|
||||
Client.OnCommitCreated += Client_OnCommitCreated;
|
||||
}
|
||||
}
|
||||
public Receiver() { }
|
||||
|
||||
|
||||
/// <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
|
||||
/// <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
|
||||
)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
/// <summary>
|
||||
/// Gets and converts the data of the last commit on the Stream
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Receive()
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
if (Client == null || string.IsNullOrEmpty(StreamId))
|
||||
throw new Exception("Receiver has not been initialized. Please call Init().");
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var mainBranch = await Client.BranchGet(StreamId, BranchName, 1);
|
||||
if (!mainBranch.commits.items.Any())
|
||||
throw new Exception("This branch has no commits");
|
||||
var commit = mainBranch.commits.items[0];
|
||||
GetAndConvertObject(
|
||||
commit.referencedObject,
|
||||
commit.id,
|
||||
commit.sourceApplication,
|
||||
commit.authorId
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#region private methods
|
||||
|
||||
#region private methods
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a new commit is created on this stream
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void GetAndConvertObject(string objectId, string commitId)
|
||||
{
|
||||
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);
|
||||
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
/// <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)
|
||||
{
|
||||
var root = new GameObject()
|
||||
{
|
||||
name = commitId,
|
||||
};
|
||||
if (e.branchName == BranchName)
|
||||
{
|
||||
Debug.Log("New commit created");
|
||||
GetAndConvertObject(e.objectId, e.id, e.sourceApplication, e.authorId);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
private async void GetAndConvertObject(
|
||||
string objectId,
|
||||
string commitId,
|
||||
string sourceApplication,
|
||||
string authorId
|
||||
)
|
||||
{
|
||||
streamId = StreamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing!
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Client?.CommitCreatedSubscription?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,134 +16,150 @@ 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]
|
||||
public class Sender : MonoBehaviour
|
||||
{
|
||||
|
||||
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
|
||||
/// A Speckle Sender, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions for you
|
||||
/// </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)
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
[Obsolete("See " + nameof(SpeckleSender))]
|
||||
public class Sender : MonoBehaviour
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
private ServerTransport transport;
|
||||
private RecursiveConverter converter;
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
#nullable enable
|
||||
|
||||
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)
|
||||
private void Awake()
|
||||
{
|
||||
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,
|
||||
});
|
||||
converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
onDataSentAction?.Invoke(res);
|
||||
}, cancellationToken);
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CancelOperations();
|
||||
}
|
||||
|
||||
public void CancelOperations()
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
transport?.Dispose();
|
||||
cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
#region private methods
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-10
@@ -29,16 +29,18 @@ 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);
|
||||
|
||||
@@ -46,10 +48,9 @@ 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())
|
||||
@@ -57,13 +58,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +83,4 @@ namespace Speckle.ConnectorUnity.Components
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
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)
|
||||
);
|
||||
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)
|
||||
);
|
||||
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
|
||||
)
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 942bf0cb27c5c5045bc4cbb7fc0fad71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+281
-71
@@ -2,63 +2,264 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
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>
|
||||
public readonly struct ConversionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public readonly Exception? exception;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used for Successful conversions
|
||||
/// </summary>
|
||||
/// <param name="traversalContext">The current traversal context</param>
|
||||
/// <param name="converted">The resultant ToNative conversion of <see cref="TraversalContext.current"/> context object</param>
|
||||
/// <exception cref="ArgumentNullException"/>
|
||||
public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted)
|
||||
: this(traversalContext, converted, null)
|
||||
{
|
||||
if (converted is null)
|
||||
throw new ArgumentNullException(nameof(converted));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used for Failed conversions
|
||||
/// </summary>
|
||||
/// <param name="traversalContext">The current conversion</param>
|
||||
/// <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
|
||||
)
|
||||
: this(traversalContext, converted, exception)
|
||||
{
|
||||
if (exception is null)
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
private ConversionResult(
|
||||
TraversalContext traversalContext,
|
||||
GameObject? converted,
|
||||
Exception? exception
|
||||
)
|
||||
{
|
||||
this.traversalContext = traversalContext;
|
||||
this.converted = converted;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/// <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
|
||||
)
|
||||
{
|
||||
converted = this.converted;
|
||||
exception = this.exception;
|
||||
return WasSuccessful();
|
||||
}
|
||||
|
||||
public bool WasSuccessful() => this.exception == null;
|
||||
|
||||
public Base SpeckleObject => traversalContext.current;
|
||||
}
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
|
||||
public IEnumerator ConvertToNative(Base rootObject, Transform? parent, IDictionary<Base, GameObject> outCreatedObjects)
|
||||
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
|
||||
/// <remarks>Calling this function will perform the conversion process synchronously</remarks>
|
||||
/// <returns>The conversion result</returns>
|
||||
public List<ConversionResult> RecursivelyConvertToNative_Sync(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
|
||||
InitializeAssetCache();
|
||||
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>
|
||||
public Coroutine RecursivelyConvertToNative_Coroutine(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
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"/>
|
||||
/// </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>
|
||||
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
|
||||
/// <returns>An unevaluated <see cref="IEnumerable"/> of all created <see cref="GameObject"/>s</returns>
|
||||
public IEnumerable<ConversionResult> RecursivelyConvertToNative_Enumerable(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
var userPredicate = predicate ?? (_ => true);
|
||||
|
||||
var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
|
||||
|
||||
var convertableObjects = traversalFunc.Traverse(rootObject)
|
||||
.Where(tc => ConverterInstance.CanConvertToNative(tc.current));
|
||||
var objectsToConvert = traversalFunc
|
||||
.Traverse(rootObject)
|
||||
.Where(x => ConverterInstance.CanConvertToNative(x.current))
|
||||
.Where(x => userPredicate(x));
|
||||
|
||||
foreach (TraversalContext tc in convertableObjects)
|
||||
Dictionary<Base, GameObject?> created = new();
|
||||
foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
|
||||
{
|
||||
Transform? nativeParent = parent;
|
||||
if (tc.parent != null && outCreatedObjects.TryGetValue(tc.parent.current, out GameObject p))
|
||||
nativeParent = p.transform;
|
||||
|
||||
GameObject? go = ConverterInstance.ConvertToNative(tc.current) as GameObject;
|
||||
if (go == null) continue;
|
||||
|
||||
go.transform.SetParent(parent, true);
|
||||
outCreatedObjects.Add(tc.current, go);
|
||||
|
||||
//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))
|
||||
go.name = AssetHelpers.GenerateObjectName(tc.current);
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
if (tc.current["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 (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
|
||||
if (!isActiveAndEnabled)
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot convert objects while {GetType()} is disabled"
|
||||
);
|
||||
|
||||
yield return go;
|
||||
yield return conversionResult;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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, Func<Base, bool> predicate)
|
||||
|
||||
/// <summary>
|
||||
/// Converts a objectTree (see <see cref="GraphTraversal"/>) to unevaluated enumerable.
|
||||
/// As this enumerable is iterated through, each context <see cref="Base"/> object will be converted to <see cref="GameObject"/> (if successful)
|
||||
/// 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)
|
||||
/// </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
|
||||
)
|
||||
{
|
||||
InitializeAssetCache();
|
||||
AssetCache.BeginWrite();
|
||||
|
||||
foreach (TraversalContext tc in objectTree)
|
||||
{
|
||||
ConversionResult result;
|
||||
try
|
||||
{
|
||||
Transform? currentParent = GetParent(tc, outCreatedObjects) ?? parent;
|
||||
|
||||
var converted = ConvertToNative(tc.current, currentParent);
|
||||
result = new ConversionResult(tc, converted);
|
||||
outCreatedObjects.TryAdd(tc.current, result.converted);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ConversionResult(tc, ex);
|
||||
}
|
||||
|
||||
yield return result;
|
||||
}
|
||||
|
||||
AssetCache.FinishWrite();
|
||||
}
|
||||
|
||||
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)
|
||||
return p.transform;
|
||||
|
||||
//Go one level up, and repeat!
|
||||
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");
|
||||
|
||||
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))
|
||||
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 (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)
|
||||
);
|
||||
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
|
||||
public IEnumerator ConvertCoroutine(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
List<GameObject> outCreatedObjects,
|
||||
Func<Base, bool> predicate
|
||||
)
|
||||
{
|
||||
foreach (string propertyName in GetPotentialChildren(rootObject))
|
||||
{
|
||||
@@ -66,7 +267,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Given <paramref name="o"/>,
|
||||
/// will recursively convert any objects in the tree
|
||||
@@ -74,15 +275,21 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <param name="o">The object to convert (<see cref="Base"/> or <see cref="List{T}"/> of)</param>
|
||||
/// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param>
|
||||
/// <returns> A list of all created <see cref="GameObject"/>s</returns>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent)
|
||||
=> RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b));
|
||||
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
|
||||
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>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent, Func<Base, bool> predicate)
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(
|
||||
object? o,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate
|
||||
)
|
||||
{
|
||||
InitializeAssetCache();
|
||||
|
||||
|
||||
var createdGameObjects = new List<GameObject>();
|
||||
try
|
||||
{
|
||||
@@ -95,10 +302,9 @@ namespace Speckle.ConnectorUnity.Components
|
||||
}
|
||||
|
||||
//TODO track event?
|
||||
|
||||
|
||||
return createdGameObjects;
|
||||
|
||||
|
||||
return createdGameObjects;
|
||||
}
|
||||
|
||||
private void InitializeAssetCache()
|
||||
@@ -111,10 +317,16 @@ namespace Speckle.ConnectorUnity.Components
|
||||
ConverterInstance.SetContextDocument(AssetCache);
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
[Obsolete]
|
||||
public virtual void RecurseTreeToNative(
|
||||
Base baseObject,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate,
|
||||
IList<GameObject> outCreatedObjects
|
||||
)
|
||||
{
|
||||
object? converted = null;
|
||||
if(predicate(baseObject))
|
||||
if (predicate(baseObject))
|
||||
converted = ConverterInstance.ConvertToNative(baseObject);
|
||||
|
||||
// Handle new GameObjects
|
||||
@@ -123,22 +335,23 @@ 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))
|
||||
go.name = AssetHelpers.GenerateObjectName(baseObject);
|
||||
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);
|
||||
|
||||
@@ -147,32 +360,29 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void ConvertChild(object? value, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
[Obsolete]
|
||||
protected virtual void ConvertChild(
|
||||
object? value,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate,
|
||||
IList<GameObject> outCreatedObjects
|
||||
)
|
||||
{
|
||||
foreach (Base b in GraphTraversal.TraverseMember(value))
|
||||
{
|
||||
RecurseTreeToNative(b, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative), true)]
|
||||
public GameObject ConvertRecursivelyToNative(Base @base, string name)
|
||||
{
|
||||
var parentObject = new GameObject(name);
|
||||
RecursivelyConvertToNative(@base, parentObject.transform);
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-17
@@ -8,7 +8,6 @@ using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
/// <summary>
|
||||
@@ -31,37 +30,44 @@ 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);
|
||||
@@ -71,7 +77,6 @@ namespace Speckle.ConnectorUnity.Components
|
||||
// Skip this object, and output any children
|
||||
outConverted.AddRange(convertedChildren);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Speckle.ConnectorUnity.Factories;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using UnityEngine;
|
||||
@@ -13,14 +14,24 @@ namespace Speckle.ConnectorUnity.Components
|
||||
[ExecuteAlways, DisallowMultipleComponent]
|
||||
public partial class RecursiveConverter : MonoBehaviour
|
||||
{
|
||||
public ISpeckleConverter ConverterInstance { get; set; } = ConverterFactory.GetDefaultConverter();
|
||||
[field: SerializeReference]
|
||||
public ISpeckleConverter ConverterInstance { get; set; } =
|
||||
ConverterFactory.GetDefaultConverter();
|
||||
|
||||
[field: SerializeField]
|
||||
public AggregateNativeCache AssetCache { get; set; }
|
||||
|
||||
private void Awake()
|
||||
protected void Awake()
|
||||
{
|
||||
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
Init();
|
||||
}
|
||||
|
||||
protected void Init()
|
||||
{
|
||||
Setup.Init(
|
||||
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
|
||||
HostApplications.Unity.Slug
|
||||
);
|
||||
|
||||
if (AssetCache == null)
|
||||
{
|
||||
@@ -28,6 +39,8 @@ namespace Speckle.ConnectorUnity.Components
|
||||
assetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup();
|
||||
this.AssetCache = assetCache;
|
||||
}
|
||||
|
||||
InitializeAssetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@
|
||||
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
|
||||
"GUID:13aec21e8e96f864bafd00df49f225fc",
|
||||
"GUID:d274441ecc3eb3f43b093eec1503d681",
|
||||
"GUID:50d889142fdf9de4b8501c6eaa4b3225"
|
||||
"GUID:50d889142fdf9de4b8501c6eaa4b3225",
|
||||
"GUID:7383cd71541a2aa48a7baf23f74b4d5f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers.Selection;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[assembly: InternalsVisibleTo("Speckle.ConnectorUnity.Components.Editor")]
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
[ExecuteAlways]
|
||||
@@ -25,144 +29,326 @@ 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; }
|
||||
|
||||
public RecursiveConverter Converter { get; private set; }
|
||||
|
||||
#nullable enable
|
||||
[Header("Events")]
|
||||
[HideInInspector]
|
||||
public CommitSelectionEvent OnCommitSelectionChange;
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction;
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction;
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown;
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete;
|
||||
public CommitSelectionEvent OnCommitSelectionChange = new();
|
||||
|
||||
#nullable enable
|
||||
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction = new();
|
||||
|
||||
//TODO runtime receiving
|
||||
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action<Base>? beforeConvertCallback = null)
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction = new();
|
||||
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown = new();
|
||||
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete = new();
|
||||
|
||||
protected CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
public CancellationToken CancellationToken => CancellationTokenSource?.Token ?? default;
|
||||
public bool IsReceiving => CancellationTokenSource != null;
|
||||
|
||||
/// <summary>
|
||||
/// Cancels any current receive operations
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note, this does not cancel any currently executing ConvertToNative, just the <see cref="Operations.Receive"/>.
|
||||
/// </remarks>
|
||||
/// <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()
|
||||
{
|
||||
Task<Base?> receiveOperation = Task.Run(ReceiveAsync);
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
Base? b = receiveOperation.Result;
|
||||
if (b == null) yield break;
|
||||
|
||||
//TODO make routine break for each catergory/object
|
||||
GameObject go = ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
|
||||
OnComplete.Invoke(go);
|
||||
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
|
||||
)
|
||||
{
|
||||
if (IsReceiving)
|
||||
{
|
||||
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 () =>
|
||||
{
|
||||
Base result = await ReceiveAsync(CancellationToken);
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
return result;
|
||||
});
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
if (receiveOperation.IsFaulted)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", receiveOperation.Exception);
|
||||
FinishOperation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
Base b = receiveOperation.Result;
|
||||
|
||||
foreach (var _ in Converter.RecursivelyConvertToNative_Enumerable(b, parent, predicate))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
OnComplete.Invoke(parent);
|
||||
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
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
BeginOperation();
|
||||
Base commitObject = await ReceiveAsync(CancellationToken).ConfigureAwait(true);
|
||||
Converter.RecursivelyConvertToNative_Sync(commitObject, parent, predicate);
|
||||
OnComplete.Invoke(parent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
FinishOperation();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives the selected commit object using async Task
|
||||
/// </summary>
|
||||
/// <returns>Awaitable commit object</returns>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <exception cref="SpeckleException">thrown when selection is incomplete</exception>
|
||||
public async Task<Base?> ReceiveAsync()
|
||||
/// <remarks>
|
||||
/// This function is safe to call concurrently from any threads.
|
||||
/// For this reason we use <paramref name="cancellationToken"/> parameter, rather than use the <see cref="CancellationToken"/> property
|
||||
/// <br/>
|
||||
/// Additionally, <see cref="OnComplete"/> and <see cref="OnErrorAction"/> won't be called.
|
||||
/// </remarks>
|
||||
public async Task<Base> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ValidateSelection(out Client? client, out Stream? stream, out Commit? commit);
|
||||
|
||||
Base result = await ReceiveAsync(
|
||||
client: client,
|
||||
streamId: stream.id,
|
||||
objectId: commit.referencedObject,
|
||||
commit: commit,
|
||||
onProgressAction: dict => OnReceiveProgressAction.Invoke(dict),
|
||||
onTotalChildrenCountKnown: c => OnTotalChildrenCountKnown.Invoke(c),
|
||||
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 account selection");
|
||||
|
||||
Stream? selectedStream = Stream.Selected;
|
||||
stream =
|
||||
selectedStream ?? throw new InvalidOperationException("Invalid stream selection");
|
||||
|
||||
Commit? selectedCommit = Commit.Selected;
|
||||
commit =
|
||||
selectedCommit ?? throw new InvalidOperationException("Invalid commit selection");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new receive operation with a <see cref="CancellationToken"/>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">already receiving</exception>
|
||||
protected internal CancellationToken BeginOperation()
|
||||
{
|
||||
if (IsReceiving)
|
||||
throw new InvalidOperationException(
|
||||
"A pending receive operation has already started"
|
||||
);
|
||||
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
if(!GetSelection(out Client? client, out Stream? stream, out Commit? commit, out string? error))
|
||||
throw new SpeckleException(error);
|
||||
|
||||
return await ReceiveAsync(
|
||||
token: CancellationTokenSource.Token,
|
||||
client: client,
|
||||
streamId: stream.id,
|
||||
objectId: commit.referencedObject,
|
||||
commitId: commit.id,
|
||||
onProgressAction: dict => OnReceiveProgressAction.Invoke(dict),
|
||||
onErrorAction: (m, e) => OnErrorAction.Invoke(m, e),
|
||||
onTotalChildrenCountKnown: c => OnTotalChildrenCountKnown.Invoke(c)
|
||||
).ConfigureAwait(false);
|
||||
CancellationTokenSource = new();
|
||||
|
||||
return CancellationTokenSource.Token;
|
||||
}
|
||||
|
||||
protected internal void FinishOperation()
|
||||
{
|
||||
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>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="streamId"></param>
|
||||
/// <param name="objectId"></param>
|
||||
/// <param name="commitId"></param>
|
||||
/// <param name="commit"></param>
|
||||
/// <param name="onProgressAction"></param>
|
||||
/// <param name="onErrorAction"></param>
|
||||
/// <param name="onTotalChildrenCountKnown"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<Base?> ReceiveAsync(CancellationToken token,
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <exception cref="Exception">Throws various types of exceptions to indicate faliure</exception>
|
||||
/// <returns>The requested Speckle object</returns>
|
||||
public static async Task<Base> ReceiveAsync(
|
||||
Client client,
|
||||
string streamId,
|
||||
string objectId,
|
||||
string? commitId,
|
||||
Commit? commit,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null,
|
||||
Action<int>? onTotalChildrenCountKnown = null)
|
||||
Action<int>? onTotalChildrenCountKnown = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
using var transport = new ServerTransportV2(client.Account, streamId);
|
||||
|
||||
transport.CancellationToken = token;
|
||||
|
||||
Base? ret = null;
|
||||
try
|
||||
{
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
transport.CancellationToken = cancellationToken;
|
||||
|
||||
ret = await Operations.Receive(
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Base? requestedObject = await Operations
|
||||
.Receive(
|
||||
objectId: objectId,
|
||||
cancellationToken: token,
|
||||
cancellationToken: cancellationToken,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction,
|
||||
onErrorAction: (s, ex) =>
|
||||
{
|
||||
//Don't wrap cancellation exceptions!
|
||||
if (ex is OperationCanceledException)
|
||||
throw ex;
|
||||
|
||||
//HACK: Sometimes, the task was cancelled, and Operations.Receive doesn't fail in a reliable way. In this case, the exception is often simply a symptom of a cancel.
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
SpeckleLog.Logger.Warning(
|
||||
ex,
|
||||
"A task was cancelled, ignoring potentially symptomatic exception"
|
||||
);
|
||||
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);
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Receive);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
//Read receipt
|
||||
try
|
||||
Analytics.TrackEvent(
|
||||
client.Account,
|
||||
Analytics.Events.Receive,
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
await client.CommitReceived(token, new CommitReceivedInput
|
||||
{ "mode", nameof(SpeckleReceiver) },
|
||||
{
|
||||
streamId = streamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Do nothing!
|
||||
Debug.LogWarning($"Failed to send read receipt\n{e}");
|
||||
"sourceHostApp",
|
||||
HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug
|
||||
},
|
||||
{ "sourceHostAppVersion", commit?.sourceApplication ?? "" },
|
||||
{ "hostPlatform", Application.platform.ToString() },
|
||||
{
|
||||
"isMultiplayer",
|
||||
commit?.authorId != 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(
|
||||
cancellationToken,
|
||||
new CommitReceivedInput
|
||||
{
|
||||
streamId = streamId,
|
||||
commitId = commit?.id,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(
|
||||
CoreUtils.GetHostAppVersion()
|
||||
)
|
||||
}
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
onErrorAction?.Invoke(e.Message, e);
|
||||
// Do nothing!
|
||||
Debug.LogWarning($"Failed to send read receipt\n{e}");
|
||||
}
|
||||
|
||||
return ret;
|
||||
return requestedObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for using <see cref="RecursiveConverter"/>.
|
||||
/// Creates blank GameObjects for each property/category of the root object.
|
||||
@@ -171,8 +357,18 @@ 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>
|
||||
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);
|
||||
|
||||
@@ -180,10 +376,9 @@ 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())
|
||||
@@ -191,52 +386,56 @@ 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>
|
||||
/// <param name="commit"></param>
|
||||
/// <param name="error">error messages for </param>
|
||||
/// <returns>true if selection is complete, as we are ready to receive</returns>
|
||||
[Obsolete("Use " + nameof(ValidateSelection))]
|
||||
public bool GetSelection(
|
||||
[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;
|
||||
@@ -244,30 +443,35 @@ 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><see langword="false"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns>
|
||||
public bool GetPreviewImage(/*bool allAngles,*/ Action<Texture2D?> callback)
|
||||
/// <returns>The executing <see cref="Coroutine"/> or <see langword="null"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns>
|
||||
public Coroutine? GetPreviewImage( /*bool allAngles,*/
|
||||
Action<Texture2D?> callback
|
||||
)
|
||||
{
|
||||
Account? account = Account.Selected;
|
||||
if (account == null) return false;
|
||||
if (account == null)
|
||||
return null;
|
||||
string? streamId = Stream.Selected?.id;
|
||||
if (streamId == null) return false;
|
||||
if (streamId == null)
|
||||
return null;
|
||||
string? commitId = Commit.Selected?.id;
|
||||
if (commitId == null) return false;
|
||||
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;
|
||||
|
||||
StartCoroutine(Utils.Utils.GetImageRoutine(url, authToken, callback));
|
||||
return true;
|
||||
|
||||
return StartCoroutine(Utils.Utils.GetImageRoutine(url, authToken, callback));
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Open Speckle Stream in Browser")]
|
||||
protected void OpenUrlInBrowser()
|
||||
@@ -276,6 +480,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
#endif
|
||||
|
||||
public string GetSelectedUrl()
|
||||
{
|
||||
string serverUrl = Account.Selected!.serverInfo.url;
|
||||
@@ -283,12 +488,15 @@ namespace Speckle.ConnectorUnity.Components
|
||||
string? branchName = Branch.Selected?.name;
|
||||
string? commitId = Commit.Selected?.id;
|
||||
|
||||
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}";
|
||||
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>();
|
||||
@@ -305,32 +513,73 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Stream.Initialise();
|
||||
Branch.Initialise();
|
||||
Commit.Initialise();
|
||||
Commit.OnSelectionChange =
|
||||
() => OnCommitSelectionChange?.Invoke(Commit.Selected);
|
||||
if(Account.Options is not {Length: > 0} || forceRefresh)
|
||||
Commit.OnSelectionChange = () => OnCommitSelectionChange?.Invoke(Commit.Selected);
|
||||
if (Account.Options is not { Length: > 0 } || forceRefresh)
|
||||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
|
||||
#region Deprecated members
|
||||
|
||||
[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)
|
||||
);
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
Base? b = receiveOperation.Result;
|
||||
if (b == null)
|
||||
yield break;
|
||||
|
||||
//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<GameObject> { }
|
||||
[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> { }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers.Selection;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
@@ -22,39 +23,49 @@ 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;
|
||||
|
||||
[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);
|
||||
|
||||
|
||||
ServerTransport transport = new ServerTransport(client.Account, stream.id);
|
||||
transport.CancellationToken = CancellationTokenSource.Token;
|
||||
|
||||
return await SendDataAsync(CancellationTokenSource.Token,
|
||||
|
||||
return await SendDataAsync(
|
||||
CancellationTokenSource.Token,
|
||||
remoteTransport: transport,
|
||||
data: data,
|
||||
client: client,
|
||||
@@ -65,89 +76,114 @@ namespace Speckle.ConnectorUnity.Components
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<string> SendDataAsync(CancellationToken cancellationToken,
|
||||
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)
|
||||
Action<string, Exception>? onErrorAction = null
|
||||
)
|
||||
{
|
||||
string res = await Operations.Send(
|
||||
data,
|
||||
cancellationToken: cancellationToken,
|
||||
new List<ITransport>{remoteTransport},
|
||||
new List<ITransport> { remoteTransport },
|
||||
useDefaultCache: true,
|
||||
disposeTransports: true,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
|
||||
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(cancellationToken, data, client, streamId, branchName, res, commitMessage);
|
||||
|
||||
string commitId = await CreateCommit(
|
||||
cancellationToken,
|
||||
data,
|
||||
client,
|
||||
streamId,
|
||||
branchName,
|
||||
res,
|
||||
commitMessage
|
||||
);
|
||||
string url = $"{client.ServerUrl}/streams/{streamId}/commits/{commitId}";
|
||||
Debug.Log($"Data successfully sent to <a href=\"{url}\">{url}</a>");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static async Task<string> CreateCommit(CancellationToken cancellationToken,
|
||||
|
||||
public static async Task<string> CreateCommit(
|
||||
CancellationToken cancellationToken,
|
||||
Base data,
|
||||
Client client,
|
||||
string streamId,
|
||||
string branchName,
|
||||
string objectId,
|
||||
string message)
|
||||
string message
|
||||
)
|
||||
{
|
||||
string commitId = await client.CommitCreate(cancellationToken,
|
||||
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,
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
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;
|
||||
@@ -155,8 +191,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Open Speckle Stream in Browser")]
|
||||
protected void OpenUrlInBrowser()
|
||||
@@ -165,24 +200,26 @@ 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();
|
||||
@@ -193,24 +230,24 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Stream.Initialise();
|
||||
Branch.Initialise();
|
||||
Branch.OnSelectionChange = () => OnBranchSelectionChange?.Invoke(Branch.Selected);
|
||||
if(Account.Options is not {Length: > 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+42
-21
@@ -1,29 +1,50 @@
|
||||
using Objects.BuiltElements;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Speckle View3D to a GameObject
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
/// <returns></returns>
|
||||
public GameObject View3DToNative(View3D speckleView)
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
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);
|
||||
[Tooltip("Enable/Disable the converting of Speckle View objects to Unity Cameras")]
|
||||
public bool shouldConvertViews;
|
||||
|
||||
AttachSpeckleProperties(go, speckleView.GetType(),speckleView.GetMembers());
|
||||
return go;
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+124
-84
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Objects.Other;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
using Speckle.Core.Logging;
|
||||
@@ -23,7 +22,6 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
|
||||
#region helper methods
|
||||
|
||||
|
||||
@@ -34,29 +32,33 @@ 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 = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
var f = GetConversionFactor(units);
|
||||
return VectorByCoordinates(x, y, z, f);
|
||||
}
|
||||
|
||||
public Vector3 VectorFromPoint(Point p) => VectorByCoordinates(p.x, p.y, p.z, p.units);
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="arr"></param>
|
||||
/// <returns></returns>
|
||||
public Vector3[] ArrayToPoints(IList<double> arr, string units)
|
||||
{
|
||||
if (arr.Count % 3 != 0) throw new Exception("Array malformed: length%3 != 0.");
|
||||
if (arr.Count % 3 != 0)
|
||||
throw new Exception("Array malformed: length not a multiple of 3");
|
||||
|
||||
Vector3[] points = new Vector3[arr.Count / 3];
|
||||
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
var f = GetConversionFactor(units);
|
||||
|
||||
for (int i = 2, k = 0; i < arr.Count; i += 3)
|
||||
points[k++] = VectorByCoordinates(arr[i - 2], arr[i - 1], arr[i], f);
|
||||
@@ -71,7 +73,7 @@ namespace Objects.Converter.Unity
|
||||
//TODO: more of these
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
@@ -80,7 +82,6 @@ namespace Objects.Converter.Unity
|
||||
//switch y and z
|
||||
return new Point(p.x, p.z, p.y);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -89,7 +90,8 @@ 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?
|
||||
|
||||
@@ -115,11 +117,10 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
Vector3 newPt = VectorByCoordinates(point.x, point.y, point.z, point.units);
|
||||
|
||||
var go = NewPointBasedGameObject(new Vector3[] {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>
|
||||
@@ -127,7 +128,11 @@ 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;
|
||||
@@ -157,37 +162,63 @@ namespace Objects.Converter.Unity
|
||||
var go = NewPointBasedGameObject(points, curve.speckle_type);
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
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 <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, Type excludeType)
|
||||
/// <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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -195,7 +226,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)
|
||||
{
|
||||
@@ -212,17 +243,14 @@ namespace Objects.Converter.Unity
|
||||
return sobject;
|
||||
}
|
||||
|
||||
public GameObject? InstanceToNative(Instance instance)
|
||||
public GameObject InstanceToNative(Instance instance)
|
||||
{
|
||||
if (instance.definition == null)
|
||||
{
|
||||
Debug.Log($"Skipping {typeof(BlockInstance)} {instance.id}, block definition was null");
|
||||
return null;
|
||||
}
|
||||
throw new ArgumentException("Definition was null", nameof(instance));
|
||||
|
||||
var defName = instance.definition["name"] as string ?? "";
|
||||
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;
|
||||
@@ -231,28 +259,37 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
|
||||
// Convert the block definition
|
||||
GameObject native = new GameObject(defName);
|
||||
|
||||
GameObject native = new(defName);
|
||||
|
||||
List<SMesh> meshes = new();
|
||||
List<Base> others = new();
|
||||
|
||||
var geometry = instance.definition is BlockDefinition b
|
||||
? b.geometry
|
||||
: GraphTraversal.TraverseMember(instance.definition["elements"]);
|
||||
|
||||
: 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 = AssetHelpers.GenerateObjectName(instance.definition);
|
||||
string name = CoreUtils.GenerateObjectName(instance.definition);
|
||||
nativeMesh.name = name;
|
||||
LoadedAssets.TrySaveObject(instance.definition, nativeMesh);
|
||||
}
|
||||
@@ -263,74 +300,82 @@ 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);
|
||||
if (instance["name"] is string instanceName) native.name = instanceName;
|
||||
|
||||
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 = Speckle.Core.Kits.Units.GetConversionFactor(speckleTransform.units, ModelUnits);
|
||||
var sf = GetConversionFactor(speckleTransform.units);
|
||||
var smatrix = speckleTransform.matrix;
|
||||
|
||||
|
||||
return new Matrix4x4
|
||||
{
|
||||
// Left (X -> X)
|
||||
[0, 0] = smatrix.M11,
|
||||
[2, 0] = smatrix.M21,
|
||||
[1, 0] = smatrix.M31,
|
||||
[3, 0] = smatrix.M41,
|
||||
|
||||
[0, 0] = (float)smatrix.M11,
|
||||
[2, 0] = (float)smatrix.M21,
|
||||
[1, 0] = (float)smatrix.M31,
|
||||
[3, 0] = (float)smatrix.M41,
|
||||
//Up (Z -> Y)
|
||||
[0, 2] = smatrix.M12,
|
||||
[2, 2] = smatrix.M22,
|
||||
[1, 2] = smatrix.M32,
|
||||
[3, 2] = smatrix.M42,
|
||||
|
||||
[0, 2] = (float)smatrix.M12,
|
||||
[2, 2] = (float)smatrix.M22,
|
||||
[1, 2] = (float)smatrix.M32,
|
||||
[3, 2] = (float)smatrix.M42,
|
||||
//Forwards (Y -> Z)
|
||||
[0, 1] = smatrix.M13,
|
||||
[2, 1] = smatrix.M23,
|
||||
[1, 1] = smatrix.M33,
|
||||
[3, 1] = smatrix.M43,
|
||||
|
||||
[0, 1] = (float)smatrix.M13,
|
||||
[2, 1] = (float)smatrix.M23,
|
||||
[1, 1] = (float)smatrix.M33,
|
||||
[3, 1] = (float)smatrix.M43,
|
||||
//Translation
|
||||
[0, 3] = (float) (smatrix.M14 * sf),
|
||||
[2, 3] = (float) (smatrix.M24 * sf),
|
||||
[1, 3] = (float) (smatrix.M34 * sf),
|
||||
[3, 3] = smatrix.M44,
|
||||
[0, 3] = (float)(smatrix.M14 * sf),
|
||||
[2, 3] = (float)(smatrix.M24 * sf),
|
||||
[1, 3] = (float)(smatrix.M34 * sf),
|
||||
[3, 3] = (float)smatrix.M44,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -342,16 +387,11 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+100
-169
@@ -3,9 +3,7 @@ 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.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
@@ -21,13 +19,8 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
|
||||
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
|
||||
@@ -37,7 +30,8 @@ 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++)
|
||||
@@ -50,31 +44,50 @@ 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;
|
||||
@@ -124,7 +137,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()));
|
||||
|
||||
@@ -148,56 +161,6 @@ 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
|
||||
|
||||
|
||||
@@ -209,13 +172,10 @@ namespace Objects.Converter.Unity
|
||||
/// <param name="element">The <see cref="Base"/> object being converted</param>
|
||||
/// <param name="meshes">Collection of <see cref="Objects.Geometry.Mesh"/>es that shall be converted</param>
|
||||
/// <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)
|
||||
public GameObject MeshesToNative(Base element, IReadOnlyCollection<SMesh> meshes)
|
||||
{
|
||||
if (!meshes.Any())
|
||||
{
|
||||
Debug.Log($"Skipping {element.GetType()} {element.id}, zero {typeof(SMesh)} provided");
|
||||
return null;
|
||||
}
|
||||
throw new ArgumentException("Expected at least one Mesh", nameof(meshes));
|
||||
|
||||
Material[] nativeMaterials = RenderMaterialsToNative(meshes);
|
||||
|
||||
@@ -223,11 +183,11 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
//Convert a new one
|
||||
MeshToNativeMesh(meshes, out nativeMesh, out center);
|
||||
string name = AssetHelpers.GenerateObjectName(element);
|
||||
string name = CoreUtils.GenerateObjectName(element);
|
||||
nativeMesh.name = name;
|
||||
LoadedAssets.TrySaveObject(element, nativeMesh);
|
||||
}
|
||||
|
||||
|
||||
var go = new GameObject();
|
||||
go.transform.position = center;
|
||||
go.SafeMeshSet(nativeMesh, nativeMaterials);
|
||||
@@ -235,21 +195,17 @@ namespace Objects.Converter.Unity
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts <paramref name="speckleMesh"/> to a <see cref="GameObject"/> with a <see cref="MeshRenderer"/>
|
||||
/// </summary>
|
||||
/// <param name="speckleMesh">Mesh to convert</param>
|
||||
/// <returns></returns>
|
||||
public GameObject? MeshToNative(SMesh speckleMesh)
|
||||
public GameObject MeshToNative(SMesh speckleMesh)
|
||||
{
|
||||
if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
|
||||
{
|
||||
Debug.Log($"Skipping mesh {speckleMesh.id}, mesh data was empty");
|
||||
return null;
|
||||
}
|
||||
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)));
|
||||
@@ -257,17 +213,18 @@ 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;
|
||||
}
|
||||
@@ -276,30 +233,33 @@ 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;
|
||||
@@ -310,7 +270,6 @@ namespace Objects.Converter.Unity
|
||||
nativeMesh.SetUVs(0, uvs);
|
||||
nativeMesh.SetColors(vertexColors);
|
||||
|
||||
|
||||
int j = 0;
|
||||
foreach (var subMeshTriangles in subMeshes)
|
||||
{
|
||||
@@ -326,12 +285,14 @@ 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>();
|
||||
@@ -340,17 +301,22 @@ 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();
|
||||
@@ -364,7 +330,8 @@ 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)
|
||||
{
|
||||
@@ -372,14 +339,20 @@ 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
|
||||
{
|
||||
@@ -397,12 +370,13 @@ 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)
|
||||
{
|
||||
@@ -413,89 +387,46 @@ 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 = AssetHelpers.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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
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;
|
||||
using STransform = Objects.Other.Transform;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f91633e63e64685a6b2a4fd609312a0
|
||||
timeCreated: 1689085502
|
||||
+16
-17
@@ -1,23 +1,22 @@
|
||||
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;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
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)
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
return value * f;
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,14 @@ 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();
|
||||
@@ -26,10 +32,11 @@ namespace Objects.Converter.Unity
|
||||
public string Author => "Speckle";
|
||||
public string WebsiteOrEmail => "https://speckle.systems";
|
||||
|
||||
public ProgressReport Report { get; }
|
||||
public ProgressReport Report => throw new NotImplementedException();
|
||||
public ReceiveMode ReceiveMode { get; set; }
|
||||
|
||||
public IEnumerable<string> GetServicedApplications() => new string[] {HostApplications.Unity.Name};
|
||||
public IEnumerable<string> GetServicedApplications() =>
|
||||
new[] { HostApplications.Unity.Name };
|
||||
|
||||
public AbstractNativeCache LoadedAssets { get; private set; }
|
||||
|
||||
@@ -37,13 +44,23 @@ 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);
|
||||
@@ -51,7 +68,9 @@ 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);
|
||||
}
|
||||
|
||||
@@ -87,14 +106,16 @@ 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)
|
||||
@@ -111,8 +132,10 @@ namespace Objects.Converter.Unity
|
||||
return View3DToNative(v);
|
||||
case Mesh o:
|
||||
return MeshToNative(o);
|
||||
case BlockInstance o:
|
||||
case Instance o:
|
||||
return InstanceToNative(o);
|
||||
case Collection c:
|
||||
return CollectionToNative(c);
|
||||
default:
|
||||
|
||||
//Object is not a raw geometry, convert it as display value element
|
||||
@@ -120,7 +143,11 @@ 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;
|
||||
}
|
||||
@@ -129,9 +156,8 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IList<string> DisplayValuePropertyAliases { get; set; } =
|
||||
new[] {"displayValue", "@displayValue", "displayMesh", "@displayMesh"};
|
||||
public static IList<string> DisplayValuePropertyAliases { get; set; } =
|
||||
new[] { "displayValue", "@displayValue", "displayMesh", "@displayMesh" };
|
||||
|
||||
public GameObject? DisplayValueToNative(Base @object)
|
||||
{
|
||||
@@ -143,7 +169,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);
|
||||
}
|
||||
@@ -151,16 +177,20 @@ namespace Objects.Converter.Unity
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SpeckleProperties AttachSpeckleProperties(GameObject go, Type speckleType,
|
||||
IDictionary<string, object?> properties)
|
||||
|
||||
private SpeckleProperties? AttachSpeckleProperties(
|
||||
GameObject go,
|
||||
Type speckleType,
|
||||
Func<IDictionary<string, object?>> properties
|
||||
)
|
||||
{
|
||||
if (!shouldAttachProperties)
|
||||
return null;
|
||||
var sd = go.AddComponent<SpeckleProperties>();
|
||||
sd.Data = properties;
|
||||
sd.Data = properties.Invoke();
|
||||
sd.SpeckleType = speckleType;
|
||||
return sd;
|
||||
}
|
||||
|
||||
|
||||
public List<Base> ConvertToSpeckle(List<object> objects)
|
||||
{
|
||||
@@ -172,6 +202,18 @@ 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)
|
||||
@@ -183,6 +225,21 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
public GameObject CollectionToNative(Collection collection)
|
||||
{
|
||||
var name =
|
||||
collection.name
|
||||
?? $"{collection.collectionType} -- {collection.applicationId ?? collection.id}";
|
||||
var go = new GameObject(name);
|
||||
AttachSpeckleProperties(go, collection.GetType(), () => GetProperties(collection));
|
||||
if (name == "Rooms")
|
||||
{
|
||||
go.SetActive(false);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public bool CanConvertToNative(Base @object)
|
||||
{
|
||||
switch (@object)
|
||||
@@ -195,13 +252,15 @@ namespace Objects.Converter.Unity
|
||||
// return true;
|
||||
// case Curve _:
|
||||
// return true;
|
||||
// case View3D _:
|
||||
// return true;
|
||||
case View2D _:
|
||||
return false;
|
||||
case Mesh _:
|
||||
// case View2D:
|
||||
// return false;
|
||||
case View3D _:
|
||||
return shouldConvertViews;
|
||||
case Collection:
|
||||
return true;
|
||||
case BlockInstance _:
|
||||
case Mesh:
|
||||
return true;
|
||||
case Instance:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -217,4 +276,4 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+25
-1
@@ -23,6 +23,9 @@ PluginImporter:
|
||||
Exclude WebGL: 0
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
Exclude WindowsStoreApps: 0
|
||||
Exclude iOS: 0
|
||||
Exclude tvOS: 0
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
@@ -74,9 +77,30 @@ PluginImporter:
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
enabled: 1
|
||||
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:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
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,67 +9,69 @@
|
||||
".NETStandard,Version=v2.0/": {
|
||||
"SpeckleCore2/2.0.999-local": {
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "5.1.1",
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.3",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"NETStandard.Library": "2.0.3",
|
||||
"Polly": "7.2.3",
|
||||
"Polly.Contrib.WaitAndRetry": "1.1.1",
|
||||
"Polly.Extensions.Http": "3.0.0",
|
||||
"Sentry": "3.29.0",
|
||||
"Sentry.Serilog": "3.29.0",
|
||||
"Sentry": "3.33.0",
|
||||
"Sentry.Serilog": "3.33.0",
|
||||
"Serilog": "2.12.0",
|
||||
"Serilog.Enrichers.ClientInfo": "1.2.0",
|
||||
"Serilog.Enrichers.ClientInfo": "1.3.0",
|
||||
"Serilog.Enrichers.GlobalLogContext": "3.0.0",
|
||||
"Serilog.Exceptions": "8.4.0",
|
||||
"Serilog.Sinks.Console": "4.1.0",
|
||||
"Serilog.Sinks.Seq": "5.2.2",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2"
|
||||
"SerilogTimings": "3.0.1",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"System.DoubleNumerics": "3.1.3"
|
||||
},
|
||||
"runtime": {
|
||||
"SpeckleCore2.dll": {}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client/5.1.1": {
|
||||
"GraphQL.Client/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "5.1.1",
|
||||
"GraphQL.Client.Abstractions.Websocket": "5.1.1",
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions/5.1.1": {
|
||||
"GraphQL.Client.Abstractions/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "5.1.1"
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.Abstractions.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
|
||||
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "5.1.1"
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.Abstractions.Websocket.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives/5.1.1": {
|
||||
"GraphQL.Primitives/6.0.0": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Primitives.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -142,20 +144,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite/7.0.3": {
|
||||
"Microsoft.Data.Sqlite/7.0.5": {
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.3",
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core/7.0.3": {
|
||||
"Microsoft.Data.Sqlite.Core/7.0.5": {
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Microsoft.Data.Sqlite.dll": {
|
||||
"assemblyVersion": "7.0.3.0",
|
||||
"fileVersion": "7.0.323.6302"
|
||||
"assemblyVersion": "7.0.5.0",
|
||||
"fileVersion": "7.0.523.16503"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -245,30 +247,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sentry/3.29.0": {
|
||||
"Sentry/3.33.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.Threading.Tasks.Extensions": "4.5.4"
|
||||
"System.Text.Json": "5.0.2"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Sentry.dll": {
|
||||
"assemblyVersion": "3.29.0.0",
|
||||
"fileVersion": "3.29.0.0"
|
||||
"assemblyVersion": "3.33.0.0",
|
||||
"fileVersion": "3.33.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sentry.Serilog/3.29.0": {
|
||||
"Sentry.Serilog/3.33.0": {
|
||||
"dependencies": {
|
||||
"Sentry": "3.29.0",
|
||||
"Sentry": "3.33.0",
|
||||
"Serilog": "2.12.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Sentry.Serilog.dll": {
|
||||
"assemblyVersion": "3.29.0.0",
|
||||
"fileVersion": "3.29.0.0"
|
||||
"assemblyVersion": "3.33.0.0",
|
||||
"fileVersion": "3.33.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -280,7 +279,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog.Enrichers.ClientInfo/1.2.0": {
|
||||
"Serilog.Enrichers.ClientInfo/1.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http": "2.1.1",
|
||||
"Serilog": "2.12.0"
|
||||
@@ -373,6 +372,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -435,6 +445,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -560,33 +581,33 @@
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"GraphQL.Client/5.1.1": {
|
||||
"GraphQL.Client/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6bfM9qU4AMcFWm4BHd2M6LE5+rLtK47/+VRtypggwb9appC8sbF58ytVBVOKpqKhKpmZERfPLGJap8O/FH3w5w==",
|
||||
"path": "graphql.client/5.1.1",
|
||||
"hashPath": "graphql.client.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"path": "graphql.client/6.0.0",
|
||||
"hashPath": "graphql.client.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Client.Abstractions/5.1.1": {
|
||||
"GraphQL.Client.Abstractions/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/znG7Lcz3rzG9VSCq+V2ARb63c/uIs8idGOvXyltZ32Wy570GX/I8HNUIZ1yDThmQRJ5KOGSd9Mzk37lFg49rg==",
|
||||
"path": "graphql.client.abstractions/5.1.1",
|
||||
"hashPath": "graphql.client.abstractions.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"path": "graphql.client.abstractions/6.0.0",
|
||||
"hashPath": "graphql.client.abstractions.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
|
||||
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-n1gU3GlqJ0jQceb/VEEr4c0D2vpQc5AtDwthK89+yX7VpzlhJKqE5B4RJwx//Jb33mKybfJioWwDgVfSOPAwdw==",
|
||||
"path": "graphql.client.abstractions.websocket/5.1.1",
|
||||
"hashPath": "graphql.client.abstractions.websocket.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"path": "graphql.client.abstractions.websocket/6.0.0",
|
||||
"hashPath": "graphql.client.abstractions.websocket.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Primitives/5.1.1": {
|
||||
"GraphQL.Primitives/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RlGNsv19gbz6sQwkzif9J6Jd148nuIg1kRQf2AFOLp5K00IA+pKMdJvHF5t5llDR52Rok46ynhJv/wg+ps9ZhQ==",
|
||||
"path": "graphql.primitives/5.1.1",
|
||||
"hashPath": "graphql.primitives.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==",
|
||||
"path": "graphql.primitives/6.0.0",
|
||||
"hashPath": "graphql.primitives.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.AspNetCore.Http/2.1.1": {
|
||||
"type": "package",
|
||||
@@ -630,19 +651,19 @@
|
||||
"path": "microsoft.csharp/4.7.0",
|
||||
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.Sqlite/7.0.3": {
|
||||
"Microsoft.Data.Sqlite/7.0.5": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-uumx0bb4FsN7ApP0ZoQDfSJi9c2Xen0PlXCT2BF27cM+yUMFzDEhqxR7/1/DV8ck4mYtL9yShBoOa7jeJ3736w==",
|
||||
"path": "microsoft.data.sqlite/7.0.3",
|
||||
"hashPath": "microsoft.data.sqlite.7.0.3.nupkg.sha512"
|
||||
"sha512": "sha512-KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"path": "microsoft.data.sqlite/7.0.5",
|
||||
"hashPath": "microsoft.data.sqlite.7.0.5.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core/7.0.3": {
|
||||
"Microsoft.Data.Sqlite.Core/7.0.5": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-pCmzLLWTIrIv94o7JtQ1qcPD0oc1YNY9XvlO6/tOF9YCcUfDZ3Tx9Z//CM7hFnprduHFPekif7jteBc/sXQ31Q==",
|
||||
"path": "microsoft.data.sqlite.core/7.0.3",
|
||||
"hashPath": "microsoft.data.sqlite.core.7.0.3.nupkg.sha512"
|
||||
"sha512": "sha512-FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"path": "microsoft.data.sqlite.core/7.0.5",
|
||||
"hashPath": "microsoft.data.sqlite.core.7.0.5.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": {
|
||||
"type": "package",
|
||||
@@ -721,19 +742,19 @@
|
||||
"path": "polly.extensions.http/3.0.0",
|
||||
"hashPath": "polly.extensions.http.3.0.0.nupkg.sha512"
|
||||
},
|
||||
"Sentry/3.29.0": {
|
||||
"Sentry/3.33.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-0AQbZte9ETORcrLj+gmzZcjbB3UwLl6KmdRVC9mcFfWTPftnSqVrgWDQHR70t2EYK5w6Y1pM8FAFyLDSJWvyRA==",
|
||||
"path": "sentry/3.29.0",
|
||||
"hashPath": "sentry.3.29.0.nupkg.sha512"
|
||||
"sha512": "sha512-8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
|
||||
"path": "sentry/3.33.0",
|
||||
"hashPath": "sentry.3.33.0.nupkg.sha512"
|
||||
},
|
||||
"Sentry.Serilog/3.29.0": {
|
||||
"Sentry.Serilog/3.33.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1g+x8fkoYe3/X6qH2F5hJ6eGkfhGqkMcib0P+dGH6lndnVseP+Lgx8HeO8zk3x3bkS2/GUEV7fn/QnFdh32R4A==",
|
||||
"path": "sentry.serilog/3.29.0",
|
||||
"hashPath": "sentry.serilog.3.29.0.nupkg.sha512"
|
||||
"sha512": "sha512-V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
|
||||
"path": "sentry.serilog/3.33.0",
|
||||
"hashPath": "sentry.serilog.3.33.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog/2.12.0": {
|
||||
"type": "package",
|
||||
@@ -742,12 +763,12 @@
|
||||
"path": "serilog/2.12.0",
|
||||
"hashPath": "serilog.2.12.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog.Enrichers.ClientInfo/1.2.0": {
|
||||
"Serilog.Enrichers.ClientInfo/1.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZJx2eJQKX6+GxjZM9Y++7DPunR7Nizk9Vdq+BqMs/YPfW3Sv+qDm3PVC88srywJMDKfRaecHFWktBjw5F2pmoQ==",
|
||||
"path": "serilog.enrichers.clientinfo/1.2.0",
|
||||
"hashPath": "serilog.enrichers.clientinfo.1.2.0.nupkg.sha512"
|
||||
"sha512": "sha512-mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
|
||||
"path": "serilog.enrichers.clientinfo/1.3.0",
|
||||
"hashPath": "serilog.enrichers.clientinfo.1.3.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog.Enrichers.GlobalLogContext/3.0.0": {
|
||||
"type": "package",
|
||||
@@ -798,6 +819,13 @@
|
||||
"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,
|
||||
@@ -847,6 +875,13 @@
|
||||
"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,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
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,6 +23,9 @@ PluginImporter:
|
||||
Exclude WebGL: 0
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
Exclude WindowsStoreApps: 0
|
||||
Exclude iOS: 0
|
||||
Exclude tvOS: 0
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
@@ -47,7 +50,7 @@ PluginImporter:
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
@@ -74,9 +77,30 @@ PluginImporter:
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
enabled: 1
|
||||
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:
|
||||
|
||||
@@ -15,109 +15,124 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
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 {
|
||||
/// 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>();
|
||||
|
||||
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
|
||||
public void Update()
|
||||
{
|
||||
lock (_executionQueue)
|
||||
{
|
||||
while (_executionQueue.Count > 0)
|
||||
{
|
||||
_executionQueue.Dequeue().Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
lock(_executionQueue) {
|
||||
while (_executionQueue.Count > 0) {
|
||||
_executionQueue.Dequeue().Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <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 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
|
||||
/// </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
|
||||
/// </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>();
|
||||
/// <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 = null;
|
||||
|
||||
private static Dispatcher _instance = null;
|
||||
public static bool Exists()
|
||||
{
|
||||
return _instance != null;
|
||||
}
|
||||
|
||||
public static bool Exists() {
|
||||
return _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 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;
|
||||
}
|
||||
void Awake()
|
||||
{
|
||||
Setup.Init(
|
||||
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
|
||||
HostApplications.Unity.Slug
|
||||
);
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
|
||||
if (_instance == null) {
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
void OnDestroy()
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using Speckle.Core.Kits;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Factories
|
||||
{
|
||||
|
||||
public static class ConverterFactory
|
||||
{
|
||||
public static ISpeckleConverter GetDefaultConverter()
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Speckle.ConnectorUnity.NativeCache
|
||||
{
|
||||
#nullable enable
|
||||
[ExecuteAlways]
|
||||
public abstract class AbstractNativeCache : ScriptableObject
|
||||
{
|
||||
|
||||
protected bool isWriting = false;
|
||||
public abstract bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : Object;
|
||||
public abstract bool TryGetObject<T>(
|
||||
Base speckleObject,
|
||||
[NotNullWhen(true)] out T? nativeObject
|
||||
)
|
||||
where T : Object;
|
||||
|
||||
public abstract bool TrySaveObject(Base speckleObject, Object nativeObject);
|
||||
|
||||
@@ -40,7 +44,6 @@ namespace Speckle.ConnectorUnity.NativeCache
|
||||
{
|
||||
FinishWrite();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class AssetHelpers
|
||||
@@ -48,12 +51,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");
|
||||
}
|
||||
@@ -63,55 +66,33 @@ 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);
|
||||
string name = GenerateObjectName(speckleObject);
|
||||
string name = CoreUtils.GenerateObjectName(speckleObject);
|
||||
|
||||
string sanitisedName = new(name.Where(x => !InvalidChars.Contains(x)).ToArray());
|
||||
return $"{sanitisedName}{suffix}";
|
||||
}
|
||||
|
||||
|
||||
public const string OBJECT_NAME_SEPERATOR = " -- ";
|
||||
|
||||
/// <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}{OBJECT_NAME_SEPERATOR}{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];
|
||||
}
|
||||
|
||||
|
||||
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(GenerateObjectName))]
|
||||
|
||||
[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,12 +10,14 @@ 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;
|
||||
@@ -24,12 +26,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -57,6 +60,5 @@ namespace Speckle.ConnectorUnity.NativeCache
|
||||
}
|
||||
base.FinishWrite();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,48 @@ 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.ContainsKey(speckleObject.id))
|
||||
if (LoadedAssets.TryGetValue(speckleObject.id, out List<Object>? assets))
|
||||
{
|
||||
LoadedAssets[speckleObject.id].Add(nativeObject);
|
||||
assets.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,19 +14,25 @@ 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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Speckle.ConnectorUnity.NativeCache",
|
||||
"rootNamespace": "Speckle.ConnectorUnity",
|
||||
"references": [],
|
||||
"references": ["GUID:50d889142fdf9de4b8501c6eaa4b3225"],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
|
||||
@@ -8,11 +8,12 @@ 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)
|
||||
@@ -21,7 +22,6 @@ 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,4 +46,3 @@ namespace Speckle.ConnectorUnity
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,55 +7,55 @@
|
||||
"targets": {
|
||||
".NETStandard,Version=v2.0": {},
|
||||
".NETStandard,Version=v2.0/": {
|
||||
"Objects/2.1.1": {
|
||||
"Objects/2.0.999-local": {
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "2.0.3",
|
||||
"Speckle.Core": "2.1.0"
|
||||
"Speckle.Core": "2.0.999-local"
|
||||
},
|
||||
"runtime": {
|
||||
"Objects.dll": {}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client/5.1.1": {
|
||||
"GraphQL.Client/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "5.1.1",
|
||||
"GraphQL.Client.Abstractions.Websocket": "5.1.1",
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions/5.1.1": {
|
||||
"GraphQL.Client.Abstractions/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "5.1.1"
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.Abstractions.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
|
||||
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "5.1.1"
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Client.Abstractions.Websocket.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives/5.1.1": {
|
||||
"GraphQL.Primitives/6.0.0": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/GraphQL.Primitives.dll": {
|
||||
"assemblyVersion": "5.1.1.0",
|
||||
"fileVersion": "5.1.1.0"
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -128,20 +128,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite/7.0.3": {
|
||||
"Microsoft.Data.Sqlite/7.0.5": {
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.3",
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core/7.0.3": {
|
||||
"Microsoft.Data.Sqlite.Core/7.0.5": {
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Microsoft.Data.Sqlite.dll": {
|
||||
"assemblyVersion": "7.0.3.0",
|
||||
"fileVersion": "7.0.323.6302"
|
||||
"assemblyVersion": "7.0.5.0",
|
||||
"fileVersion": "7.0.523.16503"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -231,30 +231,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sentry/3.29.0": {
|
||||
"Sentry/3.33.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.Threading.Tasks.Extensions": "4.5.4"
|
||||
"System.Text.Json": "5.0.2"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Sentry.dll": {
|
||||
"assemblyVersion": "3.29.0.0",
|
||||
"fileVersion": "3.29.0.0"
|
||||
"assemblyVersion": "3.33.0.0",
|
||||
"fileVersion": "3.33.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sentry.Serilog/3.29.0": {
|
||||
"Sentry.Serilog/3.33.0": {
|
||||
"dependencies": {
|
||||
"Sentry": "3.29.0",
|
||||
"Sentry": "3.33.0",
|
||||
"Serilog": "2.12.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Sentry.Serilog.dll": {
|
||||
"assemblyVersion": "3.29.0.0",
|
||||
"fileVersion": "3.29.0.0"
|
||||
"assemblyVersion": "3.33.0.0",
|
||||
"fileVersion": "3.33.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -266,7 +263,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog.Enrichers.ClientInfo/1.2.0": {
|
||||
"Serilog.Enrichers.ClientInfo/1.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http": "2.1.1",
|
||||
"Serilog": "2.12.0"
|
||||
@@ -359,6 +356,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -541,38 +549,38 @@
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Objects/2.1.1": {
|
||||
"Objects/2.0.999-local": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"GraphQL.Client/5.1.1": {
|
||||
"GraphQL.Client/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6bfM9qU4AMcFWm4BHd2M6LE5+rLtK47/+VRtypggwb9appC8sbF58ytVBVOKpqKhKpmZERfPLGJap8O/FH3w5w==",
|
||||
"path": "graphql.client/5.1.1",
|
||||
"hashPath": "graphql.client.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"path": "graphql.client/6.0.0",
|
||||
"hashPath": "graphql.client.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Client.Abstractions/5.1.1": {
|
||||
"GraphQL.Client.Abstractions/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/znG7Lcz3rzG9VSCq+V2ARb63c/uIs8idGOvXyltZ32Wy570GX/I8HNUIZ1yDThmQRJ5KOGSd9Mzk37lFg49rg==",
|
||||
"path": "graphql.client.abstractions/5.1.1",
|
||||
"hashPath": "graphql.client.abstractions.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"path": "graphql.client.abstractions/6.0.0",
|
||||
"hashPath": "graphql.client.abstractions.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket/5.1.1": {
|
||||
"GraphQL.Client.Abstractions.Websocket/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-n1gU3GlqJ0jQceb/VEEr4c0D2vpQc5AtDwthK89+yX7VpzlhJKqE5B4RJwx//Jb33mKybfJioWwDgVfSOPAwdw==",
|
||||
"path": "graphql.client.abstractions.websocket/5.1.1",
|
||||
"hashPath": "graphql.client.abstractions.websocket.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"path": "graphql.client.abstractions.websocket/6.0.0",
|
||||
"hashPath": "graphql.client.abstractions.websocket.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"GraphQL.Primitives/5.1.1": {
|
||||
"GraphQL.Primitives/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RlGNsv19gbz6sQwkzif9J6Jd148nuIg1kRQf2AFOLp5K00IA+pKMdJvHF5t5llDR52Rok46ynhJv/wg+ps9ZhQ==",
|
||||
"path": "graphql.primitives/5.1.1",
|
||||
"hashPath": "graphql.primitives.5.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==",
|
||||
"path": "graphql.primitives/6.0.0",
|
||||
"hashPath": "graphql.primitives.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.AspNetCore.Http/2.1.1": {
|
||||
"type": "package",
|
||||
@@ -616,19 +624,19 @@
|
||||
"path": "microsoft.csharp/4.7.0",
|
||||
"hashPath": "microsoft.csharp.4.7.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.Sqlite/7.0.3": {
|
||||
"Microsoft.Data.Sqlite/7.0.5": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-uumx0bb4FsN7ApP0ZoQDfSJi9c2Xen0PlXCT2BF27cM+yUMFzDEhqxR7/1/DV8ck4mYtL9yShBoOa7jeJ3736w==",
|
||||
"path": "microsoft.data.sqlite/7.0.3",
|
||||
"hashPath": "microsoft.data.sqlite.7.0.3.nupkg.sha512"
|
||||
"sha512": "sha512-KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"path": "microsoft.data.sqlite/7.0.5",
|
||||
"hashPath": "microsoft.data.sqlite.7.0.5.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core/7.0.3": {
|
||||
"Microsoft.Data.Sqlite.Core/7.0.5": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-pCmzLLWTIrIv94o7JtQ1qcPD0oc1YNY9XvlO6/tOF9YCcUfDZ3Tx9Z//CM7hFnprduHFPekif7jteBc/sXQ31Q==",
|
||||
"path": "microsoft.data.sqlite.core/7.0.3",
|
||||
"hashPath": "microsoft.data.sqlite.core.7.0.3.nupkg.sha512"
|
||||
"sha512": "sha512-FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"path": "microsoft.data.sqlite.core/7.0.5",
|
||||
"hashPath": "microsoft.data.sqlite.core.7.0.5.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": {
|
||||
"type": "package",
|
||||
@@ -707,19 +715,19 @@
|
||||
"path": "polly.extensions.http/3.0.0",
|
||||
"hashPath": "polly.extensions.http.3.0.0.nupkg.sha512"
|
||||
},
|
||||
"Sentry/3.29.0": {
|
||||
"Sentry/3.33.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-0AQbZte9ETORcrLj+gmzZcjbB3UwLl6KmdRVC9mcFfWTPftnSqVrgWDQHR70t2EYK5w6Y1pM8FAFyLDSJWvyRA==",
|
||||
"path": "sentry/3.29.0",
|
||||
"hashPath": "sentry.3.29.0.nupkg.sha512"
|
||||
"sha512": "sha512-8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
|
||||
"path": "sentry/3.33.0",
|
||||
"hashPath": "sentry.3.33.0.nupkg.sha512"
|
||||
},
|
||||
"Sentry.Serilog/3.29.0": {
|
||||
"Sentry.Serilog/3.33.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1g+x8fkoYe3/X6qH2F5hJ6eGkfhGqkMcib0P+dGH6lndnVseP+Lgx8HeO8zk3x3bkS2/GUEV7fn/QnFdh32R4A==",
|
||||
"path": "sentry.serilog/3.29.0",
|
||||
"hashPath": "sentry.serilog.3.29.0.nupkg.sha512"
|
||||
"sha512": "sha512-V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
|
||||
"path": "sentry.serilog/3.33.0",
|
||||
"hashPath": "sentry.serilog.3.33.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog/2.12.0": {
|
||||
"type": "package",
|
||||
@@ -728,12 +736,12 @@
|
||||
"path": "serilog/2.12.0",
|
||||
"hashPath": "serilog.2.12.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog.Enrichers.ClientInfo/1.2.0": {
|
||||
"Serilog.Enrichers.ClientInfo/1.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZJx2eJQKX6+GxjZM9Y++7DPunR7Nizk9Vdq+BqMs/YPfW3Sv+qDm3PVC88srywJMDKfRaecHFWktBjw5F2pmoQ==",
|
||||
"path": "serilog.enrichers.clientinfo/1.2.0",
|
||||
"hashPath": "serilog.enrichers.clientinfo.1.2.0.nupkg.sha512"
|
||||
"sha512": "sha512-mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
|
||||
"path": "serilog.enrichers.clientinfo/1.3.0",
|
||||
"hashPath": "serilog.enrichers.clientinfo.1.3.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog.Enrichers.GlobalLogContext/3.0.0": {
|
||||
"type": "package",
|
||||
@@ -784,6 +792,13 @@
|
||||
"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,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -196,4 +196,4 @@ namespace System.Collections.Concurrent
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,35 +8,35 @@ using Speckle.Core.Logging;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
public static class Streams
|
||||
{
|
||||
public static async Task<List<Stream>> List(int limit = 10)
|
||||
public static class Streams
|
||||
{
|
||||
var account = AccountManager.GetDefaultAccount();
|
||||
if (account == null)
|
||||
return new List<Stream>();
|
||||
var client = new Client(account);
|
||||
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 res = await client.StreamsGet(limit);
|
||||
var res = await client.StreamsGet(limit);
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
public static async Task<Stream> Get(string streamId, int limit = 10)
|
||||
{
|
||||
var account = AccountManager.GetDefaultAccount();
|
||||
if (account == null)
|
||||
return null;
|
||||
var client = new Client(account);
|
||||
|
||||
var res = await client.StreamGet(streamId, limit);
|
||||
|
||||
if (res.branches.items != null)
|
||||
{
|
||||
res.branches.items.Reverse();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Stream> Get(string streamId, int limit = 10)
|
||||
{
|
||||
var account = AccountManager.GetDefaultAccount();
|
||||
if (account == null)
|
||||
return null;
|
||||
var client = new Client(account);
|
||||
|
||||
var res = await client.StreamGet(streamId, limit);
|
||||
|
||||
if (res.branches.items != null)
|
||||
{
|
||||
res.branches.items.Reverse();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,64 @@
|
||||
#nullable enable
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
namespace Speckle.ConnectorUnity.Utils
|
||||
{
|
||||
public static class CoreUtils
|
||||
{
|
||||
public static void SetupInit()
|
||||
{
|
||||
SpeckleLog.Initialize(HostApplications.Unity.Slug, HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()));
|
||||
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.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
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,13 @@ 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;
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f9ac668e8174e13be50ba5a5de9f732
|
||||
timeCreated: 1689088698
|
||||
@@ -1,97 +1,133 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity.Utils
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
|
||||
public static void SafeDestroy(UnityEngine.Object obj)
|
||||
public static class Utils
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
UnityEngine.Object.Destroy(obj);
|
||||
public static void SafeDestroy(UnityEngine.Object obj)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
UnityEngine.Object.Destroy(obj);
|
||||
else
|
||||
UnityEngine.Object.DestroyImmediate(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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-11
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
@@ -11,40 +10,42 @@ 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) => 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -9,38 +9,43 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
[Serializable]
|
||||
public sealed class BranchSelection : OptionSelection<Branch>
|
||||
{
|
||||
[field: SerializeField, Range(1,100), Tooltip("Number of branches to request")]
|
||||
public int BranchesLimit { get; set; } = 30;
|
||||
[field: SerializeField, Range(1,100), Tooltip("Number of commits to request")]
|
||||
public int CommitsLimit { get; set; } = 15;
|
||||
[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?.name;
|
||||
|
||||
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
Stream? stream = StreamSelection.Selected;
|
||||
if (stream == null) return;
|
||||
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
-9
@@ -6,35 +6,32 @@ 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;
|
||||
|
||||
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
Branch? branch = BranchSelection!.Selected;
|
||||
if (branch == null) return;
|
||||
if (branch == null)
|
||||
return;
|
||||
List<Commit> commits = branch.commits.items;
|
||||
GenerateOptions(commits, (_, i) => i == 0);
|
||||
}
|
||||
|
||||
+19
-10
@@ -18,7 +18,8 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
public abstract class OptionSelection<TOption>
|
||||
where TOption : class
|
||||
{
|
||||
[SerializeField] private int selectedIndex = -1;
|
||||
[SerializeField]
|
||||
private int selectedIndex = -1;
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
@@ -34,8 +35,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Options == null) return null;
|
||||
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null;
|
||||
if (Options is null)
|
||||
return null;
|
||||
if (SelectedIndex < 0 || SelectedIndex >= Options.Length)
|
||||
return null;
|
||||
return Options[SelectedIndex];
|
||||
}
|
||||
}
|
||||
@@ -52,22 +55,28 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
|
||||
protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault)
|
||||
{
|
||||
List<TOption> optionsToAdd = new List<TOption>(source.Count);
|
||||
List<TOption> optionsToAdd = new(source.Count);
|
||||
int defaultOption = -1;
|
||||
int index = 0;
|
||||
foreach (TOption? a in source)
|
||||
{
|
||||
if (a == null) continue;
|
||||
if (a == null)
|
||||
continue;
|
||||
optionsToAdd.Add(a);
|
||||
if (isDefault(a, index)) defaultOption = index;
|
||||
if (isDefault(a, index))
|
||||
defaultOption = index;
|
||||
index++;
|
||||
}
|
||||
|
||||
TOption? currentSelected = Selected;
|
||||
bool selectionOutOfRange = SelectedIndex < 0 || SelectedIndex >= optionsToAdd.Count;
|
||||
if (selectionOutOfRange
|
||||
|| (currentSelected != null
|
||||
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex])))
|
||||
if (
|
||||
selectionOutOfRange
|
||||
|| (
|
||||
currentSelected != null
|
||||
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex])
|
||||
)
|
||||
)
|
||||
{
|
||||
selectedIndex = defaultOption;
|
||||
}
|
||||
@@ -77,4 +86,4 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
OnSelectionChange?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -9,17 +9,20 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
[Serializable]
|
||||
public sealed class StreamSelection : OptionSelection<Stream>
|
||||
{
|
||||
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;
|
||||
private const int DefaultRequestLimit = 50;
|
||||
|
||||
[field: SerializeField, Range(1, 100), Tooltip("Number of streams to request")]
|
||||
public int StreamsLimit { get; set; } = DefaultRequestLimit;
|
||||
|
||||
[field: SerializeReference]
|
||||
public AccountSelection AccountSelection { get; private set; }
|
||||
|
||||
|
||||
public StreamSelection(AccountSelection accountSelection)
|
||||
{
|
||||
AccountSelection = accountSelection;
|
||||
Initialise();
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
AccountSelection.OnSelectionChange = RefreshOptions;
|
||||
@@ -28,15 +31,17 @@ 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;
|
||||
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>();
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Collections.Generic;
|
||||
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
|
||||
@@ -15,19 +17,20 @@ 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)
|
||||
{
|
||||
@@ -39,21 +42,17 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
[SerializeField, HideInInspector]
|
||||
private string _serializedSpeckleType;
|
||||
private Type _speckleType = typeof(Base);
|
||||
public Type SpeckleType {
|
||||
get
|
||||
{
|
||||
return _speckleType ??= typeof(Base);
|
||||
}
|
||||
public Type SpeckleType
|
||||
{
|
||||
get => _speckleType ??= typeof(Base);
|
||||
set
|
||||
{
|
||||
|
||||
Debug.Assert(typeof(Base).IsAssignableFrom(value));
|
||||
Debug.Assert(!value.IsAbstract);
|
||||
|
||||
|
||||
_speckleType = value;
|
||||
_hasChanged = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SpeckleProperties()
|
||||
@@ -63,7 +62,7 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
_hasChanged = true;
|
||||
SpeckleType = typeof(Base);
|
||||
}
|
||||
|
||||
|
||||
private void CollectionChangeHandler(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
_hasChanged = true;
|
||||
@@ -71,19 +70,22 @@ 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()
|
||||
{
|
||||
Base speckleData = Operations.Deserialize(_serializedData);
|
||||
var deserializer = new BaseObjectDeserializerV2();
|
||||
Base speckleData = deserializer.Deserialize(_serializedData);
|
||||
|
||||
Data = speckleData.GetMembers();
|
||||
_hasChanged = false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
_speckleType = Type.GetType(_serializedSpeckleType);
|
||||
@@ -96,7 +98,7 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class SpeckleData : Base
|
||||
private sealed class SpeckleData : Base
|
||||
{
|
||||
public SpeckleData(IDictionary<string, object> data)
|
||||
{
|
||||
@@ -107,4 +109,4 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "systems.speckle.speckle-unity",
|
||||
"version": "2.13.0",
|
||||
"version": "2.16.0",
|
||||
"displayName": "Speckle Unity Connector",
|
||||
"description": "AEC Interoperability for Unity through Speckle",
|
||||
"unity": "2018.4",
|
||||
"unity": "2021.1",
|
||||
"documentationUrl": "https://speckle.guide/user/unity.html",
|
||||
"changelogUrl": "https://speckle.systems/blog/",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,4 +21,4 @@
|
||||
"email": "hello@speckle.systems",
|
||||
"url": "https://speckle.systems"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,14 @@
|
||||
[](https://twitter.com/SpeckleSystems) [](https://discourse.speckle.works) [](https://speckle.systems) [](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 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).
|
||||
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).
|
||||
|
||||

|
||||
|
||||
@@ -20,26 +22,28 @@ 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 support Unity 2020 and 2021 (newer versions likely work, but aren't currently part of our test pipeline).
|
||||
|
||||
We officially support Unity 2021.3 or newer.
|
||||
|
||||
Features:
|
||||
- Receive Speckle Objects at Editor or Runtime
|
||||
- Send Speckle Objects at Runtime (editor support in the works!)
|
||||
- Send Speckle Objects at Editor or Runtime
|
||||
- Material override/substitution
|
||||
- Automatic receiving changes
|
||||
|
||||
Currently tested on Windows and MacOS. Experimental support for Android [in the works](https://github.com/specklesystems/speckle-unity/issues/68).
|
||||
Currently tested on Windows, Linux, and MacOS.
|
||||
|
||||
## 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.
|
||||
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.
|
||||
```
|
||||
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
|
||||
## Installation (Package)
|
||||
|
||||
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)
|
||||
@@ -59,18 +63,18 @@ We encourage everyone interested to hack / contribute / debug / give feedback to
|
||||
|
||||
### Requirements
|
||||
|
||||
- Unity 2020.3+
|
||||
- Unity 2021 or greater
|
||||
- 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 the Asset folder until we figure out how to best reference Core.
|
||||
All dependencies to Speckle Core have been included; compiled in `systems.speckle.speckle-unity` package.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
|
||||
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.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Reference in New Issue
Block a user