Skins improvements/fixes (#3943)

* feat: only initialize batch renderer if needed & head storage

* feat: support webp storage of skin renders if supported (falls back to png if not)

* fix: performance improvements with cache loading+saving

* fix: mirrored skins + remove cape model for embedded cape

* feat: antialiasing

* fix: leg jumping & store fbx's for reference

* fix: lint issues

* fix: lint issues

* feat: tweaks to radial spotlight

* fix: app nav btn colors
This commit is contained in:
IMB11
2025-07-09 22:41:36 +01:00
committed by GitHub
parent 3c79607d1f
commit cb72d2ac80
15 changed files with 4882 additions and 1355 deletions

View File

@@ -59,25 +59,23 @@ export function applyTexture(model: THREE.Object3D, texture: THREE.Texture): voi
model.traverse((child) => {
if ((child as THREE.Mesh).isMesh) {
const mesh = child as THREE.Mesh
// Skip cape meshes
if (mesh.name === 'Cape') return
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
materials.forEach((mat: THREE.Material) => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.map = texture
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true
mat.side = THREE.DoubleSide
mat.alphaTest = 0.1
mat.depthWrite = true
if (mat.name !== 'cape') {
mat.map = texture
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true
mat.side = THREE.DoubleSide
mat.alphaTest = 0.1
mat.depthWrite = true
}
}
})
}
@@ -96,41 +94,27 @@ export function applyCapeTexture(
materials.forEach((mat: THREE.Material) => {
if (mat instanceof THREE.MeshStandardMaterial) {
mat.map = texture || transparentTexture || null
mat.transparent = transparentTexture ? true : false
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true
mat.depthWrite = true
mat.side = THREE.DoubleSide
mat.alphaTest = 0.1
if (mat.name === 'cape') {
mat.map = texture || transparentTexture || null
mat.transparent = !texture || transparentTexture ? true : false
mat.metalness = 0
mat.color.set(0xffffff)
mat.toneMapped = false
mat.flatShading = true
mat.roughness = 1
mat.needsUpdate = true
mat.depthTest = true
mat.depthWrite = true
mat.side = THREE.DoubleSide
mat.alphaTest = 0.1
mat.visible = !!texture
}
}
})
}
})
}
export function attachCapeToBody(
bodyNode: THREE.Object3D,
capeModel: THREE.Object3D,
position = { x: 0, y: -1, z: 0.01 },
rotation = { x: 0, y: Math.PI / 2, z: 0 },
): void {
if (!bodyNode || !capeModel) return
if (capeModel.parent) {
capeModel.parent.remove(capeModel)
}
capeModel.position.set(position.x, position.y, position.z)
capeModel.rotation.set(rotation.x, rotation.y, rotation.z)
bodyNode.add(capeModel)
}
export function findBodyNode(model: THREE.Object3D): THREE.Object3D | null {
let bodyNode: THREE.Object3D | null = null
@@ -162,39 +146,25 @@ export function createTransparentTexture(): THREE.Texture {
export async function setupSkinModel(
modelUrl: string,
textureUrl: string,
capeModelUrl?: string,
capeTextureUrl?: string,
config: SkinRendererConfig = {},
): Promise<{
model: THREE.Object3D
bodyNode: THREE.Object3D | null
capeModel: THREE.Object3D | null
}> {
// Load model and texture in parallel
const [gltf, texture] = await Promise.all([loadModel(modelUrl), loadTexture(textureUrl, config)])
const model = gltf.scene.clone()
applyTexture(model, texture)
const bodyNode = findBodyNode(model)
let capeModel: THREE.Object3D | null = null
// Load cape if provided
if (capeModelUrl && capeTextureUrl) {
const [capeGltf, capeTexture] = await Promise.all([
loadModel(capeModelUrl),
loadTexture(capeTextureUrl, config),
])
capeModel = capeGltf.scene.clone()
applyCapeTexture(capeModel, capeTexture)
if (bodyNode && capeModel) {
attachCapeToBody(bodyNode, capeModel)
}
if (capeTextureUrl) {
const capeTexture = await loadTexture(capeTextureUrl, config)
applyCapeTexture(model, capeTexture)
}
return { model, bodyNode, capeModel }
const bodyNode = findBodyNode(model)
return { model, bodyNode }
}
export function disposeCaches(): void {