So I'm making a map generator that makes random islands. It uses Perlin Noise at the heart of the generator and then a method using circles with gradients to make the islands.
The circle method creates a number of circles in the centerish of the map with a gradient from a colour starting at 64 down to 0. The issue is that this method is creating a un-natrual look at parts of the map with circular edges. When the perlin noise is generated for a pixel it will get that pixel on the gradient map and then mutliply it by the blue value.
So if the perlin noise gives a one on pixel 1, 5 and the blue value on the gradient map is 54 it will output a noise value of 54. If the perlin noise on pixel 130, 560 is 0.5 and the gradient colour value is 64 then the noise value of 32.
Here is what I am getting:
There is two key points to the code, the perlin bit:
noise = NoiseGenerator.Noise(x, y);
double gradColour = getGradColour(x, y).B;
double addedNoise = noise * gradColour;
double gradNoise = addedNoise;// - gradColour;
And then the gradient map generator:
public static void DrawGrad(float X, float Y, float R, Color C1, Color C2)
{
Graphics g = Graphics.FromImage(imgGrad);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(X, Y, R, R);
PathGradientBrush pathGrBrush = new PathGradientBrush(path);
pathGrBrush.CenterColor = C1;
Color[] colours = { C2 };
pathGrBrush.SurroundColors = colours;
g.FillEllipse(pathGrBrush, X, Y, R, R);
//g.FillEllipse(Brushes.Red, X, Y, R, R);
g.Flush();
}
int amount = rnd.Next(25, 30);
for (int i = 0; i < amount / 4; i++)
{
float X = rnd.Next(-800, 1748);
float Y = rnd.Next(-800, 1748);
float R = rnd.Next(1000, 1200);
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(15, 20)), Color.FromArgb(0, 0, 0, 0));
}
for (int i = 0; i < amount; i++)
{
double positionDiv = 1.98;
double X1 = rnd.Next(0, 450) / positionDiv;
double Y1 = rnd.Next(0, 450) / positionDiv;
double R1 = rnd.Next(300, 650) / 4;
float R = (float)R1;
float X = (float)X1;
float Y = (float)Y1;
while (X + R > 1004)
{
X = 924 - R;
}
while (Y + R > 1004)
{
Y = 924 - R;
}
if(X < 30)
{
X = 30;
}
if(Y < 30)
{
Y = 30;
}
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(40, 64)), Color.FromArgb(0, 0, 0, rnd.Next(13, 17)));
}
I was just wondering if anyone else knows any other methods in C# that could create an island using perlin noise? Any advice would be greatly appreciated.
As I mentioned in the comment diamond and square is much easier with good enough results. So the algorithm:
configure generation properties
Here you need to have set of parameters like min,max elevation, sea level, elevation ranges for vegetation, sand/rock/dirt, etc, slope parameters etc.
create terrain height map I call it zed[][]
For this you need slightly modified Diamond&Square algorithm. The problem is this algorithm produces "inland" like terrain.
To adjust it so it produces island like terrains you need to initialize it with lowest possible elevation in corners. Also you need to ignore the first diamond step and initialize the mid point with some random value instead (not average of corners). And last after each square iteration correct the border points to the minimal (underwater) elevation (or some random value near it).
To achieve the good output I use approximately range <-2^15 , 2^16> while generation. After this I find min and max elevation in the generated terrain and rescale to configured elevation ranges.
Do not forget that Diamond and square need map of resolution (2^n)+1 !!!
create surface map I call it typ[][]
When terrain map is finished you can add elevation based features like these in ascending order:
watter,sand,vegetation type,mountine rocks,snow
Then add parameters based on slope of terrain
rocks
Then you can add additional things like (based on some rules):
rivers,streams,watter-falls,building,roads,...
I do it in C++ like this:
void map_random(int _xs,int _ys)
{
// config
int h0=-1000,h1=3000; // [m] terrain elevation range
int h_water= 0; // [m] sea level
int h_sand=15; // [m] sand level
int h_evergreen=1500; // [m] evergreen level
int h_snow=2000; // [m] snow level
int h_rock=1800; // [m] mountine rock level
float a_rock=60.0; // [deg] mountine rock slope
float d_pixel=15.0; // [m] pixel size
bool _island=true;
// types
enum _cover_enum
{
_cover_none=0,
_cover_water,
_cover_snow,
_covers,
_cover_shift=0,
_cover_mask=15,
};
DWORD _cover[_covers]=
{
// RRGGBB
0x00000000, // none
0x00004080, // water
0x008F8F8F, // snow
};
enum _terrain_enum
{
_terrain_enum_beg=-1,
_terrain_dirt,
_terrain_sand,
_terrain_rock,
_terrains,
_terrain_shift=4,
_terrain_mask=15,
};
DWORD _terrain[_terrains]=
{
// RRGGBB
0x00301510, // dirt
0x00EEC49A, // sand
0x00777777, // rock
};
enum _flora_enum
{
_flora_enum_beg=-1,
_flora_none,
_flora_grass,
_flora_hardwood,
_flora_evergreen,
_flora_deadwood,
_floras,
_flora_shift=8,
_flora_mask=15,
};
DWORD _flora[_floras]=
{
// RRGGBB
0x00000000, // none
0x007F7F3F, // grass
0x001FFF1F, // hardwood
0x00007F00, // evergreen
0x007F3F1F, // deadwood
};
// variables
float a,b; int c,t,f;
int x,y,z,xx,yy,mxs,mys,dx,dy,dx2,dy2,r,r2;
int **ter=NULL,**typ=NULL;
Randomize();
// align resolution to power of 2
for (mxs=1;mxs+1<_xs;mxs<<=1); if (mxs<3) mxs=3;
for (mys=1;mys+1<_ys;mys<<=1); if (mys<3) mys=3;
ter=new int*[mys+1]; for (y=0;y<=mys;y++) ter[y]=new int[mxs+1];
typ=new int*[mys+1]; for (y=0;y<=mys;y++) typ[y]=new int[mxs+1];
// [Terrain]
// diamond & square random height map -> ter[][]
dx=mxs; dx2=dx>>1; r=1<<16; // init step,half step and randomness
dy=mys; dy2=dy>>1; r2=r>>1;
// set corners values
if (_island)
{
t=-r2;
ter[ 0][ 0]=t;
ter[ 0][mxs]=t;
ter[mys][ 0]=t;
ter[mys][mxs]=t;
ter[dy2][dx2]=r2;
}
else{
ter[ 0][ 0]=Random(r);
ter[ 0][mxs]=Random(r);
ter[mys][ 0]=Random(r);
ter[mys][mxs]=Random(r);
}
for (;dx2|dy2;dx=dx2,dx2>>=1,dy=dy2,dy2>>=1) // subdivide step until full image is filled
{
if (!dx) dx=1;
if (!dy) dy=1;
// diamond (skip first one for islands)
if ((!_island)||(dx!=mxs))
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y-dy2][x-dx2]+ter[y-dy2][x+dx2]+ter[y+dy2][x-dx2]+ter[y+dy2][x+dx2])>>2)+Random(r)-r2;
// square
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx ,xx=mxs-dx ;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (y=dy ,yy=mys-dy ;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
{
y= 0; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y+dy2][x])/3)+Random(r)-r2;
y=mys; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x])/3)+Random(r)-r2;
}
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
{
x= 0; ter[y][x]=((ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
x=mxs; ter[y][x]=((ter[y][x-dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
}
// adjust border
if (_island)
{
for (y=0;y<=mys;y+=dy2) { ter[y][0]=t; ter[y][mxs]=t; }
for (x=0;x<=mxs;x+=dx2) { ter[0][x]=t; ter[mys][x]=t; }
}
// adjust randomness
// r=(r*100)>>8; if (r<2) r=2; r2=r>>1;
r>>=1; if (r<2) r=2; r2=r>>1;
}
// rescale to <h0,h1>
xx=ter[0][0]; yy=xx;
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
if (xx>z) xx=z;
if (yy<z) yy=z;
}
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
ter[y][x]=h0+(((ter[y][x]-xx)*(h1-h0))/(yy-xx));
// [Surface]
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
// max slope [deg]
a=atan2(ter[y][x+1]-z,d_pixel);
b=atan2(ter[y+1][x]-z,d_pixel);
if (a<b) a=b; a*=180.0/M_PI;
c=_cover_none;
if (z<=h_water) c=_cover_water;
if (z>=h_snow ) c=_cover_snow;
t=_terrain_dirt;
if (z<=h_sand) t=_terrain_sand;
if (z>=h_rock) t=_terrain_rock;
if (a>=a_rock) t=_terrain_rock;
f=_flora_none;
if (t==_terrain_dirt)
{
r=Random(100);
if (r>10) f=_flora_grass;
if (r>50)
{
if (z>h_evergreen) f=_flora_evergreen;
else{
r=Random(h_evergreen);
if (r<=z) f=_flora_evergreen;
else f=_flora_hardwood;
}
}
if (r<5) f=_flora_deadwood;
}
typ[y][x]=(c<<_cover_shift)|(t<<_terrain_shift)|(f<<_flora_shift);
}
// [copy data] rewrite this part to suite your needs it just compute color based on type of terrain and height
// ter[][] is elevation in meters
// typ[][] is surface type
/*
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
pic.p[y][x].dd=(((ter[y][x]-h0)*255)/(h1-h0))*0x00010101;
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
{
r=typ[y][x];
c=(r>> _cover_shift)& _cover_mask;
t=(r>>_terrain_shift)&_terrain_mask;
f=(r>> _flora_shift)& _flora_mask;
r=_terrain[t];
if (c) r= _cover[c];
if (c==_cover_water)
{
xx=256-((ter[y][x]<<7)/h0);
yy=int(r>>16)&255; yy=(yy*xx)>>8; r=(r&0x0000FFFF)|(yy<<16);
yy=int(r>> 8)&255; yy=(yy*xx)>>8; r=(r&0x00FF00FF)|(yy<< 8);
yy=int(r )&255; yy=(yy*xx)>>8; r=(r&0x00FFFF00)|(yy );
}
if (f){ if (c) r|=_flora[f]; else r=_flora[f]; };
pic.p[y][x+_xs].dd=r;
}
*/
// free ter[][],typ[][]
for (y=0;y<=mys;y++) delete[] ter[y]; delete[] ter; ter=NULL;
for (y=0;y<=mys;y++) delete[] typ[y]; delete[] typ; typ=NULL;
}
The output with current settings is like this:
[Notes]
This approach usually produce only single big hill on the island. (Inland is generated OK) If you want more of them you can create more terrain maps and average them together.
I do following instead: I set the middle point to max height and ignore first diamond pass. After the first square pass I set the middle point back to some random value. This adds the possibility of more central hills then just one. Using this approach and adding lighting (ambient + normal shading) to preview and slight tweaking of pixel size (35m) I got this result:
On rare occasion this can generate inland like map (if the central area is too small. To handle it you can scan corners for watter. if there is land generate again or add some bias for central points randomness in first pass.
You can play with the code for example add rivers:
find topest hill
get random location close/around it
set it to river type
find smallest height neighbor pixel not set to river type
if it is on edge of map or set to sea/water type stop otherwise loop #3
If you want more then one rivers then do not forget to use some temp type for already done rivers so the algorithm can work properly. You can also increase the river volume with distance from start... Here is the result:
After this you should also equalize the formed lakes water level.
Related
I'd like to take a range of four random integers between 1-64, and generate a random value somewhere within the range but leaning towards a more weighted average.
The practical application is that you take a pixel, grab the 4 surrounding pixels and use those plus the current pixel to generate a value that can then be used as the base weight for a Gaussian random number generator. So you have a pixel of 10 brightness, surrounded by 8,8,9,9. Add them all up, average out to 8.8. 8.8 is then the weight for the Gaussian random number generator. So you have a random result within a range, but close to the average brightness which is 8.8, and still with some element of randomness.
The issue comes when you have wide variations because of random noise.
To give a pseudo example of how I would like it to work..
Input = [1,16,19,21]
The average of this is 14.25, but that has too much movement because of the "1" bringing the average way down. The average of this should be more around the 18 mark, because more of the numbers are clustered around that area.
I would like to see a random result coming out that is between 1 and 64, but heavily weighted between 15 and 22, with a lower possibility of it being towards the 1(Because it is still within the range as a whole) and a much lower possibility of it being over 22(Because that is completely outside of the range).
Additional The purpose of this is to generate a galactic map. I have got to the point where I have a good set of galaxy shaped data, giving me the rough density of each sector on the map. Now I need to generate specific sets of data and generate exact numbers of stars in each sector. Taking the average of the 4 surrounding sectors and using that to work out how "dense" this sector should be is the main purpose. The main thing I want to avoid is that sectors bordering an empty region of space do not also end up mostly empty, as this does not fit with general observations of galaxies.
You could imagine that the four numbers are points in a line, the x axis. Around that points there is a sphere of probability with a radius of 64, with the probability more concentrated in the proximity of the points rather than on the edges. Pick randomly one of the four points, calculate a random point inside the sphere of that number and take its x coordinate. Repeat if it is out of the range 1..64.
using System;
using System.Collections.Generic;
namespace ProbabilityDistribution1
{
class Program
{
// This derived class converts the uniformly distributed random
// numbers generated by base.Sample( ) to another distribution.
class RandomProportional : Random
{
// The Sample method generates a distribution more concentrated around the 0, in the range [0.0, 1.0].
protected override double Sample()
{
double BSample = base.Sample();
const double concentrationAroundInputs = 5;//more concentrated when greater
double result = Math.Pow(BSample, concentrationAroundInputs);
return result;
}
}
static double XCoordinateOfRandomUnitInsideSphere(Random aRandom)
{
//Even with uniform distribution the probability of exiting is greater than 0.5 on each iteration
while (true)
{
double x = aRandom.NextDouble();
double y = aRandom.NextDouble();
double z = aRandom.NextDouble();
if ((x * x + y * y + z * z) < 1) //inside the sphere
{
return x;
}
}
}
static void TestDistribution()
{
double[] Input = { 1, 16, 19, 21 };
List<int> sampleValues = new List<int>();
Random aRandom = new Random();
RandomProportional aRandomProportinal = new RandomProportional();
for (int i = 0; i < 100; i++)
{
int value = 0;
do
{
int indexChosen = aRandom.Next(4);
double xCoordinate = XCoordinateOfRandomUnitInsideSphere(aRandomProportinal);
if (aRandom.Next(2)==0)
{
xCoordinate = -xCoordinate;
}
double xRandomResult = xCoordinate * 64;
value = (int)(Input[indexChosen] + xRandomResult);
} while (value < 1 || value > 64);
sampleValues.Add((int)value);
}
sampleValues.Sort();
Console.WriteLine();
foreach (int i in sampleValues)
{
Console.Write(" {0:00} ", i);
}
Console.WriteLine();
}
static void Main(string[] args)
{
TestDistribution();
Console.WriteLine("end");
Console.ReadLine();
}
}
}
I am drawing on canvas based on device movement, I want to draw different characters in canvas based on mobile movement.
Currently its working, but I want to find time difference and i want to detect pause, pause means when user is not trying to draw and user is not moving mobile phone, so that Application ca assume that now user want to draw next character.
How to find pause in accelerometer values. Any logic? Also tell me how i can smooth accelerometer values, so that user can draw lines without noise.
I cannot help with the accelerator part, but for the noise in the data, here is one approach using Weighted Moving Average.
The basics are simple:
Find out how many points before current you want to use for smoothing
Calculate a weight based on length, f.ex. if length is 5 then the weight = 1+2+3+4+5 = 15
Iterate each data point starting from length of weight (you can start at 1 and cut the weighting short - below I'll demo the latter approach)
For point current - 5 multiply with 1/15, for current - 4 multiply with 2/15 and so forth. The sum is stored as value for this point, repeat for the next value points
Live demo
Below is a demo (enter full page to see all graphics). I wrote it in JavaScript so it could be shown live here in the answer. I think you should have little problem converting it into the language you're using (which is not stated).
Move the slider to increase number of points to weight. You can run the data through several passes to smooth even more. The original data is a sinus curve with noise jitter. With many points you can see the curve smooths to replicate this. Just using 9-10 points length over 2 passes will give a good result with very little time delay:
var ctx = document.querySelector("canvas").getContext("2d"),
rng = document.querySelector("input"),
val = document.querySelector("span"),
data = [], scale = 30;
// generate sinus wave with noise jitters
for(var i = 0; i < ctx.canvas.width; i += 2)
data.push(Math.sin(i*0.1) * Math.random() + Math.random())
// draw initial smoothed curve (length=1, no smoothing)
drawWMA();
// calculate moving average
function drawWMA() {
var len = +rng.value, // get smoothing length (number of previous points)
dataa = [], datab = [], // pass A and B arrays
weight = 0; // calc weight based on length
val.innerHTML = len;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.beginPath();
// calc weight
for(var i = 1; i <= len; i++) weight += i; // add range together [1, length]
// plot original data at top of canvas
plot(data, 30);
// PASS 1: Calc new smoothed array
dataa = calcWMA(data, len, weight);
// plot smoothed curve
ctx.fillText("FIRST PASS:", 0, 100);
plot(dataa, 120);
// PASS 2 (optional)
datab = calcWMA(dataa, len, weight);
ctx.fillText("SECOND PASS:", 0, 190);
plot(datab, 210);
ctx.stroke(); // render plots
}
function calcWMA(data, len, weight) {
var i, t, datao = [];
// calc new smoothed array
for(i = 0; i < data.length; i++) { // iterate from length to end of data
var v = 0; // calc average value for this position
for(t = 0; t < len; t++) { // [1, len]
if (i-t >= 0)
v += data[i-t] * ((t+1) / weight); // weight previous values based on -delta
}
datao.push(v); // store new value
}
return datao
}
function plot(data, y) {
ctx.moveTo(0, y + data[0]*scale);
for(i = 1; i < data.length; i++) ctx.lineTo(i * 2, y + data[i]*scale);
}
rng.onchange = rng.oninput = drawWMA;
<label>Points to consider: <input type="range" min=1 max=50 value=1></label><span>1</span><br>
<canvas width=600 height=300></canvas>
A different approach would be to use a Savitzky–Golay filter which gives a similar result, but not "sacrifice" any points at the end (moving average will push forward or crop at the end).
So i'm trying to write a simple 3d-application (for study only).
I want to do shading using Phong. So i'm reading this article and see
Since this can be slow, there are various ways you can speed it up if you don't mind a little loss of freedom. If you assume that the light source is at the same place as the camera, then you can ignore the V and H vectors altogether. Instead take the X and Y components of the normal vector, multiply by 128 and add 127 (assuming that is that the magnitude of the normal vector is 1).
okay, i can do it, but now, what should I do with this X and Y? I have array of triangles, each has calculated normal. I'm calculating this X and Y, but what should i do with? This code works, but not properly, because in this case light source is in 0,0,0 when i assume that it's in camera point.
Invalid code is here:
public override void Draw(Graphics graphics, VisualizableShape shape)
{
if (!shape.IsVisible)
return;
base.Draw(graphics, shape); // draw edge
int x = (int) (shape.Normal.X*128 + 127);
int y = (int) (shape.Normal.Y*128 + 127);
int z = x + y;
var color = Color.FromArgb(255, (Pen.Color.R*z)/511, ((Pen.Color.G*z)/511), (Pen.Color.B*z)/511);
graphics.FillPolygon(new SolidBrush(color), shape.Points.CastToPointF());
}
just dunno what should I do in this case...
I've spent a few hours today researching how random terrain generation tends to be done and after reading about the plasma fractal (midpoint displacement and diamond square algo's) I decided to try and have a go at implementing one. My result was actually not terriable, but I have these horrible square/line/grid type artefacts that I just can not seem to get rid of!
When rendered as a gray scale image my height map looks something like:
height map http://sphotos-d.ak.fbcdn.net/hphotos-ak-ash3/535816_10151739010123327_225111175_n.jpg
Obviously there is a fair amount of code involved in this but I will try to post what is only relevant. I've not not posted the code that turns it into a texture for example, but do not worry I have already tried just filling my height array with a smooth gradient and the texture comes out fine :)
I begin by setting the four corners of the map to random values between 0 and 1 and then start the recursive displacement algo:
public void GenerateTerrainLayer()
{
//set the four corners of the map to have random values
TerrainData[0, 0] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, 0] = (float)RandomGenerator.NextDouble();
TerrainData[0, GenSize] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, GenSize] = (float)RandomGenerator.NextDouble();
//begin midpoint displacement algorithm...
MidPointDisplace(new Vector2_I(0, 0), new Vector2_I(GenSize, 0), new Vector2_I(0, GenSize), new Vector2_I(GenSize, GenSize));
}
TerrainData is simply a 2D array of floats*. Vector2_I is just my own integer vector class. The last four functions are MidPointDisplace which is the recursive function, CalculateTerrainPointData which averages 2 data values and adds some noise, CalculateTerrainPointData2 which averages 4 data values and adds some noise and has a slightly higher scale value (its only used for center points) and finally my noise function which atm is just some random noise and not a real noise like perlin etc. They look like this:
private void MidPointDisplace(Vector2_I topleft, Vector2_I topright, Vector2_I bottomleft, Vector2_I bottomright)
{
//check size of square working on.. if its shorter than a certain amount stop the algo, we've done enough
if (topright.X - topleft.X < DisplacementMaxLOD)
{
return;
}
//calculate the positions of all the middle points for the square that has been passed to the function
Vector2_I MidLeft, MidRight, MidTop, MidBottom, Center;
MidLeft.X = topleft.X;
MidLeft.Y = topleft.Y + ((bottomleft.Y - topleft.Y) / 2);
MidRight.X = topright.X;
MidRight.Y = topright.Y + ((bottomright.Y - topright.Y) / 2);
MidTop.X = topleft.X + ((topright.X - topleft.X) / 2);
MidTop.Y = topleft.Y;
MidBottom.X = bottomleft.X + ((bottomright.X - bottomleft.X) / 2);
MidBottom.Y = bottomleft.Y;
Center.X = MidTop.X;
Center.Y = MidLeft.Y;
//collect the existing data from the corners of the area passed to algo
float TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat;
TopLeftDat = GetTerrainData(topleft.X, topleft.Y);
TopRightDat = GetTerrainData(topright.X, topright.Y);
BottomLeftDat = GetTerrainData(bottomleft.X, bottomleft.Y);
BottomRightDat = GetTerrainData(bottomright.X, bottomright.Y);
//and the center
//adverage data and insert for midpoints..
SetTerrainData(MidLeft.X, MidLeft.Y, CalculateTerrainPointData(TopLeftDat, BottomLeftDat, MidLeft.X, MidLeft.Y));
SetTerrainData(MidRight.X, MidRight.Y, CalculateTerrainPointData(TopRightDat, BottomRightDat, MidRight.X, MidRight.Y));
SetTerrainData(MidTop.X, MidTop.Y, CalculateTerrainPointData(TopLeftDat, TopRightDat, MidTop.X, MidTop.Y));
SetTerrainData(MidBottom.X, MidBottom.Y, CalculateTerrainPointData(BottomLeftDat, BottomRightDat, MidBottom.X, MidBottom.Y));
SetTerrainData(Center.X, Center.Y, CalculateTerrainPointData2(TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat, Center.X, Center.Y));
debug_displacement_iterations++;
//and recursively fire off new calls to the function to do the smaller squares
Rectangle NewTopLeft = new Rectangle(topleft.X, topleft.Y, Center.X - topleft.X, Center.Y - topleft.Y);
Rectangle NewTopRight = new Rectangle(Center.X, topright.Y, topright.X - Center.X, Center.Y - topright.Y);
Rectangle NewBottomLeft = new Rectangle(bottomleft.X, Center.Y, Center.X - bottomleft.X, bottomleft.Y - Center.Y);
Rectangle NewBottomRight = new Rectangle(Center.X , Center.Y, bottomright.X - Center.X, bottomright.Y - Center.Y);
MidPointDisplace(new Vector2_I(NewTopLeft.Left, NewTopLeft.Top), new Vector2_I(NewTopLeft.Right, NewTopLeft.Top), new Vector2_I(NewTopLeft.Left, NewTopLeft.Bottom), new Vector2_I(NewTopLeft.Right, NewTopLeft.Bottom));
MidPointDisplace(new Vector2_I(NewTopRight.Left, NewTopRight.Top), new Vector2_I(NewTopRight.Right, NewTopRight.Top), new Vector2_I(NewTopRight.Left, NewTopRight.Bottom), new Vector2_I(NewTopRight.Right, NewTopRight.Bottom));
MidPointDisplace(new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Bottom), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Bottom));
MidPointDisplace(new Vector2_I(NewBottomRight.Left, NewBottomRight.Top), new Vector2_I(NewBottomRight.Right, NewBottomRight.Top), new Vector2_I(NewBottomRight.Left, NewBottomRight.Bottom), new Vector2_I(NewBottomRight.Right, NewBottomRight.Bottom));
}
//helper function to return a data value adveraged from two inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData(float DataA, float DataB, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB) / 2.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.0f;
}
//helper function to return a data value adveraged from four inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData2(float DataA, float DataB, float DataC, float DataD, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB + DataC + DataD) / 4.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.5f;
}
private float NoiseFunction(int x, int y)
{
return (float)(RandomGenerator.NextDouble() - 0.5) * 0.5f;
}
Ok thanks for taking the time to look - hopefully someone knows where this grid-like pattern is appearing from :)
*edit - accidently wrote ints, corrected to floats
I identified 3 problems in your code. (2 of which are related)
You don't scale down the randomness in each step. There must be a reduction of the randomness in each step. Otherwise you get white(-ish) noise. You choose a factor (0.5-0.7 worked fine for my purposes) and multiply the reduction by alpha in each recursion and scale the generated random number by that factor.
You swapped the diamond and square step. First the diamonds, then the squares. The other way round is impossible (see next).
Your square step uses only points in one direction. This one probably causes the rectangular structures you are talking about. The squares must average the values to all four sides. This means that the square step depends on the point generated by the diamond step. And not only the diamond step of the rectangle you are currently looking at, also of the rectangles next to it. For values outside of the map, you can either wrap, use a fixed value or only average 3 values.
I see a problem in your CalculateTerrainPointData implementation: you're not scaling down the result of NoiseFunction with each iteration.
See this description of the Midpoint Displacement algorithm:
Start with a single horizontal line segment.
Repeat for a sufficiently large number of times:
Repeat over each line segment in the scene:
Find the midpoint of the line segment.
Displace the midpoint in Y by a random amount.
Reduce the range for random numbers.
A fast way to do it in your code without changing too much is by adding some scale parameter to MidPointDisplace (with default set to 1.0f) and CalculateTerrainPointData; use it in CalculateTerrainPointData to multiply result of NoiseFunction; and reduce it with each recursive call to MidPointDisplace(..., 0.5f * scale).
Not sure though if that is the only cause to your image looking wrong or there are other problems.
According to Wikipedia's summary of midpoint displacement, only the average for the center most point get noise added to it - try only adding noise via CalculateTerrainPointData2 & removing the noise in CalculateTerrainPointData.
I'm currently trying to understand per pixel collision detection.
This is the code I don't understand:
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) +
(y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
(y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
I really haven't understood the all loop. I'll be glad for some explanation how it works.
First up, it finds the region the two image rectangles intersect, then it iterates through each pixel in that region, and compares the alpha values of each image of each pixel. If neither has an alpha value of 0, they are both considered 'solid' and therefore colliding.
it's not that hard (in this case) - you give the algorithm the two bounding-boxes of your objects (so the hole object is inside this box), and a array with color-information for them.
Tha algorithm assumes that a point belongs to the object IFF it is not transparent - this is important.
The first step is to calculate the intersecting rectangle - if you intersect two rectangles that have sides parallel to the axes like in this case - you will get a rectangle again or an empty set.
The next step is to iterate in this intersecting rectangle for all (x,y) -coordinates insiede - first y, then x -so you get your normal first x, then y inside, but this is minor point and not important.
Then finally the algorithm gets the color for object A and B at the current pixel (x,y) - if both colors are NOT transparent then the pixel is in both objects and the objects have to intersect at this point - so the algorithm terminates with "YES they intersect"
If all pixels in the intersection of the bounding boxes where checked and no common (e.g. not transparent) pixel was found the object don't intersect and so the algorithm terminates with "NO they don't intersect"
I hope this helps.
for (int y = top; y < bottom; y++) loops over the lines of the resulting rectangle from top to bottom, and for (int x = left; x < right; x++) loops over pixels inside each line, left to right.