Untitled
unknown
plain_text
2 years ago
8.3 kB
3
Indexable
Never
using System.Collections.Generic; using UnityEngine; namespace DynamicMeshCutter { public delegate void OnCut(bool success, Info info); //after cut, before mesh creation public delegate void OnCreated(Info info, MeshCreationData creationData); //after original mesh target has been destroyed. after new meshes have been created and instantiate public class Info { //basic info public MeshTarget MeshTarget; public VirtualPlane Plane; ////advanced info public Mesh TargetOriginalMesh; public VirtualMesh TargetVirtualMesh; public Matrix4x4[] Bindposes; public Info(MeshTarget target, VirtualPlane plane, OnCut onCut, OnCreated onCreated, object boxed) { this.MeshTarget = target; this.Plane = plane; OnCutCallback = onCut; OnCreatedCallback = onCreated; BoxedUserData = boxed; MeshCreation.GetMeshInfo(target, out TargetOriginalMesh, out Bindposes); TargetVirtualMesh = new VirtualMesh(TargetOriginalMesh); if (target.DynamicRagdoll != null) //dynamic ragdoll could be missing { TargetVirtualMesh.AssignRagdoll(target.DynamicRagdoll); } } //info created during cutting tasks public VirtualMesh[] CreatedMeshes; public int[] Sides; public int[] BT; //buttom (0) or top (1) //callbacks public OnCut OnCutCallback; public OnCreated OnCreatedCallback; public object BoxedUserData; } public abstract class CutterBehaviour : MonoBehaviour { public float Separation = 0.02f; public bool DestroyTargets = true; public bool UseAsync = true; public Material DefaultMaterial; private bool _cutterIsEnabled; public bool CutterIsEnabled => _cutterIsEnabled; public static bool ApplicationHasQuit = true; private AsycWorker _asyncWorker; //does the async work public AsycWorker AsyncWorker { get { if (_asyncWorker == null) InitializeWorker(); return _asyncWorker; } } private List<Info> _successes = new List<Info>(); private List<Info> _fails = new List<Info>(); private Queue<Info> _qSuccesses = new Queue<Info>(); private Queue<Info> _qFails = new Queue<Info>(); private bool _isInitialized = false; void InitializeWorker() { if (_isInitialized) { return; } _isInitialized = true; _asyncWorker = new AsycWorker(this); _asyncWorker.OnCut += OnCut; } void Terminate() { _asyncWorker = null; } private void OnApplicationQuit() { ApplicationHasQuit = true; } private void Awake() { ApplicationHasQuit = false; } private void OnEnable() { _cutterIsEnabled = true; } private void OnDisable() { _cutterIsEnabled = false; Terminate(); } protected virtual void Update() { if (_successes.Count != 0) { lock (_successes) { for (int i = 0; i < _successes.Count; i++) { _qSuccesses.Enqueue(_successes[i]); } _successes.Clear(); } } while (_qSuccesses.Count != 0) { Info info = _qSuccesses.Dequeue(); info.OnCutCallback?.Invoke(true, info); CreateGameObjects(info); } if (_fails.Count != 0) { lock (_fails) { for (int i = 0; i < _fails.Count; i++) { _qFails.Enqueue(_fails[i]); } _fails.Clear(); } } while (_qFails.Count != 0) { Info info = _qFails.Dequeue(); info.OnCutCallback?.Invoke(false, info); } } public void Cut(MeshTarget target, Vector3 worldPosition, Vector3 worldNormal, OnCut onCut = null, OnCreated onCreated = null, object boxedUserData = null) { if (!target.isActiveAndEnabled) return; Matrix4x4 worldToLocalMatrix = target.transform.worldToLocalMatrix; if (target.RequireLocal) { Matrix4x4 scalingMatrix = Matrix4x4.Scale(target.transform.lossyScale); worldToLocalMatrix = scalingMatrix * worldToLocalMatrix; } //Get Local Position Vector4 worldP = new Vector4(worldPosition.x, worldPosition.y, worldPosition.z, 1f); Vector4[] worldPColumn = new Vector4[4]; Vector3 localP = worldToLocalMatrix * worldP; //Get Local Normal Vector3 worldN = new Vector4(worldNormal.x, worldNormal.y, worldNormal.z, 1f); Matrix4x4 worldToLocalMatrixNormal = new Matrix4x4(); for (int i = 0; i < 4; i++) { var column = worldToLocalMatrix.GetColumn(i); if (i == 4) column = new Vector4(0, 0, 0, 1f); worldToLocalMatrixNormal.SetColumn(i, column); } worldToLocalMatrixNormal = worldToLocalMatrixNormal.inverse.transpose; Vector3 localN = worldToLocalMatrixNormal * worldN; localN.Normalize(); VirtualPlane plane = new VirtualPlane(localP, localN, worldPosition, worldNormal); Info info = new Info(target, plane, onCut, onCreated, boxedUserData); if (!UseAsync) { var watch = new System.Diagnostics.Stopwatch(); watch.Start(); int amount = 0; MeshCutting meshcutting = new MeshCutting(); VirtualMesh[] virtualMeshes = meshcutting.Cut(ref info); info.CreatedMeshes = virtualMeshes; if (virtualMeshes == null) OnCut(false, info); else { OnCut(true, info); amount = virtualMeshes.Length; } watch.Stop(); Debug.Log($"Synchronus cut creating {amount} meshes took {watch.ElapsedMilliseconds} ms. Success ? {virtualMeshes != null}"); } else { AsyncWorker.Enqeue(info); } } protected virtual void CreateGameObjects(Info info) { MeshCreationData creationInfo = MeshCreation.CreateObjects(info, DefaultMaterial); if (DestroyTargets) { if (info.MeshTarget) { info.MeshTarget.transform.position = new Vector3(0, -10000, 0); if (info.MeshTarget.GameobjectRoot != null) Destroy(info.MeshTarget.GameobjectRoot, 0); else Destroy(info.MeshTarget.gameObject, 0); } } if (creationInfo.CreatedObjects == null) Debug.LogWarning("Cut created empty objects"); info.OnCreatedCallback?.Invoke(info, creationInfo); } private void OnCut(bool success, Info info) { if (success) { lock (_successes) { _successes.Add(info); } } else { lock (_fails) { _fails.Add(info); } } } } }