public static List <Point2d> GetSettlementLocations(WaterTableField wtf, IField2d <float> wateriness, float noiseScale = 0.1f, float noiseBlur = 3f, float noiseContribution = 0.15f) { IField2d <float> noise = new BlurredField(new MountainNoise(wtf.Width, wtf.Height, noiseScale), noiseBlur); Transformation2d <float, float, float> combination = new Transformation2d <float, float, float>(wateriness, noise, (w, n) => w + noiseContribution * n); List <Point2d> locations = new List <Point2d>(); for (int y = 1; y < combination.Height - 1; y++) { for (int x = 1; x < combination.Width - 1; x++) { if (wtf[y, x] > 0f && combination[y - 1, x - 1] < combination[y, x] && combination[y - 1, x + 0] < combination[y, x] && combination[y - 1, x + 1] < combination[y, x] && combination[y + 0, x + 1] < combination[y, x] && combination[y + 1, x + 1] < combination[y, x] && combination[y + 1, x + 0] < combination[y, x] && combination[y + 1, x - 1] < combination[y, x] && combination[y + 0, x - 1] < combination[y, x]) { locations.Add(new Point2d(x, y)); } } } return(locations); }
public void Resize(int width, int height) { GL.Viewport(0, 0, width, height); // tell OpenGL to use the whole window for drawing //TODO: Save the view port aspect ratio for use in rendering _windowAspectRatio = height / (float)width; var viewport = Transformation2d.Combine(Transformation2d.Translate(Vector2.One), Transformation2d.Scale(width / 2f, height / 2f)); InvViewportMatrix = viewport.Inverted(); GL.Ortho(0, width, height, 0, -1, 1); UpdateMatrix(); }
public void OutputMapForRectangle(Rectangle sourceRect, Bitmap bmp, string dir = "C:\\Users\\Justin Murray\\Desktop\\terrain\\", string name = "submap") { // Hack that adds buffers, use to work around unidentified bugs and differences of behavior near edges. Rectangle rect = new Rectangle( sourceRect.Left - sourceRect.Width / 20, sourceRect.Top - sourceRect.Height / 20, sourceRect.Width * 11 / 10, sourceRect.Height * 11 / 10); int width = (int)Math.Ceiling(bmp.Width * 1.1f); int height = (int)Math.Ceiling(bmp.Height * 1.1f); IField2d <float> waterTable = new BlurredField(new SubContinuum <float>(width, height, new ContinuousField(this.wtf), rect), 0.5f * width / rect.Width); IField2d <float> mountains = new SubContinuum <float>(width, height, this.mountainNoise, rect); // TODO: hills? IField2d <float> roughness = new BlurredField(new SubContinuum <float>(width, height, new ContinuousField(this.args.roughnessDrawing), rect), 0.5f * width / rect.Width); IField2d <float> riverbeds = GetRiverFieldForRectangle(width, height, rect); IField2d <float> damping = GetDampingFieldForRectangle(rect, riverbeds); IField2d <float> heightmap; { IField2d <float> dampedNoise = new Transformation2d <float, float, float>(mountains, damping, (m, d) => Math.Max(1f - d, 0f) * m); IField2d <float> scaledDampedNoise = new Transformation2d <float, float, float>(dampedNoise, roughness, (n, r) => n * r * this.args.mountainHeightMaxInMeters); IField2d <float> groundHeight = new Transformation2d <float, float, float>(waterTable, scaledDampedNoise, (w, m) => w + m); // TODO: Erode the groundHeight. heightmap = new Transformation2d <float, float, float>(groundHeight, riverbeds, Math.Min); //DEBUG heightmap = Erosion.DropletHydraulic( heightmap, 2 * heightmap.Width * heightmap.Height, 100, maxHeight: this.args.baseHeightMaxInMeters + this.args.mountainHeightMaxInMeters, radius: (int)(this.args.erosionRadiusInMeters / (this.args.metersPerPixel * sourceRect.Width / heightmap.Width)) ); } IField2d <float> riverField = new SubField <float>(riverbeds, new Rectangle(bmp.Width / 20, bmp.Height / 20, bmp.Width, bmp.Height)); IField2d <float> heightField = new SubField <float>(heightmap, new Rectangle(bmp.Width / 20, bmp.Height / 20, bmp.Width, bmp.Height)); // TODO: DEBUG OutputAsOBJ(heightField, new Transformation2d <float, bool>(riverField, r => !float.IsPositiveInfinity(r)), sourceRect, bmp, dir, name); OutputAsPreciseHeightmap(heightField, riverField, dir + name + ".png"); }
private static ContinuousField InitializeDistanceFromWater(WaterTableField wtf, Args args) { var dists = new Transformation2d <Point2d, float>(wtf.DrainageField, (x, y, p) => { if (wtf.HydroField[y, x] != HydrologicalField.LandType.Land) { return(0f); } // We subtract 1 to compute the distance as water-adjacent as well as on-the-water. // This is a slight bit of a hack, but helps account for problems at varying resolutions. return(args.metersPerPixel * (Point2d.Distance(new Point2d(x, y), p) - 1f)); }); return(new ContinuousField(dists)); }
internal void PlacePath(float x, float y, KeyboardState keyboard) { var cam = _view.Camera; var fromViewportToWorld = Transformation2d.Combine(cam.InvViewportMatrix, cam.CameraMatrix.Inverted()); var pixelCoordinates = new Vector2(x, y); var world = pixelCoordinates.Transform(fromViewportToWorld); if (world.X < 0 || _model.Grid.Columns < world.X) { return; } if (world.Y < 0 || _model.Grid.Rows < world.Y) { return; } var column = (int)Math.Truncate(world.X); var row = (int)Math.Truncate(world.Y); var cell = _model.CheckCell(column, row); if (cell == Grid.CellType.Empty) { //Path setzen if (keyboard.IsKeyDown(Keys.Space)) { if (cell != Grid.CellType.Empty) { return; } else { if (_model.PlacePath(column, row)) { return; } } return; } } }
public void OutputAsOBJ(IField2d <float> heights, IField2d <bool> riverField, Rectangle rect, Bitmap bmp, string outputDir, string outputName = "terrain") { using (var objWriter = new StreamWriter(File.OpenWrite(outputDir + outputName + ".obj"))) { objWriter.WriteLine("mtllib " + outputName + ".mtl"); objWriter.WriteLine("o " + outputName + "_o"); float metersPerPixel = this.args.metersPerPixel * rect.Width / heights.Width; IField2d <vFloat> verts = new Transformation2d <float, vFloat>(heights, (x, y, z) => new vFloat(x * metersPerPixel, -y * metersPerPixel, z)); // One vertex per pixel. for (int y = 0; y < verts.Height; y++) { for (int x = 0; x < verts.Width; x++) { vFloat v = verts[y, x]; objWriter.WriteLine("v " + v[0] + " " + v[1] + " " + v[2]); } } // Normal of the vertex is the cross product of skipping vectors in X and Y. If skipping is unavailable, use the local vector. for (int y = 0; y < verts.Height; y++) { for (int x = 0; x < verts.Width; x++) { int xl = Math.Max(x - 1, 0); int xr = Math.Min(x + 1, verts.Width - 1); int yl = Math.Max(y - 1, 0); int yr = Math.Min(y + 1, verts.Height - 1); vFloat xAxis = verts[y, xr] - verts[y, xl]; vFloat yAxis = verts[yr, x] - verts[yl, x]; vFloat n = vFloat.Cross3d(xAxis, yAxis); if (n[2] < 0f) { n = -n; } n = n.norm(); objWriter.WriteLine("vn " + n[0] + " " + n[1] + " " + n[2]); } } // Texture coordinate is trivial. for (int y = 0; y < verts.Height; y++) { for (int x = 0; x < verts.Width; x++) { objWriter.WriteLine("vt " + (1f * x / (verts.Width - 1)) + " " + (1f - 1f * y / (verts.Height - 1))); } } objWriter.WriteLine("g " + outputName + "_g"); objWriter.WriteLine("usemtl " + outputName + "_mtl"); // Now the faces. Since we're not optimizing at all, this is incredibly simple. // TODO: Fix bug where including edge pixels causes tris to go across entire map for some reason. For now, just workaround. for (int y = 1; y < verts.Height - 2; y++) { for (int x = 1; x < verts.Width - 2; x++) { int tl = y * verts.Width + x; int tr = tl + 1; int bl = tl + verts.Width; int br = bl + 1; objWriter.WriteLine("f " + tl + "/" + tl + "/" + tl + " " + br + "/" + br + "/" + br + " " + tr + "/" + tr + "/" + tr); objWriter.WriteLine("f " + tl + "/" + tl + "/" + tl + " " + bl + "/" + bl + "/" + bl + " " + br + "/" + br + "/" + br); } } } using (var mtlWriter = new StreamWriter(File.OpenWrite(outputDir + outputName + ".mtl"))) { mtlWriter.WriteLine("newmtl " + outputName + "_mtl"); mtlWriter.WriteLine("Ka 0.000000 0.000000 0.000000"); mtlWriter.WriteLine("Kd 0.800000 0.800000 0.800000"); mtlWriter.WriteLine("Ks 0.200000 0.200000 0.200000"); mtlWriter.WriteLine("Ns 1.000000"); mtlWriter.WriteLine("d 1.000000"); mtlWriter.WriteLine("illum 1"); mtlWriter.WriteLine("map_Kd " + outputName + ".jpg"); } for (int x = 0, y = 0; y < heights.Height; y += ++x / heights.Width, x %= heights.Width) { float value = heights[y, x]; Color color; if (riverField[y, x]) { color = Color.DodgerBlue; } else if (value > 2250f) { color = Lerp(Color.Gray, Color.White, (value - 2250f) / 1750f); } else if (value > 1250f) { color = Lerp(Color.Red, Color.Gray, (value - 1250f) / 1000f); } else if (value > 750f) { color = Lerp(Color.Yellow, Color.Red, (value - 750f) / 500f); } else if (value > 250f) { color = Lerp(Color.Green, Color.Yellow, (value - 250f) / 500f); } else if (value > 5f) { color = Lerp(Color.DarkGreen, Color.Green, (value - 5f) / 245f); } else { color = Color.DodgerBlue; } bmp.SetPixel(x, y, color); } bmp.Save(outputDir + outputName + ".jpg"); }
internal void Click(float x, float y, KeyboardState keyboard) { var cam = _view.Camera; var fromViewportToWorld = Transformation2d.Combine(cam.InvViewportMatrix, cam.CameraMatrix.Inverted()); var pixelCoordinates = new Vector2(x, y); var world = pixelCoordinates.Transform(fromViewportToWorld); if (world.X < 0 || _model.Grid.Columns < world.X) { return; } if (world.Y < 0 || _model.Grid.Rows < world.Y) { return; } var column = (int)Math.Truncate(world.X); var row = (int)Math.Truncate(world.Y); var cell = _model.CheckCell(column, row); if (_model.gameOver == false) { //Sniper verkaufen if (cell == Grid.CellType.Sniper && keyboard.IsKeyDown(Keys.Delete)) { foreach (Tower tower in _model.towers.ToList()) { if (tower.Center.X == column && tower.Center.Y == row) { _model.ClearCell(column, row, tower); Console.WriteLine("sold sniper, new balance: " + _model.cash); } else { continue; } } return; } //Rifle verkaufen if (cell == Grid.CellType.Rifle && keyboard.IsKeyDown(Keys.Delete)) { //Sell Rifle foreach (Tower tower in _model.towers.ToList()) { if (tower.Center.X == column && tower.Center.Y == row) { _model.ClearCell(column, row, tower); Console.WriteLine("sold rifle, new balance: " + _model.cash); } else { continue; } } return; } //Bouncer verkaufen if (cell == Grid.CellType.Bouncer && keyboard.IsKeyDown(Keys.Delete)) { foreach (Tower tower in _model.towers.ToList()) { if (tower.Center.X == column && tower.Center.Y == row) { _model.ClearCell(column, row, tower); Console.WriteLine("sold sniper, new balance: " + _model.cash); } else { continue; } } return; } //Schauen ob Cell leer ist if (cell == Grid.CellType.Empty) { //Sniper kaufen if (keyboard.IsKeyDown(Keys.D2)) { if (cell != Grid.CellType.Empty) { return; } else { _model.PlaceSniper(column, row); } return; } //Rifle kaufen if (keyboard.IsKeyDown(Keys.D1)) { if (cell != Grid.CellType.Empty) { return; } else { _model.PlaceRifle(column, row); } return; } //Bouncer kaufen if (keyboard.IsKeyDown(Keys.D3)) { if (cell != Grid.CellType.Empty) { return; } else { _model.PlaceBouncer(column, row); } return; } } } }
internal void ShowTowerSample(float x, float y, KeyboardState keyboard) { var cam = _view.Camera; var fromViewportToWorld = Transformation2d.Combine(cam.InvViewportMatrix, cam.CameraMatrix.Inverted()); var pixelCoordinates = new Vector2(x, y); var world = pixelCoordinates.Transform(fromViewportToWorld); if (world.X < 0 || _model.Grid.Columns < world.X) { return; } if (world.Y < 0 || _model.Grid.Rows < world.Y) { return; } var column = (int)Math.Truncate(world.X); var row = (int)Math.Truncate(world.Y); var cell = _model.CheckCell(column, row); if (cell == Grid.CellType.Empty) { if (keyboard.IsKeyDown(Keys.D2)) { if (cell != Grid.CellType.Empty) { return; } else { _view.sampleSniper = true; _view.sampleColRow = new Vector2(column, row); }//Snake return; } else { _view.sampleSniper = false; } if (keyboard.IsKeyDown(Keys.D1)) { if (cell != Grid.CellType.Empty) { return; } else { _view.sampleRifle = true; _view.sampleColRow = new Vector2(column, row); }//Snake return; } else { _view.sampleRifle = false; } if (keyboard.IsKeyDown(Keys.D3)) { if (cell != Grid.CellType.Empty) { return; } else { _view.sampleBouncer = true; _view.sampleColRow = new Vector2(column, row); }//Snake return; } else { _view.sampleBouncer = false; } } }
public static void RunZoomedInScenario() { // 32x32 up to 1024x1024 will, from the fifth-of-a-mile-per-pixel source, get us approximately 10m per pixel. // 16x16 would get us 5 // 8x8 would get us 2.5 // 4x4 would get us 1.25 // 2x2 would get us .75 // 1x1 would get us .375, which is slightly over 1 foot. // I think 16x16 is the sweet spot. That's just over 9 square miles per small map. const int SMALL_MAP_SIDE_LEN = 64; const float STARTING_SCALE = 0.005f * SMALL_MAP_SIDE_LEN / 32; const int SMALL_MAP_RESIZED_LEN = 1024; Random random = new Random(); WaterTableArgs args = new WaterTableArgs(); Bitmap bmp = new Bitmap(args.inputPath + "rivers.png"); IField2d <float> baseMap = new Utils.FieldFromBitmap(new Bitmap(args.inputPath + "base_heights.png")); baseMap = new ReResField(baseMap, (float)bmp.Width / baseMap.Width); var wtf = Utils.GenerateWaters(bmp, baseMap); Utils.OutputAsColoredMap(wtf, wtf.RiverSystems, bmp, args.outputPath + "colored_map.png"); var hasWater = new Transformation2d <HydrologicalField.LandType, float>(wtf.HydroField, t => t == HydrologicalField.LandType.Land ? 0f : 1f); var noiseDamping = new Transformation2d(new BlurredField(hasWater, 2f), v => 3.5f * v); // Create the spline map. SparseField2d <List <SplineTree> > relevantSplines = new SparseField2d <List <SplineTree> >(wtf.Width, wtf.Height, null); { //HashSet<TreeNode<Point2d>> relevantRivers = new HashSet<TreeNode<Point2d>>(); foreach (var system in wtf.RiverSystems) { SplineTree tree = null; foreach (var p in system.value) { if (relevantSplines[p.value.y, p.value.x] == null) { relevantSplines[p.value.y, p.value.x] = new List <SplineTree>(); } relevantSplines[p.value.y, p.value.x].Add(tree ?? (tree = new SplineTree(system.value, wtf, random))); } } } Rectangle rect = new Rectangle(518 + 15, 785 + 45, SMALL_MAP_SIDE_LEN, SMALL_MAP_SIDE_LEN); var smallMap = new SubField <float>(wtf, rect); var scaledUp = new BlurredField(new ReResField(smallMap, SMALL_MAP_RESIZED_LEN / smallMap.Width), SMALL_MAP_RESIZED_LEN / (4 * SMALL_MAP_SIDE_LEN)); var smallDamp = new SubField <float>(noiseDamping, rect); var scaledDamp = new BlurredField(new ReResField(smallDamp, SMALL_MAP_RESIZED_LEN / smallMap.Width), SMALL_MAP_RESIZED_LEN / (4 * SMALL_MAP_SIDE_LEN)); // Do spline-y things. Field2d <float> riverbeds; List <SplineTree> splines = new List <SplineTree>(); { // Collect a comprehensive list of the spline trees for the local frame. for (int y = rect.Top - 1; y <= rect.Bottom + 1; y++) { for (int x = rect.Left - 1; x <= rect.Right + 1; x++) { List <SplineTree> trees = relevantSplines[y, x]; if (trees != null) { splines.AddRange(trees); } } } // Crafts the actual river kernel. Probably not the best way to go about this. riverbeds = new Field2d <float>(new ConstantField <float>(SMALL_MAP_RESIZED_LEN, SMALL_MAP_RESIZED_LEN, float.MaxValue)); foreach (var s in splines) { var samples = s.GetSamplesPerControlPoint(1f * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN); int priorX = int.MinValue; int priorY = int.MinValue; foreach (var p in samples) { int x = (int)((p[0] - rect.Left) * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN); int y = (int)((p[1] - rect.Top) * SMALL_MAP_RESIZED_LEN / SMALL_MAP_SIDE_LEN); if (x == priorX && y == priorY) { continue; } else { priorX = x; priorY = y; } if (0 <= x && x < SMALL_MAP_RESIZED_LEN && 0 <= y && y < SMALL_MAP_RESIZED_LEN) { const int r = 1024 / SMALL_MAP_SIDE_LEN; for (int j = -r; j <= r; j++) { for (int i = -r; i <= r; i++) { int xx = x + i; int yy = y + j; if (0 <= xx && xx < SMALL_MAP_RESIZED_LEN && 0 <= yy && yy < SMALL_MAP_RESIZED_LEN) { float dSq = i * i + j * j; riverbeds[yy, xx] = Math.Min(riverbeds[yy, xx], p[2] + dSq / (512f * 32 / SMALL_MAP_SIDE_LEN)); //scaledDamp[yy, xx] = 1f; //scaledUp[yy, xx] = Math.Min(scaledUp[yy, xx], p[2] + (float)Math.Sqrt(xx * xx + yy * yy) / 1f); } } } } } } //Utils.OutputField(riverbeds, new Bitmap(riverbeds.Width, riverbeds.Height), args.outputPath + "river_field.png"); } var mountainous = new ScaleTransform(new MountainNoise(1024, 1024, STARTING_SCALE), 1f); var hilly = new ScaleTransform(new Simplex2D(1024, 1024, STARTING_SCALE * 4), 0.1f); var terrainNoise = new Transformation2d <float, float, float>(mountainous, hilly, (x, y, m, h) => { float a = scaledUp[y, x]; float sh = Math.Max(-2f * Math.Abs(a - 0.2f) / 3f + 1f, 0f); float sm = Math.Min(1.3f * a, 1f); return(h * sh + m * sm); }); IField2d <float> combined = new NormalizedComposition2d <float>( new Transformation2d <float, float, float>(riverbeds, new Composition2d <float>(scaledUp, new Transformation2d <float, float, float>(scaledDamp, terrainNoise, (s, m) => (1 - Math.Min(1, s)) * m) ), (r, c) => r == float.MaxValue ? c : Math.Min(r, c)) ); Bitmap img = new Bitmap(combined.Width, combined.Height); Utils.OutputField(combined, img, args.outputPath + "combined.png"); Utils.OutputAsOBJ(combined, splines, rect, img, args.outputPath); }