worldgen: actually generating ores

This commit is contained in:
minjaesong
2023-10-26 15:37:48 +09:00
parent 2bd54c8316
commit 012d3482dc
17 changed files with 394 additions and 256 deletions

View File

@@ -2,7 +2,7 @@
"0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INCONSEQUENTIAL,AIR"
"1";"0";"0";"BLOCK_UPDATE";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INTERNAL"
"16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK"
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
"18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE"
"19";"19";"19";"BLOCK_STONE_BRICKS";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"STONE"
"20";"20";"20";"BLOCK_STONE_DEEP";"0.1252";"0.1252";"0.1252";"0.1252";"80";"24600";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
1 id drop spawn name shdr shdg shdb shduv str dsty mate solid wall grav dlfn fv fr lumr lumg lumb lumuv colour vscs refl tags
2 0 0 0 BLOCK_AIR 0.0312 0.0312 0.0312 0.0312 1 1 NULL 0 1 N/A 0 0 4 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 INCONSEQUENTIAL,AIR
3 1 0 0 BLOCK_UPDATE 0.0312 0.0312 0.0312 0.0312 1 1 NULL 0 1 N/A 0 0 4 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 INTERNAL
4 16 17 17 BLOCK_STONE 0.1252 0.1252 0.1252 0.1252 48 2400 ROCK 1 1 N/A 0 4 16 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 ROCK,NATURAL
5 17 17 17 BLOCK_STONE_QUARRIED 0.1252 0.1252 0.1252 0.1252 48 2400 ROCK 1 1 N/A 0 4 16 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 ROCK ROCK,NATURAL
6 18 18 18 BLOCK_STONE_TILE_WHITE 0.1252 0.1252 0.1252 0.1252 48 2400 ROCK 1 1 N/A 0 4 16 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.18 STONE
7 19 19 19 BLOCK_STONE_BRICKS 0.1252 0.1252 0.1252 0.1252 48 2400 ROCK 1 1 N/A 0 4 16 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 STONE
8 20 20 20 BLOCK_STONE_DEEP 0.1252 0.1252 0.1252 0.1252 80 24600 ROCK 1 1 N/A 0 4 16 0.0000 0.0000 0.0000 0.0000 N/A N/A 0.0 ROCK,NATURAL

View File

@@ -17,6 +17,7 @@ id;classname
16;net.torvald.terrarum.modulebasegame.gameitems.ItemWorkbench
128;net.torvald.terrarum.modulebasegame.gameitems.OreCopper
129;net.torvald.terrarum.modulebasegame.gameitems.OreIron
256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
1 id classname
17 16 net.torvald.terrarum.modulebasegame.gameitems.ItemWorkbench
18 128 net.torvald.terrarum.modulebasegame.gameitems.OreCopper
19 256 129 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak net.torvald.terrarum.modulebasegame.gameitems.OreIron
20 256 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
21 257 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
22 258 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorBirch
23 259 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorRosewood

Binary file not shown.

BIN
assets/mods/basegame/ores/2.tga LFS Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
"id";"item";"tags"
"1";"item@basegame:128";"COPPER,MALACHITE"
# "2";"item@basegame:129";"IRON,HEMATITE"
"2";"item@basegame:129";"IRON,HEMATITE"
# "3";"item@basegame:130";"GOLD,NATURAL_GOLD"
# "4";"item@basegame:131";"COAL,CARBON"
# "5";"item@basegame:132";"ZINC,SPHALERITE"
Can't render this file because it contains an unexpected character in line 3 and column 3.

View File

@@ -0,0 +1,4 @@
"id";"freq";"power";"scale";"comment"
"1";"0.026";"0.01";"0.505";"copper (malachite)"
"2";"0.040";"0.01";"0.505";"iron (haematite)"
#"3";"0.040";"0.08";"0.501";"coal"
Can't render this file because it contains an unexpected character in line 4 and column 2.

View File

