Compare commits
13 Commits
2.13.0
...
jrm/playground
| Author | SHA1 | Date | |
|---|---|---|---|
| f406cb8229 | |||
| 62875c0a27 | |||
| b3c6b59721 | |||
| d9f7895b3f | |||
| dc58f6b0b0 | |||
| 556a7eaddf | |||
| 39a79be700 | |||
| 73ef71f4fd | |||
| 6a07ecfd5b | |||
| e512df9c82 | |||
| 59221e89ba | |||
| 32533ddb21 | |||
| a89e4cdfe7 |
@@ -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");
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Speckle/Extras/Receive From Url")]
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
public class ReceiveFromURL : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Url of your speckle object/commit/branch/stream")]
|
||||
public string url;
|
||||
|
||||
private RecursiveConverter _converter;
|
||||
|
||||
#nullable enable
|
||||
private CancellationTokenSource? _tokenSource;
|
||||
void Awake()
|
||||
{
|
||||
_converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Receive))]
|
||||
public void Receive()
|
||||
{
|
||||
StartCoroutine(Receive_Routine());
|
||||
}
|
||||
|
||||
public IEnumerator Receive_Routine()
|
||||
{
|
||||
if (IsBusy()) throw new InvalidOperationException("A receive operation has already started");
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
StreamWrapper sw = new(url);
|
||||
|
||||
if (!sw.IsValid)
|
||||
throw new InvalidOperationException("Speckle url input is not a valid speckle stream/branch/commit");
|
||||
|
||||
var accountTask = new Utils.WaitForTask<Account>(async () => await GetAccount(sw));
|
||||
yield return accountTask;
|
||||
|
||||
_tokenSource.Token.ThrowIfCancellationRequested();
|
||||
using Client c = new(accountTask.Result);
|
||||
|
||||
var objectIdTask = new Utils.WaitForTask<(string, Commit?)>(async () => await GetObjectID(sw, c));
|
||||
yield return objectIdTask;
|
||||
(string objectId, Commit? commit) = objectIdTask.Result;
|
||||
|
||||
Debug.Log($"Receiving from {sw.ServerUrl}...");
|
||||
|
||||
var receiveTask = new Utils.WaitForTask<Base>(async () => await SpeckleReceiver.ReceiveAsync(
|
||||
c,
|
||||
sw.StreamId,
|
||||
objectId,
|
||||
commit,
|
||||
cancellationToken: _tokenSource.Token));
|
||||
yield return receiveTask;
|
||||
|
||||
Debug.Log("Converting to native...");
|
||||
_converter.RecursivelyConvertToNative_Sync(receiveTask.Result, transform);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_tokenSource.Dispose();
|
||||
_tokenSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task<(string objectId, Commit? commit)> GetObjectID(StreamWrapper sw, Client client)
|
||||
{
|
||||
string objectId;
|
||||
Commit? commit = null;
|
||||
//OBJECT URL
|
||||
if (!string.IsNullOrEmpty(sw.ObjectId))
|
||||
{
|
||||
objectId = sw.ObjectId;
|
||||
}
|
||||
//COMMIT URL
|
||||
else if (!string.IsNullOrEmpty(sw.CommitId))
|
||||
{
|
||||
commit = await client.CommitGet(sw.StreamId, sw.CommitId).ConfigureAwait(false);
|
||||
objectId = commit.referencedObject;
|
||||
}
|
||||
//BRANCH URL OR STREAM URL
|
||||
else
|
||||
{
|
||||
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
|
||||
|
||||
var branch = await client.BranchGet(sw.StreamId, branchName, 1).ConfigureAwait(false);
|
||||
if (!branch.commits.items.Any())
|
||||
throw new SpeckleException("The selected branch has no commits.");
|
||||
|
||||
commit = branch.commits.items[0];
|
||||
objectId = branch.commits.items[0].referencedObject;
|
||||
}
|
||||
|
||||
return (objectId, commit);
|
||||
}
|
||||
[ContextMenu(nameof(Cancel))]
|
||||
public void Cancel()
|
||||
{
|
||||
if (IsNotBusy()) throw new InvalidOperationException("There are no pending receive operations to cancel");
|
||||
_tokenSource!.Cancel();
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Cancel), true)]
|
||||
public bool IsBusy()
|
||||
{
|
||||
return _tokenSource is not null;
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Receive), true)]
|
||||
internal bool IsNotBusy() => !IsBusy();
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
}
|
||||
|
||||
|
||||
private async Task<Account> GetAccount(StreamWrapper sw)
|
||||
{
|
||||
Account account;
|
||||
try
|
||||
{
|
||||
account = await sw.GetAccount().ConfigureAwait(false);
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sw.StreamId))
|
||||
throw e;
|
||||
|
||||
//Fallback to a non authed account
|
||||
account = new Account
|
||||
{
|
||||
token = "",
|
||||
serverInfo = new ServerInfo { url = sw.ServerUrl },
|
||||
userInfo = new UserInfo()
|
||||
};
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dd598fed5008c44a815ba09e81a2d19
|
||||
guid: 942bf0cb27c5c5045bc4cbb7fc0fad71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -9,6 +9,7 @@ using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[RequireComponent(typeof(Sender)), ExecuteAlways]
|
||||
[Obsolete]
|
||||
public class SendChildrenToSpeckle : MonoBehaviour
|
||||
{
|
||||
public LayerMask layerMask;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Speckle.Extra",
|
||||
"references":[ "GUID:eed1b8b83e2c0074d9e5de2348e3ff72", "GUID:e6adfdc4e436206479f48eafc82f32b5", "GUID:d274441ecc3eb3f43b093eec1503d681" ]
|
||||
"references":[ "GUID:eed1b8b83e2c0074d9e5de2348e3ff72", "GUID:e6adfdc4e436206479f48eafc82f32b5", "GUID:d274441ecc3eb3f43b093eec1503d681", "GUID:50d889142fdf9de4b8501c6eaa4b3225" ]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ using UnityEngine.UI;
|
||||
using Text = UnityEngine.UI.Text;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
{
|
||||
[Obsolete]
|
||||
public class InteractionLogic : MonoBehaviour
|
||||
{
|
||||
private Receiver receiver;
|
||||
@@ -216,4 +217,4 @@ namespace Speckle.ConnectorUnity
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Speckle.Core.Credentials;
|
||||
@@ -8,6 +9,7 @@ using Stream = Speckle.Core.Api.Stream;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
[Obsolete]
|
||||
public class SpeckleExamples : MonoBehaviour
|
||||
{
|
||||
public Text SelectStreamText;
|
||||
@@ -129,4 +131,4 @@ namespace Speckle.ConnectorUnity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2726
-168
File diff suppressed because one or more lines are too long
@@ -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:
|
||||
+191
-105
@@ -1,9 +1,8 @@
|
||||
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;
|
||||
|
||||
@@ -18,6 +17,77 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
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));
|
||||
Debug.Log(value);
|
||||
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();
|
||||
@@ -28,6 +98,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
private void Init()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
@@ -42,39 +113,92 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t);
|
||||
}
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
private async Task ReceiveSelection(int progressId)
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
//Preview image
|
||||
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
|
||||
if (foldOutStatus)
|
||||
bool shouldCancel = false;
|
||||
|
||||
Progress.RegisterCancelCallback(progressId, () =>
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f/4f);
|
||||
if(previewImage != null) GUI.DrawTexture(rect, previewImage);
|
||||
speckleReceiver.Cancel();
|
||||
shouldCancel = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
var token = speckleReceiver.BeginOperation();
|
||||
commitObject = await Task.Run(async () => await ReceiveCommit(progressId).ConfigureAwait(false),
|
||||
token
|
||||
)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
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)
|
||||
int totalChildren = (int) Math.Min(commitObject.totalChildrenCount, int.MaxValue);
|
||||
float totalChildrenFloat = commitObject.totalChildrenCount;
|
||||
|
||||
var convertProgress = Progress.Start("Converting To Native", "Preparing...", Progress.Options.Indefinite | Progress.Options.Sticky, progressId);
|
||||
|
||||
bool BeforeConvert(TraversalContext context)
|
||||
{
|
||||
generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
Base b = context.current;
|
||||
|
||||
|
||||
if (receive)
|
||||
{
|
||||
await ReceiveAndConvert(speckleReceiver).ConfigureAwait(false);;
|
||||
//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()
|
||||
@@ -83,114 +207,75 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
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;
|
||||
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
|
||||
|
||||
var gameObject = Convert(speckleReceiver, commitObject, commit.id);
|
||||
Debug.Log($"Successfully received and converted commit: {commit.id}", target);
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
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");
|
||||
// Ensure it gets reparented if this was a context click (otherwise does nothing)
|
||||
@@ -208,5 +293,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
EditorGUIUtility.SetIconForObject(go, icon);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,12 +106,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => go.activeInHierarchy);
|
||||
}
|
||||
|
||||
private void CancelSend()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +163,10 @@ 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 =>
|
||||
{
|
||||
@@ -179,8 +180,15 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
);
|
||||
|
||||
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;
|
||||
|
||||
+3
-3
@@ -46,8 +46,8 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
("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()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
{
|
||||
("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),
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -5,10 +5,13 @@ 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 JetBrains.Annotations;
|
||||
using Sentry;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -18,7 +21,8 @@ namespace Speckle.ConnectorUnity
|
||||
/// A Speckle Receiver, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions and subscriptions for you
|
||||
/// </summary>
|
||||
[RequireComponent( typeof( RecursiveConverter ) )]
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
[Obsolete]
|
||||
public class Receiver : MonoBehaviour
|
||||
{
|
||||
public string StreamId;
|
||||
@@ -93,7 +97,7 @@ namespace Speckle.ConnectorUnity
|
||||
if (!mainBranch.commits.items.Any())
|
||||
throw new Exception("This branch has no commits");
|
||||
var commit = mainBranch.commits.items[0];
|
||||
GetAndConvertObject(commit.referencedObject, commit.id);
|
||||
GetAndConvertObject(commit.referencedObject, commit.id, commit.sourceApplication, commit.authorId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -116,12 +120,12 @@ namespace Speckle.ConnectorUnity
|
||||
if (e.branchName == BranchName)
|
||||
{
|
||||
Debug.Log("New commit created");
|
||||
GetAndConvertObject(e.objectId, e.id);
|
||||
GetAndConvertObject(e.objectId, e.id, e.sourceApplication, e.authorId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void GetAndConvertObject(string objectId, string commitId)
|
||||
private async void GetAndConvertObject(string objectId, string commitId, string sourceApplication, string authorId)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -136,7 +140,14 @@ namespace Speckle.ConnectorUnity
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(Client.Account, Analytics.Events.Receive);
|
||||
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(() =>
|
||||
{
|
||||
@@ -186,4 +197,4 @@ namespace Speckle.ConnectorUnity
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Speckle.ConnectorUnity
|
||||
/// that handles conversions for you
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
[Obsolete]
|
||||
public class Sender : MonoBehaviour
|
||||
{
|
||||
|
||||
@@ -146,4 +147,4 @@ namespace Speckle.ConnectorUnity
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+207
-48
@@ -2,62 +2,225 @@
|
||||
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();
|
||||
var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
|
||||
|
||||
var convertableObjects = traversalFunc.Traverse(rootObject)
|
||||
.Where(tc => ConverterInstance.CanConvertToNative(tc.current));
|
||||
|
||||
foreach (TraversalContext tc in convertableObjects)
|
||||
{
|
||||
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;
|
||||
|
||||
yield return go;
|
||||
}
|
||||
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 objectsToConvert = traversalFunc
|
||||
.Traverse(rootObject)
|
||||
.Where(x => ConverterInstance.CanConvertToNative(x.current))
|
||||
.Where(x => userPredicate(x));
|
||||
|
||||
Dictionary<Base, GameObject?> created = new();
|
||||
foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
|
||||
{
|
||||
if (!isActiveAndEnabled) throw new InvalidOperationException($"Cannot convert objects while {GetType()} is disabled");
|
||||
|
||||
yield return conversionResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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))
|
||||
@@ -74,11 +237,13 @@ 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>
|
||||
[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>
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent, Func<Base, bool> predicate)
|
||||
{
|
||||
InitializeAssetCache();
|
||||
@@ -111,6 +276,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
ConverterInstance.SetContextDocument(AssetCache);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
object? converted = null;
|
||||
@@ -129,7 +295,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
//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);
|
||||
go.name = CoreUtils.GenerateObjectName(baseObject);
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
if (baseObject["physicsLayer"] is string layerName)
|
||||
{
|
||||
@@ -150,6 +316,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private IEnumerable<string> GetPotentialChildren(Base baseObject)
|
||||
{
|
||||
return ConverterInstance.CanConvertToNative(baseObject)
|
||||
@@ -157,7 +324,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
: baseObject.GetMembers().Keys;
|
||||
}
|
||||
|
||||
|
||||
[Obsolete]
|
||||
protected virtual void ConvertChild(object? value, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
foreach (Base b in GraphTraversal.TraverseMember(value))
|
||||
@@ -165,14 +332,6 @@ namespace Speckle.ConnectorUnity.Components
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -18,7 +19,12 @@ namespace Speckle.ConnectorUnity.Components
|
||||
[field: SerializeField]
|
||||
public AggregateNativeCache AssetCache { get; set; }
|
||||
|
||||
private void Awake()
|
||||
protected void Awake()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
protected void Init()
|
||||
{
|
||||
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
|
||||
@@ -30,4 +36,4 @@ namespace Speckle.ConnectorUnity.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
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;
|
||||
@@ -37,132 +40,257 @@ namespace Speckle.ConnectorUnity.Components
|
||||
|
||||
public RecursiveConverter Converter { get; private set; }
|
||||
|
||||
#nullable enable
|
||||
[Header("Events")]
|
||||
[HideInInspector]
|
||||
public CommitSelectionEvent OnCommitSelectionChange;
|
||||
public CommitSelectionEvent OnCommitSelectionChange = new();
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction;
|
||||
public OperationProgressEvent OnReceiveProgressAction = new();
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction;
|
||||
public ErrorActionEvent OnErrorAction = new();
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown;
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown = new();
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete;
|
||||
public ReceiveCompleteHandler OnComplete = new();
|
||||
|
||||
#nullable enable
|
||||
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
protected CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
public CancellationToken CancellationToken => CancellationTokenSource?.Token ?? default;
|
||||
public bool IsReceiving => CancellationTokenSource != null;
|
||||
|
||||
//TODO runtime receiving
|
||||
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action<Base>? beforeConvertCallback = 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);
|
||||
if (CancellationTokenSource == null) return false;
|
||||
CancellationTokenSource.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive the selected <see cref="Commit"/> object, and converts ToNative as children of <paramref name="parent"/>
|
||||
/// </summary>
|
||||
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
|
||||
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
|
||||
/// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> upon completion</remarks>
|
||||
/// <seealso cref="ReceiveAsync(System.Threading.CancellationToken)"/>
|
||||
/// <seealso cref="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
|
||||
public IEnumerator ReceiveAndConvert_Routine(Transform? parent, Predicate<TraversalContext>? predicate = null)
|
||||
{
|
||||
if (IsReceiving)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", new InvalidOperationException("A pending receive operation has already started"));
|
||||
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);
|
||||
|
||||
Base? b = receiveOperation.Result;
|
||||
if (b == null) yield break;
|
||||
if (receiveOperation.IsFaulted)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", receiveOperation.Exception);
|
||||
FinishOperation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
Base b = receiveOperation.Result;
|
||||
|
||||
//TODO make routine break for each catergory/object
|
||||
GameObject go = ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
|
||||
OnComplete.Invoke(go);
|
||||
foreach (var _ in Converter.RecursivelyConvertToNative_Enumerable(b, parent, predicate))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
OnComplete.Invoke(parent);
|
||||
FinishOperation();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="ReceiveAndConvert_Routine"/>
|
||||
public async void ReceiveAndConvert_Async(Transform? parent, 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();
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <exception cref="Exception">Throws various types of exceptions to indicate faliure</exception>
|
||||
/// <returns></returns>
|
||||
public static async Task<Base?> ReceiveAsync(CancellationToken token,
|
||||
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;
|
||||
transport.CancellationToken = cancellationToken;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Base? requestedObject = await Operations.Receive(
|
||||
objectId: objectId,
|
||||
cancellationToken: cancellationToken,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: (s, ex) =>
|
||||
{
|
||||
//Don't wrap cancellation exceptions!
|
||||
if (ex is OperationCanceledException)
|
||||
throw ex;
|
||||
|
||||
//HACK: Sometimes, the task was cancelled, and Operations.Receive doesn't fail in a reliable way. In this case, the exception is often simply a symptom of a cancel.
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
SpeckleLog.Logger.Warning(ex, "A task was cancelled, ignoring potentially symptomatic exception");
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
//Treat all operation errors as fatal
|
||||
throw new SpeckleException($"Failed to receive requested object {objectId} from server: {s}", ex);
|
||||
},
|
||||
onTotalChildrenCountKnown: onTotalChildrenCountKnown,
|
||||
disposeTransports: false
|
||||
).ConfigureAwait(false);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Receive, new Dictionary<string, object>()
|
||||
{
|
||||
{"mode", nameof(SpeckleReceiver)},
|
||||
{"sourceHostApp", HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug},
|
||||
{"sourceHostAppVersion", commit?.sourceApplication ?? ""},
|
||||
{"hostPlatform", Application.platform.ToString()},
|
||||
{"isMultiplayer", commit != null && commit.authorId != client.Account.userInfo.id},
|
||||
});
|
||||
|
||||
if (requestedObject == null)
|
||||
throw new SpeckleException($"Operation {nameof(Operations.Receive)} returned null");
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
//Read receipt
|
||||
try
|
||||
{
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
ret = await Operations.Receive(
|
||||
objectId: objectId,
|
||||
cancellationToken: token,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction,
|
||||
onTotalChildrenCountKnown: onTotalChildrenCountKnown,
|
||||
disposeTransports: false
|
||||
).ConfigureAwait(false);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Receive);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
//Read receipt
|
||||
try
|
||||
await client.CommitReceived(cancellationToken, new CommitReceivedInput
|
||||
{
|
||||
await client.CommitReceived(token, new CommitReceivedInput
|
||||
{
|
||||
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}");
|
||||
}
|
||||
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,6 +299,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <param name="rootObjectName">The name of the parent <see cref="GameObject"/> to create</param>
|
||||
/// <param name="beforeConvertCallback">Callback for each object converted</param>
|
||||
/// <returns>The created parent <see cref="GameObject"/></returns>
|
||||
[Obsolete("Use " + nameof(RecursiveConverter) + " Now we have implemented support for " + nameof(Collection) + "s, receiving any collection is now the default behaviour")]
|
||||
public GameObject ConvertToNativeWithCategories(Base @base, string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback)
|
||||
{
|
||||
@@ -203,7 +332,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
|
||||
return rootObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -212,6 +341,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <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,
|
||||
@@ -250,22 +380,21 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// </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 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
|
||||
@@ -276,6 +405,8 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public string GetSelectedUrl()
|
||||
{
|
||||
string serverUrl = Account.Selected!.serverInfo.url;
|
||||
@@ -311,12 +442,16 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
public void OnDisable()
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
@@ -325,12 +460,32 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
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
|
||||
GameObject go = 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 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;
|
||||
@@ -84,7 +85,11 @@ namespace Speckle.ConnectorUnity.Components
|
||||
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)
|
||||
{
|
||||
|
||||
+20
-13
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Objects.Other;
|
||||
using Speckle.ConnectorUnity;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
@@ -212,15 +213,11 @@ 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;
|
||||
}
|
||||
if (instance.definition == 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))
|
||||
{
|
||||
@@ -231,15 +228,19 @@ 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);
|
||||
@@ -252,7 +253,7 @@ namespace Objects.Converter.Unity
|
||||
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);
|
||||
}
|
||||
@@ -269,8 +270,14 @@ namespace Objects.Converter.Unity
|
||||
|
||||
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;
|
||||
return native;
|
||||
}
|
||||
|
||||
@@ -354,4 +361,4 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+9
-15
@@ -6,6 +6,7 @@ using Speckle.ConnectorUnity.Utils;
|
||||
using Objects.Other;
|
||||
using Objects.Utils;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
@@ -209,13 +210,9 @@ 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;
|
||||
}
|
||||
if (!meshes.Any()) throw new ArgumentException("Expected at least one Mesh", nameof(meshes));
|
||||
|
||||
Material[] nativeMaterials = RenderMaterialsToNative(meshes);
|
||||
|
||||
@@ -223,7 +220,7 @@ 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);
|
||||
}
|
||||
@@ -241,15 +238,12 @@ namespace Objects.Converter.Unity
|
||||
/// </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;
|
||||
}
|
||||
|
||||
GameObject? converted = MeshesToNative(speckleMesh, new[] {speckleMesh});
|
||||
throw new ArgumentException("mesh data was empty", nameof(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)));
|
||||
@@ -483,7 +477,7 @@ namespace Objects.Converter.Unity
|
||||
|
||||
var c = renderMaterial.diffuse.ToUnityColor();
|
||||
mat.color = new Color(c.r, c.g, c.b, (float) renderMaterial.opacity);
|
||||
mat.name = AssetHelpers.GenerateObjectName(renderMaterial);
|
||||
mat.name = CoreUtils.GenerateObjectName(renderMaterial);
|
||||
mat.SetFloat(Metallic, (float) renderMaterial.metalness);
|
||||
mat.SetFloat(Glossiness, 1 - (float) renderMaterial.roughness);
|
||||
if (renderMaterial.emissive != SColor.Black.ToArgb()) mat.EnableKeyword("_EMISSION");
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Objects.BuiltElements;
|
||||
using Objects.Organization;
|
||||
using Objects.Other;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
@@ -111,8 +112,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
|
||||
@@ -183,6 +186,19 @@ 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 +211,17 @@ namespace Objects.Converter.Unity
|
||||
// return true;
|
||||
// case Curve _:
|
||||
// return true;
|
||||
// case View3D _:
|
||||
// case View2D:
|
||||
// return false;
|
||||
// case View _:
|
||||
// return true;
|
||||
case View2D _:
|
||||
return false;
|
||||
case Mesh _:
|
||||
case Model:
|
||||
return false; //This allows us to traverse older commits pre-collections
|
||||
case Collection:
|
||||
return true;
|
||||
case BlockInstance _:
|
||||
case Mesh:
|
||||
return true;
|
||||
case Instance:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -212,9 +232,9 @@ namespace Objects.Converter.Unity
|
||||
if (@object[alias] is IList)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ 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;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#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
|
||||
{
|
||||
@@ -69,38 +70,12 @@ namespace Speckle.ConnectorUnity.NativeCache
|
||||
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";
|
||||
@@ -108,7 +83,7 @@ namespace Speckle.ConnectorUnity.NativeCache
|
||||
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();
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Speckle.ConnectorUnity.NativeCache",
|
||||
"rootNamespace": "Speckle.ConnectorUnity",
|
||||
"references": [],
|
||||
"references": ["GUID:50d889142fdf9de4b8501c6eaa4b3225"],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
"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": {}
|
||||
@@ -541,7 +541,7 @@
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Objects/2.1.1": {
|
||||
"Objects/2.0.999-local": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
||||
#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);
|
||||
Setup.Init(HostApplications.Unity.GetVersion(GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
}
|
||||
|
||||
public static HostAppVersion GetHostAppVersion()
|
||||
@@ -31,6 +32,30 @@ namespace Speckle.ConnectorUnity
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c62e8ef712f821d418d1a8a248e75df3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
#nullable enable
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Utils
|
||||
{
|
||||
public static class CoreUtils
|
||||
{
|
||||
public static void SetupInit()
|
||||
{
|
||||
Setup.Init(HostApplications.Unity.GetVersion(GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
}
|
||||
|
||||
public static HostAppVersion GetHostAppVersion()
|
||||
{
|
||||
#if UNITY_2019
|
||||
return HostAppVersion.v2019;
|
||||
#elif UNITY_2020
|
||||
return HostAppVersion.v2020;
|
||||
#elif UNITY_2021
|
||||
return HostAppVersion.v2021;
|
||||
#elif UNITY_2022
|
||||
return HostAppVersion.v2022;
|
||||
#elif UNITY_2023
|
||||
return HostAppVersion.v2023;
|
||||
#elif UNITY_2024
|
||||
return HostAppVersion.v2024;
|
||||
#elif UNITY_2025
|
||||
return HostAppVersion.v2025;
|
||||
#else
|
||||
return HostAppVersion.v;
|
||||
#endif
|
||||
}
|
||||
|
||||
public const string ObjectNameSeparator = " -- ";
|
||||
|
||||
/// <param name="speckleObject">The object to be named</param>
|
||||
/// <returns>A human-readable Object name unique to the given <paramref name="speckleObject"/></returns>
|
||||
public static string GenerateObjectName(Base speckleObject)
|
||||
{
|
||||
var prefix = GetFriendlyObjectName(speckleObject) ?? SimplifiedSpeckleType(speckleObject);
|
||||
return $"{prefix}{ObjectNameSeparator}{speckleObject.id}";
|
||||
}
|
||||
|
||||
public static string? GetFriendlyObjectName(Base speckleObject)
|
||||
{
|
||||
return speckleObject["name"] as string
|
||||
?? speckleObject["Name"] as string
|
||||
?? speckleObject["family"] as string;
|
||||
}
|
||||
|
||||
/// <param name="speckleObject"></param>
|
||||
/// <returns>The most significant type in a given <see cref="Base.speckle_type"/></returns>
|
||||
public static string SimplifiedSpeckleType(Base speckleObject)
|
||||
{
|
||||
return speckleObject.speckle_type.Split(':')[^1];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 280d43bfe5dc1d14181295ae5c365183
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Speckle.Core.Models;
|
||||
|
||||
namespace Speckle.ConnectorUnity.SpeckleUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="Base"/> object models
|
||||
/// </summary>
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets a property dynamically, checking if an instance prop with the same name exists
|
||||
/// </summary>
|
||||
/// <param name="speckleObject"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="value"></param>
|
||||
#pragma warning disable CS0618
|
||||
public static void SetDetachedPropertyChecked(this Base speckleObject, string propertyName, object? value)
|
||||
{
|
||||
if(speckleObject.GetInstanceMembersNames().Any(name => name == propertyName))
|
||||
speckleObject[propertyName] = value;
|
||||
else
|
||||
speckleObject[$"@{propertyName}"] = value;
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f88837bf55bd20642a1aed6b2a3b6b81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Speckle.ConnectorUnity.SpeckleUtils",
|
||||
"rootNamespace": "Speckle.ConnectorUnity",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": true
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e71520b51b4847d45ab0b1dacbf935c5
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d74d43435d909534d9b12f5ed3758603
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,155 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Speckle.ConnectorUnity.UnityUtils
|
||||
{
|
||||
public static class EngineUtils
|
||||
{
|
||||
|
||||
public static void SafeDestroy(UnityEngine.Object obj)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
UnityEngine.Object.Destroy(obj);
|
||||
|
||||
else
|
||||
UnityEngine.Object.DestroyImmediate(obj);
|
||||
|
||||
}
|
||||
|
||||
public static bool Valid(this string name) => !string.IsNullOrEmpty(name);
|
||||
|
||||
public static Mesh SafeMeshGet(this MeshFilter mf) => Application.isPlaying ? mf.mesh : mf.sharedMesh;
|
||||
|
||||
|
||||
|
||||
public static void SafeMeshSet(this GameObject go, Mesh m, Material[] materials, bool addMeshComponentsIfNotFound = true)
|
||||
{
|
||||
MeshFilter? mf = go.GetComponent<MeshFilter>();
|
||||
MeshRenderer? mr = go.GetComponent<MeshRenderer>();
|
||||
|
||||
if (addMeshComponentsIfNotFound)
|
||||
{
|
||||
if (mf == null)
|
||||
mf = go.AddComponent<MeshFilter>();
|
||||
if (mr == null)
|
||||
mr = go.AddComponent<MeshRenderer>();
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
mf.mesh = m;
|
||||
mr.materials = materials;
|
||||
}
|
||||
else
|
||||
{
|
||||
mf.sharedMesh = m;
|
||||
mr.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Unity color to an ARBG int
|
||||
/// </summary>
|
||||
public static int ToIntColor(this Color c)
|
||||
{
|
||||
return
|
||||
System.Drawing.Color
|
||||
.FromArgb(Convert.ToInt32(c.r * 255), Convert.ToInt32(c.g * 255), Convert.ToInt32(c.b * 255))
|
||||
.ToArgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a ARGB formatted int to a Unity Color
|
||||
/// </summary>
|
||||
public static Color ToUnityColor(this int c)
|
||||
{
|
||||
var argb = System.Drawing.Color.FromArgb(c);
|
||||
return new Color(argb.R / 255f, argb.G / 255f, argb.B / 255f);
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerator GetImageRoutine(string url, string authToken, Action<Texture2D?> callback)
|
||||
{
|
||||
using UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
|
||||
www.SetRequestHeader("Authorization", $"Bearer {authToken}");
|
||||
UnityWebRequestAsyncOperation request = www.SendWebRequest();
|
||||
|
||||
yield return request;
|
||||
|
||||
if(www.result != UnityWebRequest.Result.Success )
|
||||
{
|
||||
bool isDataError = www.result == UnityWebRequest.Result.DataProcessingError;
|
||||
string error = isDataError
|
||||
? $"{www.result}: {www.downloadHandler.error}"
|
||||
: www.error;
|
||||
|
||||
Debug.LogWarning( $"Error fetching image from {www.url}: {error}" );
|
||||
yield break;
|
||||
}
|
||||
Texture2D? texture = DownloadHandlerTexture.GetContent(www);
|
||||
callback.Invoke(texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine <see cref="CustomYieldInstruction"/> that starts and waits for an async <see cref="System.Threading.Tasks.Task"/>
|
||||
/// to complete.
|
||||
/// </summary>
|
||||
/// <remarks>Useful for running async tasks from coroutines</remarks>
|
||||
public class WaitForTask : CustomYieldInstruction
|
||||
{
|
||||
public readonly Task Task;
|
||||
public override bool keepWaiting => !Task.IsCompleted;
|
||||
|
||||
public WaitForTask(Func<Task> function)
|
||||
{
|
||||
Task = Task.Run(function);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="WaitForTask"/>
|
||||
public sealed class WaitForTask<TResult> : CustomYieldInstruction
|
||||
{
|
||||
public readonly Task<TResult> Task;
|
||||
public TResult Result => Task.Result;
|
||||
public override bool keepWaiting => !Task.IsCompleted;
|
||||
public WaitForTask(Func<Task<TResult>> function)
|
||||
{
|
||||
this.Task = System.Threading.Tasks.Task.Run(function);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class PerformanceThrottle : IEnumerator
|
||||
{
|
||||
private readonly IEnumerator _routine;
|
||||
public PerformanceThrottle(IEnumerator routine)
|
||||
{
|
||||
_routine = routine;
|
||||
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_routine.MoveNext())
|
||||
{
|
||||
_ = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_routine.Reset();
|
||||
}
|
||||
|
||||
public object? Current => _routine.Current;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98317fb9e5c090c46a7802a177a3c691
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Speckle.ConnectorUnity.Utils.Unity",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Utils.Unity",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13c1dc1fefb5ca44aa3d755118da5f6c
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,9 +1,10 @@
|
||||
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
|
||||
@@ -93,5 +94,34 @@ namespace Speckle.ConnectorUnity.Utils
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,9 +10,9 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
public sealed class BranchSelection : OptionSelection<Branch>
|
||||
{
|
||||
[field: SerializeField, Range(1,100), Tooltip("Number of branches to request")]
|
||||
public int BranchesLimit { get; set; } = 30;
|
||||
public int BranchesLimit { get; set; } = 100;
|
||||
[field: SerializeField, Range(1,100), Tooltip("Number of commits to request")]
|
||||
public int CommitsLimit { get; set; } = 15;
|
||||
public int CommitsLimit { get; set; } = 25;
|
||||
|
||||
[field: SerializeReference]
|
||||
public StreamSelection StreamSelection { get; private set; }
|
||||
|
||||
+3
-3
@@ -34,7 +34,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Options == null) return null;
|
||||
if (Options is null) return null;
|
||||
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null;
|
||||
return Options[SelectedIndex];
|
||||
}
|
||||
@@ -52,7 +52,7 @@ 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)
|
||||
@@ -77,4 +77,4 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
||||
OnSelectionChange?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -18,8 +20,10 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private string _serializedData = "";
|
||||
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private bool _hasChanged;
|
||||
|
||||
private ObservableConcurrentDictionary<string, object> _data;
|
||||
|
||||
public IDictionary<string, object> Data
|
||||
@@ -40,10 +44,7 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
private string _serializedSpeckleType;
|
||||
private Type _speckleType = typeof(Base);
|
||||
public Type SpeckleType {
|
||||
get
|
||||
{
|
||||
return _speckleType ??= typeof(Base);
|
||||
}
|
||||
get => _speckleType ??= typeof(Base);
|
||||
set
|
||||
{
|
||||
|
||||
@@ -80,7 +81,9 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Base speckleData = Operations.Deserialize(_serializedData);
|
||||
var deserializer = new BaseObjectDeserializerV2();
|
||||
Base speckleData = deserializer.Deserialize(_serializedData);
|
||||
|
||||
Data = speckleData.GetMembers();
|
||||
_hasChanged = false;
|
||||
|
||||
@@ -96,7 +99,7 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class SpeckleData : Base
|
||||
private sealed class SpeckleData : Base
|
||||
{
|
||||
public SpeckleData(IDictionary<string, object> data)
|
||||
{
|
||||
@@ -107,4 +110,4 @@ namespace Speckle.ConnectorUnity.Wrappers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "systems.speckle.speckle-unity",
|
||||
"version": "2.13.0",
|
||||
"version": "2.14.2",
|
||||
"displayName": "Speckle Unity Connector",
|
||||
"description": "AEC Interoperability for Unity through Speckle",
|
||||
"unity": "2018.4",
|
||||
|
||||
Reference in New Issue
Block a user