Example #1
0
        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();
        }
Example #5
0
        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);
        }
Example #6
0
    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;
    }
Example #8
0
    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);
    }