/// <summary> /// カットが終わった際のコールバックハンドラ /// </summary> /// <param name="cuttedMeshes">切断されたメッシュ</param> private void OnCuttedHandler(CutterInfo info) { lock (_cuttedInfoList) { _cuttedInfoList.Add(info); } }
/// <summary> /// Failed event handler. /// </summary> private void OnFailedHanlder(CutterInfo info) { lock (_failedCutInfoList) { _failedCutInfoList.Add(info); } }
/// <summary> /// Enqueue cut task. /// </summary> /// <param name="info">CutterInfo.</param> private void EnqueueTask(CutterInfo info) { CutTask task = new CutTask { Callback = new WaitCallback(CutProc), Args = info, }; _taskQueue.Enqueue(task); _putNotification.Set(); }
private void Update() { if (_cuttedInfoList.Count != 0) { lock (_cuttedInfoList) { for (int i = 0; i < _cuttedInfoList.Count; i++) { _cuttedInfoQueue.Enqueue(_cuttedInfoList[i]); } _cuttedInfoList.Clear(); } } while (_cuttedInfoQueue.Count != 0) { CutterInfo info = _cuttedInfoQueue.Dequeue(); CreateCuttedObjects(info); } if (_failedCutInfoList.Count != 0) { lock (_failedCutInfoList) { for (int i = 0; i < _failedCutInfoList.Count; i++) { _failedCutInfoQueue.Enqueue(_failedCutInfoList[i]); } _failedCutInfoList.Clear(); } } while (_failedCutInfoQueue.Count != 0) { CutterInfo info = _failedCutInfoQueue.Dequeue(); if (info.CutterCallback != null) { info.CutterCallback.Invoke(false, null, info.CutterPlane, info.UserData); } if (OnFailed != null) { OnFailed.Invoke(info.CutterTarget.gameObject); } } }
/// <summary> /// Perform cutting. /// </summary> /// <param name="target">Cut target object.</param> /// <param name="position">Cut plane position.</param> /// <param name="normal">Cut plane normal.</param> /// <param name="callback">Callback for cutting process. (optional)</param> /// <param name="userdata">User data. (optional)</param> public void Cut(CutterTarget target, Vector3 position, Vector3 normal, CutterCallback callback = null, object userdata = null) { _normal = normal; // Create convert matrix for position. Matrix4x4 worldToLocMat = target.MeshTransform.worldToLocalMatrix; if (target.NeedsConvertLocal) { Matrix4x4 scaleMat = Matrix4x4.Scale(target.MeshTransform.lossyScale); worldToLocMat = scaleMat * worldToLocMat; } Vector4 pos = new Vector4(position.x, position.y, position.z, 1f); Vector3 localPos = worldToLocMat * pos; // Create convert matrix for normal. Vector3 nor = new Vector4(normal.x, normal.y, normal.z, 1f); Vector4 c0 = worldToLocMat.GetColumn(0); Vector4 c1 = worldToLocMat.GetColumn(1); Vector4 c2 = worldToLocMat.GetColumn(2); Vector4 c3 = new Vector4(0, 0, 0, 1f); Matrix4x4 worldToLocNormMat = new Matrix4x4(); worldToLocNormMat.SetColumn(0, c0); worldToLocNormMat.SetColumn(1, c1); worldToLocNormMat.SetColumn(2, c2); worldToLocNormMat.SetColumn(3, c3); worldToLocNormMat = worldToLocNormMat.inverse.transpose; Vector3 localNor = worldToLocNormMat * nor; localNor.Normalize(); CutterPlane blade = new CutterPlane(localPos, localNor); CutterInfo info = new CutterInfo { CutterTarget = target, CutterObject = new CutterObject(target, _needAnimation), CutterPlane = blade, CutterCallback = callback, UserData = userdata, }; _worker.Cut(info); }
/// <summary> /// Cut process in sub thread. /// </summary> /// <param name="args">Cut args.</param> private void CutProc(object args) { CutterInfo info = args as CutterInfo; if (info == null) { return; } // Check error. // MeshCut maybe occuered error, if gameobject has destroyed in cutting. try { CutterMesh[] cuttedMeshes = _meshCut.Cut(info.CutterObject, info.CutterPlane); info.CutterMeshes = cuttedMeshes; if (cuttedMeshes == null) { if (OnFailed != null) { OnFailed.Invoke(info); } return; } if (OnCutted != null) { OnCutted.Invoke(info); } } catch (Exception e) { Debug.LogError("Error occuered in cutting. " + e); if (OnFailed != null) { OnFailed.Invoke(info); } } }
/// <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); } }
/// <summary> /// Perform cutting in sub thread. /// </summary> /// <param name="info">Cutter information.</param> public void Cut(CutterInfo info) { EnqueueTask(info); }