Beispiel #1
0
        float DistanceToEdgeSquared(TectonicEdge edge, ivec2 position)
        {
            vec2 v = new vec2(edge.A.x, edge.A.y);
            vec2 w = new vec2(edge.B.x, edge.B.y);

            vec2 p = new vec2(position.x, position.y);

            // Return minimum distance between line segment vw and point p
            float l2 = (v - w).LengthSqr;  // i.e. |w-v|^2 -  avoid a sqrt

            if (l2 == 0f)
            {
                return((p - v).LengthSqr);            // v == w case
            }
            // Consider the line extending the segment, parameterized as v + t (w - v).
            // We find projection of point p onto the line.
            // It falls where t = [(p-v) . (w-v)] / |w-v|^2
            float t = glm.Dot(p - v, w - v) / l2;

            if (t < 0)
            {
                return((p - v).LengthSqr);             // Beyond the 'v' end of the segment
            }
            else if (t > 1)
            {
                return((p - w).LengthSqr);      // Beyond the 'w' end of the segment
            }
            vec2 projection = v + t * (w - v);  // Projection falls on the segment

            return((p - projection).LengthSqr);
        }
Beispiel #2
0
        void drawLine(TectonicEdge edge, MapPixel[,] map)
        {
            int x0 = edge.A.x;
            int y0 = edge.A.y;
            int x1 = edge.B.x;
            int y1 = edge.B.y;
            int dx = Math.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
            int dy = -Math.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
            int err = dx + dy, e2; // error value e_xy

            if (dx > map.GetLength(0) * 4 || dy > map.GetLength(1) * 4)
            {
                //TODO: Investigate broken edge coordinates
                return;
            }

            for (; ;)
            {
                if (x0 >= 0 && x0 < MapSize.x &&
                    y0 >= 0 && y0 < MapSize.y)
                {
                    map[x0, y0].Edges.Add(edge);
                    map[x0, y0].Plate = edge.LeftPlate;
                }

                if (x0 == x1 && y0 == y1)
                {
                    break;
                }

                e2 = 2 * err;

                // horizontal step?
                if (e2 > dy)
                {
                    err += dy;
                    x0  += sx;
                }
                else if (e2 < dx)
                {
                    err += dx;
                    y0  += sy;
                }
            }
        }
Beispiel #3
0
        // Create a basic set of
        public List <TectonicPlate> GeneratePlates()
        {
            // Spawn some random cell centers within a grid.
            // Add one row and column outside of the map so no cells inside the map are border cells.
            List <TectonicPlate> plates = new List <TectonicPlate>();

            for (int left = -PlateSize; left < MapSize.x + PlateSize; left += PlateSize)
            {
                for (int bottom = -PlateSize; bottom < MapSize.y + PlateSize; bottom += PlateSize)
                {
                    int right = left + PlateSize;
                    int top   = bottom + PlateSize;
                    plates.Add(new TectonicPlate
                    {
                        Center          = new ivec2(rand.Next(left, right), rand.Next(bottom, top)),
                        AngularVelocity = (float)rand.NextDouble() * maxPlateAngluarVelocity,
                        LinearVelocity  = new vec2((float)rand.NextDouble() * maxPlateLinearVelocity, (float)rand.NextDouble() * maxPlateLinearVelocity),
                        BaseHeight      = (float)rand.NextDouble() + 1f
                    });
                }
            }

            // Compute voronoi triangulation for plate edges
            var plateVectors = new Dictionary <Vector, TectonicPlate>();

            foreach (var tectonicPlate in plates)
            {
                var center = new Vector(tectonicPlate.Center.x, tectonicPlate.Center.y);
                plateVectors[center] = tectonicPlate;
            }

            VoronoiGraph graph = Fortune.ComputeVoronoiGraph(plateVectors.Keys);

            foreach (var edge in graph.Edges)
            {
                ivec2 a = new ivec2((int)edge.VVertexA[0], (int)edge.VVertexA[1]);
                ivec2 b = new ivec2((int)edge.VVertexB[0], (int)edge.VVertexB[1]);

                // Ignore edges into infinity. We generate cells outside of the map so we have only finite edges in the mep
                if (a.x == Int32.MaxValue || a.x == Int32.MinValue ||
                    a.y == Int32.MaxValue || a.y == Int32.MinValue ||
                    b.x == Int32.MaxValue || b.x == Int32.MinValue ||
                    b.y == Int32.MaxValue || b.y == Int32.MinValue)
                {
                    continue;
                }

                a.x = Math.Min(Math.Max(-200, a.x), MapSize.x + 200);
                a.y = Math.Min(Math.Max(-200, a.y), MapSize.y + 200);
                b.x = Math.Min(Math.Max(-200, b.x), MapSize.x + 200);
                b.y = Math.Min(Math.Max(-200, b.y), MapSize.y + 200);

                // left and right cells of the edges given by the fortune voronoi implementation are incorrect, compute the correct cells again

                ivec2 middle = (a + b) / 2;

                // Find the two plate centers closest to the edge middle point
                List <TectonicPlate> neighborCells = new List <TectonicPlate>();
                neighborCells.AddRange(plates.OrderBy(p => (p.Center - middle).Length).Take(2));

                TectonicPlate left  = neighborCells[0];
                TectonicPlate right = neighborCells[1];

                // left/right correct?
                if (EdgeAngle(neighborCells[0].Center, a, b) > 0)
                {
                    right = neighborCells[0];
                    left  = neighborCells[1];
                }

                float mountainFactor = rand.NextFloat(-1f, 1f);

                var tectonicEdge = new TectonicEdge {
                    A = a, B = b, LeftPlate = left, RightPlate = right, MountainFactor = mountainFactor
                };

                left.Edges.Add(tectonicEdge);
                right.Edges.Add(tectonicEdge);
            }

            SavePlateImage(plates, "plates.svg");

            return(plates);
        }
