diff --git a/package.json b/package.json index 6bf46b236..6afdbfa08 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mc-assets": "^0.2.23", + "mc-assets": "^0.2.25", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5731f755a..27893e9af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -355,8 +355,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.23 - version: 0.2.23 + specifier: ^0.2.25 + version: 0.2.25 minecraft-inventory-gui: specifier: github:zardoy/minecraft-inventory-gui#next version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) @@ -6604,8 +6604,8 @@ packages: peerDependencies: react: ^18.2.0 - mc-assets@0.2.23: - resolution: {integrity: sha512-sLbPhsSOYdW8nYllIyPZbVPnLu7V3bZTgIO4mI4nlG525q17NIbUNEjItHKtdi60u0vI6qLgHKjf0CoNRqa/Nw==} + mc-assets@0.2.25: + resolution: {integrity: sha512-MdtncPBC6kwIkYXsBsSEJGP+q2e+7Q4Wnb4j3FjS7gmafz50Vjp4E/S3MsM7H8R3FoDrjVIx6qR24l/rneW/Lw==} engines: {node: '>=18.0.0'} md5-file@4.0.0: @@ -17498,7 +17498,7 @@ snapshots: dependencies: react: 18.2.0 - mc-assets@0.2.23: {} + mc-assets@0.2.25: {} md5-file@4.0.0: {} diff --git a/prismarine-viewer/examples/baseScene.ts b/prismarine-viewer/examples/baseScene.ts index 07f11c93b..bbd89af78 100644 --- a/prismarine-viewer/examples/baseScene.ts +++ b/prismarine-viewer/examples/baseScene.ts @@ -210,7 +210,7 @@ export class BasePlaygroundScene { renderer.setSize(window.innerWidth, window.innerHeight) // Create viewer - const viewer = new Viewer(renderer, { numWorkers: 6, showChunkBorders: false, }) + const viewer = new Viewer(renderer, { numWorkers: 6, showChunkBorders: false, isPlayground: true }) viewer.setFirstPersonCamera(null, viewer.camera.rotation.y, viewer.camera.rotation.x) window.viewer = viewer viewer.world.blockstatesModels = blockstatesModels diff --git a/prismarine-viewer/examples/webgpuBlockModels.ts b/prismarine-viewer/examples/webgpuBlockModels.ts index a74834645..13dbdade1 100644 --- a/prismarine-viewer/examples/webgpuBlockModels.ts +++ b/prismarine-viewer/examples/webgpuBlockModels.ts @@ -3,6 +3,7 @@ import worldBlockProvider from 'mc-assets/dist/worldBlockProvider' import PrismarineBlock, { Block } from 'prismarine-block' import { IndexedBlock } from 'minecraft-data' import { getPreflatBlock } from '../viewer/lib/mesher/getPreflatBlock' +import { WEBGPU_FULL_TEXTURES_LIMIT } from './webgpuRendererShared' export const prepareCreateWebgpuBlocksModelsData = () => { const blocksMap = { @@ -27,15 +28,17 @@ export const prepareCreateWebgpuBlocksModelsData = () => { } const isPreflat = versionToNumber(viewer.world.version!) < versionToNumber('1.13') - const provider = worldBlockProvider(viewer.world.blockstatesModels, viewer.world.blocksAtlases, 'latest') + const provider = worldBlockProvider(viewer.world.blockstatesModels, viewer.world.blocksAtlasParser?.atlasJson ?? viewer.world.blocksAtlases, 'latest') const PBlockOriginal = PrismarineBlock(viewer.world.version!) + const interestedTextureTiles = new Set() + const blocksDataModelDebug = {} as AllBlocksDataModels const blocksDataModel = {} as AllBlocksDataModels const blocksProccessed = {} as Record let i = 0 const allBlocksStateIdToModelIdMap = {} as AllBlocksStateIdToModelIdMap - const addBlockModel = (state: number, name: string, props: Record, mcBlockData?: IndexedBlock) => { + const addBlockModel = (state: number, name: string, props: Record, mcBlockData?: IndexedBlock, defaultState = false) => { const models = provider.getAllResolvedModels0_1({ name, properties: props @@ -75,10 +78,14 @@ export const prepareCreateWebgpuBlocksModelsData = () => { if (Math.floor(blockData.rotation[faceIndex]) !== blockData.rotation[faceIndex]) { throw new Error(`Invalid rotation ${rotation} ${name}`) } + interestedTextureTiles.add(texture.debugName) } const k = i++ allBlocksStateIdToModelIdMap[state] = k blocksDataModel[k] = blockData + if (defaultState) { + blocksDataModelDebug[name] ??= blockData + } blocksProccessed[name] = true if (mcBlockData) { blockData.transparent = mcBlockData.transparent @@ -91,19 +98,19 @@ export const prepareCreateWebgpuBlocksModelsData = () => { water: 'water_still', lava: 'lava_still' } - for (const b of loadedData.blocksArray) { + outer: for (const b of loadedData.blocksArray) { for (let state = b.minStateId; state <= b.maxStateId; state++) { + if (interestedTextureTiles.size >= WEBGPU_FULL_TEXTURES_LIMIT) { + console.warn(`Limit in ${WEBGPU_FULL_TEXTURES_LIMIT} textures reached for full blocks, skipping others!`) + break outer + } const mapping = blocksMap[b.name] const block = PBlockOriginal.fromStateId(mapping && loadedData.blocksByName[mapping] ? loadedData.blocksByName[mapping].defaultState : state, 0) if (isPreflat) { getPreflatBlock(block) } - const textureOverride = textureOverrideFullBlocks[block.name] - if (!textureOverride && (block.shapes.length === 0 || !block.shapes.every(shape => { - return shape[0] === 0 && shape[1] === 0 && shape[2] === 0 && shape[3] === 1 && shape[4] === 1 && shape[5] === 1 - }))) { - continue - } + + const textureOverride = textureOverrideFullBlocks[block.name] as string | undefined if (textureOverride) { const k = i++ const texture = provider.getTextureInfo(textureOverride) @@ -119,14 +126,24 @@ export const prepareCreateWebgpuBlocksModelsData = () => { filterLight: b.filterLight } blocksDataModel[k] = blockData - } else { - addBlockModel(state, block.name, block.getProperties(), b) + interestedTextureTiles.add(textureOverride) + continue + } + + if (block.shapes.length === 0 || !block.shapes.every(shape => { + return shape[0] === 0 && shape[1] === 0 && shape[2] === 0 && shape[3] === 1 && shape[4] === 1 && shape[5] === 1 + })) { + continue } + + addBlockModel(state, block.name, block.getProperties(), b, state === b.defaultState) } } return { blocksDataModel, - allBlocksStateIdToModelIdMap + allBlocksStateIdToModelIdMap, + interestedTextureTiles, + blocksDataModelDebug } } export type AllBlocksDataModels = Record diff --git a/prismarine-viewer/examples/webgpuRenderer.ts b/prismarine-viewer/examples/webgpuRenderer.ts index e1ba747bc..557805568 100644 --- a/prismarine-viewer/examples/webgpuRenderer.ts +++ b/prismarine-viewer/examples/webgpuRenderer.ts @@ -525,7 +525,7 @@ export class WebgpuRenderer { postMessage({ type: 'rendererProblem', isContextLost: true, message: info.message }) }) - this.setBlocksModelData() + this.updateBlocksModelData() this.createNewDataBuffers() this.indirectDrawParams = new Uint32Array([quadVertexCount, 0, 0, 0]) @@ -564,7 +564,7 @@ export class WebgpuRenderer { } } - private setBlocksModelData () { + public updateBlocksModelData () { const keys = Object.keys(this.blocksDataModel) // const modelsDataLength = keys.length const modelsDataLength = +keys.at(-1)! @@ -581,6 +581,7 @@ export class WebgpuRenderer { modelsBuffer[+i * 2 + 1] = tempBuffer2 } + this.modelsBuffer?.destroy() this.modelsBuffer = this.createVertexStorage(modelsDataLength * cubeByteLength, 'modelsBuffer') this.device.queue.writeBuffer(this.modelsBuffer, 0, modelsBuffer) } diff --git a/prismarine-viewer/examples/webgpuRendererShared.ts b/prismarine-viewer/examples/webgpuRendererShared.ts index b514995ed..4eb9f5441 100644 --- a/prismarine-viewer/examples/webgpuRendererShared.ts +++ b/prismarine-viewer/examples/webgpuRendererShared.ts @@ -22,6 +22,9 @@ export const rendererParamsGui = { earlyZRejection: true } +export const WEBGPU_FULL_TEXTURES_LIMIT = 1024 +export const WEBGPU_HEIGHT_LIMIT = 1024 + export type RendererInitParams = GPURequestAdapterOptions & {} export type RendererParams = typeof defaultWebgpuRendererParams diff --git a/prismarine-viewer/examples/webgpuRendererWorker.ts b/prismarine-viewer/examples/webgpuRendererWorker.ts index 55a530b6a..0da4e45d5 100644 --- a/prismarine-viewer/examples/webgpuRendererWorker.ts +++ b/prismarine-viewer/examples/webgpuRendererWorker.ts @@ -162,6 +162,10 @@ export const workerProxyType = createWorkerProxy({ updateMaxFps (fps) { maxFps = fps }, + updateModels (blocksDataModel: WebgpuRenderer['blocksDataModel']) { + webgpuRenderer!.blocksDataModel = blocksDataModel + webgpuRenderer!.updateBlocksModelData() + }, addAddBlocksFlat (positions: number[]) { const chunks = new Map() for (let i = 0; i < positions.length; i += 3) { diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 53a59bdef..6dc8240b1 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -29,7 +29,8 @@ export const worldCleanup = buildCleanupDecorator('resetWorld') export const defaultWorldRendererConfig = { showChunkBorders: false, - numWorkers: 4 + numWorkers: 4, + isPlayground: false } export type WorldRendererConfig = typeof defaultWorldRendererConfig @@ -45,7 +46,6 @@ export abstract class WorldRendererCommon threejsCursorLineMaterial: LineMaterial @worldCleanup() cursorBlock = null as Vec3 | null - isPlayground = false displayStats = true @worldCleanup() worldConfig = { minY: 0, worldHeight: 256 } @@ -312,14 +312,15 @@ export abstract class WorldRendererCommon } } - async updateTexturesData (resourcePackUpdate = false) { + async updateTexturesData (resourcePackUpdate = false, prioritizeBlockTextures?: string[]) { const blocksAssetsParser = new AtlasParser(this.blocksAtlases, blocksAtlasLatest, blocksAtlasLegacy) const itemsAssetsParser = new AtlasParser(this.itemsAtlases, itemsAtlasLatest, itemsAtlasLegacy) + const customBlockTextures = Object.keys(this.customTextures.blocks?.textures ?? {}).filter(x => x.includes('/')) const { atlas: blocksAtlas, canvas: blocksCanvas } = await blocksAssetsParser.makeNewAtlas(this.texturesVersion ?? this.version ?? 'latest', (textureName) => { const texture = this.customTextures?.blocks?.textures[textureName] if (!texture) return return texture - }, this.customTextures?.blocks?.tileSize) + }, /* this.customTextures?.blocks?.tileSize */undefined, prioritizeBlockTextures, customBlockTextures) const { atlas: itemsAtlas, canvas: itemsCanvas } = await itemsAssetsParser.makeNewAtlas(this.texturesVersion ?? this.version ?? 'latest', (textureName) => { const texture = this.customTextures?.items?.textures[textureName] if (!texture) return diff --git a/prismarine-viewer/viewer/lib/worldrendererWebgpu.ts b/prismarine-viewer/viewer/lib/worldrendererWebgpu.ts index f2727fbd7..71464916a 100644 --- a/prismarine-viewer/viewer/lib/worldrendererWebgpu.ts +++ b/prismarine-viewer/viewer/lib/worldrendererWebgpu.ts @@ -8,7 +8,7 @@ import type { workerProxyType } from '../../examples/webgpuRendererWorker' import { useWorkerProxy } from '../../examples/workerProxy' import { defaultWebgpuRendererParams, rendererParamsGui } from '../../examples/webgpuRendererShared' import { loadJSON } from './utils.web' -import { WorldRendererCommon } from './worldrendererCommon' +import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon' import { MesherGeometryOutput } from './mesher/shared' import { addNewStat, addNewStat2, updateStatText } from './ui/newStats' import { isMobile } from './simpleUtils' @@ -28,16 +28,16 @@ export class WorldRendererWebgpu extends WorldRendererCommon { postRender = () => {} preRender = () => {} rendererParams = defaultWebgpuRendererParams + initCalled = false webgpuChannel: typeof workerProxyType['__workerProxy'] = this.getPlaceholderChannel() rendererDevice = '...' powerPreference: string | undefined - constructor (config, { powerPreference } = {} as any) { + constructor (config: WorldRendererConfig, { powerPreference } = {} as any) { super(config) this.powerPreference = powerPreference - void this.initWebgpu() void this.readyWorkerPromise.then(() => { this.addWebgpuListener('rendererProblem', (data) => { this.issueReporter.reportProblem(data.isContextLost, data.message) @@ -163,7 +163,12 @@ export class WorldRendererWebgpu extends WorldRendererCommon { } async updateTexturesData (resourcePackUpdate = false): Promise { - await super.updateTexturesData() + const { blocksDataModelDebug: blocksDataModelBefore, interestedTextureTiles } = prepareCreateWebgpuBlocksModelsData() + await super.updateTexturesData(undefined, [...interestedTextureTiles].map(x => x.replace('block/', ''))) + const { blocksDataModel, blocksDataModelDebug, allBlocksStateIdToModelIdMap } = prepareCreateWebgpuBlocksModelsData() + // this.webgpuChannel.updateModels(blocksDataModel) + this.sendDataForWebgpuRenderer({ allBlocksStateIdToModelIdMap }) + void this.initWebgpu(blocksDataModel) if (resourcePackUpdate) { const blob = await fetch(this.material.map!.image.src).then(async (res) => res.blob()) this.webgpuChannel.updateTexture(blob) @@ -203,23 +208,15 @@ export class WorldRendererWebgpu extends WorldRendererCommon { } } - async initWebgpu () { + async initWebgpu (blocksDataModel) { + if (this.initCalled) return + this.initCalled = true // do not use worker in safari, it is bugged const USE_WORKER = defaultWebgpuRendererParams.webgpuWorker - const playground = this.isPlayground - if (!this.material.map) { - await new Promise(resolve => { - // this.material.map!.image.onload = () => { - // resolve() - // } - this.renderUpdateEmitter.once('textureDownloaded', resolve) - }) - } + const playground = this.config.isPlayground const { image } = (this.material.map!) const imageBlob = await fetch(image.src).then(async (res) => res.blob()) - const { blocksDataModel: modelsData, allBlocksStateIdToModelIdMap } = prepareCreateWebgpuBlocksModelsData() - this.sendDataForWebgpuRenderer({ allBlocksStateIdToModelIdMap }) const existingCanvas = document.getElementById('viewer-canvas') existingCanvas?.remove() @@ -252,7 +249,7 @@ export class WorldRendererWebgpu extends WorldRendererCommon { imageBlob, playground, pickObj(localStorage, 'vertShader', 'fragShader', 'computeShader'), - modelsData, + blocksDataModel, { powerPreference: this.powerPreference as GPUPowerPreference } )