diff --git a/.gitignore b/.gitignore index d1f9514..4e8c12b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ /[Ll]ogs/ /[Uu]ser[Ss]ettings/ +#Test space content +/[Tt]estSpace/ + # MemoryCaptures can get excessive in size. # They also could contain extremely sensitive data /[Mm]emoryCaptures/ diff --git a/Assets/Speckle Connector/ConverterUnity.Geometry.cs b/Assets/Speckle Connector/ConverterUnity.Geometry.cs index f879ecb..b22b5ed 100644 --- a/Assets/Speckle Connector/ConverterUnity.Geometry.cs +++ b/Assets/Speckle Connector/ConverterUnity.Geometry.cs @@ -6,385 +6,374 @@ using System.Reflection; using Objects.Other; using Speckle.ConnectorUnity; using Speckle.Core.Models; +using UnityEditor; using UnityEngine; using Mesh = Objects.Geometry.Mesh; +using Object = UnityEngine.Object; -namespace Objects.Converter.Unity -{ - public partial class ConverterUnity - { +namespace Objects.Converter.Unity { + public partial class ConverterUnity { #region helper methods + /// + /// + /// + /// + /// + /// + /// + public Vector3 VectorByCoordinates( double x, double y, double z, string units ) + { + // switch y and z + return new Vector3( (float) ScaleToNative( x, units ), (float) ScaleToNative( z, units ), + (float) ScaleToNative( y, units ) ); + } - /// - /// - /// - /// - /// - /// - /// - public Vector3 VectorByCoordinates(double x, double y, double z, string units) - { - // switch y and z - return new Vector3((float) ScaleToNative(x, units), (float) ScaleToNative(z, units), - (float) ScaleToNative(y, units)); - } + public Vector3 VectorFromPoint( Point p ) + { + // switch y and z + return new Vector3( (float) ScaleToNative( p.x, p.units ), (float) ScaleToNative( p.z, p.units ), + (float) ScaleToNative( p.y, p.units ) ); + } - public Vector3 VectorFromPoint(Point p) - { - // switch y and z - return new Vector3((float) ScaleToNative(p.x, p.units), (float) ScaleToNative(p.z, p.units), - (float) ScaleToNative(p.y, p.units)); - } + /// + /// + /// + /// + /// + // public Vector3 ArrayToPoint(double[] ptValues, string units) + // { + // double x = ptValues[0]; + // double y = ptValues[1]; + // double z = ptValues[2]; + // + // return PointByCoordinates(x, y, z, units); + // } - /// - /// - /// - /// - /// - // public Vector3 ArrayToPoint(double[] ptValues, string units) - // { - // double x = ptValues[0]; - // double y = ptValues[1]; - // double z = ptValues[2]; - // - // return PointByCoordinates(x, y, z, units); - // } + /// + /// + /// + /// + /// + public Vector3[ ] ArrayToPoints( IEnumerable arr, string units ) + { + if ( arr.Count( ) % 3 != 0 ) throw new Exception( "Array malformed: length%3 != 0." ); - /// - /// - /// - /// - /// - public Vector3[] ArrayToPoints(IEnumerable arr, string units) - { - if (arr.Count() % 3 != 0) throw new Exception("Array malformed: length%3 != 0."); - - Vector3[] points = new Vector3[arr.Count() / 3]; - var asArray = arr.ToArray(); - for (int i = 2, k = 0; i < arr.Count(); i += 3) - points[k++] = VectorByCoordinates(asArray[i - 2], asArray[i - 1], asArray[i], units); - - return points; - } + Vector3[ ] points = new Vector3[ arr.Count( ) / 3 ]; + var asArray = arr.ToArray( ); + for ( int i = 2, k = 0; i < arr.Count( ); i += 3 ) + points[ k++ ] = VectorByCoordinates( asArray[ i - 2 ], asArray[ i - 1 ], asArray[ i ], units ); + return points; + } #endregion #region ToSpeckle + //TODO: more of these - //TODO: more of these - - /// - /// - /// - /// - /// - public Point PointToSpeckle(Vector3 p) - { - //switch y and z - return new Point(p.x, p.z, p.y); - } + /// + /// + /// + /// + /// + public Point PointToSpeckle( Vector3 p ) + { + //switch y and z + return new Point( p.x, p.z, p.y ); + } - /// - /// Converts a Speckle mesh to a GameObject with a mesh renderer - /// - /// - /// - public Mesh MeshToSpeckle(GameObject go) - { - //TODO: support multiple filters? - var filter = go.GetComponent(); - if (filter == null) - { - return null; - } + /// + /// Converts a Speckle mesh to a GameObject with a mesh renderer + /// + /// + /// + public Mesh MeshToSpeckle( GameObject go ) + { + //TODO: support multiple filters? + var filter = go.GetComponent( ); + if ( filter == null ) { + return null; + } - //convert triangle array into speckleMesh faces - List faces = new List(); - int i = 0; - //store them here, makes it like 1000000x faster? - var triangles = filter.mesh.triangles; - while (i < triangles.Length) - { - faces.Add(0); + //convert triangle array into speckleMesh faces + List faces = new List( ); + int i = 0; + //store them here, makes it like 1000000x faster? + var triangles = filter.mesh.triangles; + while (i < triangles.Length) { + faces.Add( 0 ); - faces.Add(triangles[i + 0]); - faces.Add(triangles[i + 2]); - faces.Add(triangles[i + 1]); - i += 3; - } + faces.Add( triangles[ i + 0 ] ); + faces.Add( triangles[ i + 2 ] ); + faces.Add( triangles[ i + 1 ] ); + i += 3; + } - var mesh = new Mesh(); - // get the speckle data from the go here - // so that if the go comes from speckle, typed props will get overridden below - AttachUnityProperties(mesh, go); + var mesh = new Mesh( ); + // get the speckle data from the go here + // so that if the go comes from speckle, typed props will get overridden below + AttachUnityProperties( mesh, go ); - mesh.units = ModelUnits; + mesh.units = ModelUnits; - var vertices = filter.mesh.vertices; - foreach (var vertex in vertices) - { - var p = go.transform.TransformPoint(vertex); - var sp = PointToSpeckle(p); - mesh.vertices.Add(sp.x); - mesh.vertices.Add(sp.y); - mesh.vertices.Add(sp.z); - } + var vertices = filter.mesh.vertices; + foreach ( var vertex in vertices ) { + var p = go.transform.TransformPoint( vertex ); + var sp = PointToSpeckle( p ); + mesh.vertices.Add( sp.x ); + mesh.vertices.Add( sp.y ); + mesh.vertices.Add( sp.z ); + } - mesh.faces = faces; - - return mesh; - } + mesh.faces = faces; + return mesh; + } #endregion #region ToNative + private GameObject NewPointBasedGameObject( Vector3[ ] points, string name ) + { + if ( points.Length == 0 ) return null; - private GameObject NewPointBasedGameObject(Vector3[] points, string name) - { - if (points.Length == 0) return null; + float pointDiameter = 1; //TODO: figure out how best to change this? - float pointDiameter = 1; //TODO: figure out how best to change this? + var go = new GameObject( ); + go.name = name; - var go = new GameObject(); - go.name = name; + var lineRenderer = go.AddComponent( ); - var lineRenderer = go.AddComponent(); + lineRenderer.positionCount = points.Length; + lineRenderer.SetPositions( points ); + lineRenderer.numCornerVertices = lineRenderer.numCapVertices = 8; + lineRenderer.startWidth = lineRenderer.endWidth = pointDiameter; - lineRenderer.positionCount = points.Length; - lineRenderer.SetPositions(points); - lineRenderer.numCornerVertices = lineRenderer.numCapVertices = 8; - lineRenderer.startWidth = lineRenderer.endWidth = pointDiameter; + return go; + } - return go; - } + /// + /// Converts a Speckle point to a GameObject with a line renderer + /// + /// + /// + public GameObject PointToNative( Point point ) + { + Vector3 newPt = VectorByCoordinates( point.x, point.y, point.z, point.units ); - /// - /// Converts a Speckle point to a GameObject with a line renderer - /// - /// - /// - public GameObject PointToNative(Point point) - { - Vector3 newPt = VectorByCoordinates(point.x, point.y, point.z, point.units); - - var go = NewPointBasedGameObject(new Vector3[2] {newPt, newPt}, point.speckle_type); - return go; - } + var go = NewPointBasedGameObject( new Vector3[ 2 ] {newPt, newPt}, point.speckle_type ); + return go; + } - /// - /// Converts a Speckle line to a GameObject with a line renderer - /// - /// - /// - public GameObject LineToNative(Line line) - { - var points = new List {VectorFromPoint(line.start), VectorFromPoint(line.end)}; + /// + /// Converts a Speckle line to a GameObject with a line renderer + /// + /// + /// + public GameObject LineToNative( Line line ) + { + var points = new List {VectorFromPoint( line.start ), VectorFromPoint( line.end )}; - var go = NewPointBasedGameObject(points.ToArray(), line.speckle_type); - return go; - } + var go = NewPointBasedGameObject( points.ToArray( ), line.speckle_type ); + return go; + } - /// - /// Converts a Speckle polyline to a GameObject with a line renderer - /// - /// - /// - public GameObject PolylineToNative(Polyline polyline) - { - var points = polyline.points.Select(x => VectorFromPoint(x)); + /// + /// Converts a Speckle polyline to a GameObject with a line renderer + /// + /// + /// + public GameObject PolylineToNative( Polyline polyline ) + { + var points = polyline.points.Select( x => VectorFromPoint( x ) ); - var go = NewPointBasedGameObject(points.ToArray(), polyline.speckle_type); - return go; - } + var go = NewPointBasedGameObject( points.ToArray( ), polyline.speckle_type ); + return go; + } - /// - /// Converts a Speckle curve to a GameObject with a line renderer - /// - /// - /// - public GameObject CurveToNative(Curve curve) - { - var points = ArrayToPoints(curve.points, curve.units); - var go = NewPointBasedGameObject(points.ToArray(), curve.speckle_type); - return go; - } + /// + /// Converts a Speckle curve to a GameObject with a line renderer + /// + /// + /// + public GameObject CurveToNative( Curve curve ) + { + var points = ArrayToPoints( curve.points, curve.units ); + var go = NewPointBasedGameObject( points.ToArray( ), curve.speckle_type ); + return go; + } - public GameObject MeshToNative(Base speckleMeshObject) - { - if (!(speckleMeshObject["displayMesh"] is Mesh)) - return null; + public GameObject MeshToNative( Base speckleMeshObject ) + { + if ( !( speckleMeshObject[ "displayMesh" ] is Mesh ) ) + return null; - return MeshToNative(speckleMeshObject["displayMesh"] as Mesh, - speckleMeshObject["renderMaterial"] as RenderMaterial, speckleMeshObject.GetMembers()); - } + return MeshToNative( speckleMeshObject[ "displayMesh" ] as Mesh, + speckleMeshObject[ "renderMaterial" ] as RenderMaterial, speckleMeshObject.GetMembers( ) ); + } - /// - /// Converts a Speckle mesh to a GameObject with a mesh renderer - /// - /// Mesh to convert - /// If provided will override the renderMaterial on the mesh itself - /// If provided will override the properties on the mesh itself - /// - public GameObject MeshToNative(Mesh speckleMesh, RenderMaterial renderMaterial = null, - Dictionary properties = null) - { - if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0) - { - return null; - } + /// + /// Converts a Speckle mesh to a GameObject with a mesh renderer + /// + /// Mesh to convert + /// If provided will override the renderMaterial on the mesh itself + /// If provided will override the properties on the mesh itself + /// + public GameObject MeshToNative( + Mesh speckleMesh, RenderMaterial renderMaterial = null, + Dictionary properties = null + ) + { + if ( speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0 ) { + return null; + } - var recentreMeshTransforms = true; //TODO: figure out how best to change this? - - var verts = ArrayToPoints(speckleMesh.vertices, speckleMesh.units).ToList(); - //convert speckleMesh.faces into triangle array - List tris = new List(); - int i = 0; - while (i < speckleMesh.faces.Count) - { - if (speckleMesh.faces[i] == 0) - { - //Triangles - tris.Add(speckleMesh.faces[i + 1]); - tris.Add(speckleMesh.faces[i + 3]); - tris.Add(speckleMesh.faces[i + 2]); - i += 4; - } - else - { - //Quads to triangles - tris.Add(speckleMesh.faces[i + 1]); - tris.Add(speckleMesh.faces[i + 3]); - tris.Add(speckleMesh.faces[i + 2]); - - tris.Add(speckleMesh.faces[i + 1]); - tris.Add(speckleMesh.faces[i + 4]); - tris.Add(speckleMesh.faces[i + 3]); - - i += 5; - } - } + var recenterMeshTransforms = true; //TODO: figure out how best to change this? - var go = new GameObject(); - go.name = speckleMesh.speckle_type; + var verts = ArrayToPoints( speckleMesh.vertices, speckleMesh.units ); - var mesh = go.AddComponent().mesh; - var meshRenderer = go.AddComponent(); + //convert speckleMesh.faces into triangle array + List tris = new List( ); + int i = 0; + while (i < speckleMesh.faces.Count) { + if ( speckleMesh.faces[ i ] == 0 ) { + //Triangles + tris.Add( speckleMesh.faces[ i + 1 ] ); + tris.Add( speckleMesh.faces[ i + 3 ] ); + tris.Add( speckleMesh.faces[ i + 2 ] ); + i += 4; + } else { + //Quads to triangles + tris.Add( speckleMesh.faces[ i + 1 ] ); + tris.Add( speckleMesh.faces[ i + 3 ] ); + tris.Add( speckleMesh.faces[ i + 2 ] ); - var speckleMaterial = renderMaterial ?? (RenderMaterial)speckleMesh["renderMaterial"]; - meshRenderer.material = GetMaterial(speckleMaterial); + tris.Add( speckleMesh.faces[ i + 1 ] ); + tris.Add( speckleMesh.faces[ i + 4 ] ); + tris.Add( speckleMesh.faces[ i + 3 ] ); + + i += 5; + } + } - if (verts.Count >= 65535) - mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + var go = new GameObject {name = speckleMesh.speckle_type}; + var mesh = new UnityEngine.Mesh {name = speckleMesh.speckle_type}; - // center transform pivot according to the bounds of the model - if (recentreMeshTransforms) - { - Bounds meshBounds = new Bounds(); - meshBounds.center = verts[0]; - - verts.ForEach(x => meshBounds.Encapsulate(x)); - - go.transform.position = meshBounds.center; - - // offset mesh vertices - for (int l = 0; l < verts.Count; l++) - { - verts[l] -= meshBounds.center; - } - } - - // assign mesh properties - mesh.vertices = verts.ToArray(); - mesh.triangles = tris.ToArray(); + if ( verts.Length >= 65535 ) + mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; - mesh.Optimize(); - mesh.RecalculateNormals(); - mesh.RecalculateTangents(); + // center transform pivot according to the bounds of the model + if ( recenterMeshTransforms ) { + Bounds meshBounds = new Bounds { + center = verts[ 0 ] + }; + + foreach ( var vert in verts ) { + meshBounds.Encapsulate( vert ); + } + // verts.ForEach( x => meshBounds.Encapsulate( x ) ); + + go.transform.position = meshBounds.center; + + // offset mesh vertices + for ( int l = 0; l < verts.Length; l++ ) { + verts[ l ] -= meshBounds.center; + } + } + + mesh.SetVertices( verts ); + mesh.SetTriangles( tris, 0 ); + + mesh.RecalculateNormals( ); + mesh.RecalculateBounds( ); + mesh.Optimize( ); - //generate uvs doesn't work as intended. Leaving out for now - //GenerateUVs (ref mesh); + //generate uvs doesn't work as intended. Leaving out for now + //GenerateUVs (ref mesh); - //Add mesh collider - MeshCollider mc = go.AddComponent(); - mc.sharedMesh = mesh; - //mc.convex = true; + // Setting mesh to filter once all mesh modifying is done + go.SafeMeshSet( mesh, true ); + // go.AddComponent( ).mesh = mesh; - //attach properties on this very mesh - //means the mesh originated in Rhino or similar - if (properties == null) - { - var meshprops = typeof(Mesh).GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(x => x.Name) - .ToList(); - properties = speckleMesh.GetMembers() - .Where(x => !meshprops.Contains(x.Key)) - .ToDictionary(x => x.Key, x => x.Value); - } + var meshRenderer = go.AddComponent( ); + var speckleMaterial = renderMaterial ?? (RenderMaterial) speckleMesh[ "renderMaterial" ]; + meshRenderer.sharedMaterial = GetMaterial( speckleMaterial ); - AttachSpeckleProperties(go, properties); - return go; - } + //Add mesh collider + // MeshCollider mc = go.AddComponent( ); + // mc.sharedMesh = mesh; + //mc.convex = true; + + //attach properties on this very mesh + //means the mesh originated in Rhino or similar + if ( properties == null ) { + var meshprops = typeof( Mesh ).GetProperties( BindingFlags.Instance | BindingFlags.Public ).Select( x => x.Name ) + .ToList( ); + properties = speckleMesh.GetMembers( ) + .Where( x => !meshprops.Contains( x.Key ) ) + .ToDictionary( x => x.Key, x => x.Value ); + } + + AttachSpeckleProperties( go, properties ); + return go; + } #endregion - private Material GetMaterial(RenderMaterial renderMaterial) - { - //todo support more complex materials - var shader = Shader.Find("Standard"); - Material mat = new Material(shader); + private Material GetMaterial( RenderMaterial renderMaterial ) + { + //todo support more complex materials + var shader = Shader.Find( "Standard" ); + Material mat = new Material( shader ); - //if a renderMaterial is passed use that, otherwise try get it from the mesh itself + //if a renderMaterial is passed use that, otherwise try get it from the mesh itself - if (renderMaterial != null) - { - // 1. match material by name, if any - var matByName = ContextObjects.FirstOrDefault(x => ((Material)x.NativeObject).name == renderMaterial.name); - if (matByName!=null) - { - return matByName.NativeObject as Material; - } - - // 2. re-create material by setting diffuse color and transparency on standard shaders - if (renderMaterial.opacity < 1) - { - shader = Shader.Find("Transparent/Diffuse"); - mat = new Material(shader); - } + if ( renderMaterial != null ) { + // 1. match material by name, if any + var matByName = ContextObjects.FirstOrDefault( x => ( (Material) x.NativeObject ).name == renderMaterial.name ); + if ( matByName != null ) { + return matByName.NativeObject as Material; + } - var c = renderMaterial.diffuse.ToUnityColor(); - mat.color = new Color(c.r, c.g, c.b, Convert.ToSingle(renderMaterial.opacity)); + // 2. re-create material by setting diffuse color and transparency on standard shaders + if ( renderMaterial.opacity < 1 ) { + shader = Shader.Find( "Transparent/Diffuse" ); + mat = new Material( shader ); + } - return mat; - } + var c = renderMaterial.diffuse.ToUnityColor( ); + mat.color = new Color( c.r, c.g, c.b, Convert.ToSingle( renderMaterial.opacity ) ); - // 3. if not renderMaterial was passed, the default shader will be used - return mat; + return mat; + } + + // 3. if not renderMaterial was passed, the default shader will be used + return mat; + } + + private void AttachSpeckleProperties( GameObject go, Dictionary properties ) + { + var sd = go.AddComponent( ); + sd.Data = properties; + } + + + private void AttachUnityProperties( Base @base, GameObject go ) + { + var sd = go.GetComponent( ); + if ( sd == null || sd.Data == null ) + return; + + foreach ( var key in sd.Data.Keys ) { + @base[ key ] = sd.Data[ key ]; + } + } } - - private void AttachSpeckleProperties(GameObject go, Dictionary properties) - { - var sd = go.AddComponent(); - sd.Data = properties; - } - - - private void AttachUnityProperties(Base @base, GameObject go) - { - var sd = go.GetComponent(); - if (sd == null || sd.Data == null) - return; - foreach (var key in sd.Data.Keys) - { - @base[key] = sd.Data[key]; - } - } - } } \ No newline at end of file diff --git a/Assets/Speckle Connector/ConverterUnity.cs b/Assets/Speckle Connector/ConverterUnity.cs index 6b8d52c..660b15c 100644 --- a/Assets/Speckle Connector/ConverterUnity.cs +++ b/Assets/Speckle Connector/ConverterUnity.cs @@ -27,6 +27,7 @@ namespace Objects.Converter.Unity public List ContextObjects { get; set; } = new List(); + public void SetContextDocument(object doc) => throw new NotImplementedException(); public void SetContextObjects(List objects) => ContextObjects = objects; diff --git a/Assets/Speckle Connector/RecursiveConverter.cs b/Assets/Speckle Connector/RecursiveConverter.cs index 3ea1764..abf36c3 100644 --- a/Assets/Speckle Connector/RecursiveConverter.cs +++ b/Assets/Speckle Connector/RecursiveConverter.cs @@ -8,184 +8,158 @@ using Speckle.Core.Logging; using Speckle.Core.Models; using UnityEngine; -namespace Speckle.ConnectorUnity -{ - public class RecursiveConverter : MonoBehaviour - { +namespace Speckle.ConnectorUnity { + public class RecursiveConverter : MonoBehaviour { - private ConverterUnity _converter = new ConverterUnity(); + private ConverterUnity _converter = new ConverterUnity( ); - public RecursiveConverter() - { - - } - /// - /// Converts a Base object to a GameObject Recursively - /// - /// - /// - public GameObject ConvertRecursivelyToNative(Base @base, string name) - { - //using the ApplicationPlaceholderObject to pass materials - //available in Assets/Materials to the converters - var materials = Resources.LoadAll("Materials", typeof(Material)).Cast() - .Select(x => new ApplicationPlaceholderObject {NativeObject = x}).ToList(); - _converter.SetContextObjects(materials); - - - - // case 1: it's an item that has a direct conversion method, eg a point - if (_converter.CanConvertToNative(@base)) - { - var go = TryConvertItemToNative(@base); - return go; - } - - // case 2: it's a wrapper Base - // 2a: if there's only one member unpack it - // 2b: otherwise return dictionary of unpacked members - var members = @base.GetMemberNames().ToList(); - if (members.Count() == 1) - { - var go = RecurseTreeToNative(@base[members.First()]); - go.name = members.First(); - return go; - } - else - { - //empty game object with the commit id as name, used to contain all the rest - var go = new GameObject(); - go.name = name; - foreach (var member in members) - { - var goo = RecurseTreeToNative(@base[member]); - if (goo != null) - { - goo.name = member; - goo.transform.parent = go.transform; - } - } - - return go; - } - } - - - /// - /// Converts an object recursively to a list of GameObjects - /// - /// - /// - private GameObject RecurseTreeToNative(object @object) - { - if (IsList(@object)) - { - var list = ((IEnumerable) @object).Cast(); - var objects = list.Select(x => RecurseTreeToNative(x)).Where(x => x != null).ToList(); - if (objects.Any()) - { - var go = new GameObject(); - go.name = "List"; - objects.ForEach(x => x.transform.parent = go.transform); - return go; - } - } - else - { - return TryConvertItemToNative(@object); - } - - return null; - } - - private GameObject TryConvertItemToNative(object value) - { - if (value == null) - return null; - - //it's a simple type or not a Base - if (value.GetType().IsSimpleType() || !(value is Base)) - { - return null; - } - - var @base = (Base) value; - - //it's an unsupported Base, go through each of its property and try convert that - if (!_converter.CanConvertToNative(@base)) - { - var members = @base.GetMemberNames().ToList(); - - //empty game object with the commit id as name, used to contain all the rest - var go = new GameObject(); - go.name = @base.speckle_type; - var goos = new List(); - foreach (var member in members) - { - var goo = RecurseTreeToNative(@base[member]); - if (goo != null) - { - goo.name = member; - goo.transform.parent = go.transform; - goos.Add(goo); - } - } - - //if no children is valid, return null - if (!goos.Any()) - { - Destroy(go); - return null; - } - - return go; - } - else - { - try - { - var go = _converter.ConvertToNative(@base) as GameObject; - // Some revit elements have nested elements in a "elements" property - // for instance hosted families on a wall - if (go != null && @base["elements"] is List l && l.Any()) - { - var goo = RecurseTreeToNative(l); - if (goo != null) + /// + /// Converts a Base object to a GameObject Recursively + /// + /// + /// + public GameObject ConvertRecursivelyToNative( Base @base, string name ) { - goo.name = "elements"; - goo.transform.parent = go.transform; + //using the ApplicationPlaceholderObject to pass materials + //available in Assets/Materials to the converters + var materials = Resources.LoadAll( "Materials", typeof( Material ) ).Cast( ) + .Select( x => new ApplicationPlaceholderObject {NativeObject = x} ).ToList( ); + _converter.SetContextObjects( materials ); + + + // case 1: it's an item that has a direct conversion method, eg a point + if ( _converter.CanConvertToNative( @base ) ) { + var go = TryConvertItemToNative( @base ); + return go; + } + + // case 2: it's a wrapper Base + // 2a: if there's only one member unpack it + // 2b: otherwise return dictionary of unpacked members + var members = @base.GetMemberNames( ).ToList( ); + if ( members.Count( ) == 1 ) { + var go = RecurseTreeToNative( @base[ members.First( ) ] ); + go.name = members.First( ); + return go; + } else { + //empty game object with the commit id as name, used to contain all the rest + var go = new GameObject { + name = name.Valid( ) ? name : "Base" + }; + + foreach ( var member in members ) { + var goo = RecurseTreeToNative( @base[ member ] ); + if ( goo != null ) { + goo.name = member; + goo.transform.parent = go.transform; + } + } + + return go; + } } - } - return go; - } - catch (Exception e) - { - throw new SpeckleException(e.Message, e, true, SentryLevel.Error); - } - } - return null; + /// + /// Converts an object recursively to a list of GameObjects + /// + /// + /// + private GameObject RecurseTreeToNative( object @object ) + { + if ( IsList( @object ) ) { + var list = ( (IEnumerable) @object ).Cast( ); + var objects = list.Select( x => RecurseTreeToNative( x ) ).Where( x => x != null ).ToList( ); + if ( objects.Any( ) ) { + var go = new GameObject( ); + go.name = "List"; + objects.ForEach( x => x.transform.parent = go.transform ); + return go; + } + } else { + return TryConvertItemToNative( @object ); + } + + return null; + } + + private GameObject TryConvertItemToNative( object value ) + { + if ( value == null ) + return null; + + //it's a simple type or not a Base + if ( value.GetType( ).IsSimpleType( ) || !( value is Base ) ) { + return null; + } + + var @base = (Base) value; + + //it's an unsupported Base, go through each of its property and try convert that + if ( !_converter.CanConvertToNative( @base ) ) { + var members = @base.GetMemberNames( ).ToList( ); + + //empty game object with the commit id as name, used to contain all the rest + var go = new GameObject( ); + go.name = @base.speckle_type; + var goos = new List( ); + foreach ( var member in members ) { + var goo = RecurseTreeToNative( @base[ member ] ); + if ( goo != null ) { + goo.name = member; + goo.transform.parent = go.transform; + goos.Add( goo ); + } + } + + //if no children is valid, return null + if ( !goos.Any( ) ) { + Utils.SafeDestroy( go ); + return null; + } + + return go; + } else { + try { + var go = _converter.ConvertToNative( @base ) as GameObject; + // Some revit elements have nested elements in a "elements" property + // for instance hosted families on a wall + if ( go != null && @base[ "elements" ] is List l && l.Any( ) ) { + var goo = RecurseTreeToNative( l ); + if ( goo != null ) { + goo.name = "elements"; + goo.transform.parent = go.transform; + } + } + + return go; + } + catch ( Exception e ) { + throw new SpeckleException( e.Message, e, true, SentryLevel.Error ); + } + } + + return null; + } + + + private static bool IsList( object @object ) + { + if ( @object == null ) + return false; + + var type = @object.GetType( ); + return ( typeof( IEnumerable ).IsAssignableFrom( type ) && !typeof( IDictionary ).IsAssignableFrom( type ) && + type != typeof( string ) ); + } + + private static bool IsDictionary( object @object ) + { + if ( @object == null ) + return false; + + Type type = @object.GetType( ); + return type.IsGenericType && type.GetGenericTypeDefinition( ) == typeof( Dictionary<,> ); + } } - - - private static bool IsList(object @object) - { - if (@object == null) - return false; - - var type = @object.GetType(); - return (typeof(IEnumerable).IsAssignableFrom(type) && !typeof(IDictionary).IsAssignableFrom(type) && - type != typeof(string)); - } - - private static bool IsDictionary(object @object) - { - if (@object == null) - return false; - - Type type = @object.GetType(); - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); - } - } } \ No newline at end of file diff --git a/Assets/Speckle Connector/StreamManager.cs b/Assets/Speckle Connector/StreamManager.cs index 4ddaab3..3f3f1b6 100644 --- a/Assets/Speckle Connector/StreamManager.cs +++ b/Assets/Speckle Connector/StreamManager.cs @@ -5,30 +5,39 @@ using Speckle.Core.Credentials; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Speckle.Core.Models; using UnityEditor; using UnityEngine; using UnityEngine.Events; -namespace Speckle.ConnectorUnity -{ - [ExecuteAlways] - public class StreamManager : MonoBehaviour - { +namespace Speckle.ConnectorUnity { + [ExecuteAlways] + public class StreamManager : MonoBehaviour { - public int SelectedAccountIndex = -1; - public int SelectedStreamIndex = -1; - public int SelectedBranchIndex = -1; - public int SelectedCommitIndex = -1; - public int OldSelectedAccountIndex = -1; - public int OldSelectedStreamIndex = -1; - - public Client Client; - public Account SelectedAccount; - public Stream SelectedStream; + public int SelectedAccountIndex = -1; + public int SelectedStreamIndex = -1; + public int SelectedBranchIndex = -1; + public int SelectedCommitIndex = -1; + public int OldSelectedAccountIndex = -1; + public int OldSelectedStreamIndex = -1; - public List Accounts; - public List Streams; - public List Branches; + public Client Client; + public Account SelectedAccount; + public Stream SelectedStream; - } + public List Accounts; + public List Streams; + public List Branches; + + public GameObject ConvertRecursivelyToNative( Base @base, string id ) + { + + var rc = GetComponent( ); + if ( rc == null ) + rc = gameObject.AddComponent( ); + + return rc.ConvertRecursivelyToNative( @base, + Branches[ SelectedBranchIndex ].commits.items[ SelectedCommitIndex ].id ); + } + } } \ No newline at end of file diff --git a/Assets/Speckle Connector/Utils.cs b/Assets/Speckle Connector/Utils.cs index fdc92ae..2af7c60 100644 --- a/Assets/Speckle Connector/Utils.cs +++ b/Assets/Speckle Connector/Utils.cs @@ -1,23 +1,69 @@ using System; using UnityEngine; -namespace Speckle.ConnectorUnity -{ - public static class Utils - { - public static int ToIntColor(this Color c) - { - return - System.Drawing.Color - .FromArgb(Convert.ToInt32(c.r * 255), Convert.ToInt32(c.r * 255), Convert.ToInt32(c.r * 255)) - .ToArgb(); - } +namespace Speckle.ConnectorUnity { + public static class Utils { + + 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, bool addMeshFilterIfNotFound ) + { + + var mf = go.GetComponent( ); + if ( mf == null ) { + if(!addMeshFilterIfNotFound) return; + + mf = go.AddComponent( ); + } + + + if ( Application.isPlaying ) + mf.mesh = m; + else + mf.sharedMesh = m; + } + + + public static void SafeMeshSet( this GameObject go, Mesh m ) + { + var mf = go.GetComponent( ); + if ( mf == null ) return; + + + if ( Application.isPlaying ) + mf.mesh = m; + else + mf.sharedMesh = m; + } + + + public static int ToIntColor( this Color c ) + { + return + System.Drawing.Color + .FromArgb( Convert.ToInt32( c.r * 255 ), Convert.ToInt32( c.r * 255 ), Convert.ToInt32( c.r * 255 ) ) + .ToArgb( ); + } + + public static Color ToUnityColor( this int c ) + { + var argb = System.Drawing.Color.FromArgb( c ); + return new Color( argb.R / 255.0f, argb.G / 255.0f, argb.B / 255.0f ); + } - public static Color ToUnityColor(this int c) - { - var argb = System.Drawing.Color.FromArgb(c); - return new Color(argb.R / 255.0f, argb.G / 255.0f, argb.B / 255.0f); } - - } } \ No newline at end of file