mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
641 lines
24 KiB
Kotlin
641 lines
24 KiB
Kotlin
package net.torvald.terrarum.tests
|
|
|
|
import com.badlogic.gdx.ApplicationAdapter
|
|
import com.badlogic.gdx.Gdx
|
|
import com.badlogic.gdx.Input
|
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplication
|
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
|
|
import com.badlogic.gdx.graphics.Color
|
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
|
import com.badlogic.gdx.graphics.Pixmap
|
|
import com.badlogic.gdx.graphics.Texture
|
|
import com.badlogic.gdx.graphics.g2d.BitmapFont
|
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
|
import com.sudoplay.joise.Joise
|
|
import com.sudoplay.joise.module.*
|
|
import net.torvald.UnsafePtr
|
|
import net.torvald.random.HQRNG
|
|
import net.torvald.terrarum.concurrent.*
|
|
import net.torvald.terrarum.gameworld.fmod
|
|
import net.torvald.terrarum.inUse
|
|
import net.torvald.terrarum.modulebasegame.worldgenerator.BiomegenParams
|
|
import net.torvald.terrarum.modulebasegame.worldgenerator.TerragenParams
|
|
import net.torvald.terrarum.modulebasegame.worldgenerator.shake
|
|
import net.torvald.terrarum.worlddrawer.toRGBA
|
|
import kotlin.math.cos
|
|
import kotlin.math.sin
|
|
import kotlin.random.Random
|
|
import kotlinx.coroutines.*
|
|
|
|
const val WIDTH = 768
|
|
const val HEIGHT = 512
|
|
const val TWO_PI = Math.PI * 2
|
|
|
|
/**
|
|
* Created by minjaesong on 2019-07-23.
|
|
*/
|
|
class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|
|
|
private lateinit var batch: SpriteBatch
|
|
private lateinit var camera: OrthographicCamera
|
|
private lateinit var font: BitmapFont
|
|
|
|
private lateinit var testTex: Pixmap
|
|
private lateinit var tempTex: Texture
|
|
|
|
private val RNG = HQRNG()
|
|
private var seed = 10000L
|
|
|
|
private var initialGenDone = false
|
|
private var generateKeyLatched = false
|
|
|
|
private var generationTimeInMeasure = false
|
|
private var generationStartTime = 0L
|
|
|
|
override fun create() {
|
|
font = BitmapFont() // use default because fuck it
|
|
|
|
batch = SpriteBatch()
|
|
camera = OrthographicCamera(WIDTH.toFloat(), HEIGHT.toFloat())
|
|
camera.setToOrtho(false) // some elements are pre-flipped, while some are not. The statement itself is absolutely necessary to make edge of the screen as the origin
|
|
camera.update()
|
|
batch.projectionMatrix = camera.combined
|
|
Gdx.gl20.glViewport(0, 0, WIDTH, HEIGHT)
|
|
|
|
testTex = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
|
|
testTex.blending = Pixmap.Blending.None
|
|
tempTex = Texture(1, 1, Pixmap.Format.RGBA8888)
|
|
|
|
println("Init done")
|
|
}
|
|
|
|
private var generationTime = 0f
|
|
|
|
override fun render() {
|
|
if (!initialGenDone) {
|
|
renderNoise()
|
|
}
|
|
|
|
// draw using pixmap
|
|
batch.inUse {
|
|
tempTex.dispose()
|
|
tempTex = Texture(testTex)
|
|
batch.draw(tempTex, 0f, 0f)
|
|
}
|
|
|
|
// read key input
|
|
if (!generateKeyLatched && Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
|
|
generateKeyLatched = true
|
|
seed = RNG.nextLong()
|
|
renderNoise()
|
|
}
|
|
|
|
val coroutineExecFinished = coroutineJobs.fold(true) { acc, it -> acc and it.isCompleted }
|
|
// check if generation is done
|
|
if (coroutineExecFinished) {
|
|
generateKeyLatched = false
|
|
}
|
|
|
|
// finish time measurement
|
|
if (coroutineExecFinished && generationTimeInMeasure) {
|
|
generationTimeInMeasure = false
|
|
val time = System.nanoTime() - generationStartTime
|
|
generationTime = time / 1000000000f
|
|
}
|
|
|
|
// draw timer
|
|
batch.inUse {
|
|
if (!generationTimeInMeasure) {
|
|
font.draw(batch, "Generation time: ${generationTime} seconds", 8f, HEIGHT - 8f)
|
|
}
|
|
else {
|
|
font.draw(batch, "Generating...", 8f, HEIGHT - 8f)
|
|
}
|
|
}
|
|
}
|
|
|
|
private val NOISE_MAKER = BiomeMaker
|
|
|
|
private fun getNoiseGenerator(SEED: Long): List<Joise> {
|
|
//return NOISE_MAKER.getGenerator(SEED, TerragenParams())
|
|
return NOISE_MAKER.getGenerator(SEED, BiomegenParams())
|
|
}
|
|
|
|
val colourNull = Color(0x1b3281ff)
|
|
|
|
private val sampleOffset = WIDTH / 8.0
|
|
|
|
private val testColSet = arrayOf(
|
|
Color(0xff0000ff.toInt()),
|
|
Color(0xffff00ff.toInt()),
|
|
Color(0x00ff00ff.toInt()),
|
|
Color(0x00ffffff.toInt()),
|
|
Color(0x0000ffff.toInt()),
|
|
Color(0xff00ffff.toInt()),
|
|
Color(0xffffffff.toInt()),
|
|
Color(0xff)
|
|
)
|
|
private val testColSet2 = arrayOf(
|
|
0xff0000ff.toInt(),
|
|
0xffff00ff.toInt(),
|
|
0x00ff00ff.toInt(),
|
|
0x00ffffff.toInt(),
|
|
0x0000ffff.toInt(),
|
|
0xff00ffff.toInt(),
|
|
0xffffffff.toInt(),
|
|
0xff
|
|
)
|
|
|
|
fun <T> Array<T>.shuffle() {
|
|
val rng = Random(System.nanoTime())
|
|
for (k in this.size - 1 downTo 1) {
|
|
val r = rng.nextInt(k + 1)
|
|
|
|
val t = this[r]
|
|
this[r] = this[k]
|
|
this[k] = t
|
|
}
|
|
}
|
|
|
|
//private val xSlices = (0 until WIDTH).sliceEvenly(ThreadExecutor.threadCount)
|
|
private val xSlices = (0 until WIDTH).sliceEvenly(maxOf(WIDTH, ThreadExecutor.threadCount, WIDTH / 8))
|
|
|
|
private val runs = (0 until WIDTH).map { x -> (x until WIDTH * HEIGHT step WIDTH) }.flatten()
|
|
|
|
private lateinit var coroutineJobs: List<Job>
|
|
|
|
private fun renderNoise() {
|
|
generationStartTime = System.nanoTime()
|
|
generationTimeInMeasure = true
|
|
|
|
// erase first
|
|
testTex.setColor(colourNull)
|
|
testTex.fill()
|
|
|
|
testColSet.shuffle()
|
|
testColSet2.shuffle()
|
|
|
|
// render noisemap to pixmap
|
|
|
|
/*
|
|
I've got two ideas to resolve noisy artefact when noise generation runs concurrently:
|
|
|
|
1) 1 block = 1 coroutine
|
|
2) 1 thread has its own copy of Joise (threads have different INSTANCEs of Joise with same params)
|
|
|
|
Method 1) seemingly works but may break if the operation is more complex
|
|
Method 2) also works
|
|
|
|
--CuriousTorvald, 2020-04-29
|
|
*/
|
|
|
|
// 0. naive concurrent approach
|
|
// CULPRIT: one global instance of Joise that all the threads try to access (and modify local variables) at the same time
|
|
/*val runnables: List<RunnableFun> = xSlices.map { range ->
|
|
{
|
|
for (x in range) {
|
|
for (y in 0 until HEIGHT) {
|
|
val sampleTheta = (x.toDouble() / WIDTH) * TWO_PI
|
|
val sampleX = sin(sampleTheta) * sampleOffset + sampleOffset // plus sampleOffset to make only
|
|
val sampleZ = cos(sampleTheta) * sampleOffset + sampleOffset // positive points are to be sampled
|
|
val sampleY = y.toDouble()
|
|
|
|
NOISE_MAKER.draw(x, y, joise.map { it.get(sampleX, sampleY, sampleZ) }, testTex)
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
|
|
// 1. stupid one-block-is-one-coroutine approach (seemingly works?)
|
|
/*val joise = getNoiseGenerator(seed)
|
|
val runnables: List<RunnableFun> = runs.map { i -> {
|
|
val (x, y) = (i % WIDTH) to (i / WIDTH)
|
|
val sampleTheta = (x.toDouble() / WIDTH) * TWO_PI
|
|
val sampleX = sin(sampleTheta) * sampleOffset + sampleOffset // plus sampleOffset to make only
|
|
val sampleZ = cos(sampleTheta) * sampleOffset + sampleOffset // positive points are to be sampled
|
|
val sampleY = y.toDouble()
|
|
|
|
NOISE_MAKER.draw(x, y, joise.map { it.get(sampleX, sampleY, sampleZ) }, testTex)
|
|
} }*/
|
|
|
|
// 2. each runner gets their own copy of Joise
|
|
val runnables: List<RunnableFun> = xSlices.map { range -> {
|
|
val localJoise = getNoiseGenerator(seed)
|
|
for (x in range) {
|
|
for (y in 0 until HEIGHT) {
|
|
val sampleTheta = (x.toDouble() / WIDTH) * TWO_PI
|
|
val sampleX = sin(sampleTheta) * sampleOffset + sampleOffset // plus sampleOffset to make only
|
|
val sampleZ = cos(sampleTheta) * sampleOffset + sampleOffset // positive points are to be sampled
|
|
val sampleY = y.toDouble()
|
|
|
|
NOISE_MAKER.draw(x, y, localJoise.map { it.get(sampleX, sampleY, sampleZ) }, testTex)
|
|
}
|
|
}
|
|
} }
|
|
|
|
|
|
coroutineJobs = runnables.map { r -> GlobalScope.launch { r() } }
|
|
|
|
initialGenDone = true
|
|
}
|
|
|
|
override fun dispose() {
|
|
testTex.dispose()
|
|
tempTex.dispose()
|
|
}
|
|
}
|
|
|
|
fun main(args: Array<String>) {
|
|
ShaderProgram.pedantic = false
|
|
|
|
val appConfig = LwjglApplicationConfiguration()
|
|
appConfig.vSyncEnabled = false
|
|
appConfig.resizable = false
|
|
appConfig.width = WIDTH
|
|
appConfig.height = HEIGHT
|
|
appConfig.backgroundFPS = 60
|
|
appConfig.foregroundFPS = 60
|
|
appConfig.forceExit = false
|
|
|
|
LwjglApplication(WorldgenNoiseSandbox(), appConfig)
|
|
}
|
|
|
|
internal interface NoiseMaker {
|
|
fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap)
|
|
fun getGenerator(seed: Long, params: Any): List<Joise>
|
|
}
|
|
|
|
val locklock = java.lang.Object()
|
|
|
|
internal object BiomeMaker : NoiseMaker {
|
|
|
|
override fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap) {
|
|
val colPal = biomeColors
|
|
val control = noiseValue[0].times(colPal.size).minus(0.00001f).toInt().fmod(colPal.size)
|
|
|
|
outTex.setColor(colPal[control])
|
|
outTex.drawPixel(x, y)
|
|
}
|
|
|
|
override fun getGenerator(seed: Long, params: Any): List<Joise> {
|
|
val params = params as BiomegenParams
|
|
//val biome = ModuleBasisFunction()
|
|
//biome.setType(ModuleBasisFunction.BasisType.SIMPLEX)
|
|
|
|
// simplex AND fractal for more noisy edges, mmmm..!
|
|
val fractal = ModuleFractal()
|
|
fractal.setType(ModuleFractal.FractalType.MULTI)
|
|
fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.SIMPLEX)
|
|
fractal.setNumOctaves(4)
|
|
fractal.setFrequency(1.0)
|
|
fractal.seed = seed shake 0x7E22A
|
|
|
|
val autocorrect = ModuleAutoCorrect()
|
|
autocorrect.setSource(fractal)
|
|
autocorrect.setRange(0.0, 1.0)
|
|
|
|
val scale = ModuleScaleDomain()
|
|
scale.setSource(autocorrect)
|
|
scale.setScaleX(1.0 / params.featureSize) // adjust this value to change features size
|
|
scale.setScaleY(1.0 / params.featureSize)
|
|
scale.setScaleZ(1.0 / params.featureSize)
|
|
|
|
val last = scale
|
|
|
|
return listOf(Joise(last))
|
|
}
|
|
|
|
// with this method, only TWO distinct (not bland) biomes are possible. CLUT order is important here.
|
|
val biomeColors = intArrayOf(
|
|
//0x2288ccff.toInt(), // ísland
|
|
0x229944ff.toInt(), // woodlands
|
|
0x77bb77ff.toInt(), // shrubland
|
|
0x88bb66ff.toInt(), // plains
|
|
0xeeddbbff.toInt(), // sands
|
|
0x888888ff.toInt() // rockyland
|
|
)
|
|
}
|
|
|
|
// http://accidentalnoise.sourceforge.net/minecraftworlds.html
|
|
internal object AccidentalCave {
|
|
|
|
private infix fun Color.mul(other: Color) = this.mul(other)
|
|
|
|
private val notationColours = arrayOf(
|
|
Color.WHITE,
|
|
Color.MAGENTA,
|
|
Color(0f, 186f/255f, 1f, 1f),
|
|
Color(.5f, 1f, .5f, 1f),
|
|
Color(1f, 0.93f, 0.07f, 1f),
|
|
Color(0.97f, 0.6f, 0.56f, 1f)
|
|
)
|
|
|
|
fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap) {
|
|
// simple one-source draw
|
|
/*val c = noiseValue[0].toFloat()
|
|
val selector = c.minus(0.0001).floorInt() fmod notationColours.size
|
|
val selecteeColour = Color(c - selector, c - selector, c - selector, 1f)
|
|
if (c < 0) {
|
|
outTex.setColor(-c, 0f, 0f, 1f)
|
|
}
|
|
else {
|
|
outTex.setColor(selecteeColour.mul(notationColours[selector]))
|
|
}
|
|
outTex.drawPixel(x, y)*/
|
|
|
|
|
|
fun Double.tiered(vararg tiers: Double): Int {
|
|
tiers.reversed().forEachIndexed { index, it ->
|
|
if (this >= it) return (tiers.lastIndex - index) // why??
|
|
}
|
|
return tiers.lastIndex
|
|
}
|
|
|
|
val groundDepthCol = listOf(
|
|
Color(0f, 0f, 0f, 1f),
|
|
Color(0.55f, 0.4f, 0.24f, 1f),
|
|
Color(.6f, .6f, .6f, 1f)
|
|
)
|
|
val n1 = noiseValue[0].tiered(.0, .5, .88)
|
|
//var n2 = noiseValue[1].toFloat()
|
|
//if (n2 != 1f) n2 = 0.5f
|
|
val c1 = groundDepthCol[n1]
|
|
//val c2 = Color(n2, n2, n2, 1f)
|
|
//val cout = c1 mul c23
|
|
val cout = c1
|
|
|
|
outTex.drawPixel(x, y, cout.toRGBA())
|
|
}
|
|
|
|
fun getGenerator(seed: Long, params: Any): List<Joise> {
|
|
val params = params as TerragenParams
|
|
|
|
val lowlandMagic: Long = 0x41A21A114DBE56 // Maria Lindberg
|
|
val highlandMagic: Long = 0x0114E091 // Olive Oyl
|
|
val mountainMagic: Long = 0x115AA4DE2504 // Lisa Anderson
|
|
val selectionMagic: Long = 0x41E10D9B100 // Melody Blue
|
|
|
|
val caveMagic: Long = 0x00215741CDF // Urist McDF
|
|
val cavePerturbMagic: Long = 0xA2410C // Armok
|
|
val caveBlockageMagic: Long = 0xD15A57E5 // Disaster
|
|
|
|
|
|
val groundGradient = ModuleGradient()
|
|
groundGradient.setGradient(0.0, 0.0, 0.0, 1.0)
|
|
|
|
/* lowlands */
|
|
|
|
val lowlandShapeFractal = ModuleFractal()
|
|
lowlandShapeFractal.setType(ModuleFractal.FractalType.BILLOW)
|
|
lowlandShapeFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
lowlandShapeFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
lowlandShapeFractal.setNumOctaves(2)
|
|
lowlandShapeFractal.setFrequency(0.25)
|
|
lowlandShapeFractal.seed = seed shake lowlandMagic
|
|
|
|
val lowlandAutoCorrect = ModuleAutoCorrect()
|
|
lowlandAutoCorrect.setSource(lowlandShapeFractal)
|
|
lowlandAutoCorrect.setLow(0.0)
|
|
lowlandAutoCorrect.setHigh(1.0)
|
|
|
|
val lowlandScale = ModuleScaleOffset()
|
|
lowlandScale.setScale(0.125)
|
|
lowlandScale.setOffset(params.lowlandScaleOffset) // TODO linearly alters the height
|
|
|
|
val lowlandYScale = ModuleScaleDomain()
|
|
lowlandYScale.setSource(lowlandScale)
|
|
lowlandYScale.setScaleY(0.02) // greater = more distortion, overhangs
|
|
|
|
val lowlandTerrain = ModuleTranslateDomain()
|
|
lowlandTerrain.setSource(groundGradient)
|
|
lowlandTerrain.setAxisYSource(lowlandYScale)
|
|
|
|
/* highlands */
|
|
|
|
val highlandShapeFractal = ModuleFractal()
|
|
highlandShapeFractal.setType(ModuleFractal.FractalType.FBM)
|
|
highlandShapeFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
highlandShapeFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
highlandShapeFractal.setNumOctaves(4)
|
|
highlandShapeFractal.setFrequency(2.0)
|
|
highlandShapeFractal.seed = seed shake highlandMagic
|
|
|
|
val highlandAutocorrect = ModuleAutoCorrect()
|
|
highlandAutocorrect.setSource(highlandShapeFractal)
|
|
highlandAutocorrect.setLow(-1.0)
|
|
highlandAutocorrect.setHigh(1.0)
|
|
|
|
val highlandScale = ModuleScaleOffset()
|
|
highlandScale.setSource(highlandAutocorrect)
|
|
highlandScale.setScale(0.25)
|
|
highlandScale.setOffset(params.highlandScaleOffset) // TODO linearly alters the height
|
|
|
|
val highlandYScale = ModuleScaleDomain()
|
|
highlandYScale.setSource(highlandScale)
|
|
highlandYScale.setScaleY(0.14) // greater = more distortion, overhangs
|
|
|
|
val highlandTerrain = ModuleTranslateDomain()
|
|
highlandTerrain.setSource(groundGradient)
|
|
highlandTerrain.setAxisYSource(highlandYScale)
|
|
|
|
/* mountains */
|
|
|
|
val mountainShapeFractal = ModuleFractal()
|
|
mountainShapeFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
mountainShapeFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
mountainShapeFractal.setNumOctaves(8)
|
|
mountainShapeFractal.setFrequency(1.0)
|
|
mountainShapeFractal.seed = seed shake mountainMagic
|
|
|
|
val mountainAutocorrect = ModuleAutoCorrect()
|
|
mountainAutocorrect.setSource(mountainShapeFractal)
|
|
mountainAutocorrect.setLow(-1.0)
|
|
mountainAutocorrect.setHigh(1.0)
|
|
|
|
val mountainScale = ModuleScaleOffset()
|
|
mountainScale.setSource(mountainAutocorrect)
|
|
mountainScale.setScale(0.45)
|
|
mountainScale.setOffset(params.mountainScaleOffset) // TODO linearly alters the height
|
|
|
|
val mountainYScale = ModuleScaleDomain()
|
|
mountainYScale.setSource(mountainScale)
|
|
mountainYScale.setScaleY(params.mountainDisturbance) // greater = more distortion, overhangs
|
|
|
|
val mountainTerrain = ModuleTranslateDomain()
|
|
mountainTerrain.setSource(groundGradient)
|
|
mountainTerrain.setAxisYSource(mountainYScale)
|
|
|
|
/* selection */
|
|
|
|
val terrainTypeFractal = ModuleFractal()
|
|
terrainTypeFractal.setType(ModuleFractal.FractalType.FBM)
|
|
terrainTypeFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
terrainTypeFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
terrainTypeFractal.setNumOctaves(3)
|
|
terrainTypeFractal.setFrequency(0.125)
|
|
terrainTypeFractal.seed = seed shake selectionMagic
|
|
|
|
val terrainAutocorrect = ModuleAutoCorrect()
|
|
terrainAutocorrect.setSource(terrainTypeFractal)
|
|
terrainAutocorrect.setLow(0.0)
|
|
terrainAutocorrect.setHigh(1.0)
|
|
|
|
val terrainTypeYScale = ModuleScaleDomain()
|
|
terrainTypeYScale.setSource(terrainAutocorrect)
|
|
terrainTypeYScale.setScaleY(0.0)
|
|
|
|
val terrainTypeCache = ModuleCache()
|
|
terrainTypeCache.setSource(terrainTypeYScale)
|
|
|
|
val highlandMountainSelect = ModuleSelect()
|
|
highlandMountainSelect.setLowSource(highlandTerrain)
|
|
highlandMountainSelect.setHighSource(mountainTerrain)
|
|
highlandMountainSelect.setControlSource(terrainTypeCache)
|
|
highlandMountainSelect.setThreshold(0.55)
|
|
highlandMountainSelect.setFalloff(0.2)
|
|
|
|
val highlandLowlandSelect = ModuleSelect()
|
|
highlandLowlandSelect.setLowSource(lowlandTerrain)
|
|
highlandLowlandSelect.setHighSource(highlandMountainSelect)
|
|
highlandLowlandSelect.setControlSource(terrainTypeCache)
|
|
highlandLowlandSelect.setThreshold(0.25)
|
|
highlandLowlandSelect.setFalloff(0.15)
|
|
|
|
val highlandLowlandSelectCache = ModuleCache()
|
|
highlandLowlandSelectCache.setSource(highlandLowlandSelect)
|
|
|
|
val groundSelect = ModuleSelect()
|
|
groundSelect.setLowSource(0.0)
|
|
groundSelect.setHighSource(1.0)
|
|
groundSelect.setThreshold(0.5)
|
|
groundSelect.setControlSource(highlandLowlandSelectCache)
|
|
|
|
val groundSelect2 = ModuleSelect()
|
|
groundSelect2.setLowSource(0.0)
|
|
groundSelect2.setHighSource(1.0)
|
|
groundSelect2.setThreshold(0.8)
|
|
groundSelect2.setControlSource(highlandLowlandSelectCache)
|
|
|
|
/* caves */
|
|
|
|
val caveShape = ModuleFractal()
|
|
caveShape.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
|
caveShape.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
caveShape.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
caveShape.setNumOctaves(1)
|
|
caveShape.setFrequency(params.caveShapeFreq) // TODO adjust the "density" of the caves
|
|
caveShape.seed = seed shake caveMagic
|
|
|
|
val caveAttenuateBias = ModuleBias()
|
|
caveAttenuateBias.setSource(highlandLowlandSelectCache)
|
|
caveAttenuateBias.setBias(params.caveAttenuateBias) // TODO (0.5+) adjust the "concentration" of the cave gen. Lower = larger voids
|
|
|
|
val caveShapeAttenuate = ModuleCombiner()
|
|
caveShapeAttenuate.setType(ModuleCombiner.CombinerType.MULT)
|
|
caveShapeAttenuate.setSource(0, caveShape)
|
|
caveShapeAttenuate.setSource(1, caveAttenuateBias)
|
|
|
|
val cavePerturbFractal = ModuleFractal()
|
|
cavePerturbFractal.setType(ModuleFractal.FractalType.FBM)
|
|
cavePerturbFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
cavePerturbFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
cavePerturbFractal.setNumOctaves(6)
|
|
cavePerturbFractal.setFrequency(3.0)
|
|
cavePerturbFractal.seed = seed shake cavePerturbMagic
|
|
|
|
val cavePerturbScale = ModuleScaleOffset()
|
|
cavePerturbScale.setSource(cavePerturbFractal)
|
|
cavePerturbScale.setScale(0.45)
|
|
cavePerturbScale.setOffset(0.0)
|
|
|
|
val cavePerturb = ModuleTranslateDomain()
|
|
cavePerturb.setSource(caveShapeAttenuate)
|
|
cavePerturb.setAxisXSource(cavePerturbScale)
|
|
|
|
val caveSelect = ModuleSelect()
|
|
caveSelect.setLowSource(1.0)
|
|
caveSelect.setHighSource(0.0)
|
|
caveSelect.setControlSource(cavePerturb)
|
|
caveSelect.setThreshold(params.caveSelectThre) // TODO also adjust this if you've touched the bias value. Number can be greater than 1.0
|
|
caveSelect.setFalloff(0.0)
|
|
|
|
val caveBlockageFractal = ModuleFractal()
|
|
caveBlockageFractal.setType(ModuleFractal.FractalType.RIDGEMULTI)
|
|
caveBlockageFractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
|
caveBlockageFractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
|
caveBlockageFractal.setNumOctaves(2)
|
|
caveBlockageFractal.setFrequency(params.caveBlockageFractalFreq) // TODO same as caveShape frequency?
|
|
caveBlockageFractal.seed = seed shake caveBlockageMagic
|
|
|
|
// will only close-up deeper caves. Shallow caves will be less likely to be closed up
|
|
val caveBlockageAttenuate = ModuleCombiner()
|
|
caveBlockageAttenuate.setType(ModuleCombiner.CombinerType.MULT)
|
|
caveBlockageAttenuate.setSource(0, caveBlockageFractal)
|
|
caveBlockageAttenuate.setSource(1, caveAttenuateBias)
|
|
|
|
val caveBlockageSelect = ModuleSelect()
|
|
caveBlockageSelect.setLowSource(0.0)
|
|
caveBlockageSelect.setHighSource(1.0)
|
|
caveBlockageSelect.setControlSource(caveBlockageAttenuate)
|
|
caveBlockageSelect.setThreshold(params.caveBlockageSelectThre) // TODO adjust cave cloing-up strength. Larger = more closing
|
|
caveBlockageSelect.setFalloff(0.0)
|
|
|
|
// note: gradient-multiply DOESN'T generate "naturally cramped" cave entrance
|
|
|
|
val caveInMix = ModuleCombiner()
|
|
caveInMix.setType(ModuleCombiner.CombinerType.ADD)
|
|
caveInMix.setSource(0, caveSelect)
|
|
caveInMix.setSource(1, caveBlockageSelect)
|
|
|
|
/*val groundCaveMult = ModuleCombiner()
|
|
groundCaveMult.setType(ModuleCombiner.CombinerType.MULT)
|
|
groundCaveMult.setSource(0, caveInMix)
|
|
//groundCaveMult.setSource(0, caveSelect) // disables the cave-in for quick cavegen testing
|
|
groundCaveMult.setSource(1, groundSelect)*/
|
|
|
|
// this noise tree WILL generate noise value greater than 1.0
|
|
// they should be treated properly when you actually generate the world out of the noisemap
|
|
// for the visualisation, no treatment will be done in this demo app.
|
|
|
|
val groundClamp = ModuleClamp()
|
|
groundClamp.setRange(0.0, 100.0)
|
|
groundClamp.setSource(highlandLowlandSelectCache)
|
|
|
|
val groundScaling = ModuleScaleDomain()
|
|
groundScaling.setScaleX(1.0 / params.featureSize) // adjust this value to change features size
|
|
groundScaling.setScaleY(1.0 / params.featureSize)
|
|
groundScaling.setScaleZ(1.0 / params.featureSize)
|
|
groundScaling.setSource(groundClamp)
|
|
|
|
|
|
val caveClamp = ModuleClamp()
|
|
caveClamp.setRange(0.0, 1.0)
|
|
caveClamp.setSource(caveInMix)
|
|
|
|
val caveScaling = ModuleScaleDomain()
|
|
caveScaling.setScaleX(1.0 / params.featureSize) // adjust this value to change features size
|
|
caveScaling.setScaleY(1.0 / params.featureSize)
|
|
caveScaling.setScaleZ(1.0 / params.featureSize)
|
|
caveScaling.setSource(caveClamp)
|
|
|
|
//return Joise(caveInMix)
|
|
return listOf(
|
|
Joise(groundScaling),
|
|
Joise(caveScaling)
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*infix fun Long.shake(other: Long): Long {
|
|
var s0 = this
|
|
var s1 = other
|
|
|
|
s1 = s1 xor s0
|
|
s0 = s0 shl 55 or s0.ushr(9) xor s1 xor (s1 shl 14)
|
|
s1 = s1 shl 36 or s1.ushr(28)
|
|
|
|
return s0 + s1
|
|
}*/ |