Пример #1
0
    public static void SetContinentsInertia(float movement, float rotation)
    {
        TectonicMotion = new Vector3[World.Cells.Count()];

        for (int i = 0; i <= World.Tectonics.Count(); i++)
        {
            TectonicPlate t = World.Tectonics[i];
            t.Movement = t.LocalMatrix.MultiplyPoint3x4(new Vector3(UnityEngine.Random.Range(-movement, movement), UnityEngine.Random.Range(-movement, movement), 0)) - t.Location;

            foreach (uint u in t.PlateCellList)
            {
                Vector3 vec = rotation * t.LocalMatrix.inverse.MultiplyPoint3x4(World.Cells[u].Location);
                TectonicMotion[u] += t.LocalMatrix.MultiplyPoint3x4(vec) - World.Cells[u].Location;
            }
        }
    }
Пример #2
0
        void TryFillPixel(MapPixel[,] map, ivec2 pos)
        {
            MapPixel p = map[pos.x, pos.y];

            var plateVotes = new Dictionary <TectonicPlate, int>();

            // Look for a neighbor pixel with a plate assigned
            foreach (ivec2 d in neighborMask)
            {
                int dx = pos.x + d.x;
                int dy = pos.y + d.y;

                if (dx >= 0 && dx < MapSize.x &&
                    dy >= 0 && dy < MapSize.y)
                {
                    TectonicPlate votedPlate = null;
                    if (map[dx, dy].Plate != null && map[dx, dy].Edges.Count == 0)
                    {
                        votedPlate = map[dx, dy].Plate;
                    }

                    if (votedPlate == null)
                    {
                        continue;
                    }

                    if (!plateVotes.ContainsKey(votedPlate))
                    {
                        plateVotes[votedPlate] = 1;
                    }
                    else
                    {
                        plateVotes[votedPlate] += 1;
                    }
                }
            }

            if (plateVotes.Count == 0)
            {
                return;
            }

            p.Plate = (from plateVote in plateVotes orderby plateVote.Value descending select plateVote.Key).First();
        }
Пример #3
0
        TectonicPlate SelectSide(ivec2 position, List <TectonicEdge> edges)
        {
            var plateVotes = new Dictionary <TectonicPlate, int>();

            foreach (var edge in edges)
            {
                float angle = EdgeAngle(position, edge.A, edge.B);

                TectonicPlate votedPlate = angle > 0 ? edge.RightPlate : edge.LeftPlate;

                if (!plateVotes.ContainsKey(votedPlate))
                {
                    plateVotes[votedPlate] = 1;
                }
                else
                {
                    plateVotes[votedPlate] += 1;
                }
            }

            return((from plateVote in plateVotes orderby plateVote.Value descending select plateVote.Key).First());
        }
Пример #4
0
    private void GenerateElevation(Dictionary <Site, float> initialSiteElevations, bool pointElevation = false)
    {
        float elevationCutoff = 0.01f;

        Queue <Site>            updateSites = new Queue <Site>();
        Queue <float>           elevations  = new Queue <float>();
        Dictionary <Site, bool> queuedSites = new Dictionary <Site, bool>();

        // Queue initial sites for processing.
        foreach (KeyValuePair <Site, float> kv in initialSiteElevations)
        {
            Site site = kv.Key;
            updateSites.Enqueue(site);
            elevations.Enqueue(initialSiteElevations[site]);
            queuedSites.Add(site, true);
        }

        // Propagate elevation.
        while (updateSites.Any())
        {
            Site          site         = updateSites.Dequeue();
            TectonicPlate sitePlate    = this.plateMetadata[site];
            float         newElevation = elevations.Dequeue();

            // Preserve any elevation that has been generated before.
            if (this.sectionMetadata[site].Elevation < newElevation)
            {
                this.sectionMetadata[site].Elevation = newElevation;
            }

            foreach (KeyValuePair <Site, Edge> kv in site.NeighborSiteEdges())
            {
                Site          neighborSite  = kv.Key;
                TectonicPlate neighborPlate = this.plateMetadata[neighborSite];

                // Don't queue any sites for processing that have already been queued.
                bool alreadyQueued = false;
                queuedSites.TryGetValue(neighborSite, out alreadyQueued);
                if (!alreadyQueued)
                {
                    // Generate perturbed elevations based on parent elevation.
                    float range = neighborPlate.Density;
                    float perturbation;
                    if (pointElevation)
                    {
                        // Constant elevation decay rate primarily for testing.
                        perturbation = 0.95f;
                    }
                    else
                    {
                        perturbation = (float)randGen.NextDouble() * range + 1.1f - range;
                    }
                    float neighborHeight = newElevation * perturbation;

                    if (newElevation > elevationCutoff)
                    {
                        updateSites.Enqueue(neighborSite);
                        elevations.Enqueue(neighborHeight);
                        queuedSites.Add(neighborSite, true);
                    }
                }
            }
        }
    }
