diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/AbstractPerlin.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/AbstractPerlin.kt index d98a469..a83e9de 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/AbstractPerlin.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/AbstractPerlin.kt @@ -5,12 +5,10 @@ import io.github.addoncommunity.galactifun.scripting.RequiredProperty @PlanetDsl abstract class AbstractPerlin : GeneratorBuilder() { - - var config: PerlinConfig by RequiredProperty() - var generateBedrock = true var averageHeight: Int by RequiredProperty() + var maxDeviation: Int by RequiredProperty() var minHeight: Int by RequiredProperty() var surfaceHeight = 10 @@ -21,10 +19,5 @@ abstract class AbstractPerlin : GeneratorBuilder() { var frequency: Double by RequiredProperty() var flattenFactor: Double = 1.0 - var smoothen: Boolean = false } -} - -inline fun AbstractPerlin.noiseConfig(block: AbstractPerlin.PerlinConfig.() -> Unit) { - config = AbstractPerlin.PerlinConfig().apply(block) } \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/NoiseMap.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/NoiseMap.kt new file mode 100644 index 0000000..b9e7485 --- /dev/null +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/NoiseMap.kt @@ -0,0 +1,48 @@ +package io.github.addoncommunity.galactifun.scripting.dsl.gen + +import io.github.addoncommunity.galactifun.util.gen.DoubleChunkGrid +import org.bukkit.util.noise.OctaveGenerator +import org.bukkit.util.noise.SimplexOctaveGenerator +import kotlin.math.pow + +class NoiseMap(noises: Map) { + + private val noises = noises.mapValues { Noise(it.value) } + + @Volatile + private var initted = false + + internal fun init(seed: Long) { + if (initted) return + synchronized(this) { + if (initted) return + noises.values.forEach { it.init(seed) } + initted = true + } + } + + class Noise internal constructor(config: AbstractPerlin.PerlinConfig) { + + private val octaves = config.octaves + private val scale = config.scale + private val amplitude = config.amplitude + private val frequency = config.frequency + private val flattenFactor = config.flattenFactor + + @Volatile + private lateinit var noise: OctaveGenerator + + private val grid = DoubleChunkGrid() + + internal fun init(seed: Long) { + noise = SimplexOctaveGenerator(seed, octaves) + noise.setScale(scale) + } + + operator fun invoke(x: Int, z: Int): Double { + return grid.getOrSet(x, z) { + noise.noise(x.toDouble(), z.toDouble(), frequency, amplitude, true).pow(flattenFactor) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/PerlinBuilder.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/PerlinBuilder.kt index d080fd3..03f663c 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/PerlinBuilder.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/scripting/dsl/gen/PerlinBuilder.kt @@ -1,38 +1,29 @@ package io.github.addoncommunity.galactifun.scripting.dsl.gen import io.github.addoncommunity.galactifun.api.objects.planet.gen.WorldGenerator +import io.github.addoncommunity.galactifun.scripting.RequiredProperty import org.bukkit.Material import org.bukkit.generator.WorldInfo -import org.bukkit.util.noise.SimplexOctaveGenerator import java.util.* -import kotlin.math.pow +import kotlin.math.roundToInt class PerlinBuilder : AbstractPerlin() { - var noiseGenerator: (WorldInfo, Random, Int, Int, Int, Int) -> Material = - { _, _, _, _, _, _ -> Material.AIR } - - var surfaceGenerator: (WorldInfo, Random, Int, Int, Int, Int) -> Material = - { _, _, _, _, _, _ -> Material.AIR } + var noiseGenerator: GenInfo.() -> Material by RequiredProperty() + var noiseCombiner: NoiseInfo.() -> Double by RequiredProperty() + val noises = mutableMapOf() override fun build(): WorldGenerator { - // Prevent performance issues by unboxing before building the generator - val octaves = config.octaves - val scale = config.scale - val amplitude = config.amplitude - val frequency = config.frequency - val flattenFactor = config.flattenFactor - val smoothen = config.smoothen - val averageHeight = averageHeight + val maxDeviation = maxDeviation val minHeight = minHeight + if (generateBedrock) 1 else 0 + val noises = NoiseMap(noises) + return object : WorldGenerator() { - override val biomeProvider = this@PerlinBuilder.biomeProvider - @Volatile - private lateinit var baseNoise: SimplexOctaveGenerator + override val biomeProvider = this@PerlinBuilder.biomeProvider fun getMinHeight(worldInfo: WorldInfo): Int = minHeight.coerceAtLeast(worldInfo.minHeight) @@ -46,11 +37,18 @@ class PerlinBuilder : AbstractPerlin() { val cx = chunkX * 16 val cz = chunkZ * 16 val min = getMinHeight(worldInfo) + val info = GenInfo(worldInfo, random, chunkX, chunkZ) for (x in 0..15) { for (z in 0..15) { - val height = getHeight(worldInfo, cx + x, cz + z) + val realX = cx + x + val realZ = cz + z + val height = getHeight(worldInfo, random, realX, realZ) + info.x = realX + info.z = realZ + info.height = height for (y in min until height - surfaceHeight) { - chunkData.setBlock(x, y, z, noiseGenerator(worldInfo, random, cx + x, y, cz + z, height)) + info.y = y + chunkData.setBlock(x, y, z, noiseGenerator(info)) } } } @@ -65,11 +63,18 @@ class PerlinBuilder : AbstractPerlin() { ) { val cx = chunkX * 16 val cz = chunkZ * 16 + val info = GenInfo(worldInfo, random, chunkX, chunkZ) for (x in 0..15) { for (z in 0..15) { - val height = getHeight(worldInfo, cx + x, cz + z) + val realX = cx + x + val realZ = cz + z + val height = getHeight(worldInfo, random, realX, realZ) + info.x = realX + info.z = realZ + info.height = height for (y in height - surfaceHeight until height) { - chunkData.setBlock(x, y, z, surfaceGenerator(worldInfo, random, cx + x, y, cz + z, height)) + info.y = y + chunkData.setBlock(x, y, z, noiseGenerator(info)) } } } @@ -91,32 +96,41 @@ class PerlinBuilder : AbstractPerlin() { } } - private fun getHeight(worldInfo: WorldInfo, x: Int, z: Int): Int { - if (!::baseNoise.isInitialized) { - baseNoise = SimplexOctaveGenerator(worldInfo.seed, octaves) - baseNoise.setScale(scale) - } - var height = baseNoise.noise(x.toDouble(), z.toDouble(), frequency, amplitude, true) - height = height.pow(flattenFactor) - height = (height + 1) / 2 - return (height * (averageHeight - minHeight)).toInt() + private fun getHeight(worldInfo: WorldInfo, random: Random, x: Int, z: Int): Int { + noises.init(worldInfo.seed) + val height = noiseCombiner(NoiseInfo(worldInfo, noises, random, x, z)) + return (height * maxDeviation + averageHeight).roundToInt() } } } + + data class GenInfo( + val world: WorldInfo, + val random: Random, + val chunkX: Int, + val chunkZ: Int + ) { + var x: Int = 0 + var y: Int = 0 + var z: Int = 0 + var height: Int = 0 + } + + data class NoiseInfo(val world: WorldInfo, val noise: NoiseMap, val random: Random, val x: Int, val z: Int) } -fun PerlinBuilder.generateNoiseBlock( - block: (WorldInfo, Random, Int, Int, Int, Int) -> Material -) { +fun PerlinBuilder.generateBlock(block: PerlinBuilder.GenInfo.() -> Material) { noiseGenerator = block } -fun PerlinBuilder.generateSurfaceBlock( - block: (WorldInfo, Random, Int, Int, Int, Int) -> Material -) { - surfaceGenerator = block +fun PerlinBuilder.combineNoise(noise: PerlinBuilder.NoiseInfo.() -> Double) { + noiseCombiner = noise +} + +fun PerlinBuilder.noiseConfig(name: String, config: AbstractPerlin.PerlinConfig.() -> Unit) { + noises[name] = AbstractPerlin.PerlinConfig().apply(config) } -object Perlin : GeneratorBuilderProvider { +object MultiNoise : GeneratorBuilderProvider { override fun provide() = PerlinBuilder() } \ No newline at end of file