Skip to content

Commit

Permalink
Added dimension padding and other things (#40)
Browse files Browse the repository at this point in the history
* implement dimension padding on jigsaw start piece

MC-272062 fixed in 24w44a

* fix heightmaps in getBaseHeight

* add return of structure position to getStructureInChunk

* fix old blended noise

* fix end island density function

* Format some stuff

---------

Co-authored-by: Misode <[email protected]>
  • Loading branch information
jacobsjo and misode authored Dec 3, 2024
1 parent bbc6534 commit c38265e
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 31 deletions.
8 changes: 4 additions & 4 deletions src/math/noise/BlendedNoise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export class BlendedNoise {
public readonly yFactor: number,
public readonly smearScaleMultiplier: number
) {
this.minLimitNoise = new PerlinNoise(random, -15, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
this.maxLimitNoise = new PerlinNoise(random, -15, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
this.mainNoise = new PerlinNoise(random, -7, [1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0])
this.minLimitNoise = new PerlinNoise(random, -15, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], true)
this.maxLimitNoise = new PerlinNoise(random, -15, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], true)
this.mainNoise = new PerlinNoise(random, -7, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], true)
this.xzMultiplier = 684.412 * xzScale
this.yMultiplier = 684.412 * yScale
this.maxValue = this.minLimitNoise.edgeValue(this.yScale + 2) //TODO
this.maxValue = this.minLimitNoise.edgeValue(this.yMultiplier + 2)
}

