mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-14 10:05:44 -04:00
Seed each noise instance, more work on singleplayer generator.
This commit is contained in:
parent
0ab941c787
commit
3b90136e40
@ -4,7 +4,7 @@
|
||||
<ProjectGuid>{BEB1C785-5CAD-48FF-A886-876BF0A318D4}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>ClassicalSharp</RootNamespace>
|
||||
<AssemblyName>ClassicalSharp</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
@ -143,6 +143,7 @@
|
||||
<Compile Include="Game\Game.Properties.cs" />
|
||||
<Compile Include="Generator\Noise.cs" />
|
||||
<Compile Include="Generator\NotchyGenerator.cs" />
|
||||
<Compile Include="Generator\NotchyGenerator.Utils.cs" />
|
||||
<Compile Include="Particles\CollidableParticle.cs" />
|
||||
<Compile Include="Particles\Particle.cs" />
|
||||
<Compile Include="Particles\ParticleManager.cs" />
|
||||
|
@ -6,7 +6,6 @@ using System;
|
||||
|
||||
namespace ClassicalSharp.Generator {
|
||||
|
||||
// TODO:L calculate based on seed
|
||||
public abstract class Noise {
|
||||
|
||||
public abstract double Compute( double x, double y );
|
||||
@ -14,13 +13,20 @@ namespace ClassicalSharp.Generator {
|
||||
|
||||
public sealed class ImprovedNoise : Noise {
|
||||
|
||||
public ImprovedNoise( Random rnd ) {
|
||||
// make a random initial permutation based on seed,
|
||||
// instead of using fixed permutation table in original code.
|
||||
for( int i = 0; i < 256; i++ )
|
||||
p[i + 256] = p[i] = rnd.Next( 256 );
|
||||
}
|
||||
|
||||
// TODO: need to half this maybe?
|
||||
public override double Compute( double x, double y ) {
|
||||
int xFloor = Utils.Floor( x ), yFloor = Utils.Floor( y );
|
||||
// Find unit rectangle that contains point
|
||||
int X = (int)Math.Floor( x ) & 255;
|
||||
int Y = (int)Math.Floor( y ) & 255;
|
||||
int X = xFloor & 0xFF, Y = yFloor & 0xFF;
|
||||
// Find relative x, y of each point in rectangle.
|
||||
x -= Math.Floor( x );
|
||||
y -= Math.Floor( y );
|
||||
x -= Math.Floor( x ); y -= Math.Floor( y );
|
||||
|
||||
// Compute fade curves for each of x, y.
|
||||
double u = Fade( x ), v = Fade( y );
|
||||
@ -54,43 +60,26 @@ namespace ClassicalSharp.Generator {
|
||||
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||
}
|
||||
|
||||
static int[] p = new int[512];
|
||||
static int[] permutation = { 151,160,137,91,90,15,
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
};
|
||||
|
||||
static ImprovedNoise() {
|
||||
for( int i = 0; i < 256; i++ )
|
||||
p[256+i] = p[i] = permutation[i];
|
||||
}
|
||||
int[] p = new int[512];
|
||||
}
|
||||
|
||||
|
||||
public sealed class OctaveNoise : Noise {
|
||||
|
||||
readonly int octaves;
|
||||
readonly ImprovedNoise baseNoise;
|
||||
public OctaveNoise( int octaves ) {
|
||||
readonly ImprovedNoise[] baseNoise;
|
||||
public OctaveNoise( int octaves, Random rnd ) {
|
||||
this.octaves = octaves;
|
||||
baseNoise = new ImprovedNoise();
|
||||
baseNoise = new ImprovedNoise[octaves];
|
||||
for( int i = 0; i < octaves; i++ )
|
||||
baseNoise[i] = new ImprovedNoise( rnd );
|
||||
}
|
||||
|
||||
public override double Compute( double x, double y ) {
|
||||
double amplitude = 1, frequency = 1;
|
||||
double sum = 0;
|
||||
for( int i = 0; i < octaves; i++ ) {
|
||||
sum += baseNoise.Compute( x * frequency, y * frequency ) * amplitude;
|
||||
for( int i = 0; i < baseNoise.Length; i++ ) {
|
||||
sum += baseNoise[i].Compute( x * frequency, y * frequency ) * amplitude;
|
||||
amplitude *= 2;
|
||||
frequency /= 2;
|
||||
}
|
||||
|
75
ClassicalSharp/Generator/NotchyGenerator.Utils.cs
Normal file
75
ClassicalSharp/Generator/NotchyGenerator.Utils.cs
Normal file
@ -0,0 +1,75 @@
|
||||
// Based on:
|
||||
// https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-map-generation-algorithm
|
||||
// Thanks to Jerralish for originally reverse engineering classic's algorithm, then preparing a high level overview of the algorithm.
|
||||
// I believe this process adheres to clean room reverse engineering.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ClassicalSharp.Generator {
|
||||
|
||||
// TODO: figure out how noise functions differ, probably based on rnd.
|
||||
public sealed partial class NotchyGenerator {
|
||||
|
||||
void FillOblateSpheroid( int x, int y, int z, int radius, byte block ) {
|
||||
int xStart = Math.Max( x - radius, 0 ), xEnd = Math.Min( x + radius, width - 1 );
|
||||
int yStart = Math.Max( y - radius, 0 ), yEnd = Math.Min( y + radius, height - 1 );
|
||||
int zStart = Math.Max( z - radius, 0 ), zEnd = Math.Min( z + radius, length - 1 );
|
||||
int radiusSq = radius * radius;
|
||||
|
||||
for( int yy = yStart; yy <= yEnd; yy++ )
|
||||
for( int zz = zStart; zz <= zEnd; zz++ )
|
||||
for( int xx = xStart; xx <= xEnd; xx++ )
|
||||
{
|
||||
int dx = xx - x, dy = yy - y, dz = zz - z;
|
||||
if( (dx * dx + 2 * dy * dy + dz * dz) < radiusSq ) {
|
||||
int index = (yy * length + zz) * width + xx;
|
||||
if( blocks[index] == (byte)Block.Stone )
|
||||
blocks[index] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FloodFill( int startIndex, byte block ) {
|
||||
FastIntStack stack = new FastIntStack( 4 );
|
||||
stack.Push( startIndex );
|
||||
while( stack.Size > 0 ) {
|
||||
int index = stack.Pop();
|
||||
if( blocks[index] == 0 ) {
|
||||
blocks[index] = block;
|
||||
|
||||
int x = index % width;
|
||||
int y = index / oneY;
|
||||
int z = (index / width) % length;
|
||||
if( x > 0 ) stack.Push( index - 1 );
|
||||
if( x < width - 1 ) stack.Push( index + 1 );
|
||||
if( z > 0 ) stack.Push( index - width );
|
||||
if( z < length - 1 ) stack.Push( index + width );
|
||||
if( y > 0 ) stack.Push( index - oneY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class FastIntStack {
|
||||
public int[] Values;
|
||||
public int Size;
|
||||
|
||||
public FastIntStack( int capacity ) {
|
||||
Values = new int[capacity];
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
public int Pop() {
|
||||
return Values[--Size];
|
||||
}
|
||||
|
||||
public void Push( int item ) {
|
||||
if( Size == Values.Length ) {
|
||||
int[] array = new int[Values.Length * 2];
|
||||
Buffer.BlockCopy( Values, 0, array, 0, Size * sizeof(int) );
|
||||
Values = array;
|
||||
}
|
||||
Values[Size++] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,37 +3,49 @@
|
||||
// Thanks to Jerralish for originally reverse engineering classic's algorithm, then preparing a high level overview of the algorithm.
|
||||
// I believe this process adheres to clean room reverse engineering.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ClassicalSharp.Generator {
|
||||
|
||||
// TODO: figure out how noise functions differ, probably based on rnd.
|
||||
public class NotchyGenerator {
|
||||
public sealed partial class NotchyGenerator {
|
||||
|
||||
int width, height, length;
|
||||
int waterLevel;
|
||||
int waterLevel, oneY;
|
||||
byte[] blocks;
|
||||
short[] heightmap;
|
||||
Random rnd;
|
||||
|
||||
public byte[] GenerateMap( int width, int height, int length ) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.length = length;
|
||||
oneY = width * length;
|
||||
waterLevel = height / 2;
|
||||
blocks = new byte[width * height * length];
|
||||
|
||||
rnd = new Random( 0x5553200 );
|
||||
CreateHeightmap();
|
||||
CreateStrata();
|
||||
return null;
|
||||
CarveCaves();
|
||||
CarveOreVeins( 0.9f, (byte)Block.CoalOre );
|
||||
CarveOreVeins( 0.7f, (byte)Block.IronOre );
|
||||
CarveOreVeins( 0.5f, (byte)Block.GoldOre );
|
||||
|
||||
FloodFillWaterBorders();
|
||||
FloodFillWater();
|
||||
FloodFillLava();
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void CreateHeightmap() {
|
||||
Noise n1 = new CombinedNoise(
|
||||
new OctaveNoise( 8 ), new OctaveNoise( 8 ) );
|
||||
new OctaveNoise( 8, rnd ), new OctaveNoise( 8, rnd ) );
|
||||
Noise n2 = new CombinedNoise(
|
||||
new OctaveNoise( 8 ), new OctaveNoise( 8 ) );
|
||||
Noise n3 = new OctaveNoise( 6 );
|
||||
new OctaveNoise( 8, rnd ), new OctaveNoise( 8, rnd ) );
|
||||
Noise n3 = new OctaveNoise( 6, rnd );
|
||||
int index = 0;
|
||||
short[] map = new short[width * length];
|
||||
short[] hMap = new short[width * length];
|
||||
|
||||
for( int z = 0; z < length; z++ ) {
|
||||
for( int x = 0; x < width; x++ ) {
|
||||
@ -42,28 +54,139 @@ namespace ClassicalSharp.Generator {
|
||||
|
||||
double height = n3.Compute( x, z ) > 0 ? hLow : Math.Max( hLow, hHigh );
|
||||
if( height < 0 ) height *= 0.8f;
|
||||
map[index++] = (short)(height + waterLevel);
|
||||
hMap[index++] = (short)(height + waterLevel);
|
||||
}
|
||||
}
|
||||
heightmap = map;
|
||||
heightmap = hMap;
|
||||
}
|
||||
|
||||
void CreateStrata() {
|
||||
Noise n = new OctaveNoise( 8 );
|
||||
Noise n = new OctaveNoise( 8, rnd );
|
||||
//Noise n = new ImprovedNoise( rnd );
|
||||
|
||||
int mapIndex = 0;
|
||||
int hMapIndex = 0;
|
||||
for( int z = 0; z < length; z++ ) {
|
||||
for( int x = 0; x < width; x++ ) {
|
||||
int dirtThickness = (int)(n.Compute( x, z ) / 24 - 4);
|
||||
int dirtHeight = heightmap[mapIndex];
|
||||
int dirtHeight = heightmap[hMapIndex++];
|
||||
|
||||
int stoneHeight = dirtHeight + dirtThickness;
|
||||
int mapIndex = z * width + x;
|
||||
|
||||
for( int y = 0; y < height; y++ ) {
|
||||
|
||||
blocks[mapIndex] = (byte)Block.Lava;
|
||||
mapIndex += oneY;
|
||||
for( int y = 1; y < height; y++ ) {
|
||||
byte type = 0;
|
||||
if( y <= stoneHeight ) type = (byte)Block.Stone;
|
||||
else if( y <= dirtHeight ) type = (byte)Block.Dirt;
|
||||
blocks[mapIndex] = type;
|
||||
mapIndex += oneY;
|
||||
}
|
||||
mapIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CarveCaves() {
|
||||
int cavesCount = blocks.Length / 8192;
|
||||
for( int i = 0; i < cavesCount; i++ ) {
|
||||
double caveX = rnd.Next( width );
|
||||
double caveY = rnd.Next( height );
|
||||
double caveZ = rnd.Next( length );
|
||||
|
||||
int caveLen = (int)(rnd.NextDouble() * rnd.NextDouble() * 200);
|
||||
double theta = rnd.NextDouble() * 2 * Math.PI, deltaTheta = 0;
|
||||
double phi = rnd.NextDouble() * 2 * Math.PI, deltaPhi = 0;
|
||||
double caveRadius = rnd.NextDouble() * rnd.NextDouble();
|
||||
|
||||
for( int j = 0; j < caveLen; j++ ) {
|
||||
caveX += Math.Sin( theta ) * Math.Cos( phi );
|
||||
caveY += Math.Cos( theta ) * Math.Cos( phi );
|
||||
caveZ += Math.Sin( phi );
|
||||
|
||||
theta = deltaTheta * 0.2;
|
||||
deltaTheta = deltaTheta * 0.9 + rnd.NextDouble() - rnd.NextDouble();
|
||||
phi = phi / 2 + deltaPhi / 4;
|
||||
deltaPhi = deltaPhi * 0.75 + rnd.NextDouble() - rnd.NextDouble();
|
||||
if( rnd.NextDouble() < 0.25 ) continue;
|
||||
|
||||
int cenX = (int)(caveX + (rnd.Next( 4 ) - 2) * 0.2);
|
||||
int cenY = (int)(caveY + (rnd.Next( 4 ) - 2) * 0.2);
|
||||
int cenZ = (int)(caveZ + (rnd.Next( 4 ) - 2) * 0.2);
|
||||
double radius = (height - cenY) / (double)height;
|
||||
radius = 1.2 + (radius * 3.5 + 1) * caveRadius;
|
||||
radius = radius + Math.Sin( j * Math.PI / caveLen );
|
||||
FillOblateSpheroid( cenX, cenY, cenZ, (int)radius, (byte)Block.Air );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CarveOreVeins( float abundance, byte block ) {
|
||||
int numVeins = (int)(blocks.Length * abundance / 16384);
|
||||
for( int i = 0; i < numVeins; i++ ) {
|
||||
double veinX = rnd.Next( width );
|
||||
double veinY = rnd.Next( height );
|
||||
double veinZ = rnd.Next( length );
|
||||
|
||||
int veinLen = (int)(rnd.NextDouble() * rnd.NextDouble() * 75 * abundance);
|
||||
double theta = rnd.NextDouble() * 2 * Math.PI, deltaTheta = 0;
|
||||
double phi = rnd.NextDouble() * 2 * Math.PI, deltaPhi = 0;
|
||||
|
||||
for( int j = 0; j < veinLen; j++ ) {
|
||||
veinX += Math.Sin( theta ) * Math.Cos( phi );
|
||||
veinY += Math.Cos( theta ) * Math.Cos( phi );
|
||||
veinZ += Math.Sin( phi );
|
||||
|
||||
theta = deltaTheta * 0.2;
|
||||
deltaTheta = deltaTheta * 0.9 + rnd.NextDouble() - rnd.NextDouble();
|
||||
phi = phi / 2 + deltaPhi / 4;
|
||||
deltaPhi = deltaPhi * 0.9 + rnd.NextDouble() - rnd.NextDouble();
|
||||
|
||||
int radius = (int)(abundance * Math.Sin( j * Math.PI / veinLen ) + 1);
|
||||
FillOblateSpheroid( (int)veinX, (int)veinY, (int)veinZ, radius, block );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FloodFillWaterBorders() {
|
||||
int waterY = waterLevel - 1;
|
||||
int index1 = (waterY * length + 0) * width + 0;
|
||||
int index2 = (waterY * length + (length - 1)) * width + 0;
|
||||
for( int x = 0; x < width; x++ ) {
|
||||
FloodFill( index1, (byte)Block.Water );
|
||||
FloodFill( index2, (byte)Block.Water );
|
||||
index1++; index2++;
|
||||
}
|
||||
|
||||
index1 = (waterY * length + 0) * width + 0;
|
||||
index2 = (waterY * length + 0) * width + (width - 1);
|
||||
for( int z = 0; z < length; z++ ) {
|
||||
FloodFill( index1, (byte)Block.Water );
|
||||
FloodFill( index2, (byte)Block.Water );
|
||||
index1 += width; index2 += width;
|
||||
}
|
||||
}
|
||||
|
||||
void FloodFillWater() {
|
||||
int numSources = width * length / 800;
|
||||
for( int i = 0; i < numSources; i++ ) {
|
||||
int x = rnd.Next( width ), z = rnd.Next( length );
|
||||
int y = waterLevel - rnd.Next( 1, 3 );
|
||||
FloodFill( (y * length + z) * width + x, (byte)Block.Water );
|
||||
}
|
||||
}
|
||||
|
||||
void FloodFillLava() {
|
||||
int numSources = width * length / 20000;
|
||||
for( int i = 0; i < numSources; i++ ) {
|
||||
int x = rnd.Next( width ), z = rnd.Next( length );
|
||||
int y = (int)((waterLevel - 3) * rnd.NextDouble() * rnd.NextDouble());
|
||||
FloodFill( (y * length + z) * width + x, (byte)Block.Lava );
|
||||
}
|
||||
}
|
||||
|
||||
void CreateSurfaceLayer() {
|
||||
Noise n1 = new OctaveNoise( 8, rnd ), n2 = new OctaveNoise( 8, rnd );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,8 @@ namespace ClassicalSharp.Singleplayer {
|
||||
|
||||
game.Events.RaiseBlockPermissionsChanged();
|
||||
NewMap();
|
||||
MakeMap( 128, 128, 128 );
|
||||
//MakeMap( 128, 128, 128 );
|
||||
MakeMap( 128, 64, 128 );
|
||||
game.CommandManager.RegisterCommand( new GenerateCommand() );
|
||||
}
|
||||
|
||||
@ -68,12 +69,14 @@ namespace ClassicalSharp.Singleplayer {
|
||||
}
|
||||
|
||||
internal unsafe void MakeMap( int width, int height, int length ) {
|
||||
byte[] map = new byte[width * height * length];
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
fixed( byte* ptr = map ) {
|
||||
MapSet( width, length, ptr, 0, height / 2 - 2, (byte)Block.Dirt );
|
||||
MapSet( width, length, ptr, height / 2 - 1, height / 2 - 1, (byte)Block.Grass );
|
||||
}
|
||||
//byte[] map = new byte[width * height * length];
|
||||
//var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
//fixed( byte* ptr = map ) {
|
||||
// MapSet( width, length, ptr, 0, height / 2 - 2, (byte)Block.Dirt );
|
||||
// MapSet( width, length, ptr, height / 2 - 1, height / 2 - 1, (byte)Block.Grass );
|
||||
//}
|
||||
byte[] map = new ClassicalSharp.Generator.NotchyGenerator()
|
||||
.GenerateMap( width, height, length );
|
||||
game.Map.SetData( map, width, height, length );
|
||||
game.Events.RaiseOnNewMapLoaded();
|
||||
game.SetNewScreen( null );
|
||||
|
@ -242,6 +242,10 @@ namespace ClassicalSharp {
|
||||
return value >= 0 ? (int)value : (int)value - 1;
|
||||
}
|
||||
|
||||
public static int Floor( double value ) {
|
||||
return value >= 0 ? (int)value : (int)value - 1;
|
||||
}
|
||||
|
||||
public static int AdjViewDist( int value ) {
|
||||
return (int)(1.4142135 * value);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user