private void CreateHullMapping() { if (hullMapping == null) { // hullMapping = new Dictionary<Hull, Collider>(); hullMapping = new List <HullMapping>(); } // Remove any invalid entries from the hull mapping // null entries are garbage and can be dropped // null source hull means the hull has been deleted and this mapping is no longer relevant // missing *both* generated collider *and* target child means there's no data to point at, so might as well remove it and regen from scratch for (int i = hullMapping.Count - 1; i >= 0; i--) { HullMapping mapping = hullMapping[i]; if (mapping == null || mapping.sourceHull == null || (mapping.generatedCollider == null && mapping.targetChild == null)) { hullMapping.RemoveAt(i); } } // Check to see if any existing mappings need updating (hull.type doesn't match Collider type, or child type no longer matches) foreach (Hull hull in paintingData.hulls) { if (IsMapped(hull)) { // We already have a mapping for this, but is it still of the correct type? Collider value = FindExistingCollider(hullMapping, hull); bool isHullOk = (hull.type == HullType.ConvexHull && value is MeshCollider); bool isBoxOk = (hull.type == HullType.Box && value is BoxCollider); bool isSphereOk = (hull.type == HullType.Sphere && value is SphereCollider); bool isFaceOk = (hull.type == HullType.Face && value is MeshCollider); bool isColliderTypeOk = (isHullOk || isBoxOk || isSphereOk || isFaceOk); bool isChildTypeOk = value == null || ((hull.isChildCollider) == (value.transform.parent == this.transform)); if (isColliderTypeOk && isChildTypeOk) { // All good } else { // Mismatch - hull.type doesn't match collider type // Delete the collider and remove the mapping // This hull will then be orphaned, and a new collider added back in accordingly DestroyImmediateWithUndo(value); RemoveMapping(hull); } } } // Connect orphans // // Find hulls without a Collider // Find Colliders without hulls // Try and map the two together // First find orphans - hull, colliders or childs that aren't already mapped List <Hull> orphanedHulls = new List <Hull>(); List <Collider> orphanedColliders = new List <Collider>(); List <HullPainterChild> orphanedChilds = new List <HullPainterChild>(); foreach (Hull h in paintingData.hulls) { if (!IsMapped(h)) { orphanedHulls.Add(h); } } foreach (Collider c in FindLocal <Collider>()) { if (!IsMapped(c)) { orphanedColliders.Add(c); } } foreach (HullPainterChild c in FindLocal <HullPainterChild>()) { if (!IsMapped(c)) { orphanedChilds.Add(c); } } // Try and connect orphaned hulls with orphaned colliders for (int i = orphanedHulls.Count - 1; i >= 0; i--) { Hull h = orphanedHulls[i]; for (int j = orphanedColliders.Count - 1; j >= 0; j--) { Collider c = orphanedColliders[j]; // Find the HullPainterChild adjacent to the collider (if a child collider) HullPainterChild child = null; if (c.transform.parent == this.transform) { child = c.gameObject.GetComponent <HullPainterChild>(); } // todo needs better handling bool isMatchingChild = h.isChildCollider && c.transform.parent == this.transform; if (isMatchingChild) { BoxCollider boxCol = c as BoxCollider; SphereCollider sphereCol = c as SphereCollider; MeshCollider meshCol = c as MeshCollider; bool isMatchingBox = h.type == HullType.Box && c is BoxCollider && Approximately(h.collisionBox.center, boxCol.center) && Approximately(h.collisionBox.size, boxCol.size); bool isMatchingSphere = h.type == HullType.Sphere && c is SphereCollider && h.collisionSphere != null && Approximately(h.collisionSphere.center, sphereCol.center) && Approximately(h.collisionSphere.radius, sphereCol.radius); bool isMatchingConvexHull = h.type == HullType.ConvexHull && c is MeshCollider && meshCol.sharedMesh == h.collisionMesh; bool isMatchingFace = h.type == HullType.Face && c is MeshCollider && meshCol.sharedMesh == h.faceCollisionMesh; if (isMatchingBox || isMatchingSphere || isMatchingConvexHull || isMatchingFace) { // Found a pair, so add a mapping and remove the orphans AddMapping(h, c, child); // These are no longer orphaned, so remove them from these lists orphanedHulls.RemoveAt(i); orphanedColliders.RemoveAt(j); // Remove the no-longer orphaned child for (int k = 0; k < orphanedChilds.Count; k++) { if (orphanedChilds[k] == child) { orphanedChilds.RemoveAt(k); break; } } break; } } } } // We've tried to connect hulls to existing colliders, now try and connect hulls to existing HullPainterChilds // These will be child without a collider (as otherwise they'd have be picked up earlier) for (int i = orphanedHulls.Count - 1; i >= 0; i--) { Hull h = orphanedHulls[i]; if (!h.isChildCollider) { continue; } for (int j = orphanedChilds.Count - 1; j >= 0; j--) { HullPainterChild child = orphanedChilds[j]; HullMapping mapping = FindMapping(child); if (mapping != null && mapping.sourceHull != null) { // Found a match for hull-mapping-child // Ensure this still has a collider if (mapping.generatedCollider == null) { // Recreate the collider of the correct type with the existing hull-mapping-child RecreateChildCollider(mapping); } orphanedHulls.RemoveAt(i); orphanedChilds.RemoveAt(j); break; } } } // Create colliders for any hull mapping children without colliders foreach (HullMapping mapping in hullMapping) { if (mapping.targetChild != null && mapping.generatedCollider == null) { RecreateChildCollider(mapping); } } // Create child components for child colliders without them foreach (HullMapping mapping in hullMapping) { if (mapping.targetChild == null && mapping.generatedCollider != null && mapping.generatedCollider.transform.parent == this.transform) { // Mapping has a child collider but no HullPainterChild // Recreate the child component HullPainterChild newChild = AddComponent <HullPainterChild>(mapping.generatedCollider.gameObject); newChild.parent = this; mapping.targetChild = newChild; } } // Create colliders for any left over hulls foreach (Hull h in orphanedHulls) { if (h.type == HullType.Box) { CreateCollider <BoxCollider>(h); } else if (h.type == HullType.Sphere) { CreateCollider <SphereCollider>(h); } else if (h.type == HullType.ConvexHull) { CreateCollider <MeshCollider>(h); } else if (h.type == HullType.Face) { CreateCollider <MeshCollider>(h); } } // Delete any left over colliders // TODO: This probably isn't properly undo-aware foreach (Collider c in orphanedColliders) { if (c.gameObject == this.gameObject) { DestroyImmediateWithUndo(c); } else { // Child collider - delete collider, HullPainterChild (if any) and GameObject (if empty) GameObject go = c.gameObject; DestroyImmediateWithUndo(c); DestroyImmediateWithUndo(go.GetComponent <HullPainterChild>()); if (IsDeletable(go)) { DestroyImmediateWithUndo(go); } } } // Delete any left over hull painter childs // TODO: This probably isn't undo-aware foreach (HullPainterChild child in orphanedChilds) { if (child == null) { continue; } // Delete child, collider (if any) and GameObject (if empty) GameObject go = child.gameObject; DestroyImmediateWithUndo(child); DestroyImmediateWithUndo(go.GetComponent <Collider>()); if (IsDeletable(go)) { DestroyImmediateWithUndo(go); } } // Sanity check - all hull mappings should have a collider of the right type now // foreach (HullMapping mapping in hullMapping) // { // if (mapping.generatedCollider == null) // Debug.LogWarning("Null collider for hull: " + mapping.sourceHull.name); // } }
private void CreateHullMapping() { if (hullMapping == null) { hullMapping = new Dictionary <Hull, Collider> (); } List <Hull> keys = new List <Hull> (hullMapping.Keys); // take a copy of the keys so we can remove from hullMapping as we iterate over it foreach (Hull h in keys) { if (h == null || hullMapping[h] == null) { Debug.Log("Removing invalid entry from hull mapping"); hullMapping.Remove(h); } } // Check to see if any existing mappings need updating (hull.type doesn't match Collider type) foreach (Hull hull in paintingData.hulls) { if (hullMapping.ContainsKey(hull)) { // We already have a mapping for this, but is it still of the correct type? Collider value = hullMapping[hull]; bool isHullOk = (hull.type == HullType.ConvexHull && value is MeshCollider); bool isBoxOk = (hull.type == HullType.Box && value is BoxCollider); bool isSphereOk = (hull.type == HullType.Sphere && value is SphereCollider); bool isFaceOk = (hull.type == HullType.Face && value is MeshCollider); if (!(isHullOk || isBoxOk || isSphereOk || isFaceOk)) { // Mismatch - hull.type doesn't match collider type // Delete the collider and remove the mapping // This hull will then be orphaned, and a new collider added back in accordingly GameObject.DestroyImmediate(value); hullMapping.Remove(hull); } } } // Connect orphans // // Find hulls without a Collider // Find Colliders without hulls // Try and map the two together // First find orphans List <Hull> orphanedHulls = new List <Hull> (); List <Collider> orphanedColliders = new List <Collider> (); foreach (Hull h in paintingData.hulls) { if (!hullMapping.ContainsKey(h)) { orphanedHulls.Add(h); } } foreach (Collider c in GetComponents <Collider>()) { if (!hullMapping.ContainsValue(c)) { orphanedColliders.Add(c); } } // Try and connect orphaned hulls with orphaned colliders for (int i = orphanedHulls.Count - 1; i >= 0; i--) { Hull h = orphanedHulls[i]; for (int j = orphanedColliders.Count - 1; j >= 0; j--) { Collider c = orphanedColliders[j]; BoxCollider boxCol = c as BoxCollider; SphereCollider sphereCol = c as SphereCollider; MeshCollider meshCol = c as MeshCollider; bool isMatchingBox = h.type == HullType.Box && c is BoxCollider && Approximately(h.collisionBox.center, boxCol.center) && Approximately(h.collisionBox.size, boxCol.size); bool isMatchingSphere = h.type == HullType.Sphere && c is SphereCollider && h.collisionSphere != null && Approximately(h.collisionSphere.center, sphereCol.center) && Approximately(h.collisionSphere.radius, sphereCol.radius); bool isMatchingConvexHull = h.type == HullType.ConvexHull && c is MeshCollider && meshCol.sharedMesh == h.collisionMesh; bool isMatchingFace = h.type == HullType.Face && c is MeshCollider && meshCol.sharedMesh == h.faceCollisionMesh; if (isMatchingBox || isMatchingSphere || isMatchingConvexHull || isMatchingFace) { // Found a pair, so add a mapping and remove the orphans hullMapping.Add(h, c); // These are no longer orphaned, so remove them from these lists orphanedHulls.RemoveAt(i); orphanedColliders.RemoveAt(j); break; } } } // Create colliders for any left over hulls foreach (Hull h in orphanedHulls) { if (h.type == HullType.Box) { #if UNITY_EDITOR BoxCollider box = (BoxCollider)Undo.AddComponent(this.gameObject, typeof(BoxCollider)); #else BoxCollider box = this.gameObject.AddComponent <BoxCollider>(); #endif hullMapping.Add(h, box); } else if (h.type == HullType.Sphere) { #if UNITY_EDITOR SphereCollider sphere = (SphereCollider)Undo.AddComponent(this.gameObject, typeof(SphereCollider)); #else SphereCollider sphere = this.gameObject.AddComponent <SphereCollider>(); #endif hullMapping.Add(h, sphere); } else if (h.type == HullType.ConvexHull) { #if UNITY_EDITOR MeshCollider mesh = (MeshCollider)Undo.AddComponent(this.gameObject, typeof(MeshCollider)); #else MeshCollider mesh = this.gameObject.AddComponent <MeshCollider>(); #endif hullMapping.Add(h, mesh); } else if (h.type == HullType.Face) { #if UNITY_EDITOR MeshCollider mesh = (MeshCollider)Undo.AddComponent(this.gameObject, typeof(MeshCollider)); #else MeshCollider mesh = this.gameObject.AddComponent <MeshCollider>(); #endif hullMapping.Add(h, mesh); } } // Delete any left over colliders foreach (Collider c in orphanedColliders) { GameObject.DestroyImmediate(c); } }
private void DrawHullGUILine(int hullIndex, Hull hull, Dictionary <Collumn, GUILayoutOption> widths, Dictionary <Collumn, float> rawWidths) { HullPainter currentHullPainter = sceneManipulator.GetCurrentHullPainter(); Undo.RecordObject(currentHullPainter.paintingData, "Edit Hull"); GUILayout.BeginHorizontal(); { if (IsCollumnVisible(Collumn.Visibility)) { if (GUILayout.Button(hull.isVisible ? hullVisibleIcon : hullInvisibleIcon, EditorStyles.miniButton, GUILayout.Height(16), widths[Collumn.Visibility])) { hull.isVisible = !hull.isVisible; regenerateOverlay = true; } } if (IsCollumnVisible(Collumn.Name)) { hull.name = EditorGUILayout.TextField(hull.name, GUILayout.MinWidth(60), widths[Collumn.Name]); } if (IsCollumnVisible(Collumn.Colour)) { Color prevColour = hull.colour; hull.colour = EditorGUILayout.ColorField("", hull.colour, widths[Collumn.Colour]); if (prevColour != hull.colour) { regenerateOverlay = true; repaintSceneView = true; } } if (IsCollumnVisible(Collumn.Type)) { hull.type = (HullType)EditorGUILayout.EnumPopup(hull.type, widths[Collumn.Type]); } if (IsCollumnVisible(Collumn.Material)) { hull.material = (PhysicMaterial)EditorGUILayout.ObjectField(hull.material, typeof(PhysicMaterial), false, widths[Collumn.Material]); } if (IsCollumnVisible(Collumn.Inflate)) { float toggleSize = 12; float floatSize = rawWidths[Collumn.Inflate] - toggleSize - 4; hull.enableInflation = EditorGUILayout.Toggle(hull.enableInflation, GUILayout.Width(toggleSize)); hull.inflationAmount = EditorGUILayout.FloatField(hull.inflationAmount, GUILayout.Width(floatSize)); } if (IsCollumnVisible(Collumn.IsChild)) { if (GUILayout.Button(hull.isChildCollider ? isChildIcon : nonChildIcon, EditorStyles.miniButton, widths[Collumn.IsChild])) { hull.isChildCollider = !hull.isChildCollider; } } if (IsCollumnVisible(Collumn.Trigger)) { if (GUILayout.Button(hull.isTrigger ? triggerOnIcon : triggerOffIcon, EditorStyles.miniButton, widths[Collumn.Trigger])) { hull.isTrigger = !hull.isTrigger; } } if (IsCollumnVisible(Collumn.Paint)) { int prevHullIndex = currentHullPainter.paintingData.activeHull; bool isPainting = (currentHullPainter.paintingData.activeHull == hullIndex); int nowSelected = GUILayout.Toolbar(isPainting ? 0 : -1, new Texture[] { isPainting?paintOnIcon: paintOffIcon }, EditorStyles.miniButton, widths[Collumn.Paint]); if (nowSelected == 0 && prevHullIndex != hullIndex) { // Now painting this index! currentHullPainter.paintingData.activeHull = hullIndex; } } if (IsCollumnVisible(Collumn.Delete)) { if (GUILayout.Button(deleteIcon, EditorStyles.miniButton, widths[Collumn.Delete])) { hullToDelete = hullIndex; regenerateOverlay = true; repaintSceneView = true; } } } GUILayout.EndHorizontal(); }
private void GenerateConvexHull(Hull hull, Vector3[] meshVertices, int[] meshIndices, Mesh destMesh) { // Generate array of input points int totalFaces = hull.selectedFaces.Count; Point3d[] inputPoints = new Point3d[totalFaces * 3]; for (int i = 0; i < hull.selectedFaces.Count; i++) { int faceIndex = hull.selectedFaces[i]; Vector3 p0 = meshVertices[meshIndices[faceIndex * 3]]; Vector3 p1 = meshVertices[meshIndices[faceIndex * 3 + 1]]; Vector3 p2 = meshVertices[meshIndices[faceIndex * 3 + 2]]; inputPoints[i * 3] = new Point3d(p0.x, p0.y, p0.z); inputPoints[i * 3 + 1] = new Point3d(p1.x, p1.y, p1.z); inputPoints[i * 3 + 2] = new Point3d(p2.x, p2.y, p2.z); } // Calculate the convex hull QuickHull3D qHull = new QuickHull3D(); try { qHull.build(inputPoints); } catch (System.Exception) { Debug.LogError("Could not generate hull for " + this.name + "'s '" + hull.name + "' (input " + inputPoints.Length + " points)"); } // Get calculated hull vertices and indices Point3d[] hullVertices = qHull.getVertices(); int[][] hullFaceIndices = qHull.getFaces(); hull.numColliderFaces = hullFaceIndices.Length; Debug.Log("Calculated collider for '" + hull.name + "' has " + hullFaceIndices.Length + " faces"); if (hullFaceIndices.Length >= 256) { hull.hasColliderError = true; return; } // Convert to dest vertices Vector3[] destVertices = new Vector3[hullVertices.Length]; for (int i = 0; i < destVertices.Length; i++) { destVertices[i] = new Vector3((float)hullVertices[i].x, (float)hullVertices[i].y, (float)hullVertices[i].z); } // Convert to dest incices List <int> destIndices = new List <int>(); for (int i = 0; i < hullFaceIndices.Length; i++) { int faceVerts = hullFaceIndices[i].Length; for (int j = 1; j < faceVerts - 1; j++) { destIndices.Add(hullFaceIndices[i][0]); destIndices.Add(hullFaceIndices[i][j]); destIndices.Add(hullFaceIndices[i][j + 1]); } } int[] destIndicesArray = new int[destIndices.Count]; for (int i = 0; i < destIndices.Count; i++) { destIndicesArray[i] = destIndices[i]; } // Push to collision mesh hull.collisionMesh.vertices = destVertices; hull.collisionMesh.triangles = destIndicesArray; hull.collisionMesh.RecalculateBounds(); }
public void GenerateCollisionMesh(Hull hull, Vector3[] meshVertices, int[] meshIndices) { hull.hasColliderError = false; if (hull.type == HullType.Box) { if (hull.selectedFaces.Count > 0) { Vector3 first = meshVertices[meshIndices[hull.selectedFaces[0] * 3]]; Vector3 min = first; Vector3 max = first; for (int i = 0; i < hull.selectedFaces.Count; i++) { int faceIndex = hull.selectedFaces[i]; Vector3 p0 = meshVertices[meshIndices[faceIndex * 3]]; Vector3 p1 = meshVertices[meshIndices[faceIndex * 3 + 1]]; Vector3 p2 = meshVertices[meshIndices[faceIndex * 3 + 2]]; Inflate(p0, ref min, ref max); Inflate(p1, ref min, ref max); Inflate(p2, ref min, ref max); } hull.collisionBox.center = (min + max) * 0.5f; hull.collisionBox.size = max - min; } } else if (hull.type == HullType.Sphere) { Vector3 sphereCenter; float sphereRadius; if (CalculateBoundingSphere(hull, meshVertices, meshIndices, out sphereCenter, out sphereRadius)) { if (hull.collisionSphere == null) { hull.collisionSphere = new Sphere(); } hull.collisionSphere.center = sphereCenter; hull.collisionSphere.radius = sphereRadius; } } else if (hull.type == HullType.ConvexHull) { if (hull.collisionMesh == null) { hull.collisionMesh = new Mesh(); } hull.collisionMesh.name = hull.name; hull.collisionMesh.triangles = new int[0]; hull.collisionMesh.vertices = new Vector3[0]; GenerateConvexHull(hull, meshVertices, meshIndices, hull.collisionMesh); } else if (hull.type == HullType.Face) { if (hull.faceCollisionMesh == null) { hull.faceCollisionMesh = new Mesh(); } hull.faceCollisionMesh.name = hull.name; hull.faceCollisionMesh.triangles = new int[0]; hull.faceCollisionMesh.vertices = new Vector3[0]; GenerateFace(hull, meshVertices, meshIndices, faceThickness); } }
private void SyncOverlay(HullPainter hullPainter) { // Debug.Log ("SyncOverlay - overlayObject: " + overlayObject); if (hullPainter != null && hullPainter.paintingData != null) { int totalFaces = 0; for (int i = 0; i < hullPainter.paintingData.hulls.Count; i++) { Hull hull = hullPainter.paintingData.hulls[i]; if (hull.isVisible) { totalFaces += hull.selectedFaces.Count; } } // Debug.Log("Overlay has "+totalFaces+" faces"); Vector3[] vertices = new Vector3[totalFaces * 3]; Color[] colours = new Color[totalFaces * 3]; int[] indices = new int[totalFaces * 3]; // Rebuild vertex buffers int nextIndex = 0; for (int i = 0; i < hullPainter.paintingData.hulls.Count; i++) { Hull hull = hullPainter.paintingData.hulls[i]; if (!hull.isVisible) { continue; } for (int j = 0; j < hull.selectedFaces.Count; j++) { int faceIndex = hull.selectedFaces[j]; int i0 = faceIndex * 3; int i1 = faceIndex * 3 + 1; int i2 = faceIndex * 3 + 2; if (i0 < targetTriangles.Length && i1 < targetTriangles.Length && i2 < targetTriangles.Length) { int t0 = targetTriangles[i0]; int t1 = targetTriangles[i1]; int t2 = targetTriangles[i2]; if (t0 < targetVertices.Length && t1 < targetVertices.Length && t2 < targetVertices.Length) { Vector3 p0 = targetVertices[t0]; Vector3 p1 = targetVertices[t1]; Vector3 p2 = targetVertices[t2]; colours[nextIndex] = hull.colour; colours[nextIndex + 1] = hull.colour; colours[nextIndex + 2] = hull.colour; vertices[nextIndex] = p0; vertices[nextIndex + 1] = p1; vertices[nextIndex + 2] = p2; nextIndex += 3; } else { Debug.LogWarning("Skipping face because vertex index out of bounds"); } } else { Debug.LogWarning("Skipping face because triangle index out of bounds: " + faceIndex); // TODO: Should we advance nextIndex or not? // Maybe we should have a validate step rather than trying to solve this problem now? } } } // Generate the indices for (int i = 0; i < indices.Length; i++) { indices [i] = i; } Mesh overlayMesh = overlayFilter.sharedMesh; overlayMesh.triangles = new int[0]; overlayMesh.vertices = vertices; overlayMesh.colors = colours; overlayMesh.triangles = indices; overlayFilter.sharedMesh = overlayMesh; overlayRenderer.enabled = true; } else { // No hull painter selected, clear everything overlayFilter.sharedMesh.Clear(); overlayRenderer.enabled = false; } }
public bool DoMouseDown(PickMode initialMode) { if (currentToolSelection == ToolSelection.TrianglePainting) { if (targetMeshCollider != null) { if (currentHullPainter != null && currentHullPainter.paintingData != null) { Undo.RecordObject(currentHullPainter.paintingData, "Paint Hull"); pickMode = PickTriangle(initialMode); if (pickMode != PickMode.Undecided) { // Debug.Log ("Start drag"); Sync(); EditorUtility.SetDirty(currentHullPainter.paintingData); isSelectingFaces = true; return(true); } else { // Debug.Log ("Abandon drag"); } } else { // This can happen when unity triggers scene callbacks in an odd order and the currentHullPainter isn't set yet // Debug.LogError("SceneManipulator has no currentHullPainter!"); } } else { Debug.Log("Mouse down but no targetMeshCollider, ignoring"); } } else if (currentToolSelection == ToolSelection.Pipette) { // Raycast against the target mesh collider and see if the triangle we hit is in any current hull bool anyFound = false; Ray pickRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); int hitTriIndex = -1; if (targetMeshCollider != null && RaycastUtil.Raycast(targetMeshCollider, pickRay, out hitTriIndex, 10000.0f)) { for (int i = 0; i < currentHullPainter.paintingData.hulls.Count; i++) { Hull hull = currentHullPainter.paintingData.hulls[i]; if (hull.selectedFaces.Contains(hitTriIndex)) { // Now painting this hull! currentHullPainter.paintingData.activeHull = i; currentToolSelection = ToolSelection.TrianglePainting; anyFound = true; break; } } } if (!anyFound) { currentToolSelection = ToolSelection.TrianglePainting; currentHullPainter.paintingData.activeHull = -1; } } return(false); }
private void SyncOverlay(HullPainter hullPainter) { // Debug.Log ("SyncOverlay - overlayObject: " + overlayObject); if (hullPainter != null && hullPainter.paintingData != null) { int totalFaces = 0; for (int i = 0; i < hullPainter.paintingData.hulls.Count; i++) { totalFaces += hullPainter.paintingData.hulls[i].selectedFaces.Count; } // Debug.Log("Overlay has "+totalFaces+" faces"); Vector3[] vertices = new Vector3[totalFaces * 3]; Color[] colours = new Color[totalFaces * 3]; int[] indices = new int[totalFaces * 3]; // Rebuild vertex buffers int nextIndex = 0; for (int i = 0; i < hullPainter.paintingData.hulls.Count; i++) { Hull hull = hullPainter.paintingData.hulls[i]; for (int j = 0; j < hull.selectedFaces.Count; j++) { int faceIndex = hull.selectedFaces[j]; Vector3 p0 = targetVertices[targetTriangles[faceIndex * 3 + 0]]; Vector3 p1 = targetVertices[targetTriangles[faceIndex * 3 + 1]]; Vector3 p2 = targetVertices[targetTriangles[faceIndex * 3 + 2]]; colours[nextIndex] = hull.colour; colours[nextIndex + 1] = hull.colour; colours[nextIndex + 2] = hull.colour; vertices[nextIndex] = p0; vertices[nextIndex + 1] = p1; vertices[nextIndex + 2] = p2; nextIndex += 3; } } // Generate the indices for (int i = 0; i < indices.Length; i++) { indices [i] = i; } Mesh overlayMesh = overlayFilter.sharedMesh; overlayMesh.triangles = new int[0]; overlayMesh.vertices = vertices; overlayMesh.colors = colours; overlayMesh.triangles = indices; overlayFilter.sharedMesh = overlayMesh; overlayRenderer.enabled = true; } else { // No hull painter selected, clear everything overlayFilter.sharedMesh.Clear(); overlayRenderer.enabled = false; } }