#if (UNITY_EDITOR && !UNITY_EDITOR_LINUX) using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; using System; using System.IO; using UnityEditor; 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. 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 #IF UNITY_EDITOR_WIN and OSX, 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 "VHACD_OSX.bundle" or "VHACD_OSX" 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 #if UNITY_EDITOR_WIN const string dllName = "VHACD"; // windows dll name #elif UNITY_EDITOR_OSX // mac bundle name, sometimes unity can find it as just VHACD_OSX, and sometimes it only works if its VHACD_OSX.bundle const string dllName = "VHACD_OSX"; #endif [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); /// /// Initializes a VHACD instance /// /// Use async process? /// public bool Init(bool async = true) { if (async) { // 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 #IF UNITY_EDITOR_WIN and OSX, 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 "VHACD_OSX.bundle" or "VHACD_OSX" 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 return Create_ASYNC(); } else { return Create(); } } /// /// Destroys the VHACD instance /// /// public bool Clean() { Destroy(); return true; } /// /// Saves all currently computed convex hulls to path /// /// path to save /// convex hull meshes 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; } /// /// Gets the number of convex hulls calcalated /// /// # convex hulls computed public int GetConvexHullCount() { return GetNumberOfConvexHulls(); } /// /// Checks if the async computation of convex hulls is complete /// /// true if finished public bool IsComputeFinished() { return IsReady(); } /// /// Checks to see if each convex hull is under 256 vertices and 256 triangles. /// /// True if under limits 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; } /// /// Sets the vertices and triangles array for VHACD from mesh /// /// mesh filter of the source mesh /// transform the mesh collider will be attached to /// true if preparation succeeds, false otherwise 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 localAttachVertices = new List(); 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; } /// /// prepares an array of meshfilters for a single convex hull decomposition. /// /// array of meshfilters public bool PrepareMeshData(List 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 localAttachVertices = new List(); List triangles = new List(); 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; } /// /// Recalculates the current convex hulls based on max triangles and vertices with an aim to get max triangles below 256 /// /// 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; } /// /// Calls compute method on the current VHACD instance /// /// public bool RunVHACD() { Compute(); return true; } /// /// Sets parameters on the current VHACD instance /// /// parameters to set 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; } } /// /// Fill mode for VHACD /// public enum VHACD_FILL_MODE { FLOOD_FILL, SURFACE_ONLY, RAYCAST_FILL, } [System.Serializable] public class VHACDParameters { /// /// is the current calculation for displaying a preview? /// public bool IsCalculationForPreview; /// /// the suffix to add to saved convex hulls: objectName_suffix_01 etc. /// public string SaveSuffix = "_ConvexHull_"; /// /// Method to use to attach resulting convex hulls to attach to object. /// public VHACD_RESULT_METHOD vhacdResultMethod = VHACD_RESULT_METHOD.AttachTo; /// /// Run VHACD only on the selected vertices? /// public bool UseSelectedVertices = false; /// /// Current mesh filter VHACD is calculating. /// public int CurrentMeshFilter = 0; /// /// Should child meshes be seperately done in the calculation / adding of convex hulls. /// public bool SeparateChildMeshes = false; /// /// List of mesh filters for VHACD calculation. /// public List MeshFilters = new List(); /// /// Gameobject to attach mesh colliders to using the result of VHACD. /// public GameObject AttachTo; /// /// Save path of current VHACD meshes /// public string SavePath; /// /// maximum concavity /// public double concavity; /// /// controls bias toward clipping along symmetry planes /// public double alpha; /// /// controls bias toward clipping along revolution axes /// public double beta; /// /// controls adaptive sampling of the generated convex-hulls /// public double minVolumePerCH; /// /// maximum number of voxels generated during voxelization stage /// public int resolution; /// /// controls maximum number of triangles per convex hull /// public int maxNumVerticesPerConvexHull; /// /// controls the granularity of the search for the "best" clipping plane /// public int planeDownsampling; /// /// controls the precision of the convex-hull generation process during the clipping plane selection stage /// public int convexhullDownSampling; /// /// maximum number of convex hulls /// public int maxConvexHulls; /// /// When enabled, will project the output convex hull vertices onto the original source mesh /// public bool projectHullVertices; /// /// Fill mode to determine what is inside/outside the mesh /// Flood fill: basic flood fill algorithm /// Raycast fill: raycasts are used to determine inside/outside /// Surface: used when the surface is to represent a hollow object. /// public VHACD_FILL_MODE fillMode; /// /// Should we force recalculation when resulting hulls have a hull with triangle count >=256. /// public bool forceUnder256Triangles; /// /// Creates a VHACD parameters object with default values. /// public VHACDParameters() { concavity = 0.0025; alpha = 0.05; beta = 0.05; minVolumePerCH = 0.0001; resolution = 10000; maxNumVerticesPerConvexHull = 64; planeDownsampling = 4; convexhullDownSampling = 4; maxConvexHulls = 1; projectHullVertices = true; fillMode = VHACD_FILL_MODE.FLOOD_FILL; forceUnder256Triangles = true; } /// /// Creates a VHACDParameters object with the values of another VHACDParam /// /// Values to copy from protected VHACDParameters(VHACDParameters other) { SaveSuffix = other.SaveSuffix; vhacdResultMethod = other.vhacdResultMethod; AttachTo = other.AttachTo; concavity = other.concavity; alpha = other.alpha; beta = other.beta; minVolumePerCH = other.minVolumePerCH; resolution = other.resolution; maxNumVerticesPerConvexHull = other.maxNumVerticesPerConvexHull; planeDownsampling = other.planeDownsampling; convexhullDownSampling = other.convexhullDownSampling; maxConvexHulls = other.maxConvexHulls; projectHullVertices = other.projectHullVertices; fillMode = other.fillMode; forceUnder256Triangles = other.forceUnder256Triangles; SeparateChildMeshes = other.SeparateChildMeshes; UseSelectedVertices = other.UseSelectedVertices; MeshFilters = new List(); foreach (MeshFilter f in other.MeshFilters) { MeshFilters.Add(f); } } /// /// Clones the current instance of VHACDParameters. /// /// Copy of the VHACDParameters instance. public VHACDParameters Clone() { return new VHACDParameters(this); } } } #endif