using System.Collections.Generic;
using UnityEngine;
#if (UNITY_EDITOR)
using UnityEditor;
#endif
using System.Linq;
using System.IO;
namespace ECE
{
///
/// Class to calculate and add colliders
///
public class EasyColliderCreator
{
#if (UNITY_EDITOR)
///
/// Just an easy way to get an instance of preferences to create colliders with.
///
///
private EasyColliderPreferences ECEPreferences
{
get { return EasyColliderPreferences.Preferences; }
}
#endif
///
/// Data struct from calculating a best fit sphere
///
private struct BestFitSphere
{
///
/// Center of the sphere
///
public Vector3 Center;
///
/// Radius of the sphere
///
public float Radius;
///
/// Best Fit Sphere
///
/// Center of the sphere
/// Radius of the sphere
public BestFitSphere(Vector3 center, float radius)
{
this.Center = center;
this.Radius = radius;
}
}
// merge colliders are editor-only.
#if (UNITY_EDITOR)
#region MergeColliders
///
/// Merges all colliders in the list to a single resultant collider and returns it.
///
/// List of colliders to merge
/// Type of collider we want the colliders merged into
/// Properties to set on the new collider
/// Single merged collider.
public Collider MergeColliders(List collidersToMerge, CREATE_COLLIDER_TYPE result, EasyColliderProperties properties)
{
if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED)
{
properties.AttachTo = GetFirstNonNullTransform(collidersToMerge).gameObject;
}
EasyColliderData data = MergeCollidersPreview(collidersToMerge, result, properties.AttachTo.transform);
if (result == CREATE_COLLIDER_TYPE.BOX || result == CREATE_COLLIDER_TYPE.ROTATED_BOX)
{
if (result == CREATE_COLLIDER_TYPE.ROTATED_BOX)
{
properties.AttachTo = GetFirstNonNullTransform(collidersToMerge).gameObject;
}
return CreateBoxCollider(data as BoxColliderData, properties);
}
else if (result == CREATE_COLLIDER_TYPE.CAPSULE || result == CREATE_COLLIDER_TYPE.ROTATED_CAPSULE)
{
if (result == CREATE_COLLIDER_TYPE.ROTATED_CAPSULE)
{
properties.AttachTo = GetFirstNonNullTransform(collidersToMerge).gameObject;
}
return CreateCapsuleCollider(data as CapsuleColliderData, properties);
}
else if (result == CREATE_COLLIDER_TYPE.SPHERE)
{
return CreateSphereCollider(data as SphereColliderData, properties);
}
else if (result == CREATE_COLLIDER_TYPE.CONVEX_MESH || result == CREATE_COLLIDER_TYPE.CYLINDER)
{
MeshColliderData d = data as MeshColliderData;
if (ECEPreferences.SaveConvexHullAsAsset)
{
EasyColliderSaving.CreateAndSaveMeshAsset(d.ConvexMesh, properties.AttachTo);
}
return CreateConvexMeshCollider(d.ConvexMesh, properties.AttachTo, properties);
}
return null;
}
///
/// Returns the first transform of a non-null collider
///
/// list of colliders
/// first non-null collider's transform, null if no non-null colliders
private Transform GetFirstNonNullTransform(List collidersList)
{
foreach (Collider c in collidersList)
{
if (c != null)
{
return c.transform;
}
}
return null;
}
///
/// Calculates the preview data for merged colliders.
///
///
///
///
///
public EasyColliderData MergeCollidersPreview(List collidersToMerge, CREATE_COLLIDER_TYPE result, Transform attachTo)
{
List worldVertices = new List();
foreach (Collider col in collidersToMerge)
{
// Get world vertices for mesh collider.
MeshCollider mc = col as MeshCollider;
if (mc != null)
{
AddWorldVerts(mc, worldVertices);
continue;
}
BoxCollider box = col as BoxCollider;
if (box != null)
{
AddWorldVerts(box, worldVertices);
continue;
}
CapsuleCollider capsule = col as CapsuleCollider;
if (capsule != null)
{
AddWorldVerts(capsule, worldVertices);
continue;
}
SphereCollider sphere = col as SphereCollider;
if (sphere != null)
{
AddWorldVerts(sphere, worldVertices);
continue;
}
}
EasyColliderData d = new EasyColliderData();
if (result == CREATE_COLLIDER_TYPE.CONVEX_MESH)
{
return EasyColliderQuickHull.CalculateHullData(worldVertices, attachTo);
}
else if (result == CREATE_COLLIDER_TYPE.BOX || result == CREATE_COLLIDER_TYPE.ROTATED_BOX)
{
if (result == CREATE_COLLIDER_TYPE.ROTATED_BOX)
{
attachTo = GetFirstNonNullTransform(collidersToMerge);
}
return CalculateBox(worldVertices, attachTo, false);
}
else if (result == CREATE_COLLIDER_TYPE.SPHERE)
{
// does it make sense to allow different sphere methods -> or just min-max method.
if (ECEPreferences.SphereColliderMethod == SPHERE_COLLIDER_METHOD.MinMax)
{
return CalculateSphereMinMax(worldVertices, attachTo);
}
else if (ECEPreferences.SphereColliderMethod == SPHERE_COLLIDER_METHOD.Distance)
{
return CalculateSphereDistance(worldVertices, attachTo);
}
else if (ECEPreferences.SphereColliderMethod == SPHERE_COLLIDER_METHOD.BestFit)
{
return CalculateSphereBestFit(worldVertices, attachTo);
}
}
else if (result == CREATE_COLLIDER_TYPE.CAPSULE || result == CREATE_COLLIDER_TYPE.ROTATED_CAPSULE)
{
if (result == CREATE_COLLIDER_TYPE.ROTATED_CAPSULE)
{
attachTo = GetFirstNonNullTransform(collidersToMerge);
}
// does it make sense to allow capsule methods? -> can use various min max + dia, radius etc.
if (ECEPreferences.CapsuleColliderMethod == CAPSULE_COLLIDER_METHOD.BestFit)
{
return CalculateCapsuleBestFit(worldVertices, attachTo, false);
}
else
{
return CalculateCapsuleMinMax(worldVertices, attachTo, ECEPreferences.CapsuleColliderMethod, false);
}
}
else if (result == CREATE_COLLIDER_TYPE.CYLINDER)
{
return CalculateCylinderCollider(worldVertices, attachTo.transform);
}
return d;
}
///
/// Adds the vertices of a mesh collider to the world vertices list
///
/// mesh collider
/// world vertices list
private void AddWorldVerts(MeshCollider meshCollider, List worldVertices)
{
Vector3[] vertices = meshCollider.sharedMesh.vertices;
Transform t = meshCollider.transform;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = t.TransformPoint(vertices[i]);
}
worldVertices.AddRange(vertices);
}
///
/// Adds the vertices of a box collider to the world vertices list
///
/// box collider
/// world vertices list
private void AddWorldVerts(BoxCollider boxCollider, List worldVertices)
{
Vector3 halfSize = boxCollider.size / 2;
Vector3 center = boxCollider.center;
Vector3[] vertices = new Vector3[8]{
center + halfSize, //0
center + new Vector3(halfSize.x, halfSize.y, -halfSize.z), //1
center + new Vector3(halfSize.x, -halfSize.y, halfSize.z), //2
center + new Vector3(halfSize.x, -halfSize.y, -halfSize.z), //3
center + new Vector3(-halfSize.x, halfSize.y, halfSize.z), //4
center + new Vector3(-halfSize.x, halfSize.y, -halfSize.z), //5
center + new Vector3(-halfSize.x, -halfSize.y, halfSize.z), //6
center - halfSize, //7
};
int triangleOffset = worldVertices.Count;
Transform t = boxCollider.transform;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = t.TransformPoint(vertices[i]);
}
// add triangles and verts
worldVertices.AddRange(vertices);
}
///
/// Adds the vertices of a sphere collider to the world vertices list
///
/// sphere collider
/// world vertices list
private void AddWorldVerts(SphereCollider sphereCollider, List worldVertices)
{
AddWorldVertsSphere(sphereCollider.transform, sphereCollider.center, sphereCollider.radius, worldVertices);
}
///
/// Adds the vertices of a capsule collider to the world vertices list
///
/// capsule collider
/// world vertices list
private void AddWorldVerts(CapsuleCollider capsuleCollider, List worldVertices)
{
Vector3 top = Vector3.zero;
Vector3 bottom = Vector3.zero;
if (capsuleCollider.direction == 0) //x
{
top = capsuleCollider.center + Vector3.right * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
bottom = capsuleCollider.center - Vector3.right * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
}
else if (capsuleCollider.direction == 1) //y
{
top = capsuleCollider.center + Vector3.up * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
bottom = capsuleCollider.center - Vector3.up * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
}
else if (capsuleCollider.direction == 2) //z
{
top = capsuleCollider.center + Vector3.forward * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
bottom = capsuleCollider.center - Vector3.forward * ((capsuleCollider.height - capsuleCollider.radius * 2) / 2);
}
// Easiest to just add the full top and bottom spheres as the result is the same for any collider.
// as they contain the min and max values of collider, and the middle section is all on the same plane as the halfsphere's base for convex meshes.
// we could write a seperate method to only add the half-spheres, but there's no need.
// top sphere
AddWorldVertsSphere(capsuleCollider.transform, top, capsuleCollider.radius, worldVertices);
// bottom sphere
AddWorldVertsSphere(capsuleCollider.transform, bottom, capsuleCollider.radius, worldVertices);
}
///
/// Adds world space points around a sphere
///
/// transform of the collider
/// center of the sphere
/// radius of the sphere
///
private void AddWorldVertsSphere(Transform t, Vector3 center, float radius, List worldVertices)
{
int accuracy = ECEPreferences.MergeCollidersRoundnessAccuracy;
// 360 degrees in radians.
float sin, cos = sin = 0.0f;
for (int i = 1; i < accuracy; i++)
{
// center shifted to the z-axis
float h = (i / (float)accuracy) * radius * 2;
Vector3 centerX = center - (radius - (i / (float)accuracy) * radius * 2) * Vector3.right;
Vector3 centerY = center - (radius - (i / (float)accuracy) * radius * 2) * Vector3.up;
Vector3 centerZ = center - (radius - (i / (float)accuracy) * radius * 2) * Vector3.forward;
float newRadius = Mathf.Sqrt(radius * 2 * h - Mathf.Pow(h, 2));
for (int j = 0; j <= accuracy; j++)
{
float angleStep = ((j / (float)accuracy) * 360f) * Mathf.Deg2Rad;
sin = Mathf.Sin(angleStep);
cos = Mathf.Cos(angleStep);
// constant z.
float xZ = centerZ.x + newRadius * sin;
float yZ = centerZ.y + newRadius * cos;
// constant x.
float yX = centerX.y + newRadius * sin;
float zX = centerX.z + newRadius * cos;
// constant y.
float zY = centerY.z + (newRadius * sin);
float xY = centerY.x + (newRadius * cos);
//
worldVertices.Add(t.TransformPoint(new Vector3(centerX.x, yX, zX)));
worldVertices.Add(t.TransformPoint(new Vector3(xY, centerY.y, zY)));
worldVertices.Add(t.TransformPoint(new Vector3(xZ, yZ, centerZ.z)));
}
}
}
#endregion
#endif
#region ColliderDataCalculation
///
/// Calculates the best fit sphere for a series of points. Providing a larger list of points increases accuracy.
///
/// Local space vertices
/// The best fit sphere
private BestFitSphere CalculateBestFitSphere(List localVertices)
{
// # of points.
int n = localVertices.Count;
// Calculate average x, y, and z value of vertices.
float xAvg, yAvg, zAvg = xAvg = yAvg = 0.0f;
foreach (Vector3 vertex in localVertices)
{
xAvg += vertex.x;
yAvg += vertex.y;
zAvg += vertex.z;
}
xAvg = xAvg * (1.0f / n);
yAvg = yAvg * (1.0f / n);
zAvg = zAvg * (1.0f / n);
// Do some fun math with matrices
// B Vector.
Vector3 B = Vector3.zero;
// Can use a 4x4 as a 3x3 with the 4x4 as 0,0,0,1 in the last row/column.
Matrix4x4 AM = new Matrix4x4(Vector4.zero, Vector4.zero, Vector4.zero, new Vector4(0, 0, 0, 1));
float x2, y2, z2 = x2 = y2 = 0.0f;
foreach (Vector3 vertex in localVertices)
{
AM[0, 0] += 2 * (vertex.x * (vertex.x - xAvg)) / n;
AM[0, 1] += 2 * (vertex.x * (vertex.y - yAvg)) / n;
AM[0, 2] += 2 * (vertex.x * (vertex.z - zAvg)) / n;
AM[1, 0] += 2 * (vertex.y * (vertex.x - xAvg)) / n;
AM[1, 1] += 2 * (vertex.y * (vertex.y - yAvg)) / n;
AM[1, 2] += 2 * (vertex.y * (vertex.z - zAvg)) / n;
AM[2, 0] += 2 * (vertex.z * (vertex.x - xAvg)) / n;
AM[2, 1] += 2 * (vertex.z * (vertex.y - yAvg)) / n;
AM[2, 2] += 2 * (vertex.z * (vertex.z - zAvg)) / n;
x2 = vertex.x * vertex.x;
y2 = vertex.y * vertex.y;
z2 = vertex.z * vertex.z;
B.x += ((x2 + y2 + z2) * (vertex.x - xAvg)) / n;
B.y += ((x2 + y2 + z2) * (vertex.y - yAvg)) / n;
B.z += ((x2 + y2 + z2) * (vertex.z - zAvg)) / n;
}
// Calculate the center of the best-fit sphere.
Vector3 center = (AM.transpose * AM).inverse * AM.transpose * B;
// Calculate radius.
float radius = 0.0f;
foreach (Vector3 vertex in localVertices)
{
radius += Mathf.Pow((vertex.x - center.x), 2) + Mathf.Pow(vertex.y - center.y, 2) + Mathf.Pow(vertex.z - center.z, 2);
}
radius = Mathf.Sqrt(radius / localVertices.Count);
BestFitSphere bfs = new BestFitSphere(center, radius);
return bfs;
}
///
/// Calculates a box's data from the given values
///
/// list of vertices in world space
/// transform the box will be attached to
/// are we creating a rotated box?
/// Data appropriate variables set for a box collider
public BoxColliderData CalculateBox(List worldVertices, Transform attachTo, bool isRotated)
{
if (isRotated && worldVertices.Count < 3)
{
return new BoxColliderData();
}
else if (worldVertices.Count < 2)
{
return new BoxColliderData();
}
Quaternion q = Quaternion.identity;
Matrix4x4 m;
List localVertices = new List();
if (isRotated && worldVertices.Count >= 3)
{
// for rotated colliders we also re-calculate the to local even though the transform is changed.
// this better handles scale-shearing.
Vector3 forward = worldVertices[1] - worldVertices[0];
Vector3 up = Vector3.Cross(forward, worldVertices[2] - worldVertices[1]);
q = Quaternion.LookRotation(forward, up);
m = Matrix4x4.TRS(attachTo.position, q, Vector3.one);
for (int i = 0; i < worldVertices.Count; i++)
{
localVertices.Add(m.inverse.MultiplyPoint3x4(worldVertices[i]));
}
}
else
{
localVertices = ToLocalVerts(attachTo, worldVertices);
m = attachTo.localToWorldMatrix;
}
BoxColliderData data = CalculateBoxLocal(localVertices);
data.ColliderType = isRotated ? CREATE_COLLIDER_TYPE.ROTATED_BOX : CREATE_COLLIDER_TYPE.BOX;
data.Matrix = m;
return data;
}
///
/// Calculates box collider data for a list of local space vertices
///
/// list of local space vertices
/// box collider data with center and size set
public BoxColliderData CalculateBoxLocal(List vertices)
{
float xMin, yMin, zMin = xMin = yMin = Mathf.Infinity;
float xMax, yMax, zMax = xMax = yMax = -Mathf.Infinity;
foreach (Vector3 vertex in vertices)
{
//x min & max.
xMin = (vertex.x < xMin) ? vertex.x : xMin;
xMax = (vertex.x > xMax) ? vertex.x : xMax;
//y min & max
yMin = (vertex.y < yMin) ? vertex.y : yMin;
yMax = (vertex.y > yMax) ? vertex.y : yMax;
//z min & max
zMin = (vertex.z < zMin) ? vertex.z : zMin;
zMax = (vertex.z > zMax) ? vertex.z : zMax;
}
Vector3 max = new Vector3(xMax, yMax, zMax);
Vector3 min = new Vector3(xMin, yMin, zMin);
Vector3 size = max - min;
Vector3 center = (max + min) / 2;
// set data from calculated values
BoxColliderData data = new BoxColliderData();
data.Center = center;
data.ColliderType = CREATE_COLLIDER_TYPE.BOX;
data.IsValid = true;
data.Size = size;
return data;
}
///
/// Calculates a capsule's data from the given values using the best fit method
///
/// list of vertices in world space
/// transform the capsule will be attached to
/// are we creating a rotated capsule?
/// Data with appropriate variables set for a capsule collider
public CapsuleColliderData CalculateCapsuleBestFit(List worldVertices, Transform attachTo, bool isRotated)
{
if (worldVertices.Count >= 3)
{
Quaternion q = Quaternion.identity;
Matrix4x4 m;
List localVertices = new List();
if (isRotated)
{
// for rotated colliders we also re-calculate the to local even though the transform is changed.
// this better handles scale-shearing.
Vector3 forward = worldVertices[1] - worldVertices[0];
Vector3 up = Vector3.Cross(forward, worldVertices[2] - worldVertices[1]);
q = Quaternion.LookRotation(forward, up);
m = Matrix4x4.TRS(attachTo.position, q, Vector3.one);
for (int i = 0; i < worldVertices.Count; i++)
{
localVertices.Add(m.inverse.MultiplyPoint3x4(worldVertices[i]));
}
}
else
{
localVertices = ToLocalVerts(attachTo, worldVertices);
m = attachTo.localToWorldMatrix;
}
CapsuleColliderData data = CalculateCapsuleBestFitLocal(localVertices);
data.ColliderType = isRotated ? CREATE_COLLIDER_TYPE.ROTATED_CAPSULE : CREATE_COLLIDER_TYPE.CAPSULE;
data.Matrix = m;
return data;
}
return new CapsuleColliderData();
}
///
/// Calculates a best-fit capsule collider from a list of local space vertices
///
/// local space vertices
/// Capsule collider data with center, direction and height
public CapsuleColliderData CalculateCapsuleBestFitLocal(List localVertices)
{
if (localVertices.Count < 3)
{
Debug.LogWarning("EasyColliderCreator: Too few vertices passed to calculate a best fit capsule collider.");
return new CapsuleColliderData();
}
// height from first 2 verts selected.
Vector3 v0 = localVertices[0];
Vector3 v1 = localVertices[1];
float height = Vector3.Distance(v0, v1);
float dX = Mathf.Abs(v1.x - v0.x);
float dY = Mathf.Abs(v1.y - v0.y);
float dZ = Mathf.Abs(v1.z - v0.z);
localVertices.RemoveAt(1);
localVertices.RemoveAt(0);
BestFitSphere bfs = CalculateBestFitSphere(localVertices);
Vector3 center = bfs.Center;
int direction = 0;
if (dX > dY && dX > dZ)
{
direction = 0;
center.x = (v1.x + v0.x) / 2;
}
else if (dY > dX && dY > dZ)
{
direction = 1;
center.y = (v1.y + v0.y) / 2;
}
else
{
direction = 2;
center.z = (v1.z + v0.z) / 2;
}
CapsuleColliderData data = new CapsuleColliderData();
data.Center = center;
data.ColliderType = CREATE_COLLIDER_TYPE.CAPSULE;
data.Direction = direction;
data.Height = height;
data.IsValid = true;
data.Radius = bfs.Radius;
return data;
}
///
/// Calculates a capsule's data from the given values using the min max method
///
/// list of vertices in world space
/// transform the capsule will be attached to
/// method we are using to create the capsule (ie MinMaxPlusRadius)
/// are we creating a rotated capsule?
/// Data with appropriate variables set for a capsule collider
public CapsuleColliderData CalculateCapsuleMinMax(List worldVertices, Transform attachTo, CAPSULE_COLLIDER_METHOD method, bool isRotated)
{
if (isRotated && worldVertices.Count < 3)
{
return new CapsuleColliderData();
}
else if (worldVertices.Count < 2)
{
return new CapsuleColliderData();
}
List localVertices = new List();
Matrix4x4 m;
Quaternion q;
if (isRotated && worldVertices.Count >= 3)
{
Vector3 forward = worldVertices[1] - worldVertices[0];
Vector3 up = Vector3.Cross(forward, worldVertices[2] - worldVertices[1]);
q = Quaternion.LookRotation(forward, up);
m = Matrix4x4.TRS(attachTo.position, q, Vector3.one);
for (int i = 0; i < worldVertices.Count; i++)
{
localVertices.Add(m.inverse.MultiplyPoint3x4(worldVertices[i]));
}
}
else
{
localVertices = ToLocalVerts(attachTo.transform, worldVertices);
m = attachTo.localToWorldMatrix;
}
CapsuleColliderData data = CalculateCapsuleMinMaxLocal(localVertices, method);
data.ColliderType = isRotated ? CREATE_COLLIDER_TYPE.ROTATED_CAPSULE : CREATE_COLLIDER_TYPE.CAPSULE;
data.Matrix = m;
return data;
}
///
/// Calculates a capsule collider from a list of local space vertices
///
/// List of local space vertices
/// method to use when calculating (used to add radius or diameter to height of capsule)
/// Capsule collider data with center, direction, and height
public CapsuleColliderData CalculateCapsuleMinMaxLocal(List localVertices, CAPSULE_COLLIDER_METHOD method)
{
// calculate min and max points from vertices.
Vector3 min = new Vector3(Mathf.Infinity, Mathf.Infinity, Mathf.Infinity);
Vector3 max = new Vector3(-Mathf.Infinity, -Mathf.Infinity, -Mathf.Infinity);
foreach (Vector3 vertex in localVertices)
{
// Calc minimums
min.x = vertex.x < min.x ? vertex.x : min.x;
min.y = vertex.y < min.y ? vertex.y : min.y;
min.z = vertex.z < min.z ? vertex.z : min.z;
// Calc maximums
max.x = vertex.x > max.x ? vertex.x : max.x;
max.y = vertex.y > max.y ? vertex.y : max.y;
max.z = vertex.z > max.z ? vertex.z : max.z;
}
// Deltas for max-min
float dX = max.x - min.x;
float dY = max.y - min.y;
float dZ = max.z - min.z;
// center is between min and max values.
Vector3 center = (max + min) / 2;
int direction = 0;
float height = 0;
// set direction and height.
if (dX > dY && dX > dZ) // direction is x
{
direction = 0;
// height is the max difference in x.
height = dX;
}
else if (dY > dX && dY > dZ) // direction is y
{
direction = 1;
height = dY;
}
else // direction is z.
{
direction = 2;
height = dZ;
}
// Calculate radius, makes sure that all vertices are within the radius.
// Esentially to points on plane defined by direction axis, and find the furthest distance.
float maxRadius = -Mathf.Infinity;
Vector3 current = Vector3.zero;
foreach (Vector3 vertex in localVertices)
{
current = vertex;
if (direction == 0)
{
current.x = center.x;
}
else if (direction == 1)
{
current.y = center.y;
}
else if (direction == 2)
{
current.z = center.z;
}
float d = Vector3.Distance(current, center);
if (d > maxRadius)
{
maxRadius = d;
}
}
// method add radius / diameter
if (method == CAPSULE_COLLIDER_METHOD.MinMaxPlusRadius)
{
height += maxRadius;
}
else if (method == CAPSULE_COLLIDER_METHOD.MinMaxPlusDiameter)
{
height += maxRadius * 2;
}
CapsuleColliderData data = new CapsuleColliderData();
data.Center = center;
data.ColliderType = CREATE_COLLIDER_TYPE.CAPSULE;
data.Direction = direction;
data.Height = height;
data.IsValid = true;
data.Radius = maxRadius;
return data;
}
//TODO: Do a local-only method for cylinders.
///
/// Calculates the data needed to create a cylinder shaped convex mesh collider using a list of world space vertices
///
/// list of selected world space vertices
/// transform the collider will be attached to
/// number of sides on the cylinder
/// Data to create a a cylinder collider with type, convex mesh, validity, and matrix set
public MeshColliderData CalculateCylinderCollider(List worldVertices, Transform attachTo, int numberOfSides = 12)
{
MeshColliderData data = new MeshColliderData();
#if (UNITY_EDITOR)
List cylinderLocalPoints = CalculateLocalCylinderPoints(worldVertices, attachTo, ECEPreferences.CylinderNumberOfSides);
#else
List cylinderLocalPoints = CalculateLocalCylinderPoints(worldVertices, attachTo, numberOfSides);
#endif
// Mesh mesh = CreateMesh_QuickHull(cylinderLocalPoints, attachTo.gameObject, true);
EasyColliderQuickHull qh = EasyColliderQuickHull.CalculateHull(cylinderLocalPoints);
data.ColliderType = CREATE_COLLIDER_TYPE.CONVEX_MESH;
data.ConvexMesh = qh.Result;
if (qh.Result != null)
{
data.IsValid = true;
}
data.Matrix = attachTo.transform.localToWorldMatrix;
return data;
}
///
/// Calculates the mesh collider data for a cylinder shaped convex mesh collider using a list of local space vertices
///
/// list of local space vertices
/// number of sides on the cylinder
/// Mesh collider data with convex mesh set
public MeshColliderData CalculateCylinderColliderLocal(List vertices, int numberOfSides = 12)
{
MeshColliderData data = new MeshColliderData();
#if (UNITY_EDITOR)
List cylinderLocalPoints = CalculateLocalCylinderPoints(vertices, null, ECEPreferences.CylinderNumberOfSides);
#else
List cylinderLocalPoints = CalculateLocalCylinderPoints(vertices, null, numberOfSides);
#endif
EasyColliderQuickHull qh = EasyColliderQuickHull.CalculateHull(cylinderLocalPoints);
data.ColliderType = CREATE_COLLIDER_TYPE.CONVEX_MESH;
data.ConvexMesh = qh.Result;
if (qh.Result != null)
{
data.IsValid = true;
}
data.Matrix = new Matrix4x4();
return data;
}
///
/// Calculates mesh collider data for a list of world space vertices
///
/// list of world space vertices
/// transform the mesh collider will be attached to
/// Mesh collider data
public MeshColliderData CalculateMeshColliderQuickHull(List vertices, Transform attachTo)
{
List localVertices = ToLocalVerts(attachTo.transform, vertices);
MeshColliderData data = CalculateMeshColliderQuickHullLocal(localVertices);
data.Matrix = attachTo.localToWorldMatrix;
return data;
}
///
/// Calculates mesh collider data for a list of local space vertices
///
/// list of local space vertices
/// Mesh collider data with convex mesh set
public MeshColliderData CalculateMeshColliderQuickHullLocal(List localVertices)
{
MeshColliderData data = new MeshColliderData();
EasyColliderQuickHull qh = EasyColliderQuickHull.CalculateHull(localVertices);
data.ConvexMesh = qh.Result;
if (qh.Result != null)
{
data.IsValid = true;
}
return data;
}
///
/// Calculates a sphere using the best fit method
///
/// list of vertex positions in world space
/// transform sphere would be attached to
/// Data with appropriate variables set for a sphere collider
public SphereColliderData CalculateSphereBestFit(List worldVertices, Transform attachTo)
{
if (worldVertices.Count < 2)
{
return new SphereColliderData();
}
List localVertices = ToLocalVerts(attachTo, worldVertices);
// set data from values
SphereColliderData data = CalculateSphereBestFitLocal(localVertices);
data.Matrix = attachTo.localToWorldMatrix;
return data;
}
///
/// Calculates a best fit sphere collider using a list of local space vertices
///
/// list of local space vertices
/// Sphere collider data with center and radius set
public SphereColliderData CalculateSphereBestFitLocal(List localVertices)
{
BestFitSphere bfs = CalculateBestFitSphere(localVertices);
// set data from values
SphereColliderData data = new SphereColliderData();
data.Center = bfs.Center;
data.ColliderType = CREATE_COLLIDER_TYPE.SPHERE;
data.IsValid = true;
data.Radius = bfs.Radius;
return data;
}
// distance sphere is editor-only for now
///
/// Calculates a sphere using the distance method
///
/// list of vertex positions in world space
/// transform sphere would be attached to
/// Data with appropriate variables set for a sphere collider
public SphereColliderData CalculateSphereDistance(List worldVertices, Transform attachTo)
{
if (worldVertices.Count < 2)
{
return new SphereColliderData();
}
List localVertices = ToLocalVerts(attachTo, worldVertices);
// set data from values
SphereColliderData data = CalculateSphereDistanceLocal(localVertices);
data.Matrix = attachTo.localToWorldMatrix;
return data;
}
///
/// Calculates a sphere collider using a list of local space vertices
///
/// list of local space vertices
/// Sphere collider data with center and radius
public SphereColliderData CalculateSphereDistanceLocal(List localVertices)
{
// if calculations take to long, it switches to a faster less accurate algorithm using the mean.
bool switchToFasterAlgorithm = false;
#if (UNITY_EDITOR)
double startTime = EditorApplication.timeSinceStartup;
#else
double startTime = Time.realtimeSinceStartup;
#endif
double maxTime = 0.1f;
Vector3 distanceVert1 = Vector3.zero;
Vector3 distanceVert2 = Vector3.zero;
float maxDistance = -Mathf.Infinity;
float distance = 0;
for (int i = 0; i < localVertices.Count; i++)
{
for (int j = i + 1; j < localVertices.Count; j++)
{
distance = Vector3.Distance(localVertices[i], localVertices[j]);
if (distance > maxDistance)
{
maxDistance = distance;
distanceVert1 = localVertices[i];
distanceVert2 = localVertices[j];
}
}
#if (UNITY_EDITOR)
if (EditorApplication.timeSinceStartup - startTime > maxTime)
{
switchToFasterAlgorithm = true;
break;
}
#else
if (Time.realtimeSinceStartup - startTime > maxTime)
{
switchToFasterAlgorithm = true;
break;
}
#endif
}
if (switchToFasterAlgorithm)
{
// use a significantly faster algorithm that is less accurate for a large # of points.
Vector3 mean = Vector3.zero;
foreach (Vector3 vertex in localVertices)
{
mean += vertex;
}
mean = mean / localVertices.Count;
foreach (Vector3 vertex in localVertices)
{
distance = Vector3.Distance(vertex, mean);
if (distance > maxDistance)
{
distanceVert1 = vertex;
maxDistance = distance;
}
}
maxDistance = -Mathf.Infinity;
foreach (Vector3 vertex in localVertices)
{
distance = Vector3.Distance(vertex, distanceVert1);
if (distance > maxDistance)
{
maxDistance = distance;
distanceVert2 = vertex;
}
}
}
// set data from values
SphereColliderData data = new SphereColliderData();
data.Center = (distanceVert1 + distanceVert2) / 2;
data.ColliderType = CREATE_COLLIDER_TYPE.SPHERE;
data.IsValid = true;
data.Radius = maxDistance / 2;
return data;
}
///
/// Calculates a sphere using the min max method
///
/// list of vertex positions in world space
/// transform sphere would be attached to
/// Data with appropriate variables set for a sphere collider
public SphereColliderData CalculateSphereMinMax(List worldVertices, Transform attachTo)
{
if (worldVertices.Count < 2)
{
return new SphereColliderData();
}
// use local space verts.
List localVertices = ToLocalVerts(attachTo, worldVertices);
SphereColliderData data = CalculateSphereMinMaxLocal(localVertices);
data.Matrix = attachTo.localToWorldMatrix;
return data;
}
///
/// Calculates a sphere collider using a list of local space vertices
///
/// local space vertices
/// Sphere collider data with center and radius set
public SphereColliderData CalculateSphereMinMaxLocal(List localVertices)
{
float xMin, yMin, zMin = xMin = yMin = Mathf.Infinity;
float xMax, yMax, zMax = xMax = yMax = -Mathf.Infinity;
for (int i = 0; i < localVertices.Count; i++)
{
//x min & max.
xMin = (localVertices[i].x < xMin) ? localVertices[i].x : xMin;
xMax = (localVertices[i].x > xMax) ? localVertices[i].x : xMax;
//y min & max
yMin = (localVertices[i].y < yMin) ? localVertices[i].y : yMin;
yMax = (localVertices[i].y > yMax) ? localVertices[i].y : yMax;
//z min & max
zMin = (localVertices[i].z < zMin) ? localVertices[i].z : zMin;
zMax = (localVertices[i].z > zMax) ? localVertices[i].z : zMax;
}
// calculate center
Vector3 center = (new Vector3(xMin, yMin, zMin) + new Vector3(xMax, yMax, zMax)) / 2;
// calculate radius to contain all points
float maxDistance = 0.0f;
float distance = 0.0f;
foreach (Vector3 vertex in localVertices)
{
distance = Vector3.Distance(vertex, center);
if (distance > maxDistance)
{
maxDistance = distance;
}
}
SphereColliderData data = new SphereColliderData();
data.Center = center;
data.ColliderType = CREATE_COLLIDER_TYPE.SPHERE;
data.IsValid = true;
data.Radius = maxDistance;
return data;
}
#endregion
// editor only
#region CreateMeshColliders
#if (UNITY_EDITOR)
///
/// Create and saves a mesh with a minimum number of triangles that includes all selected vertices. Editor only.
///
/// Full path to save location including a base object name ie: "C:/UnityProjects/ProjectName/Assets/ConvexHulls/SaveNameBase"
/// Vertices to create the mesh with in world space
/// Gameobject the mesh will be attached to
/// The created mesh
public Mesh CreateMesh_Messy(List worldSpaceVertices, GameObject attachTo)
{
// use vertices to make a useable mesh that contains all the selected points.
// The mesh is only used to generate the convex hull
Mesh mesh = new Mesh();
// get all vertices in world space and convert to local space.
List localVertices = worldSpaceVertices.Select(vertex => attachTo.transform.InverseTransformPoint(vertex)).ToList();
while (localVertices.Count % 3 != 0)
{
localVertices.Add(localVertices[localVertices.Count % 3]);
}
// attempt to deal with degenerate triangles (so if user changes the mesh collider flags manually, no crashes will occur)
Vector3 p0, p1, p2 = p1 = p0 = Vector3.zero;
Vector3 s1, s2 = s1 = Vector3.zero;
List verts = new List();
int index = localVertices.Count - 1;
while (index >= 0) // need to make sure we include the last vertex.
{
p0 = localVertices[index];
p1 = localVertices[(index - 1 >= 0) ? index - 1 : localVertices.Count - 1];
p2 = localVertices[(index - 2 >= 0) ? index - 2 : localVertices.Count - 2];
s1 = (p0 - p1).normalized;
s2 = (p0 - p2).normalized;
int degenIndex = localVertices.Count; // so we can automatically re-use the last vertices if needed.
bool degenFixed = false;
while (s1 == s2 || -s1 == s2 || (s2 == Vector3.zero && s1 != Vector3.zero))
{
degenFixed = true;
degenIndex--;
if (degenIndex < 0)
{
Debug.LogError("Easy Collider Editor: Unable to generate a valid mesh collider from the selected points. This happens when all points are in a straight line.");
return null;
}
p2 = localVertices[degenIndex];
s2 = (p0 - p2).normalized;
}
// if we fixed a degenerate we still need to do the last vertex, so only move back 2 indexs' in that case.
index -= degenFixed ? 2 : 3;
verts.Add(p0);
verts.Add(p1);
verts.Add(p2);
}
int[] triangles = new int[verts.Count];
for (int i = 0; i < verts.Count; i++)
{
triangles[i] = i;
}
// mesh.vertices = vertices;
mesh.vertices = verts.ToArray();
mesh.triangles = triangles;
// the mesh has to be saved somewhere so it can actually be used (although this is still just optional)
try
{
EasyColliderSaving.CreateAndSaveMeshAsset(mesh, attachTo);
return mesh;
}
catch
{
Debug.LogError("EasyColliderEditor: Error saving mesh at path:" + EasyColliderSaving.GetValidConvexHullPath(attachTo));
return null;
}
}
///
/// Creates and saves (if set in preferences) a convex mesh collider using QuickHull. Editor only.
///
/// Local or world space vertices
/// Gameobject the collider will be attached to
/// are the vertices already in local space?
///
public Mesh CreateMesh_QuickHull(List vertices, GameObject attachTo, bool isLocal = false)
{
List localVerts = isLocal ? vertices : ToLocalVerts(attachTo.transform, vertices);
EasyColliderQuickHull qh = EasyColliderQuickHull.CalculateHull(localVerts);
if (ECEPreferences.SaveConvexHullAsAsset)
{
EasyColliderSaving.CreateAndSaveMeshAsset(qh.Result, attachTo);
}
return qh.Result;
}
#endif
#endregion
// creating colliders uses undos, the data itself can be used during runtime.
#region CreatePrimitiveColliders
///
/// Creates a Box collider
///
/// data to create box from
/// properties to set on collider
/// Created collider
private BoxCollider CreateBoxCollider(BoxColliderData data, EasyColliderProperties properties)
{
#if (UNITY_EDITOR)
BoxCollider boxCollider = Undo.AddComponent(properties.AttachTo);
#else
BoxCollider boxCollider = properties.AttachTo.AddComponent();
#endif
boxCollider.size = data.Size;
boxCollider.center = data.Center;
SetPropertiesOnCollider(boxCollider, properties);
return boxCollider;
}
///
/// Creates a box collider by calculating the min and max x, y, and z.
///
/// List of world space vertices
/// Properties of collider
///
public BoxCollider CreateBoxCollider(List worldVertices, EasyColliderProperties properties)
{
if (worldVertices.Count >= 2)
{
BoxColliderData data;
if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED)
{
if (worldVertices.Count >= 3)
{
GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Box Collider");
// still want to recalculate using the transform matrix, as it better handles uneven scale / shearing across multiple children
if (obj != null)
{
obj.layer = properties.Layer;
properties.AttachTo = obj;
}
data = CalculateBox(worldVertices, properties.AttachTo.transform, true);
}
else
{
Debug.LogWarning("Easy Collider Editor: Creating a Rotated Box Collider requires at least 3 points to be selected.");
return null;
}
}
else
{
data = CalculateBox(worldVertices, properties.AttachTo.transform, false);
}
return CreateBoxCollider(data, properties);
}
return null;
}
///
/// Creates a capsule collider (editor undoable)
///
/// data to create capsule from
/// properties to set on collider
/// created capsule collider
private CapsuleCollider CreateCapsuleCollider(CapsuleColliderData data, EasyColliderProperties properties)
{
#if (UNITY_EDITOR)
CapsuleCollider capsuleCollider = Undo.AddComponent(properties.AttachTo);
#else
CapsuleCollider capsuleCollider = properties.AttachTo.AddComponent();
#endif
capsuleCollider.direction = data.Direction;
capsuleCollider.height = data.Height;
capsuleCollider.center = data.Center;
capsuleCollider.radius = data.Radius;
// set properties
SetPropertiesOnCollider(capsuleCollider, properties);
return capsuleCollider;
}
///
/// Creates a capsule collider using the height from first 2 vertices, and then getting radius from the best fit sphere algorithm.
///
/// List of world vertices
/// Properties of collider
///
public CapsuleCollider CreateCapsuleCollider_BestFit(List worldVertices, EasyColliderProperties properties)
{
if (worldVertices.Count >= 3)
{
CapsuleColliderData data = new CapsuleColliderData();
if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED)
{
GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Capsule Collider");
if (obj != null)
{
properties.AttachTo = obj;
obj.layer = properties.Layer;
}
data = CalculateCapsuleBestFit(worldVertices, properties.AttachTo.transform, true);
}
else
{
data = CalculateCapsuleBestFit(worldVertices, properties.AttachTo.transform, false);
}
return CreateCapsuleCollider(data, properties);
}
return null;
}
///
/// Creates a capsule collider using the Min-Max method
///
/// List of world space vertices
/// Properties to set on collider
/// Min-Max method to use to add radius' to height.
///
public CapsuleCollider CreateCapsuleCollider_MinMax(List worldVertices, EasyColliderProperties properties, CAPSULE_COLLIDER_METHOD method)
{
CapsuleColliderData data;
if (properties.Orientation == COLLIDER_ORIENTATION.ROTATED && worldVertices.Count >= 3)
{
GameObject obj = CreateGameObjectOrientation(worldVertices, properties.AttachTo, "Rotated Capsule Collider");
if (obj != null)
{
properties.AttachTo = obj;
obj.layer = properties.AttachTo.layer;
}
data = CalculateCapsuleMinMax(worldVertices, properties.AttachTo.transform, method, true);
}
else
{
data = CalculateCapsuleMinMax(worldVertices, properties.AttachTo.transform, method, false);
}
return CreateCapsuleCollider(data, properties);
}
///
/// Creates a convex mesh collider component from the mesh using all cooking options, so mesh does not have to be "valid"
///
/// Mesh to make a convex hull from
/// Gameobject the convex hull will be attached to
/// Parameters to set on created collider
public MeshCollider CreateConvexMeshCollider(Mesh mesh, GameObject attachToObject, EasyColliderProperties properties)
{
// Create a mesh collider
#if (UNITY_EDITOR)
MeshCollider createdCollider = Undo.AddComponent(attachToObject);
#else
MeshCollider createdCollider = attachToObject.AddComponent();
#endif
createdCollider.sharedMesh = mesh;
// Auto inflate mesh to the minimum amount
#if UNITY_2018_3_OR_NEWER
createdCollider.cookingOptions = MeshColliderCookingOptions.CookForFasterSimulation | MeshColliderCookingOptions.EnableMeshCleaning | MeshColliderCookingOptions.WeldColocatedVertices;
#elif UNITY_2017_3_OR_NEWER
createdCollider.cookingOptions = MeshColliderCookingOptions.CookForFasterSimulation | MeshColliderCookingOptions.InflateConvexMesh | MeshColliderCookingOptions.EnableMeshCleaning | MeshColliderCookingOptions.WeldColocatedVertices;
createdCollider.skinWidth = 0.000001f;
#else
createdCollider.inflateMesh = true;
createdCollider.skinWidth = 0.000001f;
#endif
// Would be nice if we could do a try/catch on the baking to only inflate if we have to, but that doesn't work.
createdCollider.convex = true;
SetPropertiesOnCollider(createdCollider, properties);
return createdCollider;
}
///
/// Creates a sphere collider, editor undo-able
///
/// data to create the sphere collider from
/// properties to set on the collider
/// the created sphere collider
private SphereCollider CreateSphereCollider(SphereColliderData data, EasyColliderProperties properties)
{
#if (UNITY_EDITOR)
SphereCollider sphereCollider = Undo.AddComponent(properties.AttachTo);
#else
SphereCollider sphereCollider = properties.AttachTo.AddComponent();
#endif
sphereCollider.radius = data.Radius;
sphereCollider.center = data.Center;
SetPropertiesOnCollider(sphereCollider, properties);
return sphereCollider;
}
///
/// Creates a sphere collider using the best fit sphere algorithm.
///
/// List of world space vertices
/// Properties of collider
///
public SphereCollider CreateSphereCollider_BestFit(List worldVertices, EasyColliderProperties properties)
{
if (worldVertices.Count >= 2)
{
// Convert to local space.
SphereColliderData data = CalculateSphereBestFit(worldVertices, properties.AttachTo.transform);
return CreateSphereCollider(data, properties);
}
return null;
}
///
/// Creates a Sphere Collider by finding the 2 points with a maximum distance between them.
///
/// List of world space vertices
/// Properties of collider
///
public SphereCollider CreateSphereCollider_Distance(List worldVertices, EasyColliderProperties properties)
{
if (worldVertices.Count >= 2)
{
SphereColliderData data = CalculateSphereDistance(worldVertices, properties.AttachTo.transform);
return CreateSphereCollider(data, properties);
}
return null;
}
///
/// Creates a sphere collider by calculating the min and max in x, y, and z.
///
/// List of world space vertices
/// Properties of collider
///
public SphereCollider CreateSphereCollider_MinMax(List worldVertices, EasyColliderProperties properties)
{
if (worldVertices.Count >= 2)
{
SphereColliderData data = CalculateSphereMinMax(worldVertices, properties.AttachTo.transform);
return CreateSphereCollider(data, properties);
}
return null;
}
#endregion
#region OtherHelperMethods
///
/// Calculates points around for a cylinder shaped convex-mesh collider.
///
/// vertices currently selected
/// Transform the collider will be attached, null if verts are already local
/// number of subdivisons on the circle. Clamped between 3 and 64.
/// List of local space points for creating a cylinder with quickhull
private List CalculateLocalCylinderPoints(List vertices, Transform attachTo, int numberOfSides)
{
// convex mesh colliders can cause errors with > 256 verts or triangles in some version of unity
// so max number of sides would be roughly 256 = 2(n-2) + 2n, n = 64 which works out pretty well.
// clamp to min/max number of sides.
numberOfSides = Mathf.Clamp(numberOfSides, 3, 64);
// calculate the data for a capsule, min max + diameter means the cylinder section will contain
// all of the vertices passed in, perfect!
CapsuleColliderData capsuleData;
if (attachTo != null)
{
capsuleData = CalculateCapsuleMinMax(vertices, attachTo, CAPSULE_COLLIDER_METHOD.MinMaxPlusDiameter, false);
}
else
{
capsuleData = CalculateCapsuleMinMaxLocal(vertices, CAPSULE_COLLIDER_METHOD.MinMaxPlusDiameter);
}
return CalculateLocalCylinderPoints(capsuleData, numberOfSides);
}
///
/// Calculates points around for a cylinder shaped convex-mesh collider.
///
/// capsule data to create the poitns from
/// number of sides on the cylinder
/// list of local space points to create a cylinder collider with
private List CalculateLocalCylinderPoints(CapsuleColliderData capsuleData, int numberOfSides)
{
List localPoints = new List();
// angle increase for each calculation.
float angleIncrement = 360f / numberOfSides;
// offset points for our top and bottom circle.
Vector3 top, bottom = top = capsuleData.Center;
// height includes the half spheres so subtract the radius.
if (capsuleData.Direction == 0)
{
top.x += (capsuleData.Height / 2) - capsuleData.Radius;
bottom.x -= (capsuleData.Height / 2) - capsuleData.Radius;
}
else if (capsuleData.Direction == 1)
{
top.y += (capsuleData.Height / 2) - capsuleData.Radius;
bottom.y -= (capsuleData.Height / 2) - capsuleData.Radius;
}
else if (capsuleData.Direction == 2)
{
top.z += (capsuleData.Height / 2) - capsuleData.Radius;
bottom.z -= (capsuleData.Height / 2) - capsuleData.Radius;
}
for (float a = 0; a < 360f; a += angleIncrement)
{
// doesn't really matter if b or c is used for x or y or z, as long as both are not used
float b = capsuleData.Radius * Mathf.Sin(a * Mathf.Deg2Rad);
float c = capsuleData.Radius * Mathf.Cos(a * Mathf.Deg2Rad);
if (capsuleData.Direction == 0)
{
// position + offset by center
top.y = b + capsuleData.Center.y;
bottom.y = b + capsuleData.Center.y;
top.z = c + capsuleData.Center.z;
bottom.z = c + capsuleData.Center.z;
}
else if (capsuleData.Direction == 1)
{
top.x = b + capsuleData.Center.x;
bottom.x = b + capsuleData.Center.x;
top.z = c + capsuleData.Center.z;
bottom.z = c + capsuleData.Center.z;
}
else if (capsuleData.Direction == 2)
{
top.x = b + capsuleData.Center.x;
bottom.x = b + capsuleData.Center.x;
top.y = c + capsuleData.Center.y;
bottom.y = c + capsuleData.Center.y;
}
localPoints.Add(top);
localPoints.Add(bottom);
}
return localPoints;
}
///
/// Creates a gameobject attach to parent with it's local position at zero, and it's up direction oriented in the direction of the first 2 world vertices.
///
/// List of world space vertices
/// Parent to attach gameobject to
/// Name of gameobject to create
///
private GameObject CreateGameObjectOrientation(List worldVertices, GameObject parent, string name)
{
GameObject obj = new GameObject(name);
if (worldVertices.Count >= 3)
{
// calculate forward and up.
Vector3 forward = worldVertices[1] - worldVertices[0];
Vector3 up = Vector3.Cross(forward, worldVertices[2] - worldVertices[1]);
obj.transform.rotation = Quaternion.LookRotation(forward, up);
obj.transform.SetParent(parent.transform);
obj.transform.localPosition = Vector3.zero;
#if (UNITY_EDITOR) // undo for unity editor.
Undo.RegisterCreatedObjectUndo(obj, "Create Rotated GameObject");
#endif
return obj;
}
return null;
}
///
/// Just a helper method to draw a point in world space
///
///
///
private void DebugDrawPoint(Vector3 worldLoc, Color color)
{
Debug.DrawLine(worldLoc - Vector3.up * 0.01f, worldLoc + Vector3.up * 0.01f, color, 10f, false);
Debug.DrawLine(worldLoc - Vector3.left * 0.01f, worldLoc + Vector3.left * 0.01f, color, 10f, false);
Debug.DrawLine(worldLoc - Vector3.forward * 0.01f, worldLoc + Vector3.forward * 0.01f, color, 10f, false);
}
///
/// Sets the collider properties isTrigger and physicMaterial.
///
/// Collider to set properties on
/// Properties object with the properties you want to set
private void SetPropertiesOnCollider(Collider collider, EasyColliderProperties properties)
{
if (collider != null)
{
collider.isTrigger = properties.IsTrigger;
collider.sharedMaterial = properties.PhysicMaterial;
}
}
///
/// Converts the list of world vertices to local positions
///
/// Transform to use for local space
/// World space position of vertices
/// Localspace position w.r.t transform of worldVertices
private List ToLocalVerts(Transform transform, List worldVertices)
{
List localVerts = new List(worldVertices.Count);
foreach (Vector3 v in worldVertices)
{
localVerts.Add(transform.InverseTransformPoint(v));
}
return localVerts;
}
#endregion
}
}