pages235/renderer/viewer/three/hand.ts
2025-07-11 17:56:37 +03:00

89 lines
3 KiB
TypeScript

import * as THREE from 'three'
import { loadSkinFromUsername, loadSkinImage } from '../lib/utils/skins'
import { steveTexture } from './entities'
export const getMyHand = async (image?: string, userName?: string) => {
let newMap: THREE.Texture
if (!image && !userName) {
newMap = await steveTexture
} else {
if (!image) {
image = await loadSkinFromUsername(userName!, 'skin')
}
if (!image) {
return
}
const { canvas } = await loadSkinImage(image)
newMap = new THREE.CanvasTexture(canvas)
}
newMap.magFilter = THREE.NearestFilter
newMap.minFilter = THREE.NearestFilter
// right arm
const box = new THREE.BoxGeometry()
const material = new THREE.MeshStandardMaterial()
const slim = false
const mesh = new THREE.Mesh(box, material)
mesh.scale.x = slim ? 3 : 4
mesh.scale.y = 12
mesh.scale.z = 4
setSkinUVs(box, 40, 16, slim ? 3 : 4, 12, 4)
material.map = newMap
material.needsUpdate = true
const group = new THREE.Group()
group.add(mesh)
group.scale.set(0.1, 0.1, 0.1)
mesh.rotation.z = Math.PI
return group
}
function setUVs (
box: THREE.BoxGeometry,
u: number,
v: number,
width: number,
height: number,
depth: number,
textureWidth: number,
textureHeight: number
): void {
const toFaceVertices = (x1: number, y1: number, x2: number, y2: number) => [
new THREE.Vector2(x1 / textureWidth, 1 - y2 / textureHeight),
new THREE.Vector2(x2 / textureWidth, 1 - y2 / textureHeight),
new THREE.Vector2(x2 / textureWidth, 1 - y1 / textureHeight),
new THREE.Vector2(x1 / textureWidth, 1 - y1 / textureHeight),
]
const top = toFaceVertices(u + depth, v, u + width + depth, v + depth)
const bottom = toFaceVertices(u + width + depth, v, u + width * 2 + depth, v + depth)
const left = toFaceVertices(u, v + depth, u + depth, v + depth + height)
const front = toFaceVertices(u + depth, v + depth, u + width + depth, v + depth + height)
const right = toFaceVertices(u + width + depth, v + depth, u + width + depth * 2, v + height + depth)
const back = toFaceVertices(u + width + depth * 2, v + depth, u + width * 2 + depth * 2, v + height + depth)
const uvAttr = box.attributes.uv as THREE.BufferAttribute
const uvRight = [right[3], right[2], right[0], right[1]]
const uvLeft = [left[3], left[2], left[0], left[1]]
const uvTop = [top[3], top[2], top[0], top[1]]
const uvBottom = [bottom[0], bottom[1], bottom[3], bottom[2]]
const uvFront = [front[3], front[2], front[0], front[1]]
const uvBack = [back[3], back[2], back[0], back[1]]
// Create a new array to hold the modified UV data
const newUVData = [] as number[]
// Iterate over the arrays and copy the data to uvData
for (const uvArray of [uvRight, uvLeft, uvTop, uvBottom, uvFront, uvBack]) {
for (const uv of uvArray) {
newUVData.push(uv.x, uv.y)
}
}
uvAttr.set(new Float32Array(newUVData))
uvAttr.needsUpdate = true
}
function setSkinUVs (box: THREE.BoxGeometry, u: number, v: number, width: number, height: number, depth: number): void {
setUVs(box, u, v, width, height, depth, 64, 64)
}