refactor and document more of the veronoi noise

This commit is contained in:
Moritz Zwerger 2023-12-07 18:59:04 +01:00
parent 1fab559a68
commit 255e14cd82
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
2 changed files with 104 additions and 61 deletions

View File

@ -23,7 +23,7 @@ import org.testng.annotations.Test
class VoronoiBiomeAccessorTest {
private val getBiomeOffset = VoronoiBiomeAccessor::class.java.getDeclaredMethod("getBiomeOffset", Long::class.java, Int::class.java, Int::class.java, Int::class.java).apply { isAccessible = true }
// TODO: those values are too far off. They match vanilla, yes, but I am still not going to allow that. The noise should be fairly smooth around the data
// Those values are actually undefined, the getBiomeOffset method only allows values from 0 to 15
@Test(enabled = false)
fun testBiomeNoise1() {
assertEquals(calculate(129, 3274, 91, 1823123L), Vec3i(32, 818, 22))
@ -48,14 +48,52 @@ class VoronoiBiomeAccessorTest {
assertEquals(calculate(0, 3, 1, -33135639), Vec3i(0, 0, 0))
}
@Test(enabled = false)
fun testBiomeNoise6() {
assertEquals(calculate(16, 15, -16, 561363374), Vec3i(4, 3, -4))
}
@Test(enabled = false)
fun testBiomeNoise7() {
assertEquals(calculate(16, -15, -16, 79707367), Vec3i(4, -4, -5))
}
fun `noise at (0,0,0) seed1`() {
assertEquals(calculate(0, 0, 0, -33135639), Vec3i(-1, 0, -1))
}
fun `noise at (0,0,0) seed2`() {
assertEquals(calculate(0, 0, 0, 1234567891234567891L), Vec3i(-1, 0, -1))
}
fun `noise at (0,0,0) seed3`() {
assertEquals(calculate(0, 0, 0, -987654321987654319L), Vec3i(-1, 0, 0))
}
fun `noise at (15,15,15) seed1`() {
assertEquals(calculate(15, 15, 15, -33135639), Vec3i(3, 3, 3))
}
fun `noise at (15,15,15) seed2`() {
assertEquals(calculate(15, 15, 15, 1234567891234567891L), Vec3i(3, 3, 3))
}
fun `noise at (15,15,15) seed3`() {
assertEquals(calculate(15, 15, 15, -987654321987654319L), Vec3i(3, 3, 3))
}
fun `noise at (8,15,4) seed1`() {
assertEquals(calculate(8, 15, 4, -33135639), Vec3i(1, 3, 0))
}
fun `noise at (8,15,4) seed2`() {
assertEquals(calculate(8, 15, 4, 1234567891234567891L), Vec3i(1, 3, 1))
}
fun `noise at (8,15,4) seed3`() {
assertEquals(calculate(8, 15, 4, -987654321987654319L), Vec3i(1, 4, 0))
}
private fun calculate(x: Int, y: Int, z: Int, seed: Long): Vec3i {
val accessor = VoronoiBiomeAccessor::class.java.allocate()
val index = getBiomeOffset.invoke(accessor, seed, x, y, z) as Int

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.world.biome.accessor.noise
import de.bixilon.kutil.math.simple.DoubleMath.square
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
class VoronoiBiomeAccessor(
@ -41,61 +42,65 @@ class VoronoiBiomeAccessor(
}
private fun getBiomeOffset(seed: Long, x: Int, y: Int, z: Int): Int {
val m = x - 2
val n = y - 2
val o = z - 2
// all xyz coordinates are from 0..15
val p = m shr 2
val q = n shr 2
val r = o shr 2
// target biome can also be on the negative side, offset by -2
val cX = x - 2
val cY = y - 2
val cZ = z - 2
val d = (m and 0x03) / 4.0
val e = (n and 0x03) / 4.0
val f = (o and 0x03) / 4.0
// array source
val sX = cX shr 2
val sY = cY shr 2
val sZ = cZ shr 2
var s = 0
var g = Double.POSITIVE_INFINITY
// in array
val iX = (cX and 0x03) / 4.0
val iY = (cY and 0x03) / 4.0
val iZ = (cZ and 0x03) / 4.0
for (i in 0 until 8) {
var u = p
var xFraction = d
if (i and 0x04 != 0) {
u++
xFraction -= 1.0
var minXYZ = 0
var minDistance = Double.POSITIVE_INFINITY
for (xyz in 0 until 2 * 2 * 2) {
var uX = sX
var offsetX = iX
if (xyz and 0x04 != 0) {
uX++
offsetX -= 1.0
}
var v = q
var yFraction = e
if (i and 0x02 != 0) {
v++
yFraction -= 1.0
var uY = sY
var offsetY = iY
if (xyz and 0x02 != 0) {
uY++
offsetY -= 1.0
}
var w = r
var zFraction = f
if (i and 0x01 != 0) {
w++
zFraction -= 1.0
var uZ = sZ
var offsetZ = iZ
if (xyz and 0x01 != 0) {
uZ++
offsetZ -= 1.0
}
val d3 = calculateFiddle(seed, u, v, w, xFraction, yFraction, zFraction)
if (g > d3) {
s = i
g = d3
}
val distance = noiseDistance(seed, uX, uY, uZ, offsetX, offsetY, offsetZ)
if (distance > minDistance) continue
minXYZ = xyz
minDistance = distance
}
var biomeX = p
if (s and 0x04 != 0) {
var biomeX = sX
if (minXYZ and 0x04 != 0) {
biomeX++
}
var biomeY = q
if (s and 0x02 != 0) {
var biomeY = sY
if (minXYZ and 0x02 != 0) {
biomeY++
}
var biomeZ = r
if (s and 0x01 != 0) {
var biomeZ = sZ
if (minXYZ and 0x01 != 0) {
biomeZ++
}
@ -103,32 +108,32 @@ class VoronoiBiomeAccessor(
}
private fun calculateFiddle(seed: Long, x: Int, y: Int, z: Int, xFraction: Double, yFraction: Double, zFraction: Double): Double {
var ret = seed
private fun noiseDistance(seed: Long, x: Int, y: Int, z: Int, offsetX: Double, offsetY: Double, offsetZ: Double): Double {
var ret = mix(seed, x, y, z)
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
val noiseX = nextNoiseOffset(ret); ret = next(ret, seed)
val noiseY = nextNoiseOffset(ret); ret = next(ret, seed)
val noiseZ = nextNoiseOffset(ret)
val xSalt = distribute(ret)
ret = next(ret, seed)
val ySalt = distribute(ret)
ret = next(ret, seed)
val zSalt = distribute(ret)
return (xFraction + xSalt).square() + (yFraction + ySalt).square() + (zFraction + zSalt).square()
return (offsetX + noiseX).square() + (offsetY + noiseY).square() + (offsetZ + noiseZ).square()
}
private fun distribute(seed: Long): Double {
val d = Math.floorMod(seed shr 24, 1024L).toInt() / 1024.0
return (d - 0.5) * 0.9
private fun mix(seed: Long, x: Int, y: Int, z: Int): Long {
var mixed = seed
mixed = next(mixed, x)
mixed = next(mixed, y)
mixed = next(mixed, z)
mixed = next(mixed, x)
mixed = next(mixed, y)
mixed = next(mixed, z)
return mixed
}
private fun nextNoiseOffset(seed: Long): Double {
val floor = Math.floorMod(seed shr 24, SpatialBiomeArray.SIZE.toLong()).toInt()
val double = (floor - (SpatialBiomeArray.SIZE / 2)) / SpatialBiomeArray.SIZE.toDouble()
return double * 0.9
}
// https://en.wikipedia.org/wiki/Linear_congruential_generator