private bool ChangePolygonColor(Brush brush, Polygon polygon) { bool anyChanged = false; Vector3 hoverPointLocal = brush.transform.InverseTransformPoint(mouseHoverPoint); Undo.RecordObject(brush, "Paint"); csgModel.UndoRecordContext("Paint"); for (int j = 0; j < polygon.Vertices.Length; j++) { float squareDistance = (polygon.Vertices[j].Position - hoverPointLocal).sqrMagnitude; if (squareDistance <= (brushRadius * brushRadius)) { float distance = Mathf.Sqrt(squareDistance); polygon.Vertices[j].Color = PaintColor(polygon.Vertices[j].Color, distance / brushRadius); anyChanged = true; } } PolygonEntry entry = csgModel.GetVisualPolygonEntry(polygon.UniqueIndex); if (entry != null) { if (entry.BuiltMesh != null) { Undo.RecordObject(entry.BuiltMesh, "Change Vertex Color"); Vector3[] meshVertices = entry.BuiltMesh.vertices; Color32[] meshColors = entry.BuiltMesh.colors32; Color32[] colors = entry.Colors; for (int vertexIndex = 0; vertexIndex < entry.Positions.Length; vertexIndex++) { float squareDistance = (meshVertices[entry.BuiltVertexOffset + vertexIndex] - mouseHoverPoint).sqrMagnitude; if (squareDistance <= (brushRadius * brushRadius)) { float distance = Mathf.Sqrt(squareDistance); colors[vertexIndex] = PaintColor(colors[vertexIndex], distance / brushRadius); meshColors[entry.BuiltVertexOffset + vertexIndex] = PaintColor(meshColors[entry.BuiltVertexOffset + vertexIndex], distance / brushRadius); anyChanged = true; } } if (anyChanged) { entry.Colors = colors; entry.BuiltMesh.colors32 = meshColors; EditorHelper.SetDirty(entry.BuiltMesh); } } } return(anyChanged); }
internal static void BuildCollision(Transform meshGroupHolder, PolygonEntry[] polygonIndex, CSGBuildSettings buildSettings, List <Mesh> collisionMeshDictionary) { collisionMeshDictionary.Clear(); if (polygonIndex.Length > 0) { Mesh mesh = new Mesh(); List <Vector3> positionsList = new List <Vector3>(); List <Vector3> normalsList = new List <Vector3>(); List <Vector2> uvList = new List <Vector2>(); List <int> trianglesList = new List <int>(); for (int i = 0; i < polygonIndex.Length; i++) { if (polygonIndex[i] != null) { int positionOffset = positionsList.Count; int triangleOffset = trianglesList.Count; PolygonEntry polygonEntry = polygonIndex[i]; if (PolygonEntry.IsValid(polygonEntry) && polygonEntry.Positions.Length > 0 && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built { if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) { FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); mesh = new Mesh(); positionsList.Clear(); normalsList.Clear(); uvList.Clear(); trianglesList.Clear(); positionOffset = 0; } positionsList.AddRange(polygonEntry.Positions); normalsList.AddRange(polygonEntry.Normals); uvList.AddRange(polygonEntry.UV); for (int j = 0; j < polygonEntry.Triangles.Length; j++) { trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); } polygonEntry.BuiltMesh = mesh; polygonEntry.BuiltVertexOffset = positionOffset; polygonEntry.BuiltTriangleOffset = triangleOffset; } } } FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); } }
public int AssignUniqueIDs(int startingIndex, bool isVisible, PolygonEntry[] oldVisualPolygonIndex, PolygonEntry[] newVisualPolygonIndex, bool hasCollision, PolygonEntry[] oldCollisionPolygonIndex, PolygonEntry[] newCollisionPolygonIndex) { int assignedCount = 0; for (int i = 0; i < polygons.Length; i++) { int previousUniqueIndex = polygons[i].UniqueIndex; int newUniqueIndex = startingIndex + i; polygons[i].UniqueIndex = newUniqueIndex; // Preserve existing geometry if this brush has remained built or is additive if (built || mode == CSGMode.Add) { if (isVisible) { if (previousUniqueIndex != -1 && oldVisualPolygonIndex.Length > previousUniqueIndex) { // Transfer mapping from old index to new PolygonEntry entry = oldVisualPolygonIndex[previousUniqueIndex]; newVisualPolygonIndex[newUniqueIndex] = entry; } } if (hasCollision) { if (previousUniqueIndex != -1 && oldCollisionPolygonIndex.Length > previousUniqueIndex) { // Transfer mapping from old index to new PolygonEntry entry = oldCollisionPolygonIndex[previousUniqueIndex]; newCollisionPolygonIndex[newUniqueIndex] = entry; } } } } assignedCount = polygons.Length; return(assignedCount); }
internal static void BuildVisual(Transform meshGroupHolder, PolygonEntry[] polygonIndex, CSGBuildSettings buildSettings, CSGBuildContext.BuildContext buildContext, MaterialMeshDictionary materialMeshDictionary) { materialMeshDictionary.Clear(); // Reset statistics buildContext.buildMetrics.TotalMeshes = 0; buildContext.buildMetrics.TotalVertices = 0; buildContext.buildMetrics.TotalTriangles = 0; Dictionary <Material, List <PolygonEntry> > polygonMaterialTable = new Dictionary <Material, List <PolygonEntry> >(); for (int i = 0; i < polygonIndex.Length; i++) { PolygonEntry entry = polygonIndex[i]; if (PolygonEntry.IsValid(entry) && entry.Positions.Length > 0 && !entry.ExcludeFromBuild) // Skip polygons that weren't built { Material material = entry.Material; if (material == null) { material = buildSettings.DefaultVisualMaterial; } if (polygonMaterialTable.ContainsKey(material)) { polygonMaterialTable[material].Add(entry); } else { polygonMaterialTable.Add(material, new List <PolygonEntry>() { entry }); } } } foreach (KeyValuePair <Material, List <PolygonEntry> > row in polygonMaterialTable) { Mesh mesh = new Mesh(); List <Vector3> positionsList = new List <Vector3>(); List <Vector3> normalsList = new List <Vector3>(); List <Vector2> uvList = new List <Vector2>(); List <Color> colorsList = new List <Color>(); List <int> trianglesList = new List <int>(); for (int i = 0; i < row.Value.Count; i++) { int positionOffset = positionsList.Count; int triangleOffset = trianglesList.Count; PolygonEntry polygonEntry = row.Value[i]; if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) { FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); mesh = new Mesh(); positionsList.Clear(); normalsList.Clear(); uvList.Clear(); colorsList.Clear(); trianglesList.Clear(); positionOffset = 0; } positionsList.AddRange(polygonEntry.Positions); normalsList.AddRange(polygonEntry.Normals); uvList.AddRange(polygonEntry.UV); colorsList.AddRange(polygonEntry.Colors); for (int j = 0; j < polygonEntry.Triangles.Length; j++) { trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); } row.Value[i].BuiltMesh = mesh; row.Value[i].BuiltVertexOffset = positionOffset; row.Value[i].BuiltTriangleOffset = triangleOffset; } FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); } }
internal static void CoreBuild(object state) { MeshGroupManager.OnFinalizeVisualMesh = onFinalizeVisualMesh; MeshGroupManager.OnFinalizeCollisionMesh = onFinalizeCollisionMesh; if (forceRebuild) { buildContext.ClearAll(); } buildStartTime = DateTime.Now; int brushesToBuild = 0; brushesBuilt = 0; BrushCache[] allBrushCaches = new BrushCache[brushes.Count]; int totalPolygons = 0; for (int i = 0; i < allBrushCaches.Length; i++) { allBrushCaches[i] = brushes[i].BrushCache; totalPolygons += allBrushCaches[i].Polygons.Length; } PolygonEntry[] oldVisualPolygonIndex = buildContext.VisualPolygonIndex; PolygonEntry[] oldCollisionPolygonIndex = buildContext.CollisionPolygonIndex; PolygonEntry[] newVisualPolygonIndex = new PolygonEntry[totalPolygons]; PolygonEntry[] newCollisionPolygonIndex = new PolygonEntry[totalPolygons]; int polygonUniqueID = 0; // Walk through each brush, assigning unique IDs to each polygon (since Unity serialisation will break // references on a recompile, reload etc) for (int i = 0; i < allBrushCaches.Length; i++) { brushes[i].AssignUniqueIDs(polygonUniqueID); // TODO: Find a way to remove this when Nova is removed polygonUniqueID += allBrushCaches[i].AssignUniqueIDs(polygonUniqueID, brushes[i].IsVisible, oldVisualPolygonIndex, newVisualPolygonIndex, brushes[i].HasCollision, oldCollisionPolygonIndex, newCollisionPolygonIndex); } buildContext.VisualPolygonIndex = newVisualPolygonIndex; buildContext.CollisionPolygonIndex = newCollisionPolygonIndex; // Create a list of builders that need to be built bool[] shouldBuildVisible = new bool[allBrushCaches.Length]; bool[] shouldBuildCollision = new bool[allBrushCaches.Length]; for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (!allBrushCaches[brushIndex].Built && brushes[brushIndex].IsVisible) { shouldBuildVisible[brushIndex] = true; brushesToBuild++; // Mark all the intersecting brushes as needing to build foreach (BrushCache brushCache in allBrushCaches[brushIndex].IntersectingVisualBrushCaches) { // TODO: This is slower than it needs to be int otherIndex = Array.IndexOf(allBrushCaches, brushCache); if (otherIndex != -1) { if (!shouldBuildVisible[otherIndex] && brushes[otherIndex].IsVisible) { shouldBuildVisible[otherIndex] = true; brushesToBuild++; if (brushes[otherIndex].Mode == CSGMode.Subtract) { foreach (BrushCache brushCache2 in allBrushCaches[otherIndex].IntersectingVisualBrushCaches) { // TODO: This is slower than it needs to be int otherIndex2 = Array.IndexOf(allBrushCaches, brushCache2); if (otherIndex2 != -1) { if (!shouldBuildVisible[otherIndex2] && brushes[otherIndex2].IsVisible) { shouldBuildVisible[otherIndex2] = true; brushesToBuild++; } } } } } } } } } for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (!allBrushCaches[brushIndex].Built && brushes[brushIndex].HasCollision) { shouldBuildCollision[brushIndex] = true; // TODO: This is slower than it needs to be foreach (BrushCache brushCache in allBrushCaches[brushIndex].IntersectingCollisionBrushCaches) { int otherIndex = Array.IndexOf(allBrushCaches, brushCache); if (otherIndex != -1) { if (!shouldBuildCollision[otherIndex] && brushes[otherIndex].HasCollision) { shouldBuildCollision[otherIndex] = true; brushesToBuild++; if (brushes[otherIndex].Mode == CSGMode.Subtract) { foreach (BrushCache brushCache2 in allBrushCaches[otherIndex].IntersectingCollisionBrushCaches) { // TODO: This is slower than it needs to be int otherIndex2 = Array.IndexOf(allBrushCaches, brushCache2); if (otherIndex2 != -1) { if (!shouldBuildCollision[otherIndex2] && brushes[otherIndex2].HasCollision) { shouldBuildCollision[otherIndex2] = true; brushesToBuild++; } } } } } } } } } for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildVisible[brushIndex] || shouldBuildCollision[brushIndex]) { allBrushCaches[brushIndex].ResetForBuild(newVisualPolygonIndex, newCollisionPolygonIndex); } } bool showProgressBar = false; if (DateTime.Now - buildStartTime > new TimeSpan(0, 0, 1)) { showProgressBar = true; if (state == null && onProgressChange != null) // Can only fire on main thread { onProgressChange(0); } } // TODO: All this should be possible to parallelise. for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildVisible[brushIndex]) { // Intersecting builders can probably be calculated at edit time BrushBuilder.Build(allBrushCaches[brushIndex], brushIndex, allBrushCaches, false); brushesBuilt++; // If we are not required to build collision (either for this brush, or at all) then we've built it! if (!shouldBuildCollision[brushIndex] || !buildSettings.GenerateCollisionMeshes) { allBrushCaches[brushIndex].SetBuilt(); } if (showProgressBar) { if (state == null && onProgressChange != null) // Can only fire on main thread { onProgressChange(brushesBuilt / (float)brushesToBuild); } } else { if (DateTime.Now - buildStartTime > new TimeSpan(0, 0, 1)) { showProgressBar = true; if (state == null && onProgressChange != null) // Can only fire on main thread { onProgressChange(brushesBuilt / (float)brushesToBuild); } } } } } for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildVisible[brushIndex]) { // Intersecting builders can probably be calculated at edit time BrushCache.ReclaimStolenVisualPolygons(allBrushCaches[brushIndex]); } } if (buildSettings.GenerateCollisionMeshes) { for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildCollision[brushIndex]) { if (allBrushCaches[brushIndex].CollisionVisualEqual) { } else { // Intersecting builders can probably be calculated at edit time BrushBuilder.Build(allBrushCaches[brushIndex], brushIndex, allBrushCaches, true); brushesBuilt++; if (showProgressBar) { if (state == null && onProgressChange != null) // Can only fire on main thread { onProgressChange(brushesBuilt / (float)brushesToBuild); } } else { if (DateTime.Now - buildStartTime > new TimeSpan(0, 0, 1)) { showProgressBar = true; if (state == null && onProgressChange != null) // Can only fire on main thread { onProgressChange(brushesBuilt / (float)brushesToBuild); } } } } allBrushCaches[brushIndex].SetBuilt(); } } for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildCollision[brushIndex]) { if (!allBrushCaches[brushIndex].CollisionVisualEqual) { // Intersecting builders can probably be calculated at edit time BrushCache.ReclaimStolenCollisionPolygons(allBrushCaches[brushIndex]); } } } } time1 = DateTime.Now; // TODO: Can parallelise the vertex/index buffer generation, built putting them into mesh needs to be main thread // Triangulate the new polygons if (brushesBuilt > 0) { //VisualDebug.ClearAll(); Dictionary <int, List <Polygon> > allGroupedPolygons = new Dictionary <int, List <Polygon> >(); for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildVisible[brushIndex]) { // Grab the polygons grouped by ID, optimizing them if requested Dictionary <int, List <Polygon> > groupedPolygons = null; if (buildSettings.OptimizeGeometry) { // Return the optimal list of polygons (this call also updates BuiltVisualPolygons) groupedPolygons = BrushCache.OptimizeVisual(allBrushCaches[brushIndex]); } else { groupedPolygons = allBrushCaches[brushIndex].GetGroupedBuiltVisualPolygons(); } // Set the visual mapping from the built polygons (used by SurfaceEditor) List <Polygon> polygons = allBrushCaches[brushIndex].BuiltVisualPolygons; buildContext.SetVisualMapping(brushes[brushIndex], polygons); // Add each set of polygons to the dictionary to be triangulated foreach (KeyValuePair <int, List <Polygon> > row in groupedPolygons) { allGroupedPolygons.Add(row.Key, row.Value); } } } bool useIndividualVertices = buildSettings.GenerateLightmapUVs; // Generate individual vertices for unwrapped geometry MeshGroupManager.TriangulateNewPolygons(useIndividualVertices, allGroupedPolygons, buildContext.VisualPolygonIndex); if (buildSettings.GenerateCollisionMeshes) { allGroupedPolygons.Clear(); for (int brushIndex = 0; brushIndex < allBrushCaches.Length; brushIndex++) { if (shouldBuildCollision[brushIndex]) { if (allBrushCaches[brushIndex].CollisionVisualEqual) { // Intersection sets equal, so just copy visual triangulation List <Polygon> builtVisualPolygons = allBrushCaches[brushIndex].BuiltVisualPolygons; for (int i = 0; i < builtVisualPolygons.Count; i++) { PolygonEntry visualPolygon = buildContext.VisualPolygonIndex[builtVisualPolygons[i].UniqueIndex]; if (visualPolygon != null) { buildContext.CollisionPolygonIndex[builtVisualPolygons[i].UniqueIndex] = visualPolygon.DeepCopy(); } } } else { // Intersection sets are different, need to triangulate separately Dictionary <int, List <Polygon> > groupedPolygons = null; if (buildSettings.OptimizeGeometry) { // Return the optimal list of polygons (this call also updates BuiltCollisionPolygons) groupedPolygons = BrushCache.OptimizeCollision(allBrushCaches[brushIndex]); } else { groupedPolygons = allBrushCaches[brushIndex].GetGroupedBuiltCollisionPolygons(); } // Add each set of polygons to the dictionary to be triangulated foreach (KeyValuePair <int, List <Polygon> > row in groupedPolygons) { allGroupedPolygons.Add(row.Key, row.Value); } } } } useIndividualVertices = false; // Never use individual vertices for collision geometry MeshGroupManager.TriangulateNewPolygons(useIndividualVertices, allGroupedPolygons, buildContext.CollisionPolygonIndex); } } // If multithreaded if (state != null) { // All done, tell the main thread to finish the build up readyForFinalize = true; } }