Updated speckle properties to support selection of Base type

This commit is contained in:
JR-Morgan
2022-07-18 20:13:59 +01:00
parent dca1908a1e
commit 32db50d03b
22 changed files with 100484 additions and 688 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f7f8762fcd0c1174782d2ba683cb6b74
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+88
View File
@@ -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...
// *
// */
//
//
// }
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bb07d1449c8af34c8531fc28e235d33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because one or more lines are too long
+68
View File
@@ -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");
});
});
}
}
+11
View File
@@ -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",