Files
speckle-unity/ConverterUnity.Geometry.cs
T
2020-12-29 18:25:39 +00:00

253 lines
6.6 KiB
C#

using Objects.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Mesh = Objects.Geometry.Mesh;
namespace Objects.Converter.Unity
{
public partial class ConverterUnity
{
#region helper methods
/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <returns></returns>
public Vector3 PointByCoordinates(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));
}
/// <summary>
///
/// </summary>
/// <param name="ptValues"></param>
/// <returns></returns>
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);
}
/// <summary>
///
/// </summary>
/// <param name="arr"></param>
/// <returns></returns>
public Vector3[] ArrayToPoints(IEnumerable<double> 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++] = PointByCoordinates(asArray[i - 2], asArray[i - 1], asArray[i], units);
return points;
}
#endregion
#region ToSpeckle
//TODO: more of these
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
//public Point PointToSpeckle(Vector3 p)
//{
// //switch y and z
// return new Point(p.x, p.z, p.y);
//}
#endregion
#region ToNative
private GameObject NewPointBasedGameObject(Vector3[] points, string name)
{
if (points.Length == 0) return null;
float pointDiameter = 1; //TODO: figure out how best to change this?
var go = new GameObject();
go.name = name;
var lineRenderer = go.AddComponent<LineRenderer>();
lineRenderer.positionCount = points.Length;
lineRenderer.SetPositions(points);
lineRenderer.numCornerVertices = lineRenderer.numCapVertices = 8;
lineRenderer.startWidth = lineRenderer.endWidth = pointDiameter;
return go;
}
/// <summary>
/// Converts a Speckle ioint to a GameObject with a line renderer
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public GameObject PointToNative(Point point)
{
Vector3 newPt = ArrayToPoint(point.value.ToArray(), point.units);
return NewPointBasedGameObject(new Vector3[2] { newPt, newPt }, point.speckle_type);
}
/// <summary>
/// Converts a Speckle line to a GameObject with a line renderer
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public GameObject LineToNative(Line line)
{
Vector3[] points = ArrayToPoints(line.value, line.units);
return NewPointBasedGameObject(points, line.speckle_type);
}
/// <summary>
/// Converts a Speckle polyline to a GameObject with a line renderer
/// </summary>
/// <param name="polyline"></param>
/// <returns></returns>
public GameObject PolylineToNative(Polyline polyline)
{
Vector3[] points = ArrayToPoints(polyline.value, polyline.units);
return NewPointBasedGameObject(points, polyline.speckle_type);
}
/// <summary>
/// Converts a Speckle curve to a GameObject with a line renderer
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
public GameObject CurveToNative(Curve curve)
{
Vector3[] points = ArrayToPoints(curve.displayValue.value, curve.units);
return NewPointBasedGameObject(points, curve.speckle_type);
}
/// <summary>
/// Converts a Speckle mesh to a GameObject with a mesh renderer
/// </summary>
/// <param name="speckleMesh"></param>
/// <returns></returns>
public GameObject MeshToNative(Mesh speckleMesh)
{
if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
{
return null;
}
var recentreMeshTransforms = false; //TODO: figure out how best to change this?
var verts = ArrayToPoints(speckleMesh.vertices, speckleMesh.units).ToList();
//convert speckleMesh.faces into triangle array
List<int> tris = new List<int>();
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 + 3]);
tris.Add(speckleMesh.faces[i + 1]);
tris.Add(speckleMesh.faces[i + 4]);
i += 5;
}
}
var go = new GameObject();
go.name = speckleMesh.speckle_type;
var mesh = go.AddComponent<MeshFilter>().mesh;
var meshRenderer = go.AddComponent<MeshRenderer>();
if (verts.Count >= 65535)
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
// 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();
mesh.RecalculateNormals();
mesh.RecalculateTangents();
//generate uvs doesn't work as intended. Leaving out for now
//GenerateUVs (ref mesh);
//Add mesh collider
MeshCollider mc = go.AddComponent<MeshCollider>();
mc.sharedMesh = mesh;
return go;
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="brep"></param>
/// <returns></returns>
//public static UnityMesh ToNative(this Brep brep)
//{
// Mesh speckleMesh = brep.displayValue;
// return MeshToNative(speckleMesh);
//}
}
}