mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-15 04:54:05 +09:00
worldgensandbox: reviving old feature of showing real progress
This commit is contained in:
@@ -19,12 +19,11 @@ import com.sudoplay.joise.ModuleMap
|
|||||||
import com.sudoplay.joise.ModulePropertyMap
|
import com.sudoplay.joise.ModulePropertyMap
|
||||||
import com.sudoplay.joise.module.*
|
import com.sudoplay.joise.module.*
|
||||||
import net.torvald.random.HQRNG
|
import net.torvald.random.HQRNG
|
||||||
import net.torvald.terrarum.FlippingSpriteBatch
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.concurrent.RunnableFun
|
import net.torvald.terrarum.concurrent.RunnableFun
|
||||||
import net.torvald.terrarum.concurrent.ThreadExecutor
|
import net.torvald.terrarum.concurrent.ThreadExecutor
|
||||||
import net.torvald.terrarum.concurrent.sliceEvenly
|
import net.torvald.terrarum.concurrent.sliceEvenly
|
||||||
import net.torvald.terrarum.inUse
|
|
||||||
import net.torvald.terrarum.modulebasegame.worldgenerator.*
|
import net.torvald.terrarum.modulebasegame.worldgenerator.*
|
||||||
import net.torvald.terrarum.worlddrawer.toRGBA
|
import net.torvald.terrarum.worlddrawer.toRGBA
|
||||||
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
|
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
|
||||||
@@ -32,7 +31,7 @@ import java.util.concurrent.Future
|
|||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen
|
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen
|
||||||
import net.torvald.terrarum.sqr
|
import java.io.PrintStream
|
||||||
|
|
||||||
const val NOISEBOX_WIDTH = 1200
|
const val NOISEBOX_WIDTH = 1200
|
||||||
const val NOISEBOX_HEIGHT = 2400
|
const val NOISEBOX_HEIGHT = 2400
|
||||||
@@ -56,9 +55,7 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|||||||
private var seed = 10000L
|
private var seed = 10000L
|
||||||
|
|
||||||
private var initialGenDone = false
|
private var initialGenDone = false
|
||||||
private var generateKeyLatched = false
|
|
||||||
|
|
||||||
private var generationTimeInMeasure = false
|
|
||||||
private var generationStartTime = 0L
|
private var generationStartTime = 0L
|
||||||
private var genSlices: Int = 0
|
private var genSlices: Int = 0
|
||||||
private var genFutures: Array<Future<*>?> = arrayOfNulls(genSlices)
|
private var genFutures: Array<Future<*>?> = arrayOfNulls(genSlices)
|
||||||
@@ -92,6 +89,7 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|||||||
|
|
||||||
if (!initialGenDone) {
|
if (!initialGenDone) {
|
||||||
renderNoise(NOISEMAKER)
|
renderNoise(NOISEMAKER)
|
||||||
|
initialGenDone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw using pixmap
|
// draw using pixmap
|
||||||
@@ -102,28 +100,15 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read key input
|
// read key input
|
||||||
if (!generateKeyLatched && Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
|
if (Gdx.input.isKeyPressed(Input.Keys.SPACE) && worldgenDone) {
|
||||||
generateKeyLatched = true
|
|
||||||
seed = RNG.nextLong()
|
seed = RNG.nextLong()
|
||||||
renderNoise(NOISEMAKER)
|
renderNoise(NOISEMAKER)
|
||||||
}
|
}
|
||||||
|
|
||||||
val coroutineExecFinished = genFutures.fold(true) { acc, it -> acc and (it?.isDone ?: true) }
|
|
||||||
// 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
|
// draw timer
|
||||||
batch.inUse {
|
batch.inUse {
|
||||||
if (!generationTimeInMeasure) {
|
if (worldgenDone) {
|
||||||
font.draw(batch, "Generation time: ${generationTime} seconds", 8f, 8f)
|
font.draw(batch, "Generation time: ${generationTime} seconds", 8f, 8f)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -168,10 +153,18 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun printStackTrace(obj: Any, out: PrintStream = System.out) {
|
||||||
|
val indentation = " ".repeat(obj.javaClass.simpleName.length + 4)
|
||||||
|
Thread.currentThread().stackTrace.forEachIndexed { index, it ->
|
||||||
|
if (index == 1)
|
||||||
|
out.println("[${obj.javaClass.simpleName}]> $it")
|
||||||
|
else if (index > 1)
|
||||||
|
out.println("$indentation$it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderNoise(noiseMaker: Pair<NoiseMaker, Any>) {
|
private fun renderNoise(noiseMaker: Pair<NoiseMaker, Any>, callback: () -> Unit = {}) {
|
||||||
generationStartTime = System.nanoTime()
|
generationStartTime = System.nanoTime()
|
||||||
generationTimeInMeasure = true
|
|
||||||
|
|
||||||
// erase first
|
// erase first
|
||||||
testTex.setColor(colourNull)
|
testTex.setColor(colourNull)
|
||||||
@@ -180,83 +173,55 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
|
|||||||
testColSet.shuffle()
|
testColSet.shuffle()
|
||||||
testColSet2.shuffle()
|
testColSet2.shuffle()
|
||||||
|
|
||||||
// render noisemap to pixmap
|
worldgenDone = false
|
||||||
|
|
||||||
/*
|
Thread {
|
||||||
I've got two ideas to resolve noisy artefact when noise generation runs concurrently:
|
val runnables: List<RunnableFun> = (0 until testTex.width).sliceEvenly(genSlices).map { range ->
|
||||||
|
{
|
||||||
|
val localJoise = noiseMaker.first.getGenerator(seed, noiseMaker.second)
|
||||||
|
for (x in range) {
|
||||||
|
for (y in 0 until NOISEBOX_HEIGHT) {
|
||||||
|
val sampleTheta = (x.toDouble() / NOISEBOX_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 - (NOISEBOX_HEIGHT - Terragen.YHEIGHT_MAGIC) * Terragen.YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant
|
||||||
|
|
||||||
1) 1 block = 1 coroutine
|
noiseMaker.first.draw(x, y, localJoise.mapIndexed { index, it ->
|
||||||
2) 1 thread has its own copy of Joise (threads have different INSTANCEs of Joise with same params)
|
it.get(sampleX, sampleY, sampleZ)
|
||||||
|
}, testTex)
|
||||||
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?)
|
threadExecutor.renew()
|
||||||
/*val joise = getNoiseGenerator(seed)
|
runnables.forEach {
|
||||||
val runnables: List<RunnableFun> = runs.map { i -> {
|
threadExecutor.submit(it)
|
||||||
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)
|
threadExecutor.join()
|
||||||
} }*/
|
|
||||||
|
|
||||||
// 2. each runner gets their own copy of Joise
|
worldgenDone = true
|
||||||
val runnables: List<RunnableFun> = (0 until testTex.width).sliceEvenly(genSlices).map { range -> {
|
|
||||||
val localJoise = noiseMaker.first.getGenerator(seed, noiseMaker.second)
|
|
||||||
for (x in range) {
|
|
||||||
for (y in 0 until NOISEBOX_HEIGHT) {
|
|
||||||
val sampleTheta = (x.toDouble() / NOISEBOX_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 - (NOISEBOX_HEIGHT - Terragen.YHEIGHT_MAGIC) * Terragen.YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant
|
|
||||||
|
|
||||||
noiseMaker.first.draw(x, y, localJoise.mapIndexed { index, it ->
|
val time = System.nanoTime() - generationStartTime
|
||||||
it.get(sampleX, sampleY, sampleZ)
|
generationTime = time / 1000000000f
|
||||||
}, testTex)
|
callback()
|
||||||
}
|
}.start()
|
||||||
}
|
|
||||||
} }
|
|
||||||
|
|
||||||
|
|
||||||
threadExecutor.renew()
|
|
||||||
runnables.forEach {
|
|
||||||
threadExecutor.submit(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
threadExecutor.join()
|
|
||||||
|
|
||||||
initialGenDone = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var worldgenDone = true; private set
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
testTex.dispose()
|
testTex.dispose()
|
||||||
tempTex.dispose()
|
tempTex.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
ShaderProgram.pedantic = false
|
ShaderProgram.pedantic = false
|
||||||
|
|
||||||
@@ -270,7 +235,7 @@ fun main(args: Array<String>) {
|
|||||||
Lwjgl3Application(WorldgenNoiseSandbox(), appConfig)
|
Lwjgl3Application(WorldgenNoiseSandbox(), appConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface NoiseMaker {
|
interface NoiseMaker {
|
||||||
fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap)
|
fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap)
|
||||||
fun getGenerator(seed: Long, params: Any): List<Joise>
|
fun getGenerator(seed: Long, params: Any): List<Joise>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user