feat: display items in hand of entities (#293)

This commit is contained in:
Max Lee 2025-03-07 13:18:54 +01:00 committed by GitHub
commit a7e6f9772c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1147 additions and 361 deletions

View file

@ -505,11 +505,13 @@ export class Entities extends EventEmitter {
if (textureUv && 'resolvedModel' in textureUv) {
const mesh = getBlockMeshFromModel(this.viewer.world.material, textureUv.resolvedModel, textureUv.modelName)
let SCALE = 1
if (specificProps['minecraft:display_context'] === 'ground') {
const SCALE = 0.5
mesh.scale.set(SCALE, SCALE, SCALE)
mesh.position.set(0, 0.2, 0)
SCALE = 0.5
} else if (specificProps['minecraft:display_context'] === 'thirdperson') {
SCALE = 6
}
mesh.scale.set(SCALE, SCALE, SCALE)
const outerGroup = new THREE.Group()
outerGroup.add(mesh)
return {
@ -554,6 +556,13 @@ export class Entities extends EventEmitter {
new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }),
material, materialFlipped,
])
let SCALE = 1
if (specificProps['minecraft:display_context'] === 'ground') {
SCALE = 0.5
} else if (specificProps['minecraft:display_context'] === 'thirdperson') {
SCALE = 6
}
mesh.scale.set(SCALE, SCALE, SCALE)
return {
mesh,
isBlock: false,
@ -589,6 +598,9 @@ export class Entities extends EventEmitter {
if (entity.delete) {
if (!e) return
if (e.additionalCleanup) e.additionalCleanup()
e.traverse(c => {
if (c['additionalCleanup']) c['additionalCleanup']()
})
this.emit('remove', entity)
this.viewer.scene.remove(e)
disposeObject(e)
@ -702,6 +714,8 @@ export class Entities extends EventEmitter {
// check if entity has armor
if (entity.equipment) {
this.addItemModel(e, 'left', entity.equipment[0])
this.addItemModel(e, 'right', entity.equipment[1])
addArmorModel(e, 'feet', entity.equipment[2])
addArmorModel(e, 'legs', entity.equipment[3], 2)
addArmorModel(e, 'chest', entity.equipment[4])
@ -775,7 +789,7 @@ export class Entities extends EventEmitter {
c.setRotationFromEuler(poseToEuler(armorStandMeta.body_pose))
}
break
case 'bone_leftarm':
case 'bone_rightarm':
if (c.parent?.name !== 'bone_armor') {
this.setVisible(c, hasArms)
}
@ -785,7 +799,7 @@ export class Entities extends EventEmitter {
c.setRotationFromEuler(poseToEuler({ 'yaw': -10, 'pitch': -10, 'roll': 0 }))
}
break
case 'bone_rightarm':
case 'bone_leftarm':
if (c.parent?.name !== 'bone_armor') {
this.setVisible(c, hasArms)
}
@ -795,14 +809,14 @@ export class Entities extends EventEmitter {
c.setRotationFromEuler(poseToEuler({ 'yaw': 10, 'pitch': -10, 'roll': 0 }))
}
break
case 'bone_leftleg':
case 'bone_rightleg':
if (armorStandMeta.left_leg_pose) {
c.setRotationFromEuler(poseToEuler(armorStandMeta.left_leg_pose))
} else {
c.setRotationFromEuler(poseToEuler({ 'yaw': -1, 'pitch': -1, 'roll': 0 }))
}
break
case 'bone_rightleg':
case 'bone_leftleg':
if (armorStandMeta.right_leg_pose) {
c.setRotationFromEuler(poseToEuler(armorStandMeta.right_leg_pose))
} else {
@ -852,9 +866,13 @@ export class Entities extends EventEmitter {
})
if (itemMesh) {
itemMesh.mesh.position.set(0, 0, 0.43)
itemMesh.mesh.scale.set(0.5, 0.5, 0.5)
if (itemMesh.isBlock) {
itemMesh.mesh.scale.set(0.25, 0.25, 0.25)
} else {
itemMesh.mesh.scale.set(0.5, 0.5, 0.5)
}
itemMesh.mesh.rotateY(Math.PI)
itemMesh.mesh.rotateZ(rotation * Math.PI / 4)
itemMesh.mesh.rotateZ(-rotation * Math.PI / 4)
itemMesh.mesh.name = 'item'
e.add(itemMesh.mesh)
}
@ -951,6 +969,45 @@ export class Entities extends EventEmitter {
return texture
}
addItemModel (entityMesh: SceneEntity, hand: 'left' | 'right', item: Item) {
const parentName = `bone_${hand}item`
// remove existing item
entityMesh.traverse(c => {
if (c.parent?.name.toLowerCase() === parentName) {
c.removeFromParent()
if (c['additionalCleanup']) c['additionalCleanup']()
}
})
if (!item) return
const itemObject = this.getItemMesh(item, {
'minecraft:display_context': 'thirdperson',
})
if (itemObject) {
entityMesh.traverse(c => {
if (c.name.toLowerCase() === parentName) {
const group = new THREE.Object3D()
group['additionalCleanup'] = () => {
// important: avoid texture memory leak and gpu slowdown
itemObject.itemsTexture?.dispose()
itemObject.itemsTextureFlipped?.dispose()
}
const itemMesh = itemObject.mesh
group.rotation.z = -Math.PI / 16
if (itemObject.isBlock) {
group.rotation.y = Math.PI / 4
} else {
itemMesh.rotation.z = -Math.PI / 4
group.rotation.y = Math.PI / 2
group.scale.multiplyScalar(2)
}
group.add(itemMesh)
c.add(group)
}
})
}
}
handleDamageEvent (entityId, damageAmount) {
const entityMesh = this.entities[entityId]?.children.find(c => c.name === 'mesh')
if (entityMesh) {

View file

@ -84,7 +84,7 @@
]
},
{
"name": "rightarm",
"name": "leftarm",
"parent": "armor",
"pivot": [5, 10, 0],
"cubes": [
@ -97,7 +97,7 @@
]
},
{
"name": "leftarm",
"name": "rightarm",
"parent": "armor",
"pivot": [-5, 10, 0],
"cubes": [
@ -133,7 +133,7 @@
]
},
{
"name": "rightleg",
"name": "leftleg",
"parent": "armor",
"pivot": [1.9, 1, 0],
"cubes": [
@ -146,7 +146,7 @@
]
},
{
"name": "leftleg",
"name": "rightleg",
"parent": "armor",
"pivot": [-1.9, 1, 0],
"cubes": [
@ -169,7 +169,7 @@
"bones": [
{"name": "armor", "pivot": [0, 12, 0]},
{
"name": "rightleg",
"name": "leftleg",
"parent": "armor",
"pivot": [1.9, 1, 0],
"cubes": [
@ -182,7 +182,7 @@
]
},
{
"name": "leftleg",
"name": "rightleg",
"parent": "armor",
"pivot": [-1.9, 1, 0],
"cubes": [

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,4 @@ export { default as warden } from './models/warden.obj'
export { default as witch } from './models/witch.obj'
export { default as wolf } from './models/wolf.obj'
export { default as zombie_villager } from './models/zombie_villager.obj'
export { default as zombie } from './models/zombie.obj'
export { default as husk } from './models/zombie.obj'
export { default as boat } from './models/boat.obj'

View file

@ -1,325 +0,0 @@
# Made in Blockbench 4.10.4
mtllib materials.mtl
o /head
v 0.125 0.5 0.125
v 0.125 0.5 -0.125
v 0.125 0.25 0.125
v 0.125 0.25 -0.125
v -0.125 0.5 -0.125
v -0.125 0.5 0.125
v -0.125 0.25 -0.125
v -0.125 0.25 0.125
vt 0.125 0.875
vt 0.25 0.875
vt 0.25 0.75
vt 0.125 0.75
vt 0 0.875
vt 0.125 0.875
vt 0.125 0.75
vt 0 0.75
vt 0.375 0.875
vt 0.5 0.875
vt 0.5 0.75
vt 0.375 0.75
vt 0.25 0.875
vt 0.375 0.875
vt 0.375 0.75
vt 0.25 0.75
vt 0.25 0.875
vt 0.125 0.875
vt 0.125 1
vt 0.25 1
vt 0.25 1
vt 0.375 1
vt 0.375 0.875
vt 0.25 0.875
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 4/4/1 7/3/1 5/2/1 2/1/1
f 3/8/2 4/7/2 2/6/2 1/5/2
f 8/12/3 3/11/3 1/10/3 6/9/3
f 7/16/4 8/15/4 6/14/4 5/13/4
f 6/20/5 1/19/5 2/18/5 5/17/5
f 7/24/6 4/23/6 3/22/6 8/21/6
o /right_arm
v -0.125 0.25 0.0625
v -0.125 0.25 -0.3125
v -0.125 0.125 0.0625
v -0.125 0.125 -0.3125
v -0.25 0.25 -0.3125
v -0.25 0.25 0.0625
v -0.25 0.125 -0.3125
v -0.25 0.125 0.0625
vt 0.8125 0.6875
vt 0.75 0.6875
vt 0.75 0.75
vt 0.8125 0.75
vt 0.6875 0.6875
vt 0.6875 0.5
vt 0.625 0.5
vt 0.625 0.6875
vt 0.75 0.6875
vt 0.6875 0.6875
vt 0.6875 0.75
vt 0.75 0.75
vt 0.75 0.5
vt 0.75 0.6875
vt 0.8125 0.6875
vt 0.8125 0.5
vt 0.75 0.5
vt 0.6875 0.5
vt 0.6875 0.6875
vt 0.75 0.6875
vt 0.8125 0.6875
vt 0.875 0.6875
vt 0.875 0.5
vt 0.8125 0.5
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 12/28/7 15/27/7 13/26/7 10/25/7
f 11/32/8 12/31/8 10/30/8 9/29/8
f 16/36/9 11/35/9 9/34/9 14/33/9
f 15/40/10 16/39/10 14/38/10 13/37/10
f 14/44/11 9/43/11 10/42/11 13/41/11
f 15/48/12 12/47/12 11/46/12 16/45/12
o /left_leg
v 0.12187499999999996 -0.125 0.0625
v 0.12187499999999996 -0.125 -0.0625
v 0.12187499999999996 -0.5 0.0625
v 0.12187499999999996 -0.5 -0.0625
v -0.0031250000000000444 -0.125 -0.0625
v -0.0031250000000000444 -0.125 0.0625
v -0.0031250000000000444 -0.5 -0.0625
v -0.0031250000000000444 -0.5 0.0625
vt 0.0625 0.6875
vt 0.125 0.6875
vt 0.125 0.5
vt 0.0625 0.5
vt 0 0.6875
vt 0.0625 0.6875
vt 0.0625 0.5
vt 0 0.5
vt 0.1875 0.6875
vt 0.25 0.6875
vt 0.25 0.5
vt 0.1875 0.5
vt 0.125 0.6875
vt 0.1875 0.6875
vt 0.1875 0.5
vt 0.125 0.5
vt 0.125 0.6875
vt 0.0625 0.6875
vt 0.0625 0.75
vt 0.125 0.75
vt 0.125 0.75
vt 0.1875 0.75
vt 0.1875 0.6875
vt 0.125 0.6875
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 20/52/13 23/51/13 21/50/13 18/49/13
f 19/56/14 20/55/14 18/54/14 17/53/14
f 24/60/15 19/59/15 17/58/15 22/57/15
f 23/64/16 24/63/16 22/62/16 21/61/16
f 22/68/17 17/67/17 18/66/17 21/65/17
f 23/72/18 20/71/18 19/70/18 24/69/18
o /left_arm
v 0.25 0.25 0.0625
v 0.25 0.25 -0.3125
v 0.25 0.125 0.0625
v 0.25 0.125 -0.3125
v 0.125 0.25 -0.3125
v 0.125 0.25 0.0625
v 0.125 0.125 -0.3125
v 0.125 0.125 0.0625
vt 0.8125 0.6875
vt 0.75 0.6875
vt 0.75 0.75
vt 0.8125 0.75
vt 0.6875 0.6875
vt 0.6875 0.5
vt 0.625 0.5
vt 0.625 0.6875
vt 0.75 0.6875
vt 0.6875 0.6875
vt 0.6875 0.75
vt 0.75 0.75
vt 0.75 0.5
vt 0.75 0.6875
vt 0.8125 0.6875
vt 0.8125 0.5
vt 0.75 0.5
vt 0.6875 0.5
vt 0.6875 0.6875
vt 0.75 0.6875
vt 0.8125 0.6875
vt 0.875 0.6875
vt 0.875 0.5
vt 0.8125 0.5
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 28/76/19 31/75/19 29/74/19 26/73/19
f 27/80/20 28/79/20 26/78/20 25/77/20
f 32/84/21 27/83/21 25/82/21 30/81/21
f 31/88/22 32/87/22 30/86/22 29/85/22
f 30/92/23 25/91/23 26/90/23 29/89/23
f 31/96/24 28/95/24 27/94/24 32/93/24
o /right_leg
v 0.0031250000000000444 -0.125 0.0625
v 0.0031250000000000444 -0.125 -0.0625
v 0.0031250000000000444 -0.5 0.0625
v 0.0031250000000000444 -0.5 -0.0625
v -0.12187499999999996 -0.125 -0.0625
v -0.12187499999999996 -0.125 0.0625
v -0.12187499999999996 -0.5 -0.0625
v -0.12187499999999996 -0.5 0.0625
vt 0.0625 0.6875
vt 0.125 0.6875
vt 0.125 0.5
vt 0.0625 0.5
vt 0 0.6875
vt 0.0625 0.6875
vt 0.0625 0.5
vt 0 0.5
vt 0.1875 0.6875
vt 0.25 0.6875
vt 0.25 0.5
vt 0.1875 0.5
vt 0.125 0.6875
vt 0.1875 0.6875
vt 0.1875 0.5
vt 0.125 0.5
vt 0.125 0.6875
vt 0.0625 0.6875
vt 0.0625 0.75
vt 0.125 0.75
vt 0.125 0.75
vt 0.1875 0.75
vt 0.1875 0.6875
vt 0.125 0.6875
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 36/100/25 39/99/25 37/98/25 34/97/25
f 35/104/26 36/103/26 34/102/26 33/101/26
f 40/108/27 35/107/27 33/106/27 38/105/27
f 39/112/28 40/111/28 38/110/28 37/109/28
f 38/116/29 33/115/29 34/114/29 37/113/29
f 39/120/30 36/119/30 35/118/30 40/117/30
o /hat
v 0.140625 0.515625 0.140625
v 0.140625 0.515625 -0.140625
v 0.140625 0.234375 0.140625
v 0.140625 0.234375 -0.140625
v -0.140625 0.515625 -0.140625
v -0.140625 0.515625 0.140625
v -0.140625 0.234375 -0.140625
v -0.140625 0.234375 0.140625
vt 0.625 0.875
vt 0.75 0.875
vt 0.75 0.75
vt 0.625 0.75
vt 0.5 0.875
vt 0.625 0.875
vt 0.625 0.75
vt 0.5 0.75
vt 0.875 0.875
vt 1 0.875
vt 1 0.75
vt 0.875 0.75
vt 0.75 0.875
vt 0.875 0.875
vt 0.875 0.75
vt 0.75 0.75
vt 0.75 0.875
vt 0.625 0.875
vt 0.625 1
vt 0.75 1
vt 0.75 1
vt 0.875 1
vt 0.875 0.875
vt 0.75 0.875
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 44/124/31 47/123/31 45/122/31 42/121/31
f 43/128/32 44/127/32 42/126/32 41/125/32
f 48/132/33 43/131/33 41/130/33 46/129/33
f 47/136/34 48/135/34 46/134/34 45/133/34
f 46/140/35 41/139/35 42/138/35 45/137/35
f 47/144/36 44/143/36 43/142/36 48/141/36
o /body
v 0.125 0.25 0.0625
v 0.125 0.25 -0.0625
v 0.125 -0.125 0.0625
v 0.125 -0.125 -0.0625
v -0.125 0.25 -0.0625
v -0.125 0.25 0.0625
v -0.125 -0.125 -0.0625
v -0.125 -0.125 0.0625
vt 0.3125 0.6875
vt 0.4375 0.6875
vt 0.4375 0.5
vt 0.3125 0.5
vt 0.25 0.6875
vt 0.3125 0.6875
vt 0.3125 0.5
vt 0.25 0.5
vt 0.5 0.6875
vt 0.625 0.6875
vt 0.625 0.5
vt 0.5 0.5
vt 0.4375 0.6875
vt 0.5 0.6875
vt 0.5 0.5
vt 0.4375 0.5
vt 0.4375 0.6875
vt 0.3125 0.6875
vt 0.3125 0.75
vt 0.4375 0.75
vt 0.4375 0.75
vt 0.5625 0.75
vt 0.5625 0.6875
vt 0.4375 0.6875
vn 0 0 -1
vn 1 0 0
vn 0 0 1
vn -1 0 0
vn 0 1 0
vn 0 -1 0
usemtl none
f 52/148/37 55/147/37 53/146/37 50/145/37
f 51/152/38 52/151/38 50/150/38 49/149/38
f 56/156/39 51/155/39 49/154/39 54/153/39
f 55/160/40 56/159/40 54/158/40 53/157/40
f 54/164/41 49/163/41 50/162/41 53/161/41
f 55/168/42 52/167/42 51/166/42 56/165/42

View file

@ -176,7 +176,7 @@ Object.defineProperty(window, 'world', {
})
// todo unify
viewer.entities.getItemUv = (item, specificProps) => {
const idOrName = item.itemId ?? item.blockId
const idOrName = item.itemId ?? item.blockId ?? item.name
try {
const name = typeof idOrName === 'number' ? loadedData.items[idOrName]?.name : idOrName
if (!name) throw new Error(`Item not found: ${idOrName}`)