using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(Rigidbody))]
public class Slice : MonoBehaviour
{
public SliceOptions sliceOptions;
public CallbackOptions callbackOptions;
///
/// The number of times this fragment has been re-sliced.
///
private int currentSliceCount;
///
/// Collector object that stores the produced fragments
///
private GameObject fragmentRoot;
///
/// Slices the attached mesh along the cut plane
///
/// The cut plane normal vector in world coordinates.
/// The cut plane origin in world coordinates.
public void ComputeSlice(Vector3 sliceNormalWorld, Vector3 sliceOriginWorld)
{
var mesh = this.GetComponent().sharedMesh;
if (mesh != null)
{
// If the fragment root object has not yet been created, create it now
if (this.fragmentRoot == null)
{
// Create a game object to contain the fragments
this.fragmentRoot = new GameObject($"{this.name}Slices");
this.fragmentRoot.transform.SetParent(this.transform.parent);
// Each fragment will handle its own scale
this.fragmentRoot.transform.position = this.transform.position;
this.fragmentRoot.transform.rotation = this.transform.rotation;
this.fragmentRoot.transform.localScale = Vector3.one;
}
var sliceTemplate = CreateSliceTemplate();
var sliceNormalLocal = this.transform.InverseTransformDirection(sliceNormalWorld);
var sliceOriginLocal = this.transform.InverseTransformPoint(sliceOriginWorld);
Fragmenter.Slice(this.gameObject,
sliceNormalLocal,
sliceOriginLocal,
this.sliceOptions,
sliceTemplate,
this.fragmentRoot.transform);
// Done with template, destroy it
GameObject.Destroy(sliceTemplate);
// Deactivate the original object
this.gameObject.SetActive(false);
// Fire the completion callback
if (callbackOptions.onCompleted != null)
{
callbackOptions.onCompleted.Invoke();
}
}
}
///
/// Creates a template object which each fragment will derive from
///
///
private GameObject CreateSliceTemplate()
{
// If pre-fracturing, make the fragments children of this object so they can easily be unfrozen later.
// Otherwise, parent to this object's parent
GameObject obj = new GameObject();
obj.name = "Slice";
obj.tag = this.tag;
// Update mesh to the new sliced mesh
obj.AddComponent();
// Add materials. Normal material goes in slot 1, cut material in slot 2
var meshRenderer = obj.AddComponent();
meshRenderer.sharedMaterials = new Material[2] {
this.GetComponent().sharedMaterial,
this.sliceOptions.insideMaterial
};
// Copy collider properties to fragment
var thisCollider = this.GetComponent();
var fragmentCollider = obj.AddComponent();
fragmentCollider.convex = true;
fragmentCollider.sharedMaterial = thisCollider.sharedMaterial;
fragmentCollider.isTrigger = thisCollider.isTrigger;
// Copy rigid body properties to fragment
var thisRigidBody = this.GetComponent();
var fragmentRigidBody = obj.AddComponent();
fragmentRigidBody.linearVelocity = thisRigidBody.linearVelocity;
fragmentRigidBody.angularVelocity = thisRigidBody.angularVelocity;
fragmentRigidBody.linearDamping = thisRigidBody.linearDamping;
fragmentRigidBody.angularDamping = thisRigidBody.angularDamping;
fragmentRigidBody.useGravity = thisRigidBody.useGravity;
// If refracturing is enabled, create a copy of this component and add it to the template fragment object
if (this.sliceOptions.enableReslicing &&
(this.currentSliceCount < this.sliceOptions.maxResliceCount))
{
CopySliceComponent(obj);
}
return obj;
}
///
/// Convenience method for copying this component to another component
///
/// The GameObject to copy this component to
private void CopySliceComponent(GameObject obj)
{
var sliceComponent = obj.AddComponent();
sliceComponent.sliceOptions = this.sliceOptions;
sliceComponent.callbackOptions = this.callbackOptions;
sliceComponent.currentSliceCount = this.currentSliceCount + 1;
sliceComponent.fragmentRoot = this.fragmentRoot;
}
}