diff --git a/package-lock.json b/package-lock.json index ce7b83eb..d500e43e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@mcschema/locales": "^0.1.45", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.9.0-beta.7", + "deepslate": "^0.9.0-beta.8", "deepslate-rs": "^0.1.6", "howler": "^2.2.3", "js-yaml": "^3.14.1", @@ -1679,9 +1679,9 @@ "dev": true }, "node_modules/deepslate": { - "version": "0.9.0-beta.7", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.9.0-beta.7.tgz", - "integrity": "sha512-BLdojH5hs5O8Lpcx/RoCM/FgiXNtWr0YWI/9jkm2Xg50EHVlAo/IzmFBbF7ERkocY8ZqkD4GqjxDsmyl2pv4Jg==", + "version": "0.9.0-beta.8", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.9.0-beta.8.tgz", + "integrity": "sha512-tq8IHZ3EWxuIb/sF0FgnXmYLzSmAwLufLkFjThuM/JUMKCOopeYyQq6sNjFOf0VQi9a3P9My2sgkZgJL1DkRlg==", "dependencies": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", @@ -5663,9 +5663,9 @@ "dev": true }, "deepslate": { - "version": "0.9.0-beta.7", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.9.0-beta.7.tgz", - "integrity": "sha512-BLdojH5hs5O8Lpcx/RoCM/FgiXNtWr0YWI/9jkm2Xg50EHVlAo/IzmFBbF7ERkocY8ZqkD4GqjxDsmyl2pv4Jg==", + "version": "0.9.0-beta.8", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.9.0-beta.8.tgz", + "integrity": "sha512-tq8IHZ3EWxuIb/sF0FgnXmYLzSmAwLufLkFjThuM/JUMKCOopeYyQq6sNjFOf0VQi9a3P9My2sgkZgJL1DkRlg==", "requires": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", diff --git a/package.json b/package.json index dd9c3e43..51b4d0f8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@mcschema/locales": "^0.1.45", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.9.0-beta.7", + "deepslate": "^0.9.0-beta.8", "deepslate-rs": "^0.1.6", "howler": "^2.2.3", "js-yaml": "^3.14.1", diff --git a/src/app/components/BtnInput.tsx b/src/app/components/BtnInput.tsx index 88ff165c..3eab5abb 100644 --- a/src/app/components/BtnInput.tsx +++ b/src/app/components/BtnInput.tsx @@ -1,16 +1,19 @@ import { useEffect, useRef } from 'preact/hooks' import { Octicon } from '.' +import { hexId } from '../Utils' type BtnInputProps = { icon?: keyof typeof Octicon, label?: string, large?: boolean, + larger?: boolean, doSelect?: number, value?: string, placeholder?: string, + dataList?: string[], onChange?: (value: string) => unknown, } -export function BtnInput({ icon, label, large, doSelect, value, placeholder, onChange }: BtnInputProps) { +export function BtnInput({ icon, label, large, larger, doSelect, value, placeholder, dataList, onChange }: BtnInputProps) { const onInput = onChange === undefined ? () => {} : (e: any) => { const value = (e.target as HTMLInputElement).value onChange?.(value) @@ -23,9 +26,14 @@ export function BtnInput({ icon, label, large, doSelect, value, placeholder, onC } }, [doSelect]) - return
e.stopPropagation()}> + const dataListId = dataList && hexId() + + return
e.stopPropagation()}> {icon && Octicon[icon]} {label && {label}} - + + {dataList && + {dataList.map(e => }
} diff --git a/src/app/components/previews/NoiseSettingsPreview.tsx b/src/app/components/previews/NoiseSettingsPreview.tsx index d970019b..bb00bf2c 100644 --- a/src/app/components/previews/NoiseSettingsPreview.tsx +++ b/src/app/components/previews/NoiseSettingsPreview.tsx @@ -1,15 +1,16 @@ -import { useEffect, useRef, useState } from 'preact/hooks' +import { useEffect, useMemo, useRef, useState } from 'preact/hooks' import type { PreviewProps } from '.' import { Btn, BtnInput, BtnMenu } from '..' import { useLocale } from '../../contexts' import { useCanvas } from '../../hooks' -import { noiseSettings } from '../../previews' -import { checkVersion } from '../../services' +import { getNoiseBlock, noiseSettings } from '../../previews' +import { CachedCollections, checkVersion } from '../../services' import { randomSeed } from '../../Utils' export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => { const { locale } = useLocale() const [seed, setSeed] = useState(randomSeed()) + const [biome, setBiome] = useState('minecraft:plains') const [biomeScale, setBiomeScale] = useState(0.2) const [biomeDepth, setBiomeDepth] = useState(0.1) const [focused, setFocused] = useState(undefined) @@ -22,16 +23,18 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => return [size, size] }, async draw(img) { - const options = { biomeDepth, biomeScale, offset: offset.current, width: img.width, seed, version } + const options = { biome, biomeDepth, biomeScale, offset: offset.current, width: img.width, seed, version } noiseSettings(data, img, options) }, async onDrag(dx) { offset.current += dx * size redraw() }, - async onHover(_, y) { + async onHover(x, y) { + const worldX = Math.floor(x * size - offset.current) const worldY = size - Math.max(1, Math.ceil(y * size)) + (data?.noise?.min_y ?? 0) - setFocused(`${worldY}`) + const block = getNoiseBlock(worldX, worldY) + setFocused(block ? `Y=${worldY} (${block.getName().replace(/^minecraft:/, '')})` : `Y=${worldY}`) }, onLeave() { setFocused(undefined) @@ -42,17 +45,21 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => if (shown) { redraw() } - }, [state, seed, shown]) + }, [state, seed, shown, biome, biomeScale, biomeDepth]) + + const allBiomes = useMemo(() => CachedCollections?.get('worldgen/biome') ?? [], [version]) return <>
- {focused && } - {checkVersion(version, undefined, '1.17') && - + {focused && } + + {checkVersion(version, undefined, '1.17') ? <> setBiomeScale(Number(v))} /> setBiomeDepth(Number(v))} /> - - } + : + + } + setSeed(randomSeed())} />
diff --git a/src/app/previews/NoiseSettings.ts b/src/app/previews/NoiseSettings.ts index 1433fa16..b1829d0b 100644 --- a/src/app/previews/NoiseSettings.ts +++ b/src/app/previews/NoiseSettings.ts @@ -1,6 +1,6 @@ import { DataModel } from '@mcschema/core' -import type { BlockPos, BlockState } from 'deepslate' -import { Chunk, ChunkPos, FixedBiome, NoiseChunkGenerator, NoiseGeneratorSettings } from 'deepslate' +import type { BlockState } from 'deepslate' +import { BlockPos, Chunk, ChunkPos, FixedBiome, NoiseChunkGenerator, NoiseGeneratorSettings } from 'deepslate' import { getOctaves } from '../components' import type { VersionId } from '../services' import { checkVersion } from '../services' @@ -8,6 +8,7 @@ import { deepClone, deepEqual } from '../Utils' import { NoiseChunkGenerator as OldNoiseChunkGenerator } from './noise/NoiseChunkGenerator' export type NoiseSettingsOptions = { + biome?: string, biomeScale?: number, biomeDepth?: number, offset: number, @@ -22,8 +23,18 @@ const colors: Record = { 'minecraft:air': [150, 160, 170], 'minecraft:water': [20, 80, 170], 'minecraft:lava': [200, 100, 0], - 'minecraft:stone': [50, 50, 50], + 'minecraft:stone': [55, 55, 55], + 'minecraft:deepslate': [34, 34, 36], + 'minecraft:bedrock': [10, 10, 10], + 'minecraft:grass_block': [47, 120, 23], + 'minecraft:dirt': [64, 40, 8], + 'minecraft:gravel': [70, 70, 70], + 'minecraft:sand': [196, 180, 77], + 'minecraft:sandstone': [148, 135, 52], 'minecraft:netherrack': [100, 40, 40], + 'minecraft:crimson_nylium': [144, 22, 22], + 'minecraft:warped_nylium': [28, 115, 113], + 'minecraft:basalt': [73, 74, 85], 'minecraft:end_stone': [200, 200, 140], } @@ -36,7 +47,7 @@ export function noiseSettings(state: any, img: ImageData, options: NoiseSettings const { settings, generator } = getCached(state, options) const slice = new LevelSlice(-options.offset, options.width, settings.noise.minY, settings.noise.height) - slice.fill(generator) + slice.generate(generator, options.biome ?? 'minecraft:plains') const data = img.data for (let x = 0; x < options.width; x += 1) { @@ -70,11 +81,19 @@ export function noiseSettings(state: any, img: ImageData, options: NoiseSettings } } +export function getNoiseBlock(x: number, y: number) { + const chunk = chunkCache.find(c => ChunkPos.minBlockX(c.pos) <= x && ChunkPos.maxBlockX(c.pos) >= x) + if (!chunk) { + return undefined + } + return chunk.getBlockState(BlockPos.create(x, y, Z)) +} + function getCached(state: unknown, options: NoiseSettingsOptions) { const settings = NoiseGeneratorSettings.fromJson(DataModel.unwrapLists(state)) settings.octaves = getOctaves(settings) - const newState = [state, `${options.seed}`] + const newState = [state, `${options.seed}`, options.biome] if (!deepEqual(newState, cacheState)) { cacheState = deepClone(newState) chunkCache = [] @@ -99,7 +118,7 @@ function getColor(noise: number[], y: number): number { class LevelSlice { private readonly chunks: Chunk[] - private readonly filled: boolean[] + private readonly done: boolean[] constructor( private readonly minX: number, @@ -107,24 +126,25 @@ class LevelSlice { minY: number, height: number, ) { - this.filled = [] + this.done = [] this.chunks = [...Array(Math.ceil(width / 16) + 1)] .map((_, i) => { const x = (minX >> 4) + i const cached = chunkCache.find(c => c.pos[0] === x) if (cached) { - this.filled[i] = true + this.done[i] = true return cached } return new Chunk(minY, height, ChunkPos.create(x, Z >> 4)) }) } - public fill(generator: NoiseChunkGenerator) { + public generate(generator: NoiseChunkGenerator, forcedBiome: string) { this.chunks.forEach((chunk, i) => { - if (!this.filled[i]) { + if (!this.done[i]) { generator.fill(chunk) - this.filled[i] = true + generator.buildSurface(chunk, forcedBiome) + this.done[i] = true chunkCache.push(chunk) } }) diff --git a/src/app/services/Schemas.ts b/src/app/services/Schemas.ts index 4c6e799e..cb9faa36 100644 --- a/src/app/services/Schemas.ts +++ b/src/app/services/Schemas.ts @@ -51,6 +51,7 @@ const versionGetter: { export let CachedDecorator: INode export let CachedFeature: INode +export let CachedCollections: CollectionRegistry async function getVersion(id: VersionId): Promise { if (!Versions[id]) { @@ -112,6 +113,7 @@ export async function getModel(version: VersionId, id: string): Promise { const versionData = await getVersion(version) + CachedCollections = versionData.collections return versionData.collections } diff --git a/src/locales/en.json b/src/locales/en.json index d8e6bf60..0a63908e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -80,6 +80,7 @@ "title.sounds": "Sound Explorer", "presets": "Presets", "preview": "Visualize", + "preview.biome": "Biome", "preview.scale": "Scale", "preview.depth": "Depth", "preview.factor": "Factor", diff --git a/src/styles/global.css b/src/styles/global.css index 5ea4df25..b923a28a 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -518,6 +518,10 @@ main.has-preview { width: 100px; } +.btn-input.larger-input input { + width: 200px; +} + .btn-input.large-input { padding: 5px; padding-left: 11px;