Beispiel #4
0
        MapPixel[,] GenerateHeightMap(List <TectonicPlate> plates, float progressMin, float progressMax)
        {
            var map = new MapPixel[MapSize.x, MapSize.y];

            UpdateGenerationInfo("Initialize with random heightmap");

            for (int x = 0; x < MapSize.x; x++)
            {
                for (int y = 0; y < MapSize.y; y++)
                {
                    map[x, y]        = new MapPixel();
                    map[x, y].Height = 0.5f + 0.5f * SimplexNoise.RecursiveNoise.Noise2D(x / 200f, y / 200f, 8);
                }
            }

            var drawnEdges = new HashSet <TectonicEdge>();

            UpdateGenerationInfo("Initializing tectonic edges");

            // Initialize the edges and plate centers
            foreach (var tectonicPlate in plates)
            {
                foreach (var edge in tectonicPlate.Edges)
                {
                    if (drawnEdges.Contains(edge))
                    {
                        continue;
                    }
                    drawLine(edge, map);
                    drawnEdges.Add(edge);
                }

                int x = tectonicPlate.Center.x;
                int y = tectonicPlate.Center.y;

                if (x >= 0 && x < MapSize.x &&
                    y >= 0 && y < MapSize.y)
                {
                    map[x, y].Plate = tectonicPlate;
                }
            }

            UpdateGenerationInfo("Computing tectonic surface");

            // Flood fill from the plate centers
            bool done = false;

            for (int i = 1; !done; i++)
            {
                done = true;
                for (int x = 0; x < MapSize.x; x++)
                {
                    for (int y = 0; y < MapSize.y; y++)
                    {
                        // Found a pixel without a Plate assigned?
                        MapPixel p = map[x, y];
                        if (p.Plate == null)
                        {
                            TryFillPixel(map, new ivec2(x, y));
                            if (p.Plate != null)
                            {
                                done = false;
                            }
                        }
                    }
                }
                for (int x = MapSize.x - 1; x > 0; x--)
                {
                    for (int y = MapSize.y - 1; y > 0; y--)
                    {
                        // Found a pixel without a Plate assigned?
                        MapPixel p = map[x, y];
                        if (p.Plate == null)
                        {
                            TryFillPixel(map, new ivec2(x, y));
                            if (p.Plate != null)
                            {
                                done = false;
                            }
                        }
                    }
                }
            }
            UpdateProgress(progressMin + (progressMax - progressMin) * 0.5f);

            // Find missing plates
            UpdateGenerationInfo("Find unused tectonic plates");
            List <TectonicPlate> missingPlates = FindUnusedPlates(map, plates);

            // Set height values depending on the base heights of the plates and the
            UpdateGenerationInfo("Set base height of terrain");
            Parallel.For(0, MapSize.x, x =>
            {
                for (int y = 0; y < MapSize.y; y++)
                {
                    var p = map[x, y];

                    TectonicEdge bestEdge = null;
                    float bestDistance    = Math.Max(MapSize.x, MapSize.y);

                    float baseHeight = -1f;

                    if (p.Plate != null)
                    {
                        baseHeight = p.Plate.BaseHeight;

                        foreach (var edge in p.Plate.Edges)
                        {
                            if (!missingPlates.Contains(edge.LeftPlate) &&
                                !missingPlates.Contains(edge.RightPlate))
                            {
                                float dist = DistanceToEdgeSquared(edge, new ivec2(x, y));
                                if (dist < bestDistance)
                                {
                                    bestDistance = dist;
                                    bestEdge     = edge;
                                }
                            }
                        }
                    }

                    float tectonicHeight = 0f;

                    if (bestEdge != null)
                    {
                        tectonicHeight = bestEdge.MountainFactor * 3f * (1f / ((float)Math.Pow(1.2f + 0.02f * bestDistance, 2))) *
                                         (1f + SimplexNoise.RecursiveNoise.Noise2D(250f - x / 200f, 250f - y / 200f, 10));
                    }

                    float noise = SimplexNoise.RecursiveNoise.Noise2D(x / 100f, y / 100f, 8);

                    p.Height = baseHeight + tectonicHeight + noise;
                }
            });

            UpdateProgress(progressMax);

            return(map);
        }
