/// <summary> /// Performs the specified operation on the provided meshes. /// </summary> /// <param name="first">The first mesh</param> /// <param name="second">The second mesh</param> /// <param name="operation">The mesh opration to perform on the two meshes</param> /// <returns>A triangular mesh resulting from performing the specified operation. If the resulting mesh is empty, will return null.</returns> public static Mesh3 PerformCSG(Mesh3 first, Mesh3 second, CSGOperations operation) { Mesh3 finalResult = null; unsafe { InteropMesh a = new InteropMesh(); InteropMesh b = new InteropMesh(); Vector3[] aTransformed = first.GetTransformedVertices(); Vector3[] bTransformed = second.GetTransformedVertices(); InteropMesh *result; fixed(Vector3 *aVerts = &aTransformed[0], bVerts = &bTransformed[0]) { fixed(int *aTris = &first.TriangleIndices[0], bTris = &second.TriangleIndices[0]) { a.vertsArrayLength = first.Vertices.Length * 3; a.triArrayLength = first.TriangleIndices.Length; a.vertices = (double *)aVerts; a.triangleIndices = aTris; b.vertsArrayLength = second.Vertices.Length * 3; b.triArrayLength = second.TriangleIndices.Length; b.vertices = (double *)bVerts; b.triangleIndices = bTris; try { result = performCSG(ref a, ref b, operation); } catch (SEHException ex) { ArgumentException e = new ArgumentException("Carve has thrown an error. Possible reason is corrupt or self-intersecting meshes", ex); throw e; } } } if (result->vertsArrayLength == 0) { freeMesh(result); return(null); } Vector3[] vertices = new Vector3[result->vertsArrayLength / 3]; int[] triangleIndices = new int[result->triArrayLength]; // Copy the results back in parallel Parallel.For(0, vertices.Length, i => { vertices[i] = new Vector3(result->vertices[3 * i], result->vertices[3 * i + 1], result->vertices[3 * i + 2]); }); Parallel.For(0, triangleIndices.Length, i => { triangleIndices[i] = result->triangleIndices[i]; }); // If none of the vertices had colors, return whatever we have if (!first.HasColours && !second.HasColours) { finalResult = new Mesh3(vertices, triangleIndices); freeMesh(result); } else // Assign colors to the resulting mesh { uint[] colors = new uint[vertices.Length]; uint grayCode = 4286611584; // The uint value of the color gray (representing no color) // Assign the default gray to all vertices Parallel.For(0, colors.Length, i => { colors[i] = grayCode; }); #region Worst practices of parallel coding /** * The procedure for color matching is creating a map of (vertex=>color) and then * comparing all vertices of the resulting mesh (in parallel) with this map and * assigning colors as necessary */ if (first.HasColours) { ConcurrentDictionary <Vector3, uint> firstMap = new ConcurrentDictionary <Vector3, uint>(); // Create vertex to color map Parallel.For(0, aTransformed.Length, i => { firstMap[aTransformed[i]] = first.VertexColours[i]; }); // Assign colors Parallel.For(0, vertices.Length, i => { if (firstMap.ContainsKey(vertices[i])) { colors[i] = firstMap[vertices[i]]; } }); } if (second.HasColours) { ConcurrentDictionary <Vector3, uint> secondMap = new ConcurrentDictionary <Vector3, uint>(); Parallel.For(0, bTransformed.Length, i => { secondMap[bTransformed[i]] = second.VertexColours[i]; }); Parallel.For(0, vertices.Length, i => { if (secondMap.ContainsKey(vertices[i])) { colors[i] = secondMap[vertices[i]]; } }); } #endregion finalResult = new Mesh3(vertices, triangleIndices, colors); } } // end-unsafe Matrix3 transform = first.Transform.Invert(); finalResult = new Mesh3(finalResult.Vertices.Select(x => transform.Transform(x - first.Position)).ToArray(), finalResult.TriangleIndices, finalResult.VertexColours); finalResult.Position = first.Position; finalResult.Transform = first.Transform; return(finalResult); }