wip implementation of traversal refactor
This commit is contained in:
+174
-32
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Struct that encapsulates the result of a ToNative conversion of a single SpeckleObject <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;
|
||||
|
||||
public IEnumerator ConvertToNative(Base rootObject, Transform? parent, IDictionary<Base, GameObject> outCreatedObjects)
|
||||
/// <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)
|
||||
{
|
||||
|
||||
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));
|
||||
/// <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 == 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;
|
||||
}
|
||||
/// <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 this.exception == null;
|
||||
}
|
||||
|
||||
public Base SpeckleObject => traversalContext.current;
|
||||
}
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Coroutine for
|
||||
/// </summary>
|
||||
/// <param name="rootObject"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicate">Optional filter function </param>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<ConversionResult> RecursivelyConvertToNative(Base rootObject, Transform? parent,
|
||||
Func<TraversalContext, bool>? predicate = null)
|
||||
{
|
||||
var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
|
||||
|
||||
var objectsToConvert = traversalFunc.Traverse(rootObject);
|
||||
|
||||
if(predicate != null) objectsToConvert = objectsToConvert.Where(predicate);
|
||||
|
||||
Dictionary<Base, GameObject> 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);
|
||||
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public IEnumerable<ConversionResult> ConvertTree(IEnumerable<TraversalContext> objectTree, Transform? parent, IDictionary<Base, GameObject?> 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<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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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<Base>? beforeConvertCallback = null)
|
||||
{
|
||||
|
||||
+9
-7
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+7
-13
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Speckle.ConnectorUnity
|
||||
return HostAppVersion.v;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static bool Tautology<T>(T _) => true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user