diff --git a/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs b/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs
index 842e9d8..970d21f 100644
--- a/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs
+++ b/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs
@@ -5,18 +5,12 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Objects.Other;
-using Objects.Utils;
using Speckle.ConnectorUnity;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEditor;
using UnityEngine;
-using UnityEngine.Rendering;
-using Material = UnityEngine.Material;
-using Mesh = UnityEngine.Mesh;
-using Object = UnityEngine.Object;
using SMesh = Objects.Geometry.Mesh;
-using SColor = System.Drawing.Color;
using Transform = UnityEngine.Transform;
using STransform = Objects.Other.Transform;
@@ -26,20 +20,19 @@ namespace Objects.Converter.Unity
public partial class ConverterUnity
{
- protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
- protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
- protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
-
#region helper methods
-
-
- public Vector3 VectorByCoordinates(double x, double y, double z, double scaleFactor)
+
+ ///
+ /// Converts a 3D vector from Speckle's RH Z-up to Unity's LH Y-up coordinate system
+ ///
+ /// Scaled Vector in Unity coordinate space
+ public Vector3 VectorByCoordinates(double x, double y, double z, double scaleFactor = 1d)
{
// switch y and z //TODO is this correct? LH -> RH
- return new Vector3((float)(x * scaleFactor), (float)(z * scaleFactor), (float)(y * scaleFactor));
+ return new Vector3((float) (x * scaleFactor), (float) (z * scaleFactor), (float) (y * scaleFactor));
}
-
+
public Vector3 VectorByCoordinates(double x, double y, double z, string units)
{
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
@@ -60,16 +53,17 @@ namespace Objects.Converter.Unity
Vector3[] points = new Vector3[arr.Count / 3];
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
-
+
for (int i = 2, k = 0; i < arr.Count; i += 3)
points[k++] = VectorByCoordinates(arr[i - 2], arr[i - 1], arr[i], f);
-
+
return points;
}
-
+
#endregion
#region ToSpeckle
+
//TODO: more of these
///
@@ -82,173 +76,13 @@ namespace Objects.Converter.Unity
//switch y and z
return new Point(p.x, p.z, p.y);
}
-
-
-
-
- public virtual List? MeshToSpeckle(MeshFilter meshFilter)
- {
- Material[]? materials = meshFilter.GetComponent()?.materials;
-#if UNITY_EDITOR
- var nativeMesh = meshFilter.sharedMesh;
-#else
- var nativeMesh = meshFilter.mesh;
-#endif
- if (nativeMesh == null) return null;
-
- List convertedMeshes = new List(nativeMesh.subMeshCount);
- for (int i = 0; i < nativeMesh.subMeshCount; i++)
- {
- var subMesh = nativeMesh.GetSubMesh(i);
- SMesh converted;
- switch (subMesh.topology)
- {
- // case MeshTopology.Points:
- // //TODO convert as pointcloud
- // continue;
- case MeshTopology.Triangles:
- converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 3);
- convertedMeshes.Add(converted);
- break;
- case MeshTopology.Quads:
- converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 4);
- convertedMeshes.Add(converted);
- break;
- default:
- Debug.LogError($"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored.");
- continue;
- }
- if (materials == null || materials.Length <= i) continue;
-
- Material mat = materials[i];
- if(mat != null) converted["renderMaterial"] = MaterialToSpeckle(mat);
- }
-
- return convertedMeshes;
- }
-
-
- protected virtual SMesh SubMeshToSpeckle(Mesh nativeMesh, Transform instanceTransform, SubMeshDescriptor subMesh, int subMeshIndex, int faceN)
- {
- var nFaces = nativeMesh.GetIndices( subMeshIndex, true);
- int numFaces = nFaces.Length / faceN;
- List sFaces = new List(numFaces * (faceN + 1));
-
- int indexOffset = subMesh.firstVertex;
-
- // int i = 0;
- // int j = 0;
- // while (i < nFaces.Length)
- // {
- // if (j == 0)
- // {
- // sFaces.Add(faceN);
- // j = faceN;
- // }
- // sFaces.Add(nFaces[i] - indexOffset);
- // j--;
- // i++;
- // }
-
- int i = nFaces.Length - 1;
- int j = 0;
- while (i >= 0) //Traverse backwards to ensure CCW face orientation
- {
- if (j == 0)
- {
- //Add face cardinality indicator ever
- sFaces.Add(faceN);
- j = faceN;
- }
- sFaces.Add(nFaces[i] - indexOffset);
- j--;
- i--;
- }
-
- int vertexTake = subMesh.vertexCount;
- var nVertices = nativeMesh.vertices.Skip(indexOffset).Take(vertexTake);
- List sVertices = new List(subMesh.vertexCount * 3);
- foreach (var vertex in nVertices)
- {
- var p = instanceTransform.TransformPoint(vertex);
- sVertices.Add(p.x);
- sVertices.Add(p.z); //z and y swapped //TODO is this correct? LH -> RH
- sVertices.Add(p.y);
- }
-
- var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray();;
- List sColors = new List(nColors.Length);
- sColors.AddRange(nColors.Select(c => c.ToIntColor()));
-
- var nTexCoords = nativeMesh.uv.Skip(indexOffset).Take(vertexTake).ToArray();
- List sTexCoords = new List(nTexCoords.Length * 2);
- foreach (var uv in nTexCoords)
- {
- sTexCoords.Add(uv.x);
- sTexCoords.Add(uv.y);
- }
-
- var convertedMesh = new SMesh
- {
- vertices = sVertices,
- faces = sFaces,
- colors = sColors,
- textureCoordinates = sTexCoords,
- units = ModelUnits
- };
-
- return convertedMesh;
- }
-
- protected static HashSet SupportedShadersToSpeckle = new()
- {
- "Legacy Shaders/Transparent/Diffuse", "Standard"
- };
- public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
- {
- //Warning message for unknown shaders
- if(!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name)) Debug.LogWarning($"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect");
-
- var color = nativeMaterial.color;
- var opacity = 1f;
- if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
- {
- opacity = color.a;
- color.a = 255;
- }
-
- var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
- ? nativeMaterial.GetColor(EmissionColor).ToIntColor()
- : SColor.Black.ToArgb();
-
- var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
- ? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
- : $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
-
- var metalness = nativeMaterial.HasProperty(Metallic)
- ? nativeMaterial.GetFloat(Metallic)
- : 0;
-
- var roughness = nativeMaterial.HasProperty(Glossiness)
- ? 1 - nativeMaterial.GetFloat(Glossiness)
- : 1;
-
- return new RenderMaterial
- {
- name = materialName,
- diffuse = color.ToIntColor(),
- opacity = opacity,
- metalness = metalness,
- roughness = roughness,
- emissive = emissive,
- };
- }
#endregion
-
+
#region ToNative
+
protected GameObject? NewPointBasedGameObject(Vector3[] points, string name)
{
if (points.Length == 0) return null;
@@ -277,7 +111,7 @@ namespace Objects.Converter.Unity
{
Vector3 newPt = VectorByCoordinates(point.x, point.y, point.z, point.units);
- var go = NewPointBasedGameObject(new Vector3[] { newPt, newPt }, point.speckle_type);
+ var go = NewPointBasedGameObject(new Vector3[] {newPt, newPt}, point.speckle_type);
return go;
}
@@ -289,7 +123,7 @@ namespace Objects.Converter.Unity
///
public GameObject? LineToNative(Line line)
{
- var points = new List { VectorFromPoint(line.start), VectorFromPoint(line.end) };
+ var points = new List {VectorFromPoint(line.start), VectorFromPoint(line.end)};
var go = NewPointBasedGameObject(points.ToArray(), line.speckle_type);
return go;
@@ -320,301 +154,52 @@ namespace Objects.Converter.Unity
return go;
}
- ///
- /// Converts multiple (e.g. with different materials) into one native mesh
- ///
- /// Collection of es that shall be converted
- /// A with the converted , , and
- public GameObject? MeshesToNative(IReadOnlyCollection meshes)
- {
- if (!meshes.Any()) return null;
-
- var go = new GameObject();
- MeshDataToNative(meshes, out var nativeMesh, out var nativeMaterials, out var center);
-
- go.transform.position = center;
- go.SafeMeshSet(nativeMesh, true);
-
- var meshRenderer = go.AddComponent();
-
- meshRenderer.sharedMaterials = nativeMaterials;
-
- return go;
- }
-
public Dictionary GetProperties(Base o) => GetProperties(o, typeof(Base));
+
public Dictionary GetProperties(Base o, Type excludeType)
{
- var excludeProps = new HashSet(excludeType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
+ var excludeProps = new HashSet(excludeType
+ .GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(x => x.Name));
foreach (string alias in DisplayValuePropertyAliases)
{
excludeProps.Add(alias);
}
+
excludeProps.Add("renderMaterial");
excludeProps.Add("elements");
excludeProps.Add("name");
//excludeProps.Add("tag");
excludeProps.Add("physicsLayer");
-
+
return o.GetMembers()
.Where(x => !(excludeProps.Contains(x.Key) || excludeProps.Contains(x.Key.TrimStart('@'))))
- .ToDictionary(x => x.Key, x => (object?)x.Value);
- }
-
- ///
- /// Converts to a with a
- ///
- /// Mesh to convert
- ///
- public GameObject? MeshToNative(SMesh speckleMesh)
- {
- if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
- {
- Debug.Log($"Skipping mesh {speckleMesh.id}, mesh data was empty");
- return null;
- }
-
- GameObject? converted = MeshesToNative(new[] {speckleMesh});
- //if(converted != null) AttachSpeckleProperties(converted,speckleMesh.GetType(), GetProperties(speckleMesh, typeof(Mesh)));
- return converted;
+ .ToDictionary(x => x.Key, x => (object?) x.Value);
}
- ///
- ///
- ///
- /// meshes to be converted as SubMeshes
- /// The converted native mesh
- /// The converted materials (one per converted sub-mesh)
- /// Center position for the mesh
- public void MeshDataToNative(IReadOnlyCollection meshes, out Mesh nativeMesh, out Material[] nativeMaterials, out Vector3 center)
- {
- var verts = new List();
-
- var uvs = new List();
- var vertexColors = new List();
-
- var materials = new List(meshes.Count);
- var subMeshes = new List>(meshes.Count);
-
- foreach (SMesh m in meshes)
- {
- if(m.vertices.Count == 0 || m.faces.Count == 0 ) continue;
- List tris = new List();
- SubmeshToNative(m, verts, tris, uvs, vertexColors, materials);
- subMeshes.Add(tris);
- }
- nativeMaterials = materials.ToArray();
-
- Debug.Assert(verts.Count >= 0);
- Debug.Assert(verts.Count >= 0);
- nativeMesh = new Mesh();
-
- RecenterVertices(verts, out center);
-
- nativeMesh.subMeshCount = subMeshes.Count;
-
- nativeMesh.SetVertices(verts);
- nativeMesh.SetUVs(0, uvs);
- nativeMesh.SetColors(vertexColors);
-
-
- int j = 0;
- foreach(var subMeshTriangles in subMeshes)
- {
- nativeMesh.SetTriangles(subMeshTriangles, j);
- j++;
- }
-
- if (nativeMesh.vertices.Length >= UInt16.MaxValue)
- nativeMesh.indexFormat = IndexFormat.UInt32;
-
- nativeMesh.Optimize();
- nativeMesh.RecalculateBounds();
- nativeMesh.RecalculateNormals();
- nativeMesh.RecalculateTangents();
- }
-
-
- protected void SubmeshToNative(SMesh speckleMesh, List verts, List tris, List texCoords, List vertexColors, List materials)
- {
- speckleMesh.AlignVerticesWithTexCoordsByIndex();
- speckleMesh.TriangulateMesh();
-
- int indexOffset = verts.Count;
-
- // Convert Vertices
- verts.AddRange(ArrayToPoints(speckleMesh.vertices, speckleMesh.units));
-
- // Convert texture coordinates
- bool hasValidUVs = speckleMesh.TextureCoordinatesCount == speckleMesh.VerticesCount;
- if(speckleMesh.textureCoordinates.Count > 0 && !hasValidUVs) Debug.LogWarning($"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}");
-
- if (hasValidUVs)
- {
- texCoords.Capacity += speckleMesh.TextureCoordinatesCount;
- for (int j = 0; j < speckleMesh.TextureCoordinatesCount; j++)
- {
- var (u, v) = speckleMesh.GetTextureCoordinate(j);
- texCoords.Add(new Vector2((float)u,(float)v));
- }
- }
- else if (speckleMesh.bbox != null)
- {
- //Attempt to generate some crude UV coordinates using bbox //TODO this will be broken for submeshes
- texCoords.AddRange(GenerateUV(verts, (float)speckleMesh.bbox.xSize.Length, (float)speckleMesh.bbox.ySize.Length));
- }
-
- // Convert vertex colors
- if (speckleMesh.colors != null)
- {
- if (speckleMesh.colors.Count == speckleMesh.VerticesCount)
- {
- vertexColors.AddRange(speckleMesh.colors.Select(c => c.ToUnityColor()));
- }
- else if (speckleMesh.colors.Count != 0)
- {
- //TODO what if only some submeshes have colors?
- Debug.LogWarning($"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}");
- }
- }
-
- // Convert faces
- tris.Capacity += (int) (speckleMesh.faces.Count / 4f) * 3;
-
- for (int i = 0; i < speckleMesh.faces.Count; i += 4)
- {
- //We can safely assume all faces are triangles since we called TriangulateMesh
- tris.Add(speckleMesh.faces[i + 1] + indexOffset);
- tris.Add(speckleMesh.faces[i + 3] + indexOffset);
- tris.Add(speckleMesh.faces[i + 2] + indexOffset);
- }
-
- // Convert RenderMaterial
- materials.Add(RenderMaterialToNative(speckleMesh["renderMaterial"] as RenderMaterial));
- }
-
-
-
- private static IEnumerable GenerateUV(IReadOnlyList verts, float xSize, float ySize)
- {
- var uv = new Vector2[verts.Count];
- for (int i = 0; i < verts.Count; i++)
- {
-
- var vert = verts[i];
- uv[i] = new Vector2(vert.x / xSize, vert.y / ySize);
- }
- return uv;
- }
- private static Matrix4x4 UnflattenMatrix(IList flatMatrix)
- {
- Matrix4x4 matrix = new Matrix4x4();
- for(int row = 0; row < 4; row++)
- for(int col = 0; col < 4; col++)
- {
- matrix[row,col] = (float)flatMatrix[row * 4 + col];
- }
- return matrix.transpose;
- }
#endregion
-
- public static void RecenterVertices(List vertices, out Vector3 center)
- {
- center = Vector3.zero;
-
- if (vertices == null || !vertices.Any()) return;
-
- Bounds meshBounds = new Bounds { center = vertices[0] };
-
- foreach (var vert in vertices)
- meshBounds.Encapsulate(vert);
-
- center = meshBounds.center;
-
- for (int i = 0; i < vertices.Count; i++)
- vertices[i] -= meshBounds.center;
- }
-
- private Material RenderMaterialToNative(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 (renderMaterial == null) return mat;
-
- // 1. match material by name, if any
- string materialName = string.IsNullOrWhiteSpace(renderMaterial.name)
- ? $"material-{renderMaterial.id}"
- : renderMaterial.name.Replace('/', '-');
-
- if (LoadedAssets.TryGetValue(materialName, out Object asset)
- && asset is Material loadedMaterial) return loadedMaterial;
-
- // 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);
- }
-
- var c = renderMaterial.diffuse.ToUnityColor();
- mat.color = new Color(c.r, c.g, c.b, (float)renderMaterial.opacity);
- mat.name = materialName;
- mat.SetFloat(Metallic, (float)renderMaterial.metalness);
- mat.SetFloat(Glossiness, 1 - (float)renderMaterial.roughness);
-
- if (renderMaterial.emissive != SColor.Black.ToArgb()) mat.EnableKeyword ("_EMISSION");
- mat.SetColor(EmissionColor, renderMaterial.emissive.ToUnityColor());
-#if UNITY_EDITOR
- if (StreamManager.GenerateMaterials)
- {
- string name = mat.name.Trim(Path.GetInvalidFileNameChars());
- if (!AssetDatabase.IsValidFolder("Assets/Resources")) AssetDatabase.CreateFolder("Assets", "Resources");
- if (!AssetDatabase.IsValidFolder("Assets/Resources/Materials")) AssetDatabase.CreateFolder("Assets/Resources", "Materials");
- if (!AssetDatabase.IsValidFolder("Assets/Resources/Materials/Speckle Generated")) AssetDatabase.CreateFolder("Assets/Resources/Materials", "Speckle Generated");
-
- if (AssetDatabase.LoadAllAssetsAtPath($"Assets/Resources/Materials/Speckle Generated/" + name + ".mat").Length == 0) AssetDatabase.CreateAsset(mat, "Assets/Resources/Materials/Speckle Generated/" + name + ".mat");
-
- }
-#endif
-
- return mat;
- // 3. if not renderMaterial was passed, the default shader will be used
- }
-
- private SpeckleProperties AttachSpeckleProperties(GameObject go, Type speckleType, IDictionary properties)
- {
- var sd = go.AddComponent();
- sd.Data = properties;
- sd.SpeckleType = speckleType;
- return sd;
- }
-
private Base CreateSpeckleObjectFromProperties(GameObject go)
{
var sd = go.GetComponent();
if (sd == null || sd.Data == null)
return new Base();
-
- Base sobject = (Base)Activator.CreateInstance(sd.SpeckleType);
-
+
+ Base sobject = (Base) Activator.CreateInstance(sd.SpeckleType);
+
foreach (var key in sd.Data.Keys)
{
try
{
sobject[key] = sd.Data[key];
}
- catch(SpeckleException)
+ catch (SpeckleException)
{
// Ignore SpeckleExceptions that may be caused by get only properties
}
@@ -622,5 +207,134 @@ namespace Objects.Converter.Unity
return sobject;
}
+
+ public GameObject? BlockToNative(BlockInstance block)
+ {
+ if (block.blockDefinition == null)
+ {
+ Debug.Log($"Skipping {typeof(BlockInstance)} {block.id}, block definition was null");
+ return null;
+ }
+
+#if UNITY_EDITOR
+ // Check `Resources` for existing cached prefab asset
+ // TODO: this isn't how we check for existing materials, maybe there's a reason not to call LoadAssetPath constantly
+ string assetName = $"{GetAssetName(block.blockDefinition)}.prefab"
+ .Trim(Path.GetInvalidFileNameChars());
+ const string assetPath = "Assets/Resources/Prefabs/";
+ if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
+ {
+ GameObject? existing = AssetDatabase.LoadAssetAtPath($"{assetPath}/{assetName}");
+ if (existing)
+ {
+ var go = (GameObject) PrefabUtility.InstantiatePrefab(existing);
+ go.name = block.blockDefinition.name ?? "";
+ return go;
+ }
+ }
+#endif
+
+ // No existing found, so we Convert the block
+
+ GameObject native = new GameObject(block.blockDefinition.name ?? "");
+ TransformToNativeTransform(native.transform, block.transform);
+
+ List meshes = new();
+ List others = new();
+ foreach (Base geo in block.blockDefinition.geometry)
+ {
+ if (geo is SMesh m) meshes.Add(m);
+ else if (geo is Brep s) meshes.AddRange(s.displayValue);
+ else others.Add(geo);
+ }
+
+ if (meshes.Any())
+ {
+ MeshToNativeMesh(meshes, out var nativeMesh);
+ var nativeMaterials = RenderMaterialsToNative(meshes);
+ native.SafeMeshSet(nativeMesh, nativeMaterials);
+ }
+
+ foreach (Base child in others)
+ {
+ GameObject? c = ConvertToNativeGameObject(child);
+ if (c == null) continue;
+ c.transform.SetParent(native.transform, false);
+ }
+
+#if UNITY_EDITOR
+ if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
+ {
+ CreateDirectoryFromAssetPath(assetPath);
+ PrefabUtility.SaveAsPrefabAssetAndConnect(native, $"Assets/Resources/Prefabs/{assetName}",
+ InteractionMode.AutomatedAction);
+ }
+#endif
+
+ return native;
+ }
+
+
+
+ ///
+ /// Converts a 4x4 transformation matrix from Speckle's format,
+ /// to a Unity . Applying Z -> Y up conversion, and applying units to the translation
+ ///
+ ///
+ /// Transformation matrix in Unity's coordinate system
+ public Matrix4x4 TransformToNativeMatrix(STransform speckleTransform)
+ {
+ double VD(int i) => speckleTransform.value[i];
+ float V(int i) => (float) VD(i);
+
+ var sf = Speckle.Core.Kits.Units.GetConversionFactor(speckleTransform.units, ModelUnits);
+
+ return new Matrix4x4
+ {
+ // Left (X -> X)
+ [0, 0] = V(0),
+ [2, 0] = V(4),
+ [1, 0] = V(8),
+ [3, 0] = V(12),
+
+ //Up (Z -> Y)
+ [0, 2] = V(1),
+ [2, 2] = V(5),
+ [1, 2] = V(9),
+ [3, 2] = V(13),
+
+ //Forwards (Y -> Z)
+ [0, 1] = V(2),
+ [2, 1] = V(6),
+ [1, 1] = V(10),
+ [3, 1] = V(14),
+
+ //Translation
+ [0, 3] = (float) (VD(3) * sf),
+ [2, 3] = (float) (VD(7) * sf),
+ [1, 3] = (float) (VD(11) * sf),
+ [3, 3] = V(15),
+ };
+ }
+
+ public void TransformToNativeTransform(Transform transform, STransform speckleTransform)
+ {
+ Matrix4x4 matrix = TransformToNativeMatrix(speckleTransform);
+ ApplyMatrixToTransform(transform, matrix);
+ }
+
+ protected static void ApplyMatrixToTransform(Transform transform, Matrix4x4 m)
+ {
+ transform.localScale =
+ m.lossyScale; //doesn't work for non TRS, maybe we could fallback to squareSum approach (see TransformVectorized::SetFromMatrix in UE src)
+
+ //We can't use m.rotation, as it gives us incorrect results (perhaps because of RH -> LH? or maybe our MatrixToNative is broken?)
+ transform.localRotation = Quaternion.LookRotation(
+ m.GetColumn(2),
+ m.GetColumn(1)
+ );
+ transform.localPosition = m.GetPosition();
+ }
}
+
}
\ No newline at end of file
diff --git a/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs b/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs
new file mode 100644
index 0000000..1fb664c
--- /dev/null
+++ b/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs
@@ -0,0 +1,534 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Objects.Other;
+using Objects.Utils;
+using PlasticPipe.Certificates;
+using Speckle.ConnectorUnity;
+using Speckle.Core.Models;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Rendering;
+using Material = UnityEngine.Material;
+using Mesh = UnityEngine.Mesh;
+using Object = UnityEngine.Object;
+using SMesh = Objects.Geometry.Mesh;
+using SColor = System.Drawing.Color;
+using Transform = UnityEngine.Transform;
+using STransform = Objects.Other.Transform;
+
+#nullable enable
+namespace Objects.Converter.Unity
+{
+ public partial class ConverterUnity
+ {
+
+ protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
+ protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
+ protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
+
+ #region ToSpeckle
+
+ public virtual List? MeshToSpeckle(MeshFilter meshFilter)
+ {
+ Material[]? materials = meshFilter.GetComponent()?.materials;
+#if UNITY_EDITOR
+ var nativeMesh = meshFilter.sharedMesh;
+#else
+ var nativeMesh = meshFilter.mesh;
+#endif
+ if (nativeMesh == null) return null;
+
+ List convertedMeshes = new List(nativeMesh.subMeshCount);
+ for (int i = 0; i < nativeMesh.subMeshCount; i++)
+ {
+ var subMesh = nativeMesh.GetSubMesh(i);
+ SMesh converted;
+ switch (subMesh.topology)
+ {
+ // case MeshTopology.Points:
+ // //TODO convert as pointcloud
+ // continue;
+ case MeshTopology.Triangles:
+ converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 3);
+ convertedMeshes.Add(converted);
+ break;
+ case MeshTopology.Quads:
+ converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 4);
+ convertedMeshes.Add(converted);
+ break;
+ default:
+ Debug.LogError(
+ $"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored.");
+ continue;
+ }
+
+ if (materials == null || materials.Length <= i) continue;
+
+ Material mat = materials[i];
+ if (mat != null) converted["renderMaterial"] = MaterialToSpeckle(mat);
+ }
+
+ return convertedMeshes;
+ }
+
+
+ protected virtual SMesh SubMeshToSpeckle(Mesh nativeMesh, Transform instanceTransform,
+ SubMeshDescriptor subMesh, int subMeshIndex, int faceN)
+ {
+ var nFaces = nativeMesh.GetIndices(subMeshIndex, true);
+ int numFaces = nFaces.Length / faceN;
+ List sFaces = new List(numFaces * (faceN + 1));
+
+ int indexOffset = subMesh.firstVertex;
+
+ // int i = 0;
+ // int j = 0;
+ // while (i < nFaces.Length)
+ // {
+ // if (j == 0)
+ // {
+ // sFaces.Add(faceN);
+ // j = faceN;
+ // }
+ // sFaces.Add(nFaces[i] - indexOffset);
+ // j--;
+ // i++;
+ // }
+
+ int i = nFaces.Length - 1;
+ int j = 0;
+ while (i >= 0) //Traverse backwards to ensure CCW face orientation
+ {
+ if (j == 0)
+ {
+ //Add face cardinality indicator ever
+ sFaces.Add(faceN);
+ j = faceN;
+ }
+
+ sFaces.Add(nFaces[i] - indexOffset);
+ j--;
+ i--;
+ }
+
+ int vertexTake = subMesh.vertexCount;
+ var nVertices = nativeMesh.vertices.Skip(indexOffset).Take(vertexTake);
+ List sVertices = new List(subMesh.vertexCount * 3);
+ foreach (var vertex in nVertices)
+ {
+ var p = instanceTransform.TransformPoint(vertex);
+ sVertices.Add(p.x);
+ sVertices.Add(p.z); //z and y swapped //TODO is this correct? LH -> RH
+ sVertices.Add(p.y);
+ }
+
+ var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray();
+ ;
+ List sColors = new List(nColors.Length);
+ sColors.AddRange(nColors.Select(c => c.ToIntColor()));
+
+ var nTexCoords = nativeMesh.uv.Skip(indexOffset).Take(vertexTake).ToArray();
+ List sTexCoords = new List(nTexCoords.Length * 2);
+ foreach (var uv in nTexCoords)
+ {
+ sTexCoords.Add(uv.x);
+ sTexCoords.Add(uv.y);
+ }
+
+ var convertedMesh = new SMesh
+ {
+ vertices = sVertices,
+ faces = sFaces,
+ colors = sColors,
+ textureCoordinates = sTexCoords,
+ units = ModelUnits
+ };
+
+ return convertedMesh;
+ }
+
+ ///
+ /// List of officially supported shaders. Will attempt to convert shaders not on this list, but will throw warning.
+ ///
+ protected static HashSet SupportedShadersToSpeckle = new()
+ {
+ "Legacy Shaders/Transparent/Diffuse", "Standard"
+ };
+
+ public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
+ {
+ //Warning message for unknown shaders
+ if (!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name))
+ Debug.LogWarning(
+ $"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect");
+
+ var color = nativeMaterial.color;
+ var opacity = 1f;
+ if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
+ {
+ opacity = color.a;
+ color.a = 255;
+ }
+
+ var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
+ ? nativeMaterial.GetColor(EmissionColor).ToIntColor()
+ : SColor.Black.ToArgb();
+
+ var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
+ ? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
+ : $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
+
+ var metalness = nativeMaterial.HasProperty(Metallic)
+ ? nativeMaterial.GetFloat(Metallic)
+ : 0;
+
+ var roughness = nativeMaterial.HasProperty(Glossiness)
+ ? 1 - nativeMaterial.GetFloat(Glossiness)
+ : 1;
+
+ return new RenderMaterial
+ {
+ name = materialName,
+ diffuse = color.ToIntColor(),
+ opacity = opacity,
+ metalness = metalness,
+ roughness = roughness,
+ emissive = emissive,
+ };
+ }
+
+ #endregion
+
+
+ #region ToNative
+
+ ///
+ /// Converts multiple (e.g. with different materials) into one native mesh
+ ///
+ /// Root element who's name/id is used to identify the mesh
+ /// Collection of es that shall be converted
+ /// A with the converted , , and
+ public GameObject? MeshesToNative(Base element, IReadOnlyCollection meshes)
+ {
+ if (!meshes.Any())
+ {
+ Debug.Log($"Skipping {element.GetType()} {element.id}, zero {typeof(SMesh)} provided");
+ return null;
+ }
+
+ Mesh nativeMesh;
+ Material[] nativeMaterials = RenderMaterialsToNative(meshes);
+ Vector3 center;
+
+ if (LoadedAssets.TryGetValue(element.id, out var existingObj)
+ && existingObj is Mesh existing)
+ {
+ nativeMesh = existing;
+ MeshDataToNative(meshes,
+ out List verts,
+ out _,
+ out _,
+ out _);
+ center = CalculateBounds(verts).center;
+ }
+ else
+ {
+ MeshToNativeMesh(meshes, out nativeMesh, out center);
+ string name = GetAssetName(element);
+ nativeMesh.name = name;
+#if UNITY_EDITOR
+ if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
+ {
+ const string assetPath = "Assets/Resources/Meshes/Speckle Generated/";
+ CreateDirectoryFromAssetPath(assetPath);
+ AssetDatabase.CreateAsset(nativeMesh, $"{assetPath}/{name}");
+ }
+#endif
+ }
+
+ var go = new GameObject();
+ go.transform.position = center;
+ go.SafeMeshSet(nativeMesh, nativeMaterials);
+
+ return go;
+ }
+
+
+ ///
+ /// Converts to a with a
+ ///
+ /// Mesh to convert
+ ///
+ public GameObject? MeshToNative(SMesh speckleMesh)
+ {
+ if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
+ {
+ Debug.Log($"Skipping mesh {speckleMesh.id}, mesh data was empty");
+ return null;
+ }
+
+ GameObject? converted = MeshesToNative(speckleMesh, new[] {speckleMesh});
+
+ // Raw meshes shouldn't have dynamic props to attach
+ //if (converted != null) AttachSpeckleProperties(converted,speckleMesh.GetType(), GetProperties(speckleMesh, typeof(Mesh)));
+
+ return converted;
+ }
+
+ ///
+ /// Converts Speckle s as a native object
+ ///
+ /// meshes to be converted as SubMeshes
+ /// The converted native mesh
+ public void MeshToNativeMesh(IReadOnlyCollection meshes, out Mesh nativeMesh)
+ => MeshToNativeMesh(meshes, out nativeMesh, out _, false);
+
+
+ ///
+ /// when true, will recenter vertices
+ /// Center position for the mesh
+ public void MeshToNativeMesh(IReadOnlyCollection meshes,
+ out Mesh nativeMesh,
+ out Vector3 center,
+ bool recenterVerts = true)
+ {
+ MeshDataToNative(meshes,
+ out List verts,
+ out List uvs,
+ out List vertexColors,
+ out List> subMeshes);
+
+ Debug.Assert(verts.Count >= 0);
+
+ center = recenterVerts ? RecenterVertices(verts) : Vector3.zero;
+
+ nativeMesh = new Mesh();
+ nativeMesh.subMeshCount = subMeshes.Count;
+ nativeMesh.SetVertices(verts);
+ nativeMesh.SetUVs(0, uvs);
+ nativeMesh.SetColors(vertexColors);
+
+
+ int j = 0;
+ foreach (var subMeshTriangles in subMeshes)
+ {
+ nativeMesh.SetTriangles(subMeshTriangles, j);
+ j++;
+ }
+
+ if (nativeMesh.vertices.Length >= UInt16.MaxValue)
+ nativeMesh.indexFormat = IndexFormat.UInt32;
+
+ nativeMesh.Optimize();
+ nativeMesh.RecalculateBounds();
+ nativeMesh.RecalculateNormals();
+ nativeMesh.RecalculateTangents();
+ }
+
+ public void MeshDataToNative(IReadOnlyCollection meshes,
+ out List verts,
+ out List uvs,
+ out List vertexColors,
+ out List> subMeshes)
+ {
+ verts = new List();
+ uvs = new List();
+ vertexColors = new List();
+ subMeshes = new List>(meshes.Count);
+
+ foreach (SMesh m in meshes)
+ {
+ if (m.vertices.Count == 0 || m.faces.Count == 0) continue;
+
+ List tris = new List();
+ SubmeshToNative(m, verts, tris, uvs, vertexColors);
+ subMeshes.Add(tris);
+ }
+ }
+
+
+ protected void SubmeshToNative(SMesh speckleMesh, List verts, List tris, List texCoords,
+ List vertexColors)
+ {
+ speckleMesh.AlignVerticesWithTexCoordsByIndex();
+ speckleMesh.TriangulateMesh();
+
+ int indexOffset = verts.Count;
+
+ // Convert Vertices
+ verts.AddRange(ArrayToPoints(speckleMesh.vertices, speckleMesh.units));
+
+ // Convert texture coordinates
+ bool hasValidUVs = speckleMesh.TextureCoordinatesCount == speckleMesh.VerticesCount;
+ if (speckleMesh.textureCoordinates.Count > 0 && !hasValidUVs)
+ Debug.LogWarning(
+ $"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}");
+
+ if (hasValidUVs)
+ {
+ texCoords.Capacity += speckleMesh.TextureCoordinatesCount;
+ for (int j = 0; j < speckleMesh.TextureCoordinatesCount; j++)
+ {
+ var (u, v) = speckleMesh.GetTextureCoordinate(j);
+ texCoords.Add(new Vector2((float) u, (float) v));
+ }
+ }
+ else if (speckleMesh.bbox != null)
+ {
+ // Attempt to generate some crude UV coordinates using bbox
+ texCoords.AddRange(GenerateUV(indexOffset, verts, (float) speckleMesh.bbox.xSize.Length,
+ (float) speckleMesh.bbox.ySize.Length));
+ }
+ else
+ {
+ texCoords.AddRange(Enumerable.Repeat(Vector2.zero, verts.Count - indexOffset));
+ }
+
+ // Convert vertex colors
+ if (speckleMesh.colors != null)
+ {
+ if (speckleMesh.colors.Count == speckleMesh.VerticesCount)
+ {
+ vertexColors.AddRange(speckleMesh.colors.Select(c => c.ToUnityColor()));
+ }
+ else if (speckleMesh.colors.Count != 0)
+ {
+ //TODO what if only some submeshes have colors?
+ Debug.LogWarning(
+ $"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}");
+ }
+ }
+
+ // Convert faces
+ tris.Capacity += (int) (speckleMesh.faces.Count / 4f) * 3;
+
+ for (int i = 0; i < speckleMesh.faces.Count; i += 4)
+ {
+ // We can safely assume all faces are triangles since we called TriangulateMesh
+ tris.Add(speckleMesh.faces[i + 1] + indexOffset);
+ tris.Add(speckleMesh.faces[i + 3] + indexOffset);
+ tris.Add(speckleMesh.faces[i + 2] + indexOffset);
+ }
+ }
+
+
+ protected static IEnumerable GenerateUV(int indexOffset, IReadOnlyList verts, float xSize,
+ float ySize)
+ {
+ var uv = new Vector2[verts.Count - indexOffset];
+ for (int i = 0; i < verts.Count - indexOffset; i++)
+ {
+
+ var vert = verts[i];
+ uv[i] = new Vector2(vert.x / xSize, vert.y / ySize);
+ }
+
+ return uv;
+ }
+
+ protected static Vector3 RecenterVertices(IList vertices)
+ {
+ if (!vertices.Any()) return Vector3.zero;
+
+ Bounds meshBounds = CalculateBounds(vertices);
+
+ for (int i = 0; i < vertices.Count; i++)
+ vertices[i] -= meshBounds.center;
+
+ return meshBounds.center;
+ }
+
+ protected static Bounds CalculateBounds(IList points)
+ {
+ Bounds b = new Bounds {center = points[0]};
+
+ foreach (var p in points)
+ b.Encapsulate(p);
+
+ return b;
+ }
+
+
+ public Material[] RenderMaterialsToNative(IEnumerable meshes)
+ {
+ return meshes.Select(m => RenderMaterialToNative(m["renderMaterial"] as RenderMaterial)).ToArray();
+ }
+
+ public Material RenderMaterialToNative(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 (renderMaterial == null) return mat;
+
+ // 1. match material by name, if any
+ string materialName = string.IsNullOrWhiteSpace(renderMaterial.name)
+ ? $"material-{renderMaterial.id}"
+ : renderMaterial.name.Replace('/', '-');
+
+ if (LoadedAssets.TryGetValue(materialName, out Object asset)
+ && asset is Material loadedMaterial) return loadedMaterial;
+
+ // 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);
+ }
+
+ var c = renderMaterial.diffuse.ToUnityColor();
+ mat.color = new Color(c.r, c.g, c.b, (float) renderMaterial.opacity);
+ mat.name = materialName;
+ mat.SetFloat(Metallic, (float) renderMaterial.metalness);
+ mat.SetFloat(Glossiness, 1 - (float) renderMaterial.roughness);
+
+ if (renderMaterial.emissive != SColor.Black.ToArgb()) mat.EnableKeyword("_EMISSION");
+ mat.SetColor(EmissionColor, renderMaterial.emissive.ToUnityColor());
+
+
+#if UNITY_EDITOR
+ if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
+ {
+ var invalidChars = Path.GetInvalidFileNameChars();
+ string name = new(mat.name.Where(x => !invalidChars.Contains(x)).ToArray());
+
+ const string assetPath = "Assets/Resources/Materials/Speckle Generated/";
+ CreateDirectoryFromAssetPath(assetPath);
+
+ if (AssetDatabase.LoadAllAssetsAtPath($"{assetPath}/{name}.mat")
+ .Length == 0)
+ AssetDatabase.CreateAsset(mat, $"{assetPath}/{name}.mat");
+
+ }
+#endif
+
+ return mat;
+ // 3. if not renderMaterial was passed, the default shader will be used
+ }
+
+ protected static string GetAssetName(Base b, bool alwaysIncludeId = true)
+ {
+ var invalidChars = Path.GetInvalidFileNameChars();
+ string id = b.id;
+ foreach (var nameAlias in new[] {"name", "Name"})
+ {
+ string? rawName = b[nameAlias] as string;
+ if (string.IsNullOrWhiteSpace(rawName)) continue;
+
+ string name = new(rawName.Where(x => !invalidChars.Contains(x)).ToArray());
+
+ return alwaysIncludeId ? $"{name} - {id}" : name;
+ }
+
+ return id;
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs.meta b/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs.meta
new file mode 100644
index 0000000..0b734bc
--- /dev/null
+++ b/Packages/systems.speckle.speckle-unity/ConverterUnity.Mesh.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f174f59c0ccd3c74cb6b832d1a04b637
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/systems.speckle.speckle-unity/ConverterUnity.cs b/Packages/systems.speckle.speckle-unity/ConverterUnity.cs
index 7a201c2..b78cd98 100644
--- a/Packages/systems.speckle.speckle-unity/ConverterUnity.cs
+++ b/Packages/systems.speckle.speckle-unity/ConverterUnity.cs
@@ -3,203 +3,228 @@ using Speckle.Core.Models;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Text;
using Objects.BuiltElements;
+using Objects.Other;
using Speckle.ConnectorUnity;
+using UnityEditor;
using UnityEngine;
using Mesh = Objects.Geometry.Mesh;
using Object = UnityEngine.Object;
namespace Objects.Converter.Unity
{
- public partial class ConverterUnity : ISpeckleConverter
- {
- #region implemented methods
-
- public void SetConverterSettings(object settings) => throw new NotImplementedException();
-
- public string Description => "Default Speckle Kit for Unity";
- public string Name => nameof(ConverterUnity);
- public string Author => "Speckle";
- public string WebsiteOrEmail => "https://speckle.systems";
-
- public ProgressReport Report { get; }
- public ReceiveMode ReceiveMode { get; set; }
-
- public IEnumerable GetServicedApplications() => new string[] {VersionedHostApplications.Unity};
-
- public HashSet ConversionErrors { get; private set; }
-
- public Dictionary LoadedAssets { get; private set; }
-
- public void SetContextDocument(object doc)
+ public partial class ConverterUnity : ISpeckleConverter
{
- if(doc is not Dictionary loadedAssets) throw new ArgumentException($"Expected {nameof(doc)} to be of type {typeof(Dictionary)}", nameof(doc));
- LoadedAssets = loadedAssets;
- }
+ #region implemented methods
- public void SetContextObjects(List objects) => throw new NotImplementedException();
+ public void SetConverterSettings(object settings) => throw new NotImplementedException();
+
+ public string Description => "Default Speckle Kit for Unity";
+ public string Name => nameof(ConverterUnity);
+ public string Author => "Speckle";
+ public string WebsiteOrEmail => "https://speckle.systems";
+
+ public ProgressReport Report { get; }
+ public ReceiveMode ReceiveMode { get; set; }
+
+ public IEnumerable GetServicedApplications() => new string[] {VersionedHostApplications.Unity};
+
+ public Dictionary LoadedAssets { get; private set; }
+
+ public void SetContextDocument(object doc)
+ {
+ if (doc is not Dictionary loadedAssets)
+ throw new ArgumentException(
+ $"Expected {nameof(doc)} to be of type {typeof(Dictionary)}", nameof(doc));
+ LoadedAssets = loadedAssets;
+ }
+
+ public void SetContextObjects(List objects) => throw new NotImplementedException();
+
+ public void SetPreviousContextObjects(List objects) => throw new NotImplementedException();
- public void SetPreviousContextObjects(List objects) => throw new NotImplementedException();
-
#nullable enable
- public object? ConvertToNative(Base @object) => ConvertToNativeGameObject(@object);
+ public object? ConvertToNative(Base @object) => ConvertToNativeGameObject(@object);
- public Base ConvertToSpeckle(object @object)
- {
- if (!(@object is GameObject go)) throw new NotSupportedException($"Cannot convert object of type {@object.GetType()} to Speckle");
- return ConvertGameObjectToSpeckle(go);
- }
-
-#endregion implemented methods
-
-
-
- public Base ConvertGameObjectToSpeckle(GameObject go)
- {
-
- Base speckleObject = CreateSpeckleObjectFromProperties(go);
-
- speckleObject["name"] = go.name;
- //speckleObject["transform"] = TransformToSpeckle(go.Transform); //TODO
- speckleObject["tag"] = go.tag;
- speckleObject["physicsLayer"] = LayerMask.LayerToName(go.layer);
- //speckleObject["isStatic"] = go.isStatic; //todo figure out realtime-rendered static mobility interoperability (unreal)
-
- foreach (Component component in go.GetComponents())
- {
- try
+ public Base ConvertToSpeckle(object @object)
{
- switch (component)
- {
- case MeshFilter meshFilter:
- var displayValues = MeshToSpeckle(meshFilter);
-
- speckleObject.SetDetachedPropertyChecked("displayValue", displayValues);
-
- break;
- // case Camera camera:
- // speckleObject["cameraComponent"] = CameraToSpeckle(camera);
- // break;
- }
+ if (!(@object is GameObject go))
+ throw new NotSupportedException($"Cannot convert object of type {@object.GetType()} to Speckle");
+ return ConvertGameObjectToSpeckle(go);
}
- catch(Exception e)
+
+ #endregion implemented methods
+
+
+ public Base ConvertGameObjectToSpeckle(GameObject go)
{
- Debug.LogError($"Failed to convert {component.GetType()} component\n{e}", component);
+ Base speckleObject = CreateSpeckleObjectFromProperties(go);
+
+ speckleObject["name"] = go.name;
+ //speckleObject["transform"] = TransformToSpeckle(go.Transform); //TODO
+ speckleObject["tag"] = go.tag;
+ speckleObject["physicsLayer"] = LayerMask.LayerToName(go.layer);
+ //speckleObject["isStatic"] = go.isStatic; //todo figure out realtime-rendered static mobility interoperability (unreal)
+
+ foreach (Component component in go.GetComponents())
+ {
+ try
+ {
+ switch (component)
+ {
+ case MeshFilter meshFilter:
+ var displayValues = MeshToSpeckle(meshFilter);
+
+ speckleObject.SetDetachedPropertyChecked("displayValue", displayValues);
+
+ 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;
}
- }
-
- return speckleObject;
- }
-
-
-
-
-
- public GameObject? ConvertToNativeGameObject(Base speckleObject)
- {
- switch (speckleObject)
- {
- // case Point o:
- // return PointToNative(o);
- // case Line o:
- // return LineToNative(o);
- // case Polyline o:
- // return PolylineToNative(o);
- // case Curve o:
- // return CurveToNative(o);
- case View3D v:
- return View3DToNative(v);
- case Mesh o:
- return MeshToNative(o);
- default:
-
- //Object is not a raw geometry, convert it as display value element
- GameObject? element = DisplayValueToNative(speckleObject);
- if (element != null)
- {
- if(!speckleObject.speckle_type.Contains("Objects.Geometry"))
- AttachSpeckleProperties(element, speckleObject.GetType(), GetProperties(speckleObject));
-
- return element;
- }
-
- return new GameObject();
- }
-
- }
- public IList DisplayValuePropertyAliases { get; set; } = new[] {"displayValue","@displayValue","displayMesh", "@displayMesh" };
- public GameObject? DisplayValueToNative(Base @object)
- {
- foreach (string alias in DisplayValuePropertyAliases)
- {
- switch (@object[alias])
+ public GameObject? ConvertToNativeGameObject(Base speckleObject)
{
- //capture any other object that might have a mesh representation
- case IList dvCollection:
- return MeshesToNative(dvCollection.OfType().ToList());
- case Mesh dvMesh:
- return MeshesToNative(new[] {dvMesh});
- case Base dvBase:
- return ConvertToNativeGameObject(dvBase);
+ switch (speckleObject)
+ {
+ // case Point o:
+ // return PointToNative(o);
+ // case Line o:
+ // return LineToNative(o);
+ // case Polyline o:
+ // return PolylineToNative(o);
+ // case Curve o:
+ // return CurveToNative(o);
+ case View3D v:
+ return View3DToNative(v);
+ case Mesh o:
+ return MeshToNative(o);
+ case BlockInstance o:
+ return BlockToNative(o);
+ default:
+
+ //Object is not a raw geometry, convert it as display value element
+ GameObject? element = DisplayValueToNative(speckleObject);
+ if (element != null)
+ {
+ if (!speckleObject.speckle_type.Contains("Objects.Geometry"))
+ AttachSpeckleProperties(element, speckleObject.GetType(), GetProperties(speckleObject));
+
+ return element;
+ }
+
+ return new GameObject();
+ }
}
- }
- return null;
- }
- public List ConvertToSpeckle(List