2026-04-30 그린빈 추가
This commit is contained in:
478
Assets/EasyColliderEditor/Scripts/EasyColliderVHACD.cs
Normal file
478
Assets/EasyColliderEditor/Scripts/EasyColliderVHACD.cs
Normal file
@@ -0,0 +1,478 @@
|
||||
#if (UNITY_EDITOR)
|
||||
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
|
||||
Reference in New Issue
Block a user