262 lines
No EOL
8.1 KiB
C#
262 lines
No EOL
8.1 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
//using MessagePack;
|
|
using UnityEngine;
|
|
using NaughtyAttributes;
|
|
using JM.LinqFaster;
|
|
using Unity.Mathematics;
|
|
using static Unity.Mathematics.math;
|
|
using static Unity.Mathematics.noise;
|
|
using static NoiseFbm;
|
|
|
|
[CreateAssetMenu(menuName = "Aetheria/Galaxy")]
|
|
public class Galaxy : ScriptableObject
|
|
{
|
|
public GalaxyMapData MapData;
|
|
}
|
|
|
|
[Serializable]
|
|
public class GalaxyMapData
|
|
{
|
|
public int Arms = 4;
|
|
public float Twist = 10;
|
|
public float TwistPower = 2;
|
|
|
|
[HideInInspector]
|
|
public GalaxyMapLayerData StarDensity;
|
|
[HideInInspector]
|
|
public Dictionary<string,GalaxyMapLayerData> ResourceDensities = new Dictionary<string, GalaxyMapLayerData>();
|
|
//[HideInInspector]
|
|
public List<StarData> Stars = new List<StarData>();
|
|
}
|
|
|
|
public static class StarListExtensions
|
|
{
|
|
class DijkstraStar
|
|
{
|
|
public float Cost;
|
|
public DijkstraStar Parent;
|
|
public StarData Star;
|
|
}
|
|
|
|
public static List<StarData> FindPath(this List<StarData> stars, StarData source, StarData target, bool bestFirst = false)
|
|
{
|
|
SortedList<float,DijkstraStar> members = new SortedList<float,DijkstraStar>{{0,new DijkstraStar{Star = source}}};
|
|
List<DijkstraStar> searched = new List<DijkstraStar>();
|
|
while (true)
|
|
{
|
|
var s = members.FirstOrDefault(m => !searched.Contains(m.Value)).Value; // Lowest cost unsearched node
|
|
if (s == null) return null; // No vertices left unsearched
|
|
if (s.Star == target) // We found the path
|
|
{
|
|
Stack<DijkstraStar> path = new Stack<DijkstraStar>(); // Since we start at the end, use a LIFO collection
|
|
path.Push(s);
|
|
while(path.Peek().Parent!=null) // Keep pushing until we reach the start, which has no parent
|
|
path.Push(path.Peek().Parent);
|
|
return path.Select(dv => dv.Star).ToList();
|
|
}
|
|
// foreach (var dijkstraStar in s.Star.Links.Select(i => stars[i]) // All adjacent stars
|
|
// .Where(n => members.All(m => m.Value.Star != n)) // That are not members
|
|
// .Select(n => new DijkstraStar {Parent = s, Star = n, Cost = s.Cost + (s.Star.Position - n.Position).magnitude}))
|
|
// For each adjacent star (filter already visited stars unless heuristic is in use)
|
|
foreach (var dijkstraStar in s.Star.Links.WhereSelectF(i => !bestFirst || members.All(m => m.Value.Star != stars[i]),
|
|
// Cost is parent cost plus distance
|
|
i => new DijkstraStar {Parent = s, Star = stars[i], Cost = s.Cost + (s.Star.Position - stars[i].Position).magnitude}))
|
|
// Add new member to list, sorted by cost plus optional heuristic
|
|
members.Add(bestFirst ? dijkstraStar.Cost + (dijkstraStar.Star.Position - target.Position).magnitude : dijkstraStar.Cost, dijkstraStar);
|
|
searched.Add(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class StarData
|
|
{
|
|
public Vector2 Position;
|
|
public List<int> Links = new List<int>();
|
|
}
|
|
|
|
public static class NoiseFbm
|
|
{
|
|
public static float fBm(float2 p, int octaves, float frequency, float offset, float amplitude, float lacunarity, float gain)
|
|
{
|
|
float freq = frequency, amp = .5f;
|
|
float sum = 0;
|
|
for(int i = 0; i < octaves; i++)
|
|
{
|
|
sum += snoise(p * freq) * amp;
|
|
freq *= lacunarity;
|
|
amp *= gain;
|
|
}
|
|
return (sum + offset)*amplitude;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class GalaxyMapLayerData
|
|
{
|
|
public float CoreBoost = 1.05f;
|
|
public float CoreBoostOffset = .1f;
|
|
public float CoreBoostPower = 2.25f;
|
|
public float SpokeScale = 1;
|
|
public float SpokeOffset = 0;
|
|
public float EdgeReduction = 3;
|
|
public float NoiseOffset = 0;
|
|
public float NoiseAmplitude = 1.5f;
|
|
public float NoiseGain = .7f;
|
|
public float NoiseLacunarity = 2;
|
|
public int NoiseOctaves = 7;
|
|
public float NoiseFrequency = 1;
|
|
public float NoisePosition = 1337;
|
|
|
|
public float Evaluate(float2 uv, GalaxyMapData galaxy)
|
|
{
|
|
float2 offset = -float2(.5f, .5f)+uv;
|
|
float circle = (.5f-length(offset))*2;
|
|
float angle = pow(length(offset)*2,galaxy.TwistPower) * galaxy.Twist;
|
|
float2 twist = float2(offset.x*cos(angle) - offset.y*sin(angle), offset.x*sin(angle) + offset.y*cos(angle));
|
|
float atan = atan2(twist.y,twist.x);
|
|
float spokes = (sin(atan*galaxy.Arms) + SpokeOffset) * SpokeScale;
|
|
float noise = fBm(uv + float2(NoisePosition), NoiseOctaves, NoiseFrequency, NoiseOffset, NoiseAmplitude, NoiseLacunarity, NoiseGain);
|
|
float shape = lerp(spokes - EdgeReduction * length(offset), 1, pow(circle + CoreBoostOffset, CoreBoostPower) * CoreBoost);
|
|
float gal = max(shape - noise * saturate(circle), 0);
|
|
|
|
return gal;
|
|
}
|
|
}
|
|
|
|
public static class Vector2Extension {
|
|
|
|
public static Vector2 Rotate(this Vector2 v, float degrees) {
|
|
float sin = Mathf.Sin(degrees * Mathf.Deg2Rad);
|
|
float cos = Mathf.Cos(degrees * Mathf.Deg2Rad);
|
|
|
|
float tx = v.x;
|
|
float ty = v.y;
|
|
v.x = (cos * tx) - (sin * ty);
|
|
v.y = (sin * tx) + (cos * ty);
|
|
return v;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class NameData
|
|
{
|
|
public Dictionary<string, Dictionary<string, List<string>>> Names = new Dictionary<string, Dictionary<string, List<string>>>();
|
|
|
|
public void Add(string dataString)
|
|
{
|
|
//Debug.Log("Adding " + dataString);
|
|
var all = dataString.Split(',');
|
|
var name = all[0].Split(" -.".ToCharArray(),StringSplitOptions.RemoveEmptyEntries).OrderByDescending(s=>s.Length).First();
|
|
var region = all[1].Split('/');
|
|
var continent = region[0];
|
|
var capital = region[1];
|
|
if(!Names.ContainsKey(continent))
|
|
Names[continent] = new Dictionary<string, List<string>>();
|
|
if(!Names[continent].ContainsKey(capital))
|
|
Names[continent][capital] = new List<string>();
|
|
Names[continent][capital].Add(name);
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class ContinentData
|
|
{
|
|
public string Name;
|
|
public List<RegionData> Regions;
|
|
}
|
|
|
|
[Serializable]
|
|
public class RegionData
|
|
{
|
|
public string Name;
|
|
public List<string> Towns;
|
|
}
|
|
|
|
public class VoronoiLink
|
|
{
|
|
public Vertex2 point1;
|
|
public Vertex2 point2;
|
|
|
|
public float Length { get { return (point1.ToVector2() - point2.ToVector2()).magnitude; } }
|
|
|
|
public VoronoiLink(Vertex2 p1, Vertex2 p2)
|
|
{
|
|
point1 = p1;
|
|
point2 = p2;
|
|
}
|
|
|
|
public bool ContainsPoint(Vertex2 p)
|
|
{
|
|
return point1 == p || point2 == p;
|
|
}
|
|
}
|
|
|
|
public static class VoronoiLinkExtensions
|
|
{
|
|
class DijkstraVertex
|
|
{
|
|
public double Cost;
|
|
public double Heuristic;
|
|
public DijkstraVertex Parent;
|
|
public Vertex2 Vertex;
|
|
}
|
|
|
|
public static bool ContainsLine(this IEnumerable<VoronoiLink> data, VoronoiLink l)
|
|
{
|
|
return
|
|
data.Any(
|
|
line =>
|
|
(line.point1 == l.point1 && line.point2 == l.point2) ||
|
|
(line.point1 == l.point2 && line.point2 == l.point1));
|
|
}
|
|
|
|
public static IEnumerable<Vertex2> FindPath(this IEnumerable<VoronoiLink> data, Vertex2 source, Vertex2 target, bool bestFirst = true)
|
|
{
|
|
List<DijkstraVertex> members = new List<DijkstraVertex>{new DijkstraVertex{Vertex = source}};
|
|
List<Vertex2> searched = new List<Vertex2>();
|
|
while (true)
|
|
{
|
|
var v = members.Where(m=>!searched.Contains(m.Vertex)).OrderBy(m=>bestFirst?m.Heuristic:m.Cost).FirstOrDefault();
|
|
if (v == null) return null; // No vertices left unsearched
|
|
if (v.Vertex == target)
|
|
{
|
|
Stack<DijkstraVertex> path = new Stack<DijkstraVertex>();
|
|
path.Push(v);
|
|
while(path.Peek().Parent!=null)
|
|
path.Push(path.Peek().Parent);
|
|
return path.Select(dv => dv.Vertex).ToList();
|
|
}
|
|
members.AddRange(data.Where(l => l.point1 == v.Vertex).Select(l => l.point2).Concat(data.Where(l => l.point2 == v.Vertex).Select(l => l.point1))
|
|
.Where(n => members.All(m => m.Vertex != n)).Select(n =>
|
|
{
|
|
var c = new DijkstraVertex {Parent = v, Vertex = n, Cost = v.Cost + v.Vertex.Distance(n)};
|
|
if (bestFirst)
|
|
c.Heuristic = c.Cost + c.Vertex.Distance(target);
|
|
return c;
|
|
}));
|
|
searched.Add(v.Vertex);
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<Vertex2> ConnectedRegion(this IEnumerable<VoronoiLink> data, Vertex2 v)
|
|
{
|
|
List<Vertex2> members = new List<Vertex2>();
|
|
members.Add(v);
|
|
while (true)
|
|
{
|
|
int lastCount = members.Count;
|
|
// For each member, add all vertices that are connected to it via a line but are not already a member
|
|
foreach (Vertex2 m in members.ToArray())
|
|
members.AddRange(data.Where(l => l.point1 == m).Select(l => l.point2)
|
|
.Concat(data.Where(l => l.point2 == m).Select(l => l.point1))
|
|
.Where(n => !members.Contains(n)));
|
|
// If we have stopped finding neighbors, stop traversing
|
|
if (members.Count == lastCount)
|
|
return members;
|
|
}
|
|
}
|
|
} |