Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MeshDestroy => Put it on a game object with a mesh filter and renderer. Make sure to have read/write enabled on fbx import
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshDestroy : MonoBehaviour
{
private bool edgeSet = false;
private Vector3 edgeVertex = Vector3.zero;
private Vector2 edgeUV = Vector2.zero;
private Plane edgePlane = new Plane();
public int CutCascades = 1;
public float ExplodeForce = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
DestroyMesh();
}
}
private void DestroyMesh()
{
var originalMesh = GetComponent<MeshFilter>().mesh;
originalMesh.RecalculateBounds();
var parts = new List<PartMesh>();
var subParts = new List<PartMesh>();
var mainPart = new PartMesh()
{
UV = originalMesh.uv,
Vertices = originalMesh.vertices,
Normals = originalMesh.normals,
Triangles = new int[originalMesh.subMeshCount][],
Bounds = originalMesh.bounds
};
for (int i = 0; i < originalMesh.subMeshCount; i++)
mainPart.Triangles[i] = originalMesh.GetTriangles(i);
parts.Add(mainPart);
for (var c = 0; c < CutCascades; c++)
{
for (var i = 0; i < parts.Count; i++)
{
var bounds = parts[i].Bounds;
bounds.Expand(0.5f);
var plane = new Plane(UnityEngine.Random.onUnitSphere, new Vector3(UnityEngine.Random.Range(bounds.min.x, bounds.max.x),
UnityEngine.Random.Range(bounds.min.y, bounds.max.y),
UnityEngine.Random.Range(bounds.min.z, bounds.max.z)));
subParts.Add(GenerateMesh(parts[i], plane, true));
subParts.Add(GenerateMesh(parts[i], plane, false));
}
parts = new List<PartMesh>(subParts);
subParts.Clear();
}
for (var i = 0; i < parts.Count; i++)
{
parts[i].MakeGameobject(this);
parts[i].GameObject.GetComponent<Rigidbody>().AddForceAtPosition(parts[i].Bounds.center * ExplodeForce, transform.position);
}
Destroy(gameObject);
}
private PartMesh GenerateMesh(PartMesh original, Plane plane, bool left)
{
var partMesh = new PartMesh() { };
var ray1 = new Ray();
var ray2 = new Ray();
for (var i = 0; i < original.Triangles.Length; i++)
{
var triangles = original.Triangles[i];
edgeSet = false;
for (var j = 0; j < triangles.Length; j = j + 3)
{
var sideA = plane.GetSide(original.Vertices[triangles[j]]) == left;
var sideB = plane.GetSide(original.Vertices[triangles[j + 1]]) == left;
var sideC = plane.GetSide(original.Vertices[triangles[j + 2]]) == left;
var sideCount = (sideA ? 1 : 0) +
(sideB ? 1 : 0) +
(sideC ? 1 : 0);
if (sideCount == 0)
{
continue;
}
if (sideCount == 3)
{
partMesh.AddTriangle(i,
original.Vertices[triangles[j]], original.Vertices[triangles[j + 1]], original.Vertices[triangles[j + 2]],
original.Normals[triangles[j]], original.Normals[triangles[j + 1]], original.Normals[triangles[j + 2]],
original.UV[triangles[j]], original.UV[triangles[j + 1]], original.UV[triangles[j + 2]]);
continue;
}
//cut points
var singleIndex = sideB == sideC ? 0 : sideA == sideC ? 1 : 2;
ray1.origin = original.Vertices[triangles[j + singleIndex]];
var dir1 = original.Vertices[triangles[j + ((singleIndex + 1) % 3)]] - original.Vertices[triangles[j + singleIndex]];
ray1.direction = dir1;
plane.Raycast(ray1, out var enter1);
var lerp1 = enter1 / dir1.magnitude;
ray2.origin = original.Vertices[triangles[j + singleIndex]];
var dir2 = original.Vertices[triangles[j + ((singleIndex + 2) % 3)]] - original.Vertices[triangles[j + singleIndex]];
ray2.direction = dir2;
plane.Raycast(ray2, out var enter2);
var lerp2 = enter2 / dir2.magnitude;
//first vertex = ancor
AddEdge(i,
partMesh,
left ? plane.normal * -1f : plane.normal,
ray1.origin + ray1.direction.normalized * enter1,
ray2.origin + ray2.direction.normalized * enter2,
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 2) % 3)]], lerp2));
if (sideCount == 1)
{
partMesh.AddTriangle(i,
original.Vertices[triangles[j + singleIndex]],
//Vector3.Lerp(originalMesh.vertices[triangles[j + singleIndex]], originalMesh.vertices[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
//Vector3.Lerp(originalMesh.vertices[triangles[j + singleIndex]], originalMesh.vertices[triangles[j + ((singleIndex + 2) % 3)]], lerp2),
ray1.origin + ray1.direction.normalized * enter1,
ray2.origin + ray2.direction.normalized * enter2,
original.Normals[triangles[j + singleIndex]],
Vector3.Lerp(original.Normals[triangles[j + singleIndex]], original.Normals[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
Vector3.Lerp(original.Normals[triangles[j + singleIndex]], original.Normals[triangles[j + ((singleIndex + 2) % 3)]], lerp2),
original.UV[triangles[j + singleIndex]],
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 2) % 3)]], lerp2));
continue;
}
if (sideCount == 2)
{
partMesh.AddTriangle(i,
ray1.origin + ray1.direction.normalized * enter1,
original.Vertices[triangles[j + ((singleIndex + 1) % 3)]],
original.Vertices[triangles[j + ((singleIndex + 2) % 3)]],
Vector3.Lerp(original.Normals[triangles[j + singleIndex]], original.Normals[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
original.Normals[triangles[j + ((singleIndex + 1) % 3)]],
original.Normals[triangles[j + ((singleIndex + 2) % 3)]],
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
original.UV[triangles[j + ((singleIndex + 1) % 3)]],
original.UV[triangles[j + ((singleIndex + 2) % 3)]]);
partMesh.AddTriangle(i,
ray1.origin + ray1.direction.normalized * enter1,
original.Vertices[triangles[j + ((singleIndex + 2) % 3)]],
ray2.origin + ray2.direction.normalized * enter2,
Vector3.Lerp(original.Normals[triangles[j + singleIndex]], original.Normals[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
original.Normals[triangles[j + ((singleIndex + 2) % 3)]],
Vector3.Lerp(original.Normals[triangles[j + singleIndex]], original.Normals[triangles[j + ((singleIndex + 2) % 3)]], lerp2),
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 1) % 3)]], lerp1),
original.UV[triangles[j + ((singleIndex + 2) % 3)]],
Vector2.Lerp(original.UV[triangles[j + singleIndex]], original.UV[triangles[j + ((singleIndex + 2) % 3)]], lerp2));
continue;
}
}
}
partMesh.FillArrays();
return partMesh;
}
private void AddEdge(int subMesh, PartMesh partMesh, Vector3 normal, Vector3 vertex1, Vector3 vertex2, Vector2 uv1, Vector2 uv2)
{
if (!edgeSet)
{
edgeSet = true;
edgeVertex = vertex1;
edgeUV = uv1;
}
else
{
edgePlane.Set3Points(edgeVertex, vertex1, vertex2);
partMesh.AddTriangle(subMesh,
edgeVertex,
edgePlane.GetSide(edgeVertex + normal) ? vertex1 : vertex2,
edgePlane.GetSide(edgeVertex + normal) ? vertex2 : vertex1,
normal,
normal,
normal,
edgeUV,
uv1,
uv2);
}
}
public class PartMesh
{
private List<Vector3> _Verticies = new List<Vector3>();
private List<Vector3> _Normals = new List<Vector3>();
private List<List<int>> _Triangles = new List<List<int>>();
private List<Vector2> _UVs = new List<Vector2>();
public Vector3[] Vertices;
public Vector3[] Normals;
public int[][] Triangles;
public Vector2[] UV;
public GameObject GameObject;
public Bounds Bounds = new Bounds();
public PartMesh()
{
}
public void AddTriangle(int submesh, Vector3 vert1, Vector3 vert2, Vector3 vert3, Vector3 normal1, Vector3 normal2, Vector3 normal3, Vector2 uv1, Vector2 uv2, Vector2 uv3)
{
if (_Triangles.Count - 1 < submesh)
_Triangles.Add(new List<int>());
_Triangles[submesh].Add(_Verticies.Count);
_Verticies.Add(vert1);
_Triangles[submesh].Add(_Verticies.Count);
_Verticies.Add(vert2);
_Triangles[submesh].Add(_Verticies.Count);
_Verticies.Add(vert3);
_Normals.Add(normal1);
_Normals.Add(normal2);
_Normals.Add(normal3);
_UVs.Add(uv1);
_UVs.Add(uv2);
_UVs.Add(uv3);
Bounds.min = Vector3.Min(Bounds.min, vert1);
Bounds.min = Vector3.Min(Bounds.min, vert2);
Bounds.min = Vector3.Min(Bounds.min, vert3);
Bounds.max = Vector3.Min(Bounds.max, vert1);
Bounds.max = Vector3.Min(Bounds.max, vert2);
Bounds.max = Vector3.Min(Bounds.max, vert3);
}
public void FillArrays()
{
Vertices = _Verticies.ToArray();
Normals = _Normals.ToArray();
UV = _UVs.ToArray();
Triangles = new int[_Triangles.Count][];
for (var i = 0; i < _Triangles.Count; i++)
Triangles[i] = _Triangles[i].ToArray();
}
public void MakeGameobject(MeshDestroy original)
{
GameObject = new GameObject(original.name);
GameObject.transform.position = original.transform.position;
GameObject.transform.rotation = original.transform.rotation;
GameObject.transform.localScale = original.transform.localScale;
var mesh = new Mesh();
mesh.name = original.GetComponent<MeshFilter>().mesh.name;
mesh.vertices = Vertices;
mesh.normals = Normals;
mesh.uv = UV;
for(var i = 0; i < Triangles.Length; i++)
mesh.SetTriangles(Triangles[i], i, true);
Bounds = mesh.bounds;
var renderer = GameObject.AddComponent<MeshRenderer>();
renderer.materials = original.GetComponent<MeshRenderer>().materials;
var filter = GameObject.AddComponent<MeshFilter>();
filter.mesh = mesh;
var collider = GameObject.AddComponent<MeshCollider>();
collider.convex = true;
var rigidbody = GameObject.AddComponent<Rigidbody>();
var meshDestroy = GameObject.AddComponent<MeshDestroy>();
meshDestroy.CutCascades = original.CutCascades;
meshDestroy.ExplodeForce = original.ExplodeForce;
}
}
}
@IChristianPlayzI

