// 27 Slicer // Copyright 2021 Deftly Games // https://slicer.deftly.games/ using Slicer.Core; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using UnityEngine; namespace Slicer { /// /// This component will search for any MeshFilters that are a descendant of this component. It will then slice any meshes that are assigned to the found MeshFilters. /// /// ///

[REFERENCE MANUAL](xref:manual\components\mesh_slicer_component) ///
[ExecuteAlways] [DisallowMultipleComponent] [AddComponentMenu("Slicer/Mesh Slicer Component")] [HelpURL(SlicerConfiguration.SiteUrl + SlicerConfiguration.ComponentsManualPath + "mesh_slicer_component.html")] public class MeshSlicerComponent : SlicerComponent { /// /// Should the vertices of the mesh be sliced. /// /// false by default. [Tooltip("Should the vertices of the mesh be sliced.")] public bool SkipVertices = false; /// /// Should the UVs of the mesh be sliced. /// /// true by default. [Tooltip("Should the UVs of the mesh be sliced.")] public bool SkipUvs = true; public UvMappingSettings UvMappingSettings = new UvMappingSettings(); [HideInInspector] [SerializeField] private List meshDetailsList = new List(); /// /// A read only collection of Mesh Renders and Filters that are being managed by this . /// public ReadOnlyCollection MeshDetailsList { get { return meshDetailsList.AsReadOnly(); } } private void OnDestroy() { if (Application.isPlaying) { foreach (var md in meshDetailsList) { if (md == null) { continue; } md.Destroy(); } } } /// public override void PreGatherDetails() { foreach (var md in meshDetailsList) { if (md == null) { continue; } md.Remove = true; } } /// public override void GatherDetails(Transform childTransform, Transform rootTransform) { var childMeshFilter = childTransform.GetComponent(); var childMeshRenderer = childTransform.GetComponent(); if (childMeshFilter == null || childMeshRenderer == null || childMeshRenderer.enabled == false) { return; } MeshDetails md = meshDetailsList.FirstOrDefault(e => { if (e == null) { return false; } if (e.Transform == childTransform) { return true; } if (e.Id == childMeshFilter.GetInstanceID()) { return true; } return false; }); if (md == null) { if (childMeshFilter.sharedMesh == null) { return; } md = new MeshDetails(); md.Id = childMeshFilter.GetInstanceID(); md.Transform = childTransform; md.MeshFilter = childMeshFilter; md.MeshRenderer = childMeshRenderer; var copiedModifiedMesh = meshDetailsList.FirstOrDefault(e => e.SlicedMesh == childMeshFilter.sharedMesh); if (copiedModifiedMesh != null && copiedModifiedMesh.OriginalSharedMesh != null) { md.OriginalSharedMesh = copiedModifiedMesh.OriginalSharedMesh; } else { var slicedMeshInParent = GetSlicedMeshInParent(rootTransform, childMeshFilter.sharedMesh); if (slicedMeshInParent != null) { md.OriginalSharedMesh = slicedMeshInParent; } else { md.OriginalSharedMesh = childMeshFilter.sharedMesh; } } if (md.SlicedMesh == null && md.OriginalSharedMesh != null && md.OriginalSharedMesh.isReadable) { CopySharedMeshToSlicedMesh(md); } meshDetailsList.Add(md); } else if (md.Id != childMeshFilter.GetInstanceID() || (md.Id == childMeshFilter.GetInstanceID() && md.Transform == null)) { md.Id = childMeshFilter.GetInstanceID(); md.Transform = childTransform; md.MeshFilter = childMeshFilter; md.MeshRenderer = childMeshRenderer; if (md.OriginalSharedMesh != null && md.OriginalSharedMesh.isReadable) { CopySharedMeshToSlicedMesh(md); } } else if (((md.SlicedMesh != md.MeshFilter.sharedMesh && SlicingEnabled) || // The user just replaced the mesh while slicing is enabled OR (md.MeshFilter.sharedMesh != md.OriginalSharedMesh && !SlicingEnabled)) && // The user just replaced the mesh while slicing is disabled (md.MeshFilter.sharedMesh == null || md.MeshFilter.sharedMesh.isReadable)) { md.OriginalSharedMesh = md.MeshFilter.sharedMesh; CopySharedMeshToSlicedMesh(md); } else if (md.SlicedMesh == null && md.OriginalSharedMesh != null && md.OriginalSharedMesh.isReadable) { // The mesh details has a original mesh, but no mesh to slice CopySharedMeshToSlicedMesh(md); } if (md.OriginalSharedMesh == null) { return; } if (md.OriginalSharedMesh.isReadable) { md.OriginalBounds = MeshUtility.CalculateBounds(md.OriginalSharedMesh, md.Transform, rootTransform); } if (SlicingEnabled) { if (md.MeshFilter.sharedMesh != md.SlicedMesh) { md.EnableSlicing(); } } else { if (md.MeshFilter.sharedMesh != md.OriginalSharedMesh) { md.DisableSlicing(); } } md.Remove = false; } private Mesh GetSlicedMeshInParent(Transform transform, Mesh mesh) { var parentMeshSlicer = GetMeshSlicerInParent(transform); if (parentMeshSlicer != null) { var copiedModifiedMesh = parentMeshSlicer.meshDetailsList.FirstOrDefault(e => e.SlicedMesh == mesh); if (copiedModifiedMesh != null && copiedModifiedMesh.OriginalSharedMesh != null) { return copiedModifiedMesh.OriginalSharedMesh; } } return null; } private MeshSlicerComponent GetMeshSlicerInParent(Transform transform) { var parentTransform = transform.parent; if (parentTransform == null) { return null; } var meshSlicer = parentTransform.GetComponent(); if (meshSlicer != null) { return meshSlicer; } return GetMeshSlicerInParent(parentTransform); } private void CopySharedMeshToSlicedMesh(MeshDetails md) { md.DestroySlicedMesh(); if (md.OriginalSharedMesh == null) { md.SlicedMesh = null; } else { md.SlicedMesh = MeshUtility.CopyMesh(md.OriginalSharedMesh); } md.ResetHashes(); } /// public override Hash128 PostGatherDetails() { var hash = base.PostGatherDetails(); var skipVertHash = HashUtility.CalculateHash(SkipVertices, 3); var skipUvHash = HashUtility.CalculateHash(SkipUvs, 4); var uvMappingHash = UvMappingSettings.CalculateHash(); HashUtility.AppendHash(uvMappingHash, skipVertHash, skipUvHash, ref hash); for (int i = meshDetailsList.Count - 1; i >= 0; i--) { var md = meshDetailsList[i]; if (md == null) { meshDetailsList.RemoveAt(i); continue; } if (!md.Remove) { var tempHash = md.CalculateHash(); HashUtility.AppendHash(tempHash, ref hash); continue; } md.DisableSlicing(); md.Destroy(); meshDetailsList.RemoveAt(i); } return hash; } /// public override Bounds? CalculateBounds() { if (SkipBoundsCalculation) { return null; } Bounds? encapsulatedBounds = null; foreach (var md in meshDetailsList) { encapsulatedBounds = BoundsUtility.Encapsulate(encapsulatedBounds, md.OriginalBounds); } return encapsulatedBounds; } /// public override void Slice(Vector3 size, Transform rootTransform, Bounds completeBounds, Bounds slicedBounds, Vector3 slices) { foreach (var md in meshDetailsList) { if (md.OriginalSharedMesh == null) { continue; } if (!md.OriginalSharedMesh.isReadable) { continue; } var vertHash = SliceUtility.SliceVerts(md.OriginalSharedMesh, md.SlicedMesh, md.Transform, size, rootTransform, completeBounds, slicedBounds, SkipVertices, md.SlicedVertHash); var uvHash = UvUtility.SliceUvs(md.OriginalSharedMesh, md.SlicedMesh, md.MeshRenderer, md.Transform, size, rootTransform, completeBounds, slicedBounds, slices, SkipUvs, md.SlicedUvHash, UvMappingSettings); if (vertHash != md.SlicedVertHash || uvHash != md.SlicedUvHash || !SlicerConfiguration.SkipUnmodifiedSlices) { md.SlicedMesh.UploadMeshData(false); md.SlicedMesh.RecalculateBounds(); md.SlicedVertHash = vertHash; md.SlicedUvHash = uvHash; } } } /// public override void DisableSlicing() { base.DisableSlicing(); foreach (var md in meshDetailsList) { md.DisableSlicing(); } } /// public override void EnableSlicing() { base.EnableSlicing(); foreach (var md in meshDetailsList) { md.EnableSlicing(); } } /// public override void FinalizeSlicing() { foreach (var meshDetail in meshDetailsList) { meshDetail.FinalizeSlicing(); } meshDetailsList.Clear(); SlicerController.SafeDestroy(this); } /// /// Gets the of a Mesh if it is being tracked by this MeshSlicerComponent. /// /// the transform to search for. /// the mesh to search for. this could be either the or . /// Returns the of the mesh, or null if it is not tracked by this MeshSlicerComponent. public MeshDetails GetMeshDetailsByMesh(Transform transform, Mesh mesh) { if (mesh == null) { return null; } foreach (var meshDetail in meshDetailsList) { if (meshDetail.Transform != transform) { continue; } if (meshDetail.OriginalSharedMesh == mesh || meshDetail.SlicedMesh == mesh) { return meshDetail; } // last ditch effort to match // Reverting a prefab results in a new mesh with a different id // So try and match it by name instead var meshName = mesh.name.Replace(" Editor ", " mode ").Replace(" Runtime ", " mode "); if (meshDetail.OriginalSharedMesh != null) { var originalSharedMeshName = meshDetail.OriginalSharedMesh.name.Replace(" Editor ", " mode ").Replace(" Runtime ", " mode "); if (meshName == originalSharedMeshName) { return meshDetail; } } if (meshDetail.SlicedMesh != null) { var slicedMeshName = meshDetail.SlicedMesh.name.Replace(" Editor ", " mode ").Replace(" Runtime ", " mode "); if (meshName == slicedMeshName) { return meshDetail; } } } return null; } /// /// Gets the of a Mesh if it is being tracked by this MeshSlicerComponent. /// /// the transform to search for. /// Returns the of the mesh, or null if it is not tracked by this MeshSlicerComponent. public MeshDetails GetMeshDetailsByTransform(Transform transform) { var md = meshDetailsList.FirstOrDefault(e => e.Transform == transform); return md; } } }