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, foreground: true,
enableDebugOverlay: false, enableDebugOverlay: false,
_experimentalSmoothChunkLoading: true, _experimentalSmoothChunkLoading: true,
_renderByChunks: false _renderByChunks: false,
volume: 1
} }
export type WorldRendererConfig = typeof defaultWorldRendererConfig export type WorldRendererConfig = typeof defaultWorldRendererConfig
@ -239,7 +240,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
await Promise.all([ await Promise.all([
this.resetWorkers(), this.resetWorkers(),
(async () => { (async () => {
if (this.resourcesManager.currentResources) { if (this.resourcesManager.currentResources?.itemsRenderer) {
await this.updateAssetsData() await this.updateAssetsData()
} }
})() })()
@ -812,7 +813,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
}) })
worldEmitter.on('onWorldSwitch', () => { 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) => { worldEmitter.on('time', (timeOfDay) => {

View file

@ -302,13 +302,8 @@ export class Entities {
const dz = entity.position.z - botPos.z const dz = entity.position.z - botPos.z
const distanceSquared = dx * dx + dy * dy + dz * dz 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 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) 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') { playAnimation (entityPlayerId, animation: 'walking' | 'running' | 'oneSwing' | 'idle' | 'crouch' | 'crouchWalking') {
const playerObject = this.getPlayerObject(entityPlayerId) const playerObject = this.getPlayerObject(entityPlayerId)
if (!playerObject) return if (!playerObject) return
@ -693,7 +694,7 @@ export class Entities {
return return
} }
let mesh let mesh: THREE.Object3D | undefined
if (e === undefined) { if (e === undefined) {
const group = new THREE.Group() const group = new THREE.Group()
if (entity.name === 'item' || entity.name === 'tnt' || entity.name === 'falling_block') { if (entity.name === 'item' || entity.name === 'tnt' || entity.name === 'falling_block') {
@ -722,7 +723,7 @@ export class Entities {
if (entity.name === 'item') { if (entity.name === 'item') {
mesh.onBeforeRender = () => { mesh.onBeforeRender = () => {
const delta = clock.getDelta() const delta = clock.getDelta()
mesh.rotation.y += delta mesh!.rotation.y += delta
} }
} }
@ -846,7 +847,7 @@ export class Entities {
//@ts-expect-error //@ts-expect-error
// set visibility // set visibility
const isInvisible = entity.metadata?.[0] & 0x20 const isInvisible = entity.metadata?.[0] & 0x20
for (const child of mesh.children ?? []) { for (const child of mesh!.children ?? []) {
if (child.name !== 'nametag') { if (child.name !== 'nametag') {
child.visible = !isInvisible child.visible = !isInvisible
} }
@ -885,8 +886,8 @@ export class Entities {
const hasArms = (parseInt(armorStandMeta.client_flags, 10) & 0x04) !== 0 const hasArms = (parseInt(armorStandMeta.client_flags, 10) & 0x04) !== 0
const hasBasePlate = (parseInt(armorStandMeta.client_flags, 10) & 0x08) === 0 const hasBasePlate = (parseInt(armorStandMeta.client_flags, 10) & 0x08) === 0
const isMarker = (parseInt(armorStandMeta.client_flags, 10) & 0x10) !== 0 const isMarker = (parseInt(armorStandMeta.client_flags, 10) & 0x10) !== 0
mesh.castShadow = !isMarker mesh!.castShadow = !isMarker
mesh.receiveShadow = !isMarker mesh!.receiveShadow = !isMarker
if (isSmall) { if (isSmall) {
e.scale.set(0.5, 0.5, 0.5) e.scale.set(0.5, 0.5, 0.5)
} else { } else {
@ -955,7 +956,9 @@ export class Entities {
// TODO: fix type // TODO: fix type
// todo! fix errors in mc-data (no entities data prior 1.18.2) // 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 } } } } 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.rotation.x = -entity.pitch
e.children.find(c => { e.children.find(c => {
if (c.name.startsWith('map_')) { if (c.name.startsWith('map_')) {
@ -972,25 +975,33 @@ export class Entities {
} }
return false return false
})?.removeFromParent() })?.removeFromParent()
if (item && (item.itemId ?? item.blockId ?? 0) !== 0) { 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 rotation = (itemFrameMeta.rotation as any as number) ?? 0
const mapNumber = item.nbtData?.value?.map?.value ?? item.components?.find(x => x.type === 'map_id')?.data const mapNumber = item.nbtData?.value?.map?.value ?? item.components?.find(x => x.type === 'map_id')?.data
if (mapNumber) { if (mapNumber) {
// TODO: Use proper larger item frame model when a map exists // 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) this.addMapModel(e, mapNumber, rotation)
} else { } else {
// Handle regular item rotation (8 possibilities, 45° increments)
const itemMesh = this.getItemMesh(item, { const itemMesh = this.getItemMesh(item, {
'minecraft:display_context': 'fixed', 'minecraft:display_context': 'fixed',
}) })
if (itemMesh) { 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) { if (itemMesh.isBlock) {
itemMesh.mesh.scale.set(0.25, 0.25, 0.25) itemMesh.mesh.scale.set(0.25, 0.25, 0.25)
} else { } else {
itemMesh.mesh.scale.set(0.5, 0.5, 0.5) itemMesh.mesh.scale.set(0.5, 0.5, 0.5)
} }
// Rotate 180° around Y axis first
itemMesh.mesh.rotateY(Math.PI) itemMesh.mesh.rotateY(Math.PI)
// Then apply the 45° increment rotation
itemMesh.mesh.rotateZ(-rotation * Math.PI / 4) itemMesh.mesh.rotateZ(-rotation * Math.PI / 4)
itemMesh.mesh.name = 'item' itemMesh.mesh.name = 'item'
e.add(itemMesh.mesh) e.add(itemMesh.mesh)
@ -1105,6 +1116,7 @@ export class Entities {
} else { } else {
mapMesh.position.set(0, 0, 0.437) 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.rotateZ(Math.PI * 2 - rotation * Math.PI / 2)
mapMesh.name = `map_${mapNumber}` mapMesh.name = `map_${mapNumber}`

View file

@ -710,6 +710,18 @@ export class WorldRendererThree extends WorldRendererCommon {
super.destroy() 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 () { updateSectionOffsets () {
const currentTime = performance.now() const currentTime = performance.now()
for (const [key, anim] of Object.entries(this.sectionsOffsetsAnimations)) { for (const [key, anim] of Object.entries(this.sectionsOffsetsAnimations)) {