Skip to content

Instantly share code, notes, and snippets.

@Andicraft
Created September 13, 2024 13:46
Show Gist options
  • Save Andicraft/ec28b8690c7d6cbb13bb4fe29e7a7645 to your computer and use it in GitHub Desktop.
Save Andicraft/ec28b8690c7d6cbb13bb4fe29e7a7645 to your computer and use it in GitHub Desktop.
CoaCD collision generation for Godot 4
using System.Runtime.InteropServices;
using Godot;
using Godot.Collections;
namespace CoACD_Godot;
/* Generates colliders using CoaCD - https://github.com/SarahWeiii/CoACD
* This is NOT a drag and drop file, but it should give you the framework you need to get started.
*
* Requires you to build the CoaCD library yourself to get the latest version.
* Put the lib_coacd.dll/so/dylib next to your Godot executable.
*
* Because this is unsafe code that interfaces with an unmanaged library,
* you might have to put this into a separate C# project from your main Godot project.
*/
public unsafe class CoACD
{
[Serializable]
public struct MeshInterface
{
public double* vertices_ptr;
public ulong vertices_count;
public int* triangles_ptr;
public ulong triangles_count;
}
[Serializable]
public struct MeshArrayInterface : IDisposable
{
public MeshInterface* meshes_ptr;
public ulong meshes_count;
public void Dispose()
{
if (meshes_ptr != null) {
Free(this);
meshes_ptr = null;
}
}
}
[DllImport("lib_coacd", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CoACD_setLogLevel")]
static extern void SetLogLevel([MarshalAs(UnmanagedType.LPStr)] string level);
[DllImport("lib_coacd", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CoACD_freeMeshArray")]
static extern void Free(MeshArrayInterface array);
[DllImport("lib_coacd", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CoACD_run")]
static extern MeshArrayInterface Run(ref MeshInterface mesh,
double threshold,
int max_convex_hull,
int preprocess_mode,
int prep_resolution,
int sample_resolution,
int mcts_nodes,
int mcts_iteration,
int mcts_max_depth,
bool pca,
bool merge,
bool decimate,
int max_ch_vertex,
bool extrude,
double extrude_margin,
int apx_mode,
uint seed);
public enum PreprocessMode { Auto = 0, On = 1, Off = 2 }
public enum LogLevel { Off, Info, Warn, Error, Critical }
[Serializable]
public unsafe struct CoaCDParams
{
public static CoaCDParams Init() => new CoaCDParams
{
threshold = 0.05,
preprocessMode = PreprocessMode.Auto,
preprocessResolution = 50,
sampleResolution = 2000,
mctsNodes = 20,
mctsIteration = 150,
mctsMaxDepth = 3,
pca = false,
merge = true,
maxConvexHull = -1,
decimate = true,
max_ch_vertex = 65,
extrude = false,
extrude_margin = 0.01,
apx_mode = 0,
seed = 0
};
//[Range(0.01f, 1f)]
// Concavity threshold for terminating the decomposition
public double threshold;
// Choose manifold preprocessing mode ('auto': automatically check input mesh manifoldness; 'on': force turn on the pre-processing; 'off': force turn off the pre-processing)
public PreprocessMode preprocessMode;
//[Range(20, 100)]
// Resolution for manifold preprocess
public int preprocessResolution;
//[Range(1000, 10000)]
// Sampling resolution for Hausdorff distance calculation
public int sampleResolution;
//[Range(10, 40)]
// Max number of child nodes in MCTS
public int mctsNodes;
//[Range(60, 2000)]
// Number of search iterations in MCTS
public int mctsIteration;
//[Range(2, 7)]
// Max search depth in MCTS
public int mctsMaxDepth;
// Flag to enable PCA pre-processing
public bool pca;
public bool merge;
// Max number of convex hulls generated, -1 for no limit
public int maxConvexHull;
// Max # convex hulls in the result, -1 for no maximum limitation, works only when merge is enabled, default = -1 (may introduce convex hull with a concavity larger than the threshold
public bool decimate; // enable max vertex constraint per convex hull, default = false.
public int max_ch_vertex; // max vertex value for each convex hull, only when decimate is enabled, default = 256.
public bool extrude; // extrude neighboring convex hulls along the overlapping faces (other faces unchanged), default = false.
public double extrude_margin; // extrude margin, only when extrude is enabled, default = 0.01.
public int apx_mode; // approximation shape type ("ch" for convex hulls, "box" for cubes), default = "ch".
public uint seed;
}
public static Array<ConvexPolygonShape3D> RunACD(ArrayMesh mesh, CoaCDParams parameters)
{
int vcount = mesh.GetFaces().Length;
var unityV = mesh.GetFaces();
var unityF = new int[vcount];
var v = new double[vcount * 3];
for (var i = 0; i < vcount; i++) {
v[3 * i + 0] = unityV[i].X;
v[3 * i + 1] = unityV[i].Y;
v[3 * i + 2] = unityV[i].Z;
unityF[i] = i;
}
fixed (double* vptr = v) {
fixed (int* fptr = unityF) {
var mi = new MeshInterface() {vertices_ptr = vptr, vertices_count = (ulong) vcount, triangles_ptr = fptr, triangles_count = (ulong) (unityF.LongLength / 3)};
using var res = Run(ref mi, parameters.threshold, parameters.maxConvexHull, (int) parameters.preprocessMode, parameters.preprocessResolution, parameters.sampleResolution,
parameters.mctsNodes, parameters.mctsIteration, parameters.mctsMaxDepth, parameters.pca, parameters.merge, parameters.decimate, parameters.max_ch_vertex, parameters.extrude, parameters.extrude_margin, parameters.apx_mode, parameters.seed);
var shapes = new Array<ConvexPolygonShape3D>();
for (ulong i = 0; i < res.meshes_count; i++) {
var shape = new ConvexPolygonShape3D();
var verts = new Vector3[res.meshes_ptr[i].vertices_count];
for (ulong j = 0; j < res.meshes_ptr[i].vertices_count; j++) {
verts[j] = new Vector3((float) res.meshes_ptr[i].vertices_ptr[j * 3 + 0], (float) res.meshes_ptr[i].vertices_ptr[j * 3 + 1],
(float) res.meshes_ptr[i].vertices_ptr[j * 3 + 2]);
}
shape.Points = verts;
shapes.Add(shape);
}
return shapes;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment