// 27 Slicer
// Copyright 2021 Deftly Games
// https://slicer.deftly.games/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Slicer.Core
{
///
/// Contains useful methods for slicing many types of objects.
///
public static class SliceUtility
{
///
/// The hash that is to be used when Verts are not being sliced.
///
public static readonly Hash128 SkipVertHash = HashUtility.CalculateHash(94771);
///
/// The hash that is to be used when UVs are not being sliced.
///
public static readonly Hash128 SkipUvHash = HashUtility.CalculateHash(63857);
///
/// Slices a bounds as if it is made up of discrete vertices.
///
/// The bounds to slice
/// The transform of the GameObject that contains the bounds
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The transform of the parent SlicerController"
/// The complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Returns the sliced bounds
public static Bounds SliceVerts(Bounds origBounds, Transform boundsTransform, Vector3 size, Transform rootTransform, Bounds completeBounds, Bounds slicedBounds)
{
BoundsUtility.GetVerts(origBounds, TempCollections.Vector3);
var matrix = MatrixUtility.BuildTransformMatrix(boundsTransform, rootTransform);
Vector3 completeBoundsCenter = completeBounds.center;
Vector3 completeBoundsExtents = completeBounds.extents;
Vector3 slicedBoundsExtents = slicedBounds.extents;
for (int i = 0; i < TempCollections.Vector3.Count; i++)
{
var vert = TempCollections.Vector3[i];
vert = SliceVector(vert, matrix, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
TempCollections.Vector3[i] = vert;
}
var bounds = BoundsUtility.Encapsulate(TempCollections.Vector3);
return bounds;
}
///
/// Slices the vertices of the supplied origSharedMesh and applies it to the supplied modifiedMesh.
///
/// The original mesh.
/// The mesh the slicing will be applied to.
/// The transform of the GameObject that contains the mesh.
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The transform of the parent SlicerController"
/// The complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Should the verts be sliced, if true the vertices will not be sliced.
/// The previous hash from slicing, used to determine if any changes should be uploaded to the GPU etc.
/// Returns the hash of the sliced verts, or if skipVertices is set to true.
public static Hash128 SliceVerts(Mesh origSharedMesh, Mesh modifiedMesh, Transform meshTransform, Vector3 size, Transform rootTransform, Bounds completeBounds, Bounds slicedBounds, bool skipVertices, Hash128 previousHash)
{
origSharedMesh.GetVertices(TempCollections.Vector3);
if (skipVertices)
{
// if the previous hash is already skipVertHash then we have already skipped prior, no need to do it again
if (previousHash != SkipVertHash || !SlicerConfiguration.SkipUnmodifiedSlices)
{
modifiedMesh.SetVertices(TempCollections.Vector3);
}
return SkipVertHash;
}
var matrix = MatrixUtility.BuildTransformMatrix(meshTransform, rootTransform);
Vector3 completeBoundsCenter = completeBounds.center;
Vector3 completeBoundsExtents = completeBounds.extents;
Vector3 slicedBoundsCenter = slicedBounds.center;
Vector3 slicedBoundsExtents = slicedBounds.extents;
Vector3 offset = slicedBoundsCenter - completeBoundsCenter;
completeBoundsCenter = slicedBoundsCenter;
Hash128 hash;
{
// The following hashes should be enough to determine if the vertices are going to be changed
// It assumes that the verts in origSharedMesh is not changed.
// If the verts are modified, slightly tweaking the first vert will be enough to change the hash
var origSharedMeshHash = HashUtility.CalculateHash(origSharedMesh);
var matrixHash = HashUtility.CalculateHash(matrix);
var sizeHash = HashUtility.CalculateHash(size);
var completeBoundsCenterHash = HashUtility.CalculateHash(completeBoundsCenter);
var completeBoundsExtentsHash = HashUtility.CalculateHash(completeBoundsExtents);
var slicedBoundsExtentsHash = HashUtility.CalculateHash(slicedBoundsExtents);
var offsetHash = HashUtility.CalculateHash(offset);
HashUtility.AppendHash(origSharedMeshHash, matrixHash, sizeHash, completeBoundsCenterHash, completeBoundsExtentsHash, offsetHash, ref slicedBoundsExtentsHash);
hash = slicedBoundsExtentsHash;
if (TempCollections.Vector3.Count > 0)
{
var firstVertHash = HashUtility.CalculateHash(TempCollections.Vector3[0]);
}
}
if (hash == previousHash && SlicerConfiguration.SkipUnmodifiedSlices)
{
return hash;
}
for (int i = 0; i < TempCollections.Vector3.Count; i++)
{
var vert = TempCollections.Vector3[i];
vert += offset;
vert = SliceVector(vert, matrix, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
TempCollections.Vector3[i] = vert;
}
modifiedMesh.SetVertices(TempCollections.Vector3);
return hash;
}
///
/// Slices a single vector after transforming it by the supplied matrix.
///
/// The vector to apply slicing to.
/// The matrix to apply to the vector before slicing.
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The center vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Returns the sliced vector.
public static Vector3 SliceVector(Vector3 vert, Matrix4x4 matrix, Vector3 size, Vector3 completeBoundsCenter, Vector3 completeBoundsExtents, Vector3 slicedBoundsExtents)
{
var transformedVert = VectorUtility.TransformVector(vert, matrix);
transformedVert = SliceVector(transformedVert, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
vert = VectorUtility.TransformVector(transformedVert, matrix.inverse);
return vert;
}
///
/// Slices a single vector.
///
/// The vector to apply slicing to.
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Returns the sliced vector.
public static Vector3 SliceVector(Vector3 vert, Vector3 size, Bounds completeBounds, Bounds slicedBounds)
{
return SliceVector(vert, size, completeBounds.center, completeBounds.extents, slicedBounds.extents);
}
///
/// Slices a single vector.
///
/// The vector to apply slicing to.
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The center vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Returns the sliced vector.
public static Vector3 SliceVector(Vector3 vert, Vector3 size, Vector3 completeBoundsCenter, Vector3 completeBoundsExtents, Vector3 slicedBoundsExtents)
{
vert.x = SliceSingleVectorDimension(0, vert, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
vert.y = SliceSingleVectorDimension(1, vert, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
vert.z = SliceSingleVectorDimension(2, vert, size, completeBoundsCenter, completeBoundsExtents, slicedBoundsExtents);
return vert;
}
///
/// Slices a single dimension of a single vector.
///
/// The dimension index of the supplied transformedVertVec.
/// The Vector that will have its single dimension sliced.
/// The final size (as a scale) of all the items that are to be sliced. See SlicerController.Size for more information.
/// The center vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the complete bounding box of all sliced items after being sliced. See SlicerController.CompleteBounds for more information.
/// The extents vector of the bounding box of the slices that will be made. See SlicerController.SlicedBounds for more information.
/// Returns the sliced vector dimension.
private static float SliceSingleVectorDimension(int i, Vector3 transformedVertVec, Vector3 sizeVec, Vector3 completeBoundsCenterVec, Vector3 completeBoundsExtentsVec, Vector3 slicedBoundsExtentsVec)
{
float boundsCenter = completeBoundsCenterVec[i];
float boundsExtents = completeBoundsExtentsVec[i];
float slicedBoundsExtents = slicedBoundsExtentsVec[i];
float size = sizeVec[i];
var transformedVert = transformedVertVec[i];
if (transformedVert <= boundsCenter - slicedBoundsExtents)
{
var offset = (boundsCenter - boundsExtents) - transformedVert;
transformedVert = (boundsCenter - (boundsExtents * size)) - offset;
}
else if (transformedVert >= boundsCenter + slicedBoundsExtents)
{
var offset = (boundsCenter + boundsExtents) - transformedVert;
transformedVert = (boundsCenter + (boundsExtents * size)) - offset;
}
else
{
var slicedBoundsMin = boundsCenter - slicedBoundsExtents;
var slicedBoundsMax = boundsCenter + slicedBoundsExtents;
var normalizedPos = (transformedVert - slicedBoundsMin) / (slicedBoundsMax - slicedBoundsMin);
var scaledSlicedExtents = slicedBoundsExtents * size;
transformedVert = Mathf.Lerp(boundsCenter - scaledSlicedExtents, boundsCenter + scaledSlicedExtents, normalizedPos);
}
return transformedVert;
}
}
}