From a89e4cdfe7b245d8f36841d1ba1c676f84da9abe Mon Sep 17 00:00:00 2001
From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Date: Fri, 21 Apr 2023 22:34:59 +0100
Subject: [PATCH] wip implementation of traversal refactor
---
.../Components/RecursiveConverter.ToNative.cs | 206 +++++++++++++++---
.../Runtime/Components/SpeckleReceiver.cs | 3 +-
.../Unity/ConverterUnity.Geometry.cs | 16 +-
.../Converter/Unity/ConverterUnity.Mesh.cs | 20 +-
.../Runtime/Utils/CoreUtils.cs | 3 +
5 files changed, 195 insertions(+), 53 deletions(-)
diff --git a/Packages/systems.speckle.speckle-unity/Runtime/Components/RecursiveConverter.ToNative.cs b/Packages/systems.speckle.speckle-unity/Runtime/Components/RecursiveConverter.ToNative.cs
index d5f676c..abe0faf 100644
--- a/Packages/systems.speckle.speckle-unity/Runtime/Components/RecursiveConverter.ToNative.cs
+++ b/Packages/systems.speckle.speckle-unity/Runtime/Components/RecursiveConverter.ToNative.cs
@@ -2,55 +2,197 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Speckle.ConnectorUnity.NativeCache;
+using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.GraphTraversal;
using UnityEngine;
+using UnityEngine.Networking;
namespace Speckle.ConnectorUnity.Components
{
- public partial class RecursiveConverter
+
+ ///
+ /// Struct that encapsulates the result of a ToNative conversion of a single SpeckleObject
+ ///
+ public readonly struct ConversionResult
{
+ ///
+ /// The context that was converted ToNative
+ ///
+ public readonly TraversalContext traversalContext;
+
+ ///
+ /// The result of conversion a successful conversion
+ ///
+ public readonly GameObject? converted;
+
+ ///
+ /// The result of conversion a failed conversion
+ ///
+ public readonly Exception? exception;
- public IEnumerator ConvertToNative(Base rootObject, Transform? parent, IDictionary outCreatedObjects)
+ ///
+ /// Constructor used for Successful conversions
+ ///
+ /// The current traversal context
+ /// The resultant ToNative conversion of context object
+ ///
+ public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted)
+ : this(traversalContext, converted, null)
{
-
- InitializeAssetCache();
- var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
+ if (converted == null) throw new ArgumentNullException(nameof(converted));
+ }
- var convertableObjects = traversalFunc.Traverse(rootObject)
- .Where(tc => ConverterInstance.CanConvertToNative(tc.current));
+ ///
+ /// Constructor used for Failed conversions
+ ///
+ /// The current conversion
+ /// The operation halting exception that occured
+ /// Optional converted GameObject
+ ///
+ public ConversionResult(TraversalContext traversalContext, [NotNull] Exception? exception,
+ GameObject? converted = null)
+ : this(traversalContext, converted, exception)
+ {
+ if (exception == null) throw new ArgumentNullException(nameof(exception));
+ }
- 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;
+ private ConversionResult(TraversalContext traversalContext, GameObject? converted, Exception? exception)
+ {
+ this.traversalContext = traversalContext;
+ this.converted = converted;
+ this.exception = exception;
+ }
- yield return go;
- }
+ ///
+ ///
+ ///
+ /// The converted
+ /// The that occured during conversion
+ /// True if the conversion was successful
+ public bool WasSuccessful(
+ [NotNullWhen(true)] out GameObject? converted,
+ [NotNullWhen(false)] out Exception? exception)
+ {
+ converted = this.converted;
+ exception = this.exception;
+ return this.exception == null;
}
+ public Base SpeckleObject => traversalContext.current;
+ }
+
+ public partial class RecursiveConverter
+ {
+ ///
+ /// Coroutine for
+ ///
+ ///
+ ///
+ /// Optional filter function
+ ///
+ public IEnumerator RecursivelyConvertToNative(Base rootObject, Transform? parent,
+ Func? predicate = null)
+ {
+ var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
+
+ var objectsToConvert = traversalFunc.Traverse(rootObject);
+
+ if(predicate != null) objectsToConvert = objectsToConvert.Where(predicate);
+
+ Dictionary created = new();
+ foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
+ {
+ Base speckleObject = conversionResult.SpeckleObject;
+ if (conversionResult.WasSuccessful(out var converted, out var ex))
+ {
+ created.Add(speckleObject, converted);
+ }
+ else
+ {
+ Debug.LogWarning($"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}",this);
+ }
+
+ yield return conversionResult;
+ }
+
+ Debug.Log($"Finished converting {rootObject.id} to native. Created {created.Count} {nameof(GameObject)}s ",this);
+
+ }
+ ///
+ /// Converts a objectTree (see ) to unevaluated enumerable.
+ /// As this enumerable is iterated through, each context object will be converted to (if successful)
+ /// or if not.
+ ///
+ ///
+ /// You may enumerate over multiple frames (e.g. coroutine) but you must ensure the output eventually gets fully enumerated (exactly once)
+ ///
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable ConvertTree(IEnumerable objectTree, Transform? parent, IDictionary outCreatedObjects)
+ {
+ InitializeAssetCache();
+ AssetCache.BeginWrite();
+
+ foreach (TraversalContext tc in objectTree)
+ {
+ Transform? currentParent = GetParent(tc, outCreatedObjects) ?? parent;
+
+ ConversionResult result;
+ try
+ {
+ 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();
+ }
+
+ private Transform? GetParent(TraversalContext? tc, IDictionary 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);
+ }
+
+ private 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 = AssetHelpers.GenerateObjectName(speckleObject);
+ //if (baseObject["tag"] is string t) go.tag = t;
+ 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["isStatic"] is bool isStatic) go.isStatic = isStatic;
+ return go;
+ }
diff --git a/Packages/systems.speckle.speckle-unity/Runtime/Components/SpeckleReceiver.cs b/Packages/systems.speckle.speckle-unity/Runtime/Components/SpeckleReceiver.cs
index 4058f96..6bce1b3 100644
--- a/Packages/systems.speckle.speckle-unity/Runtime/Components/SpeckleReceiver.cs
+++ b/Packages/systems.speckle.speckle-unity/Runtime/Components/SpeckleReceiver.cs
@@ -51,7 +51,8 @@ namespace Speckle.ConnectorUnity.Components
#nullable enable
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
-
+
+
//TODO runtime receiving
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action? beforeConvertCallback = null)
{
diff --git a/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Geometry.cs b/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Geometry.cs
index 155097a..b3b1fef 100644
--- a/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Geometry.cs
+++ b/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Geometry.cs
@@ -212,13 +212,9 @@ 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 ?? "";
// Check for existing converted object
@@ -269,8 +265,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 = AssetHelpers.GetFriendlyObjectName(instance) != null
+ ? AssetHelpers.GenerateObjectName(instance)
+ : defName;
+
+ native.name = instanceName;
return native;
}
diff --git a/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Mesh.cs b/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Mesh.cs
index 4c15d03..bf70db1 100644
--- a/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Mesh.cs
+++ b/Packages/systems.speckle.speckle-unity/Runtime/Converter/Unity/ConverterUnity.Mesh.cs
@@ -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
/// The object being converted
/// Collection of es that shall be converted
/// A with the converted , , and
- public GameObject? MeshesToNative(Base element, IReadOnlyCollection meshes)
+ public GameObject MeshesToNative(Base element, IReadOnlyCollection 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);
@@ -241,15 +238,12 @@ namespace Objects.Converter.Unity
///
/// Mesh to convert
///
- 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)));
diff --git a/Packages/systems.speckle.speckle-unity/Runtime/Utils/CoreUtils.cs b/Packages/systems.speckle.speckle-unity/Runtime/Utils/CoreUtils.cs
index 8786c5b..94b94cd 100644
--- a/Packages/systems.speckle.speckle-unity/Runtime/Utils/CoreUtils.cs
+++ b/Packages/systems.speckle.speckle-unity/Runtime/Utils/CoreUtils.cs
@@ -31,6 +31,9 @@ namespace Speckle.ConnectorUnity
return HostAppVersion.v;
#endif
}
+
+
+ public static bool Tautology(T _) => true;
}
}