From b3807fff657f0a0829e966cfbf0ef8cc9261786f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 13 Jun 2025 08:05:17 +0300 Subject: [PATCH 1/2] fix: fix rotation of item frames & items within it --- renderer/viewer/lib/worldrendererCommon.ts | 25 ++++++++----- renderer/viewer/three/entities.ts | 40 +++++++++++++-------- renderer/viewer/three/worldrendererThree.ts | 12 +++++++ src/customChannels.ts | 2 +- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index 2b37742c..ae36f204 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -52,7 +52,8 @@ export const defaultWorldRendererConfig = { foreground: true, enableDebugOverlay: false, _experimentalSmoothChunkLoading: true, - _renderByChunks: false + _renderByChunks: false, + volume: 1 } export type WorldRendererConfig = typeof defaultWorldRendererConfig @@ -239,7 +240,7 @@ export abstract class WorldRendererCommon await Promise.all([ this.resetWorkers(), (async () => { - if (this.resourcesManager.currentResources) { + if (this.resourcesManager.currentResources?.itemsRenderer) { await this.updateAssetsData() } })() @@ -265,15 +266,14 @@ export abstract class WorldRendererCommon return this.highestBlocksByChunks.get(chunkKey) } - updateCustomBlock (chunkKey: string, blockPos: string, model: string) { + updateCustomBlock (chunkKey: string, blockKey: number | string, blockPos: { x: number, y: number, z: number }, model: string) { this.protocolCustomBlocks.set(chunkKey, { ...this.protocolCustomBlocks.get(chunkKey), - [blockPos]: model + [blockKey]: model }) - this.logWorkerWork(() => `-> updateCustomBlock ${chunkKey} ${blockPos} ${model} ${this.wasChunkSentToWorker(chunkKey)}`) + this.logWorkerWork(() => `-> updateCustomBlock ${chunkKey} ${blockKey} ${model} ${this.wasChunkSentToWorker(chunkKey)}`) if (this.wasChunkSentToWorker(chunkKey)) { - const [x, y, z] = blockPos.split(',').map(Number) - this.setBlockStateId(new Vec3(x, y, z), undefined) + this.setBlockStateId(new Vec3(blockPos.x, blockPos.y, blockPos.z), undefined) } } @@ -812,7 +812,16 @@ export abstract class WorldRendererCommon }) worldEmitter.on('onWorldSwitch', () => { - for (const fn of this.onWorldSwitched) fn() + for (const fn of this.onWorldSwitched) { + try { + fn() + } catch (e) { + setTimeout(() => { + console.log('[Renderer Backend] Error in onWorldSwitched:') + throw e + }, 0) + } + } }) worldEmitter.on('time', (timeOfDay) => { diff --git a/renderer/viewer/three/entities.ts b/renderer/viewer/three/entities.ts index 1a5e7c3f..ea4c3751 100644 --- a/renderer/viewer/three/entities.ts +++ b/renderer/viewer/three/entities.ts @@ -302,13 +302,8 @@ export class Entities { const dz = entity.position.z - botPos.z const distanceSquared = dx * dx + dy * dy + dz * dz - // Get chunk coordinates - const chunkX = Math.floor(entity.position.x / 16) * 16 - const chunkZ = Math.floor(entity.position.z / 16) * 16 - const chunkKey = `${chunkX},${chunkZ}` - // Entity is visible if within 16 blocks OR in a finished chunk - entity.visible = !!(distanceSquared < VISIBLE_DISTANCE || this.worldRenderer.finishedChunks[chunkKey]) + entity.visible = !!(distanceSquared < VISIBLE_DISTANCE || this.worldRenderer.shouldObjectVisible(entity)) this.maybeRenderPlayerSkin(entityId) } @@ -540,6 +535,12 @@ export class Entities { } } + debugSwingArm () { + const playerObject = Object.values(this.entities).find(entity => entity.playerObject?.animation instanceof WalkingGeneralSwing) + if (!playerObject) return + (playerObject.playerObject!.animation as WalkingGeneralSwing).swingArm() + } + playAnimation (entityPlayerId, animation: 'walking' | 'running' | 'oneSwing' | 'idle' | 'crouch' | 'crouchWalking') { const playerObject = this.getPlayerObject(entityPlayerId) if (!playerObject) return @@ -693,7 +694,7 @@ export class Entities { return } - let mesh + let mesh: THREE.Object3D | undefined if (e === undefined) { const group = new THREE.Group() if (entity.name === 'item' || entity.name === 'tnt' || entity.name === 'falling_block') { @@ -722,7 +723,7 @@ export class Entities { if (entity.name === 'item') { mesh.onBeforeRender = () => { const delta = clock.getDelta() - mesh.rotation.y += delta + mesh!.rotation.y += delta } } @@ -846,7 +847,7 @@ export class Entities { //@ts-expect-error // set visibility const isInvisible = entity.metadata?.[0] & 0x20 - for (const child of mesh.children ?? []) { + for (const child of mesh!.children ?? []) { if (child.name !== 'nametag') { child.visible = !isInvisible } @@ -885,8 +886,8 @@ export class Entities { const hasArms = (parseInt(armorStandMeta.client_flags, 10) & 0x04) !== 0 const hasBasePlate = (parseInt(armorStandMeta.client_flags, 10) & 0x08) === 0 const isMarker = (parseInt(armorStandMeta.client_flags, 10) & 0x10) !== 0 - mesh.castShadow = !isMarker - mesh.receiveShadow = !isMarker + mesh!.castShadow = !isMarker + mesh!.receiveShadow = !isMarker if (isSmall) { e.scale.set(0.5, 0.5, 0.5) } else { @@ -955,7 +956,9 @@ export class Entities { // TODO: fix type // todo! fix errors in mc-data (no entities data prior 1.18.2) const item = (itemFrameMeta?.item ?? entity.metadata?.[8]) as any as { itemId, blockId, components, nbtData: { value: { map: { value: number } } } } - mesh.scale.set(1, 1, 1) + mesh!.scale.set(1, 1, 1) + mesh!.position.set(0, 0, -0.5) + e.rotation.x = -entity.pitch e.children.find(c => { if (c.name.startsWith('map_')) { @@ -972,25 +975,33 @@ export class Entities { } return false })?.removeFromParent() + if (item && (item.itemId ?? item.blockId ?? 0) !== 0) { + // Get rotation from metadata, default to 0 if not present + // Rotation is stored in 45° increments (0-7) for items, 90° increments (0-3) for maps const rotation = (itemFrameMeta.rotation as any as number) ?? 0 const mapNumber = item.nbtData?.value?.map?.value ?? item.components?.find(x => x.type === 'map_id')?.data if (mapNumber) { // TODO: Use proper larger item frame model when a map exists - mesh.scale.set(16 / 12, 16 / 12, 1) + mesh!.scale.set(16 / 12, 16 / 12, 1) + // Handle map rotation (4 possibilities, 90° increments) this.addMapModel(e, mapNumber, rotation) } else { + // Handle regular item rotation (8 possibilities, 45° increments) const itemMesh = this.getItemMesh(item, { 'minecraft:display_context': 'fixed', }) if (itemMesh) { - itemMesh.mesh.position.set(0, 0, 0.43) + itemMesh.mesh.position.set(0, 0, -0.05) + // itemMesh.mesh.position.set(0, 0, 0.43) if (itemMesh.isBlock) { itemMesh.mesh.scale.set(0.25, 0.25, 0.25) } else { itemMesh.mesh.scale.set(0.5, 0.5, 0.5) } + // Rotate 180° around Y axis first itemMesh.mesh.rotateY(Math.PI) + // Then apply the 45° increment rotation itemMesh.mesh.rotateZ(-rotation * Math.PI / 4) itemMesh.mesh.name = 'item' e.add(itemMesh.mesh) @@ -1105,6 +1116,7 @@ export class Entities { } else { mapMesh.position.set(0, 0, 0.437) } + // Apply 90° increment rotation for maps (0-3) mapMesh.rotateZ(Math.PI * 2 - rotation * Math.PI / 2) mapMesh.name = `map_${mapNumber}` diff --git a/renderer/viewer/three/worldrendererThree.ts b/renderer/viewer/three/worldrendererThree.ts index a8e08068..95698478 100644 --- a/renderer/viewer/three/worldrendererThree.ts +++ b/renderer/viewer/three/worldrendererThree.ts @@ -710,6 +710,18 @@ export class WorldRendererThree extends WorldRendererCommon { super.destroy() } + shouldObjectVisible (object: THREE.Object3D) { + // Get chunk coordinates + const chunkX = Math.floor(object.position.x / 16) * 16 + const chunkZ = Math.floor(object.position.z / 16) * 16 + const sectionY = Math.floor(object.position.y / 16) * 16 + + const chunkKey = `${chunkX},${chunkZ}` + const sectionKey = `${chunkX},${sectionY},${chunkZ}` + + return !!this.finishedChunks[chunkKey] || !!this.sectionObjects[sectionKey] + } + updateSectionOffsets () { const currentTime = performance.now() for (const [key, anim] of Object.entries(this.sectionsOffsetsAnimations)) { diff --git a/src/customChannels.ts b/src/customChannels.ts index 57c057d5..784d9921 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -69,7 +69,7 @@ const registerBlockModelsChannel = () => { const chunkKey = `${chunkX},${chunkZ}` const blockPosKey = `${x},${y},${z}` - getThreeJsRendererMethods()?.updateCustomBlock(chunkKey, blockPosKey, model) + getThreeJsRendererMethods()?.updateCustomBlock(chunkKey, blockPosKey, { x: Number(x), y: Number(y), z: Number(z) }, model) }, true) } From 02a1be8eeaf79fee88d478622ec96b832a6ea7e0 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 13 Jun 2025 13:26:00 +0300 Subject: [PATCH 2/2] revert custom channel change --- renderer/viewer/lib/worldrendererCommon.ts | 9 +++++---- src/customChannels.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index ae36f204..d0ad8133 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -266,14 +266,15 @@ export abstract class WorldRendererCommon return this.highestBlocksByChunks.get(chunkKey) } - updateCustomBlock (chunkKey: string, blockKey: number | string, blockPos: { x: number, y: number, z: number }, model: string) { + updateCustomBlock (chunkKey: string, blockPos: string, model: string) { this.protocolCustomBlocks.set(chunkKey, { ...this.protocolCustomBlocks.get(chunkKey), - [blockKey]: model + [blockPos]: model }) - this.logWorkerWork(() => `-> updateCustomBlock ${chunkKey} ${blockKey} ${model} ${this.wasChunkSentToWorker(chunkKey)}`) + this.logWorkerWork(() => `-> updateCustomBlock ${chunkKey} ${blockPos} ${model} ${this.wasChunkSentToWorker(chunkKey)}`) if (this.wasChunkSentToWorker(chunkKey)) { - this.setBlockStateId(new Vec3(blockPos.x, blockPos.y, blockPos.z), undefined) + const [x, y, z] = blockPos.split(',').map(Number) + this.setBlockStateId(new Vec3(x, y, z), undefined) } } diff --git a/src/customChannels.ts b/src/customChannels.ts index 784d9921..57c057d5 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -69,7 +69,7 @@ const registerBlockModelsChannel = () => { const chunkKey = `${chunkX},${chunkZ}` const blockPosKey = `${x},${y},${z}` - getThreeJsRendererMethods()?.updateCustomBlock(chunkKey, blockPosKey, { x: Number(x), y: Number(y), z: Number(z) }, model) + getThreeJsRendererMethods()?.updateCustomBlock(chunkKey, blockPosKey, model) }, true) }