public int Hash(Vector3 p) { for (int i = 0; i < count; i++) { var item = buckets[i]; float diffX = p.x - item.x; float diffY = p.y - item.y; float diffZ = p.z - item.z; float sqrMag = diffX * diffX + diffY * diffY + diffZ * diffZ; if (sqrMag < bucketSize2) { return(i); } } if (count >= buckets.Length) { ExploderUtils.Log("Hash out of range: " + count + " " + buckets.Length); return(count - 1); } buckets[count++] = p; return(count - 1); }
/// <summary> /// Compute intersection between a segment line (a, b) and a plane (p) /// from Real-Time Collision Detection Book by Christer Ericson /// </summary> /// <param name="a">first point of a segment</param> /// <param name="b">second point of a segment</param> /// <param name="t">normalized distance of intersection point on vector (ab)</param> /// <param name="q">point in intersection</param> /// <returns>true if there is an intersection</returns> public bool IntersectSegment(Vector3 a, Vector3 b, out float t, ref Vector3 q) { var abx = b.x - a.x; var aby = b.y - a.y; var abz = b.z - a.z; var dot0 = Normal.x * a.x + Normal.y * a.y + Normal.z * a.z; var dot1 = Normal.x * abx + Normal.y * aby + Normal.z * abz; t = (Distance - dot0) / dot1; if (t >= 0.0f - epsylon && t <= 1.0f + epsylon) { q.x = a.x + t * abx; q.y = a.y + t * aby; q.z = a.z + t * abz; return(true); } ExploderUtils.Log("IntersectSegment failed: " + t); q = Vector3.zero; return(false); }
float Cut(ExploderMesh mesh, ExploderTransform meshTransform, Plane plane, bool triangulateHoles, bool allowOpenMesh, ref List <ExploderMesh> meshes) { var stopWatch = new Stopwatch(); stopWatch.Start(); #if PROFILING MeasureIt.Begin("CutAllocations"); #endif // cache mesh data var trianglesNum = mesh.triangles.Length; var verticesNum = mesh.vertices.Length; var meshTriangles = mesh.triangles; var meshTangents = mesh.tangents; var meshColors = mesh.colors32; var meshVertices = mesh.vertices; var meshNormals = mesh.normals; var meshUV = mesh.uv; var useMeshTangents = meshTangents != null && meshTangents.Length > 0; var useVertexColors = meshColors != null && meshColors.Length > 0; var useNormals = meshNormals != null && meshNormals.Length > 0; // preallocate buffers AllocateBuffers(trianglesNum, verticesNum, useMeshTangents, useVertexColors); ExploderMesh mesh0, mesh1; #if PROFILING MeasureIt.End("CutAllocations"); MeasureIt.Begin("CutCycleFirstPass"); #endif // first pass - find complete triangles on both sides of the plane for (int i = 0; i < trianglesNum; i += 3) { // get triangle points var v0 = meshVertices[meshTriangles[i]]; var v1 = meshVertices[meshTriangles[i + 1]]; var v2 = meshVertices[meshTriangles[i + 2]]; var side0 = plane.GetSideFix(ref v0); var side1 = plane.GetSideFix(ref v1); var side2 = plane.GetSideFix(ref v2); meshVertices[meshTriangles[i]] = v0; meshVertices[meshTriangles[i + 1]] = v1; meshVertices[meshTriangles[i + 2]] = v2; // Utils.Log(plane.Pnt + " " + v0 + " " + v1 + " " + " " + v2); // all points on one side if (side0 == side1 && side1 == side2) { var idx = side0 ? 0 : 1; if (meshTriangles[i] >= triCache.Length) { ExploderUtils.Log("TriCacheError " + meshTriangles[i] + " " + triCache.Length + " " + meshVertices.Length); } if (triCache[meshTriangles[i]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i]]); uvs[idx].Add(meshUV[meshTriangles[i]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i]]); } centroid[idx] += meshVertices[meshTriangles[i]]; triCache[meshTriangles[i]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i]] - 1); } if (triCache[meshTriangles[i + 1]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 1]]); uvs[idx].Add(meshUV[meshTriangles[i + 1]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 1]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i + 1]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i + 1]]); } centroid[idx] += meshVertices[meshTriangles[i + 1]]; triCache[meshTriangles[i + 1]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 1]] - 1); } if (triCache[meshTriangles[i + 2]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 2]]); uvs[idx].Add(meshUV[meshTriangles[i + 2]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 2]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i + 2]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i + 2]]); } centroid[idx] += meshVertices[meshTriangles[i + 2]]; triCache[meshTriangles[i + 2]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 2]] - 1); } } else { // intersection triangles add to list and process it in second pass cutTris.Add(i); } } if (vertices[0].Count == 0) { centroid[0] = meshVertices[0]; } else { centroid[0] /= vertices[0].Count; } if (vertices[1].Count == 0) { centroid[1] = meshVertices[1]; } else { centroid[1] /= vertices[1].Count; } // UnityEngine.Debug.LogFormat("cut: {0} -- {1}, normal: {2}, tris: {3}", vertices[0].Count, vertices[1].Count, plane.Normal, cutTris.Count); #if PROFILING MeasureIt.End("CutCycleFirstPass"); MeasureIt.Begin("CutCycleSecondPass"); #endif if (cutTris.Count < 1) { stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } AllocateContours(cutTris.Count); // second pass - cut intersecting triangles in half foreach (var cutTri in cutTris) { var triangle = new Triangle { ids = new[] { meshTriangles[cutTri + 0], meshTriangles[cutTri + 1], meshTriangles[cutTri + 2] }, pos = new[] { meshVertices[meshTriangles[cutTri + 0]], meshVertices[meshTriangles[cutTri + 1]], meshVertices[meshTriangles[cutTri + 2]] }, normal = useNormals ? new[] { meshNormals[meshTriangles[cutTri + 0]], meshNormals[meshTriangles[cutTri + 1]], meshNormals[meshTriangles[cutTri + 2]] } : new[] { Vector3.zero, Vector3.zero, Vector3.zero }, uvs = new[] { meshUV[meshTriangles[cutTri + 0]], meshUV[meshTriangles[cutTri + 1]], meshUV[meshTriangles[cutTri + 2]] }, tangents = useMeshTangents ? new[] { meshTangents[meshTriangles[cutTri + 0]], meshTangents[meshTriangles[cutTri + 1]], meshTangents[meshTriangles[cutTri + 2]] } : new [] { Vector4.zero, Vector4.zero, Vector4.zero }, colors = useVertexColors ? new[] { meshColors[meshTriangles[cutTri + 0]], meshColors[meshTriangles[cutTri + 1]], meshColors[meshTriangles[cutTri + 2]] } : new Color32[] { Color.white, Color.white, Color.white }, }; // check points with a plane var side0 = plane.GetSide(triangle.pos[0]); var side1 = plane.GetSide(triangle.pos[1]); var side2 = plane.GetSide(triangle.pos[2]); float t0, t1; Vector3 s0 = Vector3.zero, s1 = Vector3.zero; var idxLeft = side0 ? 0 : 1; var idxRight = 1 - idxLeft; if (side0 == side1) { var a = plane.IntersectSegment(triangle.pos[2], triangle.pos[0], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[2], triangle.pos[1], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v1Left = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, v0, s1) triangles[idxLeft].Add(s0Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // Triangle (s1, v0, v1) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(v1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s0, s1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else if (side0 == side2) { var a = plane.IntersectSegment(triangle.pos[1], triangle.pos[0], out t0, ref s1); var b = plane.IntersectSegment(triangle.pos[1], triangle.pos[2], out t1, ref s0); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v2Left = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, s0) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(s0Left); // Triangle (v2, v0, s1) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, s1, v1) triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else { var a = plane.IntersectSegment(triangle.pos[0], triangle.pos[1], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[0], triangle.pos[2], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // right side ... 2 triangles var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, v1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // Triangle (s1, s0, v1) triangles[idxRight].Add(s1Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(v1Right); // left side ... 1 triangle var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s1, v0, s0) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s0Left); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } } #if PROFILING MeasureIt.End("CutCycleSecondPass"); #endif if (triangulateHoles) { #if PROFILING MeasureIt.Begin("FindContours"); #endif contour.FindContours(); if (contour.contour.Count == 0 || contour.contour[0].Count < 3) { // triangulateHoles = false; if (allowOpenMesh) { triangulateHoles = false; } else { stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } } #if PROFILING MeasureIt.End("FindContours"); #endif } List <int>[] trianglesCut = null; var centroid0 = Vector3.zero; var centroid1 = Vector3.zero; var min0 = Vector3.zero; var max0 = Vector3.zero; var min1 = Vector3.zero; var max1 = Vector3.zero; ExploderMesh.CalculateCentroid(vertices[0], ref centroid0, ref min0, ref max0); ExploderMesh.CalculateCentroid(vertices[1], ref centroid1, ref min1, ref max1); if (triangulateHoles) { #if PROFILING MeasureIt.Begin("Triangulate"); #endif trianglesCut = new List <int>[2] { new List <int>(contour.MidPointsCount), new List <int>(contour.MidPointsCount) }; Triangulate(contour.contour, plane, vertices, normals, uvs, tangents, vertexColors, trianglesCut, true, useMeshTangents, useVertexColors, useNormals); #if PROFILING MeasureIt.End("Triangulate"); #endif } if (vertices[0].Count > 3 && vertices[1].Count > 3) { #if PROFILING MeasureIt.Begin("CutEndCopyBack"); #endif mesh0 = new ExploderMesh(); mesh1 = new ExploderMesh(); var verticesArray0 = vertices[0].ToArray(); var verticesArray1 = vertices[1].ToArray(); mesh0.vertices = verticesArray0; mesh0.uv = uvs[0].ToArray(); mesh1.vertices = verticesArray1; mesh1.uv = uvs[1].ToArray(); if (useNormals) { mesh0.normals = normals[0].ToArray(); mesh1.normals = normals[1].ToArray(); } if (useMeshTangents) { mesh0.tangents = tangents[0].ToArray(); mesh1.tangents = tangents[1].ToArray(); } if (useVertexColors) { mesh0.colors32 = vertexColors[0].ToArray(); mesh1.colors32 = vertexColors[1].ToArray(); } if (trianglesCut != null && trianglesCut[0].Count > 3) { triangles[0].AddRange(trianglesCut[0]); triangles[1].AddRange(trianglesCut[1]); } mesh0.triangles = triangles[0].ToArray(); mesh1.triangles = triangles[1].ToArray(); mesh0.centroid = centroid0; mesh0.min = min0; mesh0.max = max0; mesh1.centroid = centroid1; mesh1.min = min1; mesh1.max = max1; #if PROFILING MeasureIt.End("CutEndCopyBack"); #endif meshes = new List <ExploderMesh> { mesh0, mesh1 }; stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } stopWatch.Stop(); // UnityEngine.Debug.Log("Empty cut! " + vertices[0].Count + " " + vertices[1].Count); return(stopWatch.ElapsedMilliseconds); }
private void Cut() { bool cutting = true; var cycleCounter = 0; cutAttempt = 0; while (cutting) { cycleCounter++; if (cycleCounter > core.parameters.TargetFragments) { ExploderUtils.Log("Explode Infinite loop!"); break; } newFragments.Clear(); meshToRemove.Clear(); cutting = false; foreach (var mesh in meshSet) { if (core.targetFragments[mesh.id] > 1) { var plane = cuttingPlane.GetPlane(mesh.mesh, cutAttempt); var triangulateHoles = true; var crossSectionVertexColour = Color.white; var crossSectionUV = new Vector4(0, 0, 1, 1); if (mesh.option) { triangulateHoles = !mesh.option.Plane2D; crossSectionVertexColour = mesh.option.CrossSectionVertexColor; crossSectionUV = mesh.option.CrossSectionUV; core.splitMeshIslands |= mesh.option.SplitMeshIslands; } if (core.parameters.Use2DCollision) { triangulateHoles = false; } List <ExploderMesh> meshes = null; cutter.Cut(mesh.mesh, mesh.transform, plane, triangulateHoles, core.parameters.DisableTriangulation, ref meshes, crossSectionVertexColour, crossSectionUV); cutting = true; if (meshes != null) { foreach (var cutterMesh in meshes) { newFragments.Add(new MeshObject { mesh = cutterMesh, material = mesh.material, transform = mesh.transform, id = mesh.id, original = mesh.original, skinnedOriginal = mesh.skinnedOriginal, bakeObject = mesh.bakeObject, parent = mesh.transform.parent, position = mesh.transform.position, rotation = mesh.transform.rotation, localScale = mesh.transform.localScale, option = mesh.option, }); } meshToRemove.Add(mesh); core.targetFragments[mesh.id] -= 1; } else { cutAttempt++; } } } meshSet.ExceptWith(meshToRemove); meshSet.UnionWith(newFragments); } }
public override bool Run(float frameBudget) { var count = core.pool.Count; while (core.poolIdx < count) { var fragment = core.pool[core.poolIdx]; var mesh = core.postList[core.poolIdx]; core.poolIdx++; if (!mesh.original) { continue; } var unityMesh = mesh.mesh.ToUnityMesh(); fragment.meshFilter.sharedMesh = unityMesh; // choose proper material if (mesh.option && mesh.option.FragmentMaterial) { fragment.meshRenderer.sharedMaterial = mesh.option.FragmentMaterial; } else { if (core.parameters.FragmentOptions.FragmentMaterial != null) { fragment.meshRenderer.sharedMaterial = core.parameters.FragmentOptions.FragmentMaterial; } else { fragment.meshRenderer.sharedMaterial = mesh.material; } } unityMesh.RecalculateBounds(); var oldParent = fragment.transform.parent; fragment.transform.parent = mesh.parent; fragment.transform.position = mesh.position; fragment.transform.rotation = mesh.rotation; fragment.transform.localScale = mesh.localScale; fragment.transform.parent = null; fragment.transform.parent = oldParent; if (core.parameters.PartialExplosion) { } else { if (mesh.original != core.parameters.ExploderGameObject) { ExploderUtils.SetActiveRecursively(mesh.original, false); } else { ExploderUtils.EnableCollider(mesh.original, false); ExploderUtils.SetVisible(mesh.original, false); } if (mesh.skinnedOriginal && mesh.skinnedOriginal != core.parameters.ExploderGameObject) { ExploderUtils.SetActiveRecursively(mesh.skinnedOriginal, false); } else { ExploderUtils.EnableCollider(mesh.skinnedOriginal, false); ExploderUtils.SetVisible(mesh.skinnedOriginal, false); } if (mesh.skinnedOriginal && mesh.bakeObject) { GameObject.DestroyObject(mesh.bakeObject, 1); } } var plane = mesh.option && mesh.option.Plane2D; var use2d = core.parameters.Use2DCollision; if (!core.parameters.FragmentOptions.DisableColliders) { if (core.parameters.MeshColliders && !use2d) { // dont use mesh colliders for 2d plane if (!plane) { fragment.meshCollider.sharedMesh = unityMesh; } } else { if (core.parameters.Use2DCollision) { MeshUtils.GeneratePolygonCollider(fragment.polygonCollider2D, unityMesh); } else { fragment.boxCollider.center = unityMesh.bounds.center; fragment.boxCollider.size = unityMesh.bounds.extents; } } } if (mesh.option) { mesh.option.DuplicateSettings(fragment.options); } fragment.Explode(); var force = core.parameters.Force; if (mesh.option && mesh.option.UseLocalForce) { force = mesh.option.Force; } // apply force to rigid body fragment.ApplyExplosion(mesh.transform, mesh.mesh.centroid, core.parameters.Position, core.parameters.FragmentOptions, core.parameters.UseForceVector, core.parameters.ForceVector, force, mesh.original, core.parameters.TargetFragments); #if SHOW_DEBUG_LINES UnityEngine.Debug.DrawLine(settings.Position, forceVector * settings.Force, Color.yellow, 3); #endif if (Watch.ElapsedMilliseconds > frameBudget) { return(false); } } if (core.parameters.DestroyOriginalObject) { foreach (var mesh in core.postList) { if (mesh.original && !mesh.original.GetComponent <Fragment>()) { Object.Destroy(mesh.original); } if (mesh.skinnedOriginal) { Object.Destroy(mesh.skinnedOriginal); } } } if (core.parameters.ExplodeSelf) { if (!core.parameters.DestroyOriginalObject) { ExploderUtils.SetActiveRecursively(core.parameters.ExploderGameObject, false); } } if (core.parameters.HideSelf) { ExploderUtils.SetActiveRecursively(core.parameters.ExploderGameObject, false); } #if DBG ExploderUtils.Log("Explosion finished! " + postList.Count + postList[0].original.transform.gameObject.name); #endif // core.exploder.OnExplosionFinished(true); Watch.Stop(); return(true); }
protected override bool Cut(float frameBudget) { if (cutInitialized) { return(true); } localWatch.Start(); cutInitialized = true; // // thread_max = <2, 4> // Debug.Assert(THREAD_MAX > 1, "At least one worker is required!"); if (core.parameters.TargetFragments < 2 || core.meshSet.Count == core.parameters.TargetFragments) { return(true); } // // cut object if necessary // var cuts = THREAD_MAX - 1 - core.meshSet.Count; // Debug.AssertFormat(cuts <= 2, "Invalid number of cuts: {0}", cuts); // var cutsTotal = cuts; if (cuts > core.parameters.TargetFragments - 1) { cuts = core.parameters.TargetFragments - 1; } var cycleCounter = 0; idCounter = core.targetFragments.Count + 1; while (cuts > 0) { newFragments.Clear(); foreach (var mesh in core.meshSet) { var meshes = CutSingleMesh(mesh); cycleCounter++; if (cycleCounter > core.parameters.TargetFragments) { ExploderUtils.Log("Explode Infinite loop!"); cuts = 0; break; } if (meshes != null) { cuts--; var ids = SplitMeshTargetFragments(mesh.id); var ctr = 0; foreach (var cutterMesh in meshes) { var fragment = new MeshObject { mesh = cutterMesh, material = mesh.material, transform = mesh.transform, id = mesh.id, original = mesh.original, skinnedOriginal = mesh.skinnedOriginal, bakeObject = mesh.bakeObject, parent = mesh.transform.parent, position = mesh.transform.position, rotation = mesh.transform.rotation, localScale = mesh.transform.localScale, option = mesh.option, }; fragment.id = ids[ctr++]; newFragments.Add(fragment); } meshToRemove.Add(mesh); break; } } core.meshSet.ExceptWith(meshToRemove); core.meshSet.UnionWith(newFragments); } if (core.meshSet.Count >= core.parameters.TargetFragments) { return(true); } // // assign meshes to workers // var meshPerThread = core.meshSet.Count / (THREAD_MAX - 1); var workerId = 0; var meshCounter = 0; foreach (var meshObject in core.meshSet) { workers[workerId].AddMesh(meshObject); meshCounter++; if (meshCounter >= meshPerThread && workerId < THREAD_MAX - 2) { meshCounter = 0; workerId++; } } core.meshSet.Clear(); // // kick off workers // foreach (var worker in workers) { worker.Run(); } localWatch.Stop(); // Debug.Log("MeshesPerThread: " + meshPerThread + " workers: " + workers.Length + " cuts: " + cutsTotal + " time: " + localWatch.ElapsedMilliseconds); return(true); }
/// <summary> /// find and isolate independent (not connecting) parts in a mesh /// </summary> public static List <ExploderMesh> IsolateMeshIslands(ExploderMesh mesh) { var triangles = mesh.triangles; var vertexCount = mesh.vertices.Length; // cache mesh data var trianglesNum = mesh.triangles.Length; var tangents = mesh.tangents; var colors = mesh.colors32; var vertices = mesh.vertices; var normals = mesh.normals; var uvs = mesh.uv; var useMeshTangents = tangents != null && tangents.Length > 0; var useVertexColors = colors != null && colors.Length > 0; var useNormals = normals != null && normals.Length > 0; if (trianglesNum <= 3) { return(null); } ExploderUtils.Assert(trianglesNum > 3, "IsolateMeshIslands error: " + trianglesNum); var lsHash = new LSHash(0.1f, vertexCount); var vertHash = new int[trianglesNum]; for (int i = 0; i < trianglesNum; i++) { vertHash[i] = lsHash.Hash(vertices[triangles[i]]); } var islands = new List <HashSet <int> > { new HashSet <int> { vertHash[0], vertHash[1], vertHash[2] } }; var islandsIdx = new List <List <int> > { new List <int>(trianglesNum) { 0, 1, 2 } }; var triVisited = new bool[trianglesNum]; triVisited[0] = true; triVisited[1] = true; triVisited[2] = true; var currIsland = islands[0]; var currIslandIdx = islandsIdx[0]; var counter = 3; var lastInvalidIdx = -1; var loopCounter = 0; while (true) { var foundIsland = false; for (int j = 3; j < trianglesNum; j += 3) { if (triVisited[j]) { continue; } if (currIsland.Contains(vertHash[j]) || currIsland.Contains(vertHash[j + 1]) || currIsland.Contains(vertHash[j + 2])) { currIsland.Add(vertHash[j]); currIsland.Add(vertHash[j + 1]); currIsland.Add(vertHash[j + 2]); currIslandIdx.Add(j); currIslandIdx.Add(j + 1); currIslandIdx.Add(j + 2); triVisited[j] = true; triVisited[j + 1] = true; triVisited[j + 2] = true; counter += 3; foundIsland = true; } else { lastInvalidIdx = j; } } if (counter == trianglesNum) { break; } if (!foundIsland) { // create new island currIsland = new HashSet <int> { vertHash[lastInvalidIdx], vertHash[lastInvalidIdx + 1], vertHash[lastInvalidIdx + 2] }; currIslandIdx = new List <int>(trianglesNum / 2) { lastInvalidIdx, lastInvalidIdx + 1, lastInvalidIdx + 2 }; islands.Add(currIsland); islandsIdx.Add(currIslandIdx); } loopCounter++; if (loopCounter > 100) { ExploderUtils.Log("10000 loop exceeded, islands: " + islands.Count); break; } } var islandNum = islands.Count; ExploderUtils.Assert(islandNum >= 1, "No island found!"); // no more than one islands if (islandNum == 1) { return(null); } var result = new List <ExploderMesh>(islands.Count); foreach (var island in islandsIdx) { var cutterMesh = new ExploderMesh(); var triCount = island.Count; var m = cutterMesh; var tt = new List <int>(triCount); var vs = new List <Vector3>(triCount); var ns = new List <Vector3>(triCount); var us = new List <Vector2>(triCount); var cs = new List <Color32>(triCount); var ts = new List <Vector4>(triCount); var triCache = new Dictionary <int, int>(trianglesNum); var centroid = Vector3.zero; var centroidCounter = 0; var triCounter = 0; foreach (var i in island) { var tri = triangles[i]; var id = 0; if (triCache.TryGetValue(tri, out id)) { tt.Add(id); continue; } tt.Add(triCounter); triCache.Add(tri, triCounter); triCounter++; centroid += vertices[tri]; centroidCounter++; vs.Add(vertices[tri]); us.Add(uvs[tri]); if (useNormals) { ns.Add(normals[tri]); } if (useVertexColors) { cs.Add(colors[tri]); } if (useMeshTangents) { ts.Add(tangents[tri]); } } m.vertices = vs.ToArray(); m.uv = us.ToArray(); if (useNormals) { m.normals = ns.ToArray(); } if (useVertexColors) { m.colors32 = cs.ToArray(); } if (useMeshTangents) { m.tangents = ts.ToArray(); } m.triangles = tt.ToArray(); cutterMesh.centroid = centroid / centroidCounter; result.Add(cutterMesh); } return(result); }
/// <summary> /// triangulate polygon, algorithm from wiki is fast enough /// http://wiki.unity3d.com/index.php?title=Triangulator /// </summary> /// <returns></returns> public bool Triangulate(Array <int> indicesArray) { // no holes supported if (holes.Count == 0) { indicesArray.Initialize(Points.Length * 3); int n = Points.Length; if (n < 3) { return(true); } var V = new int[n]; if (Area > 0) { for (int v = 0; v < n; v++) { V[v] = v; } } else { for (int v = 0; v < n; v++) { V[v] = (n - 1) - v; } } int nv = n; int count = 2 * nv; for (int m = 0, v = nv - 1; nv > 2;) { if ((count--) <= 0) { return(true); } int u = v; if (nv <= u) { u = 0; } v = u + 1; if (nv <= v) { v = 0; } int w = v + 1; if (nv <= w) { w = 0; } if (Snip(u, v, w, nv, V)) { int a, b, c, s, t; a = V[u]; b = V[v]; c = V[w]; indicesArray.Add(a); indicesArray.Add(b); indicesArray.Add(c); m++; for (s = v, t = v + 1; t < nv; s++, t++) { V[s] = V[t]; } nv--; count = 2 * nv; } } indicesArray.Reverse(); return(true); } else { // use poly2tri library to triangulate mesh with holes var p2tPoints = new List <PrimitivesPro.ThirdParty.P2T.PolygonPoint>(Points.Length); foreach (var point in Points) { p2tPoints.Add(new PrimitivesPro.ThirdParty.P2T.PolygonPoint(point.x, point.y)); } // create p2t polygon var p2tPolygon = new PrimitivesPro.ThirdParty.P2T.Polygon(p2tPoints); // add holes foreach (var polygonHole in holes) { var p2tHolePoints = new List <PrimitivesPro.ThirdParty.P2T.PolygonPoint>(polygonHole.Points.Length); foreach (var polygonPoint in polygonHole.Points) { p2tHolePoints.Add(new PrimitivesPro.ThirdParty.P2T.PolygonPoint(polygonPoint.x, polygonPoint.y)); } p2tPolygon.AddHole(new PrimitivesPro.ThirdParty.P2T.Polygon(p2tHolePoints)); } try { PrimitivesPro.ThirdParty.P2T.P2T.Triangulate(p2tPolygon); } catch (Exception ex) { ExploderUtils.Log("P2T Exception: " + ex); return(false); } var triangles = p2tPolygon.Triangles.Count; indicesArray.Initialize(triangles * 3); Points = new Vector2[triangles * 3]; var j = 0; // recalc min max Min.x = float.MaxValue; Min.y = float.MaxValue; Max.x = float.MinValue; Max.y = float.MinValue; for (int i = 0; i < triangles; i++) { indicesArray.Add((j + 0)); indicesArray.Add((j + 1)); indicesArray.Add((j + 2)); Points[j + 2].x = (float)p2tPolygon.Triangles[i].Points._0.X; Points[j + 2].y = (float)p2tPolygon.Triangles[i].Points._0.Y; Points[j + 1].x = (float)p2tPolygon.Triangles[i].Points._1.X; Points[j + 1].y = (float)p2tPolygon.Triangles[i].Points._1.Y; Points[j + 0].x = (float)p2tPolygon.Triangles[i].Points._2.X; Points[j + 0].y = (float)p2tPolygon.Triangles[i].Points._2.Y; // recalc min max for (int k = 0; k < 3; k++) { if (Points[j + k].x < Min.x) { Min.x = Points[j + k].x; } if (Points[j + k].y < Min.y) { Min.y = Points[j + k].y; } if (Points[j + k].x > Max.x) { Max.x = Points[j + k].x; } if (Points[j + k].y > Max.y) { Max.y = Points[j + k].y; } } j += 3; } return(true); } }
private List <MeshObject> GetMeshList() { List <GameObject> objects = null; if (core.parameters.Targets != null) { objects = new List <GameObject>(core.parameters.Targets); } else { if (core.parameters.DontUseTag) { var objs = Object.FindObjectsOfType(typeof(Explodable)); objects = new List <GameObject>(objs.Length); foreach (var o in objs) { var ex = (Explodable)o; if (ex) { objects.Add(ex.gameObject); } } } else { objects = new List <GameObject>(GameObject.FindGameObjectsWithTag(ExploderObject.Tag)); } } if (core.parameters.ExplodeSelf) { objects.Add(core.parameters.ExploderGameObject); } var list = new List <MeshObject>(objects.Count); int counter = 0; var ctr = Vector3.zero; var ctrCounter = 0; foreach (var o in objects) { // in case of destroyed objects if (!o) { continue; } // don't destroy the destroyer :) if (!core.parameters.ExplodeSelf && o == core.parameters.ExploderGameObject) { continue; } // stop scanning for object is case of settings.ExplodeSelf if (o != core.parameters.ExploderGameObject && core.parameters.ExplodeSelf && core.parameters.DisableRadiusScan) { continue; } if (core.parameters.Targets != null || IsInRadius(o)) { var meshData = GetMeshData(o); var meshDataLen = meshData.Count; for (var i = 0; i < meshDataLen; i++) { var centroid = meshData[i].centroid; // overwrite settings.Position in case of settings.Target if (core.parameters.Targets != null) { core.parameters.Position = centroid; ctr += centroid; ctrCounter++; } var distance = (centroid - core.parameters.Position).magnitude; // UnityEngine.Debug.Log("Distance: " + distance + " " + meshData[i].gameObject.name); list.Add(new MeshObject { id = counter++, mesh = new ExploderMesh(meshData[i].sharedMesh), material = meshData[i].sharedMaterial, transform = new ExploderTransform(meshData[i].gameObject.transform), parent = meshData[i].gameObject.transform.parent, position = meshData[i].gameObject.transform.position, rotation = meshData[i].gameObject.transform.rotation, localScale = meshData[i].gameObject.transform.localScale, bakeObject = meshData[i].gameObject, distanceRatio = GetDistanceRatio(distance, core.parameters.Radius), original = meshData[i].parentObject, skinnedOriginal = meshData[i].skinnedBakeOriginal, option = o.GetComponent <ExploderOption>(), }); } } } if (ctrCounter > 0) { ctr /= ctrCounter; core.parameters.Position = ctr; } if (list.Count == 0) { ExploderUtils.Log("No explodable objects found!"); return(list); } if (core.parameters.UniformFragmentDistribution || core.parameters.Targets != null) { var fragmentPerObject = core.parameters.TargetFragments / list.Count; int cnt = core.parameters.TargetFragments; foreach (var meshObject in list) { core.targetFragments[meshObject.id] = fragmentPerObject; cnt -= fragmentPerObject; } while (cnt > 0) { cnt--; var randMeshObject = list[UnityEngine.Random.Range(0, list.Count - 1)]; core.targetFragments[randMeshObject.id] += 1; } } else { var sum = 0.0f; var sumFragments = 0; foreach (var o in list) { sum += o.distanceRatio; } foreach (var mesh in list) { core.targetFragments[mesh.id] = (int)((mesh.distanceRatio / sum) * core.parameters.TargetFragments); sumFragments += core.targetFragments[mesh.id]; } if (sumFragments < core.parameters.TargetFragments) { var diff = core.parameters.TargetFragments - sumFragments; while (diff > 0) { foreach (var mesh in list) { core.targetFragments[mesh.id] += 1; diff--; if (diff == 0) { break; } } } } } return(list); }
private List <MeshData> GetMeshData(GameObject obj) { var renderers = obj.GetComponentsInChildren <MeshRenderer>(); var meshFilters = obj.GetComponentsInChildren <MeshFilter>(); ExploderUtils.Warning(renderers.Length == meshFilters.Length, "Renderers and meshes don't match!"); if (renderers.Length != meshFilters.Length) { return(new List <MeshData>()); } var outList = new List <MeshData>(renderers.Length); for (int i = 0; i < renderers.Length; i++) { if (meshFilters[i].sharedMesh == null) { ExploderUtils.Log("Missing shared mesh in " + meshFilters[i].name); continue; } if (!meshFilters[i].sharedMesh || !meshFilters[i].sharedMesh.isReadable) { UnityEngine.Debug.LogWarning("Mesh is not readable: " + meshFilters[i].name); continue; } if (/*IsExplodable(meshFilters[i].gameObject)*/ true) { outList.Add(new MeshData { sharedMesh = meshFilters[i].sharedMesh, sharedMaterial = renderers[i].sharedMaterial, gameObject = renderers[i].gameObject, centroid = renderers[i].bounds.center, parentObject = obj, }); } } // find skinned mesh var renderersSkinned = obj.GetComponentsInChildren <SkinnedMeshRenderer>(); for (int i = 0; i < renderersSkinned.Length; i++) { var bakeMesh = new Mesh(); renderersSkinned[i].BakeMesh(bakeMesh); var bakeObj = core.bakeSkinManager.CreateBakeObject(obj.name); var meshFilter = bakeObj.AddComponent <MeshFilter>(); meshFilter.sharedMesh = bakeMesh; var meshRenderer = bakeObj.AddComponent <MeshRenderer>(); meshRenderer.sharedMaterial = renderersSkinned[i].material; bakeObj.transform.position = renderersSkinned[i].gameObject.transform.position; bakeObj.transform.rotation = renderersSkinned[i].gameObject.transform.rotation; ExploderUtils.SetVisible(bakeObj, false); outList.Add(new MeshData { sharedMesh = bakeMesh, sharedMaterial = meshRenderer.sharedMaterial, gameObject = bakeObj, centroid = meshRenderer.bounds.center, parentObject = bakeObj, skinnedBakeOriginal = obj, }); } return(outList); }
public override bool Run(float frameBudget) { var count = core.postList.Count; while (core.poolIdx < count) { var mesh = core.postList[core.poolIdx]; core.poolIdx++; var islandsFound = false; if (core.parameters.SplitMeshIslands || (mesh.option && mesh.option.SplitMeshIslands)) { var meshIslands = MeshUtils.IsolateMeshIslands(mesh.mesh); if (meshIslands != null) { islandsFound = true; foreach (var meshIsland in meshIslands) { islands.Add(new MeshObject { mesh = meshIsland, material = mesh.material, transform = mesh.transform, original = mesh.original, skinnedOriginal = mesh.skinnedOriginal, parent = mesh.transform.parent, position = mesh.transform.position, rotation = mesh.transform.rotation, localScale = mesh.transform.localScale, option = mesh.option, }); } } } if (!islandsFound) { islands.Add(mesh); } if (Watch.ElapsedMilliseconds > frameBudget) { return(false); } } #if DBG ExploderUtils.Log("Replacing fragments: " + postList.Count + " by islands: " + islands.Count); #endif // replace postList by island list core.postList = islands; Watch.Stop(); return(true); }
public bool FindContours() { if (midPoints.Count == 0) { return(false); } #if DEBUG_CONTOUR Utils.Log("MidPoint: " + midPoints.Count); foreach (var midPoint in midPoints) { if (midPoint.Value.idNext == HashType.MaxValue || midPoint.Value.idPrev == HashType.MaxValue) { Utils.Log(midPoint.Value.id + " " + midPoint.Value.idNext + " " + midPoint.Value.idPrev); var sphere = PrimitivesPro.GameObjects.Sphere.Create(0.1f, 10, 0, 0, NormalsType.Vertex, PivotPosition.Center); sphere.transform.position = midPoint.Value.position; sphere.renderer.sharedMaterial.color = Color.red; } } #endif var midContour = new Dictionary <int, int>(midPoints.Count); var loopsMax = midPoints.Count * 2; // find contour var pStart = midPoints.GetFirstValue(); midContour.Add(pStart.id, pStart.vertexId); midPoints.Remove(pStart.id); var nextP = pStart.idNext; while (midPoints.Count > 0) { if (nextP == Int32.MaxValue) { return(false); } MidPoint p; if (!midPoints.TryGetValue(nextP, out p)) { contour.Clear(); // throw new Exception("Contour failed"); return(false); } // add new point on contour midContour.Add(p.id, p.vertexId); midPoints.Remove(p.id); if (midContour.ContainsKey(p.idNext)) { if (midContour.ContainsKey(p.idPrev)) { // closing the loop! contour.Add(new Dictionary <int, int>(midContour)); midContour.Clear(); if (midPoints.Count == 0) { break; } pStart = midPoints.GetFirstValue(); midContour.Add(pStart.id, pStart.vertexId); midPoints.Remove(pStart.id); nextP = pStart.idNext; continue; } nextP = p.idPrev; } else { nextP = p.idNext; } loopsMax--; if (loopsMax == 0) { // Utils.Assert(false, "ForeverLoop!"); ExploderUtils.Log("ForeverLoop!"); // throw new Exception("Contour failed"); contour.Clear(); return(false); } } return(true); }
protected virtual bool Cut(float frameBudget) { bool cutting = true; var cycleCounter = 0; bool timeBudgetStop = false; cutAttempt = 0; while (cutting) { cycleCounter++; if (cycleCounter > core.parameters.TargetFragments) { ExploderUtils.Log("Explode Infinite loop!"); return(true); } newFragments.Clear(); meshToRemove.Clear(); cutting = false; foreach (var mesh in core.meshSet) { if (!core.targetFragments.ContainsKey(mesh.id)) { Debug.Assert(false); } if (core.targetFragments[mesh.id] > 1) { var meshes = CutSingleMesh(mesh); cutting = true; if (meshes != null) { foreach (var cutterMesh in meshes) { newFragments.Add(new MeshObject { mesh = cutterMesh, material = mesh.material, transform = mesh.transform, id = mesh.id, original = mesh.original, skinnedOriginal = mesh.skinnedOriginal, bakeObject = mesh.bakeObject, parent = mesh.transform.parent, position = mesh.transform.position, rotation = mesh.transform.rotation, localScale = mesh.transform.localScale, option = mesh.option, }); } meshToRemove.Add(mesh); core.targetFragments[mesh.id] -= 1; // computation took more than settings.FrameBudget ... if (Watch.ElapsedMilliseconds > frameBudget && cycleCounter > 2) { timeBudgetStop = true; break; } } } } core.meshSet.ExceptWith(meshToRemove); core.meshSet.UnionWith(newFragments); if (timeBudgetStop) { break; } } // explosion is finished return(!timeBudgetStop); }