Beispiel #5
0
        List <TectonicEdge> SplitTectonicEdge(TectonicEdge edge, TectonicEdge originalEdge, int depth)
        {
            var subEdges = new List <TectonicEdge>();

            vec2 a = new vec2(edge.A.x, edge.A.y);
            vec2 b = new vec2(edge.B.x, edge.B.y);

            // Randomly move on an axis perpendicular to the original voronoi edge

            // vector of original edge line
            vec2 edgeVector = new vec2(originalEdge.A.x - originalEdge.B.x, originalEdge.A.y - originalEdge.B.y);

            // vectors between left/right plate centers and original.A
            vec2 leftCenterToA  = new vec2(edge.A.x - edge.LeftPlate.Center.x, edge.A.y - edge.LeftPlate.Center.y);
            vec2 rightCenterToA = new vec2(edge.A.x - edge.RightPlate.Center.x, edge.A.y - edge.RightPlate.Center.y);

            // project centers to origVector
            float projectedCenterLeft  = (glm.Dot(edgeVector, leftCenterToA) / leftCenterToA.LengthSqr);
            float projectedCenterRight = (glm.Dot(edgeVector, rightCenterToA) / rightCenterToA.LengthSqr);

            vec2 movementAxis = new vec2(-edgeVector.y, edgeVector.x);

            movementAxis = movementAxis.Normalized;

            float shift = rand.NextFloat(0.1f, 0.9f);
            vec2  vMid  = shift * a + (1f - shift) * b;

            vMid += movementAxis * rand.NextFloat(0f, depth * depth);

            ivec2 middle = new ivec2((int)vMid.x, (int)vMid.y);

            TectonicEdge subEdge1 = new TectonicEdge
            {
                A              = edge.A,
                B              = middle,
                LeftPlate      = edge.LeftPlate,
                RightPlate     = edge.RightPlate,
                MountainFactor = edge.MountainFactor
            };

            TectonicEdge subEdge2 = new TectonicEdge
            {
                A              = middle,
                B              = edge.B,
                LeftPlate      = edge.LeftPlate,
                RightPlate     = edge.RightPlate,
                MountainFactor = edge.MountainFactor
            };

            if (depth <= 0)
            {
                subEdges.Add(subEdge1);
                subEdges.Add(subEdge2);
            }
            else
            {
                subEdges.AddRange(SplitTectonicEdge(subEdge1, originalEdge, depth - 1));
                subEdges.AddRange(SplitTectonicEdge(subEdge2, originalEdge, depth - 1));
            }

            return(subEdges);
        }