public sample(x: number, y: number, z: number) {
Expand Down
4 changes: 2 additions & 2 deletions src/math/noise/PerlinNoise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export class PerlinNoise {
public readonly lowestFreqValueFactor: number
public readonly maxValue: number

constructor(random: Random, firstOctave: number, amplitudes: number[]) {
if (random instanceof XoroshiroRandom){
constructor(random: Random, firstOctave: number, amplitudes: number[], forceLegacy: boolean = false) {
if (random instanceof XoroshiroRandom && !forceLegacy){
const forkedRandom = random.forkPositional()

this.noiseLevels = Array(amplitudes.length)
Expand Down
6 changes: 3 additions & 3 deletions src/worldgen/DensityFunction.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Holder, Identifier } from '../core/index.js'
import type { BlendedNoise, MinMaxNumberFunction, NormalNoise } from '../math/index.js'
import { clamp, clampedMap, CubicSpline, lazyLerp3, LegacyRandom, NoiseParameters, SimplexNoise } from '../math/index.js'
import { computeIfAbsent, Json } from '../util/index.js'
import { CubicSpline, LegacyRandom, NoiseParameters, SimplexNoise, clamp, clampedMap, lazyLerp3 } from '../math/index.js'
import { Json, computeIfAbsent } from '../util/index.js'
import { WorldgenRegistries } from './WorldgenRegistries.js'

export abstract class DensityFunction implements MinMaxNumberFunction<DensityFunction.Context> {
Expand Down Expand Up @@ -381,7 +381,7 @@ export namespace DensityFunction {
const z0 = Math.floor(z / 2)
const x1 = x % 2
const z1 = z % 2
let f = clamp(100 - Math.sqrt(x * x + z * z), -100, 80)
let f = clamp(100 - Math.sqrt(x * x + z * z) * 8, -100, 80)

for (let i = -12; i <= 12; i += 1) {
for (let j = -12; j <= 12; j += 1) {
Expand Down
4 changes: 4 additions & 0 deletions src/worldgen/LevelHeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface LevelHeight {
minY: number,
height: number,
}
4 changes: 2 additions & 2 deletions src/worldgen/NoiseChunkGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export class NoiseChunkGenerator {
public getBaseHeight(blockX: number, blockZ: number, heightmap: Heightmap, randomState: RandomState){
let predicate: (state: BlockState) => boolean
if (heightmap === 'OCEAN_FLOOR' || heightmap === 'OCEAN_FLOOR_WG'){
predicate = (state: BlockState) => !state.equals(BlockState.AIR)
} else {
predicate = (state: BlockState) => state.equals(BlockState.STONE)
} else {
predicate = (state: BlockState) => !state.equals(BlockState.AIR)
}
return this.iterateNoiseColumn(randomState, blockX, blockZ, undefined, predicate, BlockState.STONE) ?? this.settings.noise.minY
}
Expand Down
5 changes: 3 additions & 2 deletions src/worldgen/RandomState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Holder } from '../core/index.js'
import { Registry } from '../core/index.js'
import { BlendedNoise, computeIfAbsent, DensityFunction, Identifier, LegacyRandom, NoiseParameters, NoiseSettings, NormalNoise, XoroshiroRandom } from '../index.js'
import type { PositionalRandom } from '../math/index.js'
import type { PositionalRandom, Random } from '../math/index.js'
import { Climate } from './biome/index.js'
import type { NoiseGeneratorSettings } from './NoiseGeneratorSettings.js'
import { NoiseRouter } from './NoiseRouter.js'
Expand Down Expand Up @@ -83,7 +83,8 @@ export class RandomState {
return new DensityFunction.WeirdScaledSampler(fn.input, fn.rarityValueMapper, fn.noiseData, getNoise(fn.noiseData))
}
if (fn instanceof DensityFunction.OldBlendedNoise) {
return new DensityFunction.OldBlendedNoise(fn.xzScale, fn.yScale, fn.xzFactor, fn.yFactor, fn.smearScaleMultiplier, new BlendedNoise( this.random.fromHashOf(Identifier.create('terrain').toString()), fn.xzScale, fn.yScale, fn.xzFactor, fn.yFactor, fn.smearScaleMultiplier))
const oldBlendedNoiseRandom: Random = legacyRandom ? new LegacyRandom(this.seed + BigInt(0)) : this.random.fromHashOf(Identifier.create('terrain').toString())
return new DensityFunction.OldBlendedNoise(fn.xzScale, fn.yScale, fn.xzFactor, fn.yFactor, fn.smearScaleMultiplier, new BlendedNoise(oldBlendedNoiseRandom, fn.xzScale, fn.yScale, fn.xzFactor, fn.yFactor, fn.smearScaleMultiplier))
}
if (fn instanceof DensityFunction.EndIslands) {
return new DensityFunction.EndIslands(this.seed)
Expand Down
8 changes: 5 additions & 3 deletions src/worldgen/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
export * from './Aquifer.js'
export * from './biome/index.js'
export * from './DensityFunction.js'
export * from './Heightmap.js'
export * from './HeightProvider.js'
export * from './Heightmap.js'
export * from './LevelHeight.js'
export * from './NoiseChunk.js'
export * from './NoiseChunkGenerator.js'
export * from './NoiseGeneratorSettings.js'
export * from './NoiseRouter.js'
export * from './NoiseSettings.js'
export * from './RandomState.js'
export * from './structure/index.js'
export * from './SurfaceSystem.js'
export * from './VerticalAnchor.js'
export * from './WorldgenRegistries.js'
export * from './biome/index.js'
export * from './structure/index.js'

2 changes: 1 addition & 1 deletion src/worldgen/structure/StructurePlacement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { BlockPos, Holder, HolderSet, Identifier } from '../../core/index.js'
import type { Random } from '../../math/index.js'
import { LegacyRandom } from '../../math/index.js'
import { Json } from '../../util/index.js'
import { WorldgenRegistries } from '../WorldgenRegistries.js'
import type { Climate } from '../biome/index.js'
import { BiomeSource } from '../biome/index.js'
import { WorldgenRegistries } from '../WorldgenRegistries.js'
import { StructureSet } from './StructureSet.js'

export abstract class StructurePlacement {
Expand Down
13 changes: 8 additions & 5 deletions src/worldgen/structure/StructureSet.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { BlockPos } from '../../core/index.js'
import { Holder, Identifier, Registry } from '../../core/index.js'
import { LegacyRandom } from '../../math/index.js'
import { Json } from '../../util/index.js'
Expand All @@ -19,7 +20,7 @@ export class StructureSet {
return new StructureSet(structures, placement)
}

public getStructureInChunk(chunkX: number, chunkZ: number, context: WorldgenStructure.GenerationContext): Identifier | undefined {
public getStructureInChunk(chunkX: number, chunkZ: number, context: WorldgenStructure.GenerationContext): {id: Identifier, pos: BlockPos} | undefined {
this.placement.prepare(context.biomeSource, context.randomState.sampler, context.seed)

if (!this.placement.isStructureChunk(context.seed, chunkX, chunkZ)) {
Expand All @@ -29,8 +30,9 @@ export class StructureSet {
if (this.structures.length === 0) return undefined

if (this.structures.length === 1) {
if (this.structures[0].structure.value().tryGenerate(chunkX, chunkZ, context)) {
return this.structures[0].structure.key()
const pos = this.structures[0].structure.value().tryGenerate(chunkX, chunkZ, context)
if (pos !== undefined) {
return {id: this.structures[0].structure.key()!, pos}
}
} else {

Expand All @@ -50,8 +52,9 @@ export class StructureSet {
}
}

if (entry!.structure.value().tryGenerate(chunkX, chunkZ, context)) {
return entry!.structure.key()
const pos = entry!.structure.value().tryGenerate(chunkX, chunkZ, context)
if (pos !== undefined) {
return {id: entry!.structure.key()!, pos}
}

list.splice(id!, 1)
Expand Down
59 changes: 50 additions & 9 deletions src/worldgen/structure/WorldgenStructure.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Rotation } from '../../core/Rotation.js'
import { BlockPos, Holder, HolderSet, Identifier, Registry } from '../../core/index.js'
import { BlockPos, Holder, HolderSet, Identifier, Registry, Rotation } from '../../core/index.js'
import type { Random } from '../../math/index.js'
import { LegacyRandom } from '../../math/index.js'
import { Json } from '../../util/Json.js'
import { HeightProvider } from '../HeightProvider.js'
import type { BiomeSource } from '../biome/index.js'
import { Heightmap } from '../Heightmap.js'
import { HeightProvider } from '../HeightProvider.js'
import type { LevelHeight } from '../LevelHeight.js'
import { NoiseChunkGenerator } from '../NoiseChunkGenerator.js'
import type { NoiseGeneratorSettings } from '../NoiseGeneratorSettings.js'
import { RandomState } from '../RandomState.js'
import { WorldgenRegistries } from '../WorldgenRegistries.js'
import type { BiomeSource } from '../biome/index.js'
import { StructurePoolElement } from './StructurePoolElement.js'
import { StructureTemplatePool } from './StructureTemplatePool.js'

Expand Down Expand Up @@ -58,14 +58,14 @@ export abstract class WorldgenStructure {
return BlockPos.create(posX, this.getLowestY(context, posX, posZ, width, depth), posZ)
}

public tryGenerate(chunkX: number, chunkZ: number, context: WorldgenStructure.GenerationContext): boolean {
public tryGenerate(chunkX: number, chunkZ: number, context: WorldgenStructure.GenerationContext): BlockPos | undefined {
const random = LegacyRandom.fromLargeFeatureSeed(context.seed, chunkX, chunkZ)

const pos = this.findGenerationPoint(chunkX, chunkZ, random, context)
if (pos === undefined) return false
if (pos === undefined) return undefined
const biome = context.biomeSource.getBiome(pos[0]>>2, pos[1], pos[2]>>2, context.randomState.sampler)

return [...this.settings.validBiomes.getEntries()].findIndex((b) => b.key()?.equals(biome)) >= 0
return [...this.settings.validBiomes.getEntries()].findIndex((b) => b.key()?.equals(biome)) >= 0 ? pos : undefined
}
}

Expand All @@ -88,6 +88,7 @@ export namespace WorldgenStructure {
public readonly seed: bigint,
public readonly biomeSource: BiomeSource,
public readonly settings: NoiseGeneratorSettings,
public readonly levelHeight: LevelHeight,
) {
this.randomState = new RandomState(settings, seed)
this.chunkGenerator = new NoiseChunkGenerator(biomeSource, settings)
Expand Down Expand Up @@ -121,7 +122,8 @@ export namespace WorldgenStructure {
const startJigsawNameString = Json.readString(root.start_jigsaw_name)
const startJigsawName = startJigsawNameString ? Identifier.parse(startJigsawNameString) : undefined
const heightmap = Heightmap.fromJson(root.project_start_to_heightmap)
return new JigsawStructure(settings, startPool, startHeight, heightmap, startJigsawName)
const dimensionPadding = JigsawStructure.DimensionPadding.fromJson(root.dimension_padding)
return new JigsawStructure(settings, startPool, startHeight, heightmap, startJigsawName, dimensionPadding)
case 'jungle_temple':
return new JungleTempleStructure(settings)
case 'mineshaft':
Expand Down Expand Up @@ -155,7 +157,8 @@ export namespace WorldgenStructure {
private readonly startingPoolHolder: Holder<StructureTemplatePool>,
private readonly startHeight: HeightProvider,
private readonly projectStartToHeightmap: Heightmap | undefined,
private readonly startJigsawName: Identifier | undefined
private readonly startJigsawName: Identifier | undefined,
private readonly dimensionPadding: JigsawStructure.DimensionPadding
) {
super(settings)
}
Expand Down Expand Up @@ -194,6 +197,11 @@ export namespace WorldgenStructure {
y = templateStartPos[1]
}

boundingBox.forEach(pos => pos[1] += y - boundingBox[0][1] - 1)
if (JigsawStructure.isStartTooCloseToWorldHeightLimits(this.dimensionPadding, boundingBox, context.levelHeight)) {
return undefined
}

const generationPoint = BlockPos.create(x, y + startJigsawOffset[1], z)

//console.log(`Generating Jigsaw Structure in Chunk ${chunkX}, ${chunkZ}: rotation: ${rotation}, startingElement: ${startingElement.toString()}, center: ${x}, ${y}, ${z}`)
Expand All @@ -202,6 +210,16 @@ export namespace WorldgenStructure {
}
}

public static isStartTooCloseToWorldHeightLimits(dimensionPadding: JigsawStructure.DimensionPadding, boundingBox: [BlockPos, BlockPos], levelHeight: LevelHeight ): boolean {
if (dimensionPadding === JigsawStructure.DimensionPadding.ZERO) { // reference comparison here is correct (i.e. matching vanilla), see MC-278259
return false
}

const bottomLimit = levelHeight.minY + dimensionPadding.bottom
const topLimit = levelHeight.minY + levelHeight.height - dimensionPadding.top
return boundingBox[0][1] < bottomLimit || boundingBox[1][1] > topLimit
}

private static getRandomNamedJigsaw(element: StructurePoolElement, name: Identifier, rotation: Rotation, random: Random): BlockPos | undefined{
const jigsaws = element.getShuffledJigsawBlocks(rotation, random)
for (const jigsaw of jigsaws){
Expand All @@ -214,6 +232,29 @@ export namespace WorldgenStructure {
}
}

export namespace JigsawStructure {
export class DimensionPadding {
static ZERO: DimensionPadding = new DimensionPadding(0, 0)

constructor(
public top: number,
public bottom: number
) {}

static fromJson(obj: unknown): DimensionPadding {
if (obj === undefined) {
return DimensionPadding.ZERO
}
if (typeof obj === 'number') {
return new DimensionPadding(obj, obj)
}

const padding = Json.readObject(obj) ?? {}
return new DimensionPadding(Json.readInt(padding.top) ?? 0, Json.readInt(padding.bottom) ?? 0)
}
}
}

export class BuriedTreasureStructure extends WorldgenStructure {
public findGenerationPoint(chunkX: number, chunkZ: number, _: Random, context: WorldgenStructure.GenerationContext): BlockPos | undefined {
return this.onTopOfChunkCenter(context, chunkX, chunkZ, 'OCEAN_FLOOR_WG')
Expand Down

0 comments on commit c38265e

Please sign in to comment.