/// <summary> /// Used for creating a hull from the given BXDAMesh.BXDASubmesh. /// </summary> /// <param name="subMesh"></param> /// <param name="decompose"></param> /// <returns></returns> public static BXDAMesh.BXDASubMesh GetHull(BXDAMesh.BXDASubMesh subMesh) { List <int> indices = new List <int>(); foreach (BXDAMesh.BXDASurface surface in subMesh.surfaces) { indices.AddRange(surface.indicies); } IVHACD decomposer = new IVHACD(); ConvexLibraryWrapper.Parameters parameters = new ConvexLibraryWrapper.Parameters(); parameters.m_depth = 1; parameters.m_concavity = 1; if (!decomposer.Compute(Array.ConvertAll <double, float>(subMesh.verts, (d) => (float)d), 3, (uint)subMesh.verts.Length / 3, indices.ToArray(), 3, (uint)indices.Count / 3, parameters)) { return(null); } ConvexLibraryWrapper.ConvexHull result = decomposer.GetConvexHull(0); BXDAMesh.BXDASubMesh resultMesh = ExportSubMesh(Array.ConvertAll <double, float>(result.m_points, (d) => (float)d), result.m_nPoints, Array.ConvertAll <int, uint>(result.m_triangles, (i) => (uint)i), result.m_nTriangles); decomposer.Cancel(); decomposer.Clean(); decomposer.Release(); return(resultMesh); }
/// <summary> /// Computes a convex hull, or convex hull set for the given meshes. /// </summary> /// <param name="mesh">Mesh to compute for</param> /// <param name="decompose">If a set of convex hulls is required</param> /// <returns>The resulting list of convex hulls.</returns> public static List <BXDAMesh.BXDASubMesh> GetHull(BXDAMesh bMesh, bool decompose = false, bool OCL = false) { int vertCount = 0; int indexCount = 0; foreach (BXDAMesh.BXDASubMesh mesh in bMesh.meshes) { vertCount += mesh.verts.Length; foreach (BXDAMesh.BXDASurface surface in mesh.surfaces) { //Get total number of indicies in overall mesh. indexCount += surface.indicies.Length; } } if (vertCount <= 0) { throw new InvalidOperationException("There isn't any mesh to operate on!"); } float[] copy = new float[vertCount]; uint[] index = new uint[indexCount]; vertCount = 0; indexCount = 0; int totalChecks = 0; int onePercent = Math.Max(1, (copy.Length / 3) / 100); SynthesisGUI.Instance.ExporterReset(); foreach (BXDAMesh.BXDASubMesh mesh in bMesh.meshes) { int[] subIndices = new int[mesh.verts.Length / 3]; int addedVerts = 0; for (int i = 0; i < mesh.verts.Length; i += 3) { totalChecks++; if (totalChecks % onePercent == 0) { //Console.Write("Cleaning " + totalChecks + "/" + (copy.Length / 3) + " " + ((int) (totalChecks * 3 / (float) copy.Length * 10000f) / 100f) + "%"); double totalProgress = ((double)totalChecks / ((double)copy.Length / 3.0)) * 100.0; SynthesisGUI.Instance.ExporterSetSubText(String.Format("Clean {1} / {2}", Math.Round(totalProgress, 2), totalChecks, copy.Length / 3)); SynthesisGUI.Instance.ExporterSetProgress(totalProgress); } //Copy all the mesh vertices over, starting at the end of the last mesh copied. for (int j = 0; j < (vertCount + addedVerts) * 3; j += 3) { if (Math.Abs(mesh.verts[i] - copy[j]) < EPSILON && Math.Abs(mesh.verts[i + 1] - copy[j + 1]) < EPSILON && Math.Abs(mesh.verts[i + 2] - copy[j + 2]) < EPSILON) { subIndices[i / 3] = (j / 3) + 1;// Add one so we can just use the pre-zeroed memory. break; } } if (subIndices[i / 3] == 0) { subIndices[i / 3] = vertCount + addedVerts + 1; int offset = (vertCount + addedVerts) * 3; addedVerts++; copy[offset] = (float)mesh.verts[i]; copy[offset + 1] = (float)mesh.verts[i + 1]; copy[offset + 2] = (float)mesh.verts[i + 2]; } else { // Which one is farther from the COM int offset = (subIndices[i / 3] - 1) * 3; float dx = (float)mesh.verts[i] - bMesh.physics.centerOfMass.x; float dy = (float)mesh.verts[i + 1] - bMesh.physics.centerOfMass.y; float dz = (float)mesh.verts[i + 2] - bMesh.physics.centerOfMass.z; float dXC = copy[offset] - bMesh.physics.centerOfMass.x; float dYC = copy[offset] - bMesh.physics.centerOfMass.y; float dZC = copy[offset] - bMesh.physics.centerOfMass.z; if ((dx * dx + dy * dy + dz * dz) > (dXC * dXC + dYC * dYC + dZC * dZC)) { copy[offset] = (float)mesh.verts[i]; copy[offset + 1] = (float)mesh.verts[i + 1]; copy[offset + 2] = (float)mesh.verts[i + 2]; } } } foreach (BXDAMesh.BXDASurface surface in mesh.surfaces) { int addedInds = 0; for (int i = 0; i < surface.indicies.Length; i += 3) { if (subIndices[surface.indicies[i]] != subIndices[surface.indicies[i + 1]] && subIndices[surface.indicies[i + 1]] != subIndices[surface.indicies[i + 2]] && subIndices[surface.indicies[i + 2]] != subIndices[surface.indicies[i]]) { index[indexCount + i] = (uint)subIndices[surface.indicies[i]] - 1; index[indexCount + i + 1] = (uint)subIndices[surface.indicies[i + 1]] - 1; index[indexCount + i + 2] = (uint)subIndices[surface.indicies[i + 2]] - 1; addedInds += 3; } } indexCount += addedInds; } vertCount += addedVerts; } SynthesisGUI.Instance.ExporterSetSubText("Calculating colliders"); IVHACD decomposer = new IVHACD(); ConvexLibraryWrapper.Parameters parameters = new ConvexLibraryWrapper.Parameters(); if (!decompose) { parameters.m_depth = 1; parameters.m_concavity = 1; } if (OCL) { Console.WriteLine("Using OpenCL acceleration"); try { parameters.m_oclAcceleration = 1; decomposer.OCLInit(parameters); } catch (DllNotFoundException e) { Console.WriteLine(e.Message); OCL = false; } } bool decomposeResult = false; Thread t = new Thread(() => { decomposeResult = decomposer.Compute(copy, 3, (uint)copy.Length / 3, Array.ConvertAll <uint, int>(index, (uint ui) => (int)ui), 3, (uint)index.Length / 3, parameters); }); t.SetApartmentState(ApartmentState.MTA); List <BXDAMesh.BXDASubMesh> subs = new List <BXDAMesh.BXDASubMesh>(); try { t.Start(); int dot = 0; while (!t.Join(0)) { SynthesisGUI.Instance.ExporterSetSubText("Calculating colliders" + new String('.', dot)); dot++; if (dot > 5) { dot = 0; } System.Threading.Thread.Sleep(500); } Console.WriteLine(); if (!decomposeResult) { throw new Exception("Couldn't calculate convex hull!"); } uint hullCount = decomposer.GetNConvexHulls(); Console.WriteLine("Convex Decomposition produced " + hullCount + " hulls."); for (uint i = 0; i < hullCount; i++) { ConvexLibraryWrapper.ConvexHull result = decomposer.GetConvexHull(i); subs.Add(ExportMeshInternal(Array.ConvertAll <double, float>(result.m_points, (double ui) => (float)ui), result.m_nPoints, Array.ConvertAll <int, uint>(result.m_triangles, (int ui) => (uint)ui), result.m_nTriangles)); } } finally { } if (OCL) { decomposer.OCLRelease(parameters); } decomposer.Cancel(); decomposer.Clean(); decomposer.Release(); return(subs); }