Updated speckle properties to support selection of Base type
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7f8762fcd0c1174782d2ba683cb6b74
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
//Serialized wrapper around a Base object
|
||||
[RequireComponent(typeof(SpeckleProperties)), ExecuteAlways]
|
||||
public class SpeckleObject : MonoBehaviour, ISerializationCallbackReceiver
|
||||
{
|
||||
|
||||
private SpeckleProperties properties;
|
||||
public void Awake()
|
||||
{
|
||||
properties = GetComponent<SpeckleProperties>();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Value: ");
|
||||
foreach (var kvp in properties.Data)
|
||||
{
|
||||
//var newKey = GUILayout.TextField(kvp.Key, GUILayout.rea);
|
||||
GUILayout.Label(kvp.Key);
|
||||
var existingValue = kvp.Value;
|
||||
var newValue = CreateField(kvp.Value);
|
||||
|
||||
if(newValue != existingValue)
|
||||
properties.Data[kvp.Key] = newValue;
|
||||
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static object CreateField(object v)
|
||||
{
|
||||
const string label = "Value";
|
||||
GUILayoutOption[] options = { GUILayout.ExpandWidth(true) };
|
||||
|
||||
return v switch
|
||||
{
|
||||
int i => EditorGUILayout.IntField(i, options),
|
||||
long l => EditorGUILayout.LongField(l, options),
|
||||
float f => EditorGUILayout.FloatField(f, options),
|
||||
double d => EditorGUILayout.DoubleField(d, options),
|
||||
string s => EditorGUILayout.TextField(s, options),
|
||||
bool b => GUILayout.Toggle(b, label, options),
|
||||
Enum e => EditorGUILayout.EnumPopup(e, options),
|
||||
Object o => EditorGUILayout.ObjectField(label, o, o.GetType(), true, options),
|
||||
_ => v
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public class SpeckleObject<T> : SpeckleObject where T : Base
|
||||
// {
|
||||
//
|
||||
// public SpeckleObject(T value)
|
||||
// : base(value)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// //What I want this to look like
|
||||
// /*
|
||||
// * SubclassOf<Base> SpeckleType; //enforced type
|
||||
// * int area
|
||||
// * int prop... //explicit props defined here
|
||||
// * Map<string, object> DynamicProps...
|
||||
// *
|
||||
// */
|
||||
//
|
||||
//
|
||||
// }
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bb07d1449c8af34c8531fc28e235d33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+99303
-4
File diff suppressed because one or more lines are too long
@@ -0,0 +1,68 @@
|
||||
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
public class TestManualReceive : MonoBehaviour
|
||||
{
|
||||
|
||||
[field: SerializeField] private string AuthToken { get; set; }
|
||||
[field: SerializeField] private string ServerUrl { get; set; }
|
||||
[field: SerializeField] private string StreamId { get; set; }
|
||||
[field: SerializeField] private string ObjectId { get; set; }
|
||||
|
||||
|
||||
private RecursiveConverter receiver;
|
||||
|
||||
|
||||
void Awake()
|
||||
{
|
||||
receiver = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
|
||||
IEnumerator Start()
|
||||
{
|
||||
Debug.developerConsoleVisible = true;
|
||||
if(Time.timeSinceLevelLoad > 20) yield return null;
|
||||
Receive();
|
||||
}
|
||||
|
||||
public void Receive()
|
||||
{
|
||||
var account = new Account()
|
||||
{
|
||||
token = AuthToken,
|
||||
serverInfo = new ServerInfo() {url = ServerUrl},
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var transport = new ServerTransport(account, StreamId);
|
||||
var localTransport = new MemoryTransport();
|
||||
|
||||
var @base = await Operations.Receive(
|
||||
ObjectId,
|
||||
remoteTransport: transport,
|
||||
localTransport: localTransport,
|
||||
onErrorAction: (m, e)=> Debug.LogError(m + e),
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
|
||||
var rc = GetComponent<RecursiveConverter>();
|
||||
var parentObject = new GameObject(name);
|
||||
rc.RecursivelyConvertToNative(@base, parentObject.transform);
|
||||
|
||||
Debug.Log($"Receive {ObjectId} completed");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66c38558aa523bf4290b4fd84411df46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
using Objects.BuiltElements;
|
||||
using Objects.Geometry;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
@@ -22,7 +22,7 @@ namespace Objects.Converter.Unity
|
||||
camera.transform.up = VectorByCoordinates(speckleView.upDirection.x, speckleView.upDirection.y,
|
||||
speckleView.upDirection.z, speckleView.upDirection.units);
|
||||
|
||||
AttachSpeckleProperties(go, speckleView.GetMembers());
|
||||
AttachSpeckleProperties(go, speckleView.GetType(),speckleView.GetMembers());
|
||||
return go;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Objects.BuiltElements;
|
||||
using UnityEngine;
|
||||
using Mesh = Objects.Geometry.Mesh;
|
||||
|
||||
@@ -36,22 +38,60 @@ namespace Objects.Converter.Unity
|
||||
public void SetPreviousContextObjects(List<ApplicationPlaceholderObject> objects) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
#nullable enable
|
||||
public object? ConvertToNative(Base @object) => ConvertToNativeGameObject(@object);
|
||||
|
||||
public Base ConvertToSpeckle(object @object)
|
||||
{
|
||||
switch (@object)
|
||||
{
|
||||
case GameObject o:
|
||||
if (o.GetComponent<MeshFilter>() != null)
|
||||
return MeshToSpeckle(o);
|
||||
throw new NotSupportedException();
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (!(@object is GameObject go)) throw new NotSupportedException($"Cannot convert object of type {@object.GetType()} to Speckle");
|
||||
return ConvertGameObjectToSpeckle(go);
|
||||
}
|
||||
|
||||
#endregion implemented methods
|
||||
|
||||
public object ConvertToNative(Base @object)
|
||||
|
||||
|
||||
public Base ConvertGameObjectToSpeckle(GameObject go)
|
||||
{
|
||||
switch (@object)
|
||||
|
||||
Base speckleObject = CreateSpeckleObjectFromProperties(go);
|
||||
|
||||
speckleObject["name"] = go.name;
|
||||
//speckleObject["transform"] = TransformToSpeckle(go.Transform); //TODO
|
||||
speckleObject["tag"] = go.tag;
|
||||
speckleObject["layer"] = go.layer;
|
||||
speckleObject["isStatic"] = go.isStatic;
|
||||
|
||||
foreach (Component component in go.GetComponents<Component>())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case MeshFilter meshFilter:
|
||||
speckleObject["@displayValue"] = MeshToSpeckle(meshFilter);
|
||||
break;
|
||||
// case Camera camera:
|
||||
// speckleObject["cameraComponent"] = CameraToSpeckle(camera);
|
||||
// break;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to convert {component.GetType()} component\n{e}", component);
|
||||
}
|
||||
}
|
||||
|
||||
return speckleObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public GameObject? ConvertToNativeGameObject(Base speckleObject)
|
||||
{
|
||||
switch (speckleObject)
|
||||
{
|
||||
// case Point o:
|
||||
// return PointToNative(o);
|
||||
@@ -61,23 +101,28 @@ namespace Objects.Converter.Unity
|
||||
// return PolylineToNative(o);
|
||||
// case Curve o:
|
||||
// return CurveToNative(o);
|
||||
// case View3D o:
|
||||
// return View3DToNative(o);
|
||||
case View3D v:
|
||||
return View3DToNative(v);
|
||||
case Mesh o:
|
||||
return MeshToNative(o);
|
||||
default:
|
||||
//capture any other object that might have a mesh representation
|
||||
object fallbackObject = DisplayValueToNative(@object);
|
||||
if (fallbackObject != null) return fallbackObject;
|
||||
|
||||
Debug.LogWarning($"Skipping {@object.GetType()} {@object.id} - Not supported type");
|
||||
return null;
|
||||
//Object is not a raw geometry, convert it as display value element
|
||||
GameObject? element = DisplayValueToNative(speckleObject);
|
||||
if (element != null)
|
||||
{
|
||||
AttachSpeckleProperties(element, speckleObject.GetType(), GetProperties(speckleObject));
|
||||
return element;
|
||||
}
|
||||
|
||||
return new GameObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public IList<string> DisplayValuePropertyAliases { get; set; } = new[] {"displayValue","@displayValue","displayMesh", "@displayMesh" };
|
||||
public object DisplayValueToNative(Base @object)
|
||||
public GameObject? DisplayValueToNative(Base @object)
|
||||
{
|
||||
foreach (string alias in DisplayValuePropertyAliases)
|
||||
{
|
||||
@@ -85,14 +130,13 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
//capture any other object that might have a mesh representation
|
||||
case IList dvCollection:
|
||||
return MeshesToNative(@object, dvCollection.OfType<Mesh>().ToList());
|
||||
return MeshesToNative(dvCollection.OfType<Mesh>().ToList());
|
||||
case Mesh dvMesh:
|
||||
return MeshesToNative(@object , new[] {dvMesh});
|
||||
return MeshesToNative(new[] {dvMesh});
|
||||
case Base dvBase:
|
||||
return ConvertToNative(dvBase);
|
||||
return ConvertToNativeGameObject(dvBase);
|
||||
}
|
||||
}
|
||||
Debug.LogWarning("displayValue had no convertable objects");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -101,10 +145,9 @@ namespace Objects.Converter.Unity
|
||||
return objects.Select(ConvertToSpeckle).ToList();
|
||||
}
|
||||
|
||||
public List<object> ConvertToNative(List<Base> objects)
|
||||
public List<object?> ConvertToNative(List<Base> objects)
|
||||
{
|
||||
return objects.Select(x => ConvertToNative(x)).ToList();
|
||||
|
||||
}
|
||||
|
||||
public bool CanConvertToSpeckle(object @object)
|
||||
@@ -146,10 +189,8 @@ namespace Objects.Converter.Unity
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion implemented methods
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Speckle.Connector.Editor",
|
||||
"rootNamespace": "",
|
||||
"rootNamespace": "Speckle.ConnectorUnity",
|
||||
"references": [
|
||||
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Objects.Geometry;
|
||||
using Objects.Primitive;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Mesh = Objects.Geometry.Mesh;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity.Editor
|
||||
{
|
||||
[CustomEditor(typeof(SpeckleProperties))]
|
||||
public class SpecklePropertiesEditor : UnityEditor.Editor
|
||||
{
|
||||
private static readonly string[] SpeckleTypeOptionStrings;
|
||||
private static readonly Type[] SpeckleTypeOptions;
|
||||
|
||||
private HashSet<string> ArrayFoldoutState = new HashSet<string>();
|
||||
|
||||
static SpecklePropertiesEditor()
|
||||
{
|
||||
var options = typeof(Mesh).Assembly
|
||||
.GetTypes()
|
||||
.Where(x => x.IsSubclassOf(typeof(Base)) && !x.IsAbstract).ToList();
|
||||
|
||||
var strings = options
|
||||
.Where(x => x.FullName != null)
|
||||
.Select(x => x.FullName!.Replace('.', '/'));
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
SpeckleProperties properties = (SpeckleProperties) target;
|
||||
|
||||
// SpeckleType
|
||||
GUILayout.Label("Speckle Type: ");
|
||||
|
||||
var oldIndex = Array.IndexOf(SpeckleTypeOptions, properties.SpeckleType);
|
||||
var speckleTypeSelectedIndex = EditorGUILayout.Popup(oldIndex, SpeckleTypeOptionStrings);
|
||||
|
||||
if(oldIndex != speckleTypeSelectedIndex && speckleTypeSelectedIndex >= 0)
|
||||
{
|
||||
properties.SpeckleType = SpeckleTypeOptions[speckleTypeSelectedIndex];
|
||||
}
|
||||
|
||||
//Properties
|
||||
GUILayout.Label("Properties: ");
|
||||
|
||||
foreach (var kvp in properties.Data)
|
||||
{
|
||||
var existingValue = kvp.Value;
|
||||
var newValue = CreateField(existingValue, kvp.Key, propLayoutOptions);
|
||||
if(newValue != existingValue)
|
||||
properties.Data[kvp.Key] = newValue;
|
||||
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
}
|
||||
|
||||
private object? CreateField(object? v, string propName, params GUILayoutOption[] options)
|
||||
{
|
||||
object? ret = v switch
|
||||
{
|
||||
Enum e => EditorGUILayout.EnumPopup(propName, e, options),
|
||||
Object o => EditorGUILayout.ObjectField(propName, o, o.GetType(), true, options),
|
||||
IList l => ArrayField(propName, l, options),
|
||||
_ => CreateFieldPrimitive(v, propName, options),
|
||||
};
|
||||
if (ret != null) return ret;
|
||||
|
||||
EditorGUILayout.TextField(propName, v == null? "NULL" : v.ToString());
|
||||
return v;
|
||||
}
|
||||
|
||||
private static object? CreateFieldPrimitive(object? v, string propName, params GUILayoutOption[] options)
|
||||
{
|
||||
return v switch
|
||||
{
|
||||
int i => EditorGUILayout.IntField(propName, i, options),
|
||||
long l => EditorGUILayout.LongField(propName, l, options),
|
||||
float f => EditorGUILayout.FloatField(propName, f, options),
|
||||
double d => EditorGUILayout.DoubleField(propName, d, options),
|
||||
string s => EditorGUILayout.TextField(propName, s, options),
|
||||
bool b => GUILayout.Toggle(b, propName, options),
|
||||
Enum e => EditorGUILayout.EnumPopup(propName, e, options),
|
||||
Point p => PointToVector3(EditorGUILayout.Vector3Field(propName, new Vector3((float)p.x, (float)p.z, (float)p.z), options), p),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static Point PointToVector3(Vector3 vector, Point p)
|
||||
{
|
||||
p.x = vector.x;
|
||||
p.y = vector.y;
|
||||
p.z = vector.z;
|
||||
return p;
|
||||
}
|
||||
|
||||
private IList ArrayField(string propName, IList list, params GUILayoutOption[] options)
|
||||
{
|
||||
bool isExpanded = EditorGUILayout.Foldout(ArrayFoldoutState.Contains(propName), propName);
|
||||
if (isExpanded)
|
||||
{
|
||||
ArrayFoldoutState.Add(propName);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
object? item = list[i];
|
||||
var r = CreateFieldPrimitive(item, i.ToString(), options);
|
||||
|
||||
if (r == null)
|
||||
{
|
||||
EditorGUILayout.TextField(propName, item == null? "NULL" : item.ToString());
|
||||
continue;
|
||||
}
|
||||
//Update list item
|
||||
list[i] = r;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayFoldoutState.Remove(propName);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7001b203c4e1e644baf38bfae0a4489f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Sentry;
|
||||
using Speckle.Core.Api;
|
||||
@@ -12,13 +10,12 @@ using Speckle.Core.Logging;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
namespace Speckle.ConnectorUnity.Editor
|
||||
{
|
||||
[CustomEditor(typeof(StreamManager))]
|
||||
[CanEditMultipleObjects]
|
||||
public class StreamManagerEditor : Editor
|
||||
public class StreamManagerEditor : UnityEditor.Editor
|
||||
{
|
||||
private bool _foldOutAccount;
|
||||
private int _totalChildrenCount = 0;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Given <paramref name="baseObject"/>,
|
||||
/// will recursively convert any objects in the tree
|
||||
/// </summary>
|
||||
/// <param name="baseObject">The Speckle object to convert + its children</param>
|
||||
/// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param>
|
||||
/// <returns> A list of all created <see cref="GameObject"/>s</returns>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(Base baseObject, Transform? parent)
|
||||
=> RecursivelyConvertToNative(baseObject, parent, o => ConverterInstance.CanConvertToNative(o));
|
||||
|
||||
/// <inheritdoc cref="RecursivelyConvertToNative(Base, Transform)"/>
|
||||
/// <param name="predicate">A function to determine if an object should be converted</param>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate)
|
||||
{
|
||||
LoadMaterialOverrides();
|
||||
|
||||
var createdGameObjects = new List<GameObject>();
|
||||
RecurseTreeToNative(baseObject, parent, predicate, createdGameObjects);
|
||||
//TODO track event
|
||||
|
||||
return createdGameObjects;
|
||||
|
||||
}
|
||||
|
||||
protected string[] namePropertyAliases = {"name", "Name"};
|
||||
|
||||
protected virtual string GenerateObjectName(Base baseObject)
|
||||
{
|
||||
// 1. Use explicit name
|
||||
foreach (var nameAlias in namePropertyAliases)
|
||||
{
|
||||
string? s = baseObject[nameAlias] as string;
|
||||
if (!string.IsNullOrWhiteSpace(s)) return s!; //TODO any sanitization needed?
|
||||
}
|
||||
|
||||
// 2. Use type + id as fallback name
|
||||
// Only take the most derived type from the speckle type
|
||||
string speckleType = baseObject.speckle_type.Split(':').Last();
|
||||
return $"{speckleType} - {baseObject.id}";
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
object? converted = null;
|
||||
if(predicate(baseObject))
|
||||
converted = ConverterInstance.ConvertToNative(baseObject);
|
||||
|
||||
// Handle new GameObjects
|
||||
Transform? nextParent = parent;
|
||||
if (converted is GameObject go)
|
||||
{
|
||||
outCreatedObjects.Add(go);
|
||||
|
||||
nextParent = go.transform;
|
||||
|
||||
go.name = GenerateObjectName(baseObject);
|
||||
go.transform.SetParent(parent);
|
||||
//TODO add support for unity specific props
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
//if (baseObject["layer"] is int layer) go.layer = layer;
|
||||
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
|
||||
}
|
||||
|
||||
// For geometry, only traverse `elements` prop, otherwise, try and convert everything
|
||||
IEnumerable<string> potentialChildren;
|
||||
if (ConverterInstance.CanConvertToNative(baseObject)) potentialChildren = new []{"elements"};
|
||||
else potentialChildren = baseObject.GetMemberNames();
|
||||
|
||||
// Convert Children
|
||||
foreach (string propertyName in potentialChildren)
|
||||
{
|
||||
ConvertChild(baseObject[propertyName], nextParent, predicate, outCreatedObjects);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected virtual void ConvertChild(object? value, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
if(value == null) return;
|
||||
if(value.GetType().IsPrimitive) return;
|
||||
if (value is string) return;
|
||||
|
||||
if(value is Base o)
|
||||
{
|
||||
RecurseTreeToNative(o, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
else if (value is IDictionary dictionary)
|
||||
{
|
||||
foreach (object v in dictionary.Keys)
|
||||
{
|
||||
ConvertChild(v, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else if (value is IList collection)
|
||||
{
|
||||
foreach (object v in collection)
|
||||
{
|
||||
ConvertChild(v, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Unknown type {value.GetType()} found when traversing tree, will be safely ignored");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void LoadMaterialOverrides()
|
||||
{
|
||||
//using the ApplicationPlaceholderObject to pass materials
|
||||
//available in Assets/Materials to the converters
|
||||
var materials = Resources.LoadAll("", typeof(Material)).Cast<Material>().ToArray();
|
||||
if (materials.Length == 0) Debug.Log("To automatically assign materials to received meshes, materials have to be in the \'Assets/Resources\' folder!");
|
||||
var placeholderObjects = materials.Select(x => new ApplicationPlaceholderObject { NativeObject = x }).ToList();
|
||||
ConverterInstance.SetContextObjects(placeholderObjects);
|
||||
}
|
||||
|
||||
|
||||
[Obsolete("Use RecursivelyConvertToNative instead")]
|
||||
public GameObject ConvertRecursivelyToNative(Base @base, string name)
|
||||
{
|
||||
var parentObject = new GameObject(name);
|
||||
RecursivelyConvertToNative(@base, parentObject.transform);
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bfd6c40a9c8412f9563a84917d04f7e
|
||||
timeCreated: 1658168285
|
||||
@@ -0,0 +1,76 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Given a collection of <paramref name="rootObjects"/>,
|
||||
/// will recursively convert any <see cref="GameObject"/>s in the tree
|
||||
/// where a given <paramref name="predicate"/> function holds true.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Convert all objects in a scene that have a given tag
|
||||
/// <code>
|
||||
/// Base b = RecursivelyConvertToSpeckle(SceneManager.GetActiveScene().GetRootGameObjects(), o => o.CompareTag("myTag"));
|
||||
/// </code>
|
||||
/// Convert a selection of objects that share a common rootObject(s)
|
||||
/// <code>
|
||||
/// GameObject parent = ...
|
||||
/// ISet selection = ...
|
||||
/// Base b = RecursivelyConvertToSpeckle(parent, o => selection.contains(o));
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="rootObjects">Root objects of a tree</param>
|
||||
/// <param name="predicate">A function to determine if an object should be converted</param>
|
||||
/// <returns>A simple <see cref="Base"/> wrapping converted objects</returns>
|
||||
public virtual Base RecursivelyConvertToSpeckle(IEnumerable<GameObject> rootObjects, Func<GameObject, bool> predicate)
|
||||
{
|
||||
List<Base> convertedRootObjects = new List<Base>();
|
||||
foreach (GameObject rootObject in rootObjects)
|
||||
{
|
||||
RecurseTreeToSpeckle(rootObject, predicate, convertedRootObjects);
|
||||
}
|
||||
|
||||
return new Base()
|
||||
{
|
||||
["objects"] = convertedRootObjects,
|
||||
};
|
||||
}
|
||||
|
||||
public virtual Base RecursivelyConvertToSpeckle(GameObject rootObject, Func<GameObject, bool> predicate)
|
||||
{
|
||||
return RecursivelyConvertToSpeckle(new[] {rootObject}, predicate);
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToSpeckle(GameObject rootObject, Func<GameObject, bool> predicate, List<Base> outConverted)
|
||||
{
|
||||
// Convert children first
|
||||
var convertedChildren = new List<Base>(rootObject.transform.childCount);
|
||||
foreach(Transform child in rootObject.transform)
|
||||
{
|
||||
RecurseTreeToSpeckle(child.gameObject, predicate, convertedChildren);
|
||||
}
|
||||
|
||||
if (ConverterInstance.CanConvertToSpeckle(rootObject) && predicate(rootObject))
|
||||
{
|
||||
// Convert and output
|
||||
Base converted = ConverterInstance.ConvertToSpeckle(rootObject);
|
||||
converted["elements"] = convertedChildren;
|
||||
outConverted.Add(converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip this object, and output any children
|
||||
outConverted.AddRange(convertedChildren);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48684809ac9d4c79ae63fd79f207b49d
|
||||
timeCreated: 1658168296
|
||||
@@ -1,11 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Objects.Converter.Unity;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
@@ -15,126 +10,9 @@ namespace Speckle.ConnectorUnity
|
||||
/// </summary>
|
||||
[AddComponentMenu("Speckle/Conversion/" + nameof(RecursiveConverter))]
|
||||
[ExecuteAlways, DisallowMultipleComponent]
|
||||
public class RecursiveConverter : MonoBehaviour
|
||||
public partial class RecursiveConverter : MonoBehaviour
|
||||
{
|
||||
public virtual ISpeckleConverter ConverterInstance { get; set; } = new ConverterUnity();
|
||||
|
||||
#region ToNative
|
||||
|
||||
/// <summary>
|
||||
/// Given <paramref name="baseObject"/>,
|
||||
/// will recursively convert any objects in the tree
|
||||
/// </summary>
|
||||
/// <param name="baseObject">The Speckle object to convert + its children</param>
|
||||
/// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param>
|
||||
/// <returns> A list of all created <see cref="GameObject"/>s</returns>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(Base baseObject, Transform? parent)
|
||||
=> RecursivelyConvertToNative(baseObject, parent, o => ConverterInstance.CanConvertToNative(o));
|
||||
|
||||
/// <inheritdoc cref="RecursivelyConvertToNative(Base, Transform)"/>
|
||||
/// <param name="predicate">A function to determine if an object should be converted</param>
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate)
|
||||
{
|
||||
LoadMaterialOverrides();
|
||||
|
||||
var createdGameObjects = new List<GameObject>();
|
||||
RecurseTreeToNative(baseObject, parent, predicate, createdGameObjects);
|
||||
//TODO track event
|
||||
|
||||
return createdGameObjects;
|
||||
|
||||
}
|
||||
|
||||
protected string[] namePropertyAliases = {"name", "Name"};
|
||||
|
||||
protected virtual string GenerateObjectName(Base baseObject)
|
||||
{
|
||||
foreach (var nameAlias in namePropertyAliases)
|
||||
{
|
||||
string? s = baseObject[nameAlias] as string;
|
||||
if (!string.IsNullOrWhiteSpace(s)) return s!; //TODO any sanitization needed?
|
||||
}
|
||||
|
||||
return $"{baseObject.speckle_type} - {baseObject.id}";
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
object? converted = null;
|
||||
if(predicate(baseObject))
|
||||
converted = ConverterInstance.ConvertToNative(baseObject);
|
||||
|
||||
// Handle new GameObjects
|
||||
Transform? nextParent = parent;
|
||||
if (converted is GameObject go)
|
||||
{
|
||||
outCreatedObjects.Add(go);
|
||||
|
||||
nextParent = go.transform;
|
||||
|
||||
go.name = GenerateObjectName(baseObject);
|
||||
go.transform.SetParent(parent);
|
||||
//TODO add support for unity specific props
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
//if (baseObject["layer"] is int layer) go.layer = layer;
|
||||
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
|
||||
}
|
||||
|
||||
ConvertChildren(baseObject, nextParent, predicate, outCreatedObjects);
|
||||
}
|
||||
|
||||
protected virtual void ConvertChildren(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
{
|
||||
// Find child objects to convert,
|
||||
IEnumerable<string> potentialChildren = baseObject.GetDynamicMembers();
|
||||
if (!ConverterInstance.CanConvertToNative(baseObject))
|
||||
{
|
||||
potentialChildren = potentialChildren.Concat(baseObject.GetInstanceMembersNames());
|
||||
}
|
||||
|
||||
foreach (string? c in potentialChildren)
|
||||
{
|
||||
object? value = baseObject[c];
|
||||
|
||||
//Ignore everything but objects inheriting Base
|
||||
if(value == null) continue;
|
||||
if(value.GetType().IsSimpleType()) continue;
|
||||
|
||||
if(value is Base o)
|
||||
{
|
||||
RecurseTreeToNative(o, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
else if (value is IDictionary dictionary)
|
||||
{
|
||||
foreach (object obj in dictionary.Keys)
|
||||
{
|
||||
if(obj is Base b) RecurseTreeToNative(b, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else if (value is IList collection)
|
||||
{
|
||||
foreach (object obj in collection)
|
||||
{
|
||||
if(obj is Base b) RecurseTreeToNative(b, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(value.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected virtual void LoadMaterialOverrides()
|
||||
{
|
||||
//using the ApplicationPlaceholderObject to pass materials
|
||||
//available in Assets/Materials to the converters
|
||||
var materials = Resources.LoadAll("", typeof(Material)).Cast<Material>().ToArray();
|
||||
if (materials.Length == 0) Debug.Log("To automatically assign materials to received meshes, materials have to be in the \'Assets/Resources\' folder!");
|
||||
var placeholderObjects = materials.Select(x => new ApplicationPlaceholderObject { NativeObject = x }).ToList();
|
||||
ConverterInstance.SetContextObjects(placeholderObjects);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
{
|
||||
"name": "Speckle.Connector",
|
||||
"references":[ "GUID:24f666972ea7e9149abddaae766b9c1d" ]
|
||||
}
|
||||
"name": "Speckle.Connector",
|
||||
"rootNamespace": "Speckle.ConnectorUnity",
|
||||
"references": [
|
||||
"GUID:24f666972ea7e9149abddaae766b9c1d"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
@@ -20,7 +21,7 @@ namespace Speckle.ConnectorUnity
|
||||
|
||||
private bool _hasChanged;
|
||||
private ObservableConcurrentDictionary<string, object> _data;
|
||||
|
||||
|
||||
public IDictionary<string, object> Data
|
||||
{
|
||||
get => _data;
|
||||
@@ -35,11 +36,30 @@ namespace Speckle.ConnectorUnity
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private string _serializedSpeckleType;
|
||||
private Type _speckleType = typeof(Base);
|
||||
public Type SpeckleType {
|
||||
get
|
||||
{
|
||||
return _speckleType ??= typeof(Base);
|
||||
}
|
||||
set
|
||||
{
|
||||
Debug.Assert(typeof(Base).IsAssignableFrom(value));
|
||||
Debug.Assert(!value.IsAbstract);
|
||||
_speckleType = value;
|
||||
_hasChanged = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SpeckleProperties()
|
||||
{
|
||||
_data = new ObservableConcurrentDictionary<string, object>();
|
||||
_data.CollectionChanged += CollectionChangeHandler;
|
||||
_hasChanged = true;
|
||||
SpeckleType = typeof(Base);
|
||||
}
|
||||
|
||||
private void CollectionChangeHandler(object sender, NotifyCollectionChangedEventArgs e)
|
||||
@@ -53,6 +73,7 @@ namespace Speckle.ConnectorUnity
|
||||
|
||||
_serializedData = Operations.Serialize(new SpeckleData(Data));
|
||||
_hasChanged = false;
|
||||
_serializedSpeckleType = SpeckleType.AssemblyQualifiedName;
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
@@ -60,6 +81,16 @@ namespace Speckle.ConnectorUnity
|
||||
Base speckleData = Operations.Deserialize(_serializedData);
|
||||
Data = speckleData.GetMembers();
|
||||
_hasChanged = false;
|
||||
|
||||
try
|
||||
{
|
||||
SpeckleType = Type.GetType(_serializedSpeckleType);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e, this);
|
||||
_speckleType = typeof(Base);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Objects.Other;
|
||||
using Objects.Utils;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -12,7 +14,6 @@ namespace Speckle.ConnectorUnity
|
||||
[AddComponentMenu("Speckle/Stream Manager")]
|
||||
public class StreamManager : MonoBehaviour
|
||||
{
|
||||
|
||||
public int SelectedAccountIndex = -1;
|
||||
public int SelectedStreamIndex = -1;
|
||||
public int SelectedBranchIndex = -1;
|
||||
@@ -27,25 +28,25 @@ namespace Speckle.ConnectorUnity
|
||||
public List<Account> Accounts;
|
||||
public List<Stream> Streams;
|
||||
public List<Branch> Branches;
|
||||
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static bool GenerateMaterials = false;
|
||||
#endif
|
||||
|
||||
public List<GameObject> ConvertRecursivelyToNative(Base @base, string id)
|
||||
public List<GameObject> ConvertRecursivelyToNative(Base @base, string name)
|
||||
{
|
||||
|
||||
var rc = GetComponent<RecursiveConverter>();
|
||||
if (rc == null)
|
||||
rc = gameObject.AddComponent<RecursiveConverter>();
|
||||
|
||||
var rootObject = new GameObject()
|
||||
{
|
||||
name = id,
|
||||
};
|
||||
var rootObject = new GameObject(name);
|
||||
|
||||
return rc.RecursivelyConvertToNative(@base, rootObject.transform);
|
||||
Func<Base, bool> predicate = o =>
|
||||
rc.ConverterInstance.CanConvertToNative(o) //Accept geometry
|
||||
|| o.speckle_type == "Base" && o.totalChildrenCount > 0; // Or Base objects that have children
|
||||
|
||||
|
||||
return rc.RecursivelyConvertToNative(@base, rootObject.transform, predicate);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "systems.speckle.speckle-unity",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.2",
|
||||
"displayName": "Speckle Unity Connector",
|
||||
"description": "AEC Interoperability for Unity through Speckle",
|
||||
"unity": "2018.4",
|
||||
|
||||
Reference in New Issue
Block a user