#if UNITY_EDITOR using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; namespace O3DWB { public static class GameObjectExtensions { #region Extension Methods public static bool IsSceneObject(this GameObject gameObject) { var prefabAssetType = PrefabUtility.GetPrefabAssetType(gameObject); return prefabAssetType == PrefabAssetType.NotAPrefab; } public static Octave3DMesh GetOctave3DMesh(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (mesh == null) return null; return Octave3DMeshDatabase.Get().GetOctave3DMesh(mesh); } public static void SetMeshPivotPoint(this GameObject gameObject, Vector3 pivotPoint) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh == null) return; Vector3[] vertexPositions = mesh.vertices; for(int vIndex = 0; vIndex < vertexPositions.Length; ++vIndex) { vertexPositions[vIndex] = vertexPositions[vIndex] - pivotPoint; } mesh.vertices = vertexPositions; mesh.RecalculateBounds(); gameObject.transform.position = pivotPoint; } public static void SetWorldPosDontAffectChildren(this GameObject gameObject, Vector3 worldPos) { Transform objectTransform = gameObject.transform; Dictionary childToPos = new Dictionary(); for (int childIndex = 0; childIndex < objectTransform.childCount; ++childIndex ) { Transform childTransform = objectTransform.GetChild(childIndex); childToPos.Add(childTransform, childTransform.position); } objectTransform.position = worldPos; for (int childIndex = 0; childIndex < objectTransform.childCount; ++childIndex) { Transform childTransform = objectTransform.GetChild(childIndex); childTransform.position = childToPos[childTransform]; } } public static List GetAllMeshObjectsInHierarchy(this GameObject root) { var meshFilters = root.GetComponentsInChildren(); if (meshFilters.Length == 0) return new List(); var meshObjects = new List(meshFilters.Length); foreach(var meshFilter in meshFilters) { if(meshFilter.sharedMesh != null) meshObjects.Add(meshFilter.gameObject); } return meshObjects; } public static List GetOverlappedVertsInHierarchy(this GameObject root, OrientedBox overlapBox) { List allObjects = root.GetAllChildrenIncludingSelf(); if (allObjects.Count == 0) return new List(); var overlappedVerts = new List(allObjects.Count * 50); foreach (var gameObject in allObjects) { Mesh mesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (mesh != null) { Octave3DMesh octave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(mesh); if (octave3DMesh != null) overlappedVerts.AddRange(octave3DMesh.GetOverlappedWorldVerts(overlapBox, new TransformMatrix(gameObject.transform.localToWorldMatrix))); } } return overlappedVerts; } public static List GetAllOctave3DMeshesInHierarchy(this GameObject root) { List allObjects = root.GetAllChildrenIncludingSelf(); if (allObjects.Count == 0) return new List(); var allOctave3DMeshes = new List(allObjects.Count); foreach(var gameObject in allObjects) { Mesh mesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if(mesh != null) { Octave3DMesh octave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(mesh); if (octave3DMesh != null) allOctave3DMeshes.Add(octave3DMesh); } } return allOctave3DMeshes; } public static void EmbedInSurfaceByVertex(this GameObject root, Vector3 embedDirection, GameObject embedSurface) { if (!root.DoesHierarchyContainMesh()) return; OrientedBox worldOOBB = root.GetHierarchyWorldOrientedBox(); if (worldOOBB.IsValid()) { Vector3 worldOOBBScaledSize = worldOOBB.ScaledSize; embedDirection.Normalize(); BoxFace boxFace = worldOOBB.GetBoxFaceMostAlignedWithNormal(embedDirection); Vector3 faceCenter = worldOOBB.GetBoxFaceCenter(boxFace); Plane facePlane = worldOOBB.GetBoxFacePlane(boxFace); int sizeComponentIndex = -1; if (boxFace == BoxFace.Back || boxFace == BoxFace.Front) sizeComponentIndex = 2; else if (boxFace == BoxFace.Left || boxFace == BoxFace.Right) sizeComponentIndex = 0; else if (boxFace == BoxFace.Top || boxFace == BoxFace.Bottom) sizeComponentIndex = 1; const float vCollectSizeScale = 0.01f; const float vCollectEps = 1e-2f; Vector3 vCollectBoxSize = worldOOBB.ScaledSize; vCollectBoxSize[sizeComponentIndex] *= vCollectSizeScale; vCollectBoxSize[sizeComponentIndex] += vCollectEps; vCollectBoxSize[(sizeComponentIndex + 1) % 3] += vCollectEps; vCollectBoxSize[(sizeComponentIndex + 2) % 3] += vCollectEps; OrientedBox vCollectBox = new OrientedBox(worldOOBB); vCollectBox.Scale = Vector3.one; vCollectBox.ModelSpaceSize = vCollectBoxSize; vCollectBox.Center = (faceCenter + facePlane.normal * vCollectEps) - facePlane.normal * vCollectBoxSize[sizeComponentIndex] * 0.5f; List overlappedVerts = root.GetOverlappedVertsInHierarchy(vCollectBox); if (overlappedVerts.Count == 0) return; GameObjectRayHit surfaceRayHit; float maxDistSq = float.MinValue; bool needToMove = false; Vector3 cameraPos = SceneViewCamera.Camera.transform.position; foreach (var vertex in overlappedVerts) { // Ignore if already below the surface Ray ray = new Ray(vertex, -embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out surfaceRayHit)) continue; // Ignore if already on the surface Vector3 fromCamToPt = vertex - cameraPos; ray = new Ray(cameraPos, Vector3.Normalize(fromCamToPt)); if (embedSurface.RaycastTerrainOrMesh(ray, out surfaceRayHit)) { if (Mathf.Abs(fromCamToPt.magnitude - (surfaceRayHit.HitPoint - cameraPos).magnitude) < 1e-3f) continue; } ray = new Ray(vertex, embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out surfaceRayHit)) { float distSq = (vertex - surfaceRayHit.HitPoint).sqrMagnitude; if (distSq > maxDistSq) { maxDistSq = distSq; needToMove = true; } } } if (needToMove) root.transform.position += embedDirection * Mathf.Sqrt(maxDistSq); } } public static void EmbedAllObjectsInSurface(List gameObjects, Vector3 embedDirection, GameObject embedSurface) { foreach (GameObject gameObject in gameObjects) { if (gameObject == embedSurface) continue; gameObject.EmbedInSurface(embedDirection, embedSurface); } } public static void EmbedObjectBoxInSurface(OrientedBox objectBox, Vector3 embedDirection, GameObject embedSurface) { GameObjectRayHit rayHit; if (objectBox.IsValid()) { BoxFace boxFace = objectBox.GetBoxFaceMostAlignedWithNormal(embedDirection); //Vector3 faceCenter = objectBox.GetBoxFaceCenter(boxFace); /*Ray ray = new Ray(faceCenter, embedDirection); if (embedSurface.RaycastTerrainOrMeshReverseIfFail(ray, out rayHit)) { Vector3 moveVector = rayHit.HitPoint - faceCenter; objectBox.Center += moveVector; }*/ float maxDistSq = float.MinValue; float minDistSq = float.MaxValue; bool hitAlongPrjDir = false; bool hitAlongReversePrjDir = false; List boxFaceCorners = objectBox.GetBoxFaceCornerPoints(boxFace); foreach (var corner in boxFaceCorners) { Ray ray = new Ray(corner, -embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out rayHit)) { float distSq = (corner - rayHit.HitPoint).sqrMagnitude; if (distSq < minDistSq) { minDistSq = distSq; hitAlongReversePrjDir = true; } continue; } ray = new Ray(corner, embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out rayHit)) { float distSq = (corner - rayHit.HitPoint).sqrMagnitude; if (distSq > maxDistSq) { maxDistSq = distSq; hitAlongPrjDir = true; } } } if (hitAlongPrjDir) objectBox.Center += embedDirection * Mathf.Sqrt(maxDistSq); else if (hitAlongReversePrjDir) objectBox.Center -= embedDirection * Mathf.Sqrt(minDistSq); } } public static void EmbedInSurface(this GameObject gameObject, Vector3 embedDirection, GameObject embedSurface) { GameObjectRayHit rayHit; OrientedBox worldOOBB = gameObject.GetHierarchyWorldOrientedBox(); if (worldOOBB.IsValid()) { BoxFace boxFace = worldOOBB.GetBoxFaceMostAlignedWithNormal(embedDirection); //Vector3 faceCenter = worldOOBB.GetBoxFaceCenter(boxFace); /*Ray ray = new Ray(faceCenter, embedDirection); if (embedSurface.RaycastTerrainOrMeshReverseIfFail(ray, out rayHit)) { Vector3 moveVector = rayHit.HitPoint - faceCenter; GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position += moveVector; worldOOBB.Center += moveVector; }*/ float maxDistSq = float.MinValue; float minDistSq = float.MaxValue; bool hitAlongPrjDir = false; bool hitAlongReversePrjDir = false; List boxFaceCorners = worldOOBB.GetBoxFaceCornerPoints(boxFace); foreach (var corner in boxFaceCorners) { Ray ray = new Ray(corner, -embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out rayHit)) { float distSq = (corner - rayHit.HitPoint).sqrMagnitude; if (distSq < minDistSq) { minDistSq = distSq; hitAlongReversePrjDir = true; } continue; } ray = new Ray(corner, embedDirection); if (embedSurface.RaycastTerrainOrMesh(ray, out rayHit)) { float distSq = (corner - rayHit.HitPoint).sqrMagnitude; if (distSq > maxDistSq) { maxDistSq = distSq; hitAlongPrjDir = true; } } } if (hitAlongPrjDir) { GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position += embedDirection * Mathf.Sqrt(maxDistSq); } else if (hitAlongReversePrjDir) { GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position -= embedDirection * Mathf.Sqrt(minDistSq); } } } public static void ProjectAllObjectsOnPlane(List gameObjects, Vector3 projectionDir, Plane plane) { List parents = GameObjectExtensions.GetParents(gameObjects); foreach (GameObject gameObject in parents) { gameObject.ProjectOnPlane(projectionDir, plane); } } public static void ProjectOnPlane(this GameObject gameObject, Vector3 projectionDir, Plane plane) { OrientedBox worldOOBB = gameObject.GetHierarchyWorldOrientedBox(); if (worldOOBB.IsValid()) { BoxFace boxFace = worldOOBB.GetBoxFaceMostAlignedWithNormal(projectionDir); //Vector3 faceCenter = worldOOBB.GetBoxFaceCenter(boxFace); /*Ray ray = new Ray(faceCenter, embedDirection); if (embedSurface.RaycastTerrainOrMeshReverseIfFail(ray, out rayHit)) { Vector3 moveVector = rayHit.HitPoint - faceCenter; GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position += moveVector; worldOOBB.Center += moveVector; }*/ float maxDistSq = float.MinValue; float minDistSq = float.MaxValue; bool hitAlongPrjDir = false; bool hitAlongReversePrjDir = false; List boxFaceCorners = worldOOBB.GetBoxFaceCornerPoints(boxFace); foreach (var corner in boxFaceCorners) { Ray ray = new Ray(corner, -projectionDir); float t; if (plane.Raycast(ray, out t)) { float distSq = (corner - ray.GetPoint(t)).sqrMagnitude; if (distSq < minDistSq) { minDistSq = distSq; hitAlongReversePrjDir = true; } continue; } ray = new Ray(corner, projectionDir); if (plane.Raycast(ray, out t)) { float distSq = (corner - ray.GetPoint(t)).sqrMagnitude; if (distSq > maxDistSq) { maxDistSq = distSq; hitAlongPrjDir = true; } } } if (hitAlongPrjDir) { GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position += projectionDir * Mathf.Sqrt(maxDistSq); } else if (hitAlongReversePrjDir) { GameObjectExtensions.RecordObjectTransformsForUndo(new List { gameObject }); gameObject.transform.position -= projectionDir * Mathf.Sqrt(minDistSq); } } } public static List GetLocalAxes(this GameObject gameObject) { Transform gameObjectTransform = gameObject.transform; return new List { gameObjectTransform.right, gameObjectTransform.up, gameObjectTransform.forward }; } public static void ApplyTransformDataToRootChildren(this GameObject root, GameObject sourceRoot) { List destChildren = root.GetAllChildren(); List sourceChildren = sourceRoot.GetAllChildren(); if (destChildren.Count != sourceChildren.Count) return; // Note: Assumes there will always be a 1 to 1 mapping between the children in the hierarchy. for(int childIndex = 0; childIndex < destChildren.Count; ++childIndex) { destChildren[childIndex].transform.InheritWorldTransformFrom(sourceChildren[childIndex].transform); } } public static GameObject CloneAsWorkingObject(this GameObject gameObject, Transform parentTransform, bool allowUndoRedo = true) { Transform gameObjectTransform = gameObject.transform; GameObject clone = GameObject.Instantiate(gameObject, gameObjectTransform.position, gameObjectTransform.rotation) as GameObject; if (allowUndoRedo) UndoEx.RegisterCreatedGameObject(clone); clone.transform.localScale = gameObjectTransform.transform.lossyScale; clone.name = gameObject.name; clone.isStatic = gameObject.isStatic; clone.layer = gameObject.layer; clone.transform.parent = parentTransform; Octave3DScene.Get().RegisterObjectHierarchy(clone); SceneViewCamera.Instance.SetObjectVisibilityDirty(); return clone; } public static GameObject GetSourcePrefab(this GameObject gameObject) { return PrefabUtility.GetCorrespondingObjectFromSource(gameObject) as GameObject; } public static GameObject GetSourcePrefabRoot(this GameObject gameObject) { GameObject sourcePrefab = gameObject.GetSourcePrefab(); if (sourcePrefab == null) return null; Transform sourcePrefabTransform = sourcePrefab.transform; if (sourcePrefabTransform.root != null) sourcePrefab = sourcePrefabTransform.root.gameObject; return sourcePrefab; } public static bool RaycastBox(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; OrientedBox objectWorldOrientedBox = gameObject.GetWorldOrientedBox(); OrientedBoxRayHit objectBoxRayHit; if (objectWorldOrientedBox.Raycast(ray, out objectBoxRayHit)) objectRayHit = new GameObjectRayHit(ray, gameObject, objectBoxRayHit, null, null, null); return objectRayHit != null; } public static bool RaycastSprite(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; SpriteRenderer spriteRenderer = gameObject.GetComponent(); if (spriteRenderer == null) return false; OrientedBox objectWorldOrientedBox = gameObject.GetWorldOrientedBox(); OrientedBoxRayHit objectBoxRayHit; if(objectWorldOrientedBox.Raycast(ray, out objectBoxRayHit)) { SpriteRayHit spriteHit = new SpriteRayHit(ray, objectBoxRayHit.HitEnter, spriteRenderer, objectBoxRayHit.HitPoint, objectBoxRayHit.HitNormal); objectRayHit = new GameObjectRayHit(ray, gameObject, null, null, null, spriteHit); } return objectRayHit != null; } public static bool RaycastMesh(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; Mesh objectMesh = gameObject.GetMeshFromFilterOrSkinnedMeshRenderer(); if (objectMesh == null) return false; Octave3DMesh octaveMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(objectMesh); if (octaveMesh == null) return false; MeshRayHit meshRayHit = octaveMesh.Raycast(ray, gameObject.transform.GetWorldMatrix()); if (meshRayHit == null) return false; objectRayHit = new GameObjectRayHit(ray, gameObject, null, meshRayHit, null, null); return true; } public static bool RaycastMeshReverseIfFail(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { if (gameObject.RaycastMesh(ray, out objectRayHit)) return true; return gameObject.RaycastMesh(new Ray(ray.origin, -ray.direction), out objectRayHit); } public static bool RaycastTerrain(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; if (!gameObject.HasTerrain()) return false; TerrainCollider terrainCollider = gameObject.GetComponent(); if (terrainCollider == null) return false; RaycastHit raycastHit; if (terrainCollider.Raycast(ray, out raycastHit, float.MaxValue)) { TerrainRayHit terrainRayHit = new TerrainRayHit(ray, raycastHit); objectRayHit = new GameObjectRayHit(ray, gameObject, null, null, terrainRayHit, null); } return objectRayHit != null; } public static bool RaycastTerrainReverseIfFail(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { if (gameObject.RaycastTerrain(ray, out objectRayHit)) return true; return gameObject.RaycastTerrain(new Ray(ray.origin, -ray.direction), out objectRayHit); } public static bool RaycastTerrainOrMeshReverseIfFail(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; if(gameObject.HasTerrain() && gameObject.GetComponent() != null) return gameObject.RaycastTerrainReverseIfFail(ray, out objectRayHit); else if (gameObject.HasMesh()) return gameObject.RaycastMeshReverseIfFail(ray, out objectRayHit); return false; } public static bool RaycastTerrainOrMesh(this GameObject gameObject, Ray ray, out GameObjectRayHit objectRayHit) { objectRayHit = null; if (gameObject.HasTerrain() && gameObject.GetComponent() != null) return gameObject.RaycastTerrain(ray, out objectRayHit); else if (gameObject.HasMesh()) return gameObject.RaycastMesh(ray, out objectRayHit); return false; } public static void SetHierarchyStatic(this GameObject hierarchyRoot, bool isStatic) { List allChildren = hierarchyRoot.GetAllChildrenIncludingSelf(); foreach(GameObject child in allChildren) { child.isStatic = isStatic; } } public static void RotateHierarchyBoxAroundPoint(this GameObject root, float rotationInDegrees, Vector3 rotationAxis, Vector3 pivotPoint) { // OLD CODE: Was not taking pivot point into account. /*OrientedBox hierarchyWorldOrientedBox = root.GetHierarchyWorldOrientedBox(); Transform rootTransform = root.transform; Vector3 fromCenterToPosition = rootTransform.position - hierarchyWorldOrientedBox.Center; Quaternion oldRotation = rootTransform.rotation; rotationAxis.Normalize(); rootTransform.Rotate(rotationAxis, rotationInDegrees, Space.World); fromCenterToPosition = oldRotation.GetRelativeRotation(rootTransform.rotation) * fromCenterToPosition; rootTransform.position = hierarchyWorldOrientedBox.Center + fromCenterToPosition;*/ OrientedBox hierarchyWorldOrientedBox = root.GetHierarchyWorldOrientedBox(); Transform rootTransform = root.transform; rotationAxis.Normalize(); Quaternion rotation = Quaternion.AngleAxis(rotationInDegrees, rotationAxis); Vector3 fromPivotToCenter = rotation * (hierarchyWorldOrientedBox.Center - pivotPoint); Vector3 newCenter = pivotPoint + fromPivotToCenter; Vector3 fromCenterToPosition = rotation * (rootTransform.position - hierarchyWorldOrientedBox.Center); rootTransform.rotation = rotation * rootTransform.rotation; rootTransform.position = newCenter + fromCenterToPosition; } public static void SetHierarchyWorldRotationAndPreserveHierarchyCenter(this GameObject root, Quaternion rotation) { OrientedBox hierarchyWorldOrientedBox = root.GetHierarchyWorldOrientedBox(); Transform rootTransform = root.transform; Vector3 fromCenterToPosition = rootTransform.position - hierarchyWorldOrientedBox.Center; Quaternion oldRotation = rootTransform.rotation; rootTransform.rotation = rotation; fromCenterToPosition = oldRotation.GetRelativeRotation(rootTransform.rotation) * fromCenterToPosition; rootTransform.position = hierarchyWorldOrientedBox.Center + fromCenterToPosition; } public static void SetHierarchyWorldScaleByPivotPoint(this GameObject root, float scale, Vector3 pivotPoint) { root.SetHierarchyWorldScaleByPivotPoint(new Vector3(scale, scale, scale), pivotPoint); } public static void SetHierarchyWorldScaleByPivotPoint(this GameObject root, Vector3 scale, Vector3 pivotPoint) { Transform rootTransform = root.transform; Vector3 fromPivotToPosition = rootTransform.position - pivotPoint; Vector3 oldScale = rootTransform.lossyScale; root.SetWorldScale(scale); Vector3 invOldScaleVector = new Vector3(1.0f / oldScale.x, 1.0f / oldScale.y, 1.0f / oldScale.z); Vector3 relativeScale = Vector3.Scale(scale, invOldScaleVector); fromPivotToPosition = Vector3.Scale(relativeScale, fromPivotToPosition); rootTransform.position = pivotPoint + fromPivotToPosition; } public static void PlaceHierarchyOnPlane(this GameObject root, Plane placementPlane) { OrientedBox hierarchyWorldOrientedBox = root.GetHierarchyWorldOrientedBox(); List boxPts = hierarchyWorldOrientedBox.GetCornerPoints(); int ptIndex = placementPlane.GetIndexOfFurthestPointBehind(boxPts); if (ptIndex < 0) ptIndex = placementPlane.GetIndexOfClosestPointInFrontOrOnPlane(boxPts); if(ptIndex >= 0) { float distToPt = placementPlane.GetDistanceToPoint(boxPts[ptIndex]); root.transform.position -= placementPlane.normal * distToPt; } } public static void PlaceObjectBoxOnPlane(OrientedBox oobb, Plane plane) { List boxPts = oobb.GetCornerPoints(); int ptIndex = plane.GetIndexOfFurthestPointBehind(boxPts); if (ptIndex < 0) ptIndex = plane.GetIndexOfClosestPointInFrontOrOnPlane(boxPts); if (ptIndex >= 0) { float distToPt = plane.GetDistanceToPoint(boxPts[ptIndex]); oobb.Center -= plane.normal * distToPt; } } public static void SetSelectedHierarchyWireframeHidden(this GameObject hierarchyRoot, bool isWireframeHidden) { Renderer renderer = hierarchyRoot.GetComponent(); if (renderer != null) { #if UNITY_2017_1_OR_NEWER EditorUtility.SetSelectedRenderState(renderer, isWireframeHidden ? EditorSelectedRenderState.Hidden : EditorSelectedRenderState.Highlight); #else EditorUtility.SetSelectedWireframeHidden(renderer, isWireframeHidden); #endif } Transform gameObjectTransform = hierarchyRoot.transform; for (int childIndex = 0; childIndex < gameObjectTransform.childCount; ++childIndex) { SetSelectedHierarchyWireframeHidden(gameObjectTransform.GetChild(childIndex).gameObject, isWireframeHidden); } } public static bool DoesHierarchyContainMesh(this GameObject root) { List allChildrenIncludingSelf = root.GetAllChildrenIncludingSelf(); foreach(GameObject gameObject in allChildrenIncludingSelf) { if (gameObject.HasMesh()) return true; } return false; } public static List GetHierarchyObjectsWithMesh(this GameObject hierarchyRoot) { List allChildrenIncludingSelf = hierarchyRoot.GetAllChildrenIncludingSelf(); allChildrenIncludingSelf.RemoveAll(item => !item.HasMesh()); return allChildrenIncludingSelf; } public static List GetHierarchyObjectsWithSprites(this GameObject hierarchyRoot) { List allChildrenIncludingSelf = hierarchyRoot.GetAllChildrenIncludingSelf(); allChildrenIncludingSelf.RemoveAll(item => !item.HasSpriteRendererWithSprite()); return allChildrenIncludingSelf; } public static bool HasMesh(this GameObject gameObject) { return gameObject.HasMeshFilterWithValidMesh() || gameObject.HasSkinnedMeshRendererWithValidMesh(); } public static bool HasMeshFilterWithValidMesh(this GameObject gameObject) { MeshFilter meshFilter = gameObject.GetComponent(); return meshFilter != null && meshFilter.sharedMesh != null; } public static bool HasSkinnedMeshRendererWithValidMesh(this GameObject gameObject) { SkinnedMeshRenderer skinnedMeshRenderer = gameObject.GetComponent(); return skinnedMeshRenderer != null && skinnedMeshRenderer.sharedMesh != null; } public static bool HasTerrain(this GameObject gameObject) { return gameObject.GetComponent() != null; } public static bool HasLight(this GameObject gameObject) { return gameObject.GetComponent() != null; } public static bool HasParticleSystem(this GameObject gameObject) { return gameObject.GetComponent() != null; } public static bool HasSpriteRenderer(this GameObject gameObject) { return gameObject.GetComponent() != null; } public static bool HasSpriteRendererWithSprite(this GameObject gameObject) { SpriteRenderer spriteRenderer = gameObject.GetComponent(); if (spriteRenderer == null) return false; return spriteRenderer.sprite != null; } public static void AttachChildren(this GameObject gameObject, List children, bool allowUndoRedo) { if(allowUndoRedo) { Transform objectTransform = gameObject.transform; UndoEx.RecordForToolAction(objectTransform); foreach (GameObject child in children) { Transform childTransform = child.transform; UndoEx.RecordForToolAction(childTransform); childTransform.parent = objectTransform; } } else { Transform objectTransform = gameObject.transform; foreach (GameObject child in children) { child.transform.parent = objectTransform; } } } public static List GetAllChildrenIncludingSelf(this GameObject gameObject) { var finalObjectList = new List { gameObject }; List allChildren = gameObject.GetAllChildren(); if (allChildren.Count != 0) finalObjectList.AddRange(allChildren); return finalObjectList; } public static List GetAllChildren(this GameObject gameObject) { Transform objectTransform = gameObject.transform; Transform[] allChildTransforms = gameObject.GetComponentsInChildren(true); var allChildren = new List(); foreach(Transform childTransform in allChildTransforms) { if (objectTransform != childTransform) allChildren.Add(childTransform.gameObject); } return allChildren; } public static List GetImmediateChildren(this GameObject gameObject) { Transform objectTransform = gameObject.transform; List immediateChildTransforms = objectTransform.GetImmediateChildTransforms(); if (immediateChildTransforms.Count != 0) { List immediateChildren = new List(immediateChildTransforms.Count); foreach (Transform childTransform in immediateChildTransforms) { immediateChildren.Add(childTransform.gameObject); } return immediateChildren; } else return new List(); } public static void MoveImmediateChildrenUpOneLevel(this GameObject gameObject, bool allowUndoRedo) { Transform objectTransform = gameObject.transform; Transform objectParentTransform = objectTransform.parent; List immediateChildTransforms = objectTransform.GetImmediateChildTransforms(); if(allowUndoRedo) { foreach (Transform childTransform in immediateChildTransforms) { UndoEx.SetTransformParent(childTransform, objectParentTransform); } } else { foreach (Transform childTransform in immediateChildTransforms) { childTransform.parent = objectParentTransform; } } } public static GameObject GetParentWhichIsChildOf(this GameObject gameObject, GameObject targetParent) { // Store needed data for easy access Transform targetParentTransform = targetParent.transform; Transform currentTransform = gameObject.transform; // Keep moving up the hierarchy until we encounter the parent whose parent is 'targetParent' while (currentTransform != null && currentTransform.parent != targetParentTransform) { // Move up currentTransform = currentTransform.parent; } // If the current transform is not null, it means we found the parent which is a child of 'targetParent'. // Otherwise, it means that either 'gameObject' doesn't have any parents or its top parent is not a child // of 'targetParent'. return currentTransform != null ? currentTransform.gameObject : null; } public static GameObject GetParentWhichIsChildOf(this GameObject gameObject, List possibleParentTypes) { Transform currentTransform = gameObject.transform; while (currentTransform != null && currentTransform.parent != null) { GameObject currentParentObject = currentTransform.parent.gameObject; if (currentParentObject.HasComponentsOfAnyType(possibleParentTypes)) return currentTransform.gameObject; currentTransform = currentTransform.parent; } return null; } public static bool HasComponentsOfAnyType(this GameObject gameObject, List possibleTypes) { foreach (var type in possibleTypes) { if (gameObject.GetComponents(type).Length != 0) return true; } return false; } public static void SetWorldScale(this GameObject gameObject, float worldScale) { gameObject.SetWorldScale(new Vector3(worldScale, worldScale, worldScale)); } public static void SetWorldScale(this GameObject gameObject, Vector3 worldScale) { Transform objectTransform = gameObject.transform; Transform objectParent = objectTransform.parent; objectTransform.parent = null; objectTransform.localScale = worldScale; float minScale = 1e-4f; if (Mathf.Abs(objectTransform.localScale.x) < minScale) objectTransform.localScale = new Vector3(minScale, objectTransform.localScale.y, objectTransform.localScale.z); if (Mathf.Abs(objectTransform.localScale.y) < minScale) objectTransform.localScale = new Vector3(objectTransform.localScale.x, minScale, objectTransform.localScale.z); if (Mathf.Abs(objectTransform.localScale.z) < minScale) objectTransform.localScale = new Vector3(objectTransform.localScale.x, objectTransform.localScale.y, minScale); objectTransform.parent = objectParent; } public static Rect GetScreenRectangle(this GameObject gameObject, Camera camera) { Box worldBox = gameObject.GetWorldBox(); if (worldBox.IsValid()) return worldBox.GetScreenRectangle(camera); return new Rect(); } public static OrientedBox GetHierarchyWorldOrientedBox(this GameObject hierarchyRoot) { OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyModelSpaceOrientedBox(); hierarchyWorldOrientedBox.Transform(hierarchyRoot.transform); return hierarchyWorldOrientedBox; } public static OrientedBox GetHierarchyModelSpaceOrientedBox(this GameObject hierarchyRoot) { Box hierarchyModelSpaceBox = hierarchyRoot.GetHierarchyModelSpaceBox(); return new OrientedBox(hierarchyModelSpaceBox); } public static Box GetHierarchyWorldBox(this GameObject hierarchyRoot) { Box hierarchyWorldBox = hierarchyRoot.GetHierarchyModelSpaceBox(); hierarchyWorldBox = hierarchyWorldBox.Transform(hierarchyRoot.transform.GetWorldMatrix()); return hierarchyWorldBox; } public static Box GetHierarchyModelSpaceBox(this GameObject hierarchyRoot) { Transform rootTransform = hierarchyRoot.transform; Transform[] allChildTransforms = hierarchyRoot.GetComponentsInChildren(); bool hierarchyContainsMesh = hierarchyRoot.DoesHierarchyContainMesh(); Box hierarchyModelSpaceBox = hierarchyRoot.GetModelSpaceBox(); foreach (Transform childTransform in allChildTransforms) { GameObject childObject = childTransform.gameObject; bool objectHasMesh = childObject.HasMesh(); if(objectHasMesh) { Renderer renderer = childObject.GetRenderer(); if (renderer == null || !renderer.enabled) continue; } // If the hierarchy contains mesh objects, we will only take mesh objects into account if (!objectHasMesh && hierarchyContainsMesh) continue; if (childObject != hierarchyRoot) { /*Octave3DMesh octave3DMesh = Octave3DMeshDatabase.Get().GetOctave3DMesh(childObject.GetMeshFromFilterOrSkinnedMeshRenderer()); if(octave3DMesh != null) { TransformMatrix rootRelativeTransformMatrix = childTransform.GetRelativeTransformMatrix(rootTransform); rootRelativeTransformMatrix.Scale = rootRelativeTransformMatrix.Scale.GetVectorWithPositiveComponents(); List vertexPositions = new List(octave3DMesh.VertexPositions); vertexPositions = Vector3Extensions.GetTransformedPoints(vertexPositions, rootRelativeTransformMatrix.ToMatrix4x4x); Box vertexBox = Vector3Extensions.GetPointCloudBox(vertexPositions); if (hierarchyModelSpaceBox.IsValid()) hierarchyModelSpaceBox.Encapsulate(vertexBox); else hierarchyModelSpaceBox = new Box(vertexBox); } else*/ { Box childModelSpaceBox = childObject.GetModelSpaceBox(); if (childModelSpaceBox.IsValid()) { // Note: Negative scale values are a pain to work with, so we will set the scale to a positive value. // Negative scale causes problems inside 'Box.Transform' because it modifies the translation // in an undesirable manner. However, is it a good idea to ignore negative scale ???!!! TransformMatrix rootRelativeTransformMatrix = childTransform.GetRelativeTransformMatrix(rootTransform); rootRelativeTransformMatrix.Scale = rootRelativeTransformMatrix.Scale.GetVectorWithPositiveComponents(); childModelSpaceBox = childModelSpaceBox.Transform(rootRelativeTransformMatrix); if (hierarchyModelSpaceBox.IsValid()) hierarchyModelSpaceBox.Encapsulate(childModelSpaceBox); else hierarchyModelSpaceBox = new Box(childModelSpaceBox); } } } } return hierarchyModelSpaceBox; } /*public static OrientedBox GetHierarchyModelSpaceOrientedBox(this GameObject hierarchyRoot) { Transform rootTransform = hierarchyRoot.transform; Transform[] allChildTransforms = hierarchyRoot.GetComponentsInChildren(); bool hierarchyContainsMesh = hierarchyRoot.DoesHierarchyContainMesh(); OrientedBox hierarchyModelSpaceBox = hierarchyRoot.GetModelSpaceOrientedBox(); foreach (Transform childTransform in allChildTransforms) { GameObject childObject = childTransform.gameObject; // If the hierarchy contains mesh objects, we will only take mesh objects into account if (!childObject.HasMesh() && hierarchyContainsMesh) continue; if (childObject != hierarchyRoot) { OrientedBox childModelSpaceBox = childObject.GetModelSpaceOrientedBox(); if (childModelSpaceBox.IsValid()) { // Note: Negative scale values are a pain to work with, so we will set the scale to a positive value. // Negative scale causes problems inside 'Box.Transform' because it modifies the translation // in an undesirable manner. However, is it a good idea to ignore negative scale ???!!! TransformMatrix rootRelativeTransformMatrix = childTransform.GetRelativeTransformMatrix(rootTransform); rootRelativeTransformMatrix.Scale = rootRelativeTransformMatrix.Scale.GetVectorWithPositiveComponents(); childModelSpaceBox.Transform(rootRelativeTransformMatrix); if (hierarchyModelSpaceBox.IsValid()) hierarchyModelSpaceBox.Encapsulate(childModelSpaceBox); else hierarchyModelSpaceBox = new OrientedBox(childModelSpaceBox); } } } return hierarchyModelSpaceBox; }*/ public static OrientedBox GetWorldOrientedBox(this GameObject gameObject) { OrientedBox worldOrientedBox = gameObject.GetMeshWorldOrientedBox(); if (worldOrientedBox.IsValid()) return worldOrientedBox; return gameObject.GetNonMeshWorldOrientedBox(); } public static Box GetWorldBox(this GameObject gameObject) { Box worldBox = gameObject.GetMeshWorldBox(); if (worldBox.IsValid()) return worldBox; return gameObject.GetNonMeshWorldBox(); } public static OrientedBox GetModelSpaceOrientedBox(this GameObject gameObject) { OrientedBox modelSpaceOrientedBox = gameObject.GetMeshModelSpaceOrientedBox(); if (modelSpaceOrientedBox.IsValid()) return modelSpaceOrientedBox; return gameObject.GetNonMeshModelSpaceOrientedBox(); } public static Box GetModelSpaceBox(this GameObject gameObject) { Box modelSpaceBox = gameObject.GetMeshModelSpaceBox(); if (modelSpaceBox.IsValid()) return modelSpaceBox; return gameObject.GetNonMeshModelSpaceBox(); } public static OrientedBox GetMeshWorldOrientedBox(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh != null) return new OrientedBox(new Box(mesh.bounds), gameObject.transform); mesh = gameObject.GetMeshFromSkinnedMeshRenderer(); if (mesh != null) return new OrientedBox(new Box(gameObject.GetComponent().localBounds), gameObject.transform); return OrientedBox.GetInvalid(); } public static Box GetMeshWorldBox(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh != null) return new Box(mesh.bounds).Transform(gameObject.transform.GetWorldMatrix()); mesh = gameObject.GetMeshFromSkinnedMeshRenderer(); if (mesh != null) return new Box(gameObject.GetComponent().localBounds).Transform(gameObject.transform.GetWorldMatrix()); return Box.GetInvalid(); } public static OrientedBox GetNonMeshWorldOrientedBox(this GameObject gameObject) { SpriteRenderer spriteRenderer = gameObject.GetComponent(); if(spriteRenderer != null) { return new OrientedBox(spriteRenderer.GetModelSpaceBox(), gameObject.transform); } else { OrientedBox modelSpaceOrientedBox = gameObject.GetNonMeshModelSpaceOrientedBox(); if (!modelSpaceOrientedBox.IsValid()) return modelSpaceOrientedBox; OrientedBox worldOrientedBox = new OrientedBox(modelSpaceOrientedBox); Transform objectTransform = gameObject.transform; worldOrientedBox.Center = objectTransform.position; worldOrientedBox.Rotation = objectTransform.rotation; worldOrientedBox.Scale = objectTransform.lossyScale; return worldOrientedBox; } } public static Box GetNonMeshWorldBox(this GameObject gameObject) { Box modelSpaceBox = gameObject.GetNonMeshModelSpaceBox(); if (!modelSpaceBox.IsValid()) return modelSpaceBox; Box worldBox = modelSpaceBox.Transform(gameObject.transform.GetWorldMatrix()); return worldBox; } public static OrientedBox GetMeshModelSpaceOrientedBox(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh != null) return new OrientedBox(new Box(mesh.bounds), Quaternion.identity); mesh = gameObject.GetMeshFromSkinnedMeshRenderer(); if (mesh != null) return new OrientedBox(new Box(gameObject.GetComponent().sharedMesh.bounds), Quaternion.identity); return OrientedBox.GetInvalid(); } public static Box GetMeshModelSpaceBox(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh != null) return new Box(mesh.bounds); mesh = gameObject.GetMeshFromSkinnedMeshRenderer(); if (mesh != null) return new Box(gameObject.GetComponent().sharedMesh.bounds); return Box.GetInvalid(); } public static OrientedBox GetNonMeshModelSpaceOrientedBox(this GameObject gameObject) { SpriteRenderer spriteRenderer = gameObject.GetComponent(); if (spriteRenderer != null && spriteRenderer.sprite != null) return new OrientedBox(spriteRenderer.GetModelSpaceBox()); if (!gameObject.HasLight() && !gameObject.HasParticleSystem()) return OrientedBox.GetInvalid(); return new OrientedBox(gameObject.GetNonMeshModelSpaceBox()); } public static Box GetNonMeshModelSpaceBox(this GameObject gameObject) { SpriteRenderer spriteRenderer = gameObject.GetComponent(); if (spriteRenderer != null && spriteRenderer.sprite != null) return spriteRenderer.GetModelSpaceBox(); if (!gameObject.HasLight() && !gameObject.HasParticleSystem()) return Box.GetInvalid(); return new Box(Vector3.zero, Octave3DScene.VolumeSizeForNonMeshObjects); } public static Renderer GetRenderer(this GameObject gameObject) { return gameObject.GetComponent(); } public static bool IsSprite(this GameObject gameObject) { if (gameObject.HasMesh()) return false; SpriteRenderer spriteRenderer = gameObject.GetComponent(); return spriteRenderer != null && spriteRenderer.sprite != null; } public static Mesh GetMeshFromFilterOrSkinnedMeshRenderer(this GameObject gameObject) { Mesh mesh = gameObject.GetMeshFromMeshFilter(); if (mesh == null) mesh = gameObject.GetMeshFromSkinnedMeshRenderer(); return mesh; } public static Mesh GetMeshFromMeshFilter(this GameObject gameObject) { MeshFilter meshFilter = gameObject.GetComponent(); if (meshFilter != null && meshFilter.sharedMesh != null) return meshFilter.sharedMesh; return null; } public static Mesh GetMeshFromSkinnedMeshRenderer(this GameObject gameObject) { SkinnedMeshRenderer skinnedMeshRenderer = gameObject.GetComponent(); if (skinnedMeshRenderer != null && skinnedMeshRenderer.sharedMesh != null) return skinnedMeshRenderer.sharedMesh; return null; } #endregion #region Utilities public static List GetParents(IEnumerable gameObjects) { List topParents = new List(); foreach (GameObject gameObject in gameObjects) { bool foundParentForThisObject = false; Transform gameObjectTransform = gameObject.transform; foreach (GameObject potentialParent in gameObjects) { if (gameObject != potentialParent && gameObjectTransform.IsChildOf(potentialParent.transform)) { foundParentForThisObject = true; break; } } if (!foundParentForThisObject) topParents.Add(gameObject); } return topParents; } public static List GetAllObjectsInHierarchyCollection(List hierarchyRoots) { var allGameObjects = new List(hierarchyRoots.Count); foreach(GameObject root in hierarchyRoots) { allGameObjects.AddRange(root.GetAllChildrenIncludingSelf()); } return allGameObjects; } public static void RecordObjectTransformsForUndo(IEnumerable gameObjects) { List objectTransforms = GetObjectTransforms(gameObjects); UndoEx.RecordForToolAction(objectTransforms); } public static List GetObjectTransforms(IEnumerable gameObjects) { var objectTransforms = new List(); foreach(GameObject gameObject in gameObjects) { objectTransforms.Add(gameObject.transform); } return objectTransforms; } public static List GetHierarchyWorldOrientedBoxes(List hierarchyRoots) { if (hierarchyRoots.Count == 0) return new List(); var orientedBoxes = new List(hierarchyRoots.Count); foreach (GameObject selectedObject in hierarchyRoots) { OrientedBox orientedBox = selectedObject.GetHierarchyWorldOrientedBox(); if (orientedBox.IsValid()) orientedBoxes.Add(orientedBox); } return orientedBoxes; } public static void SetSelectedHierarchyWireframeHidden(List hierarchyRoots, bool isWireframeHidden) { foreach(GameObject root in hierarchyRoots) { root.SetSelectedHierarchyWireframeHidden(isWireframeHidden); } } public static void SetHierarchyLayer(this GameObject root, int layer, bool allowUndoRedo) { var allObjects = root.GetAllChildrenIncludingSelf(); AssignGameObjectsToLayer(allObjects, layer, allowUndoRedo); } public static void AssignGameObjectsToLayer(List gameObjects, int objectLayer, bool allowUndoRedo) { if(allowUndoRedo) { foreach (GameObject gameObject in gameObjects) { UndoEx.RecordForToolAction(gameObject); gameObject.layer = objectLayer; } } else { foreach (GameObject gameObject in gameObjects) { gameObject.layer = objectLayer; } } } public static List GetObjectsWithMesh(List gameObjects) { if (gameObjects.Count == 0) return new List(); var objectsWithMesh = new List(gameObjects.Count); foreach(var gameObject in gameObjects) { if (gameObject.HasMesh()) objectsWithMesh.Add(gameObject); } return objectsWithMesh; } #endregion } } #endif