From a903b9cff858bc9e7d8dc1964b3e2d4906bb67bd Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 1 Mar 2025 22:48:01 +0700 Subject: [PATCH] Enormous placement modifier, greatly enhance dilithium ore placement --- build.gradle.kts | 16 +-- gradle.properties | 1 + .../ru/dbotthepony/mc/otm/datagen/WorldGen.kt | 55 ++++----- .../mc/otm/data/codec/ComparableCodec.kt | 2 + .../mc/otm/registry/data/MHeightProviders.kt | 2 +- .../otm/registry/data/MPlacementModifiers.kt | 4 +- .../placement/AbstractEnormousPlacement.kt | 110 ++++++++++++++++++ .../placement}/EllipsoidPlacement.kt | 59 +++------- .../placement/EnormousEllipsoidPlacement.kt | 60 ++++++++++ .../worldgen/placement/IEllipsoidPlacement.kt | 81 +++++++++++++ .../StandardDeviationHeightProvider.kt | 2 +- 11 files changed, 306 insertions(+), 86 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/AbstractEnormousPlacement.kt rename src/main/kotlin/ru/dbotthepony/mc/otm/{data/world => worldgen/placement}/EllipsoidPlacement.kt (60%) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnormousEllipsoidPlacement.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/IEllipsoidPlacement.kt rename src/main/kotlin/ru/dbotthepony/mc/otm/{data/world => worldgen/placement}/StandardDeviationHeightProvider.kt (97%) diff --git a/build.gradle.kts b/build.gradle.kts index 19847cef5..c8df749e0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ val mod_id: String by project val handle_deps: String by project val use_commit_hash_in_version: String by project val handleDeps = handle_deps.toBoolean() +val caffeine_cache_version: String by project plugins { java @@ -142,14 +143,11 @@ dependencies { implementation("thedarkcolour:kotlinforforge-neoforge:$kotlin_for_forge_version") - jarJar("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) } - implementation("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) } + jarJar(implementation("com.github.ben-manes.caffeine:caffeine:[$caffeine_cache_version,)")!!) - jarJar("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) } - implementation("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) } - - jarJar("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) } - implementation("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) } + jarJar(implementation("ru.dbotthepony.kommons:kommons:[$kommons_version,)") { setTransitive(false) }) + jarJar(implementation("ru.dbotthepony.kommons:kommons-gson:[$kommons_version,)") { setTransitive(false) }) + jarJar(implementation("ru.dbotthepony.kommons:kommons-guava:[$kommons_version,)") { setTransitive(false) }) compileOnly("yalter.mousetweaks:MouseTweaks:2.23:api") annotationProcessor("org.spongepowered:mixin:${mixin_version}:processor") @@ -228,6 +226,10 @@ minecraft { // Log4j console level systemProperty("forge.logging.console.level", "debug") + + dependencies { + runtime("com.github.ben-manes.caffeine:caffeine:[$caffeine_cache_version,)") + } } getByName("client") { diff --git a/gradle.properties b/gradle.properties index 171915422..47fdf3e3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,6 +23,7 @@ neogradle.subsystems.parchment.minecraftVersion=1.21.1 neogradle.subsystems.parchment.mappingsVersion=2024.11.17 kommons_version=3.1.3 +caffeine_cache_version=3.1.5 jei_version=19.16.4.171 jupiter_version=5.9.2 diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt index ad8c630ef..dc28a79f1 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt @@ -9,6 +9,7 @@ import net.minecraft.tags.BlockTags import net.minecraft.util.valueproviders.ClampedNormalFloat import net.minecraft.util.valueproviders.ClampedNormalInt import net.minecraft.util.valueproviders.ConstantFloat +import net.minecraft.util.valueproviders.UniformFloat import net.minecraft.util.valueproviders.UniformInt import net.minecraft.world.level.levelgen.GenerationStep import net.minecraft.world.level.levelgen.VerticalAnchor @@ -25,11 +26,12 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest import net.neoforged.neoforge.common.world.BiomeModifier import net.neoforged.neoforge.registries.NeoForgeRegistries import ru.dbotthepony.mc.otm.core.math.Decimal -import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement -import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider +import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider import ru.dbotthepony.mc.otm.registry.game.MBlocks import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature +import ru.dbotthepony.mc.otm.worldgen.placement.AbstractEnormousPlacement +import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement private object ConfiguredFeatures { val TRITANIUM_ORE = key("tritanium_ore") @@ -71,7 +73,6 @@ fun registerConfiguredFeatures(context: BootstrapContext private object PlacedFeatures { val NORMAL_TRITANIUM = key("normal_tritanium") val DEEP_TRITANIUM = key("deep_tritanium") - val CLOUD_TITANIUM = key("cloud_tritanium") val DILITHIUM = key("dilithium") val BLACK_HOLE = key("black_hole") @@ -103,24 +104,6 @@ fun registerPlacedFeatures(context: BootstrapContext) { HeightRangePlacement.of(VeryBiasedToBottomHeight.of(VerticalAnchor.aboveBottom(4), VerticalAnchor.absolute(0), 16)) ) )) - - context.register(PlacedFeatures.CLOUD_TITANIUM, PlacedFeature( - ore, - listOf( - RarityFilter.onAverageOnceEvery(16), - InSquarePlacement.spread(), - HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0)), - EllipsoidPlacement( - x = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE), - y = ClampedNormalInt.of(0f, 12f, Int.MIN_VALUE, Int.MAX_VALUE), - z = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE), - count = ClampedNormalInt.of(60f, 60f, 40, 160), - xLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f), - yLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f), - zLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f), - ) - ) - )) } run { @@ -129,19 +112,22 @@ fun registerPlacedFeatures(context: BootstrapContext) { context.register(PlacedFeatures.DILITHIUM, PlacedFeature( ore, listOf( - RarityFilter.onAverageOnceEvery(12), - InSquarePlacement.spread(), - HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)), - EllipsoidPlacement( - x = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE), - y = ClampedNormalInt.of(0f, 20f, Int.MIN_VALUE, Int.MAX_VALUE), - z = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE), - count = ClampedNormalInt.of(200f, 200f, 200, 600), - xLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f), - // allow crystals to generate as far as standard deviation allows - // to increase chance for player to discover crystal vein - yLength = ConstantFloat.of(60f), - zLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f), + EnormousEllipsoidPlacement( + parameters = AbstractEnormousPlacement.Parameters( + chunkScanRange = 4, + placementModifiers = listOf( + RarityFilter.onAverageOnceEvery(40), + InSquarePlacement.spread(), + HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)), + ) + ), + x = ClampedNormalFloat.of(0f, 0.5f, 0f, 2f), + y = ClampedNormalFloat.of(0f, 0.5f, 0f, 2f), + z = ClampedNormalFloat.of(0f, 0.5f, 0f, 2f), + count = UniformInt.of(4800, 12400), + xLength = UniformFloat.of(60f, 80f), + yLength = UniformFloat.of(60f, 80f), + zLength = UniformFloat.of(60f, 80f), ) ) )) @@ -179,7 +165,6 @@ fun registerBiomeModifiers(context: BootstrapContext) { HolderSet.direct( placed.getOrThrow(PlacedFeatures.NORMAL_TRITANIUM), placed.getOrThrow(PlacedFeatures.DEEP_TRITANIUM), - placed.getOrThrow(PlacedFeatures.CLOUD_TITANIUM), placed.getOrThrow(PlacedFeatures.DILITHIUM), ), GenerationStep.Decoration.UNDERGROUND_ORES diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/codec/ComparableCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/codec/ComparableCodec.kt index 7788628a8..a4000b818 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/codec/ComparableCodec.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/codec/ComparableCodec.kt @@ -53,3 +53,5 @@ fun > Codec.maxRange(max: S, exclusive: Boolean = false) = ComparableCodec(this, max = max, maxExclusive = exclusive) fun > Codec.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) = ComparableCodec(this, min, max, minExclusive, maxExclusive) +fun > Codec.inRange(min: S, max: S) = + ComparableCodec(this, min, max, false, false) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MHeightProviders.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MHeightProviders.kt index 92b860932..aea660f42 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MHeightProviders.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MHeightProviders.kt @@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.registry.data import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.world.level.levelgen.heightproviders.HeightProviderType import net.neoforged.bus.api.IEventBus -import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider +import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider import ru.dbotthepony.mc.otm.registry.MDeferredRegister object MHeightProviders { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt index bfb73a091..57dabc1e9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt @@ -3,8 +3,9 @@ package ru.dbotthepony.mc.otm.registry.data import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.world.level.levelgen.placement.PlacementModifierType import net.neoforged.bus.api.IEventBus -import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement import ru.dbotthepony.mc.otm.registry.MDeferredRegister +import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement +import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement object MPlacementModifiers { private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE) @@ -14,4 +15,5 @@ object MPlacementModifiers { } val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } } + val ENORMOUS_ELLIPSOID_PLACEMENT by registry.register("enormous_ellipsoid") { PlacementModifierType { EnormousEllipsoidPlacement.CODEC } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/AbstractEnormousPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/AbstractEnormousPlacement.kt new file mode 100644 index 000000000..760c61b84 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/AbstractEnormousPlacement.kt @@ -0,0 +1,110 @@ +package ru.dbotthepony.mc.otm.worldgen.placement + +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.Scheduler +import com.mojang.serialization.Codec +import com.mojang.serialization.MapCodec +import com.mojang.serialization.codecs.RecordCodecBuilder +import it.unimi.dsi.fastutil.HashCommon +import net.minecraft.Util +import net.minecraft.core.BlockPos +import net.minecraft.core.SectionPos +import net.minecraft.util.RandomSource +import net.minecraft.world.level.ChunkPos +import net.minecraft.world.level.levelgen.placement.PlacementContext +import net.minecraft.world.level.levelgen.placement.PlacementModifier +import ru.dbotthepony.mc.otm.data.codec.inRange +import ru.dbotthepony.mc.otm.data.codec.minRange +import java.time.Duration +import java.util.stream.Stream +import kotlin.math.sqrt + +/** + * Enormous placement base, which allows it to span over several chunks without issues. + * + * MUST come as first placement modifier, other placement modifiers (such as rarity and + * shuffle of center point within chunks) must be provided inside [Parameters.placementModifiers] list, in same order as if they were + * *before* this placement + */ +abstract class AbstractEnormousPlacement(val parameters: Parameters) : PlacementModifier() { + data class Parameters( + /** + * How many chunks away to look for actual placements + * + * Too small value will cause placement cutoffs + */ + val chunkScanRange: Int, + + /** + * Baseline placement modifiers, dictating how to appear in chunk + */ + val placementModifiers: List, + + /** + * How many chunks to remember placements in, usually not required to be adjusted + */ + val chunkCacheSize: Int = 16384, + ) + + private class GeneratedChunk(positions: Stream) { + private val positions = ArrayList() + + init { + positions.forEach { this.positions.add(it) } + } + + fun getPositions(pos: ChunkPos): Stream { + return positions.stream().filter { + SectionPos.blockToSectionCoord(it.x) == pos.x && + SectionPos.blockToSectionCoord(it.z) == pos.z + } + } + } + + protected abstract fun getPositions(center: BlockPos, random: RandomSource): Stream + + private val chunkCache = Caffeine.newBuilder() + .scheduler(Scheduler.systemScheduler()) + .executor(Util.backgroundExecutor()) + .maximumSize(parameters.chunkCacheSize.toLong()) + .expireAfterWrite(Duration.ofMinutes(5)) + .softValues() + .build() + + private fun computeChunk(context: PlacementContext, pos: ChunkPos): GeneratedChunk { + val random = RandomSource.create(context.level.seed + HashCommon.murmurHash3(pos.x + pos.z * 31)) + var stream = Stream.of(BlockPos(pos.minBlockX, 0, pos.minBlockZ)) + parameters.placementModifiers.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } } + return GeneratedChunk(stream.flatMap { getPositions(it, random) }) + } + + final override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream { + val cPos = ChunkPos(pos) + val instances = ArrayList() + + for (x in -parameters.chunkScanRange .. parameters.chunkScanRange) { + for (z in -parameters.chunkScanRange .. parameters.chunkScanRange) { + // floor, so chunk scan range of 1 will give square instead of diamond + val radius = sqrt(x.toDouble() * x + z.toDouble() * z).toInt() + + if (radius <= parameters.chunkScanRange) { + val thisPos = ChunkPos(cPos.x + x, cPos.z + z) + + instances.add(chunkCache.get(thisPos) { computeChunk(context, thisPos) }) + } + } + } + + return instances.stream().flatMap { it.getPositions(cPos) } + } + + companion object { + val PARAMETERS_CODEC: MapCodec = RecordCodecBuilder.mapCodec { + it.group( + Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Parameters::chunkScanRange), + CODEC.listOf().fieldOf("placement").forGetter(Parameters::placementModifiers), + Codec.INT.inRange(0, 128_000).optionalFieldOf("cache_size", 16384).forGetter(Parameters::chunkCacheSize), + ).apply(it, ::Parameters) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/EllipsoidPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt similarity index 60% rename from src/main/kotlin/ru/dbotthepony/mc/otm/data/world/EllipsoidPlacement.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt index 2079d91ef..3bcf1107e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/EllipsoidPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EllipsoidPlacement.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.mc.otm.data.world +package ru.dbotthepony.mc.otm.worldgen.placement import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder @@ -19,16 +19,20 @@ import java.util.stream.Stream import kotlin.math.PI import kotlin.math.roundToInt -// aka "cloud placement" +/** + * Regular ellipsoid ("cloud") placement, suitable to be used as non-primary placement modifier + * + * This placement modifier is designed to be terminal; other placement modifiers after this MUST NOT be placed + */ data class EllipsoidPlacement( - val x: IntProvider, - val z: IntProvider, - val y: IntProvider, - val count: IntProvider, - val xLength: FloatProvider, - val zLength: FloatProvider, - val yLength: FloatProvider, -) : PlacementModifier() { + override val x: FloatProvider, + override val z: FloatProvider, + override val y: FloatProvider, + override val count: IntProvider, + override val xLength: FloatProvider, + override val zLength: FloatProvider, + override val yLength: FloatProvider, +) : PlacementModifier(), IEllipsoidPlacement { init { require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" } require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" } @@ -40,34 +44,7 @@ data class EllipsoidPlacement( random: RandomSource, position: BlockPos ): Stream { - var count = count.sample(random) - - if (count <= 0) { - return Stream.empty() - } - - val xLength = xLength.sample(random) - val zLength = zLength.sample(random) - val yLength = yLength.sample(random) - - val xPow = xLength * xLength - val zPow = zLength * zLength - val yPow = yLength * yLength - - count = minOf(count, (xLength * zLength * yLength * PI * (4.0 / 3.0)).roundToInt()) - count = 600 - - return Stream.generate { position + BlockPos(this.x.sample(random), this.y.sample(random), this.z.sample(random)) } - .limit(count * 10L) // failsafe - .filter { - val (ellipsoidX, ellipsoidY, ellipsoidZ) = it - position - - (ellipsoidX * ellipsoidX) / xPow + - (ellipsoidY * ellipsoidY) / yPow + - (ellipsoidZ * ellipsoidZ) / zPow <= 1.0f - } - .distinct() - .limit(count.toLong()) + return getEllipsoidPositions(random, position) } override fun type(): PlacementModifierType<*> { @@ -78,9 +55,9 @@ data class EllipsoidPlacement( val CODEC: MapCodec by lazy { RecordCodecBuilder.mapCodec { it.group( - IntProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x), - IntProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y), - IntProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z), + FloatProvider.CODEC.fieldOf("x").forGetter(EllipsoidPlacement::x), + FloatProvider.CODEC.fieldOf("y").forGetter(EllipsoidPlacement::y), + FloatProvider.CODEC.fieldOf("z").forGetter(EllipsoidPlacement::z), IntProvider.CODEC.fieldOf("count").forGetter(EllipsoidPlacement::count), FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EllipsoidPlacement::xLength), FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EllipsoidPlacement::zLength), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnormousEllipsoidPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnormousEllipsoidPlacement.kt new file mode 100644 index 000000000..ba178f5d6 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/EnormousEllipsoidPlacement.kt @@ -0,0 +1,60 @@ +package ru.dbotthepony.mc.otm.worldgen.placement + +import com.mojang.serialization.MapCodec +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.core.BlockPos +import net.minecraft.util.RandomSource +import net.minecraft.util.valueproviders.FloatProvider +import net.minecraft.util.valueproviders.IntProvider +import net.minecraft.world.level.levelgen.placement.PlacementModifierType +import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers +import java.util.stream.Stream + +/** + * Enormous ellipsoid ("cloud") placement, suitable to be used as ONLY primary placement modifier + * + * This placement modifier is designed to be terminal; other placement modifiers after this MUST NOT be placed + * + * @see AbstractEnormousPlacement + */ +class EnormousEllipsoidPlacement( + parameters: Parameters, + override val x: FloatProvider, + override val z: FloatProvider, + override val y: FloatProvider, + override val count: IntProvider, + override val xLength: FloatProvider, + override val zLength: FloatProvider, + override val yLength: FloatProvider, +) : AbstractEnormousPlacement(parameters), IEllipsoidPlacement { + init { + require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" } + require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" } + require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" } + } + + override fun type(): PlacementModifierType<*> { + return MPlacementModifiers.ENORMOUS_ELLIPSOID_PLACEMENT + } + + override fun getPositions(center: BlockPos, random: RandomSource): Stream { + return getEllipsoidPositions(random, center) + } + + companion object { + val CODEC: MapCodec by lazy { + RecordCodecBuilder.mapCodec { + it.group( + PARAMETERS_CODEC.forGetter(EnormousEllipsoidPlacement::parameters), + FloatProvider.CODEC.fieldOf("x").forGetter(EnormousEllipsoidPlacement::x), + FloatProvider.CODEC.fieldOf("y").forGetter(EnormousEllipsoidPlacement::y), + FloatProvider.CODEC.fieldOf("z").forGetter(EnormousEllipsoidPlacement::z), + IntProvider.CODEC.fieldOf("count").forGetter(EnormousEllipsoidPlacement::count), + FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("x_size").forGetter(EnormousEllipsoidPlacement::xLength), + FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("z_size").forGetter(EnormousEllipsoidPlacement::zLength), + FloatProvider.codec(1f, Float.MAX_VALUE).fieldOf("y_size").forGetter(EnormousEllipsoidPlacement::yLength), + ).apply(it, ::EnormousEllipsoidPlacement) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/IEllipsoidPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/IEllipsoidPlacement.kt new file mode 100644 index 000000000..49c69686e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/IEllipsoidPlacement.kt @@ -0,0 +1,81 @@ +package ru.dbotthepony.mc.otm.worldgen.placement + +import net.minecraft.core.BlockPos +import net.minecraft.util.RandomSource +import net.minecraft.util.valueproviders.FloatProvider +import net.minecraft.util.valueproviders.IntProvider +import ru.dbotthepony.mc.otm.core.math.component1 +import ru.dbotthepony.mc.otm.core.math.component2 +import ru.dbotthepony.mc.otm.core.math.component3 +import ru.dbotthepony.mc.otm.core.math.plus +import java.util.stream.Stream + +interface IEllipsoidPlacement { + /** + * X position within ellipsoid sampler, expected to be in range -1 .. 1 + */ + val x: FloatProvider + + /** + * Z position within ellipsoid sampler, expected to be in range -1 .. 1 + */ + val z: FloatProvider + + /** + * Y position within ellipsoid sampler, expected to be in range -1 .. 1 + */ + val y: FloatProvider + + /** + * Total amount of block positions to generate + */ + val count: IntProvider + + /** + * Ellipsoid size sampler on X axis + */ + val xLength: FloatProvider + + /** + * Ellipsoid size sampler on Z axis + */ + val zLength: FloatProvider + + /** + * Ellipsoid size sampler on Y axis + */ + val yLength: FloatProvider + + fun getEllipsoidPositions(random: RandomSource, position: BlockPos): Stream { + val count = count.sample(random) + + if (count <= 0) + return Stream.empty() + + val xLength = xLength.sample(random) + val zLength = zLength.sample(random) + val yLength = yLength.sample(random) + + val xPow = xLength * xLength + val zPow = zLength * zLength + val yPow = yLength * yLength + + return Stream.generate { + val x = this.x.sample(random) * xLength + val y = this.y.sample(random) * yLength + val z = this.z.sample(random) * zLength + BlockPos(x.toInt(), y.toInt(), z.toInt()) + } + .limit(count.toLong() * 10) + .filter { + val (ellipsoidX, ellipsoidY, ellipsoidZ) = it + + (ellipsoidX * ellipsoidX) / xPow + + (ellipsoidY * ellipsoidY) / yPow + + (ellipsoidZ * ellipsoidZ) / zPow <= 1.0f + } + .distinct() + .limit(count.toLong()) + .map { it + position } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/StandardDeviationHeightProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/StandardDeviationHeightProvider.kt similarity index 97% rename from src/main/kotlin/ru/dbotthepony/mc/otm/data/world/StandardDeviationHeightProvider.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/StandardDeviationHeightProvider.kt index 5dd139d7a..c085e35f2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/StandardDeviationHeightProvider.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/StandardDeviationHeightProvider.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.mc.otm.data.world +package ru.dbotthepony.mc.otm.worldgen.placement import com.mojang.serialization.Codec import com.mojang.serialization.MapCodec