private void determinePresence(Transform root, Vector4 plane, out Dictionary <string, Transform> transformByName, out Dictionary <string, bool> frontPresence, out Dictionary <string, bool> backPresence) { List <Transform> allChildren = new List <Transform> (); concatenateHierarchy(root, allChildren); Vector3[] positions = new Vector3[allChildren.Count]; for (int i = 0; i < positions.Length; i++) { positions [i] = allChildren [i].position; } Matrix4x4 worldToLocal = root.worldToLocalMatrix; for (int i = 0; i < positions.Length; i++) { positions [i] = worldToLocal.MultiplyPoint3x4(positions [i]); } PlaneTriResult[] ptr = new PlaneTriResult[positions.Length]; for (int i = 0; i < positions.Length; i++) { var p = positions [i]; ptr [i] = p.x * plane.x + p.y * plane.y + p.z * plane.z + plane.w > 0f ? PlaneTriResult.PTR_FRONT : PlaneTriResult.PTR_BACK; } transformByName = new Dictionary <string, Transform> (); frontPresence = new Dictionary <string, bool> (); backPresence = new Dictionary <string, bool> (); bool duplicateNameWarning = false; for (int i = 0; i < ptr.Length; i++) { Transform t = allChildren [i]; string key = t.name; if (transformByName.ContainsKey(key)) { duplicateNameWarning = true; } transformByName [key] = t; frontPresence [key] = ptr [i] == PlaneTriResult.PTR_FRONT; backPresence [key] = ptr [i] == PlaneTriResult.PTR_BACK; } if (duplicateNameWarning) { Debug.LogWarning("Sliceable has children with non-unique names. Behaviour is undefined!"); } }
private void determinePresence(Transform root, Vector4 plane, out Dictionary<string,Transform> transformByName, out Dictionary<string,bool> frontPresence, out Dictionary<string,bool> backPresence) { List<Transform> allChildren = new List<Transform>(); concatenateHierarchy(root, allChildren); Vector3[] positions = new Vector3[allChildren.Count]; for(int i = 0; i < positions.Length; i++) { positions[i] = allChildren[i].position; } Matrix4x4 worldToLocal = root.worldToLocalMatrix; for(int i = 0; i < positions.Length; i++) { positions[i] = worldToLocal.MultiplyPoint3x4(positions[i]); } PlaneTriResult[] ptr = new PlaneTriResult[positions.Length]; for(int i = 0; i < positions.Length; i++) { ptr[i] = MuffinSliceCommon.getSidePlane(ref positions[i], ref plane); } transformByName = new Dictionary<string, Transform>(); frontPresence = new Dictionary<string, bool>(); backPresence = new Dictionary<string, bool>(); bool duplicateNameWarning = false; for(int i = 0; i < ptr.Length; i++) { Transform t = allChildren[i]; string key = t.name; if(transformByName.ContainsKey(key)) duplicateNameWarning = true; transformByName[key] = t; frontPresence[key] = ptr[i] == PlaneTriResult.PTR_FRONT; backPresence[key] = ptr[i] == PlaneTriResult.PTR_BACK; } if(duplicateNameWarning) Debug.LogWarning("Sliceable has children with non-unique names. Behaviour is undefined!"); }
public GameObject[] severByJoint(GameObject go, string jointName, float rootTipProgression, Vector3?planeNormal) { rootTipProgression = Mathf.Clamp01(rootTipProgression); //These here are in local space because they're only used to copy to the resultant meshes; they're not used //to transform the vertices. We expect a world-space slice input. Hackable hackable = null; { Hackable[] hackables = go.GetComponentsInChildren <Hackable>(); if (hackables.Length > 0) { if (hackables.Length > 1) { Debug.LogWarning("Limb Hacker found multiple slice configurations on object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Behavior is undefined."); } hackable = hackables[0]; } } //We need information about which BONES are getting severed. var allBones = LimbHacker.FindBonesInTree(go); var childTransformByName = new Dictionary <string, Transform>(); var parentKeyByKey = new Dictionary <string, string>(); foreach (Transform t in GetConcatenatedHierarchy(go.transform)) { childTransformByName[t.name] = t; Transform parent = t.parent; if (t == go.transform) { parent = null; } parentKeyByKey[t.name] = parent == null ? null : parent.name; } var severedByChildName = new Dictionary <string, bool>(); { foreach (string childName in childTransformByName.Keys) { severedByChildName[childName] = childName == jointName; } bool changesMade; do { changesMade = false; foreach (string childKey in childTransformByName.Keys) { bool severed = severedByChildName[childKey]; if (severed) { continue; } string parentKey = parentKeyByKey[childKey]; bool parentSevered; if (severedByChildName.TryGetValue(parentKey, out parentSevered) == false) { continue; } if (parentSevered) { severedByChildName[childKey] = true; changesMade = true; } } }while(changesMade); } GameObject frontObject, backObject; { var bonePresenceFront = new Dictionary <string, bool>(); var bonePresenceBack = new Dictionary <string, bool>(); foreach (KeyValuePair <string, bool> kvp in severedByChildName) { bonePresenceFront[kvp.Key] = kvp.Value; bonePresenceBack[kvp.Key] = !kvp.Value; } createResultObjects(go, hackable, childTransformByName, bonePresenceFront, bonePresenceBack, out frontObject, out backObject); } var skinnedMeshRenderers = go.GetComponentsInChildren <SkinnedMeshRenderer>(true); foreach (var smr in skinnedMeshRenderers) { var m = smr.sharedMesh; LoadSkinnedMeshRendererIntoCache(smr, true); var severedByBoneIndex = new Dictionary <int, bool>(); var mandatoryByBoneIndex = new bool[smr.bones.Length]; string severedJointKey = jointName; Dictionary <string, int> boneIndexByName = new Dictionary <string, int>(); List <string> orderedBoneNames = new List <string>(); foreach (Transform bone in smr.bones) { boneIndexByName[bone.name] = orderedBoneNames.Count; orderedBoneNames.Add(bone.name); } for (int boneIndex = 0; boneIndex < orderedBoneNames.Count; boneIndex++) { string boneName = orderedBoneNames[boneIndex]; severedByBoneIndex[boneIndex] = severedByChildName[boneName]; } Vector4 plane = Vector4.zero; bool willSliceThisMesh = boneIndexByName.ContainsKey(severedJointKey); if (willSliceThisMesh) { //We need to create a slice plane in local space. We're going to do that by using the bind poses //from the SEVERED limb, its PARENT and its CHILDREN to create a position and normal. Matrix4x4[] orderedBindPoses = smr.sharedMesh.bindposes; int severedJointIndex = boneIndexByName[severedJointKey]; Matrix4x4 severedJointMatrix = orderedBindPoses[severedJointIndex].inverse; Matrix4x4 severedJointParentMatrix = Matrix4x4.identity; if (parentKeyByKey.ContainsKey(severedJointKey)) { string severedJointParentKey = parentKeyByKey[severedJointKey]; if (boneIndexByName.ContainsKey(severedJointParentKey)) { int severedJointParentIndex = boneIndexByName[severedJointParentKey]; severedJointParentMatrix = orderedBindPoses[severedJointParentIndex].inverse; } } VectorAccumulator meanChildPosition = new VectorAccumulator(); for (int i = 0; i < boneIndexByName.Count; i++) { mandatoryByBoneIndex[i] = false; } if (parentKeyByKey.ContainsKey(severedJointKey)) { string parentKey = parentKeyByKey[severedJointKey]; if (boneIndexByName.ContainsKey(parentKey)) { mandatoryByBoneIndex[boneIndexByName[parentKey]] = true; } } if (rootTipProgression > 0f) { mandatoryByBoneIndex[boneIndexByName[jointName]] = true; List <string> childKeys = new List <string>(); foreach (KeyValuePair <string, string> kvp in parentKeyByKey) { if (kvp.Value == severedJointKey) { childKeys.Add(kvp.Key); } } List <int> childIndices = new List <int>(); foreach (string key in childKeys) { int childIndex; if (boneIndexByName.TryGetValue(key, out childIndex)) { childIndices.Add(childIndex); } } foreach (int index in childIndices) { Matrix4x4 childMatrix = orderedBindPoses[index].inverse; Vector3 childPosition = childMatrix.MultiplyPoint3x4(Vector3.zero); meanChildPosition.addFigure(childPosition); } } Vector3 position0 = severedJointParentMatrix.MultiplyPoint3x4(Vector3.zero); Vector3 position1 = severedJointMatrix.MultiplyPoint3x4(Vector3.zero); Vector3 position2 = meanChildPosition.mean; Vector3 deltaParent = position0 - position1; Vector3 deltaChildren = position1 - position2; Vector3 position = Vector3.Lerp(position1, position2, rootTipProgression); Vector3 normalFromParentToChild = -Vector3.Lerp(deltaParent, deltaChildren, rootTipProgression).normalized; if (planeNormal.HasValue) { Matrix4x4 fromWorldToLocalSpaceOfBone = smr.bones[severedJointIndex].worldToLocalMatrix; Vector3 v = planeNormal.Value; v = fromWorldToLocalSpaceOfBone.MultiplyVector(v); v = severedJointMatrix.MultiplyVector(v); v.Normalize(); if (Vector3.Dot(v, normalFromParentToChild) < 0f) { v = -v; } v = MuffinSliceCommon.clampNormalToBicone(v, normalFromParentToChild, 30f); planeNormal = v; } else { planeNormal = normalFromParentToChild; } plane = (Vector4)planeNormal.Value; plane.w = -(plane.x * position.x + plane.y * position.y + plane.z * position.z); } //We're going to create two new tentative meshes which contain ALL original vertices in order, //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them //over eliminates the need to remove doubles and do an On^2 search. int submeshCount = c.indices.Length; TurboList <int>[] _frontIndices = new TurboList <int> [submeshCount]; TurboList <int>[] _backIndices = new TurboList <int> [submeshCount]; PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count]; { BoneWeight[] weights = c.weights.array; Vector3[] vertices = c.vertices.array; int count = c.vertices.Count; bool[] whollySeveredByVertexIndex = new bool[count]; bool[] severableByVertexIndex = new bool[count]; bool[] mandatoryByVertexIndex = new bool[count]; const float minimumWeightForRelevance = 0.1f; for (int i = 0; i < severableByVertexIndex.Length; i++) { BoneWeight weight = weights[i]; bool whollySevered = true; bool severable = false; bool mandatory = false; int[] indices = { weight.boneIndex0, weight.boneIndex1, weight.boneIndex2, weight.boneIndex3 }; float[] scalarWeights = { weight.weight0, weight.weight1, weight.weight2, weight.weight3 }; for (int j = 0; j < 4; j++) { if (scalarWeights[j] > minimumWeightForRelevance) { int index = indices[j]; bool _severable = severedByBoneIndex[index]; bool _mandatory = mandatoryByBoneIndex[index]; whollySevered &= _severable; severable |= _severable; mandatory |= _mandatory; } } whollySeveredByVertexIndex[i] = whollySevered; severableByVertexIndex[i] = severable; mandatoryByVertexIndex[i] = mandatory; } for (int i = 0; i < sidePlanes.Length; i++) { if (willSliceThisMesh && mandatoryByVertexIndex[i]) { sidePlanes[i] = MuffinSliceCommon.getSidePlane(ref vertices[i], ref plane); } else if (whollySeveredByVertexIndex[i]) { sidePlanes[i] = PlaneTriResult.PTR_FRONT; } else if (willSliceThisMesh && severableByVertexIndex[i]) { sidePlanes[i] = MuffinSliceCommon.getSidePlane(ref vertices[i], ref plane); } else { sidePlanes[i] = PlaneTriResult.PTR_BACK; } } } TurboList <int> frontInfill = null; TurboList <int> backInfill = null; for (int j = 0; j < submeshCount; j++) { int initialCapacityIndices = Mathf.RoundToInt((float)c.indices[j].Length * factorOfSafetyIndices); _frontIndices[j] = new TurboList <int>(initialCapacityIndices); _backIndices[j] = new TurboList <int>(initialCapacityIndices); if (hackable.infillMaterial != null && c.mats[j] == hackable.infillMaterial) { frontInfill = _frontIndices[j]; backInfill = _backIndices[j]; } } if (hackable.infillMaterial != null && frontInfill == null) { frontInfill = new TurboList <int>(1024); backInfill = new TurboList <int>(1024); } for (int j = 0; j < submeshCount; j++) { int initialCapacityIndices = Mathf.RoundToInt((float)c.indices[j].Length * factorOfSafetyIndices); int[] _indices = c.indices[j]; TurboList <int> frontIndices = _frontIndices[j]; TurboList <int> backIndices = _backIndices[j]; TurboList <int> splitPending = new TurboList <int>(initialCapacityIndices); int[] indices = new int[3]; for (int i = 0; i < _indices.Length;) { indices[0] = _indices[i++]; indices[1] = _indices[i++]; indices[2] = _indices[i++]; // compute the side of the plane each vertex is on PlaneTriResult r1 = sidePlanes[indices[0]]; PlaneTriResult r2 = sidePlanes[indices[1]]; PlaneTriResult r3 = sidePlanes[indices[2]]; if (r1 == r2 && r1 == r3) // if all three vertices are on the same side of the plane. { if (r1 == PlaneTriResult.PTR_FRONT) // if all three are in front of the plane, then copy to the 'front' output triangle. { frontIndices.AddArray(indices); } else { backIndices.AddArray(indices); } } else if (willSliceThisMesh) { splitPending.AddArray(indices); } } if (willSliceThisMesh) { splitTrianglesLH(plane, c.vertices.array, sidePlanes, splitPending.ToArray(), c, frontIndices, backIndices, hackable.infillMode, frontInfill, backInfill); } } if (hackable.infillMaterial != null) { bool alreadyPresent = System.Array.IndexOf <Material>(c.mats, hackable.infillMaterial) >= 0; if (!alreadyPresent) { int oldLength = c.mats.Length, newLength = c.mats.Length + 1; Material[] newMats = new Material[newLength]; System.Array.Copy(c.mats, newMats, oldLength); newMats[newLength - 1] = hackable.infillMaterial; c.mats = newMats; TurboList <int>[] indexArray; indexArray = new TurboList <int> [newLength]; System.Array.Copy(_backIndices, indexArray, oldLength); indexArray[newLength - 1] = backInfill; _backIndices = indexArray; indexArray = new TurboList <int> [newLength]; System.Array.Copy(_frontIndices, indexArray, oldLength); indexArray[newLength - 1] = frontInfill; _frontIndices = indexArray; submeshCount++; } } Vector3[] geoSubsetOne, geoSubsetTwo; Vector3[] normalsSubsetOne, normalsSubsetTwo; Vector2[] uvSubsetOne, uvSubsetTwo; BoneWeight[] weightSubsetOne, weightSubsetTwo; int[][] indexSubsetOne, indexSubsetTwo; indexSubsetOne = new int[submeshCount][]; indexSubsetTwo = new int[submeshCount][]; targetSubsetOne.Clear(); targetSubsetTwo.Clear(); int transferTableMaximumKey = c.vertices.Count; int[] transferTableOne = new int[transferTableMaximumKey]; int[] transferTableTwo = new int[transferTableMaximumKey]; for (int i = 0; i < transferTableOne.Length; i++) { transferTableOne[i] = -1; } for (int i = 0; i < transferTableTwo.Length; i++) { transferTableTwo[i] = -1; } for (int i = 0; i < submeshCount; i++) { perfectSubsetRD(_frontIndices[i], c.vertices, c.normals, c.UVs, c.weights, out indexSubsetOne[i], targetSubsetOne, ref transferTableOne); } for (int i = 0; i < submeshCount; i++) { perfectSubsetRD(_backIndices[i], c.vertices, c.normals, c.UVs, c.weights, out indexSubsetTwo[i], targetSubsetTwo, ref transferTableTwo); } //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an //assignment to vertices whenever the vertex count changes from zero to non-zero. Mesh frontMesh = new Mesh(); Mesh backMesh = new Mesh(); var frontSMR = GetSkinnedMeshRendererWithName(frontObject, smr.name); var backSMR = GetSkinnedMeshRendererWithName(backObject, smr.name); if (targetSubsetOne.vertices.Count > 0) { frontSMR.materials = c.mats; frontSMR.sharedMesh = frontMesh; frontMesh.vertices = targetSubsetOne.vertices.ToArray(); frontMesh.normals = targetSubsetOne.normals.ToArray(); frontMesh.uv = targetSubsetOne.UVs.ToArray(); frontMesh.boneWeights = targetSubsetOne.weights.ToArray(); frontMesh.subMeshCount = submeshCount; frontMesh.bindposes = m.bindposes; for (int i = 0; i < submeshCount; i++) { frontMesh.SetTriangles(indexSubsetOne[i], i); } } else { GameObject.DestroyImmediate(frontSMR); } if (targetSubsetTwo.vertices.Count > 0) { backSMR.materials = c.mats; backSMR.sharedMesh = backMesh; backMesh.vertices = targetSubsetTwo.vertices.ToArray(); backMesh.normals = targetSubsetTwo.normals.ToArray(); backMesh.uv = targetSubsetTwo.UVs.ToArray(); backMesh.boneWeights = targetSubsetTwo.weights.ToArray(); backMesh.subMeshCount = submeshCount; backMesh.bindposes = m.bindposes; for (int i = 0; i < submeshCount; i++) { backMesh.SetTriangles(indexSubsetTwo[i], i); } } else { GameObject.DestroyImmediate(backSMR); } } var results = new GameObject[] { frontObject, backObject }; hackable.handleSlice(results); return(results); }
public static void calcConvexDecomposition(List <float3> vertices, List <int> indices, ConvexDecompositionCallback callback, float masterVolume, int depth, int maxDepth, float concavePercent, float mergePercent) { float4 plane = new float4(); bool split = false; if (depth < maxDepth) { float volume = 0f; float c = Concavity.computeConcavity(vertices, indices, ref plane, ref volume); if (depth == 0) { masterVolume = volume; } float percent = (c * 100.0f) / masterVolume; if (percent > concavePercent) // if great than 5% of the total volume is concave, go ahead and keep splitting. { split = true; } } if (depth >= maxDepth || !split) { HullResult result = new HullResult(); HullDesc desc = new HullDesc(); desc.SetHullFlag(HullFlag.QF_TRIANGLES); desc.Vertices = vertices; HullError ret = HullUtils.CreateConvexHull(desc, ref result); if (ret == HullError.QE_OK) { ConvexResult r = new ConvexResult(result.OutputVertices, result.Indices); callback(r); } return; } List <int> ifront = new List <int>(); List <int> iback = new List <int>(); VertexPool vfront = new VertexPool(); VertexPool vback = new VertexPool(); // ok..now we are going to 'split' all of the input triangles against this plane! for (int i = 0; i < indices.Count / 3; i++) { int i1 = indices[i * 3 + 0]; int i2 = indices[i * 3 + 1]; int i3 = indices[i * 3 + 2]; FaceTri t = new FaceTri(vertices, i1, i2, i3); float3[] front = new float3[4]; float3[] back = new float3[4]; int fcount = 0; int bcount = 0; PlaneTriResult result = PlaneTri.planeTriIntersection(plane, t, 0.00001f, ref front, out fcount, ref back, out bcount); if (fcount > 4 || bcount > 4) { result = PlaneTri.planeTriIntersection(plane, t, 0.00001f, ref front, out fcount, ref back, out bcount); } switch (result) { case PlaneTriResult.PTR_FRONT: Debug.Assert(fcount == 3); addTri(vfront, ifront, front[0], front[1], front[2]); break; case PlaneTriResult.PTR_BACK: Debug.Assert(bcount == 3); addTri(vback, iback, back[0], back[1], back[2]); break; case PlaneTriResult.PTR_SPLIT: Debug.Assert(fcount >= 3 && fcount <= 4); Debug.Assert(bcount >= 3 && bcount <= 4); addTri(vfront, ifront, front[0], front[1], front[2]); addTri(vback, iback, back[0], back[1], back[2]); if (fcount == 4) { addTri(vfront, ifront, front[0], front[2], front[3]); } if (bcount == 4) { addTri(vback, iback, back[0], back[2], back[3]); } break; } } // ok... here we recursively call if (ifront.Count > 0) { // 20131224 not used int vcount = vfront.GetSize(); List <float3> vertices2 = vfront.GetVertices(); for (int i = 0; i < vertices2.Count; i++) { vertices2[i] = new float3(vertices2[i]); } // 20131224 not used int tcount = ifront.Count / 3; calcConvexDecomposition(vertices2, ifront, callback, masterVolume, depth + 1, maxDepth, concavePercent, mergePercent); } ifront.Clear(); vfront.Clear(); if (iback.Count > 0) { // 20131224 not used int vcount = vback.GetSize(); List <float3> vertices2 = vback.GetVertices(); // 20131224 not used int tcount = iback.Count / 3; calcConvexDecomposition(vertices2, iback, callback, masterVolume, depth + 1, maxDepth, concavePercent, mergePercent); } iback.Clear(); vback.Clear(); }
public static PlaneTriResult planeTriIntersection(float4 plane, FaceTri triangle, float epsilon, ref float3[] front, out int fcount, ref float3[] back, out int bcount) { fcount = 0; bcount = 0; // get the three vertices of the triangle. float3 p1 = triangle.P1; float3 p2 = triangle.P2; float3 p3 = triangle.P3; PlaneTriResult r1 = getSidePlane(p1, plane, epsilon); // compute the side of the plane each vertex is on PlaneTriResult r2 = getSidePlane(p2, plane, epsilon); PlaneTriResult r3 = getSidePlane(p3, plane, epsilon); if (r1 == r2 && r1 == r3) // if all three vertices are on the same side of the plane. { if (r1 == PlaneTriResult.PTR_FRONT) // if all three are in front of the plane, then copy to the 'front' output triangle. { add(p1, front, ref fcount); add(p2, front, ref fcount); add(p3, front, ref fcount); } else { add(p1, back, ref bcount); // if all three are in 'back' then copy to the 'back' output triangle. add(p2, back, ref bcount); add(p3, back, ref bcount); } return(r1); // if all three points are on the same side of the plane return result } // ok.. we need to split the triangle at the plane. // First test ray segment P1 to P2 if (r1 == r2) // if these are both on the same side... { if (r1 == PlaneTriResult.PTR_FRONT) { add(p1, front, ref fcount); add(p2, front, ref fcount); } else { add(p1, back, ref bcount); add(p2, back, ref bcount); } } else { float3 split = new float3(); intersect(p1, p2, split, plane); if (r1 == PlaneTriResult.PTR_FRONT) { add(p1, front, ref fcount); add(split, front, ref fcount); add(split, back, ref bcount); add(p2, back, ref bcount); } else { add(p1, back, ref bcount); add(split, back, ref bcount); add(split, front, ref fcount); add(p2, front, ref fcount); } } // Next test ray segment P2 to P3 if (r2 == r3) // if these are both on the same side... { if (r3 == PlaneTriResult.PTR_FRONT) { add(p3, front, ref fcount); } else { add(p3, back, ref bcount); } } else { float3 split = new float3(); // split the point intersect(p2, p3, split, plane); if (r3 == PlaneTriResult.PTR_FRONT) { add(split, front, ref fcount); add(split, back, ref bcount); add(p3, front, ref fcount); } else { add(split, front, ref fcount); add(split, back, ref bcount); add(p3, back, ref bcount); } } // Next test ray segment P3 to P1 if (r3 != r1) // if these are both on the same side... { float3 split = new float3(); // split the point intersect(p3, p1, split, plane); if (r1 == PlaneTriResult.PTR_FRONT) { add(split, front, ref fcount); add(split, back, ref bcount); } else { add(split, front, ref fcount); add(split, back, ref bcount); } } return(PlaneTriResult.PTR_SPLIT); }
public GameObject[] splitByPlane(GameObject go, Vector4 plane, bool destroyOriginal) { if(go.GetComponentInChildren<SkinnedMeshRenderer>() != null) { return splitByPlaneRD(go, plane, destroyOriginal); } Sliceable sliceable = ensureSliceable(go); if(!sliceable.currentlySliceable) { GameObject[] result = { go }; return result; } InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : infills; MeshCache c = null; do { MeshFilter filter = getMeshFilter(sliceable); Mesh m = filter.sharedMesh; if(m == null) { break; } if(meshCaches != null && meshCaches.ContainsKey(m)) { c = meshCaches[m]; //The mesh cache will be directly modified under the assumption that this will be discarded shortly //and thus picked up by the GC. It will grow in size; it will not shrink. Thus we do not want to //operate on the original, semi-persistent mesh caches that were preloaded on boot. Instead, we want //to make a clone. if(c.wasPreloaded) { c = c.clone(); } } else c = cacheFromGameObject(sliceable, true); } while(false); if(c == null) { Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced."); GameObject[] result = { go }; return result; } int submeshCount = c.indices.Length; //We're going to create two new tentative meshes which contain ALL original vertices in order, //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them //over eliminates the need to remove doubles and do an On^2 search. TurboList<int>[] _frontIndices = new TurboList<int>[ submeshCount ]; TurboList<int>[] _backIndices = new TurboList<int>[ submeshCount ]; PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count]; { Vector3[] vertices = c.vertices.array; for(int i = 0; i < sidePlanes.Length; i++) { sidePlanes[i] = getSidePlane(ref vertices[i], ref plane); } } for(int j = 0; j < submeshCount; j++) { int initialCapacityIndices = Mathf.RoundToInt((float) c.indices[j].Length * factorOfSafetyIndices); _frontIndices[j] = new TurboList<int>(initialCapacityIndices); _backIndices[j] = new TurboList<int>(initialCapacityIndices); int[] _indices = c.indices[j]; TurboList<int> frontIndices = _frontIndices[j]; TurboList<int> backIndices = _backIndices[j]; TurboList<int> splitPending = new TurboList<int>(initialCapacityIndices); int[] indices = new int[3]; for(int i = 0; i < _indices.Length; ) { indices[0] = _indices[i++]; indices[1] = _indices[i++]; indices[2] = _indices[i++]; // compute the side of the plane each vertex is on PlaneTriResult r1 = sidePlanes[indices[0]]; PlaneTriResult r2 = sidePlanes[indices[1]]; PlaneTriResult r3 = sidePlanes[indices[2]]; if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. { if ( r1 == PlaneTriResult.PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. { frontIndices.AddArray(indices); } else { backIndices.AddArray(indices); } } else { splitPending.AddArray(indices); } } InfillConfiguration ifc = null; if(j < c.mats.Length) { Material mat = c.mats[j]; foreach(InfillConfiguration _ifc in ourInfills) { if(_ifc.material == mat) { ifc = _ifc; } } } splitTriangles(plane, splitPending.ToArray(), c, ifc, frontIndices, backIndices); } GameObject[] results; bool onlyHaveOne = true; for(int i = 0; i < c.indices.Length; i++) { onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0; } if(onlyHaveOne) { //Do nothing results = new GameObject[1]; results[0] = go; } else { MeshCache frontCache = new MeshCache(); frontCache.vertices = c.vertices; if(sliceable.channelNormals) frontCache.normals = c.normals; frontCache.UVs = c.UVs; frontCache.mats = c.mats; MeshCache backCache = new MeshCache(); backCache.vertices = c.vertices; if(sliceable.channelNormals) backCache.normals = c.normals; backCache.UVs = c.UVs; backCache.mats = c.mats; frontCache.indices = new int[submeshCount][]; backCache.indices = new int[submeshCount][]; for(int i = 0; i < submeshCount; i++) { frontCache.indices[i] = _frontIndices[i].ToArray(); backCache.indices[i] = _backIndices[i].ToArray(); } Vector3[] geoSubsetOne, geoSubsetTwo; Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null; Vector2[] uvSubsetOne, uvSubsetTwo; int[][] indexSubsetOne, indexSubsetTwo; indexSubsetOne = new int[submeshCount][]; indexSubsetTwo = new int[submeshCount][]; //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0, //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance. int estimateOne = 0, estimateTwo = 0; TurboList<Vector3> _geoSubsetOne = null, _geoSubsetTwo = null, _normalSubsetOne = null, _normalSubsetTwo = null; TurboList<Vector2> _uvSubsetOne = null, _uvSubsetTwo = null; _geoSubsetOne = new TurboList<Vector3>(estimateOne); _geoSubsetTwo = new TurboList<Vector3>(estimateTwo); if(sliceable.channelNormals) { _normalSubsetOne = new TurboList<Vector3>(estimateOne); _normalSubsetTwo = new TurboList<Vector3>(estimateTwo); } _uvSubsetOne = new TurboList<Vector2>(estimateOne); _uvSubsetTwo = new TurboList<Vector2>(estimateTwo); int transferTableMaximumKey = c.vertices.Count; int[] transferTableOne = new int[transferTableMaximumKey]; int[] transferTableTwo = new int[transferTableMaximumKey]; for(int i = 0; i < transferTableOne.Length; i++) transferTableOne[i] = -1; for(int i = 0; i < transferTableTwo.Length; i++) transferTableTwo[i] = -1; for(int i = 0; i < submeshCount; i++) perfectSubset(_frontIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, ref transferTableOne ); for(int i = 0; i < submeshCount; i++) perfectSubset(_backIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, ref transferTableTwo ); geoSubsetOne = _geoSubsetOne.ToArray(); geoSubsetTwo = _geoSubsetTwo.ToArray(); if(sliceable.channelNormals) { normalsSubsetOne = _normalSubsetOne.ToArray(); normalsSubsetTwo = _normalSubsetTwo.ToArray(); } uvSubsetOne = _uvSubsetOne.ToArray(); uvSubsetTwo = _uvSubsetTwo.ToArray(); //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an //assignment to vertices whenever the vertex count changes from zero to non-zero. Mesh frontMesh = new Mesh(); Mesh backMesh = new Mesh(); GameObject frontObject, backObject; createResultObjects(go, sliceable, false, plane, out frontObject, out backObject); getMeshFilter(frontObject.GetComponent<Sliceable>()).mesh = frontMesh; getMeshFilter(backObject.GetComponent<Sliceable>()).mesh = backMesh; frontMesh.vertices = geoSubsetOne; backMesh.vertices = geoSubsetTwo; if(sliceable.channelNormals) { frontMesh.normals = normalsSubsetOne; backMesh.normals = normalsSubsetTwo; } frontMesh.uv = uvSubsetOne; backMesh.uv = uvSubsetTwo; frontMesh.subMeshCount = submeshCount; backMesh.subMeshCount = submeshCount; for(int i = 0 ; i < submeshCount; i++) { frontMesh.SetTriangles(indexSubsetOne[i], i); backMesh.SetTriangles(indexSubsetTwo[i], i); } if(meshCaches != null) { if(go.GetComponent<DeletionCallback>() == null) { frontObject.AddComponent<DeletionCallback>(); backObject.AddComponent<DeletionCallback>(); } DeletionCallback frontCallback = frontObject.GetComponent<DeletionCallback>(); DeletionCallback backCallback = backObject.GetComponent<DeletionCallback>(); frontCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh); backCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh); frontCallback.mesh = frontMesh; backCallback.mesh = backMesh; meshCaches[frontMesh] = frontCache; meshCaches[backMesh] = backCache; } else { DeletionCallback frontCallback = frontObject.GetComponent<DeletionCallback>(); DeletionCallback backCallback = backObject.GetComponent<DeletionCallback>(); if(frontCallback != null) GameObject.DestroyImmediate(frontCallback); if(backCallback != null) GameObject.DestroyImmediate(backCallback); } if(destroyOriginal) GameObject.Destroy(go); results = new GameObject[2]; results[0] = frontObject; results[1] = backObject; if(sliceable != null && sliceable.refreshColliders) { foreach(GameObject r in results) { Collider collider = r.GetComponent<Collider>(); if(collider != null) { if(collider is BoxCollider) { GameObject.DestroyImmediate(collider); r.AddComponent<BoxCollider>(); } else if(collider is SphereCollider) { GameObject.DestroyImmediate(collider); r.AddComponent<SphereCollider>(); } else if(collider is MeshCollider) { MeshCollider mc = (MeshCollider) collider; bool isFront = r == frontObject; Mesh mesh = isFront ? frontMesh : backMesh; mc.sharedMesh = mesh; } } } } if(sliceable != null) sliceable.handleSlice(results); } return results; }
private GameObject[] _splitByPlane(GameObject go, Vector4 planeInLocalSpace, bool destroyOriginal, bool callHandlers = true) { Sliceable sliceable = ensureSliceable(go); if(!sliceable.currentlySliceable) { GameObject[] result = { go }; return result; } InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : new InfillConfiguration[0]; MeshCache c = null; do { Mesh m = getMesh(sliceable); if(m == null) { break; } if(meshCaches.ContainsKey(m)) { c = meshCaches[m]; } else c = cacheFromGameObject(sliceable, true); } while(false); if(c == null) { Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced."); GameObject[] result = { go }; return result; } int submeshCount = c.indices.Length; //We're going to create two new tentative meshes which contain ALL original vertices in order, //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them //over eliminates the need to remove doubles and do an On^2 search. TurboList<int>[] _frontIndices = new TurboList<int>[ submeshCount ]; TurboList<int>[] _backIndices = new TurboList<int>[ submeshCount ]; PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count]; { Vector3[] vertices = c.vertices.array; for(int i = 0; i < sidePlanes.Length; i++) { sidePlanes[i] = MuffinSliceCommon.getSidePlane(ref vertices[i], ref planeInLocalSpace); } } for(int j = 0; j < submeshCount; j++) { int initialCapacityIndices = Mathf.RoundToInt((float) c.indices[j].Length * factorOfSafetyIndices); _frontIndices[j] = new TurboList<int>(initialCapacityIndices); _backIndices[j] = new TurboList<int>(initialCapacityIndices); int[] _indices = c.indices[j]; TurboList<int> frontIndices = _frontIndices[j]; TurboList<int> backIndices = _backIndices[j]; TurboList<int> splitPending = new TurboList<int>(initialCapacityIndices); int[] indices = new int[3]; for(int i = 0; i < _indices.Length; ) { indices[0] = _indices[i++]; indices[1] = _indices[i++]; indices[2] = _indices[i++]; // compute the side of the plane each vertex is on PlaneTriResult r1 = sidePlanes[indices[0]]; PlaneTriResult r2 = sidePlanes[indices[1]]; PlaneTriResult r3 = sidePlanes[indices[2]]; if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. { if ( r1 == PlaneTriResult.PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. { frontIndices.AddArray(indices); } else { backIndices.AddArray(indices); } } else { splitPending.AddArray(indices); } } InfillConfiguration ifc = null; if(j < c.mats.Length) { Material mat = c.mats[j]; foreach(InfillConfiguration _ifc in ourInfills) { if(_ifc.material == mat) { ifc = _ifc; } } } if(splitPending.Count > 0) { splitTriangles(planeInLocalSpace, splitPending.ToArray(), c, ifc, frontIndices, backIndices); } } GameObject[] results; bool onlyHaveOne = true; for(int i = 0; i < c.indices.Length; i++) { onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0; } if(onlyHaveOne) { //Do nothing results = new GameObject[1]; results[0] = go; } else { MeshCache frontCache = new MeshCache(); frontCache.vertices = c.vertices; if(sliceable.channelNormals) frontCache.normals = c.normals; frontCache.coords = c.coords; frontCache.coords2 = c.coords2; frontCache.mats = c.mats; MeshCache backCache = new MeshCache(); backCache.vertices = c.vertices; if(sliceable.channelNormals) backCache.normals = c.normals; backCache.coords = c.coords; backCache.coords2 = c.coords2; backCache.mats = c.mats; frontCache.indices = new int[submeshCount][]; backCache.indices = new int[submeshCount][]; for(int i = 0; i < submeshCount; i++) { frontCache.indices[i] = _frontIndices[i].ToArray(); backCache.indices[i] = _backIndices[i].ToArray(); } Vector3[] geoSubsetOne, geoSubsetTwo; Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null; Vector2[] uvSubsetOne, uvSubsetTwo; Vector2[] uv2SubsetOne = null, uv2SubsetTwo = null; int[][] indexSubsetOne, indexSubsetTwo; indexSubsetOne = new int[submeshCount][]; indexSubsetTwo = new int[submeshCount][]; //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0, //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance. int estimateOne = 0, estimateTwo = 0; TurboList<Vector3> _geoSubsetOne = null, _geoSubsetTwo = null, _normalSubsetOne = null, _normalSubsetTwo = null; TurboList<Vector2> _uvSubsetOne = null, _uvSubsetTwo = null; TurboList<Vector2> _uv2SubsetOne = null, _uv2SubsetTwo = null; _geoSubsetOne = new TurboList<Vector3>(estimateOne); _geoSubsetTwo = new TurboList<Vector3>(estimateTwo); if(sliceable.channelNormals) { _normalSubsetOne = new TurboList<Vector3>(estimateOne); _normalSubsetTwo = new TurboList<Vector3>(estimateTwo); } _uvSubsetOne = new TurboList<Vector2>(estimateOne); _uvSubsetTwo = new TurboList<Vector2>(estimateTwo); if(sliceable.channelUV2) { _uv2SubsetOne = new TurboList<Vector2>(estimateOne); _uv2SubsetTwo = new TurboList<Vector2>(estimateTwo); } int transferTableMaximumKey = c.vertices.Count; int[] transferTableOne = new int[transferTableMaximumKey]; int[] transferTableTwo = new int[transferTableMaximumKey]; for(int i = 0; i < transferTableOne.Length; i++) transferTableOne[i] = -1; for(int i = 0; i < transferTableTwo.Length; i++) transferTableTwo[i] = -1; for(int i = 0; i < submeshCount; i++) perfectSubset(_frontIndices[i], c.vertices, c.normals, c.coords, c.coords2, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, _uv2SubsetOne, ref transferTableOne ); for(int i = 0; i < submeshCount; i++) perfectSubset(_backIndices[i], c.vertices, c.normals, c.coords, c.coords2, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, _uv2SubsetTwo, ref transferTableTwo ); geoSubsetOne = _geoSubsetOne.ToArray(); geoSubsetTwo = _geoSubsetTwo.ToArray(); if(sliceable.channelNormals) { normalsSubsetOne = _normalSubsetOne.ToArray(); normalsSubsetTwo = _normalSubsetTwo.ToArray(); } uvSubsetOne = _uvSubsetOne.ToArray(); uvSubsetTwo = _uvSubsetTwo.ToArray(); if(sliceable.channelUV2) { uv2SubsetOne = _uv2SubsetOne.ToArray(); uv2SubsetTwo = _uv2SubsetTwo.ToArray(); } //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an //assignment to vertices whenever the vertex count changes from zero to non-zero. Mesh frontMesh = new Mesh(); Mesh backMesh = new Mesh(); GameObject frontObject, backObject; createResultObjects(go, sliceable, false, planeInLocalSpace, out frontObject, out backObject); ensureSliceable(frontObject); ensureSliceable(backObject); setMesh(frontObject.GetComponent<Sliceable>(), frontMesh); setMesh(backObject.GetComponent<Sliceable>(), backMesh); frontMesh.vertices = geoSubsetOne; backMesh.vertices = geoSubsetTwo; if(sliceable.channelTangents) { Vector4[] tangentsOne, tangentsTwo; int[] concatenatedIndicesOne = concatenateIndexArrays(indexSubsetOne); int[] concatenatedIndicesTwo = concatenateIndexArrays(indexSubsetTwo); RealculateTangents(geoSubsetOne, normalsSubsetOne, uvSubsetOne, concatenatedIndicesOne, out tangentsOne); RealculateTangents(geoSubsetTwo, normalsSubsetTwo, uvSubsetTwo, concatenatedIndicesTwo, out tangentsTwo); frontMesh.tangents = tangentsOne; backMesh.tangents = tangentsTwo; } if(sliceable.channelNormals) { frontMesh.normals = normalsSubsetOne; backMesh.normals = normalsSubsetTwo; } frontMesh.uv = uvSubsetOne; backMesh.uv = uvSubsetTwo; if(sliceable.channelUV2) { frontMesh.uv2 = uv2SubsetOne; backMesh.uv2 = uv2SubsetTwo; } frontMesh.subMeshCount = submeshCount; backMesh.subMeshCount = submeshCount; for(int i = 0 ; i < submeshCount; i++) { frontMesh.SetTriangles(indexSubsetOne[i], i); backMesh.SetTriangles(indexSubsetTwo[i], i); } TSCallbackOnDestroy frontCallback = frontObject.GetComponent<TSCallbackOnDestroy>(); TSCallbackOnDestroy backCallback = backObject.GetComponent<TSCallbackOnDestroy>(); if(frontCallback == null) { frontCallback = frontObject.AddComponent<TSCallbackOnDestroy>(); } if(backCallback == null) { backCallback = backObject.AddComponent<TSCallbackOnDestroy>(); } frontCallback.callWithMeshOnDestroy = releaseMesh; frontCallback.mesh = frontMesh; backCallback.callWithMeshOnDestroy = releaseMesh; backCallback.mesh = backMesh; meshCaches[frontMesh] = frontCache; meshCaches[backMesh] = backCache; results = new GameObject[2]; results[0] = frontObject; results[1] = backObject; if(sliceable != null && sliceable.refreshColliders) { foreach(GameObject r in results) { Collider collider = r.collider; if(collider != null) { bool isTrigger = collider.isTrigger; if(collider is BoxCollider) { GameObject.DestroyImmediate(collider); collider = r.AddComponent<BoxCollider>(); } else if(collider is SphereCollider) { GameObject.DestroyImmediate(collider); collider = r.AddComponent<SphereCollider>(); } else if(collider is MeshCollider) { MeshCollider mc = (MeshCollider) collider; bool isFront = r == frontObject; Mesh mesh = isFront ? frontMesh : backMesh; mc.sharedMesh = mesh; } collider.isTrigger = isTrigger; } } } if(callHandlers && sliceable != null) sliceable.handleSlice(results); if(destroyOriginal) GameObject.Destroy(go); } return results; }
public GameObject[] splitByPlane(GameObject go, Vector4 plane, bool destroyOriginal) { if (go.GetComponentInChildren <SkinnedMeshRenderer>() != null) { return(splitByPlaneRD(go, plane, destroyOriginal)); } Sliceable sliceable = ensureSliceable(go); if (!sliceable.currentlySliceable) { GameObject[] result = { go }; return(result); } InfillConfiguration[] ourInfills = sliceable.infillers.Length > 0 ? sliceable.infillers : infills; MeshCache c = null; do { MeshFilter filter = getMeshFilter(sliceable); Mesh m = filter.sharedMesh; if (m == null) { break; } if (meshCaches != null && meshCaches.ContainsKey(m)) { c = meshCaches[m]; //The mesh cache will be directly modified under the assumption that this will be discarded shortly //and thus picked up by the GC. It will grow in size; it will not shrink. Thus we do not want to //operate on the original, semi-persistent mesh caches that were preloaded on boot. Instead, we want //to make a clone. if (c.wasPreloaded) { c = c.clone(); } } else { c = cacheFromGameObject(sliceable, true); } }while(false); if (c == null) { Debug.LogWarning("Turbo Slicer cannot find mesh filter in object '" + go.name + "' in scene '" + Application.loadedLevelName + "'! Only objects featuring a mesh filter can be sliced."); GameObject[] result = { go }; return(result); } int submeshCount = c.indices.Length; //We're going to create two new tentative meshes which contain ALL original vertices in order, //plus room for new vertices. Not all of these copied vertices will be addressed, but copying them //over eliminates the need to remove doubles and do an On^2 search. TurboList <int>[] _frontIndices = new TurboList <int> [submeshCount]; TurboList <int>[] _backIndices = new TurboList <int> [submeshCount]; PlaneTriResult[] sidePlanes = new PlaneTriResult[c.vertices.Count]; { Vector3[] vertices = c.vertices.array; for (int i = 0; i < sidePlanes.Length; i++) { sidePlanes[i] = getSidePlane(ref vertices[i], ref plane); } } for (int j = 0; j < submeshCount; j++) { int initialCapacityIndices = Mathf.RoundToInt((float)c.indices[j].Length * factorOfSafetyIndices); _frontIndices[j] = new TurboList <int>(initialCapacityIndices); _backIndices[j] = new TurboList <int>(initialCapacityIndices); int[] _indices = c.indices[j]; TurboList <int> frontIndices = _frontIndices[j]; TurboList <int> backIndices = _backIndices[j]; TurboList <int> splitPending = new TurboList <int>(initialCapacityIndices); int[] indices = new int[3]; for (int i = 0; i < _indices.Length;) { indices[0] = _indices[i++]; indices[1] = _indices[i++]; indices[2] = _indices[i++]; // compute the side of the plane each vertex is on PlaneTriResult r1 = sidePlanes[indices[0]]; PlaneTriResult r2 = sidePlanes[indices[1]]; PlaneTriResult r3 = sidePlanes[indices[2]]; if (r1 == r2 && r1 == r3) // if all three vertices are on the same side of the plane. { if (r1 == PlaneTriResult.PTR_FRONT) // if all three are in front of the plane, then copy to the 'front' output triangle. { frontIndices.AddArray(indices); } else { backIndices.AddArray(indices); } } else { splitPending.AddArray(indices); } } InfillConfiguration ifc = null; if (j < c.mats.Length) { Material mat = c.mats[j]; foreach (InfillConfiguration _ifc in ourInfills) { if (_ifc.material == mat) { ifc = _ifc; } } } splitTriangles(plane, splitPending.ToArray(), c, ifc, frontIndices, backIndices); } GameObject[] results; bool onlyHaveOne = true; for (int i = 0; i < c.indices.Length; i++) { onlyHaveOne &= _frontIndices[i].Count == 0 || _backIndices[i].Count == 0; } if (onlyHaveOne) { //Do nothing results = new GameObject[1]; results[0] = go; } else { MeshCache frontCache = new MeshCache(); frontCache.vertices = c.vertices; if (sliceable.channelNormals) { frontCache.normals = c.normals; } frontCache.UVs = c.UVs; frontCache.mats = c.mats; MeshCache backCache = new MeshCache(); backCache.vertices = c.vertices; if (sliceable.channelNormals) { backCache.normals = c.normals; } backCache.UVs = c.UVs; backCache.mats = c.mats; frontCache.indices = new int[submeshCount][]; backCache.indices = new int[submeshCount][]; for (int i = 0; i < submeshCount; i++) { frontCache.indices[i] = _frontIndices[i].ToArray(); backCache.indices[i] = _backIndices[i].ToArray(); } Vector3[] geoSubsetOne, geoSubsetTwo; Vector3[] normalsSubsetOne = null, normalsSubsetTwo = null; Vector2[] uvSubsetOne, uvSubsetTwo; int[][] indexSubsetOne, indexSubsetTwo; indexSubsetOne = new int[submeshCount][]; indexSubsetTwo = new int[submeshCount][]; //Perfect subset will inflate the array list size if needed to the exact figure. So if we estimate 0, //and there is 1 submesh, than we will have 1 allocation, and this is optimal. Estimation can only help //if we have THREE or more submeshes, which is a silly scenario for anyone concerned about performance. int estimateOne = 0, estimateTwo = 0; TurboList <Vector3> _geoSubsetOne = null, _geoSubsetTwo = null, _normalSubsetOne = null, _normalSubsetTwo = null; TurboList <Vector2> _uvSubsetOne = null, _uvSubsetTwo = null; _geoSubsetOne = new TurboList <Vector3>(estimateOne); _geoSubsetTwo = new TurboList <Vector3>(estimateTwo); if (sliceable.channelNormals) { _normalSubsetOne = new TurboList <Vector3>(estimateOne); _normalSubsetTwo = new TurboList <Vector3>(estimateTwo); } _uvSubsetOne = new TurboList <Vector2>(estimateOne); _uvSubsetTwo = new TurboList <Vector2>(estimateTwo); int transferTableMaximumKey = c.vertices.Count; int[] transferTableOne = new int[transferTableMaximumKey]; int[] transferTableTwo = new int[transferTableMaximumKey]; for (int i = 0; i < transferTableOne.Length; i++) { transferTableOne[i] = -1; } for (int i = 0; i < transferTableTwo.Length; i++) { transferTableTwo[i] = -1; } for (int i = 0; i < submeshCount; i++) { perfectSubset(_frontIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetOne[i], _geoSubsetOne, _normalSubsetOne, _uvSubsetOne, ref transferTableOne); } for (int i = 0; i < submeshCount; i++) { perfectSubset(_backIndices[i], c.vertices, c.normals, c.UVs, out indexSubsetTwo[i], _geoSubsetTwo, _normalSubsetTwo, _uvSubsetTwo, ref transferTableTwo); } geoSubsetOne = _geoSubsetOne.ToArray(); geoSubsetTwo = _geoSubsetTwo.ToArray(); if (sliceable.channelNormals) { normalsSubsetOne = _normalSubsetOne.ToArray(); normalsSubsetTwo = _normalSubsetTwo.ToArray(); } uvSubsetOne = _uvSubsetOne.ToArray(); uvSubsetTwo = _uvSubsetTwo.ToArray(); //Note that we do not explicitly call recalculate bounds because (as per the manual) this is implicit in an //assignment to vertices whenever the vertex count changes from zero to non-zero. Mesh frontMesh = new Mesh(); Mesh backMesh = new Mesh(); GameObject frontObject, backObject; createResultObjects(go, sliceable, false, plane, out frontObject, out backObject); getMeshFilter(frontObject.GetComponent <Sliceable>()).mesh = frontMesh; getMeshFilter(backObject.GetComponent <Sliceable>()).mesh = backMesh; frontMesh.vertices = geoSubsetOne; backMesh.vertices = geoSubsetTwo; if (sliceable.channelNormals) { frontMesh.normals = normalsSubsetOne; backMesh.normals = normalsSubsetTwo; } frontMesh.uv = uvSubsetOne; backMesh.uv = uvSubsetTwo; frontMesh.subMeshCount = submeshCount; backMesh.subMeshCount = submeshCount; for (int i = 0; i < submeshCount; i++) { frontMesh.SetTriangles(indexSubsetOne[i], i); backMesh.SetTriangles(indexSubsetTwo[i], i); } if (meshCaches != null) { if (go.GetComponent <DeletionCallback>() == null) { frontObject.AddComponent <DeletionCallback>(); backObject.AddComponent <DeletionCallback>(); } DeletionCallback frontCallback = frontObject.GetComponent <DeletionCallback>(); DeletionCallback backCallback = backObject.GetComponent <DeletionCallback>(); frontCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh); backCallback.deletionListener = new DeletionOccurred(this.releaseCacheByMesh); frontCallback.mesh = frontMesh; backCallback.mesh = backMesh; meshCaches[frontMesh] = frontCache; meshCaches[backMesh] = backCache; } else { DeletionCallback frontCallback = frontObject.GetComponent <DeletionCallback>(); DeletionCallback backCallback = backObject.GetComponent <DeletionCallback>(); if (frontCallback != null) { GameObject.DestroyImmediate(frontCallback); } if (backCallback != null) { GameObject.DestroyImmediate(backCallback); } } if (destroyOriginal) { GameObject.Destroy(go); } results = new GameObject[2]; results[0] = frontObject; results[1] = backObject; if (sliceable != null && sliceable.refreshColliders) { foreach (GameObject r in results) { Collider collider = r.collider; if (collider != null) { if (collider is BoxCollider) { GameObject.DestroyImmediate(collider); r.AddComponent <BoxCollider>(); } else if (collider is SphereCollider) { GameObject.DestroyImmediate(collider); r.AddComponent <SphereCollider>(); } else if (collider is MeshCollider) { MeshCollider mc = (MeshCollider)collider; bool isFront = r == frontObject; Mesh mesh = isFront ? frontMesh : backMesh; mc.sharedMesh = mesh; } } } } if (sliceable != null) { sliceable.handleSlice(results); } } return(results); }