pages235/renderer/playground/scenes/main.ts
2025-03-25 04:01:43 +03:00

314 lines
9.4 KiB
TypeScript

//@ts-nocheck
// eslint-disable-next-line import/no-named-as-default
import GUI, { Controller } from 'lil-gui'
import * as THREE from 'three'
import JSZip from 'jszip'
import { BasePlaygroundScene } from '../baseScene'
import { TWEEN_DURATION } from '../../viewer/three/entities'
import { EntityMesh } from '../../viewer/three/entity/EntityMesh'
class MainScene extends BasePlaygroundScene {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor (...args) {
//@ts-expect-error
super(...args)
}
override initGui (): void {
// initial values
this.params = {
version: globalThis.includedVersions.at(-1),
skipQs: '',
block: '',
metadata: 0,
supportBlock: false,
entity: '',
removeEntity () {
this.entity = ''
},
entityRotate: false,
camera: '',
playSound () { },
blockIsomorphicRenderBundle () { },
modelVariant: 0
}
this.metadataGui = this.gui.add(this.params, 'metadata')
this.paramOptions = {
version: {
options: globalThis.includedVersions,
hide: false
},
block: {
options: mcData.blocksArray.map(b => b.name).sort((a, b) => a.localeCompare(b))
},
entity: {
options: mcData.entitiesArray.map(b => b.name).sort((a, b) => a.localeCompare(b))
},
camera: {
hide: true,
}
}
super.initGui()
}
blockProps = {}
metadataFolder: GUI | undefined
metadataGui: Controller
override onParamUpdate = {
version () {
// if (initialUpdate) return
// viewer.world.texturesVersion = params.version
// viewer.world.updateTexturesData()
// todo warning
},
block: () => {
this.blockProps = {}
this.metadataFolder?.destroy()
const block = mcData.blocksByName[this.params.block]
if (!block) return
console.log('block', block.name)
const props = new this.Block(block.id, 0, 0).getProperties()
const { states } = mcData.blocksByStateId[this.getBlock()?.minStateId] ?? {}
this.metadataFolder = this.gui.addFolder('metadata')
if (states) {
for (const state of states) {
let defaultValue: string | number | boolean
if (state.values) { // int, enum
defaultValue = state.values[0]
} else {
switch (state.type) {
case 'bool':
defaultValue = false
break
case 'int':
defaultValue = 0
break
case 'direction':
defaultValue = 'north'
break
default:
continue
}
}
this.blockProps[state.name] = defaultValue
if (state.values) {
this.metadataFolder.add(this.blockProps, state.name, state.values)
} else {
this.metadataFolder.add(this.blockProps, state.name)
}
}
} else {
for (const [name, value] of Object.entries(props)) {
this.blockProps[name] = value
this.metadataFolder.add(this.blockProps, name)
}
}
console.log('props', this.blockProps)
this.metadataFolder.open()
},
entity: () => {
this.continuousRender = this.params.entity === 'player'
this.entityUpdateShared()
if (!this.params.entity) return
if (this.params.entity === 'player') {
viewer.entities.updatePlayerSkin('id', viewer.entities.entities.id.username, undefined, true, true)
viewer.entities.playAnimation('id', 'running')
}
// let prev = false
// setInterval(() => {
// viewer.entities.playAnimation('id', prev ? 'running' : 'idle')
// prev = !prev
// }, 1000)
EntityMesh.getStaticData(this.params.entity)
// entityRotationFolder.destroy()
// entityRotationFolder = gui.addFolder('entity metadata')
// entityRotationFolder.add(params, 'entityRotate')
// entityRotationFolder.open()
},
supportBlock: () => {
viewer.setBlockStateId(this.targetPos.offset(0, -1, 0), this.params.supportBlock ? 1 : 0)
},
modelVariant: () => {
viewer.world.mesherConfig.debugModelVariant = this.params.modelVariant === 0 ? undefined : [this.params.modelVariant]
}
}
entityUpdateShared () {
viewer.entities.clear()
if (!this.params.entity) return
worldView!.emit('entity', {
id: 'id', name: this.params.entity, pos: this.targetPos.offset(0.5, 1, 0.5), width: 1, height: 1, username: localStorage.testUsername, yaw: Math.PI, pitch: 0
})
const enableSkeletonDebug = (obj) => {
const { children, isSkeletonHelper } = obj
if (!Array.isArray(children)) return
if (isSkeletonHelper) {
obj.visible = true
return
}
for (const child of children) {
if (typeof child === 'object') enableSkeletonDebug(child)
}
}
enableSkeletonDebug(viewer.entities.entities['id'])
setTimeout(() => {
viewer.render()
}, TWEEN_DURATION)
}
blockIsomorphicRenderBundle () {
const { renderer } = viewer
const canvas = renderer.domElement
const onlyCurrent = !confirm('Ok - render all blocks, Cancel - render only current one')
const sizeRaw = prompt('Size', '512')
if (!sizeRaw) return
const size = parseInt(sizeRaw, 10)
// const size = 512
this.ignoreResize = true
canvas.width = size
canvas.height = size
renderer.setSize(size, size)
viewer.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 10)
viewer.scene.background = null
const rad = THREE.MathUtils.degToRad(-120)
viewer.directionalLight.position.set(
Math.cos(rad),
Math.sin(rad),
0.2
).normalize()
viewer.directionalLight.intensity = 1
const cameraPos = this.targetPos.offset(2, 2, 2)
const pitch = THREE.MathUtils.degToRad(-30)
const yaw = THREE.MathUtils.degToRad(45)
viewer.camera.rotation.set(pitch, yaw, 0, 'ZYX')
// viewer.camera.lookAt(center.x + 0.5, center.y + 0.5, center.z + 0.5)
viewer.camera.position.set(cameraPos.x + 1, cameraPos.y + 0.5, cameraPos.z + 1)
const allBlocks = mcData.blocksArray.map(b => b.name)
// const allBlocks = ['stone', 'warped_slab']
let blockCount = 1
let blockName = allBlocks[0]
const updateBlock = () => {
// viewer.setBlockStateId(targetPos, mcData.blocksByName[blockName].minStateId)
this.params.block = blockName
// todo cleanup (introduce getDefaultState)
// TODO
// onUpdate.block()
// applyChanges(false, true)
}
void viewer.waitForChunksToRender().then(async () => {
// wait for next macro task
await new Promise(resolve => {
setTimeout(resolve, 0)
})
if (onlyCurrent) {
viewer.render()
onWorldUpdate()
} else {
// will be called on every render update
viewer.world.renderUpdateEmitter.addListener('update', onWorldUpdate)
updateBlock()
}
})
const zip = new JSZip()
zip.file('description.txt', 'Generated with mcraft.fun/playground')
const end = async () => {
// download zip file
const a = document.createElement('a')
const blob = await zip.generateAsync({ type: 'blob' })
const dataUrlZip = URL.createObjectURL(blob)
a.href = dataUrlZip
a.download = 'blocks_render.zip'
a.click()
URL.revokeObjectURL(dataUrlZip)
console.log('end')
viewer.world.renderUpdateEmitter.removeListener('update', onWorldUpdate)
}
async function onWorldUpdate () {
// await new Promise(resolve => {
// setTimeout(resolve, 50)
// })
const dataUrl = canvas.toDataURL('image/png')
zip.file(`${blockName}.png`, dataUrl.split(',')[1], { base64: true })
if (onlyCurrent) {
end()
} else {
nextBlock()
}
}
const nextBlock = async () => {
blockName = allBlocks[blockCount++]
console.log(allBlocks.length, '/', blockCount, blockName)
if (blockCount % 5 === 0) {
await new Promise(resolve => {
setTimeout(resolve, 100)
})
}
if (blockName) {
updateBlock()
} else {
end()
}
}
}
getBlock () {
return mcData.blocksByName[this.params.block || 'air']
}
// applyChanges (metadataUpdate = false, skipQs = false) {
override onParamsUpdate (paramName: string, object: any) {
const metadataUpdate = paramName === 'metadata'
const blockId = this.getBlock()?.id
let block: import('prismarine-block').Block
if (metadataUpdate) {
block = new this.Block(blockId, 0, this.params.metadata)
Object.assign(this.blockProps, block.getProperties())
for (const _child of this.metadataFolder!.children) {
const child = _child as import('lil-gui').Controller
child.updateDisplay()
}
} else {
try {
block = this.Block.fromProperties(blockId ?? -1, this.blockProps, 0)
} catch (err) {
console.error(err)
block = this.Block.fromStateId(0, 0)
}
}
worldView!.setBlockStateId(this.targetPos, block.stateId ?? 0)
console.log('up stateId', block.stateId)
this.params.metadata = block.metadata
this.metadataGui.updateDisplay()
}
override renderFinish () {
for (const update of Object.values(this.onParamUpdate)) {
// update(true)
update()
}
this.onParamsUpdate('', {})
this.gui.openAnimated()
}
}
export default MainScene