/*========================================================================= Library: iMSTK-Unity Copyright (c) Kitware, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =========================================================================*/ using UnityEngine; using System; using System.Collections.Generic; namespace ImstkUnity { public static class Utility { /// /// Tries to get a component, if it doesn't exists, it creates one /// /// /// /// public static T GetComponentOrCreate(this GameObject gameObj) where T : Component { T comp = gameObj.GetComponent(); if (comp == null) return gameObj.AddComponent(); else return comp; } /// /// Tries to get a component, if it doesn't exists, throws exception /// /// /// /// /// public static T GetComponentFatal(this GameObject gameObj) where T : Component { T comp = gameObj.GetComponent(); if (comp == null) throw new MissingComponentException(); return comp; } /// /// Creates a tetrahedral mesh in the shape of a cube /// /// Mesh coordinates will be in world space with regards to the center parameter /// extents of the cube /// subdivisions in each axis /// center of the cube /// public static ImstkMesh GetTetGridMesh(Vector3 size, Vector3Int dim, Vector3 center) { // Make a tetrahedral cube ImstkMesh tetMesh = ScriptableObject.CreateInstance(); tetMesh.name = "GridTetMesh"; tetMesh.geomType = GeometryType.TetrahedralMesh; Vector3[] vertices = new Vector3[dim[0] * dim[1] * dim[2]]; Vector3 div = dim - new Vector3Int(1, 1, 1); Vector3 dx = Vector3.Scale(size, div.Invert()); for (int z = 0; z < dim[2]; z++) { for (int y = 0; y < dim[1]; y++) { for (int x = 0; x < dim[0]; x++) { vertices[x + dim[0] * (y + dim[1] * z)] = Vector3.Scale(new Vector3Int(x, y, z), dx) - size * 0.5f + center; } } } tetMesh.SetVertices(vertices); List indices = new List(); for (int z = 0; z < dim[2] - 1; z++) { for (int y = 0; y < dim[1] - 1; y++) { for (int x = 0; x < dim[0] - 1; x++) { int[] cubeIndices = { x + dim[0] * (y + dim[1] * z), (x + 1) + dim[0] * (y + dim[1] * z), (x + 1) + dim[0] * (y + dim[1] * (z + 1)), x + dim[0] * (y + dim[1] * (z + 1)), x + dim[0] * ((y + 1) + dim[1] * z), (x + 1) + dim[0] * ((y + 1) + dim[1] * z), (x + 1) + dim[0] * ((y + 1) + dim[1] * (z + 1)), x + dim[0] * ((y + 1) + dim[1] * (z + 1)) }; // Alternate the pattern so the edges line up on the sides of each voxel bool a = Convert.ToBoolean(z % 2); bool b = Convert.ToBoolean(x % 2); bool c = Convert.ToBoolean(y % 2); if ((a ^ b) ^ c) { indices.InsertRange(indices.Count, new int[] { cubeIndices[0], cubeIndices[7], cubeIndices[5], cubeIndices[4] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[3], cubeIndices[7], cubeIndices[2], cubeIndices[0] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[2], cubeIndices[7], cubeIndices[5], cubeIndices[0] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[1], cubeIndices[2], cubeIndices[0], cubeIndices[5] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[2], cubeIndices[6], cubeIndices[7], cubeIndices[5] }); } else { indices.InsertRange(indices.Count, new int[] { cubeIndices[3], cubeIndices[7], cubeIndices[6], cubeIndices[4] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[1], cubeIndices[3], cubeIndices[6], cubeIndices[4] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[3], cubeIndices[6], cubeIndices[2], cubeIndices[1] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[1], cubeIndices[6], cubeIndices[5], cubeIndices[4] }); indices.InsertRange(indices.Count, new int[] { cubeIndices[0], cubeIndices[3], cubeIndices[1], cubeIndices[4] }); } } } } tetMesh.SetIndices(indices.ToArray()); // Ensure correct windings //for (int i = 0; i < indices.Count; i++) //{ // if (tetVolume(vertices[indices[i][0]], vertices[indices[i][1]], vertices[indices[i][2]], vertices[indices[i][3]]) < 0.0) // { // std::swap(indices[i][0], indices[i][2]); // } //} return tetMesh; } public static ImstkMesh GetTetCubeMesh() { // Make a tetrahedral cube ImstkMesh cubeTetMesh = ScriptableObject.CreateInstance(); cubeTetMesh.name = "CubeTetMesh"; cubeTetMesh.geomType = GeometryType.TetrahedralMesh; Vector3[] vertices = { new Vector3(-0.5f, 0.5f, -0.5f), new Vector3(0.5f, 0.5f, -0.5f), new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0.5f, 0.5f, 0.5f), new Vector3(-0.5f, -0.5f, -0.5f), new Vector3(0.5f, -0.5f, -0.5f), new Vector3(-0.5f, -0.5f, 0.5f), new Vector3(0.5f, -0.5f, 0.5f) }; cubeTetMesh.SetVertices(vertices); int[] indices = { 4, 1, 2, 7, 4, 5, 1, 7, 4, 1, 0, 2, 1, 7, 3, 2, 6, 4, 2, 7 }; cubeTetMesh.SetIndices(indices); return cubeTetMesh; } public static ImstkMesh GetXYPlane(int numXDivisions = 0, int numYDivisions = 0) { int numCellsX = numXDivisions + 1; int numCellsY = numYDivisions + 1; int numCells = numCellsX * numCellsY; int numPointsX = numXDivisions + 2; int numPointsY = numYDivisions + 2; int numPoints = numPointsX * numPointsY; Vector2 spacing = new Vector2(1.0f / numCellsX, 1.0f / numCellsY); Vector3[] vertices = new Vector3[numPoints]; Vector2[] texCoords = new Vector2[numPoints]; int[] indices = new int[numCells * 6]; ImstkMesh planeMesh = ScriptableObject.CreateInstance(); planeMesh.name = "PlaneMesh"; planeMesh.geomType = GeometryType.SurfaceMesh; int iter = 0; for (int j = 0; j < numPointsY; j++) { for (int i = 0; i < numPointsX; i++) { texCoords[iter] = new Vector2(i, j) * spacing; Vector2 pos = texCoords[iter] - new Vector2(0.5f, 0.5f); vertices[iter++] = new Vector3(pos.x, pos.y, 0.0f); } } iter = 0; for (int j = 0; j < numCellsY; j++) { for (int i = 0; i < numCellsX; i++) { int index1 = j * numPointsX + i; int index2 = index1 + numPointsX; int index3 = index1 + 1; int index4 = index2 + 1; if ((i % 2 == 0) ^ (j % 2 == 0)) { indices[iter++] = index1; indices[iter++] = index2; indices[iter++] = index3; indices[iter++] = index4; indices[iter++] = index3; indices[iter++] = index2; } else { indices[iter++] = index2; indices[iter++] = index4; indices[iter++] = index1; indices[iter++] = index4; indices[iter++] = index3; indices[iter++] = index1; } } } planeMesh.SetVertices(vertices); planeMesh.SetTexCoords(texCoords); planeMesh.SetIndices(indices); return planeMesh; } /// /// Returns a compound name for a component made up of the name of /// the component itself and the names of all the parent objects /// /// Behavior that we need the name for /// A long name for the component public static string GetFullName(MonoBehaviour behavior) { var gameObject = behavior.gameObject; var stack = new System.Collections.Generic.Stack(); stack.Push(behavior.GetType().Name); stack.Push(gameObject.name); Transform parent = gameObject.transform.parent; while (parent != null) { stack.Push(parent.gameObject.name); parent = parent.transform.parent; } var newName = stack.Pop(); while (stack.Count != 0) { newName = newName + "/" + stack.Pop(); } return newName; } } }