void HandleCollision(Collider other, Vector3 v3CollisionPos, float relativeSpeed)
    {
        if (FracturedObjectSource == null || other == null)
        {
            return;
        }

        if (other.gameObject)
        {
            FracturedChunk otherChunk = other.gameObject.GetComponent <FracturedChunk>();

            if (otherChunk)
            {
                if (otherChunk.GetComponent <Rigidbody>().isKinematic&& IsDetachedChunk == false)
                {
                    // Just intersecting with other chunk in kinematic state
                    return;
                }
            }
        }

        float fMass = Mathf.Infinity; // If there is no rigidbody we consider it static

        Rigidbody otherRigidbody = other.GetComponent <Rigidbody>();

        if (otherRigidbody != null)
        {
            fMass = otherRigidbody.mass;
        }

        if (IsDetachedChunk == false)
        {
            // Chunk still attached.
            // We are going to check if the collision is against a free chunk of the same object. This way we prevent chunks pushing each other out, we want to control
            // this only through the FractureObject.InterconnectionStrength variable

            bool bOtherIsFreeChunkFromSameObject = false;

            FracturedChunk otherChunk = other.gameObject.GetComponent <FracturedChunk>();

            if (otherChunk != null)
            {
                if (otherChunk.IsDetachedChunk == true && otherChunk.FracturedObjectSource == FracturedObjectSource)
                {
                    bOtherIsFreeChunkFromSameObject = true;
                }
            }

            if (bOtherIsFreeChunkFromSameObject == false && relativeSpeed > FracturedObjectSource.EventDetachMinVelocity && fMass > FracturedObjectSource.EventDetachMinMass && GetComponent <Rigidbody>() != null && IsDestructibleChunk())
            {
                CollisionInfo collisionInfo = new CollisionInfo(this, v3CollisionPos, true);
                FracturedObjectSource.NotifyDetachChunkCollision(collisionInfo);

                if (collisionInfo.bCancelCollisionEvent == false)
                {
                    List <FracturedChunk> listBreaks = new List <FracturedChunk>();

                    // Impact enough to make it detach. Compute random list of connected chunks that are detaching as well (we'll use the ConnectionStrength parameter).
                    listBreaks = ComputeRandomConnectionBreaks();
                    listBreaks.Add(this);
                    DetachFromObject();

                    foreach (FracturedChunk chunk in listBreaks)
                    {
                        collisionInfo.chunk   = chunk;
                        collisionInfo.bIsMain = false;
                        collisionInfo.bCancelCollisionEvent = false;

                        if (chunk != this)
                        {
                            FracturedObjectSource.NotifyDetachChunkCollision(collisionInfo);
                        }

                        if (collisionInfo.bCancelCollisionEvent == false)
                        {
                            chunk.DetachFromObject();
                            chunk.GetComponent <Rigidbody>().AddExplosionForce(relativeSpeed * FracturedObjectSource.EventDetachExitForce, otherRigidbody.transform.position, 0.0f, FracturedObjectSource.EventDetachUpwardsModifier);
                        }
                    }
                }
            }
        }
        else
        {
            // Free chunk

            Rigidbody myRigidbody = GetComponent <Rigidbody>();

            Vector3 otherVelocity     = otherRigidbody != null ? otherRigidbody.velocity : Vector3.zero;
            float   relativeSpeedFree = (otherVelocity - myRigidbody.velocity).magnitude;

            if (relativeSpeedFree > FracturedObjectSource.EventDetachedMinVelocity && fMass > FracturedObjectSource.EventDetachedMinMass)
            {
                FracturedObjectSource.NotifyFreeChunkCollision(new CollisionInfo(this, v3CollisionPos, true));
            }
        }
    }
    public override void OnInspectorGUI()
    {
        bool bCheckDetachNonSupportedChunks = false;
        bool bMarkNonSupportedChunks        = false;

        Color colorGUI = GUI.color;

        // Update the serializedProperty - always do this in the beginning of OnInspectorGUI.

        serializedObject.Update();

        FracturedChunk fracturedChunk = serializedObject.targetObject as FracturedChunk;

        PropDontDeleteAfterBroken.boolValue = EditorGUILayout.Toggle(new GUIContent("Don't Delete If Broken", "Will prevent this chunk to be deleted (due to lifetime parameters configured on the FracturedObject panel) after being broken off from the object."), PropDontDeleteAfterBroken.boolValue);
        EditorGUI.BeginChangeCheck();
        PropIsSupportChunk.boolValue = EditorGUILayout.Toggle(new GUIContent("Is Support Chunk", "Chunks that are tagged as support can't be destroyed and will hold the object together. A chunk needs to be connected to a support chunk (directly or through other chunks) or otherwise it will fall. This prevents chunks from staying static in the air and enables realistic collapsing behavior."), PropIsSupportChunk.boolValue);
        if (EditorGUI.EndChangeCheck())
        {
            if (fracturedChunk.FracturedObjectSource)
            {
                if (Application.isPlaying)
                {
                    bCheckDetachNonSupportedChunks = true;
                }

                bMarkNonSupportedChunks = true;
            }
        }

        // Apply changes to the serializedProperty.

        serializedObject.ApplyModifiedProperties();

        if (bCheckDetachNonSupportedChunks)
        {
            fracturedChunk.FracturedObjectSource.CheckDetachNonSupportedChunks();
        }

        if (bMarkNonSupportedChunks)
        {
            fracturedChunk.FracturedObjectSource.MarkNonSupportedChunks();
        }

        if (bCheckDetachNonSupportedChunks || bMarkNonSupportedChunks)
        {
            SceneView.RepaintAll();
        }

        bCheckDetachNonSupportedChunks = false;
        bMarkNonSupportedChunks        = false;

        EditorGUILayout.Space();

        if (serializedObject.targetObjects.Length == 1)
        {
            EditorGUILayout.LabelField(new GUIContent("Chunk Attached To Object: " + (fracturedChunk.IsDetachedChunk ? "No" : "Yes"), "Is the chunk attached to the object or has it been detached?"));
            EditorGUILayout.LabelField(new GUIContent("Chunk Connected To Support: " + (fracturedChunk.IsNonSupportedChunk ? "No" : "Yes"), "Is the chunk directly or indirectly connected to a support chunk?"));
            EditorGUILayout.LabelField(new GUIContent("Chunk Volume: " + string.Format("{0} ({1:0.##}% of total)", fracturedChunk.Volume, fracturedChunk.RelativeVolume * 100.0f), "The chunk volume and % of volume with respect to the object"));
            EditorGUILayout.LabelField(new GUIContent("Concave Collider Generated: " + (fracturedChunk.HasConcaveCollider ? "Yes" : "No"), "Does this chunk have one or more colliders generated by the Concave Collider utility?"));
        }
        else
        {
            GUI.color = new Color(0.5f, 1.0f, 0.0f, 1.0f);
            EditorGUILayout.LabelField("Multiselection mode");
            GUI.color = colorGUI;
        }

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        Vector4 v4GUIColor = GUI.color;

        EditorGUILayout.BeginHorizontal();
        SelectedManuallyAddChunk = EditorGUILayout.ObjectField(new GUIContent("Manually Add Connection"), SelectedManuallyAddChunk, typeof(FracturedChunk), true) as FracturedChunk;
        if (GUILayout.Button(new GUIContent("Connect", "Connects this chunk to the selected one."), GUILayout.Width(80)))
        {
            if (SelectedManuallyAddChunk)
            {
                if (serializedObject.targetObjects.Length == 1)
                {
                    if (fracturedChunk.FracturedObjectSource != SelectedManuallyAddChunk.FracturedObjectSource)
                    {
                        EditorUtility.DisplayDialog("Chunks not of the same object", "The two chunks belong to different fractured objects.", "OK");
                    }
                    else if (fracturedChunk.IsConnectedTo(SelectedManuallyAddChunk))
                    {
                        EditorUtility.DisplayDialog("Chunks already connected", "The two chunks are already connected.", "OK");
                    }
                    else
                    {
                        fracturedChunk.ConnectTo(SelectedManuallyAddChunk, Mathf.Min(Mathf.Sqrt(fracturedChunk.Volume), Mathf.Sqrt(SelectedManuallyAddChunk.Volume)));
                        bCheckDetachNonSupportedChunks = true;
                        bMarkNonSupportedChunks        = true;
                    }
                }
                else
                {
                    // Multi-edit

                    foreach (Object objectMultiselection in serializedObject.targetObjects)
                    {
                        FracturedChunk chunkMultiSelected = objectMultiselection as FracturedChunk;

                        chunkMultiSelected.ConnectTo(SelectedManuallyAddChunk, Mathf.Min(Mathf.Sqrt(chunkMultiSelected.Volume), Mathf.Sqrt(SelectedManuallyAddChunk.Volume)));
                        bCheckDetachNonSupportedChunks = true;
                        bMarkNonSupportedChunks        = true;
                    }
                }
            }
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        if (serializedObject.targetObjects.Length == 1)
        {
            GUILayout.Label("Connections to other chunks:");
        }
        else
        {
            GUILayout.Label("Common connections to other chunks:");
        }

        FracturedChunk chunkToDisconnectFrom = null;

        // Compute common connections in multi-edit mode

        List <FracturedChunk> listConnectedChunks = new List <FracturedChunk>();
        List <float>          listAreas           = new List <float>();

        for (int nConnectedChunk = 0; nConnectedChunk < fracturedChunk.ListAdjacentChunks.Count; nConnectedChunk++)
        {
            FracturedChunk.AdjacencyInfo info = fracturedChunk.ListAdjacentChunks[nConnectedChunk];

            if (info.chunk)
            {
                listConnectedChunks.Add(info.chunk);
                listAreas.Add(info.fArea);
            }
        }

        if (serializedObject.targetObjects.Length > 1)
        {
            // Multi-edit, only finish with those that are shared among all selected chunks

            foreach (Object objectMultiselection in serializedObject.targetObjects)
            {
                FracturedChunk chunkMultiSelected = objectMultiselection as FracturedChunk;

                List <FracturedChunk> listCommonChunks = new List <FracturedChunk>();

                foreach (FracturedChunk.AdjacencyInfo info in chunkMultiSelected.ListAdjacentChunks)
                {
                    if (listConnectedChunks.Contains(info.chunk))
                    {
                        listCommonChunks.Add(info.chunk);
                    }
                }

                listConnectedChunks = listCommonChunks;
            }
        }


        for (int nConnectedChunk = 0; nConnectedChunk < listConnectedChunks.Count; nConnectedChunk++)
        {
            FracturedChunk chunk = listConnectedChunks[nConnectedChunk];
            float          fArea = listAreas[nConnectedChunk];

            if (chunk)
            {
                EditorGUILayout.BeginHorizontal();
                if (chunk.IsSupportChunk)
                {
                    GUI.color = new Color(0.6f, 0.6f, 1.0f, 1.0f);
                }
                GUILayout.Label(string.Format("{0} (Surface {1})", chunk.name, serializedObject.targetObjects.Length == 1 ? fArea.ToString("F3") : ""));
                GUI.color = v4GUIColor;
                GUILayout.FlexibleSpace();
                if (GUILayout.Button(new GUIContent("Disconnect", "Disconnects the currently selected chunk from this one"), GUILayout.Width(80)))
                {
                    if (fracturedChunk.FracturedObjectSource != null)
                    {
                        chunkToDisconnectFrom = chunk;
                    }
                }
                if (GUILayout.Button(new GUIContent("Select", "Changes the active selection to this chunk"), GUILayout.Width(80)))
                {
                    if (fracturedChunk.FracturedObjectSource != null)
                    {
                        UnityEditor.Selection.activeGameObject = chunk.gameObject;
                    }
                }
                EditorGUILayout.EndHorizontal();
            }
        }

        if (chunkToDisconnectFrom != null)
        {
            foreach (Object objectMultiselection in serializedObject.targetObjects)
            {
                FracturedChunk chunkMultiSelected = objectMultiselection as FracturedChunk;
                chunkMultiSelected.DisconnectFrom(chunkToDisconnectFrom);
            }

            bCheckDetachNonSupportedChunks = true;
            bMarkNonSupportedChunks        = true;
        }

        if (bCheckDetachNonSupportedChunks)
        {
            fracturedChunk.FracturedObjectSource.CheckDetachNonSupportedChunks();
        }

        if (bMarkNonSupportedChunks)
        {
            fracturedChunk.FracturedObjectSource.MarkNonSupportedChunks();
        }

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        int nButtonWidth = 200;

        GUI.enabled = serializedObject.targetObjects.Length == 1;

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");
        if (GUILayout.Button(new GUIContent("Go To Fractured Object", "Selects the FracturedObject this chunk comes from"), GUILayout.Width(nButtonWidth)))
        {
            if (fracturedChunk.FracturedObjectSource != null)
            {
                UnityEditor.Selection.activeGameObject = fracturedChunk.FracturedObjectSource.gameObject;
            }
        }
        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");
        if (GUILayout.Button(new GUIContent("Show Non-Connected Chunks", "Enables the MeshRenderer of all the chunks that are not directly connected to this chunk."), GUILayout.Width(nButtonWidth)))
        {
            if (fracturedChunk.FracturedObjectSource != null)
            {
                foreach (FracturedChunk chunk in fracturedChunk.FracturedObjectSource.ListFracturedChunks)
                {
                    if (chunk)
                    {
                        if (fracturedChunk.IsConnectedTo(chunk) == false)
                        {
                            chunk.GetComponent <Renderer>().enabled = true;
                        }
                    }
                }
            }
        }
        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");
        if (GUILayout.Button(new GUIContent("Hide Non-Connected Chunks", "Disables the MeshRenderer of all the chunks that are not directly connected to this chunk."), GUILayout.Width(nButtonWidth)))
        {
            if (fracturedChunk.FracturedObjectSource != null)
            {
                foreach (FracturedChunk chunk in fracturedChunk.FracturedObjectSource.ListFracturedChunks)
                {
                    if (chunk)
                    {
                        if (fracturedChunk.IsConnectedTo(chunk) == false && fracturedChunk != chunk)
                        {
                            chunk.GetComponent <Renderer>().enabled = false;
                        }
                    }
                }
            }
        }
        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");
        if (GUILayout.Button(new GUIContent("Show All Other Chunks", "Enables the MeshRenderer of all the other chunks in the object."), GUILayout.Width(nButtonWidth)))
        {
            if (fracturedChunk.FracturedObjectSource != null)
            {
                foreach (FracturedChunk chunk in fracturedChunk.FracturedObjectSource.ListFracturedChunks)
                {
                    if (chunk && fracturedChunk != chunk)
                    {
                        chunk.GetComponent <Renderer>().enabled = true;
                    }
                }
            }
        }
        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label(" ");
        if (GUILayout.Button(new GUIContent("Hide All Other Chunks", "Disables the MeshRenderer of all the other chunks in the object."), GUILayout.Width(nButtonWidth)))
        {
            if (fracturedChunk.FracturedObjectSource != null)
            {
                foreach (FracturedChunk chunk in fracturedChunk.FracturedObjectSource.ListFracturedChunks)
                {
                    if (chunk && fracturedChunk != chunk)
                    {
                        chunk.GetComponent <Renderer>().enabled = false;
                    }
                }
            }
        }
        GUILayout.Label(" ");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.Space();
        EditorGUILayout.Space();

        GUI.enabled = true;

        if (bCheckDetachNonSupportedChunks || bMarkNonSupportedChunks)
        {
            SceneView.RepaintAll();
        }
    }
    void OnCollisionEnter(Collision collision)
    {
        if (FracturedObjectSource == null || collision == null)
        {
            return;
        }

        if (collision.contacts == null)
        {
            return;
        }

        if (collision.contacts.Length == 0)
        {
            return;
        }

        if (collision.gameObject)
        {
            FracturedChunk otherChunk = collision.gameObject.GetComponent <FracturedChunk>();

            if (otherChunk)
            {
                if (otherChunk.GetComponent <Rigidbody>().isKinematic&& IsDetachedChunk == false)
                {
                    // Just intersecting with other chunk in kinematic state
                    return;
                }
            }
        }

        float fMass = Mathf.Infinity; // If there is no rigidbody we consider it static

        if (collision.rigidbody)
        {
            fMass = collision.rigidbody.mass;
        }

        if (IsDetachedChunk == false)
        {
            // Chunk still attached.
            // We are going to check if the collision is against a free chunk of the same object. This way we prevent chunks pushing each other out, we want to control
            // this only through the FractureObject.InterconnectionStrength variable

            bool bOtherIsFreeChunkFromSameObject = false;

            FracturedChunk otherChunk = collision.gameObject.GetComponent <FracturedChunk>();

            if (otherChunk != null)
            {
                if (otherChunk.IsDetachedChunk == true && otherChunk.FracturedObjectSource == FracturedObjectSource)
                {
                    bOtherIsFreeChunkFromSameObject = true;
                }
            }

            if (bOtherIsFreeChunkFromSameObject == false && collision.relativeVelocity.magnitude > FracturedObjectSource.EventDetachMinVelocity && fMass > FracturedObjectSource.EventDetachMinMass && GetComponent <Rigidbody>() != null && IsSupportChunk == false)
            {
                CollisionInfo collisionInfo = new CollisionInfo(this, collision, true);
                FracturedObjectSource.NotifyDetachChunkCollision(collisionInfo);

                if (collisionInfo.bCancelCollisionEvent == false)
                {
                    List <FracturedChunk> listBreaks = new List <FracturedChunk>();

                    // Impact enough to make it detach. Compute random list of connected chunks that are detaching as well (we'll use the ConnectionStrength parameter).
                    listBreaks = ComputeRandomConnectionBreaks();
                    listBreaks.Add(this);
                    DetachFromObject();

                    foreach (FracturedChunk chunk in listBreaks)
                    {
                        collisionInfo.chunk   = chunk;
                        collisionInfo.bIsMain = false;
                        collisionInfo.bCancelCollisionEvent = false;

                        if (chunk != this)
                        {
                            FracturedObjectSource.NotifyDetachChunkCollision(collisionInfo);
                        }

                        if (collisionInfo.bCancelCollisionEvent == false)
                        {
                            chunk.DetachFromObject();
                            chunk.GetComponent <Rigidbody>().AddExplosionForce(collision.impactForceSum.magnitude * FracturedObjectSource.EventDetachExitForce, collision.contacts[0].point, 0.0f, FracturedObjectSource.EventDetachUpwardsModifier);
                        }
                    }
                }
            }
        }
        else
        {
            // Free chunk

            if (collision.relativeVelocity.magnitude > FracturedObjectSource.EventDetachedMinVelocity && fMass > FracturedObjectSource.EventDetachedMinMass)
            {
                FracturedObjectSource.NotifyFreeChunkCollision(new CollisionInfo(this, collision, true));
            }
        }
    }