diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd8cd0710..f1417af7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7533,16 +7533,16 @@ packages: version: 1.36.0 engines: {node: '>=14'} - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/aba0d8e1efa49f4c75646cdb9635cfab41ad4ad9: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/aba0d8e1efa49f4c75646cdb9635cfab41ad4ad9} + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cc4d8a232e33e946fa0929dceabe92df4d405734: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cc4d8a232e33e946fa0929dceabe92df4d405734} version: 1.38.0 engines: {node: '>=14'} prismarine-entity@2.3.1: resolution: {integrity: sha512-HOv8l7IetHNf4hwZ7V/W4vM3GNl+e6VCtKDkH9h02TRq7jWngsggKtJV+VanCce/sNwtJUhJDjORGs728ep4MA==} - prismarine-item@1.15.0: - resolution: {integrity: sha512-DysyiCzaI8S7PpRLFylAZnQo2CppXiBbaUp+8rhK+EzvzmMdS+D1/oETQm9ysB5Jw9eCer6iWGMgzZXxJE5+/w==} + prismarine-item@1.16.0: + resolution: {integrity: sha512-88Tz+/6HquYIsDuseae5G3IbqLeMews2L+ba2gX+p6K6soU9nuFhCfbwN56QuB7d/jZFcWrCYAPE5+UhwWh67w==} prismarine-nbt@2.2.1: resolution: {integrity: sha512-Mb50c58CPnuZ+qvM31DBa08tf9UumlTq1LkvpMoUpKfCuN05GZHTqCUwER3lxTSHLL0GZKghIPbYR/JQkINijQ==} @@ -9799,7 +9799,7 @@ snapshots: '@babel/traverse': 7.22.11 '@babel/types': 7.23.0 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -9867,7 +9867,7 @@ snapshots: '@babel/core': 7.22.11 '@babel/helper-compilation-targets': 7.22.10 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.4 transitivePeerDependencies: @@ -10594,7 +10594,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.22.13 '@babel/types': 7.23.0 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -11487,7 +11487,7 @@ snapshots: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.0 tar: 6.2.0 transitivePeerDependencies: - encoding @@ -12426,7 +12426,7 @@ snapshots: prompts: 2.4.2 puppeteer-core: 2.1.1 read-pkg-up: 7.0.1 - semver: 7.5.4 + semver: 7.6.0 simple-update-notifier: 2.0.0 strip-json-comments: 3.1.1 tempy: 1.0.1 @@ -12552,7 +12552,7 @@ snapshots: pretty-hrtime: 1.0.3 prompts: 2.4.2 read-pkg-up: 7.0.1 - semver: 7.5.4 + semver: 7.6.0 telejson: 7.2.0 tiny-invariant: 1.3.1 ts-dedent: 2.2.0 @@ -13117,7 +13117,7 @@ snapshots: ignore: 5.2.4 natural-compare: 1.4.0 natural-compare-lite: 1.4.0 - semver: 7.5.4 + semver: 7.6.0 ts-api-utils: 1.0.3(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -13156,7 +13156,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.1.0(typescript@5.5.4) '@typescript-eslint/utils': 6.1.0(eslint@8.50.0)(typescript@5.5.4) - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) eslint: 8.50.0 ts-api-utils: 1.0.3(typescript@5.5.4) optionalDependencies: @@ -13174,7 +13174,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.1.0 '@typescript-eslint/visitor-keys': 6.1.0 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 @@ -13188,7 +13188,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 @@ -13202,7 +13202,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -13453,7 +13453,7 @@ snapshots: node-rsa: 1.1.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.83.1) prismarine-entity: 2.3.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3a7d031069373cc3e0cd05e54512dd9461ca34b(minecraft-data@3.83.1) prismarine-windows: 2.9.0 @@ -13539,14 +13539,14 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true agent-base@7.1.0: dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -14795,9 +14795,11 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - debug@4.3.7: + debug@4.3.7(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decamelize-keys@1.1.1: dependencies: @@ -14929,7 +14931,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -14940,7 +14942,7 @@ snapshots: diamond-square@https://codeload.github.com/zardoy/diamond-square/tar.gz/cfaad2d1d5909fdfa63c8cc7bc05fb5e87782d71: dependencies: minecraft-data: 3.83.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/aba0d8e1efa49f4c75646cdb9635cfab41ad4ad9(minecraft-data@3.83.1) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cc4d8a232e33e946fa0929dceabe92df4d405734(minecraft-data@3.83.1) prismarine-registry: 1.10.0 random-seed: 0.3.0 vec3: 0.1.8 @@ -15354,7 +15356,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.18.20): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -15607,7 +15609,7 @@ snapshots: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.5.4 + semver: 7.6.0 strip-indent: 3.0.0 eslint-scope@5.1.1: @@ -15869,7 +15871,7 @@ snapshots: extract-zip@2.0.1(supports-color@8.1.1): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -16538,7 +16540,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true @@ -16582,14 +16584,14 @@ snapshots: https-proxy-agent@4.0.0: dependencies: agent-base: 5.1.1 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true @@ -16597,7 +16599,7 @@ snapshots: https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17671,7 +17673,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -17815,7 +17817,7 @@ snapshots: minecraft-data: 3.83.1 prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-entity: 2.3.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-nbt: 2.2.1 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b vec3: 0.1.8 @@ -17829,7 +17831,7 @@ snapshots: prismarine-chat: 1.10.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.83.1) prismarine-entity: 2.3.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b prismarine-recipe: 1.3.1(prismarine-registry@1.10.0) @@ -17852,7 +17854,7 @@ snapshots: prismarine-chat: 1.10.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.83.1) prismarine-entity: 2.3.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b prismarine-recipe: 1.3.1(prismarine-registry@1.10.0) @@ -18661,7 +18663,7 @@ snapshots: minecraft-data: 3.83.1 prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.7.0) prismarine-chat: 1.10.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 prismarine-registry: 1.7.0 @@ -18684,7 +18686,7 @@ snapshots: transitivePeerDependencies: - minecraft-data - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/aba0d8e1efa49f4c75646cdb9635cfab41ad4ad9(minecraft-data@3.83.1): + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cc4d8a232e33e946fa0929dceabe92df4d405734(minecraft-data@3.83.1): dependencies: prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.10.0) prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 @@ -18700,11 +18702,11 @@ snapshots: prismarine-entity@2.3.1: dependencies: prismarine-chat: 1.10.1 - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-registry: 1.10.0 vec3: 0.1.8 - prismarine-item@1.15.0: + prismarine-item@1.16.0: dependencies: prismarine-nbt: 2.5.0 prismarine-registry: 1.10.0 @@ -18766,7 +18768,7 @@ snapshots: prismarine-windows@2.9.0: dependencies: - prismarine-item: 1.15.0 + prismarine-item: 1.16.0 prismarine-registry: 1.10.0 typed-emitter: 2.1.0 @@ -18960,7 +18962,7 @@ snapshots: puppeteer-core@2.1.1: dependencies: '@types/mime-types': 2.1.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -19621,7 +19623,7 @@ snapshots: send@1.1.0: dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) destroy: 1.2.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -19947,7 +19949,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@8.1.1) socks: 2.7.1 transitivePeerDependencies: - supports-color diff --git a/prismarine-viewer/viewer/lib/entities.ts b/prismarine-viewer/viewer/lib/entities.ts index efc80b5de..f9eb34005 100644 --- a/prismarine-viewer/viewer/lib/entities.ts +++ b/prismarine-viewer/viewer/lib/entities.ts @@ -20,6 +20,7 @@ import { getMesh } from './entity/EntityMesh' import { WalkingGeneralSwing } from './entity/animations' import { disposeObject } from './threeJsUtils' import { armorModels } from './entity/objModels' +import { Viewer } from './viewer' const { loadTexture } = globalThis.isElectron ? require('./utils.electron.js') : require('./utils') export const TWEEN_DURATION = 120 @@ -163,12 +164,12 @@ const nametags = {} const isFirstUpperCase = (str) => str.charAt(0) === str.charAt(0).toUpperCase() -function getEntityMesh (entity, scene, options, overrides) { +function getEntityMesh (entity, world, options, overrides) { if (entity.name) { try { // https://github.com/PrismarineJS/prismarine-viewer/pull/410 const entityName = (isFirstUpperCase(entity.name) ? snakeCase(entity.name) : entity.name).toLowerCase() - const e = new Entity.EntityMesh('1.16.4', entityName, scene, overrides) + const e = new Entity.EntityMesh('1.16.4', entityName, world, overrides) if (e.mesh) { addNametag(entity, options, e.mesh) @@ -211,6 +212,8 @@ export class Entities extends EventEmitter { clock = new THREE.Clock() rendering = true itemsTexture: THREE.Texture | null = null + cachedMapsImages = {} as Record + itemFrameMaps = {} as Record>> getItemUv: undefined | ((idOrName: number | string) => { texture: THREE.Texture; u: number; @@ -220,7 +223,7 @@ export class Entities extends EventEmitter { size?: number; }) - constructor (public scene: THREE.Scene) { + constructor (public viewer: Viewer) { super() this.entitiesOptions = {} this.debugMode = 'none' @@ -229,7 +232,7 @@ export class Entities extends EventEmitter { clear () { for (const mesh of Object.values(this.entities)) { - this.scene.remove(mesh) + this.viewer.scene.remove(mesh) disposeObject(mesh) } this.entities = {} @@ -251,9 +254,9 @@ export class Entities extends EventEmitter { this.rendering = rendering for (const ent of entity ? [entity] : Object.values(this.entities)) { if (rendering) { - if (!this.scene.children.includes(ent)) this.scene.add(ent) + if (!this.viewer.scene.children.includes(ent)) this.viewer.scene.add(ent) } else { - this.scene.remove(ent) + this.viewer.scene.remove(ent) } } } @@ -405,6 +408,7 @@ export class Entities extends EventEmitter { } getItemMesh (item) { + // TODO: Render proper model (especially for blocks) instead of flat texture const textureUv = this.getItemUv?.(item.itemId ?? item.blockId) if (textureUv) { // todo use geometry buffer uv instead! @@ -458,9 +462,13 @@ export class Entities extends EventEmitter { update (entity: import('prismarine-entity').Entity & { delete?; pos, name }, overrides) { const isPlayerModel = entity.name === 'player' - if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { + if (entity.name === 'zombie_villager' || entity.name === 'husk') { overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}` } + if (entity.name === 'glow_item_frame') { + if (!overrides.textures) overrides.textures = [] + overrides.textures['background'] = 'block:glow_item_frame' + } // this can be undefined in case where packet entity_destroy was sent twice (so it was already deleted) let e = this.entities[entity.id] @@ -468,7 +476,7 @@ export class Entities extends EventEmitter { if (!e) return if (e.additionalCleanup) e.additionalCleanup() this.emit('remove', entity) - this.scene.remove(e) + this.viewer.scene.remove(e) disposeObject(e) // todo dispose textures as well ? delete this.entities[entity.id] @@ -539,7 +547,7 @@ export class Entities extends EventEmitter { //@ts-expect-error playerObject.animation.isMoving = false } else { - mesh = getEntityMesh(entity, this.scene, this.entitiesOptions, overrides) + mesh = getEntityMesh(entity, this.viewer.world, this.entitiesOptions, overrides) } if (!mesh) return mesh.name = 'mesh' @@ -558,7 +566,7 @@ export class Entities extends EventEmitter { group.add(mesh) group.add(boxHelper) boxHelper.visible = false - this.scene.add(group) + this.viewer.scene.add(group) e = group this.entities[entity.id] = e @@ -682,31 +690,49 @@ export class Entities extends EventEmitter { } // todo handle map, map_chunks events - // if (entity.name === 'item_frame' || entity.name === 'glow_item_frame') { - // const example = { - // "present": true, - // "itemId": 847, - // "itemCount": 1, - // "nbtData": { - // "type": "compound", - // "name": "", - // "value": { - // "map": { - // "type": "int", - // "value": 2146483444 - // }, - // "interactiveboard": { - // "type": "byte", - // "value": 1 - // } - // } - // } - // } - // const item = entity.metadata?.[8] - // if (item.nbtData) { - // const nbt = nbt.simplify(item.nbtData) - // } - // } + let itemFrameMeta = getSpecificEntityMetadata('item_frame', entity) + if (!itemFrameMeta) { + itemFrameMeta = getSpecificEntityMetadata('glow_item_frame', entity) + } + if (itemFrameMeta) { + // TODO: Figure out why this doesn't match the Item mineflayer type + const item = itemFrameMeta?.item as any as { itemId, blockId, nbtData: { value: { map: { value: number } } } } + mesh.scale.set(1, 1, 1) + e.children.find(c => { + if (c.name.startsWith('map_')) { + disposeObject(c) + const existingMapNumber = parseInt(c.name.split('_')[1], 10) + this.itemFrameMaps[existingMapNumber] = this.itemFrameMaps[existingMapNumber]?.filter(mesh => mesh !== c) + if (c instanceof THREE.Mesh) { + c.material?.map?.dispose() + } + return true + } else if (c.name === 'item') { + disposeObject(c) + return true + } + return false + })?.removeFromParent() + if (item && (item.itemId ?? item.blockId ?? 0) !== 0) { + const rotation = (itemFrameMeta.rotation as any as number) + const mapNumber = item.nbtData?.value?.map?.value + if (mapNumber) { + // TODO: Use proper larger item frame model when a map exists + mesh.scale.set(16 / 12, 16 / 12, 1) + this.addMapModel(e, mapNumber, rotation) + } else { + const itemMesh = this.getItemMesh(item) + if (itemMesh) { + itemMesh.mesh.position.set(0, 0, 0.45) + itemMesh.mesh.scale.set(0.5, 0.5, 0.5) + itemMesh.mesh.rotateY(Math.PI) + itemMesh.mesh.rotateZ(rotation * Math.PI / 4) + itemMesh.mesh.name = 'item' + e.add(itemMesh.mesh) + } + } + } + } if (entity.username) { e.username = entity.username @@ -729,6 +755,74 @@ export class Entities extends EventEmitter { } } + updateMap (mapNumber: string | number, data: string) { + this.cachedMapsImages[mapNumber] = data + let itemFrameMeshes = this.itemFrameMaps[mapNumber] + if (!itemFrameMeshes) return + itemFrameMeshes = itemFrameMeshes.filter(mesh => mesh.parent) + this.itemFrameMaps[mapNumber] = itemFrameMeshes + if (itemFrameMeshes) { + for (const mesh of itemFrameMeshes) { + mesh.material.map = this.loadMap(data) + mesh.material.needsUpdate = true + mesh.visible = true + } + } + } + + addMapModel (entityMesh: THREE.Object3D, mapNumber: number, rotation: number) { + const imageData = this.cachedMapsImages?.[mapNumber] + let texture: THREE.Texture | null = null + if (imageData) { + texture = this.loadMap(imageData) + } + const parameters = { + transparent: true, + alphaTest: 0.1, + } + if (texture?.image) { + parameters['map'] = texture + } + const material = new THREE.MeshLambertMaterial(parameters) + + const mapMesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material) + + mapMesh.rotation.set(0, Math.PI, 0) + entityMesh.add(mapMesh) + let isInvisible = false + entityMesh.traverseVisible(c => { + if (c.name === 'geometry_frame') { + isInvisible = false + } + }) + if (isInvisible) { + mapMesh.position.set(0, 0, 0.499) + } else { + mapMesh.position.set(0, 0, 0.437) + } + mapMesh.rotateZ(Math.PI * 2 - rotation * Math.PI / 2) + mapMesh.name = `map_${mapNumber}` + + if (!texture?.image) { + mapMesh.visible = false + } + + if (!this.itemFrameMaps[mapNumber]) { + this.itemFrameMaps[mapNumber] = [] + } + this.itemFrameMaps[mapNumber].push(mapMesh) + } + + loadMap (data: any) { + const texture = new THREE.TextureLoader().load(data) + if (texture) { + texture.magFilter = THREE.NearestFilter + texture.minFilter = THREE.NearestFilter + texture.needsUpdate = true + } + return texture + } + handleDamageEvent (entityId, damageAmount) { const entityMesh = this.entities[entityId]?.children.find(c => c.name === 'mesh') if (entityMesh) { @@ -796,7 +890,7 @@ function addArmorModel (entityMesh: THREE.Object3D, slotType: string, item: Item material.map = texture }) } else { - mesh = getMesh(texturePath, armorModels.armorModel[slotType]) + mesh = getMesh(viewer.world, texturePath, armorModels.armorModel[slotType]) mesh.name = meshName material = mesh.material material.side = THREE.DoubleSide diff --git a/prismarine-viewer/viewer/lib/entity/EntityMesh.js b/prismarine-viewer/viewer/lib/entity/EntityMesh.js index 69dd95d65..a2b40befa 100644 --- a/prismarine-viewer/viewer/lib/entity/EntityMesh.js +++ b/prismarine-viewer/viewer/lib/entity/EntityMesh.js @@ -94,7 +94,7 @@ function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] } -function addCube(attr, boneId, bone, cube, texWidth = 64, texHeight = 64, mirror = false) { +function addCube(attr, boneId, bone, cube, sameTextureForAllFaces = false, texWidth = 64, texHeight = 64, mirror = false) { const cubeRotation = new THREE.Euler(0, 0, 0) if (cube.rotation) { cubeRotation.x = -cube.rotation[0] * Math.PI / 180 @@ -107,8 +107,15 @@ function addCube(attr, boneId, bone, cube, texWidth = 64, texHeight = 64, mirror const eastOrWest = dir[0] !== 0 const faceUvs = [] for (const pos of corners) { - const u = (cube.uv[0] + dot(pos[3] ? u1 : u0, cube.size)) / texWidth - const v = (cube.uv[1] + dot(pos[4] ? v1 : v0, cube.size)) / texHeight + let u + let v + if (sameTextureForAllFaces) { + u = (cube.uv[0] + pos[3] * cube.size[0]) / texWidth + v = (cube.uv[1] + pos[4] * cube.size[1]) / texHeight + } else { + u = (cube.uv[0] + dot(pos[3] ? u1 : u0, cube.size)) / texWidth + v = (cube.uv[1] + dot(pos[4] ? v1 : v0, cube.size)) / texHeight + } const posX = eastOrWest && mirror ? pos[0] ^ 1 : pos[0] const posY = pos[1] @@ -148,7 +155,23 @@ function addCube(attr, boneId, bone, cube, texWidth = 64, texHeight = 64, mirror } } -export function getMesh(texture, jsonModel, overrides = {}) { +export function getMesh(world, texture, jsonModel, overrides = {}) { + let textureWidth = jsonModel.texturewidth ?? 64 + let textureHeight = jsonModel.textureheight ?? 64 + let textureOffset + const useBlockTexture = texture.startsWith('block:') + if (useBlockTexture) { + const blockName = texture.slice(6) + const textureInfo = world.blocksAtlasParser.getTextureInfo(blockName) + if (textureInfo) { + textureWidth = world.material.map.image.width + textureHeight = world.material.map.image.height + textureOffset = [textureInfo.u, textureInfo.v] + } else { + console.error(`Unknown block ${blockName}`) + } + } + const bones = {} const geoData = { @@ -186,7 +209,7 @@ export function getMesh(texture, jsonModel, overrides = {}) { if (jsonBone.cubes) { for (const cube of jsonBone.cubes) { - addCube(geoData, i, bone, cube, jsonModel.texturewidth, jsonModel.textureheight, jsonBone.mirror) + addCube(geoData, i, bone, cube, useBlockTexture, textureWidth, textureHeight, jsonBone.mirror) } } i++ @@ -215,18 +238,25 @@ export function getMesh(texture, jsonModel, overrides = {}) { mesh.bind(skeleton) mesh.scale.set(1 / 16, 1 / 16, 1 / 16) - loadTexture(texture, texture => { - if (material.map) { - // texture is already loaded - return - } - texture.magFilter = THREE.NearestFilter - texture.minFilter = THREE.NearestFilter - texture.flipY = false - texture.wrapS = THREE.RepeatWrapping - texture.wrapT = THREE.RepeatWrapping + if (textureOffset) { + texture = world.material.map.clone() + texture.offset.set(textureOffset[0], textureOffset[1]) + texture.needsUpdate = true material.map = texture - }) + } else { + loadTexture(texture.endsWith('.png') || texture.startsWith('data:image/') ? texture : texture + '.png', texture => { + if (material.map) { + // texture is already loaded + return + } + texture.magFilter = THREE.NearestFilter + texture.minFilter = THREE.NearestFilter + texture.flipY = false + texture.wrapS = THREE.RepeatWrapping + texture.wrapT = THREE.RepeatWrapping + material.map = texture + }) + } return mesh } @@ -252,6 +282,7 @@ export const temporaryMap = { 'hopper_minecart': 'minecart', 'command_block_minecart': 'minecart', 'tnt_minecart': 'minecart', + 'glow_item_frame': 'item_frame', 'glow_squid': 'squid', 'trader_llama': 'llama', 'chest_boat': 'boat', @@ -321,7 +352,7 @@ const offsetEntity = { // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class EntityMesh { - constructor(version, type, scene, /** @type {{textures?, rotation?: Record}} */overrides = {}) { + constructor(version, type, world, /** @type {{textures?, rotation?: Record}} */overrides = {}) { const originalType = type const mappedValue = temporaryMap[type] if (mappedValue) type = mappedValue @@ -388,7 +419,7 @@ export class EntityMesh { const texture = overrides.textures?.[name] ?? e.textures[name] if (!texture) continue // console.log(JSON.stringify(jsonModel, null, 2)) - const mesh = getMesh(texture + '.png', jsonModel, overrides) + const mesh = getMesh(world, texture, jsonModel, overrides) mesh.name = `geometry_${name}` this.mesh.add(mesh) diff --git a/prismarine-viewer/viewer/lib/entity/entities.json b/prismarine-viewer/viewer/lib/entity/entities.json index 9824d4182..4436a44bf 100644 --- a/prismarine-viewer/viewer/lib/entity/entities.json +++ b/prismarine-viewer/viewer/lib/entity/entities.json @@ -7838,6 +7838,53 @@ } } }, + "item_frame": { + "identifier": "minecraft:item_frame", + "materials": {"default": "item_frame"}, + "textures": { + "background": "block:item_frame", + "frame": "block:oak_planks" + }, + "geometry": { + "background": { + "bones": [ + { + "name": "base" + }, + { + "name": "background", + "parent": "base", + "rotation": [0, 180, 0], + "pivot": [0, 0, 0], + "cubes": [ + {"origin": [-5, -5, -8], "size": [10, 10, 0.5], "uv": [3, 3]} + ] + } + ], + "texturewidth": 16, + "textureheight": 16 + }, + "frame": { + "bones": [ + { + "name": "frame", + "parent": "base", + "rotation": [0, 180, 0], + "pivot": [0, 0, 0], + "cubes": [ + {"origin": [-6, -6, -8], "size": [12, 1, 1], "uv": [2, 2]}, + {"origin": [-6, 5, -8], "size": [12, 1, 1], "uv": [2, 13]}, + {"origin": [-6, -5, -8], "size": [1, 10, 1], "uv": [2, 3]}, + {"origin": [5, -5, -8], "size": [1, 10, 1], "uv": [13, 3]} + ] + } + ], + "texturewidth": 16, + "textureheight": 16 + } + }, + "render_controllers": ["controller.render.item_frame"] + }, "leash_knot": { "identifier": "minecraft:leash_knot", "materials": {"default": "leash_knot"}, @@ -7847,7 +7894,8 @@ "bones": [ { "name": "knot", - "cubes": [{"origin": [-3, 2, -3], "size": [6, 8, 6]}] + "rotation": [0, 180, 0], + "cubes": [{"origin": [5, 6, 5], "size": [6, 8, 6], "uv": [0, 0]}] } ], "texturewidth": 32, diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index c90bc811f..929b38000 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -48,7 +48,7 @@ export class Viewer { this.threeJsWorld = new WorldRendererThree(this.scene, this.renderer, worldConfig) this.setWorld() this.resetScene() - this.entities = new Entities(this.scene) + this.entities = new Entities(this) // this.primitives = new Primitives(this.scene, this.camera) this.domElement = renderer.domElement diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index 61d5a503a..e556f7a32 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -75,6 +75,10 @@ export class WorldDataEmitter extends EventEmitter { this.eventListeners = { // 'move': botPosition, entitySpawn (e: any) { + if (e.name === 'item_frame' || e.name === 'glow_item_frame') { + // Item frames use block positions in the protocol, not their center. Fix that. + e.position.translate(0.5, 0.5, 0.5) + } emitEntity(e) }, entityUpdate (e: any) { diff --git a/src/react/HeldMapUi.tsx b/src/react/HeldMapUi.tsx index 12f032982..fcf3d4484 100644 --- a/src/react/HeldMapUi.tsx +++ b/src/react/HeldMapUi.tsx @@ -6,8 +6,6 @@ export default () => { const [dataUrl, setDataUrl] = useState(null) // true means loading useEffect(() => { - bot.loadPlugin(mapDownloader) - setImageConverter((buf: Uint8Array) => { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d')! @@ -34,7 +32,7 @@ export default () => { updateHeldMap() }) - bot.on('new_map', () => { + bot.on('new_map', ({ id }) => { // total maps: Object.keys(bot.mapDownloader.maps).length updateHeldMap() }) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index 1134d7f89..a5bfb61f3 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -5,6 +5,7 @@ import * as THREE from 'three' // wouldn't better to create atlas instead? import { Vec3 } from 'vec3' import { LineMaterial } from 'three-stdlib' +import { mapDownloader } from 'mineflayer-item-map-downloader/' import { Entity } from 'prismarine-entity' import destroyStage0 from '../assets/destroy_stage_0.png' import destroyStage1 from '../assets/destroy_stage_1.png' @@ -158,6 +159,11 @@ class WorldInteraction { upLineMaterial() // todo use gamemode update only bot.on('game', upLineMaterial) + + bot.loadPlugin(mapDownloader) + bot.mapDownloader.on('new_map', ({ png, id }) => { + viewer.entities.updateMap(id, png) + }) } activateEntity (entity: Entity) {