Files
Cielonos/Assets/OtherPlugins/EasyColliderEditor/Scripts/EasyColliderVHACD.cs
SoulliesOfficial ef7b479712 initial
2025-11-25 08:19:33 -05:00

478 lines
18 KiB
C#

#if (UNITY_EDITOR && !UNITY_EDITOR_LINUX)
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using System;
using System.Linq;
namespace ECE
{
//Future potential features:
// The option to covert the convex hulls generated into other colliders, like box colliders (boxelization is a fun term).
// So that we can leverage VHACD results into alternative basic colliders.
// New version of vhacd is out but in my testing it is much slower than the older performance branch version
// additionally, it offers less adjustable parameters, which makes it harder to get a good fit,
// even though the fit isn't perfect with any vhacd implementation since it's approximate convex decomposition
// and even though the current parameters aren't particularily understandable, you can adjust them and see the output quickly until something is close enough.
public class EasyColliderVHACD
{
// If you're getting an error about DLL not found exception, you'll likely be pointed to this line by the error
// near the top of this file you'll find " const string dllName = "ECE_VHACD"; " ,
// this is where the dll name is placed (these same comments are there as well)
// To fix this on Mac: sometimes it can not recognize the .bundle, try either "ECE_VHACD.bundle" or "ECE_VHACD" as the dllName string
// Occasionally this can also happen when the dll/bundles are updated, and imported into unity after they were already used
// to correctly update when vhacd is updated, be sure to close unity, and immediately update the asset on opening the project
// currently only supports windows, and OSX (arm / silicon and intel) versions of unity.
const string dllName = "ECE_VHACD";
[DllImport(dllName, EntryPoint = "GetMaxNumVerticesPerCH")]
private static extern uint GetMaxNumVerticesPerCH();
// extern "C" VHACD_API double* GetConvexHullCenter();
[DllImport(dllName, EntryPoint = "GetConvexHullCenter")]
private static extern IntPtr GetConvexHullCenter();
// extern "C" VHACD_API unsigned int* GetConvexHullTriangles();
[DllImport(dllName, EntryPoint = "GetConvexHullTriangles")]
private static extern IntPtr GetConvexHullTriangles();
// extern "C" VHACD_API double* GetConvexHullPoints();
[DllImport(dllName, EntryPoint = "GetConvexHullPoints")]
private static extern IntPtr GetConvexHullPoints();
// extern "C" VHACD_API void Compute();
[DllImport(dllName, EntryPoint = "Compute")]
private static extern void Compute();
// extern "C" VHACD_API bool Create();
[DllImport(dllName, EntryPoint = "Create")]
private static extern bool Create();
// extern "C" VHACD_API bool Create_ASYNC();
[DllImport(dllName, EntryPoint = "Create_ASYNC")]
private static extern bool Create_ASYNC();
// extern "C" VHACD_API void Destroy();
[DllImport(dllName, EntryPoint = "Destroy")]
private static extern void Destroy();
// extern "C" VHACD_API int GetConvexHullNumPoints();
[DllImport(dllName, EntryPoint = "GetConvexHullNumPoints")]
private static extern int GetConvexHullNumPoints();
// extern "C" VHACD_API int GetConvexHullNumTriangles();
[DllImport(dllName, EntryPoint = "GetConvexHullNumTriangles")]
private static extern int GetConvexHullNumTriangles();
// extern "C" VHACD_API double GetConvexHullPoint(int index);
[DllImport(dllName, EntryPoint = "GetConvexHullPoint")]
private static extern double GetConvexHullPoint(int index);
// extern "C" VHACD_API int GetConvexHullTriangle(int index);
[DllImport(dllName, EntryPoint = "GetConvexHullTriangle")]
private static extern int GetConvexHullTriangle(int index);
// extern "C" VHACD_API double GetConvexHullVolume();
[DllImport(dllName, EntryPoint = "GetConvexHullVolume")]
private static extern double GetConvexHullVolume();
// extern "C" VHACD_API int GetNumberOfConvexHulls();
[DllImport(dllName, EntryPoint = "GetNumberOfConvexHulls")]
private static extern int GetNumberOfConvexHulls();
// extern "C" VHACD_API int GetPointSize();
[DllImport(dllName, EntryPoint = "GetPointSize")]
private static extern int GetPointSize();
// extern "C" VHACD_API int GetPointSize();
[DllImport(dllName, EntryPoint = "IsReady")]
private static extern bool IsReady();
// extern "C" VHACD_API void SetConvexHull(int index);
[DllImport(dllName, EntryPoint = "SetConvexHull")]
private static extern void SetConvexHull(int index);
// extern "C" VHACD_API void SetMaxHulls(int value);
[DllImport(dllName, EntryPoint = "SetMaxHulls")]
private static extern void SetMaxHulls(int value);
// extern "C" VHACD_API void SetMaxVerticesPerHull(int value);
[DllImport(dllName, EntryPoint = "SetMaxVerticesPerHull")]
private static extern void SetMaxVerticesPerHull(int value);
// For branch PERFORMANCE ENHANCEMENTS
// extern "C" VHACD_API void SetParameters(
// double concavity,
// double alpha,
// double beta,
// double minVolumePerConvexHull,
// int resolution,
// int maxNumVerticesPerCH,
// int planeDownsampling,
// int convexhullDownsampling,
// int maxConvexHulls,
// bool projectHullVertices,
// unsigned int fillMode);
[DllImport(dllName, EntryPoint = "SetParameters")]
private static extern void SetParameters(
double concavity,
double alpha,
double beta,
double minVolumePerConvexHull,
int resolution,
int maxNumVerticesPerCH,
int planeDownsampling,
int convexhullDownsampling,
int maxConvexHulls,
bool projectHullVertices,
uint fillMode
);
// extern "C" VHACD_API void SetPoint(int index, float value);
[DllImport(dllName, EntryPoint = "SetPoint")]
private static extern void SetPoint(int index, float value);
// extern "C" VHACD_API void SetPoints(float pointsArr[], int size);
[DllImport(dllName, EntryPoint = "SetPoints")]
private static extern void SetPoints(float[] pointsArr, int size);
// extern "C" VHACD_API void SetPointSize(int size);
[DllImport(dllName, EntryPoint = "SetPointSize")]
private static extern void SetPointSize(int size);
// extern "C" VHACD_API void SetResolution(int value);
[DllImport(dllName, EntryPoint = "SetResolution")]
private static extern void SetResolution(int value);
// extern "C" VHACD_API void SetTriangle(int index, int value);
[DllImport(dllName, EntryPoint = "SetTriangle")]
private static extern void SetTriangle(int index, int value);
// extern "C" VHACD_API void SetTriangles(int trianglesArr[], int size);
[DllImport(dllName, EntryPoint = "SetTriangles")]
private static extern void SetTriangles(int[] trianglesArr, int size);
// extern "C" VHACD_API void SetTriangleSize(int size);
[DllImport(dllName, EntryPoint = "SetTriangleSize")]
private static extern void SetTriangleSize(int size);
/// <summary>
/// Is an instance of vhacd initialized?
/// </summary>
/// this is a fix for the new version of vhacd which is not used yet for various reasons.
private bool isInitialized = false;
/// <summary>
/// Initializes a VHACD instance
/// </summary>
/// <param name="async">Use async process?</param>
/// <returns></returns>
public bool Init(bool async = true)
{
Clean();
if (async)
{
// fixes an issue in new vhacd without changing the api.
isInitialized = true;
// If you're getting an error about DLL not found exception, you'll likely be pointed to this line by the error
// near the top of this file you'll find " const string dllName = "ECE_VHACD"; " ,
// this is where the dll name is placed (these same comments are there as well)
// To fix this on Mac: sometimes it can not recognize the .bundle, try either "ECE_VHACD.bundle" or "ECE_VHACD" as the dllName string
// Occasionally this can also happen when the dll/bundles are updated, and imported into unity after they were already used
// to correctly update when vhacd is updated, be sure to close unity, and immediately update the asset on opening the project
// currently only supports windows, and OSX (arm / silicon and intel) versions of unity.
return Create_ASYNC();
}
else
{
isInitialized = true;
return Create();
}
}
/// <summary>
/// Destroys the VHACD instance, if it is already initialized.
/// </summary>
/// <returns>true if destroyed, false if not destroyed(because it wasn't initialized yet)</returns>
public bool Clean()
{
// fixes issue where vhacd isn't initialized and tries to destroy itself (in new vhacd)
if (isInitialized)
{
// Debug.Log("Clean" + dllName);
isInitialized = false;
Destroy();
return true;
}
else
{
return false;
}
}
/// <summary>
/// Gets all calculated convex hulls and turn them into meshes.
/// </summary>
/// <returns>convex hull meshes</returns>
public Mesh[] CreateConvexHullMeshes()
{
int numHulls = GetNumberOfConvexHulls();
Mesh[] meshes = new Mesh[numHulls];
for (int i = 0; i < numHulls; i++)
{
// get the current convex hull
SetConvexHull(i);
// get the ch data
int pointCount = GetConvexHullNumPoints();
int triangleCount = GetConvexHullNumTriangles();
// create new vertex and triangles array.
Vector3[] vertices = new Vector3[pointCount];
int[] triangles = new int[triangleCount * 3];
for (int j = 0; j < triangleCount * 3; j++)
{
triangles[j] = GetConvexHullTriangle(j);
}
// assing each point to a vertex in the array
Vector3 point = Vector3.zero;
for (int j = 0; j < pointCount * 3; j += 3)
{
// note that in VHACD, each vertex is not a vector.
// instead each vertex is simply the 3 values in order.
point.x = (float)GetConvexHullPoint(j);
point.y = (float)GetConvexHullPoint(j + 1);
point.z = (float)GetConvexHullPoint(j + 2);
vertices[j / 3] = point;
}
// create and save the mesh.
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
// be sure to add it to our meshes array
meshes[i] = mesh;
}
//return the array of meshes so they can be added as convex mesh colliders.
return meshes;
}
/// <summary>
/// Gets the number of convex hulls calcalated
/// </summary>
/// <returns># convex hulls computed</returns>
public int GetConvexHullCount()
{
return GetNumberOfConvexHulls();
}
/// <summary>
/// Checks if the async computation of convex hulls is complete
/// </summary>
/// <returns>true if finished</returns>
public bool IsComputeFinished()
{
return IsReady();
}
/// <summary>
/// Checks to see if each convex hull is under 256 vertices and 256 triangles.
/// </summary>
/// <returns>True if under limits</returns>
public bool IsValid()
{
// calculate max triangle count and max vertex count
int maxTriangleCount = 0;
int maxVertexCount = 0;
int numHulls = GetConvexHullCount();
for (int i = 0; i < numHulls; i++)
{
SetConvexHull(i);
int currentTriCount = GetConvexHullNumTriangles();
int currentVertCount = GetConvexHullNumPoints();
if (currentTriCount > maxTriangleCount)
{
maxTriangleCount = currentTriCount;
}
if (currentVertCount > maxVertexCount)
{
maxVertexCount = currentVertCount;
}
}
// if both are under 256, unity will generate no errors.
// (these errors are hidden in some older versions of unity.)
if (maxVertexCount < 256 && maxTriangleCount < 256)
{
return true;
}
return false;
}
/// <summary>
/// Sets the vertices and triangles array for VHACD from mesh
/// </summary>
/// <param name="meshFilter">mesh filter of the source mesh</param>
/// <param name="attachTo">transform the mesh collider will be attached to</param>
/// <returns>true if preparation succeeds, false otherwise</returns>
public bool PrepareMeshData(MeshFilter meshFilter, Transform attachTo, Mesh mesh)
{
if (mesh == null) return false;
// convert from meshes' object to world space, to attach to's local space, then calculate, then add.
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
List<Vector3> localAttachVertices = new List<Vector3>();
if (meshFilter != null && meshFilter.transform != attachTo && meshFilter.sharedMesh != null && meshFilter.sharedMesh == mesh)
{
foreach (Vector3 v in vertices)
{
// transform from mesh filters local space, to world space, to the attach to's local space.
localAttachVertices.Add(attachTo.transform.InverseTransformPoint(meshFilter.transform.TransformPoint(v)));
}
}
else
{
localAttachVertices = vertices.ToList();
}
// could have used an intptr but this way is fast enough
// and we can let VHACD take care of itself.
SetPointSize(localAttachVertices.Count * 3);
SetTriangleSize(triangles.Length);
for (int i = 0; i < localAttachVertices.Count; i++)
{
SetPoint(i * 3, localAttachVertices[i].x);
SetPoint(i * 3 + 1, localAttachVertices[i].y);
SetPoint(i * 3 + 2, localAttachVertices[i].z);
}
for (int i = 0; i < triangles.Length; i++)
{
SetTriangle(i, triangles[i]);
}
return true;
}
/// <summary>
/// prepares an array of meshfilters for a single convex hull decomposition.
/// </summary>
/// <param name="meshFilters">array of meshfilters</param>
public bool PrepareMeshData(List<MeshFilter> meshFilters, Transform attachTo)
{
// convert vertices from the mesh filters local space, to world space, to the attach to's local space.
int vertexCount = 0;
int triangleCount = 0;
List<Vector3> localAttachVertices = new List<Vector3>();
List<int> triangles = new List<int>();
foreach (MeshFilter mf in meshFilters)
{
// skip any mesh filters that were deleted, or had their shared mesh changed to null
if (mf == null || mf.sharedMesh == null) continue;
// convert to local vertices of the attach to object.
Vector3[] verts = mf.sharedMesh.vertices;
foreach (Vector3 v in verts)
{
localAttachVertices.Add(attachTo.transform.InverseTransformPoint(mf.transform.TransformPoint(v)));
}
// get current shared mesh triangles
int[] tris = mf.sharedMesh.triangles;
for (int i = 0; i < tris.Length; i++)
{
// add the vertex index, plus the offset of the total running vertex count.
triangles.Add(tris[i] + vertexCount);
}
vertexCount += mf.sharedMesh.vertices.Length;
triangleCount += mf.sharedMesh.triangles.Length;
}
// could have used intptr etc. to pass all the data at once
// but this way is still fast enough, and we can just let VHACD handle it's own stuff.
SetPointSize(vertexCount * 3);
SetTriangleSize(triangleCount);
for (int i = 0; i < localAttachVertices.Count; i++)
{
SetPoint(i * 3, localAttachVertices[i].x);
SetPoint(i * 3 + 1, localAttachVertices[i].y);
SetPoint(i * 3 + 2, localAttachVertices[i].z);
}
for (int i = 0; i < triangles.Count; i++)
{
SetTriangle(i, triangles[i]);
}
return true;
}
/// <summary>
/// Recalculates the current convex hulls based on max triangles and vertices with an aim to get max triangles below 256
/// </summary>
/// <returns></returns>
public bool RecomputeVHACD()
{
int maxTriangleCount = 0;
int maxVertexCount = 0;
int numHulls = GetConvexHullCount();
// calculate max triangles and vertices from convex hulls.
for (int i = 0; i < numHulls; i++)
{
SetConvexHull(i);
int currentTriCount = GetConvexHullNumTriangles();
int currentVertCount = GetConvexHullNumPoints();
if (currentTriCount > maxTriangleCount)
{
maxTriangleCount = currentTriCount;
}
if (currentVertCount > maxVertexCount)
{
maxVertexCount = currentVertCount;
}
}
// reduce the max number of vertices.
float trisPerVertMax = (float)maxTriangleCount / maxVertexCount;
int maxVerticesPerConvexHull = (int)(255 / trisPerVertMax);
// set the new max number of vertices
SetMaxVerticesPerHull(maxVerticesPerConvexHull);
// compute again.
Compute();
return true;
}
/// <summary>
/// Calls compute method on the current VHACD instance
/// </summary>
/// <returns></returns>
public bool RunVHACD()
{
Compute();
return true;
}
/// <summary>
/// Sets parameters on the current VHACD instance
/// </summary>
/// <param name="parameters">parameters to set</param>
public bool SetParameters(VHACDParameters parameters)
{
SetParameters(
parameters.concavity,
parameters.alpha,
parameters.beta,
parameters.minVolumePerCH,
parameters.resolution,
parameters.maxNumVerticesPerConvexHull,
parameters.planeDownsampling,
parameters.convexhullDownSampling,
parameters.maxConvexHulls,
parameters.projectHullVertices,
(uint)parameters.fillMode
);
return true;
}
}
/// <summary>
/// Fill mode for VHACD
/// </summary>
public enum VHACD_FILL_MODE
{
FLOOD_FILL,
SURFACE_ONLY,
RAYCAST_FILL,
}
}
#endif