/** * \brief Cycles through a mesh and returns a pb_IntArray[] of * triangles that point to the same point in world space. * @param _mesh The mesh to examine. * \sa pb_IntArray * \notes pbIntArray exists because Unity cannot serialize jagged arrays. * \returns A pb_IntArray[] (basically just an int[][] with some added functionality). */ public static pb_IntArray[] ExtractSharedIndices(Vector3[] v) { Dictionary <pb_IntVec3, List <int> > sorted = new Dictionary <pb_IntVec3, List <int> >(); List <int> ind; for (int i = 0; i < v.Length; i++) { if (sorted.TryGetValue(v[i], out ind)) { ind.Add(i); } else { sorted.Add(new pb_IntVec3(v[i]), new List <int>() { i }); } } pb_IntArray[] share = new pb_IntArray[sorted.Count]; int t = 0; foreach (KeyValuePair <pb_IntVec3, List <int> > kvp in sorted) { share[t++] = new pb_IntArray(kvp.Value.ToArray()); } return(share); }
public static pb_IntArray[] ToPbIntArray(this List <List <int> > val) { pb_IntArray[] arr = new pb_IntArray[val.Count]; for (int i = 0; i < arr.Length; i++) { arr[i] = (pb_IntArray)val[i].ToArray(); } return(arr); }
/** * Convert a jagged int array to a pb_IntArray. */ public static pb_IntArray[] ToPbIntArray(this int[][] val) { pb_IntArray[] arr = new pb_IntArray[val.Length]; for (int i = 0; i < arr.Length; i++) { arr[i] = (pb_IntArray)val[i]; } return(arr); }
/** * Associates all passed indices with a single shared index. Does not perfrom any additional operations * to repair triangle structure or vertex placement. */ public static int MergeSharedIndices(ref pb_IntArray[] sharedIndices, int[] indices) { if (indices.Length < 2) { return(-1); } if (sharedIndices == null) { sharedIndices = new pb_IntArray[1] { (pb_IntArray)indices }; return(0); } List <int> used = new List <int>(); List <int> newSharedIndex = new List <int>(); // Create a new int[] composed of all indices in shared selection for (int i = 0; i < indices.Length; i++) { int si = sharedIndices.IndexOf(indices[i]); if (!used.Contains(si)) { if (si > -1) { newSharedIndex.AddRange(sharedIndices[si].array); used.Add(si); } else { newSharedIndex.Add(indices[i]); } } } // Now remove the old entries int rebuiltSharedIndexLength = sharedIndices.Length - used.Count; pb_IntArray[] rebuild = new pb_IntArray[rebuiltSharedIndexLength]; int n = 0; for (int i = 0; i < sharedIndices.Length; i++) { if (!used.Contains(i)) { rebuild[n++] = sharedIndices[i]; } } sharedIndices = rebuild.Add(new pb_IntArray(newSharedIndex.ToArray())); // SetSharedIndices( rebuild.Add( new pb_IntArray(newSharedIndex.ToArray()) ) ); return(sharedIndices.Length - 1); }
public static void Rebuild() { pb_Object[] pbObjs = (pb_Object[])GameObject.FindObjectsOfType(typeof(pb_Object)); foreach(pb_Object pb in pbObjs) { pb_IntArray[] val = pb.sharedIndices; List<int> empty = new List<int>(); for(int i = 0; i < val.Length; i++) if(val[i].IsEmpty()) empty.Add(i); pb_IntArray[] trimmed = new pb_IntArray[val.Length-empty.Count]; int n = 0; for(int i = 0; i < trimmed.Length; i++) if(!empty.Contains(i)) trimmed[n++] = val[i]; pb.SetSharedIndices( trimmed ); } }
/** * Sets the internal sharedIndices cache. Also takes care of refreshing the uniqueIndices cache for you. */ public void SetSharedIndices(pb_IntArray[] si) { _sharedIndices = si; RefreshUniqueIndices(); }
public static void RemoveEmptyOrNull(ref pb_IntArray[] val) { List<pb_IntArray> valid = new List<pb_IntArray>(); foreach(pb_IntArray par in val) { if(par != null && !par.IsEmpty()) valid.Add(par); } val = valid.ToArray(); }
/** * \brief Removes the specified indices from the array, and shifts all values * down to account for removal in the vertex array. Only use when deleting * faces or vertices. For general moving around and modification of shared * index array, use #RemoveValuesAtIndex. */ public static void RemoveValuesAndShift(ref pb_IntArray[] sharedIndices, int[] remove) { // MUST BE DISTINCT remove = remove.ToDistinctArray(); // remove face indices from all shared indices caches for(int i = 0; i < sharedIndices.Length; i++) { for(int n = 0; n < remove.Length; n++) { int ind = System.Array.IndexOf(sharedIndices[i], remove[n]); if(ind > -1) sharedIndices[i].array = sharedIndices[i].array.RemoveAt(ind); } } // Remove empty or null entries caused by shifting around all them indices pb_IntArray.RemoveEmptyOrNull(ref sharedIndices); // now cycle through and shift indices for(int i = 0; i < sharedIndices.Length; i++) { for(int n = 0; n < sharedIndices[i].Length; n++) { int ind = sharedIndices[i][n]; int sub = 0; // use a count and subtract at end because indices aren't guaranteed to be in order. // ex, 9, 8, 7 would only sub 1 if we just did rm < ind; ind-- foreach(int rm in remove) { if(rm < ind) sub++; } sharedIndices[i][n] -= sub; } } }
/** * Adds a range of values to the array at index. */ public static int AddRangeAtIndex(ref pb_IntArray[] sharedIndices, int sharedIndex, int[] indices) { if(sharedIndex > -1) sharedIndices[sharedIndex].array = sharedIndices[sharedIndex].array.AddRange(indices); else sharedIndices = (pb_IntArray[])sharedIndices.Add( new pb_IntArray(indices) ); return sharedIndex > -1 ? sharedIndex : sharedIndices.Length-1; }
/** * Associates indices with a single shared index. Does not perfrom any additional operations * to repair triangle structure or vertex placement. */ public static void MergeSharedIndices(ref pb_IntArray[] sharedIndices, int a, int b) { int aIndex = sharedIndices.IndexOf(a); int oldBIndex = sharedIndices.IndexOf(b); pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, aIndex, b); int[] arr = sharedIndices[oldBIndex].array; sharedIndices[oldBIndex].array = arr.RemoveAt(System.Array.IndexOf(arr, b)); pb_IntArray.RemoveEmptyOrNull(ref sharedIndices); }
/** * Checks if an array contains a value and also compares shared indices using sharedIndices. */ public static int IndexOf(this int[] array, int val, pb_IntArray[] sharedIndices) { int indInShared = sharedIndices.IndexOf(val); if(indInShared < 0) return -1; int[] allValues = sharedIndices[indInShared]; for(int i = 0; i < array.Length; i++) if(System.Array.IndexOf(allValues, array[i]) > -1) return i; return -1; }
private void GeometryWithVerticesFacesIndices(Vector3[] v, pb_Face[] f, pb_IntArray[] s) { SetFaces(f); SetVertices(v); SetUV(new Vector2[v.Length]); SetSharedIndices(s); if(msh != null) DestroyImmediate(msh); // ToMesh builds the actual Mesh object ToMesh(); // Refresh builds out the UV, Normal, Tangents, etc. Refresh(); }
// Copy constructor public pb_IntArray(pb_IntArray intArray) { array = intArray.array; }
/** * \brief Creates an icosphere from a radius and subdivision count. * This method does not extract shared indices, so after generating * make sure to use pbVertexOps.Weldvertices() to generate them. */ public static pb_Object IcosahedronGenerator(float radius, int subdivisions) { // http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html Vector3[] v = new Vector3[ICOSAHEDRON_TRIANGLES.Length]; /** * Regular Icosahedron - 12 vertices, 20 faces. */ for(int i = 0; i < ICOSAHEDRON_TRIANGLES.Length; i+=3) { v[i+0] = ICOSAHEDRON_VERTICES[ ICOSAHEDRON_TRIANGLES[i+0] ].normalized * radius; v[i+1] = ICOSAHEDRON_VERTICES[ ICOSAHEDRON_TRIANGLES[i+1] ].normalized * radius; v[i+2] = ICOSAHEDRON_VERTICES[ ICOSAHEDRON_TRIANGLES[i+2] ].normalized * radius; } /** * Subdivide */ for(int i = 0; i < subdivisions; i++) { v = SubdivideIcosahedron(v, radius); } /** * Wind faces */ pb_Face[] f = new pb_Face[v.Length/3]; for(int i = 0; i < v.Length; i+=3) { f[i/3] = new pb_Face( new int[3] { i, i+1, i+2 } ); f[i/3].manualUV = true; } GameObject _gameObject = new GameObject(); pb_Object pb = _gameObject.AddComponent<pb_Object>(); pb.SetVertices(v); pb.SetUV(new Vector2[v.Length]); pb.SetFaces(f); pb_IntArray[] si = new pb_IntArray[v.Length]; for(int i = 0; i < si.Length; i++) si[i] = new pb_IntArray(new int[] { i }); pb.SetSharedIndices(si); pb.ToMesh(); pb.Refresh(); pb.SetName("Icosphere"); return pb; }
/** * \brief Returns a copy of the sharedIndicesUV array. */ public pb_IntArray[] GetSharedIndicesUV() { int sil = _sharedIndicesUV.Length; pb_IntArray[] sharedIndicesCopy = new pb_IntArray[sil]; for(int i = 0; i < sil; i++) { int[] arr = new int[_sharedIndicesUV[i].Length]; System.Array.Copy(_sharedIndicesUV[i].array, arr, arr.Length); sharedIndicesCopy[i] = new pb_IntArray(arr); } return sharedIndicesCopy; }
public void SetSharedIndicesUV(pb_IntArray[] si) { _sharedIndicesUV = si; }
/** * Similar to Merge vertices, expect that this method only collapses vertices within * a specified distance of one another (typically epsilon). Returns true if any action * was taken, false otherwise. Outputs indices that have been welded in the @welds var. */ public static bool WeldVertices(this pb_Object pb, int[] indices, float delta, out int[] welds) { List<int> universal = pb.sharedIndices.GetUniversalIndices(indices).ToList(); Vector3[] v = pb.vertices; HashSet<int> used = new HashSet<int>(); KDTree<int> tree = new KDTree<int>(3, 48); // dimensions (xyz), node size for(int i = 0; i < universal.Count; i++) { Vector3 vert = v[pb.sharedIndices[universal[i]][0]]; tree.AddPoint( new double[] { vert.x, vert.y, vert.z }, universal[i]); } List<List<int>> groups = new List<List<int>>(); double[] point = new double[3] { 0, 0, 0 }; int[][] si = pb.sharedIndices.ToArray(); for(int i = 0; i < universal.Count; i++) { if(used.Contains(universal[i])) { continue; } int tri = si[universal[i]][0]; point[0] = v[tri].x; point[1] = v[tri].y; point[2] = v[tri].z; NearestNeighbour<int> neighborIterator = tree.NearestNeighbors( point, 64, delta ); List<int> neighbors = new List<int>(); while(neighborIterator.MoveNext()) { if( used.Contains(neighborIterator.Current) ) continue; used.Add( neighborIterator.Current ); neighbors.Add( neighborIterator.Current ); } used.Add( universal[i] ); groups.Add( neighbors ); } pb_IntArray[] rebuilt = new pb_IntArray[groups.Count];// + remainingCount ]; welds = new int[groups.Count]; for(int i = 0; i < groups.Count; i++) { rebuilt[i] = new pb_IntArray( groups[i].SelectMany(x => pb.sharedIndices[x].array).ToArray() ); welds[i] = rebuilt[i][0]; } foreach(pb_IntArray arr in rebuilt) { Vector3 avg = pb_Math.Average(pbUtil.ValuesWithIndices(v, arr.array)); foreach(int i in arr.array) v[i] = avg; } pb.SetVertices(v); // profiler.EndSample(); pb_IntArray[] remaining = pb.sharedIndices.Where( (val, index) => !used.Contains(index) ).ToArray(); rebuilt = pbUtil.Concat(rebuilt, remaining); pb.SetSharedIndices(rebuilt); return true; }
/** * \brief Call this to ensure that the mesh is unique. Basically performs a DeepCopy and assigns back to self. */ public void MakeUnique() { pb_Face[] q = new pb_Face[_faces.Length]; for(int i = 0; i < q.Length; i++) q[i] = new pb_Face(_faces[i]); pb_IntArray[] sv = new pb_IntArray[_sharedIndices.Length]; System.Array.Copy(_sharedIndices, sv, sv.Length); SetSharedIndices(sv); SetFaces(q); Vector3[] v = new Vector3[vertexCount]; System.Array.Copy(_vertices, v, vertexCount); SetVertices(v); if(_uv != null && _uv.Length == vertexCount) { Vector2[] u = new Vector2[vertexCount]; System.Array.Copy(_uv, u, vertexCount); SetUV(u); } msh = pbUtil.DeepCopyMesh(msh); ToMesh(); Refresh(); }
/** * Convert a jagged int array to a pb_IntArray. */ public static pb_IntArray[] ToPbIntArray(this int[][] val) { pb_IntArray[] arr = new pb_IntArray[val.Length]; for(int i = 0; i < arr.Length; i++) arr[i] = (pb_IntArray)val[i]; return arr; }
/** * Associates all passed indices with a single shared index. Does not perfrom any additional operations * to repair triangle structure or vertex placement. */ public static int MergeSharedIndices(ref pb_IntArray[] sharedIndices, int[] indices) { if(indices.Length < 2) return -1; if(sharedIndices == null) { sharedIndices = new pb_IntArray[1] { (pb_IntArray)indices }; return 0; } List<int> used = new List<int>(); List<int> newSharedIndex = new List<int>(); // Create a new int[] composed of all indices in shared selection for(int i = 0; i < indices.Length; i++) { int si = sharedIndices.IndexOf(indices[i]); if(!used.Contains(si)) { if( si > -1 ) { newSharedIndex.AddRange( sharedIndices[si].array ); used.Add(si); } else { newSharedIndex.Add( indices[i] ); } } } // Now remove the old entries int rebuiltSharedIndexLength = sharedIndices.Length - used.Count; pb_IntArray[] rebuild = new pb_IntArray[rebuiltSharedIndexLength]; int n = 0; for(int i = 0; i < sharedIndices.Length; i++) { if(!used.Contains(i)) rebuild[n++] = sharedIndices[i]; } sharedIndices = rebuild.Add( new pb_IntArray(newSharedIndex.ToArray()) ); // SetSharedIndices( rebuild.Add( new pb_IntArray(newSharedIndex.ToArray()) ) ); return sharedIndices.Length-1; }
public static pb_IntArray[] ToPbIntArray(this List<List<int>> val) { pb_IntArray[] arr = new pb_IntArray[val.Count]; for(int i = 0; i < arr.Length; i++) arr[i] = (pb_IntArray)val[i].ToArray(); return arr; }
/** * Add a value to the array at index. */ public static int AddValueAtIndex(ref pb_IntArray[] sharedIndices, int sharedIndex, int value) { if(sharedIndex > -1) sharedIndices[sharedIndex].array = sharedIndices[sharedIndex].array.Add(value); else sharedIndices = (pb_IntArray[])sharedIndices.Add( new pb_IntArray(new int[]{value}) ); return sharedIndex > -1 ? sharedIndex : sharedIndices.Length-1; }
/** * \brief Cycles through a mesh and returns a pb_IntArray[] of * triangles that point to the same point in world space. * @param _mesh The mesh to examine. * \sa pb_IntArray * \notes pbIntArray exists because Unity cannot serialize jagged arrays. * \returns A pb_IntArray[] (basically just an int[][] with some added functionality). */ public static pb_IntArray[] ExtractSharedIndices(Vector3[] v) { Dictionary<pb_IntVec3, List<int>> sorted = new Dictionary<pb_IntVec3, List<int>>(); List<int> ind; for(int i = 0; i < v.Length; i++) { if( sorted.TryGetValue(v[i], out ind) ) ind.Add(i); else sorted.Add(new pb_IntVec3(v[i]), new List<int>() { i }); } pb_IntArray[] share = new pb_IntArray[sorted.Count]; int t = 0; foreach(KeyValuePair<pb_IntVec3, List<int>> kvp in sorted) share[t++] = new pb_IntArray( kvp.Value.ToArray() ); return share; }
/** * Removes all passed values from the sharedIndices jagged array. Does NOT perform any * index shifting to account for removed vertices. Use RemoveValuesAndShift * for that purpose. */ public static void RemoveValues(ref pb_IntArray[] sharedIndices, int[] remove) { // remove face indices from all shared indices caches for(int i = 0; i < sharedIndices.Length; i++) { for(int n = 0; n < remove.Length; n++) { int ind = System.Array.IndexOf(sharedIndices[i], remove[n]); if(ind > -1) sharedIndices[i].array = sharedIndices[i].array.RemoveAt(ind); } } // Remove empty or null entries caused by shifting around all them indices pb_IntArray.RemoveEmptyOrNull(ref sharedIndices); }
/** * \brief Removes the specified indices from the array, and shifts all values * down to account for removal in the vertex array. Only use when deleting * faces or vertices. For general moving around and modification of shared * index array, use #RemoveValuesAtIndex. */ public static void RemoveValuesAndShift(ref pb_IntArray[] sharedIndices, IList<int> remove) { Dictionary<int, int> lookup = sharedIndices.ToDictionary(); for(int i = 0; i < remove.Count; i++) lookup[remove[i]] = -1; sharedIndices = lookup.Where(x => x.Value > -1).ToSharedIndices(); List<int> removed_values = new List<int>(remove); removed_values.Sort(); for(int i = 0; i < sharedIndices.Length; i++) { for(int n = 0; n < sharedIndices[i].Length; n++) { int index = pbUtil.NearestIndexPriorToValue(removed_values, sharedIndices[i][n]); // add 1 because index is zero based sharedIndices[i][n] -= index+1; } } }
/** * Creates a new pb_Object instance with the provided vertices, faces, and sharedIndex information. */ public static pb_Object CreateInstanceWithElements(Vector3[] v, Vector2[] u, Color[] c, pb_Face[] f, pb_IntArray[] si, pb_IntArray[] si_uv) { GameObject _gameObject = new GameObject(); pb_Object pb = _gameObject.AddComponent<pb_Object>(); pb.SetVertices(v); pb.SetUV(u); pb.SetColors(c); pb.SetSharedIndices( si ?? pb_IntArrayUtility.ExtractSharedIndices(v) ); pb.SetSharedIndicesUV( si_uv ?? new pb_IntArray[0] {}); pb.SetFaces(f); pb.ToMesh(); pb.Refresh(); pb.GetComponent<pb_Entity>().SetEntity(EntityType.Detail); return pb; }
/** * \brief Averages shared normals with the mask of 'all' (indices contained in perimeter edge) */ private static Vector3 Norm( int tri, pb_IntArray[] shared, int[] all, Vector3[] norm ) { int sind = shared.IndexOf(tri); if(sind < 0) return Vector3.zero; int[] triGroup = shared[sind]; Vector3 n = Vector3.zero; int count = 0; for(int i = 0; i < all.Length; i++) { // this is a point in the perimeter, add it to the average if( System.Array.IndexOf(triGroup, all[i]) > -1 ) { n += norm[all[i]]; count++; } } return n / (float)count; }
/** * Creates a new pb_Object instance with the provided vertices, faces, and sharedIndex information. */ public static pb_Object CreateInstanceWithVerticesFacesSharedIndices(Vector3[] v, pb_Face[] f, pb_IntArray[] si) { GameObject _gameObject = new GameObject(); pb_Object pb = _gameObject.AddComponent<pb_Object>(); pb.GeometryWithVerticesFacesIndices(v, f, si); pb.entity.SetEntity(EntityType.Detail); return pb; }