Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Vitaly Turovsky
02a1be8eea revert custom channel change 2025-06-13 13:26:00 +03:00
Vitaly Turovsky
b3807fff65 fix: fix rotation of item frames & items within it 2025-06-13 08:05:17 +03:00
3 changed files with 51 additions and 17 deletions

View file

@ -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<WorkerSend = any, WorkerReceive = any>
await Promise.all([
this.resetWorkers(),
(async () => {
if (this.resourcesManager.currentResources) {
if (this.resourcesManager.currentResources?.itemsRenderer) {
await this.updateAssetsData()
}
})()
@ -812,7 +813,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
})
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) => {

View file

@ -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}`

View file

@ -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)) {