Пример #1
0
        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);
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        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;
            }
        }