-
-
Save aipacommander/3d6c546d1e9778a153c98b2619734670 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
// 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++) | |
// sub meshの取得 -> `GetTriangles` | |
// >サブメッシュは、マテリアルのことです。 | |
// >例えば、同じオブジェクトにマテリアルが3つアタッチされていたらサブメッシュの数は3つとなります。 | |
// https://soramamenatan.hatenablog.com/entry/2019/11/04/194332 | |
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) | |
{ | |
// Debug.Log($"start: {_Triangles.Count},{submesh}"); | |
if (_Triangles.Count - 1 < submesh) { | |
var cnt = submesh - _Triangles.Count; | |
for (var i = 0; i <= cnt; i++) { | |
_Triangles.Add(new List<int>()); | |
} | |
if (_Triangles.Count == 0) { | |
_Triangles.Add(new List<int>()); | |
} | |
} | |
// Debug.Log($"end: {_Triangles.Count},{submesh}"); | |
_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(); | |
// Debug.Log($"koko?{Triangles[i]}*{i}0{_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); | |
break; | |
} | |
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>(); | |
// スケールを掛け合わせた実際のサイズを取得 | |
Vector3 realSize; | |
realSize = Bounds.size; | |
realSize.x *= GameObject.transform.localScale.x; | |
realSize.y *= GameObject.transform.localScale.y; | |
realSize.z *= GameObject.transform.localScale.z; | |
// Consoleへ表示 | |
print(string.Format("name = {0} : Real Size(m) = ({1}, {2}, {3})", | |
mesh.name, | |
realSize.x, realSize.y, realSize.z)); | |
if (realSize.x >= 2 && realSize.y >= 2 && realSize.z >= 2) { | |
var meshDestroy = GameObject.AddComponent<MeshDestroy>(); | |
meshDestroy.CutCascades = original.CutCascades; | |
meshDestroy.ExplodeForce = original.ExplodeForce; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment