artnas / Unity-Plane-Mesh-Splitter

Split large meshes in Unity into smaller submeshes
MIT License
263 stars 41 forks source link

MeshSplitController Gizmos don't account for mesh rotation #8

Open STARasGAMES opened 2 years ago

STARasGAMES commented 2 years ago

Here is how it looks image

Transform handle is in pivot local position image

Also, Gizmos don't account for selected split axes image

STARasGAMES commented 2 years ago

I changed the way cubes are drawn. Now they properly preview how the mesh will be split by the tool, with the scale and rotation applied. However, it doesn't work when only one axis is selected.

image

Also, this script accounts for cases when the root object has MeshCollider, and in parameters GenerateColliders is set to true.

/* https://github.com/artnas/Unity-Plane-Mesh-Splitter */
/* https://github.com/artnas/Unity-Plane-Mesh-Splitter/issues/8 */

using System;
using System.Collections.Generic;
using UnityEngine;

namespace MeshSplit
{
    public class MeshSplitController : MonoBehaviour
    {
        public bool AutoSplitAtAwake;
        public MeshSplitParameters Parameters;
        public bool DrawGridGizmosWhenSelected = false;

        private Mesh _baseMesh;
        private MeshRenderer _baseRenderer;

        // generated children are kept here, so the script knows what to delete on Split() or Clear()
        [HideInInspector]
        [SerializeField]
        private List<GameObject> _children;

        private void Awake()
        {
            if (AutoSplitAtAwake)
            {
                Split();
            }
        }

        public void Split()
        {
            DestroyChildren();

            if (GetUsedAxisCount() < 1)
            {
                throw new Exception("You have to choose at least 1 axis.");
            }

            var meshFilter = GetComponent<MeshFilter>();
            if (meshFilter)
            {
                _baseMesh = meshFilter.sharedMesh;
            }
            else
            {
                throw new Exception("MeshFilter component is required.");
            }

            _baseRenderer = GetComponent<MeshRenderer>();
            if (_baseRenderer)
            {
                _baseRenderer.enabled = false;
            }

            var meshCollider = GetComponent<MeshCollider>();
            if (meshCollider && Parameters.GenerateColliders)
            {
                meshCollider.enabled = false;
            }

            var meshSplitter = new MeshSplitter(Parameters);
            var subMeshes = meshSplitter.Split(_baseMesh);

            _children = new List<GameObject>();
            foreach (var subMesh in subMeshes)
            {
                CreateChild(subMesh);
            }
        }

        private void CreateChild((Vector3Int gridPoint, Mesh mesh) subMesh)
        {
            var newGameObject = new GameObject
            {
                name = "SubMesh " + subMesh.gridPoint
            };

            newGameObject.transform.SetParent(transform, false);
            if (Parameters.UseParentLayer)
            {
                newGameObject.layer = gameObject.layer;
            }

            if (Parameters.UseParentStaticFlag)
            {
                newGameObject.isStatic = gameObject.isStatic;
            }

            // assign the new mesh to this submeshes mesh filter
            var newMeshFilter = newGameObject.AddComponent<MeshFilter>();
            newMeshFilter.sharedMesh = subMesh.mesh;

            var newMeshRenderer = newGameObject.AddComponent<MeshRenderer>();
            if (Parameters.UseParentMeshRendererSettings && _baseRenderer)
            {
                newMeshRenderer.sharedMaterial = _baseRenderer.sharedMaterial;
                newMeshRenderer.sortingOrder = _baseRenderer.sortingOrder;
                newMeshRenderer.sortingLayerID = _baseRenderer.sortingLayerID;
                newMeshRenderer.shadowCastingMode = _baseRenderer.shadowCastingMode;
            }

            if (Parameters.GenerateColliders)
            {
                var meshCollider = newGameObject.AddComponent<MeshCollider>();
                meshCollider.convex = Parameters.UseConvexColliders;
                meshCollider.sharedMesh = subMesh.mesh;
            }

            _children.Add(newGameObject);
        }

        private int GetUsedAxisCount()
        {
            return (Parameters.SplitAxisX ? 1 : 0) + (Parameters.SplitAxisY ? 1 : 0) + (Parameters.SplitAxisZ ? 1 : 0);
        }

        public void Clear()
        {
            DestroyChildren();

            var meshRenderer = GetComponent<MeshRenderer>();
            if (meshRenderer)
            {
                meshRenderer.enabled = true;
            }
        }

        private void DestroyChildren()
        {
            if (_children == null) return;

            foreach (var t in _children)
            {
                DestroyImmediate(t.GetComponent<MeshFilter>().sharedMesh);
                DestroyImmediate(t);
            }

            _children.Clear();
        }

        private void OnDrawGizmosSelected()
        {
            var meshFilter = GetComponent<MeshFilter>();
            var renderer = GetComponent<Renderer>();
            if (!DrawGridGizmosWhenSelected || !meshFilter || !meshFilter.sharedMesh || !renderer)
                return;

            var t = transform;
            var bounds = meshFilter.sharedMesh.bounds;

            var xSize = Parameters.SplitAxisX ? Mathf.Ceil(bounds.extents.x) : 0 + Parameters.GridSize / 2f;
            var ySize = Parameters.SplitAxisY ? Mathf.Ceil(bounds.extents.y) : 0 + Parameters.GridSize /2f;
            var zSize = Parameters.SplitAxisZ ? Mathf.Ceil(bounds.extents.z) : 0 + Parameters.GridSize/2f;

            var center = bounds.center;

            Gizmos.color = new Color(1, 1, 1, 0.3f);

            // X aligned lines
            for (var y = -ySize; y <= ySize; y += Parameters.GridSize)
            {
                for (var z = -zSize; z <= zSize; z += Parameters.GridSize)
                {
                    var start = t.TransformPoint(center + new Vector3(-xSize, y, z));
                    var end = t.TransformPoint(center + new Vector3(xSize, y, z));
                    Gizmos.DrawLine(start, end);
                }
            }

            // Y aligned lines
            for (var x = -xSize; x <= xSize; x += Parameters.GridSize)
            {
                for (var z = -zSize; z <= zSize; z += Parameters.GridSize)
                {
                    var start = t.TransformPoint(center + new Vector3(x, -ySize, z));
                    var end = t.TransformPoint(center + new Vector3(x, ySize, z));
                    Gizmos.DrawLine(start, end);
                }
            }

            // Z aligned lines
            for (var y = -ySize; y <= ySize + 1; y += Parameters.GridSize)
            {
                for (var x = -xSize; x <= xSize + 1; x += Parameters.GridSize)
                {
                    var start = t.TransformPoint(center + new Vector3(x, y, -zSize));
                    var end = t.TransformPoint(center + new Vector3(x, y, zSize));
                    Gizmos.DrawLine(start, end);
                }
            }
        }
    }
}
artnas commented 2 years ago

Hey. This is very cool, want to make a pull request?

However, it doesn't work when only one axis is selected.

I'll take a look at that

STARasGAMES commented 2 years ago

Hey. This is very cool, want to make a pull request?

For this, I need to fix a bunch of issues :)

  1. There should be two different modes for setting grid size: Local and Global. For now, the plugin splits mesh in its local space, but draws gizmos as if it splits mesh in global space. My version partially fixes this issue, but there is more...
  2. I don't like the idea of manually setting GridSize. Instead, there should be Vector3Int which indicates how many splits mesh needs in its local space. This way problem 1 vanishes automatically and there is no need in Parameters.SplitAxis[X,Y,Z]. Also, it makes more sense to do this way, because using GridSize may produce uneven pieces. For example, a mesh has bounds of 100x100x100, and GridSize is set to 16. Splitting will produce 4x4x4 meshes on the edges. Kinda cringe:)
  3. Need to modify mesh splitting algorithm to work properly with the approach from 2.
  4. Setting GridSize is easy and convenient, it's just a slider with instant feedback in Gizmos. Approach with Vector3Int requires writing a custom editor that will simplify the setup process.
  5. Another way is to make GridSize a Vector3 instead of float.