From 5e788e0fa0e4352cf56129f0702acf1db018efbf Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 11 Nov 2023 09:53:27 +0300 Subject: [PATCH] fix: fix rendering issues with water by correctly specifying render order fix: show chunks border now works without chunks reload --- README.MD | 2 +- prismarine-viewer/viewer/lib/viewer.ts | 4 ++ .../viewer/lib/worldDataEmitter.ts | 4 +- prismarine-viewer/viewer/lib/worldrenderer.ts | 44 +++++++++++++++++-- src/controls.ts | 3 +- 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/README.MD b/README.MD index 8297d2c0..919e2bd8 100644 --- a/README.MD +++ b/README.MD @@ -72,7 +72,7 @@ You can also drag and drop any .dat file into the browser window to see it's con - `F3` - Toggle debug overlay - `F3 + A` - Reload all chunks (these that are loaded from the server) -- `F3 + G` - Toggle chunk sections (geometries) border visibility (aka Three.js geometry helpers) - most probably need to reload chunks after toggling +- `F3 + G` - Toggle chunk sections (geometries) border visibility (aka Three.js geometry helpers) ### Notable Things that Power this Project diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index fcb64627..92860fbf 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -118,6 +118,10 @@ export class Viewer { this.setBlockStateId(new Vec3(pos.x, pos.y, pos.z), stateId) }) + emitter.on('chunkPosUpdate', ({ pos }) => { + this.world.updateViewerPosition(pos) + }) + emitter.emit('listening') this.domElement.addEventListener('pointerdown', (evt) => { diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index 3911696d..9ecb1f43 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -18,7 +18,7 @@ export class WorldDataEmitter extends EventEmitter { private eventListeners: Record = {}; private emitter: WorldDataEmitter - constructor (public world: import('prismarine-world').world.World | typeof __type_bot['world'], public viewDistance: number, position: Vec3 = new Vec3(0, 0, 0)) { + constructor(public world: import('prismarine-world').world.World | typeof __type_bot['world'], public viewDistance: number, position: Vec3 = new Vec3(0, 0, 0)) { super() this.loadedChunks = {} this.lastPos = new Vec3(0, 0, 0).update(position) @@ -91,6 +91,7 @@ export class WorldDataEmitter extends EventEmitter { } async init (pos: Vec3) { + this.emitter.emit('chunkPosUpdate', { pos }) const [botX, botZ] = chunkPos(pos) const positions = generateSpiralMatrix(this.viewDistance).map(([x, z]) => new Vec3((botX + x) * 16, 0, (botZ + z) * 16)) @@ -138,6 +139,7 @@ export class WorldDataEmitter extends EventEmitter { const [lastX, lastZ] = chunkPos(this.lastPos) const [botX, botZ] = chunkPos(pos) if (lastX !== botX || lastZ !== botZ || force) { + this.emitter.emit('chunkPosUpdate', { pos }) const newView = new ViewRect(botX, botZ, this.viewDistance) const chunksToUnload: Vec3[] = [] for (const coords of Object.keys(this.loadedChunks)) { diff --git a/prismarine-viewer/viewer/lib/worldrenderer.ts b/prismarine-viewer/viewer/lib/worldrenderer.ts index d7cea5e9..3f022a73 100644 --- a/prismarine-viewer/viewer/lib/worldrenderer.ts +++ b/prismarine-viewer/viewer/lib/worldrenderer.ts @@ -32,6 +32,7 @@ export class WorldRenderer { downloadedBlockStatesData = undefined as any downloadedTextureImage = undefined as any workers: any[] = [] + viewerPosition?: Vec3 texturesVersion?: string @@ -69,13 +70,16 @@ export class WorldRenderer { const mesh = new THREE.Mesh(geometry, this.material) mesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz) + mesh.name = 'mesh' object = new THREE.Group() object.add(mesh) - if (this.showChunkBorders) { - const boxHelper = new THREE.BoxHelper(mesh, 0xffff00) - object.add(boxHelper) + const boxHelper = new THREE.BoxHelper(mesh, 0xffff00) + boxHelper.name = 'helper' + object.add(boxHelper) + if (!this.showChunkBorders) { + boxHelper.visible = false } - // should not it compute once + // should not compute it once if (Object.keys(data.geometry.signs).length) { for (const [posKey, { isWall, rotation }] of Object.entries(data.geometry.signs)) { const [x, y, z] = posKey.split(',') @@ -85,6 +89,7 @@ export class WorldRenderer { } } this.sectionObjects[data.key] = object + this.updatePosDataChunk(data.key) this.scene.add(object) } else if (data.type === 'sectionFinished') { this.sectionsOutstanding.delete(data.key) @@ -96,6 +101,26 @@ export class WorldRenderer { } } + /** + * Optionally update data that are depedendent on the viewer position + */ + updatePosDataChunk (key: string) { + if (!this.viewerPosition) return + const [x, y, z] = key.split(',').map(x => Math.floor(+x / 16)) + const [xPlayer, yPlayer, zPlayer] = this.viewerPosition.toArray().map(x => Math.floor(x / 16)) + // sum of distances: x + y + z + const chunkDistance = Math.abs(x - xPlayer) + Math.abs(y - yPlayer) + Math.abs(z - zPlayer) + const section = this.sectionObjects[key].children.find(child => child.name === 'mesh')! + section.renderOrder = 500 - chunkDistance + } + + updateViewerPosition (pos: Vec3) { + this.viewerPosition = pos + for (const key of Object.keys(this.sectionObjects)) { + this.updatePosDataChunk(key) + } + } + renderSign (position: Vec3, rotation: number, isWall: boolean, blockEntity) { const PrismarineChat = PrismarineChatLoader(this.version!) const canvas = renderSign(blockEntity, PrismarineChat) @@ -126,6 +151,17 @@ export class WorldRenderer { return group } + updateShowChunksBorder (value: boolean) { + this.showChunkBorders = value + for (const object of Object.values(this.sectionObjects)) { + for (const child of object.children) { + if (child.name === 'helper') { + child.visible = value; + } + } + } + } + resetWorld () { this.active = false for (const mesh of Object.values(this.sectionObjects)) { diff --git a/src/controls.ts b/src/controls.ts index 3d26f3d9..bd8ee349 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -244,9 +244,8 @@ document.addEventListener('keydown', (e) => { void reloadChunks() } if (e.code === 'KeyG') { - // todo make it work without reload options.showChunkBorders = !options.showChunkBorders - void reloadChunks() + viewer.world.updateShowChunksBorder(options.showChunkBorders) } return }