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 { 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 } 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) @Test(enabled = false)
fun testBiomeNoise1() { fun testBiomeNoise1() {
assertEquals(calculate(129, 3274, 91, 1823123L), Vec3i(32, 818, 22)) 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)) assertEquals(calculate(0, 3, 1, -33135639), Vec3i(0, 0, 0))
} }
@Test(enabled = false)
fun testBiomeNoise6() { fun testBiomeNoise6() {
assertEquals(calculate(16, 15, -16, 561363374), Vec3i(4, 3, -4)) assertEquals(calculate(16, 15, -16, 561363374), Vec3i(4, 3, -4))
} }
@Test(enabled = false)
fun testBiomeNoise7() { fun testBiomeNoise7() {
assertEquals(calculate(16, -15, -16, 79707367), Vec3i(4, -4, -5)) 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 { private fun calculate(x: Int, y: Int, z: Int, seed: Long): Vec3i {
val accessor = VoronoiBiomeAccessor::class.java.allocate() val accessor = VoronoiBiomeAccessor::class.java.allocate()
val index = getBiomeOffset.invoke(accessor, seed, x, y, z) as Int 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.kutil.math.simple.DoubleMath.square
import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.World 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 import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
class VoronoiBiomeAccessor( class VoronoiBiomeAccessor(
@ -41,61 +42,65 @@ class VoronoiBiomeAccessor(
} }
private fun getBiomeOffset(seed: Long, x: Int, y: Int, z: Int): Int { private fun getBiomeOffset(seed: Long, x: Int, y: Int, z: Int): Int {
val m = x - 2 // all xyz coordinates are from 0..15
val n = y - 2
val o = z - 2
val p = m shr 2 // target biome can also be on the negative side, offset by -2
val q = n shr 2 val cX = x - 2
val r = o shr 2 val cY = y - 2
val cZ = z - 2
val d = (m and 0x03) / 4.0 // array source
val e = (n and 0x03) / 4.0 val sX = cX shr 2
val f = (o and 0x03) / 4.0 val sY = cY shr 2
val sZ = cZ shr 2
var s = 0 // in array
var g = Double.POSITIVE_INFINITY 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 minXYZ = 0
var u = p var minDistance = Double.POSITIVE_INFINITY
var xFraction = d
if (i and 0x04 != 0) { for (xyz in 0 until 2 * 2 * 2) {
u++ var uX = sX
xFraction -= 1.0 var offsetX = iX
if (xyz and 0x04 != 0) {
uX++
offsetX -= 1.0
} }
var v = q var uY = sY
var yFraction = e var offsetY = iY
if (i and 0x02 != 0) { if (xyz and 0x02 != 0) {
v++ uY++
yFraction -= 1.0 offsetY -= 1.0
} }
var w = r var uZ = sZ
var zFraction = f var offsetZ = iZ
if (i and 0x01 != 0) { if (xyz and 0x01 != 0) {
w++ uZ++
zFraction -= 1.0 offsetZ -= 1.0
} }
val d3 = calculateFiddle(seed, u, v, w, xFraction, yFraction, zFraction) val distance = noiseDistance(seed, uX, uY, uZ, offsetX, offsetY, offsetZ)
if (g > d3) { if (distance > minDistance) continue
s = i minXYZ = xyz
g = d3 minDistance = distance
}
} }
var biomeX = p var biomeX = sX
if (s and 0x04 != 0) { if (minXYZ and 0x04 != 0) {
biomeX++ biomeX++
} }
var biomeY = q var biomeY = sY
if (s and 0x02 != 0) { if (minXYZ and 0x02 != 0) {
biomeY++ biomeY++
} }
var biomeZ = r var biomeZ = sZ
if (s and 0x01 != 0) { if (minXYZ and 0x01 != 0) {
biomeZ++ 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 { private fun noiseDistance(seed: Long, x: Int, y: Int, z: Int, offsetX: Double, offsetY: Double, offsetZ: Double): Double {
var ret = seed var ret = mix(seed, x, y, z)
ret = next(ret, x) val noiseX = nextNoiseOffset(ret); ret = next(ret, seed)
ret = next(ret, y) val noiseY = nextNoiseOffset(ret); ret = next(ret, seed)
ret = next(ret, z) val noiseZ = nextNoiseOffset(ret)
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
val xSalt = distribute(ret) return (offsetX + noiseX).square() + (offsetY + noiseY).square() + (offsetZ + noiseZ).square()
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()
} }
private fun distribute(seed: Long): Double { private fun mix(seed: Long, x: Int, y: Int, z: Int): Long {
val d = Math.floorMod(seed shr 24, 1024L).toInt() / 1024.0 var mixed = seed
return (d - 0.5) * 0.9 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 // https://en.wikipedia.org/wiki/Linear_congruential_generator