mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Add decorator preview
This commit is contained in:
@@ -7,6 +7,7 @@ import { Preview } from './preview/Preview';
|
||||
import { RegistryFetcher } from './RegistryFetcher';
|
||||
import { BiomeNoisePreview } from './preview/BiomeNoisePreview';
|
||||
import { NoiseSettingsPreview } from './preview/NoiseSettingsPreview';
|
||||
import { DecoratorPreview } from './preview/DecoratorPreview';
|
||||
import config from '../config.json';
|
||||
import { locale, Locales } from './Locales';
|
||||
import { Tracker } from './Tracker';
|
||||
@@ -25,7 +26,8 @@ export const Previews: {
|
||||
[key: string]: Preview
|
||||
} = {
|
||||
'biome_noise': new BiomeNoisePreview(),
|
||||
'noise_settings': new NoiseSettingsPreview()
|
||||
'noise_settings': new NoiseSettingsPreview(),
|
||||
'decorator': new DecoratorPreview(),
|
||||
}
|
||||
|
||||
export const Models: {
|
||||
|
||||
@@ -18,6 +18,7 @@ export const Octicon = {
|
||||
mark_github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>',
|
||||
moon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M9.598 1.591a.75.75 0 01.785-.175 7 7 0 11-8.967 8.967.75.75 0 01.961-.96 5.5 5.5 0 007.046-7.046.75.75 0 01.175-.786zm1.616 1.945a7 7 0 01-7.678 7.678 5.5 5.5 0 107.678-7.678z"></path></svg>',
|
||||
note: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5zm1.75-.25a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM3.5 6.25a.75.75 0 01.75-.75h7a.75.75 0 010 1.5h-7a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h4a.75.75 0 000-1.5h-4z"></path></svg>',
|
||||
package: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.878.392a1.75 1.75 0 00-1.756 0l-5.25 3.045A1.75 1.75 0 001 4.951v6.098c0 .624.332 1.2.872 1.514l5.25 3.045a1.75 1.75 0 001.756 0l5.25-3.045c.54-.313.872-.89.872-1.514V4.951c0-.624-.332-1.2-.872-1.514L8.878.392zM7.875 1.69a.25.25 0 01.25 0l4.63 2.685L8 7.133 3.245 4.375l4.63-2.685zM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432L2.5 5.677zm6.25 8.271l4.625-2.683a.25.25 0 00.125-.216V5.677L8.75 8.432v5.516z"></path></svg>',
|
||||
play: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>',
|
||||
plus: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 2a.75.75 0 01.75.75v4.5h4.5a.75.75 0 010 1.5h-4.5v4.5a.75.75 0 01-1.5 0v-4.5h-4.5a.75.75 0 010-1.5h4.5v-4.5A.75.75 0 018 2z"></path></svg>',
|
||||
plus_circle: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.75 4.75a.75.75 0 00-1.5 0v2.5h-2.5a.75.75 0 000 1.5h2.5v2.5a.75.75 0 001.5 0v-2.5h2.5a.75.75 0 000-1.5h-2.5v-2.5z"></path></svg>',
|
||||
|
||||
@@ -25,6 +25,9 @@ export const suffixInjector: Hook<[Mounter], string | void> = {
|
||||
if (Previews.noise_settings.active(path)) {
|
||||
return setPreview(Previews.noise_settings, path, mounter)
|
||||
}
|
||||
if (Previews.decorator.active(path)) {
|
||||
return setPreview(Previews.decorator, path, mounter)
|
||||
}
|
||||
},
|
||||
|
||||
string({}, path, mounter) {
|
||||
|
||||
202
src/app/preview/DecoratorPreview.ts
Normal file
202
src/app/preview/DecoratorPreview.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { DataModel, Path, ModelPath } from "@mcschema/core"
|
||||
import seedrandom from "seedrandom"
|
||||
import { App } from "../App"
|
||||
import { clamp, hexId, stringToColor } from "../Utils"
|
||||
import { PerlinNoise } from "./noise/PerlinNoise"
|
||||
import { Preview } from './Preview'
|
||||
import { Octicon } from '../components/Octicon'
|
||||
import { View } from "../views/View"
|
||||
|
||||
type BlockPos = [number, number, number]
|
||||
type Placement = { pos: BlockPos, feature: string }
|
||||
|
||||
export class DecoratorPreview extends Preview {
|
||||
private seed: string
|
||||
private perspective: string
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.seed = hexId()
|
||||
this.perspective = 'top'
|
||||
}
|
||||
|
||||
getName() {
|
||||
return 'decorator'
|
||||
}
|
||||
|
||||
active(path: ModelPath) {
|
||||
return App.model.get()?.id === 'worldgen/feature'
|
||||
&& path.equals(new Path(['config', 'decorator']))
|
||||
&& path.pop().pop().push('type').get() === 'minecraft:decorated'
|
||||
}
|
||||
|
||||
menu(view: View, redraw: () => void) {
|
||||
return `
|
||||
<div class="btn" data-id="${view.onClick(() => {
|
||||
this.perspective = this.perspective === 'top' ? 'side' : 'top'
|
||||
redraw()
|
||||
})}">
|
||||
${Octicon.package}
|
||||
</div>`
|
||||
}
|
||||
|
||||
getSize(): [number, number] {
|
||||
return this.perspective === 'top' ? [4 * 16, 3 * 16] : [4 * 16, 128]
|
||||
}
|
||||
|
||||
draw(model: DataModel, img: ImageData) {
|
||||
const featureData = JSON.parse(JSON.stringify(model.data))
|
||||
const random = seedrandom(this.seed)
|
||||
|
||||
let placements: Placement[] = []
|
||||
for (let x = 0; x < 4; x += 1) {
|
||||
for (let z = 0; z < (this.perspective === 'top' ? 3 : 1); z += 1) {
|
||||
const p = getPlacements(random, [x * 16, 0, z * 16], featureData)
|
||||
placements = [...placements, ...p]
|
||||
}
|
||||
}
|
||||
|
||||
const data = img.data
|
||||
img.data.fill(255)
|
||||
|
||||
for (let {pos, feature} of placements) {
|
||||
const i = this.perspective === 'top'
|
||||
? (pos[2] * (img.width * 4)) + (pos[0] * 4)
|
||||
: ((127 - pos[1]) * (img.width * 4)) + (pos[0] * 4)
|
||||
const color = stringToColor(feature)
|
||||
data.set(color.map(c => clamp(30, 205, c)), i)
|
||||
}
|
||||
|
||||
for (let x = 0; x < 4 * 16; x += 1) {
|
||||
for (let y = 0; y < (this.perspective === 'top' ? 3 * 16: 128); y += 1) {
|
||||
if ((Math.floor(x/16) + (this.perspective === 'top' ? Math.floor(y/16) : 0)) % 2 === 0) continue
|
||||
const i = (y * (img.width * 4)) + (x * 4)
|
||||
for (let j = 0; j < 4; j += 1) {
|
||||
data[i + j] = data[i + j] - 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const biomeInfoNoise = new PerlinNoise(hexId(), 0, [1])
|
||||
|
||||
function getPlacements (random: seedrandom.prng, pos: BlockPos, feature: any): Placement[] {
|
||||
if (typeof feature === 'string') {
|
||||
return [{ pos, feature }]
|
||||
}
|
||||
const type = feature?.type?.replace(/^minecraft:/, '')
|
||||
const featureFn = Features[type]
|
||||
if (!featureFn) {
|
||||
return [{ pos, feature: JSON.stringify(feature) }]
|
||||
}
|
||||
return featureFn(feature.config, random, pos)
|
||||
}
|
||||
|
||||
function getPositions (random: seedrandom.prng, pos: BlockPos, decorator: any): BlockPos[] {
|
||||
const type = decorator?.type?.replace(/^minecraft:/, '')
|
||||
const decoratorFn = Decorators[type]
|
||||
if (!decoratorFn) {
|
||||
return [pos]
|
||||
}
|
||||
return decoratorFn(decorator?.config, random, pos)
|
||||
}
|
||||
|
||||
const Features: {
|
||||
[key: string]: (config: any, random: seedrandom.prng, pos: BlockPos) => Placement[]
|
||||
} = {
|
||||
decorated: (config, random, pos) => {
|
||||
const positions = getPositions(random, pos, config?.decorator)
|
||||
return positions.flatMap(p => getPlacements(random, p, config?.feature))
|
||||
},
|
||||
random_boolean_selector: (config, random, pos) => {
|
||||
const feature = random() < 0.5 ? config?.feature_true : config?.feature_false
|
||||
return getPlacements(random, pos, feature)
|
||||
},
|
||||
random_selector: (config, random, pos) => {
|
||||
for (const f of config?.features ?? []) {
|
||||
if (random() < (f?.chance ?? 0)) {
|
||||
return getPlacements(random, pos, f.feature)
|
||||
}
|
||||
}
|
||||
return getPlacements(random, pos, config?.default)
|
||||
},
|
||||
simple_random_selector: (config, random, pos) => {
|
||||
const feature = config?.features?.[nextInt(random, config?.features?.length ?? 0)]
|
||||
return getPlacements(random, pos, feature)
|
||||
}
|
||||
}
|
||||
|
||||
const Decorators: {
|
||||
[key: string]: (config: any, random: seedrandom.prng, pos: BlockPos) => BlockPos[]
|
||||
} = {
|
||||
count: (config, random, pos) => {
|
||||
return new Array(sampleUniformInt(random, config?.count ?? 1)).fill(pos)
|
||||
},
|
||||
count_extra: (config, random, pos) => {
|
||||
let count = config?.count ?? 1
|
||||
if (random() < config.extra_chance ?? 0){
|
||||
count += config.extra_count ?? 0
|
||||
}
|
||||
return new Array(count).fill(pos)
|
||||
},
|
||||
count_noise: (config, random, pos) => {
|
||||
const noise = biomeInfoNoise.getValue(pos[0] / 200, 0, pos[2] / 200)
|
||||
const count = noise < config.noise_level ? config.below_noise : config.above_noise
|
||||
return new Array(count).fill(pos)
|
||||
},
|
||||
count_noise_biased: (config, random, pos) => {
|
||||
const factor = Math.max(1, config.noise_factor)
|
||||
const noise = biomeInfoNoise.getValue(pos[0] / factor, 0, pos[2] / factor)
|
||||
const count = Math.max(0, Math.ceil((noise + config.noise_offset) * config.noise_to_count_ratio))
|
||||
return new Array(count).fill(pos)
|
||||
},
|
||||
decorated: (config, random, pos) => {
|
||||
return getPositions(random, pos, config?.outer).flatMap(p => {
|
||||
return getPositions(random, p, config?.inner)
|
||||
})
|
||||
},
|
||||
range: (config, random, pos) => {
|
||||
const y = nextInt(random, (config?.maximum ?? 1) - (config?.top_offset ?? 0)) + (config?.bottom_offset ?? 0)
|
||||
return decorateY(pos, y)
|
||||
},
|
||||
range_biased: (config, random, pos) => {
|
||||
const y = nextInt(random, nextInt(random, (config?.maximum ?? 1) - (config?.top_offset ?? 0)) + (config?.bottom_offset ?? 0))
|
||||
return decorateY(pos, y)
|
||||
},
|
||||
range_very_biased: (config, random, pos) => {
|
||||
const y = nextInt(random, nextInt(random, nextInt(random, (config?.maximum ?? 1) - (config?.top_offset ?? 0)) + (config?.bottom_offset ?? 0)) + (config?.bottom_offset ?? 0))
|
||||
return decorateY(pos, y)
|
||||
},
|
||||
spread_32_above: (config, random, pos) => {
|
||||
const y = nextInt(random, pos[1] + 32)
|
||||
return decorateY(pos, y)
|
||||
},
|
||||
magma: (config, random, pos) => {
|
||||
const y = nextInt(random, pos[1] + 32)
|
||||
return decorateY(pos, y)
|
||||
},
|
||||
square: (config, random, pos) => {
|
||||
return [[
|
||||
pos[0] + nextInt(random, 16),
|
||||
pos[1],
|
||||
pos[2] + nextInt(random, 16)
|
||||
]]
|
||||
}
|
||||
}
|
||||
|
||||
function decorateY(pos: BlockPos, y: number): BlockPos[] {
|
||||
return [[ pos[0], y, pos[2] ]]
|
||||
}
|
||||
|
||||
function sampleUniformInt(random: seedrandom.prng, value: any): number {
|
||||
if (typeof value === 'number') {
|
||||
return value
|
||||
} else {
|
||||
return (value.base ?? 1) + nextInt(random, 1 + (value.spread ?? 0))
|
||||
}
|
||||
}
|
||||
|
||||
function nextInt(random: seedrandom.prng, max: number): number {
|
||||
return Math.floor(random() * max)
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2019"
|
||||
],
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
|
||||
Reference in New Issue
Block a user