diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt index 0ef59ea11..114cae6af 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Biomegen.kt @@ -10,13 +10,15 @@ import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import kotlin.math.cos import kotlin.math.sin /** * Created by minjaesong on 2019-09-02. */ -class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashMap) : Gen(world, seed, params) { +class Biomegen(world: GameWorld, isFinal: Boolean, seed: Long, params: Any, val biomeMapOut: HashMap) : Gen(world, isFinal, seed, params) { private val YHEIGHT_MAGIC = 2800.0 / 3.0 private val YHEIGHT_DIVISOR = 2.0 / 7.0 @@ -46,28 +48,7 @@ class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashM // loadscreen.progress.set((loadscreen.progress.get() + 0x1_000000_000000L) and 0x7FFF_000000_000000L) Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).map { xs -> - Worldgen.threadExecutor.submit { - val localJoise = getGenerator(seed, params as BiomegenParams) - for (x in xs) { - for (y in 0 until world.height) { - val sampleTheta = (x.toDouble() / world.width) * TWO_PI - val sampleOffset = world.width / 8.0 - 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 - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant - // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant - val noise = localJoise.map { it.get(sampleX, sampleY, sampleZ) } - - draw(x, y, noise, world) - } - } - - - loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() App.printdbg(this, "Waking up Worldgen") @@ -112,43 +93,53 @@ class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashM } - private fun draw(x: Int, y: Int, noiseValue: List, world: GameWorld) { - val control1 = noiseValue[0].coerceIn(0.0, 0.99999).times(slices).toInt().coerceAtMost(slices - 1) - val control2 = noiseValue[1].coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1) - val control3 = noiseValue[2].coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1) - val ba = LandUtil.getBlockAddr(world, x, y) + override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + for (x in xStart until xStart + CHUNK_W) { + val sampleTheta = (x.toDouble() / world.width) * TWO_PI + val sx = sin(sampleTheta) * soff + soff // plus sampleOffset to make only + val sz = cos(sampleTheta) * soff + soff // positive points are to be sampled + for (y in yStart until yStart + CHUNK_H) { + val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant - if (y > 0) { - val tileThis = world.getTileFromTerrain(x, y) - val wallThis = world.getTileFromWall(x, y) - val nearbyTerr = nearbyArr.map { world.getTileFromTerrain(x + it.first, y + it.second) } - val nearbyWall = nearbyArr.map { world.getTileFromWall(x + it.first, y + it.second) } - val exposedToAir = nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR } - val hasNoFloor = (nearbyTerr[BT] == Block.AIR) + val control1 = + noises[0].get(sx, sy, sz).coerceIn(0.0, 0.99999).times(slices).toInt().coerceAtMost(slices - 1) + val control2 = noises[1].get(sx, sy, sz).coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1) + val control3 = noises[2].get(sx, sy, sz).coerceIn(0.0, 0.99999).times(9).toInt().coerceAtMost(9 - 1) + val ba = LandUtil.getBlockAddr(world, x, y) - val grassRock = when (control1) { - 0 -> { // woodlands - if (tileThis == Block.DIRT && exposedToAir) { - biomeMapOut[ba] = BIOME_KEY_WOODLANDS - Block.GRASS to null - } - else null to null - } - 1 -> { // sparse forest - if (tileThis == Block.DIRT && exposedToAir) { - biomeMapOut[ba] = BIOME_KEY_SPARSE_WOODS - Block.GRASS to null - } - else null to null - } - 2, 3 -> { // plains - if (tileThis == Block.DIRT && exposedToAir) { - biomeMapOut[ba] = BIOME_KEY_PLAINS - Block.GRASS to null - } - else null to null - } - /*3 -> { // sands + if (y > 0) { + val tileThis = world.getTileFromTerrain(x, y) + val wallThis = world.getTileFromWall(x, y) + val nearbyTerr = nearbyArr.map { world.getTileFromTerrain(x + it.first, y + it.second) } + val nearbyWall = nearbyArr.map { world.getTileFromWall(x + it.first, y + it.second) } + val exposedToAir = nearbyTerr.any { it == Block.AIR } && nearbyWall.any { it == Block.AIR } + val hasNoFloor = (nearbyTerr[BT] == Block.AIR) + + val grassRock = when (control1) { + 0 -> { // woodlands + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_WOODLANDS + Block.GRASS to null + } + else null to null + } + + 1 -> { // sparse forest + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_SPARSE_WOODS + Block.GRASS to null + } + else null to null + } + + 2, 3 -> { // plains + if (tileThis == Block.DIRT && exposedToAir) { + biomeMapOut[ba] = BIOME_KEY_PLAINS + Block.GRASS to null + } + else null to null + } + /*3 -> { // sands if (tileThis == Block.DIRT && (nearbyTerr[BT] == Block.STONE || nearbyTerr[BT] == Block.AIR)) { world.setTileTerrain(x, y, Block.SANDSTONE, true) } @@ -156,67 +147,75 @@ class Biomegen(world: GameWorld, seed: Long, params: Any, val biomeMapOut: HashM world.setTileTerrain(x, y, Block.SAND, true) } }*/ - 4 -> { // rockylands - if (tileThis == Block.DIRT || tileThis == Block.STONE_QUARRIED) { - if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_ROCKY - Block.STONE to Block.STONE - } - else null to null - } - else -> null to null - } - val sablum = when (control2) { - 0 -> { - if (tileThis == Block.DIRT && hasNoFloor) { - if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS - Block.STONE_QUARRIED to null - } - else if (tileThis == Block.DIRT) { - if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS - Block.GRAVEL to null - } - else null to null - } - 8 -> { - if (tileThis == Block.DIRT && hasNoFloor) { - if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY - THISWORLD_SANDSTONE to null - } - else if (tileThis == Block.DIRT) { - if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY - THISWORLD_SAND to null - } - else null to null - } - else -> null to null - } - val lutum = when (control3) { - 0 -> { - if (tileThis == Block.DIRT || wallThis == Block.DIRT) { - Block.CLAY to Block.CLAY - } - else null to null - } - else -> null to null - } + 4 -> { // rockylands + if (tileThis == Block.DIRT || tileThis == Block.STONE_QUARRIED) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_ROCKY + Block.STONE to Block.STONE + } + else null to null + } - val outTile = if (grassRock.first == Block.STONE) - grassRock - else if (sablum.first != null) - sablum - else if (lutum.first != null) - lutum - else grassRock + else -> null to null + } + val sablum = when (control2) { + 0 -> { + if (tileThis == Block.DIRT && hasNoFloor) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS + Block.STONE_QUARRIED to null + } + else if (tileThis == Block.DIRT) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_GRAVELS + Block.GRAVEL to null + } + else null to null + } + + 8 -> { + if (tileThis == Block.DIRT && hasNoFloor) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY + THISWORLD_SANDSTONE to null + } + else if (tileThis == Block.DIRT) { + if (exposedToAir) biomeMapOut[ba] = BIOME_KEY_SANDY + THISWORLD_SAND to null + } + else null to null + } + + else -> null to null + } + val lutum = when (control3) { + 0 -> { + if (tileThis == Block.DIRT || wallThis == Block.DIRT) { + Block.CLAY to Block.CLAY + } + else null to null + } + + else -> null to null + } + + val outTile = if (grassRock.first == Block.STONE) + grassRock + else if (sablum.first != null) + sablum + else if (lutum.first != null) + lutum + else grassRock - if (outTile.first != null && tileThis != Block.AIR) - world.setTileTerrain(x, y, outTile.first!!, true) - if (outTile.second != null) - world.setTileWall(x, y, outTile.second!!, true) + if (outTile.first != null && tileThis != Block.AIR) + world.setTileTerrain(x, y, outTile.first!!, true) + if (outTile.second != null) + world.setTileWall(x, y, outTile.second!!, true) + } + } } } - private fun getGenerator(seed: Long, params: BiomegenParams): List { + override fun getGenerator(seed: Long, params: Any?): List { + val params = params as BiomegenParams + return listOf( makeRandomSpotties("TERRA", params.featureSize1), makeRandomSpotties("SABLUM", params.featureSize2), diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt index dddb9408a..8e5e03b89 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Cavegen.kt @@ -8,13 +8,15 @@ import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.concurrent.sliceEvenly import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import kotlin.math.cos import kotlin.math.sin /** * Created by minjaesong on 2023-11-04. */ -class Cavegen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, seed, params) { +class Cavegen(world: GameWorld, isFinal: Boolean, val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, isFinal, seed, params) { companion object { const val YHEIGHT_MAGIC = 2800.0 / 3.0 @@ -26,18 +28,7 @@ class Cavegen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, see loadscreen.progress.set(0L) Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).mapIndexed { i, xs -> - Worldgen.threadExecutor.submit { - val localJoise = getGenerator(seed, params as TerragenParams) - for (x in xs) { - val sampleTheta = (x.toDouble() / world.width) * TWO_PI - val sampleOffset = world.width / 8.0 - draw(x, localJoise, sampleTheta, sampleOffset) - } - loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() App.printdbg(this, "Waking up Worldgen") @@ -45,24 +36,30 @@ class Cavegen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, see //private fun draw(x: Int, y: Int, width: Int, height: Int, noiseValue: List, world: GameWorld) { - private fun draw(x: Int, noises: List, st: Double, soff: Double) { - for (y in 0 until world.height) { - val sx = sin(st) * soff + soff // plus sampleOffset to make only - val sz = cos(st) * soff + soff // positive points are to be sampled - val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant - // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant - val noiseValue = noises.map { it.get(sx, sy, sz) } + override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + for (x in xStart until xStart + CHUNK_W) { + val st = (x.toDouble() / world.width) * TWO_PI - val cave = if (noiseValue[0] < 0.5) 0 else 1 + for (y in yStart until yStart + CHUNK_H) { + val sx = sin(st) * soff + soff // plus sampleOffset to make only + val sz = cos(st) * soff + soff // positive points are to be sampled + val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant + // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant + val noiseValue = noises.map { it.get(sx, sy, sz) } - if (cave == 0) { - world.setTileTerrain(x, y, Block.AIR, true) + val cave = if (noiseValue[0] < 0.5) 0 else 1 + + if (cave == 0) { + world.setTileTerrain(x, y, Block.AIR, true) + } } } } - private fun getGenerator(seed: Long, params: TerragenParams): List { + override fun getGenerator(seed: Long, params: Any?): List { + val params = params as TerragenParams + val caveMagic: Long = 0x00215741CDF // Urist McDF val cavePerturbMagic: Long = 0xA2410C // Armok val caveBlockageMagic: Long = 0xD15A57E5 // Disaster diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt index eaaf45169..c8d465995 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Oregen.kt @@ -7,6 +7,9 @@ import net.torvald.terrarum.concurrent.sliceEvenly import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_MAGIC +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import kotlin.math.cos import kotlin.math.sin import kotlin.math.sqrt @@ -14,31 +17,20 @@ import kotlin.math.sqrt /** * Created by minjaesong on 2023-10-25. */ -class Oregen(world: GameWorld, private val caveAttenuateBiasScaledCache: ModuleCache, seed: Long, private val ores: List) : Gen(world, seed) { +class Oregen(world: GameWorld, isFinal: Boolean, private val caveAttenuateBiasScaledCache: ModuleCache, seed: Long, private val ores: List) : Gen(world, isFinal, seed) { override fun getDone(loadscreen: LoadScreenBase) { loadscreen.stageValue += 1 loadscreen.progress.set(0L) Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).mapIndexed { i, xs -> - Worldgen.threadExecutor.submit { - val localJoise = getGenerator(seed) - for (x in xs) { - val sampleTheta = (x.toDouble() / world.width) * TWO_PI - val sampleOffset = world.width / 8.0 - draw(x, localJoise, sampleTheta, sampleOffset) - } - loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() } /** * @return List of noise instances, each instance refers to one of the spawnable ores */ - private fun getGenerator(seed: Long): List { + override fun getGenerator(seed: Long, any: Any?): List { return ores.map { generateOreVeinModule(caveAttenuateBiasScaledCache, seed shake it.tile, it.freq, it.power, it.scale, it.ratio) } @@ -47,24 +39,29 @@ class Oregen(world: GameWorld, private val caveAttenuateBiasScaledCache: ModuleC /** * Indices of `noises` has one-to-one mapping to the `ores` */ - private fun draw(x: Int, noises: List, st: Double, soff: Double) { - for (y in 0 until world.height) { - val sx = sin(st) * soff + soff // plus sampleOffset to make only - val sz = cos(st) * soff + soff // positive points are to be sampled - val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant - // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant + override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + for (x in xStart until xStart + CHUNK_W) { + val st = (x.toDouble() / world.width) * TWO_PI - // get the actual noise values - // the size of the two lists are guaranteed to be identical as they all derive from the same `ores` - val noiseValues = noises.map { it.get(sx, sy, sz) } - val oreTiles = ores.map { it.tile } + for (y in yStart until yStart + CHUNK_H) { + val sx = sin(st) * soff + soff // plus sampleOffset to make only + val sz = cos(st) * soff + soff // positive points are to be sampled + val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant + // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant - val tileToPut = noiseValues.zip(oreTiles).firstNotNullOfOrNull { (n, tile) -> if (n > 0.5) tile else null } - val backingTile = world.getTileFromTerrain(x, y) + // get the actual noise values + // the size of the two lists are guaranteed to be identical as they all derive from the same `ores` + val noiseValues = noises.map { it.get(sx, sy, sz) } + val oreTiles = ores.map { it.tile } - if (tileToPut != null && BlockCodex[backingTile].hasAllTagOf("ROCK", "OREBEARING")) { - // actually put the ore block - world.setTileOre(x, y, tileToPut, 0) // autotiling will be handled by the other worldgen process + val tileToPut = + noiseValues.zip(oreTiles).firstNotNullOfOrNull { (n, tile) -> if (n > 0.5) tile else null } + val backingTile = world.getTileFromTerrain(x, y) + + if (tileToPut != null && BlockCodex[backingTile].hasAllTagOf("ROCK", "OREBEARING")) { + // actually put the ore block + world.setTileOre(x, y, tileToPut, 0) // autotiling will be handled by the other worldgen process + } } } } diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt index f6fd80ea9..ce70ce68f 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/OregenAutotiling.kt @@ -1,5 +1,6 @@ package net.torvald.terrarum.modulebasegame.worldgenerator +import com.sudoplay.joise.Joise import net.torvald.random.XXHash64 import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.Point2i @@ -10,6 +11,8 @@ import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import net.torvald.terrarum.serialise.toBig64 import net.torvald.terrarum.toInt import net.torvald.terrarum.utils.OrePlacement @@ -19,18 +22,11 @@ import kotlin.math.max /** * Created by minjaesong on 2023-10-26. */ -class OregenAutotiling(world: GameWorld, seed: Long, val tilingModes: HashMap) : Gen(world, seed) { +class OregenAutotiling(world: GameWorld, isFinal: Boolean, seed: Long, val tilingModes: HashMap) : Gen(world, isFinal, seed) { override fun getDone(loadscreen: LoadScreenBase) { Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).mapIndexed { i, xs -> - Worldgen.threadExecutor.submit { - for (x in xs) { - draw(x) - } - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() } @@ -39,62 +35,74 @@ class OregenAutotiling(world: GameWorld, seed: Long, val tilingModes: HashMap, soff: Double) { + for (x in xStart until xStart + CHUNK_W) { + for (y in yStart until yStart + CHUNK_H) { - if (ore.isOre()) { - // get tiling mode - val tilingMode = tilingModes[ore] + val (ore, _) = world.getTileFromOre(x, y) - val placement = when (tilingMode) { - "a16" -> { - // get placement (tile connection) info - val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> - acc or (placement.item == ore).toInt(index) - } - BlocksDrawer.connectLut16[autotiled] + if (ore.isOre()) { + // get tiling mode + val tilingMode = tilingModes[ore] + + val placement = when (tilingMode) { + "a16" -> { + // get placement (tile connection) info + val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> + acc or (placement.item == ore).toInt(index) + } + BlocksDrawer.connectLut16[autotiled] - } - "a16x4" -> { - // get placement (tile connection) info - val mult = getHashCoord(x, y, 4) - val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> - acc or (placement.item == ore).toInt(index) } - BlocksDrawer.connectLut16[autotiled] or mult.shl(4) - } - "a16x16" -> { - // get placement (tile connection) info - val mult = getHashCoord(x, y, 16) - val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> - acc or (placement.item == ore).toInt(index) + + "a16x4" -> { + // get placement (tile connection) info + val mult = getHashCoord(x, y, 4) + val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> + acc or (placement.item == ore).toInt(index) + } + BlocksDrawer.connectLut16[autotiled] or mult.shl(4) } - BlocksDrawer.connectLut16[autotiled] or mult.shl(4) - } - "a47" -> { - // get placement (tile connection) info - val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> - acc or (placement.item == ore).toInt(index) + + "a16x16" -> { + // get placement (tile connection) info + val mult = getHashCoord(x, y, 16) + val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> + acc or (placement.item == ore).toInt(index) + } + BlocksDrawer.connectLut16[autotiled] or mult.shl(4) } - BlocksDrawer.connectLut47[autotiled] + + "a47" -> { + // get placement (tile connection) info + val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement -> + acc or (placement.item == ore).toInt(index) + } + BlocksDrawer.connectLut47[autotiled] + } + + "r16" -> { + getHashCoord(x, y, 16) + } + + "r8" -> { + getHashCoord(x, y, 8) + } + + else -> throw IllegalArgumentException("Unknown tiling mode: $tilingMode") } - "r16" -> { - getHashCoord(x, y, 16) - } - "r8" -> { - getHashCoord(x, y, 8) - } - else -> throw IllegalArgumentException("Unknown tiling mode: $tilingMode") + + + // actually put the ore block + world.setTileOre(x, y, ore, placement) // autotiling will be handled by the other worldgen process } - - - // actually put the ore block - world.setTileOre(x, y, ore, placement) // autotiling will be handled by the other worldgen process } } } + override fun getGenerator(seed: Long, params: Any?): List { + return emptyList() + } private fun getNearbyTilesPos8(x: Int, y: Int): Array { return arrayOf( diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt index d8009f3ac..6c26ab409 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Terragen.kt @@ -9,6 +9,9 @@ import net.torvald.terrarum.LoadScreenBase import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.concurrent.sliceEvenly import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import kotlin.math.cos import kotlin.math.pow import kotlin.math.sin @@ -16,7 +19,7 @@ import kotlin.math.sin /** * Created by minjaesong on 2019-07-23. */ -class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, seed, params) { +class Terragen(world: GameWorld, isFinal: Boolean , val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, isFinal, seed, params) { companion object { const val YHEIGHT_MAGIC = 2800.0 / 3.0 @@ -31,18 +34,7 @@ class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, se loadscreen.progress.set(0L) Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).mapIndexed { i, xs -> - Worldgen.threadExecutor.submit { - val localJoise = getGenerator(seed, params as TerragenParams) - for (x in xs) { - val sampleTheta = (x.toDouble() / world.width) * TWO_PI - val sampleOffset = world.width / 8.0 - draw(x, localJoise, sampleTheta, sampleOffset) - } - loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() printdbg(this, "Waking up Worldgen") @@ -64,36 +56,40 @@ class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, se private val terragenTiers = listOf(.0, .5, 1.0, 2.5).map { it * terragenYscaling } // pow 1.0 for 1-to-1 scaling; 0.75 is used to make deep-rock layers actually deep for huge world size //private fun draw(x: Int, y: Int, width: Int, height: Int, noiseValue: List, world: GameWorld) { - private fun draw(x: Int, noises: List, st: Double, soff: Double) { - var dirtStoneTransition = 0 - var stoneSlateTransition = 0 + override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + for (x in xStart until xStart + CHUNK_W) { + val st = (x.toDouble() / world.width) * TWO_PI - for (y in 0 until world.height) { - val sx = sin(st) * soff + soff // plus sampleOffset to make only - val sz = cos(st) * soff + soff // positive points are to be sampled - val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant - // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant - val noiseValue = noises.map { it.get(sx, sy, sz) } + var dirtStoneTransition = 0 + var stoneSlateTransition = 0 - val terr = noiseValue[0].tiered(terragenTiers) + for (y in yStart until yStart + CHUNK_H) { + val sx = sin(st) * soff + soff // plus sampleOffset to make only + val sz = cos(st) * soff + soff // positive points are to be sampled + val sy = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant + // DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant + val noiseValue = noises.map { it.get(sx, sy, sz) } - // mark off the position where the transition occurred - if (dirtStoneTransition == 0 && terr == 2) - dirtStoneTransition = y - if (stoneSlateTransition == 0 && terr == 3) - stoneSlateTransition = y + val terr = noiseValue[0].tiered(terragenTiers) - val isMarble = noiseValue[1] > 0.5 + // mark off the position where the transition occurred + if (dirtStoneTransition == 0 && terr == 2) + dirtStoneTransition = y + if (stoneSlateTransition == 0 && terr == 3) + stoneSlateTransition = y - val wallBlock = if (isMarble) Block.STONE_MARBLE else groundDepthBlock[terr] - val terrBlock = if (isMarble) Block.STONE_MARBLE else wallBlock + val isMarble = noiseValue[1] > 0.5 - world.setTileTerrain(x, y, terrBlock, true) - world.setTileWall(x, y, wallBlock, true) - } + val wallBlock = if (isMarble) Block.STONE_MARBLE else groundDepthBlock[terr] + val terrBlock = if (isMarble) Block.STONE_MARBLE else wallBlock - // dither shits - /* + world.setTileTerrain(x, y, terrBlock, true) + world.setTileWall(x, y, wallBlock, true) + } + + + // dither shits + /* # # - dirt-to-cobble transition, height = dirtStoneDitherSize # @@ -102,46 +98,55 @@ class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, se % * - where the stone layer actually begins */ - for (pos in 0 until dirtStoneDitherSize * 2) { - val y = pos + dirtStoneTransition - (dirtStoneDitherSize * 2) + 1 - if (y >= world.height) break - val hash = XXHash32.hashGeoCoord(x, y).and(0x7FFFFFFF) / 2147483647.0 + if (dirtStoneTransition > 0) { + for (pos in 0 until dirtStoneDitherSize * 2) { + val y = pos + dirtStoneTransition - (dirtStoneDitherSize * 2) + 1 + if (y >= world.height) break + val hash = XXHash32.hashGeoCoord(x, y).and(0x7FFFFFFF) / 2147483647.0 // val fore = world.getTileFromTerrain(x, y) // val back = world.getTileFromWall(x, y) - val newTile = if (pos < dirtStoneDitherSize) - if (hash < pos.toDouble() / dirtStoneDitherSize) Block.STONE_QUARRIED else Block.DIRT - else // don't +1 to pos.toDouble(); I've suffered - if (hash >= (pos.toDouble() - dirtStoneDitherSize) / dirtStoneDitherSize) Block.STONE_QUARRIED else Block.STONE + val newTile = if (pos < dirtStoneDitherSize) + if (hash < pos.toDouble() / dirtStoneDitherSize) Block.STONE_QUARRIED else Block.DIRT + else // don't +1 to pos.toDouble(); I've suffered + if (hash >= (pos.toDouble() - dirtStoneDitherSize) / dirtStoneDitherSize) Block.STONE_QUARRIED else Block.STONE // if (fore != Block.AIR) - world.setTileTerrain(x, y, newTile, true) + if (y in yStart until yStart + CHUNK_H) { + world.setTileTerrain(x, y, newTile, true) + world.setTileWall(x, y, newTile, true) + } + } + } - world.setTileWall(x, y, newTile, true) - } - - /* + /* # # - stone-to-slate transition, height = stoneSlateDitherSize # */ - for (pos in 0 until stoneSlateDitherSize) { - val y = pos + stoneSlateTransition - stoneSlateDitherSize + 1 - if (y >= world.height) break - val hash = XXHash32.hashGeoCoord(x, y).and(0x7FFFFFFF) / 2147483647.0 + if (stoneSlateTransition > 0) { + for (pos in 0 until stoneSlateDitherSize) { + val y = pos + stoneSlateTransition - stoneSlateDitherSize + 1 + if (y >= world.height) break + val hash = XXHash32.hashGeoCoord(x, y).and(0x7FFFFFFF) / 2147483647.0 // val fore = world.getTileFromTerrain(x, y) // val back = world.getTileFromWall(x, y) - val newTile = if (hash < pos.toDouble() / stoneSlateDitherSize) Block.STONE_SLATE else Block.STONE + val newTile = if (hash < pos.toDouble() / stoneSlateDitherSize) Block.STONE_SLATE else Block.STONE + if (y in yStart until yStart + CHUNK_H) { // if (fore != Block.AIR) - world.setTileTerrain(x, y, newTile, true) - - world.setTileWall(x, y, newTile, true) + world.setTileTerrain(x, y, newTile, true) + world.setTileWall(x, y, newTile, true) + } + } + } } } private val thicknesses = listOf(0.016, 0.021, 0.029, 0.036, 0.036, 0.029, 0.021, 0.016) - private fun getGenerator(seed: Long, params: TerragenParams): List { + override fun getGenerator(seed: Long, params: Any?): List { + val params = params as TerragenParams + // 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. diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index 5aa13027c..18a73dfba 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -1,5 +1,6 @@ package net.torvald.terrarum.modulebasegame.worldgenerator +import com.sudoplay.joise.Joise import net.torvald.random.HQRNG import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg @@ -14,31 +15,39 @@ import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIO import net.torvald.terrarum.modulebasegame.worldgenerator.Biomegen.Companion.BIOME_KEY_WOODLANDS import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import net.torvald.terrarum.serialise.toUint import kotlin.math.absoluteValue /** * Created by minjaesong on 2023-11-10. */ -class Treegen(world: GameWorld, seed: Long, val terragenParams: TerragenParams, params: TreegenParams, val biomeMap: HashMap) : Gen(world, seed, params) { +class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams: TerragenParams, params: TreegenParams, val biomeMap: HashMap) : Gen(world, isFinal, seed, params) { override fun getDone(loadscreen: LoadScreenBase) { loadscreen.stageValue += 1 loadscreen.progress.set(0L) Worldgen.threadExecutor.renew() - (0 until world.width).sliceEvenly(Worldgen.genSlices).rearrange().mapIndexed { i, xs -> - Worldgen.threadExecutor.submit { - tryToPlant(xs, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong())) - loadscreen.progress.addAndGet((xs.last - xs.first + 1).toLong()) - } - } - + submitJob(loadscreen) Worldgen.threadExecutor.join() App.printdbg(this, "Waking up Worldgen") } + + override fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) { + for (i in 0 until 10) { + val xs = (xStart + 9*i) until (xStart + 9*i) + 9 + tryToPlant(xs, makeGrassMap(xs), HQRNG(seed shake xs.last.toLong())) + } + } + + override fun getGenerator(seed: Long, params: Any?): List { + return emptyList() + } + private fun makeGrassMap(xs: IntProgression): Array> { val r = Array>(xs.last - xs.first + 1) { emptyList() } val ymax = (world.height * YHEIGHT_DIVISOR + terragenParams.featureSize).ceilToInt() diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt index 556330337..334b604a9 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Worldgen.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.modulebasegame.worldgenerator import com.jme3.math.Vector2f +import com.sudoplay.joise.Joise import com.sudoplay.joise.module.* import net.torvald.random.XXHash64 import net.torvald.terrarum.* @@ -10,7 +11,9 @@ import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.TerrarumIngame +import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil.CHUNK_H +import net.torvald.terrarum.realestate.LandUtil.CHUNK_W import kotlin.math.roundToLong /** @@ -25,7 +28,6 @@ object Worldgen { private set val threadExecutor = TerrarumIngame.worldgenThreadExecutor - var genSlices = -1 private val threadLock = java.lang.Object() @@ -60,14 +62,14 @@ object Worldgen { } } return listOf( - Work(Lang["MENU_IO_WORLDGEN_RETICULATING_SPLINES"], Terragen(world, highlandLowlandSelectCache, params.seed, params.terragenParams), listOf("TERRAIN")), - Work(Lang["MENU_IO_WORLDGEN_GROWING_MINERALS"], Oregen(world, caveAttenuateBiasScaledCache, params.seed, oreRegistry), listOf("ORES")), - Work(Lang["MENU_IO_WORLDGEN_POSITIONING_ROCKS"], OregenAutotiling(world, params.seed, oreTilingModes), listOf("ORES")), + Work(Lang["MENU_IO_WORLDGEN_RETICULATING_SPLINES"], Terragen(world, false, highlandLowlandSelectCache, params.seed, params.terragenParams), listOf("TERRAIN")), + Work(Lang["MENU_IO_WORLDGEN_GROWING_MINERALS"], Oregen(world, false, caveAttenuateBiasScaledCache, params.seed, oreRegistry), listOf("ORES")), + Work(Lang["MENU_IO_WORLDGEN_POSITIONING_ROCKS"], OregenAutotiling(world, false, params.seed, oreTilingModes), listOf("ORES")), // TODO generate rock veins // TODO generate gemstones - Work(Lang["MENU_IO_WORLDGEN_CARVING_EARTH"], Cavegen(world, highlandLowlandSelectCache, params.seed, params.terragenParams), listOf("TERRAIN", "CAVE")), - Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Biomegen(world, params.seed, params.biomegenParams, biomeMap), listOf("BIOME")), - Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Treegen(world, params.seed, params.terragenParams, params.treegenParams, biomeMap), listOf("TREES")), + Work(Lang["MENU_IO_WORLDGEN_CARVING_EARTH"], Cavegen(world, false, highlandLowlandSelectCache, params.seed, params.terragenParams), listOf("TERRAIN", "CAVE")), + Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Biomegen(world, false, params.seed, params.biomegenParams, biomeMap), listOf("BIOME")), + Work(Lang["MENU_IO_WORLDGEN_PAINTING_GREEN"], Treegen(world, true, params.seed, params.terragenParams, params.treegenParams, biomeMap), listOf("TREES")), ).filter(tagFilter) } @@ -76,8 +78,6 @@ object Worldgen { caveAttenuateBiasScaledCache = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams) biomeMap = HashMap() - genSlices = world.width / 9 - val jobs = getJobs() @@ -111,7 +111,7 @@ object Worldgen { * @return starting chunk Y index, ending chunk Y index (inclusive) */ fun getChunkGenStrip(world: GameWorld): Pair { - val start = (0.00342f * world.height - 3.22f).floorToInt() + val start = (0.00342f * world.height - 3.22f).floorToInt().coerceAtLeast(0) return start to start + 6 } @@ -340,8 +340,32 @@ object Worldgen { } -abstract class Gen(val world: GameWorld, val seed: Long, val params: Any? = null) { +abstract class Gen(val world: GameWorld, val isFinal: Boolean, val seed: Long, val params: Any? = null) { + open fun getDone(loadscreen: LoadScreenBase) { } // trying to use different name so that it won't be confused with Runnable or Callable + protected abstract fun getGenerator(seed: Long, params: Any?): List + protected abstract fun draw(xStart: Int, yStart: Int, noises: List, soff: Double) + + protected open fun getChunksRange(): List { + val (yStart, yEnd) = Worldgen.getChunkGenStrip(world) + return (0 until world.width / CHUNK_W).flatMap { cx -> + (LandUtil.chunkXYtoChunkNum(world, cx, yStart)..LandUtil.chunkXYtoChunkNum(world, cx, yEnd)).toList() + } + } + + open fun submitJob(loadscreen: LoadScreenBase) { + getChunksRange().forEach { chunkNum -> + val (chunkX, chunkY) = LandUtil.chunkNumToChunkXY(world, chunkNum) + Worldgen.threadExecutor.submit { + val localJoise = getGenerator(seed, params) + val sampleOffset = world.width / 8.0 + draw(chunkX * LandUtil.CHUNK_W, chunkY * CHUNK_H, localJoise, sampleOffset) + loadscreen.progress.addAndGet(1L) + + world.chunkFlags[chunkY][chunkX] = if (isFinal) GameWorld.CHUNK_LOADED else GameWorld.CHUNK_GENERATING + } + } + } } data class WorldgenParams(