ClassiCube/ClassicalSharp/Generator/NotchyGenerator.Utils.cs
2016-11-27 14:47:09 +11:00

114 lines
3.2 KiB
C#

// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
// 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 {
public sealed partial class NotchyGenerator {
void FillOblateSpheroid(int x, int y, int z, float radius, byte block) {
int xStart = Utils.Floor(Math.Max(x - radius, 0));
int xEnd = Utils.Floor(Math.Min(x + radius, width - 1));
int yStart = Utils.Floor(Math.Max(y - radius, 0));
int yEnd = Utils.Floor(Math.Min(y + radius, height - 1));
int zStart = Utils.Floor(Math.Max(z - radius, 0));
int zEnd = Utils.Floor(Math.Min(z + radius, length - 1));
float 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] == 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) continue;
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;
}
}
}
// Based on https://docs.oracle.com/javase/7/docs/api/java/util/Random.html
public sealed class JavaRandom {
long seed;
const long value = 0x5DEECE66DL;
const long mask = (1L << 48) - 1;
public JavaRandom(int seed) {
this.seed = (seed ^ value) & mask;
}
int Raw(int bits) {
seed = (seed * value + 0xBL) & mask;
return (int)((ulong)seed >> (48 - bits));
}
public int Next() { return Raw(32); }
public int Next(int min, int max) { return min + Next(max - min); }
public int Next(int n) {
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)Raw(31)) >> 31);
int bits, val;
do {
bits = Raw(31);
val = bits % n;
} while (bits - val + (n - 1) < 0);
return val;
}
public float NextFloat() {
return Raw(24) / ((float)(1 << 24));
}
}
}