This comment has been minimized.

Copy link

@IChristianPlayzI IChristianPlayzI commented Jun 4, 2020

so I put it on this sword i made in blender (which i imported into unity with read and write enabled), and it wont slice anything and does it have to be an fbx?

@ditzel

This comment has been minimized.

Copy link
Owner Author

@ditzel ditzel commented Aug 12, 2020

so I put it on this sword i made in blender (which i imported into unity with read and write enabled), and it wont slice anything and does it have to be an fbx?

yes

@RamonBeast

This comment has been minimized.

Copy link

@RamonBeast RamonBeast commented Nov 16, 2020

Hi @ditzel, thanks for this great script. I am playing with it but I noticed that some fragments are created directly 50m or 60m below the origin and they keep falling forever. This happens with cascade set to 1 and explosion force set to 0 (it happens with all settings, but this is easier for me to debug) usually after the first 3 cuts. Would you have a suggestion on why it might happen? Thank you

image

@Xquality

This comment has been minimized.

Copy link

@Xquality Xquality commented Dec 11, 2020

Hey @RamonBeast, you can add this at the top of the MakeGameobject function, right before GameObject = new GameObject(original.name);

if (Vertices.Length <= 100) return;

Just change the number to something that works for you. It all depends on how low-poly the mesh is.
I noticed that a lot of pieces generated have a mesh with 0 vertices, and that's probably what you're encountering :)

@DelapaixStudio

This comment has been minimized.

Copy link

@DelapaixStudio DelapaixStudio commented Dec 28, 2020

Hi @ditzel, your script is working very well !
Can I use this script in my video game ?
You can contact me at delapaix@tutanota.com

@RamonBeast

This comment has been minimized.

Copy link

@RamonBeast RamonBeast commented Dec 30, 2020

Hey @Xquality I eventually resolved that by quickly calculating the volume of each fragment and excluding all polygons smaller than a certain threshold, this was to prevent the generation of very thin slices that wouldn't be caught by calculating only the amount of vertices generated. Thanks a lot for your feedback!

@SwixDevs

This comment has been minimized.

Copy link

@SwixDevs SwixDevs commented Mar 11, 2021

Hey @Xquality I eventually resolved that by quickly calculating the volume of each fragment and excluding all polygons smaller than a certain threshold, this was to prevent the generation of very thin slices that wouldn't be caught by calculating only the amount of vertices generated. Thanks a lot for your feedback!

Hello @RamonBeast. Do you mind providing me the edited code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment