using FIMSpace.FEditor; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace FIMSpace.Generating { public static class StackedTileGenerator { [System.Serializable] public class StackSetup { public List toStack = new List(); public bool RandomFlipX = false; public bool RandomFlipY = false; public bool RandomFlipZ = false; public Vector3 SourceRotationCorrection = Vector3.zero; public Vector3 SourceScaleCorrection = Vector3.one; public Vector3 ExtraSpacing = Vector3.zero; public int StackCount = 5; public int StackCountX = 1; public Vector3 RandomOffsets = Vector3.zero; public Vector3 RandomRotations = Vector3.zero; public Vector3 RandomScale = Vector3.zero; public Vector3 Rotate = Vector3.zero; public List RemovalBoxes = new List(); public StackSetup Copy() { return (StackSetup)MemberwiseClone(); } } [System.Serializable] public struct RemovalBox { public Vector3 Position; public Vector3 Size; public Bounds ToBounds() { return new Bounds(Position, Size); } } [System.Serializable] public struct IndividualModifier { public int InstanceIndex; public Vector3 PositionOffset; public Vector3 RotationOffset; public Vector3 SizeMul; public IndividualModifier(int index) { InstanceIndex = index; PositionOffset = Vector3.zero; RotationOffset = Vector3.zero; SizeMul = Vector3.one; } } static bool _Foldout = true; public static Mesh GenerateStack(StackSetup setup, System.Random random = null) { if (setup == null) return null; if (setup.toStack == null) return null; if (setup.toStack.Count == 0) return null; if (random == null) random = new System.Random(UnityEngine.Random.Range(-100000, 100000)); Mesh finalMesh = new Mesh(); for (int i = 0; i < setup.toStack.Count; i++) { if (setup.toStack[i] == null) return null; } List shufflingList = new List(); // Prepare list of corrected meshes and use it for random shuffling for (int i = 0; i < setup.toStack.Count; i++) { shufflingList.Add(FMeshUtils.GetSourceMeshCopy(setup.toStack[i], setup.SourceRotationCorrection, setup.SourceScaleCorrection, EOrigin.Center)); } shufflingList.Shuffle(); int getI = 0; CombineInstance[] meshInstances = new CombineInstance[setup.StackCount * setup.StackCountX]; CombineInstance preInstY = new CombineInstance(); CombineInstance preInstYt = new CombineInstance(); CombineInstance preInstX = new CombineInstance(); for (int y = 0; y < setup.StackCount; y += 1) { for (int x = 0; x < setup.StackCountX; x += 1) { CombineInstance inst = new CombineInstance(); inst.mesh = shufflingList[getI]; getI += 1; if (getI == shufflingList.Count) { getI = 0; shufflingList.Shuffle(); if ( shufflingList.Count > 1) if (shufflingList[0] == preInstX.mesh) shufflingList.RemoveAt(0); } Vector3 pos; if (y == 0) { pos = inst.mesh.bounds.center; pos.y += inst.mesh.bounds.extents.y; } else { pos = preInstY.transform.PosFromMatrix(); pos.y += preInstY.mesh.bounds.extents.y; pos.y += inst.mesh.bounds.extents.y * (1f + setup.ExtraSpacing.y); } if (x > 0) { pos.x = preInstX.transform.PosFromMatrix().x; pos.x += preInstX.mesh.bounds.extents.x; pos.x += inst.mesh.bounds.extents.x * (1f + setup.ExtraSpacing.x); } if (setup.RandomOffsets.x != 0) pos.x += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomOffsets.x; if (setup.RandomOffsets.y != 0) pos.y += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomOffsets.y; if (setup.RandomOffsets.z != 0) pos.z += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomOffsets.z; Vector3 rotation = Vector3.zero; if (setup.RandomRotations.x != 0) rotation.x += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomRotations.x; if (setup.RandomRotations.y != 0) rotation.y += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomRotations.y; if (setup.RandomRotations.z != 0) rotation.z += FGenerators.GetRandom(-1f, 1f, random) * setup.RandomRotations.z; rotation += setup.Rotate; Vector3 size = Vector3.one; if (setup.RandomFlipX) size.x = random.Next(0, 2) == 1 ? 1f : -1f; if (setup.RandomFlipY) size.y = random.Next(0, 2) == 1 ? 1f : -1f; if (setup.RandomFlipZ) size.z = random.Next(0, 2) == 1 ? 1f : -1f; float from = setup.RandomScale.x < 0f ? 0f : -1f; if (setup.RandomScale.x != 0) size.x += FGenerators.GetRandom(from, 1f, random) * Mathf.Abs(setup.RandomScale.x); from = setup.RandomScale.y < 0f ? 0f : -1f; if (setup.RandomScale.y != 0) size.y += FGenerators.GetRandom(from, 1f, random) * Mathf.Abs(setup.RandomScale.y); from = setup.RandomScale.z < 0f ? 0f : -1f; if (setup.RandomScale.z != 0) size.z += FGenerators.GetRandom(from, 1f, random) * Mathf.Abs(setup.RandomScale.z); inst.transform = Matrix4x4.TRS(pos, Quaternion.Euler(rotation), size); meshInstances[y * setup.StackCountX + x] = inst; preInstX = inst; if (x == 0) preInstYt = inst; if (x == setup.StackCountX - 1) preInstY = preInstYt; } } #region Check removals if (setup.RemovalBoxes.Count > 0) { for (int i = 0; i < meshInstances.Length; i++) { Bounds meshBox = FEngineering.TransformBounding(meshInstances[i].mesh.bounds, meshInstances[i].transform); bool collided = false; for (int r = 0; r < setup.RemovalBoxes.Count; r++) { if (setup.RemovalBoxes[r].ToBounds().Intersects(meshBox)) collided = true; if (collided) break; } if (collided) { var inst = meshInstances[i]; inst.mesh = null; meshInstances[i] = inst; } } } #endregion finalMesh.CombineMeshes(meshInstances); FMeshUtils.RenameMesh(finalMesh); return finalMesh; } #region Editor Code #if UNITY_EDITOR /// Returns true when GUI change occured public static bool _Editor_DisplayStackerSettings(StackSetup setup) { EditorGUI.BeginChangeCheck(); setup.SourceRotationCorrection = EditorGUILayout.Vector3Field("Correct Rotation:", setup.SourceRotationCorrection); setup.SourceScaleCorrection = EditorGUILayout.Vector3Field("Correct Scale:", setup.SourceScaleCorrection); GUILayout.Space(5); setup.StackCountX = EditorGUILayout.IntSlider("X Stack Count:", setup.StackCountX, 1, 20); setup.StackCount = EditorGUILayout.IntSlider("Y Stack Count:", setup.StackCount, 1, 20); setup.ExtraSpacing = EditorGUILayout.Vector3Field("Spacing:", setup.ExtraSpacing); GUILayout.Space(3); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Random Flip:", GUILayout.Width(100)); EditorGUIUtility.labelWidth = 20; setup.RandomFlipX = EditorGUILayout.Toggle("X:", setup.RandomFlipX, GUILayout.Width(50)); setup.RandomFlipY = EditorGUILayout.Toggle("Y:", setup.RandomFlipY, GUILayout.Width(50)); setup.RandomFlipZ = EditorGUILayout.Toggle("Z:", setup.RandomFlipZ, GUILayout.Width(50)); EditorGUIUtility.labelWidth = 0; EditorGUILayout.EndHorizontal(); setup.RandomOffsets = EditorGUILayout.Vector3Field("Random Offsets:", setup.RandomOffsets); setup.RandomRotations = EditorGUILayout.Vector3Field("Random Rotations:", setup.RandomRotations); setup.RandomScale = EditorGUILayout.Vector3Field("Random Scale:", setup.RandomScale); GUILayout.Space(3); setup.Rotate = EditorGUILayout.Vector3Field("Rotate:", setup.Rotate); GUILayout.Space(3); EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxStyle); GUILayout.Space(2); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(_Foldout ? FGUI_Resources.Tex_DownFold : FGUI_Resources.Tex_RightFold, EditorStyles.label, GUILayout.Width(22), GUILayout.Height(18))) { _Foldout = !_Foldout; } if (GUILayout.Button("Meshes To Generate Stack Of:", EditorStyles.label)) { _Foldout = !_Foldout; } #region Drag and drop var rect = GUILayoutUtility.GetLastRect(); var dropEvent = Event.current; if (dropEvent != null) { if (dropEvent.type == EventType.DragPerform || dropEvent.type == EventType.DragUpdated) { if (rect.Contains(dropEvent.mousePosition)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (dropEvent.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); foreach (var dragged in DragAndDrop.objectReferences) { if (dragged is Mesh) { setup.toStack.Add(dragged as Mesh); } else if (dragged is GameObject) { GameObject o = dragged as GameObject; foreach (var filt in o.GetComponentsInChildren()) { if (filt.sharedMesh) setup.toStack.Add(filt.sharedMesh); } } } } Event.current.Use(); } } } #endregion GUILayout.FlexibleSpace(); if (GUILayout.Button("+", FGUI_Resources.ButtonStyle, GUILayout.Width(22))) { setup.toStack.Add(setup.toStack.Count > 0 ? setup.toStack[setup.toStack.Count - 1] : null); } EditorGUILayout.EndHorizontal(); int toRemove = -1; if (_Foldout) { EditorGUIUtility.labelWidth = 50; for (int i = 0; i < setup.toStack.Count; i++) { EditorGUILayout.BeginHorizontal(); setup.toStack[i] = (Mesh)EditorGUILayout.ObjectField("[" + i + "]", setup.toStack[i], typeof(Mesh), false); if (GUILayout.Button(FGUI_Resources.GUIC_Remove, FGUI_Resources.ButtonStyle, GUILayout.Height(19), GUILayout.Width(22))) { toRemove = i; } EditorGUILayout.EndHorizontal(); } EditorGUIUtility.labelWidth = 0; } EditorGUILayout.EndVertical(); if (toRemove != -1) setup.toStack.RemoveAt(toRemove); #region Removal Boxes GUILayout.Space(3); EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxStyle); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Removal Box Points:"); GUILayout.FlexibleSpace(); if (GUILayout.Button("+", FGUI_Resources.ButtonStyle, GUILayout.Width(22))) { setup.RemovalBoxes.Add(new RemovalBox()); } EditorGUILayout.EndHorizontal(); toRemove = -1; for (int i = 0; i < setup.RemovalBoxes.Count; i++) { EditorGUILayout.BeginHorizontal(); Editor_DrawBoundsSettings(setup, i); if (GUILayout.Button(FGUI_Resources.GUIC_Remove, FGUI_Resources.ButtonStyle, GUILayout.Height(19), GUILayout.Width(22))) toRemove = i; EditorGUILayout.EndHorizontal(); } if (toRemove != -1) setup.RemovalBoxes.RemoveAt(toRemove); EditorGUILayout.EndVertical(); #endregion GUILayout.Space(3); return EditorGUI.EndChangeCheck(); } static void Editor_DrawBoundsSettings(StackSetup setup, int i) { if (setup.RemovalBoxes.ContainsIndex(i) == false) return; RemovalBox rBox = setup.RemovalBoxes[i]; //EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("[" + i + "]", EditorStyles.boldLabel, GUILayout.Width(28)); rBox.Position = EditorGUILayout.Vector3Field("Removal Box Center:", rBox.Position); EditorGUIUtility.labelWidth = 40; GUILayout.Space(8); rBox.Size = EditorGUILayout.Vector3Field("Size:", rBox.Size); EditorGUIUtility.labelWidth = 0; //EditorGUILayout.EndHorizontal(); setup.RemovalBoxes[i] = rBox; } #endif #endregion } }