(undefined)
@@ -26,7 +29,7 @@ export const BiomeSourcePreview = ({ model, data, shown, lang, version }: Previe
return [200 / res.current, 200 / res.current]
},
async draw(img) {
- const options = { octaves, biomeColors: {}, offset: offset.current, scale, seed, res: res.current, version }
+ const options = { octaves, biomeColors: {}, layers, offset: offset.current, scale, seed, res: res.current, version }
await biomeMap(data, img, options)
if (res.current === 4) {
clearTimeout(refineTimeout.current)
@@ -44,21 +47,21 @@ export const BiomeSourcePreview = ({ model, data, shown, lang, version }: Previe
redraw()
},
async onHover(x, y) {
- const options = { octaves, biomeColors: {}, offset: offset.current, scale, seed, res: 1, version }
+ const options = { octaves, biomeColors: {}, layers, offset: offset.current, scale, seed, res: 1, version }
const biome = await getBiome(data, Math.floor(x * 200), Math.floor(y * 200), options)
setFocused(biome)
},
onLeave() {
setFocused(undefined)
},
- }, [state, scale, seed])
+ }, [state, scale, seed, layers])
useEffect(() => {
if (shown) {
res.current = type === 'multi_noise' ? 4 : 1
redraw()
}
- }, [state, scale, seed, shown])
+ }, [state, scale, seed, layers, shown])
const changeScale = (newScale: number) => {
offset.current[0] = offset.current[0] * scale / newScale
@@ -69,6 +72,19 @@ export const BiomeSourcePreview = ({ model, data, shown, lang, version }: Previe
return <>
{focused && }
+ {type === 'multi_noise' &&
+
+ {LAYERS.map(name => {
+ const enabled = layers.has(name)
+ return {
+ setLayers(new Set([name]))
+ e.stopPropagation()
+ }} />
+ })}
+ }
{(type === 'multi_noise' || type === 'checkerboard') && <>
changeScale(scale * 1.5)} />
@@ -88,38 +104,40 @@ function calculateState(data: any, octaves: NoiseOctaves) {
}
function getOctaves(obj: any): NoiseOctaves {
- if (typeof obj === 'string') {
- switch (obj.replace(/^minecraft:/, '')) {
- case 'overworld':
- case 'amplified':
- return {
- temperature: { firstOctave: -9, amplitudes: [1.5, 0, 1, 0, 0, 0] },
- humidity: { firstOctave: -7, amplitudes: [1, 1, 0, 0, 0, 0] },
- continentalness: { firstOctave: -9, amplitudes: [1, 1, 2, 2, 2, 1, 1, 1, 1] },
- erosion: { firstOctave: -9, amplitudes: [1, 1, 0, 1, 1] },
- weirdness: { firstOctave: -7, amplitudes: [1, 2, 1, 0, 0, 0] },
- shift: { firstOctave: -3, amplitudes: [1, 1, 1, 0] },
- }
- case 'end':
- case 'floating_islands':
- return {
- temperature: { firstOctave: 0, amplitudes: [0] },
- humidity: { firstOctave: 0, amplitudes: [0] },
- continentalness: { firstOctave: 0, amplitudes: [0] },
- erosion: { firstOctave: 0, amplitudes: [0] },
- weirdness: { firstOctave: 0, amplitudes: [0] },
- shift: { firstOctave: 0, amplitudes: [0] },
- }
- default:
- return {
- temperature: { firstOctave: -7, amplitudes: [1, 1] },
- humidity: { firstOctave: -7, amplitudes: [1, 1] },
- continentalness: { firstOctave: -7, amplitudes: [1, 1] },
- erosion: { firstOctave: -7, amplitudes: [1, 1] },
- weirdness: { firstOctave: -7, amplitudes: [1, 1] },
- shift: { firstOctave: 0, amplitudes: [0] },
- }
- }
+ if (typeof obj !== 'string') {
+ const settings = NoiseGeneratorSettings.fromJson(obj)
+ obj = settings.noise.densityFactor === 0 && settings.noise.densityOffset === -0.030078125
+ ? 'minecraft:nether' : 'minecraft:overworld'
+ }
+ switch (obj.replace(/^minecraft:/, '')) {
+ case 'overworld':
+ case 'amplified':
+ return {
+ temperature: { firstOctave: -9, amplitudes: [1.5, 0, 1, 0, 0, 0] },
+ humidity: { firstOctave: -7, amplitudes: [1, 1, 0, 0, 0, 0] },
+ continentalness: { firstOctave: -9, amplitudes: [1, 1, 2, 2, 2, 1, 1, 1, 1] },
+ erosion: { firstOctave: -9, amplitudes: [1, 1, 0, 1, 1] },
+ weirdness: { firstOctave: -7, amplitudes: [1, 2, 1, 0, 0, 0] },
+ shift: { firstOctave: -3, amplitudes: [1, 1, 1, 0] },
+ }
+ case 'end':
+ case 'floating_islands':
+ return {
+ temperature: { firstOctave: 0, amplitudes: [0] },
+ humidity: { firstOctave: 0, amplitudes: [0] },
+ continentalness: { firstOctave: 0, amplitudes: [0] },
+ erosion: { firstOctave: 0, amplitudes: [0] },
+ weirdness: { firstOctave: 0, amplitudes: [0] },
+ shift: { firstOctave: 0, amplitudes: [0] },
+ }
+ default:
+ return {
+ temperature: { firstOctave: -7, amplitudes: [1, 1] },
+ humidity: { firstOctave: -7, amplitudes: [1, 1] },
+ continentalness: { firstOctave: -7, amplitudes: [1, 1] },
+ erosion: { firstOctave: -7, amplitudes: [1, 1] },
+ weirdness: { firstOctave: -7, amplitudes: [1, 1] },
+ shift: { firstOctave: 0, amplitudes: [0] },
+ }
}
- return NoiseGeneratorSettings.fromJson(obj).octaves
}
diff --git a/src/app/previews/BiomeSource.ts b/src/app/previews/BiomeSource.ts
index 48383b9a..a8ee6aec 100644
--- a/src/app/previews/BiomeSource.ts
+++ b/src/app/previews/BiomeSource.ts
@@ -1,7 +1,7 @@
import { DataModel } from '@mcschema/core'
-import type { BiomeSource, Climate, NoiseOctaves } from 'deepslate'
-import { FixedBiome, NoiseGeneratorSettings, NoiseSampler, NormalNoise, Random } from 'deepslate'
-import init, { biome_parameters, climate_sampler, multi_noise } from 'deepslate-rs'
+import type { NoiseOctaves } from 'deepslate'
+import { FixedBiome, LegacyRandom, NormalNoise, TerrainShaper } from 'deepslate'
+import init, { biome_parameters, climate_noise, climate_sampler, multi_noise } from 'deepslate-rs'
// @ts-expect-error
import wasm from 'deepslate-rs/deepslate_rs_bg.wasm?url'
import { fetchPreset } from '../DataFetcher'
@@ -16,7 +16,21 @@ async function loadWasm() {
console.debug(`Loaded deepslate-rs from "${wasm}"`)
}
-type BiomeColors = Record
+const OverworldShaper = TerrainShaper.overworld()
+
+const LAYERS = {
+ temperature: [-1, 1],
+ humidity: [-1, 1],
+ continentalness: [-1.1, 1],
+ erosion: [-1, 1],
+ weirdness: [-1, 1],
+ offset: [-1, 1],
+ factor: [0, 12],
+ jaggedness: [0, 1],
+}
+
+type Triple = [number, number, number]
+type BiomeColors = Record
type BiomeSourceOptions = {
octaves: NoiseOctaves,
biomeColors: BiomeColors,
@@ -25,18 +39,20 @@ type BiomeSourceOptions = {
res: number,
seed: bigint,
version: VersionId,
+ layers: Set,
}
-interface CachedBiomeSource extends BiomeSource {
+interface CachedBiomeSource {
+ getBiome(x: number, y: number, z: number): string
getBiomes?(xFrom: number, xTo: number, xStep: number, yFrom: number, yTo: number, yStep: number, zFrom: number, zTo: number, zStep: number): string[]
+ getClimate?(layers: Set, xFrom: number, xTo: number, xStep: number, yFrom: number, yTo: number, yStep: number, zFrom: number, zTo: number, zStep: number): {[k: string]: number}[]
}
let cacheState: any
let biomeSourceCache: CachedBiomeSource
-let climateSamplerCache: Climate.Sampler
export async function biomeMap(state: any, img: ImageData, options: BiomeSourceOptions) {
- const { biomeSource, climateSampler } = await getCached(state, options)
+ const { biomeSource } = await getCached(state, options)
const data = img.data
const ox = -Math.round(options.offset[0]) - 100 + options.res / 2
@@ -44,19 +60,29 @@ export async function biomeMap(state: any, img: ImageData, options: BiomeSourceO
const row = img.width * 4 / options.res
const col = 4 / options.res
- const biomes = biomeSource.getBiomes?.(
- ox * options.scale, (200 + ox) * options.scale, options.res * options.scale,
- 64, 65, 1,
- oz * options.scale, (200 + oz) * options.scale, options.res * options.scale,
- )
+ const xRange: Triple = [ox * options.scale, (200 + ox) * options.scale, options.res * options.scale]
+ const zRange: Triple = [oz * options.scale, (200 + oz) * options.scale, options.res * options.scale]
+
+ const biomes = !options.layers.has('biomes') ? undefined : biomeSource.getBiomes?.(...xRange, 64, 65, 1, ...zRange)
+ const layers = [...options.layers].filter(l => l !== 'biomes') as (keyof typeof LAYERS)[]
+ const noise = layers.length === 0 ? undefined : biomeSource.getClimate?.(new Set(layers), ...xRange, 64, 65, 1, ...zRange)
for (let x = 0; x < 200; x += options.res) {
for (let z = 0; z < 200; z += options.res) {
const i = z * row + x * col
+ const j = (x / options.res) * 200 / options.res + z / options.res
const worldX = (x + ox) * options.scale
const worldZ = (z + oz) * options.scale
- const b = biomes?.[(x / options.res) * 200 / options.res + z / options.res] ?? biomeSource.getBiome(worldX, 64, worldZ, climateSampler)
- const color = getBiomeColor(b, options.biomeColors)
+ let color: Triple = [50, 50, 50]
+ if (options.layers.has('biomes')) {
+ const biome = biomes?.[j] ?? biomeSource.getBiome(worldX, 64, worldZ)
+ color = getBiomeColor(biome, options.biomeColors)
+ } else if (noise && layers[0]) {
+ const value = noise[j][layers[0]]
+ const [min, max] = LAYERS[layers[0]]
+ const brightness = (value - min) / (max - min) * 256
+ color = [brightness, brightness, brightness]
+ }
data[i] = color[0]
data[i + 1] = color[1]
data[i + 2] = color[2]
@@ -66,26 +92,21 @@ export async function biomeMap(state: any, img: ImageData, options: BiomeSourceO
}
export async function getBiome(state: any, x: number, z: number, options: BiomeSourceOptions): Promise {
- const { biomeSource, climateSampler } = await getCached(state, options)
+ const { biomeSource } = await getCached(state, options)
const [xx, zz] = toWorld([x, z], options)
- return biomeSource.getBiome(xx, 64, zz, climateSampler)
+ return biomeSource.getBiome(xx, 64, zz)
}
-async function getCached(state: any, options: BiomeSourceOptions): Promise<{ biomeSource: CachedBiomeSource, climateSampler: Climate.Sampler }> {
+async function getCached(state: any, options: BiomeSourceOptions): Promise<{ biomeSource: CachedBiomeSource}> {
const newState = [state, options.octaves, `${options.seed}`, options.version]
if (!deepEqual(newState, cacheState)) {
cacheState = deepClone(newState)
biomeSourceCache = await getBiomeSource(state, options)
-
- const settings = NoiseGeneratorSettings.fromJson({ octaves: options.octaves })
- const noiseSampler = new NoiseSampler(4, 4, 32, biomeSourceCache, settings.noise, options.octaves, options.seed)
- climateSamplerCache = noiseSampler.getClimate.bind(noiseSampler)
}
return {
biomeSource: biomeSourceCache,
- climateSampler: climateSamplerCache,
}
}
@@ -149,13 +170,32 @@ async function getBiomeSource(state: any, options: BiomeSourceOptions): Promise<
const ids = multi_noise(parameters, sampler, xFrom, xTo, xStep, yFrom, yTo, yStep, zFrom, zTo, zStep)
return [...ids].map(id => BiomeIds.getA(id) ?? 'unknown')
},
+ getClimate(layers, xFrom, xTo, xStep, yFrom, yTo, yStep, zFrom, zTo, zStep) {
+ const climate = climate_noise(sampler, xFrom, xTo, xStep, yFrom, yTo, yStep, zFrom, zTo, zStep)
+ const result = []
+ for (let i = 0; i < climate.length; i += 7) {
+ const [t, h, c, e, w] = climate.slice(i, i + 5)
+ const point = TerrainShaper.point(c, e, w)
+ result.push({
+ temperature: t,
+ humidity: h,
+ continentalness: c,
+ erosion: e,
+ weirdness: w,
+ ...layers.has('offset') && { offset: OverworldShaper.offset(point) },
+ ...layers.has('factor') && { factor: OverworldShaper.factor(point) },
+ ...layers.has('jaggedness') && { jaggedness: OverworldShaper.jaggedness(point) },
+ })
+ }
+ return result
+ },
}
} else {
const noise = ['altitude', 'temperature', 'humidity', 'weirdness']
.map((id, i) => {
const config = state[`${id}_noise`]
config.firstOctave = clamp(config.firstOctave ?? -7, -100, -1)
- return new NormalNoise(new Random(options.seed + BigInt(i)), config)
+ return new NormalNoise(new LegacyRandom(options.seed + BigInt(i)), config)
})
if (!Array.isArray(state.biomes) || state.biomes.length === 0) {
return new FixedBiome('unknown')
@@ -180,7 +220,7 @@ async function getBiomeSource(state: any, options: BiomeSourceOptions): Promise<
throw new Error('Unknown biome source')
}
-function getBiomeColor(biome: string, biomeColors: BiomeColors) {
+function getBiomeColor(biome: string, biomeColors: BiomeColors): Triple {
if (!biome) {
return [128, 128, 128]
}
@@ -197,7 +237,7 @@ function toWorld([x, z]: [number, number], options: BiomeSourceOptions) {
return [xx, zz]
}
-const VanillaColors: Record = {
+const VanillaColors: Record = {
'minecraft:badlands': [217,69,21],
'minecraft:badlands_plateau': [202,140,101],
'minecraft:bamboo_jungle': [118,142,20],
diff --git a/src/app/previews/Decorator.ts b/src/app/previews/Decorator.ts
index 77a709ee..c07307fb 100644
--- a/src/app/previews/Decorator.ts
+++ b/src/app/previews/Decorator.ts
@@ -1,5 +1,6 @@
import { DataModel } from '@mcschema/core'
-import { PerlinNoise, Random } from 'deepslate'
+import type { Random } from 'deepslate'
+import { LegacyRandom, PerlinNoise } from 'deepslate'
import type { VersionId } from '../Schemas'
import { clamp, stringToColor } from '../Utils'
@@ -35,7 +36,7 @@ export type DecoratorOptions = {
version: VersionId,
}
export function decorator(state: any, img: ImageData, options: DecoratorOptions) {
- const random = new Random(options.seed)
+ const random = new LegacyRandom(options.seed)
const ctx: PlacementContext = {
placements: [],
features: [],
diff --git a/src/app/previews/NoiseSettings.ts b/src/app/previews/NoiseSettings.ts
index 9fb94522..7890298e 100644
--- a/src/app/previews/NoiseSettings.ts
+++ b/src/app/previews/NoiseSettings.ts
@@ -82,7 +82,7 @@ function getCached(state: unknown, options: NoiseSettingsOptions) {
cacheState = deepClone(newState)
chunkCache = []
const biomeSource = new FixedBiome('unknown')
- generatorCache = new NoiseChunkGenerator(options.seed, biomeSource, settings, shape)
+ generatorCache = new NoiseChunkGenerator(options.seed, biomeSource, settings)
}
return {
settings,
diff --git a/src/app/previews/NormalNoise.ts b/src/app/previews/NormalNoise.ts
index 94ed2e24..aedc3e07 100644
--- a/src/app/previews/NormalNoise.ts
+++ b/src/app/previews/NormalNoise.ts
@@ -1,5 +1,5 @@
import { DataModel } from '@mcschema/core'
-import { NoiseParameters, NormalNoise, Random } from 'deepslate'
+import { LegacyRandom, NoiseParameters, NormalNoise } from 'deepslate'
import type { VersionId } from '../Schemas'
export type NoiseOptions = {
@@ -10,7 +10,7 @@ export type NoiseOptions = {
}
export function normalNoise(state: any, img: ImageData, options: NoiseOptions) {
- const random = new Random(options.seed)
+ const random = new LegacyRandom(options.seed)
const params = NoiseParameters.fromJson(DataModel.unwrapLists(state))
const noise = new NormalNoise(random, params)
diff --git a/src/app/previews/noise/NoiseChunkGenerator.ts b/src/app/previews/noise/NoiseChunkGenerator.ts
index 054687fd..422685f4 100644
--- a/src/app/previews/noise/NoiseChunkGenerator.ts
+++ b/src/app/previews/noise/NoiseChunkGenerator.ts
@@ -1,4 +1,4 @@
-import { PerlinNoise, Random } from 'deepslate'
+import { LegacyRandom, PerlinNoise } from 'deepslate'
import { clampedLerp, lerp2 } from '../../Utils'
export class NoiseChunkGenerator {
@@ -18,7 +18,7 @@ export class NoiseChunkGenerator {
private xOffset: number = 0
constructor(seed: bigint) {
- const random = new Random(seed)
+ const random = new LegacyRandom(seed)
this.minLimitPerlinNoise = new PerlinNoise(random, -15, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
this.maxLimitPerlinNoise = new PerlinNoise(random, -15, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
this.mainPerlinNoise = new PerlinNoise(random, -7, [1, 1, 1, 1, 1, 1, 1, 1])
diff --git a/src/locales/en.json b/src/locales/en.json
index 2b7e82af..45232b7e 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -5,13 +5,16 @@
"advancement": "Advancement",
"collapse": "Collapse",
"collapse_all": "Hold %0% to collapse all",
+ "configure_layers": "Configure layers",
"copy": "Copy",
"copied": "Copied!",
"copy_context": "Copy context",
"dimension_type": "Dimension Type",
"dimension": "Dimension",
+ "disabled": "Disabled",
"download": "Download",
"duplicate": "Duplicate",
+ "enabled": "Enabled",
"entries_hidden": "%0% entries hidden",
"entries_hidden.more": "Show %0% more",
"entries_hidden.all": "Show all",
@@ -31,6 +34,15 @@
"indentation.tabs": "Tabs",
"item_modifier": "Item Modifier",
"language": "Language",
+ "layer.biomes": "Biomes",
+ "layer.temperature": "Temperature",
+ "layer.humidity": "Humidity",
+ "layer.continentalness": "Continentalness",
+ "layer.erosion": "Erosion",
+ "layer.weirdness": "Weirdness",
+ "layer.offset": "Offset",
+ "layer.factor": "Factor",
+ "layer.jaggedness": "Jaggedness",
"loot_table": "Loot Table",
"more": "More",
"move_down": "Move down",