@@ -15,6 +15,8 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.worldgenerator.OregenParams
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.utils.CSVFetcher
import net.torvald.terrarum.utils.JsonFetcher
@@ -508,7 +510,23 @@ object ModMgr {
}
@JvmStatic operator fun invoke(module: String) {
// register ore codex
Terrarum.oreCodex.fromModule(module, "ores/ores.csv")
// register to worldgen
try {
CSVFetcher.readFromModule(module, "ores/worldgen.csv").forEach { rec ->
val tile = "ores@$module:${rec.get("id")}"
val freq = rec.get("freq").toDouble()
val power = rec.get("power").toDouble()
val scale = rec.get("scale").toDouble()
Worldgen.registerOre(OregenParams(tile, freq, power, scale))
}
}
catch (e: IOException) {
e.printStackTrace()
}
}
}

View File

@@ -582,6 +582,9 @@ open class GameWorld(
throw IllegalArgumentException("illegal mode input: $mode")
}
/**
* Will return (Block.AIR, 0) if there is no ore
*/
fun getTileFromOre(rawX: Int, rawY: Int): OrePlacement {
val (x, y) = coerceXY(rawX, rawY)
val (tileNum, placement) = layerOres.unsafeGetTile1(x, y)

View File

@@ -569,7 +569,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement ->
acc or (placement.item == palSelection).toInt(index)
}
val placement = BlocksDrawer.connectLut47[autotiled]
val placement = BlocksDrawer.connectLut16[autotiled]
world.setTileOre(x, y, palSelection, placement)
}

View File

@@ -102,7 +102,7 @@ open class DroppedItem : ActorWithBody {
visualItemID = BlockCodex.getOrNull(itemID)?.world ?: itemID
}
if (textureRegion == null) {
textureRegion = ItemCodex.getItemImage(visualItemID)!!
textureRegion = ItemCodex.getItemImage(visualItemID) ?: throw NullPointerException("No Item image for ${visualItemID}")
}
// copy-pasted from ActorWithBody.drawSpriteInGoodPosition()

View File

@@ -35,3 +35,8 @@ class OreCopper(originalID: ItemID) : OreItemBase(originalID) {
override val itemImage: TextureRegion
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(2,6)
}
class OreIron(originalID: ItemID) : OreItemBase(originalID) {
override var originalName = "ITEM_ORE_HAEMATITE"
override val itemImage: TextureRegion
get() = CommonResourcePool.getAsItemSheet("basegame.items").get(3,6)
}

View File

@@ -1,12 +1,18 @@
package net.torvald.terrarum.modulebasegame.worldgenerator
import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.*
import net.torvald.terrarum.BlockCodex
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_DIVISOR
import net.torvald.terrarum.modulebasegame.worldgenerator.Terragen.Companion.YHEIGHT_MAGIC
import net.torvald.terrarum.toInt
import net.torvald.terrarum.utils.OrePlacement
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.sin
@@ -14,7 +20,7 @@ import kotlin.math.sin
/**
* Created by minjaesong on 2023-10-25.
*/
class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, params) {
class Oregen(world: GameWorld, private val caveAttenuateBiasScaled: ModuleScaleDomain, seed: Long, private val ores: List<OregenParams>) : Gen(world, seed) {
private val threadExecutor = TerrarumIngame.worldgenThreadExecutor
private val genSlices = max(threadExecutor.threadCount, world.width / 8)
@@ -23,7 +29,7 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
threadExecutor.renew()
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
threadExecutor.submit {
val localJoise = getGenerator(seed, params as OregenParams)
val localJoise = getGenerator(seed)
for (x in xs) {
val sampleTheta = (x.toDouble() / world.width) * TWO_PI
val sampleOffset = world.width / 8.0
@@ -38,10 +44,15 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
/**
* @return List of noise instances, each instance refers to one of the spawnable ores
*/
private fun getGenerator(seed: Long, params: OregenParams): List<Joise> {
TODO()
private fun getGenerator(seed: Long): List<Joise> {
return ores.map {
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake it.tile, it.freq, it.power, it.scale))
}
}
/**
* Indices of `noises` has one-to-one mapping to the `ores`
*/
private fun draw(x: Int, noises: List<Joise>, st: Double, soff: Double) {
for (y in 0 until world.height) {
val sx = sin(st) * soff + soff // plus sampleOffset to make only
@@ -50,15 +61,90 @@ class Oregen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, param
// DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant
// 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 }
TODO()
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].hasTag("ROCK")) {
// actually put the ore block
world.setTileOre(x, y, tileToPut, 0) // autotiling will be handled by the other worldgen process
}
}
}
private fun applyPowMult(joiseModule: Module, pow: Double, mult: Double): Module {
return ModuleScaleOffset().also {
it.setSource(ModulePow().also {
it.setSource(joiseModule)
it.setPower(pow)
})
it.setScale(mult)
}
}
private fun generateOreVeinModule(caveAttenuateBiasScaled: ModuleScaleDomain, seed: Long, freq: Double, pow: Double, scale: Double): Module {
val oreShape = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.RIDGEMULTI)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(2)
it.setFrequency(freq) // adjust the "density" of the caves
it.seed = seed
}
val oreShape2 = ModuleScaleOffset().also {
it.setSource(oreShape)
it.setScale(1.0)
it.setOffset(-0.5)
}
val caveAttenuateBias3 = applyPowMult(caveAttenuateBiasScaled, pow, scale)
val oreShapeAttenuate = ModuleCombiner().also {
it.setType(ModuleCombiner.CombinerType.MULT)
it.setSource(0, oreShape2)
it.setSource(1, caveAttenuateBias3)
}
val orePerturbFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(6)
it.setFrequency(freq * 3.0 / 4.0)
it.seed = seed shake 0x5721CE_76E_EA276L // strike the earth
}
val orePerturbScale = ModuleScaleOffset().also {
it.setSource(orePerturbFractal)
it.setScale(20.0)
it.setOffset(0.0)
}
val orePerturb = ModuleTranslateDomain().also {
it.setSource(oreShapeAttenuate)
it.setAxisXSource(orePerturbScale)
}
val oreSelect = ModuleSelect().also {
it.setLowSource(0.0)
it.setHighSource(1.0)
it.setControlSource(orePerturb)
it.setThreshold(0.5)
it.setFalloff(0.0)
}
return oreSelect
}
}
data class OregenParams(
val oreShapeFreq: Double = 0.04, //adjust the "density" of the caves
val oreAttenuateBias: Double = 0.90, // adjust the "concentration" of the cave gen. Lower = larger voids
val oreSelectThre: Double = 0.918, // also adjust this if you've touched the bias value. Number can be greater than 1.0
val tile: String,
val freq: Double, //adjust the "density" of the caves
val power: Double, // adjust the "concentration" of the cave gen. Lower = larger voids
val scale: Double, // also adjust this if you've touched the bias value. Number can be greater than 1.0
)

View File

@@ -0,0 +1,69 @@
package net.torvald.terrarum.modulebasegame.worldgenerator
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.gameitems.isOre
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.toInt
import net.torvald.terrarum.utils.OrePlacement
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import kotlin.math.max
/**
* Created by minjaesong on 2023-10-26.
*/
class OregenAutotiling(world: GameWorld, seed: Long) : Gen(world, seed) {
private val threadExecutor = TerrarumIngame.worldgenThreadExecutor
private val genSlices = max(threadExecutor.threadCount, world.width / 8)
override fun getDone() {
threadExecutor.renew()
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
threadExecutor.submit {
for (x in xs) {
draw(x)
}
}
}
threadExecutor.join()
}
private fun draw(x: Int) {
for (y in 0 until world.height) {
val (ore, _) = world.getTileFromOre(x, y)
if (ore.isOre()) {
// get placement (tile connection) info
val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement ->
acc or (placement.item == ore).toInt(index)
}
val placement = BlocksDrawer.connectLut16[autotiled]
// actually put the ore block
world.setTileOre(x, y, ore, placement) // autotiling will be handled by the other worldgen process
}
}
}
private fun getNearbyTilesPos8(x: Int, y: Int): Array<Point2i> {
return arrayOf(
Point2i(x + 1, y),
Point2i(x + 1, y + 1),
Point2i(x, y + 1),
Point2i(x - 1, y + 1),
Point2i(x - 1, y),
Point2i(x - 1, y - 1),
Point2i(x, y - 1),
Point2i(x + 1, y - 1)
)
}
private fun getNearbyOres8(x: Int, y: Int): List<OrePlacement> {
return getNearbyTilesPos8(x, y).map { world.getTileFromOre(it.x, it.y) }
}
}

View File

@@ -15,7 +15,7 @@ import kotlin.math.sin
/**
* Created by minjaesong on 2019-07-23.
*/
class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, params) {
class Terragen(world: GameWorld, val highlandLowlandSelectCache: ModuleCache, seed: Long, params: Any) : Gen(world, seed, params) {
companion object {
const val YHEIGHT_MAGIC = 2800.0 / 3.0
@@ -137,160 +137,10 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
private fun getGenerator(seed: Long, params: TerragenParams): List<Joise> {
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().also {
it.setGradient(0.0, 0.0, 0.0, 1.0)
}
/* lowlands */
val lowlandShapeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.BILLOW)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(2)
it.setFrequency(0.25)
it.seed = seed shake lowlandMagic
}
val lowlandScale = ModuleScaleOffset().also {
it.setSource(lowlandShapeFractal)
it.setScale(0.22)
it.setOffset(params.lowlandScaleOffset) // linearly alters the height
}
val lowlandYScale = ModuleScaleDomain().also {
it.setSource(lowlandScale)
it.setScaleY(0.02) // greater = more distortion, overhangs
}
val lowlandTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(lowlandYScale)
}
/* highlands */
val highlandShapeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(4)
it.setFrequency(2.0)
it.seed = seed shake highlandMagic
}
val highlandScale = ModuleScaleOffset().also {
it.setSource(highlandShapeFractal)
it.setScale(0.5)
it.setOffset(params.highlandScaleOffset) // linearly alters the height
}
val highlandYScale = ModuleScaleDomain().also {
it.setSource(highlandScale)
it.setScaleY(0.14) // greater = more distortion, overhangs
}
val highlandTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(highlandYScale)
}
/* mountains */
val mountainShapeFractal = ModuleFractal().also {
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(8)
it.setFrequency(1.0)
it.seed = seed shake mountainMagic
}
val mountainScale = ModuleScaleOffset().also {
it.setSource(mountainShapeFractal)
it.setScale(1.0)
it.setOffset(params.mountainScaleOffset) // linearly alters the height
}
val mountainYScale = ModuleScaleDomain().also {
it.setSource(mountainScale)
it.setScaleY(params.mountainDisturbance) // greater = more distortion, overhangs
}
val mountainTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(mountainYScale)
}
/* selection */
val terrainTypeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(3)
it.setFrequency(0.125)
it.seed = seed shake selectionMagic
}
val terrainScaleOffset = ModuleScaleOffset().also {
it.setSource(terrainTypeFractal)
it.setOffset(0.5)
it.setScale(0.666666) // greater = more dynamic terrain
}
val terrainTypeYScale = ModuleScaleDomain().also {
it.setSource(terrainScaleOffset)
it.setScaleY(0.0)
}
val terrainTypeCache = ModuleCache().also {
it.setSource(terrainTypeYScale)
}
val highlandMountainSelect = ModuleSelect().also {
it.setLowSource(highlandTerrain)
it.setHighSource(mountainTerrain)
it.setControlSource(terrainTypeCache)
it.setThreshold(0.55)
it.setFalloff(0.2)
}
val highlandLowlandSelect = ModuleSelect().also {
it.setLowSource(lowlandTerrain)
it.setHighSource(highlandMountainSelect)
it.setControlSource(terrainTypeCache)
it.setThreshold(0.25)
it.setFalloff(0.15)
}
val highlandLowlandSelectCache = ModuleCache().also {
it.setSource(highlandLowlandSelect)
}
val groundSelect = ModuleSelect().also {
it.setLowSource(0.0)
it.setHighSource(1.0)
it.setThreshold(0.5)
it.setControlSource(highlandLowlandSelectCache)
}
val groundSelect2 = ModuleSelect().also {
it.setLowSource(0.0)
it.setHighSource(1.0)
it.setThreshold(0.8)
it.setControlSource(highlandLowlandSelectCache)
}
/* caves */
val caveShape = ModuleFractal().also {
@@ -401,6 +251,7 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
it.setSource(caveClamp)
}
//return Joise(caveInMix)
return listOf(
Joise(groundScaling),
@@ -422,13 +273,13 @@ data class TerragenParams(
val caveBlockageFractalFreq: Double = 8.88,
val caveBlockageSelectThre: Double = 1.40, // adjust cave cloing-up strength. Larger = more closing
val oreCopperFreq: Double = 0.024, // adjust the "density" of the ore veins
val oreCopperPower: Double = 0.01, // super-low value almost negates the depth element
val oreCopperScale: Double = 0.505,
// val oreCopperFreq: Double = 0.024, // adjust the "density" of the ore veins
// val oreCopperPower: Double = 0.01, // super-low value almost negates the depth element
// val oreCopperScale: Double = 0.505,
val oreIronFreq: Double = 0.04, // adjust the "density" of the ore veins
val oreIronPower: Double = 0.01, // super-low value almost negates the depth element
val oreIronScale: Double = 0.505,
// val oreIronFreq: Double = 0.04, // adjust the "density" of the ore veins
// val oreIronPower: Double = 0.01, // super-low value almost negates the depth element
// val oreIronScale: Double = 0.505,
// 0.01 - 0.505

View File

@@ -1,5 +1,7 @@
package net.torvald.terrarum.modulebasegame.worldgenerator
import com.sudoplay.joise.module.*
import net.torvald.random.XXHash64
import net.torvald.terrarum.App
import net.torvald.terrarum.App.*
import net.torvald.terrarum.BlockCodex
@@ -24,10 +26,27 @@ object Worldgen {
params = genParams
}
internal lateinit var highlandLowlandSelectCache: ModuleCache
internal lateinit var caveAttenuateBiasScaled: ModuleScaleDomain
/**
* Other modules are free to add their own ores to the world generator.
*/
fun registerOre(oreInfo: OregenParams) {
oreRegistry.add(oreInfo)
}
private val oreRegistry = ArrayList<OregenParams>()
fun generateMap() {
highlandLowlandSelectCache = getHighlandLowlandSelectCache(params.terragenParams, params.seed)
caveAttenuateBiasScaled = getCaveAttenuateBiasScaled(highlandLowlandSelectCache, params.terragenParams)
val jobs = listOf(
Work("Reticulating Splines", Terragen(world, params.seed, params.terragenParams)),
Work("Adding Vegetations", Biomegen(world, params.seed, params.biomegenParams))
Work("Reticulating Splines", Terragen(world, highlandLowlandSelectCache, params.seed, params.terragenParams)),
Work("Adding Rocks", Oregen(world, caveAttenuateBiasScaled, params.seed, oreRegistry)),
Work("Positioning Rocks", OregenAutotiling(world, params.seed)),
Work("Adding Vegetations", Biomegen(world, params.seed, params.biomegenParams)),
)
@@ -66,9 +85,163 @@ object Worldgen {
fun getEstimationSec(width: Int, height: Int): Long {
return (23.05 * 1.25 * (48600000.0 / bogoflops) * ((width * height) / 40095000.0) * (32.0 / THREAD_COUNT)).roundToLong()
}
private fun getHighlandLowlandSelectCache(params: TerragenParams, seed: Long): ModuleCache {
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 groundGradient = ModuleGradient().also {
it.setGradient(0.0, 0.0, 0.0, 1.0)
}
/* lowlands */
val lowlandShapeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.BILLOW)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(2)
it.setFrequency(0.25)
it.seed = seed shake lowlandMagic
}
val lowlandScale = ModuleScaleOffset().also {
it.setSource(lowlandShapeFractal)
it.setScale(0.22)
it.setOffset(params.lowlandScaleOffset) // linearly alters the height
}
val lowlandYScale = ModuleScaleDomain().also {
it.setSource(lowlandScale)
it.setScaleY(0.02) // greater = more distortion, overhangs
}
val lowlandTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(lowlandYScale)
}
/* highlands */
val highlandShapeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(4)
it.setFrequency(2.0)
it.seed = seed shake highlandMagic
}
val highlandScale = ModuleScaleOffset().also {
it.setSource(highlandShapeFractal)
it.setScale(0.5)
it.setOffset(params.highlandScaleOffset) // linearly alters the height
}
val highlandYScale = ModuleScaleDomain().also {
it.setSource(highlandScale)
it.setScaleY(0.14) // greater = more distortion, overhangs
}
val highlandTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(highlandYScale)
}
/* mountains */
val mountainShapeFractal = ModuleFractal().also {
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(8)
it.setFrequency(1.0)
it.seed = seed shake mountainMagic
}
val mountainScale = ModuleScaleOffset().also {
it.setSource(mountainShapeFractal)
it.setScale(1.0)
it.setOffset(params.mountainScaleOffset) // linearly alters the height
}
val mountainYScale = ModuleScaleDomain().also {
it.setSource(mountainScale)
it.setScaleY(params.mountainDisturbance) // greater = more distortion, overhangs
}
val mountainTerrain = ModuleTranslateDomain().also {
it.setSource(groundGradient)
it.setAxisYSource(mountainYScale)
}
/* selection */
val terrainTypeFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(3)
it.setFrequency(0.125)
it.seed = seed shake selectionMagic
}
val terrainScaleOffset = ModuleScaleOffset().also {
it.setSource(terrainTypeFractal)
it.setOffset(0.5)
it.setScale(0.666666) // greater = more dynamic terrain
}
val terrainTypeYScale = ModuleScaleDomain().also {
it.setSource(terrainScaleOffset)
it.setScaleY(0.0)
}
val terrainTypeCache = ModuleCache().also {
it.setSource(terrainTypeYScale)
}
val highlandMountainSelect = ModuleSelect().also {
it.setLowSource(highlandTerrain)
it.setHighSource(mountainTerrain)
it.setControlSource(terrainTypeCache)
it.setThreshold(0.55)
it.setFalloff(0.2)
}
val highlandLowlandSelect = ModuleSelect().also {
it.setLowSource(lowlandTerrain)
it.setHighSource(highlandMountainSelect)
it.setControlSource(terrainTypeCache)
it.setThreshold(0.25)
it.setFalloff(0.15)
}
val highlandLowlandSelectCache = ModuleCache().also {
it.setSource(highlandLowlandSelect)
}
return highlandLowlandSelectCache
}
private fun getCaveAttenuateBiasScaled(highlandLowlandSelectCache: ModuleCache, params: TerragenParams): ModuleScaleDomain {
val caveAttenuateBias = ModuleBias().also {
it.setSource(highlandLowlandSelectCache)
it.setBias(params.caveAttenuateBias) // (0.5+) adjust the "concentration" of the cave gen. Lower = larger voids
}
return ModuleScaleDomain().also {
it.setScaleX(1.0 / params.featureSize) // adjust this value to change features size
it.setScaleY(1.0 / params.featureSize)
it.setScaleZ(1.0 / params.featureSize)
it.setSource(caveAttenuateBias)
}
}
}
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any) {
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any? = null) {
open fun getDone() { } // trying to use different name so that it won't be confused with Runnable or Callable
}
@@ -90,5 +263,9 @@ infix fun Long.shake(other: Long): Long {
return s0 + s1
}
infix fun Long.shake(other: String): Long {
return this shake XXHash64.hash(other.toByteArray(), this + 31)
}
val TWO_PI = Math.PI * 2.0
val HALF_PI = Math.PI / 2.0

View File

@@ -81,7 +81,6 @@ class WorldgenNoiseSandbox : ApplicationAdapter() {
private val NM_TERR = TerragenTest to TerragenParams()
private val NM_BIOME = BiomeMaker to BiomegenParams()
private val NM_ORES = Oregen to OregenParams()
private val NOISEMAKER = NM_TERR
@@ -687,8 +686,8 @@ internal object TerragenTest : NoiseMaker {
return listOf(
Joise(groundScaling),
Joise(caveScaling),
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake 0xC08204, params.oreCopperFreq, params.oreCopperPower, params.oreCopperScale)),
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake 0xFE2204, params.oreIronFreq, params.oreIronPower, params.oreIronScale)),
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake "ores@basegame:1", 0.024, 0.01, 0.505)),
Joise(generateOreVeinModule(caveAttenuateBiasScaled, seed shake "ores@basegame:2", 0.04, 0.01, 0.505)),
)
}
@@ -759,84 +758,6 @@ internal object TerragenTest : NoiseMaker {
}
internal object Oregen : NoiseMaker {
override fun draw(x: Int, y: Int, noiseValue: List<Double>, outTex: Pixmap) {
var n = noiseValue[0]
// if (n in 0.0..1.0) n = 1.0 - n
val cout = if (n >= 0.0)
Color(n.toFloat(), n.toFloat(), n.toFloat(), 1f)
else
Color(-n.toFloat(), 0f, 1f, 1f)
outTex.drawPixel(x, y, cout.toRGBA())
}
override fun getGenerator(seed: Long, params: Any): List<Joise> {
val params = params as OregenParams
val oreMagic = 0x023L
val orePerturbMagic = 12345L
val oreShape = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.RIDGEMULTI)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(2)
it.setFrequency(params.oreShapeFreq) // adjust the "density" of the caves
it.seed = seed shake oreMagic
}
val oreShape2 = ModuleScaleOffset().also {
it.setSource(oreShape)
it.setScale(1.0)
it.setOffset(-0.5)
}
val orePerturbFractal = ModuleFractal().also {
it.setType(ModuleFractal.FractalType.FBM)
it.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
it.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
it.setNumOctaves(6)
it.setFrequency(params.oreShapeFreq * 3.0 / 4.0)
it.seed = seed shake orePerturbMagic
}
val orePerturbScale = ModuleScaleOffset().also {
it.setSource(orePerturbFractal)
it.setScale(20.0)
it.setOffset(0.0)
}
val orePerturb = ModuleTranslateDomain().also {
it.setSource(oreShape2)
it.setAxisXSource(orePerturbScale)
}
val oreSelectAttenuate = ModulePow().also {
it.setSource(ModuleGradient().also {
it.setGradient(0.0, 0.0, NOISEBOX_HEIGHT.toDouble() * 5, 100.0)
})
it.setPower(1.0 / 4.0)
}
val oreSelect = ModuleSelect().also {
it.setLowSource(0.0)
it.setHighSource(1.0)
it.setControlSource(orePerturb)
it.setThreshold(oreSelectAttenuate)
it.setFalloff(0.0)
}
return listOf(
Joise(oreSelect)
)
}
}
/*infix fun Long.shake(other: Long): Long {
var s0 = this
var s1 = other

View File

@@ -248,7 +248,7 @@ class BasicDebugInfoWindow : UICanvas() {
val wallNum = it.getTileFromWall(mouseTileX, mouseTileY)
val tileNum = it.getTileFromTerrain(mouseTileX, mouseTileY)
val oreNum = it.getTileFromOre(mouseTileX, mouseTileY).item
val (oreNum, orePlacement) = it.getTileFromOre(mouseTileX, mouseTileY)
val wires = it.getAllWiresFrom(mouseTileX, mouseTileY)
val fluid = it.getFluid(mouseTileX, mouseTileY)
val wireCount = wires.first?.size?.toString() ?: "no"
@@ -256,7 +256,7 @@ class BasicDebugInfoWindow : UICanvas() {
App.fontSmallNumbers.draw(batch, "$ccO$TERRAIN$ccG$tileNum", gap + 7f*(tileCursX + 3), line(tileCursY))
App.fontSmallNumbers.draw(batch, "$ccO$WALL$ccG$wallNum", gap + 7f*(tileCursX + 3), line(tileCursY + 1))
// App.fontSmallNumbers.draw(batch, "$ccO$LIQUID$ccG${fluid.type.padEnd(3)}$ccO$BEAKER$ccG${fluid.amount.toIntAndFrac(2)}", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
App.fontSmallNumbers.draw(batch, "$ccO$ROCK$ccG$oreNum", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
App.fontSmallNumbers.draw(batch, "$ccO$ROCK$ccG$oreNum.$orePlacement", gap + 7f*(tileCursX + 3), line(tileCursY + 2))
App.fontSmallNumbers.draw(batch, "$ccO$WIRE$ccG$wireCount ${ccY}X$ccO$mouseTileX ${ccY}Y$ccO$mouseTileY", gap + 7f*(tileCursX + 3), line(tileCursY + 3))
App.fontSmallNumbers.draw(batch, "$ccR$rawR $ccG$rawG $ccB$rawB $ccW$rawA", gap + 7f*(tileCursX + 3), line(tileCursY + 4))