/// <summary> /// Cut the specified victim, blade_plane and capMaterial. /// (指定された「victim」をカットする。ブレード(平面)とマテリアルから切断を実行する) /// </summary> /// <param name="victim">Victim.</param> /// <param name="blade_plane">Blade plane.</param> public CutterMesh[] Cut(CutterObject victim, CutterPlane blade) { // 対象のメッシュを取得 MeshCutData data = new MeshCutData(victim.CutterMesh, blade); // ここでの「3」はトライアングル bool[] sides = new bool[3]; int[] indices; int p1, p2, p3; // go throught the submeshes // サブメッシュの数だけループ for (int sub = 0; sub < victim.SubMeshCount; sub++) { // サブメッシュのインデックス数を取得 indices = victim.CutterMesh.GetIndices(sub); // List<List<int>>型のリスト。サブメッシュ一つ分のインデックスリスト data.LeftSide.SubIndices.Add(new List <int>()); // 左 data.RightSide.SubIndices.Add(new List <int>()); // 右 // サブメッシュのインデックス数分ループ for (int i = 0; i < indices.Length; i += 3) { // p1 - p3のインデックスを取得。つまりトライアングル p1 = indices[i + 0]; p2 = indices[i + 1]; p3 = indices[i + 2]; // それぞれ評価中のメッシュの頂点が、冒頭で定義された平面の左右どちらにあるかを評価。 // `GetSide` メソッドによりboolを得る。 sides[0] = blade.GetSide(data.VictimMesh.Vertices[p1]); sides[1] = blade.GetSide(data.VictimMesh.Vertices[p2]); sides[2] = blade.GetSide(data.VictimMesh.Vertices[p3]); // whole triangle // 頂点0と頂点1および頂点2がどちらも同じ側にある場合はカットしない if (sides[0] == sides[1] && sides[0] == sides[2]) { if (sides[0]) { // left side // GetSideメソッドでポジティブ(true)の場合は左側にあり data.LeftSide.AddTriangle(p1, p2, p3, sub); } else { data.RightSide.AddTriangle(p1, p2, p3, sub); } } else { // cut the triangle // そうではなく、どちらかの点が平面の反対側にある場合はカットを実行する CutFace(sub, sides, p1, p2, p3, data); } } } // Check separating verticies. if (data.LeftSide.Vertices.Count == 0 || data.RightSide.Vertices.Count == 0) { Debug.Log("No need cutting, because all verticies are in one side."); return(null); } data.LeftSide.SubIndices.Add(new List <int>()); data.RightSide.SubIndices.Add(new List <int>()); // カット開始 Capping(data); // 左側のメッシュを生成 // MeshCutSideクラスのメンバから各値をコピー CutterMesh leftHalfMesh = new CutterMesh { Vertices = data.LeftSide.Vertices.ToArray(), Triangles = data.LeftSide.Triangles.ToArray(), Normals = data.LeftSide.Normals.ToArray(), UVs = data.LeftSide.UVs.ToArray(), BoneWeights = data.LeftSide.BoneWeights.ToArray(), }; int[][] leftSubIndices = new int[data.LeftSide.SubIndices.Count][]; for (int i = 0; i < data.LeftSide.SubIndices.Count; i++) { leftSubIndices[i] = data.LeftSide.SubIndices[i].ToArray(); } leftHalfMesh.SetIndices(leftSubIndices); // 右側のメッシュも同様に生成 CutterMesh rightHalfMesh = new CutterMesh { Vertices = data.RightSide.Vertices.ToArray(), Triangles = data.RightSide.Triangles.ToArray(), Normals = data.RightSide.Normals.ToArray(), UVs = data.RightSide.UVs.ToArray(), BoneWeights = data.RightSide.BoneWeights.ToArray(), }; int[][] rightSubIndices = new int[data.RightSide.SubIndices.Count][]; for (int i = 0; i < data.RightSide.SubIndices.Count; i++) { rightSubIndices[i] = data.RightSide.SubIndices[i].ToArray(); } rightHalfMesh.SetIndices(rightSubIndices); Debug.Log("cutter finish"); // 左右のGameObjectの配列を返す return(new CutterMesh[] { leftHalfMesh, rightHalfMesh }); }
/// <summary> /// カットされたメッシュ情報を元にオブジェクトを生成する /// </summary> private void CreateCuttedObjects(CutterInfo info) { CutterTarget target = info.CutterTarget; if (target == null) { return; } // 設定されているマテリアル配列を取得 Material[] mats = GetMaterials(target.GameObject); // カット面分増やしたマテリアル配列を準備 Material[] newMats = new Material[mats.Length + 1]; // 既存のものを新しい配列にコピー mats.CopyTo(newMats, 0); // 新しいマテリアル配列の最後に、カット面用マテリアルを追加 newMats[mats.Length] = (target.CutMaterial != null) ? target.CutMaterial : _cutDefaultMaterial; // 生成したマテリアルリストを再設定 mats = newMats; CutterObject cutterObject = info.CutterObject; CutterMesh[] cuttedMeshes = info.CutterMeshes; GameObject[] cuttedObject = new GameObject[cuttedMeshes.Length]; // 生成されたメッシュ分、オブジェクトを生成する for (int i = 0; i < cuttedMeshes.Length; i++) { #region ### Create new object ### GameObject newObj = new GameObject("[Cutterd]" + target.Name + i); newObj.transform.position = target.transform.position; newObj.transform.rotation = target.transform.rotation; newObj.transform.localScale = target.transform.localScale; #endregion ### Create new object ### #region ### Create new mesh ### Mesh mesh = new Mesh(); mesh.name = "Split Mesh"; mesh.vertices = cuttedMeshes[i].Vertices; mesh.triangles = cuttedMeshes[i].Triangles; mesh.normals = cuttedMeshes[i].Normals; mesh.uv = cuttedMeshes[i].UVs; mesh.subMeshCount = cuttedMeshes[i].SubMeshCount; for (int j = 0; j < cuttedMeshes[i].SubMeshCount; j++) { mesh.SetIndices(cuttedMeshes[i].GetIndices(j), MeshTopology.Triangles, j); } #endregion ### Create new mesh ### GameObject obj; // アニメーションが必要な場合はボーンのセットアップを行う if (_needAnimation) { SkinnedMeshRenderer oriRenderer = target.GameObject.GetComponent <SkinnedMeshRenderer>(); Transform newRootBone = Instantiate(oriRenderer.rootBone.gameObject).transform; newRootBone.name = newRootBone.name.Replace("(Clone)", ""); Transform[] newBones = new Transform[oriRenderer.bones.Length]; for (int j = 0; j < oriRenderer.bones.Length; j++) { Transform bone = oriRenderer.bones[j]; Transform newBone = SearchBone(bone.name, newRootBone); newBones[j] = newBone; } Animator oriAnim = target.GetComponent <Animator>(); AnimatorStateInfo stateInfo = oriAnim.GetCurrentAnimatorStateInfo(0); newRootBone.SetParent(newObj.transform); Animator anim = newObj.AddComponent <Animator>(); anim.runtimeAnimatorController = oriAnim.runtimeAnimatorController; anim.avatar = target.GetComponent <Animator>().avatar; anim.Play(stateInfo.fullPathHash, 0, stateInfo.normalizedTime); mesh.bindposes = cutterObject.Bindposes; mesh.boneWeights = cuttedMeshes[i].BoneWeights; obj = new GameObject("Split Object", new[] { typeof(SkinnedMeshRenderer) }); obj.transform.position = target.MeshTransform.position; obj.transform.rotation = target.MeshTransform.rotation; SkinnedMeshRenderer renderer = obj.GetComponent <SkinnedMeshRenderer>(); renderer.sharedMesh = mesh; renderer.materials = mats; renderer.bones = newBones; renderer.rootBone = newRootBone; } else { obj = new GameObject("Split Object " + i, new[] { typeof(MeshFilter), typeof(MeshRenderer), typeof(Rigidbody), typeof(MeshCollider), typeof(CutterTarget), }); obj.transform.position = target.MeshTransform.position; obj.transform.rotation = target.MeshTransform.rotation; obj.transform.localScale = target.MeshTransform.lossyScale; obj.GetComponent <MeshCollider>().sharedMesh = mesh; obj.GetComponent <MeshCollider>().convex = true; obj.GetComponent <MeshCollider>().material = target.GetComponent <Collider>().material; obj.GetComponent <MeshFilter>().mesh = mesh; obj.GetComponent <MeshRenderer>().materials = mats; obj.GetComponent <CutterTarget>().CutMaterial = target.CutMaterial; obj.GetComponent <Rigidbody>().constraints = target.Rigidbody.constraints; } cuttedObject[i] = obj; obj.transform.SetParent(newObj.transform); if (cutterObject.NeedsScaleAdjust) { obj.transform.localScale = target.transform != target.MeshTransform ? target.MeshTransform.localScale : Vector3.one; } cutAnim.Anim(target, obj.GetComponent <CutterTarget>()); } // Notify end of cutting. target.Cutted(cuttedObject); if (_autoDestroyTarget) { // Destroy target object. target.transform.localScale = Vector3.zero; target.gameObject.SetActive(false); //Destroy(target.gameObject); } if (OnCutted != null) { OnCutted.Invoke(cuttedObject, info.CutterPlane); } if (info.CutterCallback != null) { info.CutterCallback.Invoke(true, cuttedObject, info.CutterPlane, info.UserData); } }