Пример #5
0
    // Simulates tectonic uplift based on plate boundary stress conditions.
    private void CreateUplift()
    {
        Dictionary <Site, float> initialSites = new Dictionary <Site, float>();

        foreach (KeyValuePair <Edge, Boundary> kv in this.plateBoundaries)
        {
            Edge edge = kv.Key;

            if (edge.LeftSite == null || edge.RightSite == null)
            {
                continue;
            }

            Boundary      boundary   = kv.Value;
            TectonicPlate leftPlate  = this.plateMetadata[edge.LeftSite];
            TectonicPlate rightPlate = this.plateMetadata[edge.RightSite];

            float leftElevation;
            float rightElevation;
            if ((boundary.Orthogonal > boundary.Parallel) &&
                !boundary.Divergent &&
                (boundary.Stress.Magnitude > 0.1f))
            {
                float minElevation = Mathf.Max(leftPlate.Elevation, rightPlate.Elevation);
                float maxElevation = Mathf.Max(leftPlate.MaxElevation, rightPlate.MaxElevation);
                leftElevation = boundary.Orthogonal / boundary.Stress.Magnitude *
                                (maxElevation - minElevation) + minElevation;
                rightElevation = boundary.Orthogonal / boundary.Stress.Magnitude *
                                 (maxElevation - minElevation) + minElevation;
            }
            else if (((boundary.Parallel > boundary.Orthogonal) || boundary.Divergent) &&
                     (boundary.Stress.Magnitude > 0.1f))
            {
                float minElevation = Mathf.Max(leftPlate.Elevation, rightPlate.Elevation);
                float maxElevation = Mathf.Max(leftPlate.MaxElevation, rightPlate.MaxElevation);
                leftElevation = boundary.Parallel / boundary.Stress.Magnitude * 0.25f *
                                (maxElevation - minElevation) + minElevation;
                rightElevation = boundary.Parallel / boundary.Stress.Magnitude * 0.25f *
                                 (maxElevation - minElevation) + minElevation;
            }
            else
            {
                leftElevation  = (leftPlate.Elevation + rightPlate.Elevation) * 0.5f;
                rightElevation = leftElevation;
            }

            try {
                // Preserve the highest elevation (generated by the highest stress).
                if (!initialSites.ContainsKey(edge.LeftSite) || (initialSites.ContainsKey(edge.LeftSite) &&
                                                                 (leftElevation > initialSites[edge.LeftSite])))
                {
                    initialSites.Add(edge.LeftSite, leftElevation);
                }
                if (!initialSites.ContainsKey(edge.RightSite) || (initialSites.ContainsKey(edge.RightSite) &&
                                                                  (rightElevation > initialSites[edge.RightSite])))
                {
                    initialSites.Add(edge.RightSite, rightElevation);
                }
            } catch (System.ArgumentException ex) {
                // Ignore.
            }
        }

        this.GenerateElevation(initialSites);
    }
