internal static bool FinalizeBuild() { if (brushesBuilt > 0) { buildContext.WriteVisualMappings(); } // Ensure a mesh group exists bool newGroupCreated = Prepare(rootTransform); if (brushesBuilt > 0 || polygonsRemoved || newGroupCreated) { MeshGroupManager.Cleanup(meshGroupHolder); MeshGroupManager.BuildVisual(meshGroupHolder, buildContext.VisualPolygonIndex, buildSettings, buildContext, materialMeshDictionary); if (buildSettings.GenerateCollisionMeshes) { MeshGroupManager.BuildCollision(meshGroupHolder, buildContext.CollisionPolygonIndex, buildSettings, collisionMeshDictionary); } // All done DateTime time2 = DateTime.Now; buildContext.buildMetrics.BuildMetaData = (time1 - buildStartTime).TotalSeconds + " " + (time2 - time1).TotalSeconds + " " + brushesBuilt; buildContext.buildMetrics.BuildTime = (float)(DateTime.Now - buildStartTime).TotalSeconds; buildInProgress = false; return(true); } else { buildInProgress = false; return(false); } }
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; } }