#if UNITY_EDITOR using UnityEngine; using System.Collections.Generic; namespace O3DWB { public static class Object2ObjectSnap { public static int MaxSourceObjects { get { return 100; } } public struct SnapResult { private bool _wasSnapped; private Vector3 _snapPivot; private Vector3 _snapDestination; public bool WasSnapped { get { return _wasSnapped; } } public Vector3 SnapPivot { get { return _snapPivot; } } public Vector3 SnapDestination { get { return _snapDestination; } } public SnapResult(bool wasSnapped, Vector3 snapPivot, Vector3 snapDestination) { _wasSnapped = wasSnapped; _snapPivot = snapPivot; _snapDestination = snapDestination; } public static SnapResult GetWasNotSnapped() { return new SnapResult(false, Vector3.zero, Vector3.zero); } } public static SnapResult Snap(List roots, float snapEpsilon, List ignoreDestSnapObjects) { if (ignoreDestSnapObjects == null) ignoreDestSnapObjects = new List(); Vector3 snapDestPt = Vector3.zero, snapPivotPt = Vector3.zero; float minSnapDistance = float.MaxValue; List allSourceObjects = new List(); foreach (var root in roots) allSourceObjects.AddRange(root.GetAllChildrenIncludingSelf()); foreach(var hierarchyRoot in roots) { List sourceObjects = hierarchyRoot.GetAllChildrenIncludingSelf(); if (sourceObjects.Count > MaxSourceObjects) continue; List meshObjectsInHierarchy = hierarchyRoot.GetHierarchyObjectsWithMesh(); List spriteObjectsInHierarchy = hierarchyRoot.GetHierarchyObjectsWithSprites(); if (meshObjectsInHierarchy.Count == 0 && spriteObjectsInHierarchy.Count == 0) return SnapResult.GetWasNotSnapped(); var object2ObjectSnapDatabase = Object2ObjectBoxSnapDatabase.Instance; if (sourceObjects.Count > 1 || spriteObjectsInHierarchy.Count == 0) { foreach (var sourceObject in sourceObjects) { Box queryBox = sourceObject.GetWorldBox(); queryBox.Size = queryBox.Size + Vector3.one * snapEpsilon; List nearbyObjects = Octave3DScene.Get().OverlapBox(queryBox); if (nearbyObjects.Count == 0) return SnapResult.GetWasNotSnapped(); nearbyObjects.RemoveAll(item => allSourceObjects.Contains(item) || ignoreDestSnapObjects.Contains(item)); Object2ObjectBoxSnapData sourceSnapData = object2ObjectSnapDatabase.GetObject2ObjectBoxSnapData(sourceObject); if (sourceSnapData == null) continue; var sourceSnapBoxes = sourceSnapData.GetWorldSnapBoxes(); foreach (var destObject in nearbyObjects) { Object2ObjectBoxSnapData destSnapData = object2ObjectSnapDatabase.GetObject2ObjectBoxSnapData(destObject); if (destSnapData == null) continue; if (destObject.HasMeshFilterWithValidMesh() || destObject.HasSkinnedMeshRendererWithValidMesh()) { var renderer = destObject.GetRenderer(); if (!renderer.enabled) continue; } else if (destObject.IsSprite()) { var spriteRenderer = destObject.GetComponent(); if (!spriteRenderer.enabled) continue; } var destSnapBoxes = destSnapData.GetWorldSnapBoxes(); foreach (var sourceSnapBox in sourceSnapBoxes) { var sourceBoxPoints = sourceSnapBox.GetCenterAndCornerPoints(); foreach (var destSnapBox in destSnapBoxes) { var destBoxPoints = destSnapBox.GetCenterAndCornerPoints(); foreach (var srcPt in sourceBoxPoints) { foreach (var destPt in destBoxPoints) { float distance = (destPt - srcPt).magnitude; if (distance < minSnapDistance) { minSnapDistance = distance; snapDestPt = destPt; snapPivotPt = srcPt; } } } } } } } } else { Box hierarchyWorldAABB = hierarchyRoot.GetHierarchyWorldBox(); Box hierarchyQueryBox = hierarchyWorldAABB; hierarchyQueryBox.Size = hierarchyQueryBox.Size + Vector3.one * snapEpsilon; List nearbyObjects = Octave3DScene.Get().OverlapBox(hierarchyQueryBox); if (nearbyObjects.Count == 0) return SnapResult.GetWasNotSnapped(); nearbyObjects.RemoveAll(item => allSourceObjects.Contains(item) || ignoreDestSnapObjects.Contains(item)); List hierarchyBoxCornerPoints = hierarchyWorldAABB.GetCornerPoints(); foreach (GameObject gameObject in nearbyObjects) { Box objectWorldBox = Box.GetInvalid(); Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh != null) objectWorldBox = gameObject.GetMeshWorldBox(); if (objectWorldBox.IsInvalid() && gameObject.HasSpriteRendererWithSprite()) objectWorldBox = gameObject.GetNonMeshWorldBox(); if (objectWorldBox.IsInvalid()) continue; List worldBoxCornerPoints = objectWorldBox.GetCornerPoints(); foreach (Vector3 hierarchyBoxPt in hierarchyBoxCornerPoints) { foreach (Vector3 objectMeshBoxPt in worldBoxCornerPoints) { float distance = (hierarchyBoxPt - objectMeshBoxPt).magnitude; if (distance < minSnapDistance) { minSnapDistance = distance; snapDestPt = objectMeshBoxPt; snapPivotPt = hierarchyBoxPt; } } } } } } if (minSnapDistance < snapEpsilon) { foreach(var root in roots) ObjectHierarchySnap.Snap(root, snapPivotPt, snapDestPt); return new SnapResult(true, snapPivotPt, snapDestPt); } return SnapResult.GetWasNotSnapped(); } public static SnapResult Snap(GameObject root, float snapEpsilon, List ignoreDestSnapObjects) { if (ignoreDestSnapObjects == null) ignoreDestSnapObjects = new List(); List sourceObjects = root.GetAllChildrenIncludingSelf(); if (sourceObjects.Count > MaxSourceObjects) return SnapResult.GetWasNotSnapped(); Vector3 snapDestPt = Vector3.zero, snapPivotPt = Vector3.zero; float minSnapDistance = float.MaxValue; List meshObjectsInHierarchy = root.GetHierarchyObjectsWithMesh(); List spriteObjectsInHierarchy = root.GetHierarchyObjectsWithSprites(); if (meshObjectsInHierarchy.Count == 0 && spriteObjectsInHierarchy.Count == 0) return SnapResult.GetWasNotSnapped(); var object2ObjectSnapDatabase = Object2ObjectBoxSnapDatabase.Instance; if (sourceObjects.Count > 1 || spriteObjectsInHierarchy.Count == 0) { foreach (var sourceObject in sourceObjects) { Box queryBox = sourceObject.GetWorldBox(); queryBox.Size = queryBox.Size + Vector3.one * snapEpsilon; List nearbyObjects = Octave3DScene.Get().OverlapBox(queryBox); if (nearbyObjects.Count == 0) continue; nearbyObjects.RemoveAll(item => item.transform.IsChildOf(root.transform) || ignoreDestSnapObjects.Contains(item)); Object2ObjectBoxSnapData sourceSnapData = object2ObjectSnapDatabase.GetObject2ObjectBoxSnapData(sourceObject); if (sourceSnapData == null) continue; var sourceSnapBoxes = sourceSnapData.GetWorldSnapBoxes(); foreach (var destObject in nearbyObjects) { Object2ObjectBoxSnapData destSnapData = object2ObjectSnapDatabase.GetObject2ObjectBoxSnapData(destObject); if (destSnapData == null) continue; var destSnapBoxes = destSnapData.GetWorldSnapBoxes(); foreach (var sourceSnapBox in sourceSnapBoxes) { var sourceBoxPoints = sourceSnapBox.GetCenterAndCornerPoints(); foreach (var destSnapBox in destSnapBoxes) { var destBoxPoints = destSnapBox.GetCenterAndCornerPoints(); foreach (var srcPt in sourceBoxPoints) { foreach (var destPt in destBoxPoints) { float distance = (destPt - srcPt).magnitude; if (distance < minSnapDistance) { minSnapDistance = distance; snapDestPt = destPt; snapPivotPt = srcPt; } } } } } } } } else { Box hierarchyWorldAABB = root.GetHierarchyWorldBox(); Box hierarchyQueryBox = hierarchyWorldAABB; hierarchyQueryBox.Size = hierarchyQueryBox.Size + Vector3.one * snapEpsilon; List nearbyObjects = Octave3DScene.Get().OverlapBox(hierarchyQueryBox); if (nearbyObjects.Count == 0) return SnapResult.GetWasNotSnapped(); nearbyObjects.RemoveAll(item => item.transform.IsChildOf(root.transform) || ignoreDestSnapObjects.Contains(item)); List hierarchyBoxCornerPoints = hierarchyWorldAABB.GetCornerPoints(); foreach (GameObject gameObject in nearbyObjects) { Box objectWorldBox = Box.GetInvalid(); Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh != null) objectWorldBox = gameObject.GetMeshWorldBox(); if (objectWorldBox.IsInvalid() && gameObject.HasSpriteRendererWithSprite()) objectWorldBox = gameObject.GetNonMeshWorldBox(); if (objectWorldBox.IsInvalid()) continue; List worldBoxCornerPoints = objectWorldBox.GetCornerPoints(); foreach (Vector3 hierarchyBoxPt in hierarchyBoxCornerPoints) { foreach (Vector3 objectMeshBoxPt in worldBoxCornerPoints) { float distance = (hierarchyBoxPt - objectMeshBoxPt).magnitude; if (distance < minSnapDistance) { minSnapDistance = distance; snapDestPt = objectMeshBoxPt; snapPivotPt = hierarchyBoxPt; } } } } } if (minSnapDistance < snapEpsilon) { ObjectHierarchySnap.Snap(root, snapPivotPt, snapDestPt); return new SnapResult(true, snapPivotPt, snapDestPt); } return SnapResult.GetWasNotSnapped(); } } } #endif