Пример #6
0
    // Generates tectonic plates by assigning sections to plates via a pseudo-simultaneous flood-fill algorithm.
    // Also generates a global list of plate boundaries.
    private void GenerateTectonicPlates(int numPlates)
    {
        Queue <Site>            updateSites = new Queue <Site>();
        Dictionary <Site, bool> queuedSites = new Dictionary <Site, bool>();
        List <Vector2f>         seeds       = this.CreateRandomPoints(numPlates);

        int numOceanPlates = 0; // Counter for number of oceanic plates generated.

        foreach (Vector2f seed in seeds)
        {
            // Select a random section to be a plate spawn point.
            Site sectionSite = this.GetClosestSection(this.SectionDiagram, seed);

            // Select a different section if the one we picked is already in the list.
            while (this.plateMetadata.ContainsKey(sectionSite))
            {
                sectionSite = this.GetClosestSection(this.SectionDiagram, this.CreateRandomPoints(1)[0]);
            }

            TectonicPlate plate = new TectonicPlate();

            // Determine whether the plate should be oceanic or continental.
            if ((float)numOceanPlates / numPlates < 0.7f)
            {
                plate.Oceanic = true;
                plate.Density = 0.4f;
                // Set elevation parameters for oceanic plates.
                plate.Elevation    = this.seafloor;
                plate.MaxElevation = this.sealevel + 0.21f;
            }
            else
            {
                plate.Oceanic = false;
                plate.Density = 0.125f;
                // Set elevation parameters for continental plates.
                plate.Elevation    = this.sealevel;
                plate.MaxElevation = 1.0f;
            }
            numOceanPlates++;

            // Create bi-directional mappings between sections and plates.
            this.plateMetadata.Add(sectionSite, plate);
            plate.Sections.Add(sectionSite);
            this.plates.Add(plate);

            // Generate a random movement vector for the plate.
            float forceX = (float)this.randGen.NextDouble();
            float forceY = (float)this.randGen.NextDouble();
            plate.Force = new Vector2f(forceX, forceY);
            plate.Force.Normalize();

            updateSites.Enqueue(sectionSite);
            queuedSites.Add(sectionSite, true);
        }

        // Assign sections to plates and build a list of boundary edges for each plate.
        while (updateSites.Any())
        {
            Site                    sectionSite  = updateSites.Dequeue();
            TectonicPlate           plateData    = this.plateMetadata[sectionSite];
            Dictionary <Site, Edge> sectionEdges = sectionSite.NeighborSiteEdges();

            foreach (Site neighborSite in sectionSite.NeighborSites())
            {
                // Don't add any sites for processing that have already been queued.
                bool alreadyQueued = false;
                queuedSites.TryGetValue(neighborSite, out alreadyQueued);
                if (!alreadyQueued)
                {
                    // Assimilate any available neighboring sections.
                    this.plateMetadata.Add(neighborSite, plateData);
                    plateData.Sections.Add(neighborSite);

                    updateSites.Enqueue(neighborSite);
                    queuedSites.Add(neighborSite, true);
                }
                else
                {
                    // If the neighbor doesn't share the same plate, the edge is a boundary.
                    if (plateData != this.plateMetadata[neighborSite])
                    {
                        // Get boundary edge.
                        Edge edge;
                        if (sectionEdges.TryGetValue(neighborSite, out edge))
                        {
                            // Add boundary edge to list for current plate.
                            try {
                                this.plateBoundaries.Add(edge, new Boundary());
                            } catch (System.ArgumentException ex) {
                                // Don't need to worry about failed attempts to add duplicates.
                            }
                        }
                    }
                }
            }
        }
    }
        private static void GenerateTectonics(Voronoi graph, int numberOfPlates, int seed)
        {
            var sites = graph.SitesIndexedByLocation.Select(s => s.Value).ToList();

            graph.Plates = new List <TectonicPlate>();

            // Always generate tectonic plates for the north and south pole
            var northPole = new TectonicPlate(-1);
            var southPole = new TectonicPlate(-2);

            foreach (var site in sites.Where(s => s.IsNorthPoleSite))
            {
                northPole.AddSite(site);
            }
            foreach (var site in sites.Where(s => s.IsSouthPoleSite))
            {
                southPole.AddSite(site);
            }

            graph.Plates.Add(northPole);
            graph.Plates.Add(southPole);

            // Randomly assign a site a tectonic number.
            // If the randomly selected plate is already assigned, retry.
            var rnger = 0;

            for (var i = 1; i <= numberOfPlates - 2; i++)
            {
                var tp        = new TectonicPlate(i);
                var siteIndex = Rng.GetRandomNumber(0, sites.Count, rnger++, seed);

                if (sites[siteIndex].TectonicPlate != null)
                {
                    i--;
                    continue;
                }

                tp.AddSite(sites[siteIndex]);
                graph.Plates.Add(tp);
            }

            // Now we've got our plates, it's time to expand them.
            // This may be a slow process, let's find out.
            Dictionary <TectonicPlate, bool> noNeighbours = new Dictionary <TectonicPlate, bool>();

            foreach (var p in graph.Plates)
            {
                noNeighbours.Add(p, false);
            }
            while (!noNeighbours.All(n => n.Value))
            {
                foreach (var plate in graph.Plates.Where(p => noNeighbours[p] == false))
                {
                    var neighbours = plate.NeighbourSites.Where(n => n.TectonicPlate == null).ToArray();
                    if (!neighbours.Any())
                    {
                        noNeighbours[plate] = true;
                        continue;
                    }

                    //var newSite = neighbours.OrderBy(n => n.Value).Last();

                    foreach (var site in neighbours)
                    {
                        plate.AddSite(site);
                    }
                }
            }

            foreach (var plate in graph.Plates)
            {
                plate.AssociatePlateToGlobe();
            }
        }
Пример #8
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);
        }