How can we use custom textures on hand? I have tried with threejs. Only one color is showing instead of texture.
You can use custom textures on the hand by ingesting the UVs and creating a hand mesh from it and then texturing the hand mesh.
We do this automatically in our Hand Mesh primitive in A-Frame but you would have to do this manually in three.js
const handMesh = (modelGeometry, material, wireframe, uvOrientation) => {
let handKind = 2
const geometry = new THREE.BufferGeometry()
// Fill geometry with default vertices.
const vertices = new Float32Array(modelGeometry.pointsPerDetection * 3)
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
// Fill geometry with default normals.
const normals = new Float32Array(modelGeometry.pointsPerDetection * 3)
geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3))
// By default, add right hand UVs to the geometry
const rightUvs = new Float32Array(modelGeometry.rightUvs.length * 2)
for (let i = 0; i < modelGeometry.pointsPerDetection; i++) {
rightUvs[2 * i] = modelGeometry.rightUvs[i].u
rightUvs[2 * i + 1] = modelGeometry.rightUvs[i].v
}
const rightUvBuffer = new THREE.BufferAttribute(rightUvs, 2)
const leftUvs = new Float32Array(modelGeometry.leftUvs.length * 2)
for (let i = 0; i < modelGeometry.pointsPerDetection; i++) {
leftUvs[2 * i] = modelGeometry.leftUvs[i].u
leftUvs[2 * i + 1] = modelGeometry.leftUvs[i].v
}
const leftUvBuffer = new THREE.BufferAttribute(leftUvs, 2)
// Grab left and right hand indices
const rightIndices = new Array(modelGeometry.rightIndices.length * 3)
for (let i = 0; i < modelGeometry.rightIndices.length; ++i) {
rightIndices[i * 3] = modelGeometry.rightIndices[i].a
rightIndices[i * 3 + 1] = modelGeometry.rightIndices[i].b
rightIndices[i * 3 + 2] = modelGeometry.rightIndices[i].c
}
const leftIndices = new Array(modelGeometry.leftIndices.length * 3)
for (let i = 0; i < modelGeometry.leftIndices.length; ++i) {
leftIndices[i * 3] = modelGeometry.leftIndices[i].a
leftIndices[i * 3 + 1] = modelGeometry.leftIndices[i].b
leftIndices[i * 3 + 2] = modelGeometry.leftIndices[i].c
}
// set UV and indices type based on hand mesh orientation
let uv
if (uvOrientation === 'left') {
uv = leftUvBuffer
geometry.setIndex(leftIndices)
} else if (uvOrientation === 'right') {
uv = rightUvBuffer
geometry.setIndex(rightIndices)
}
geometry.setAttribute('uv', uv)
if (wireframe) {
material.wireframe = true
}
const mesh = new THREE.Mesh(geometry, material)
const show = ({detail}) => {
// set indices based on handKind
if (handKind !== detail.handKind) {
handKind = detail.handKind
if (handKind === 1) {
mesh.geometry.setIndex(leftIndices)
} else {
mesh.geometry.setIndex(rightIndices)
}
}
// Update vertex positions.
for (let i = 0; i < detail.vertices.length; ++i) {
vertices[i * 3] = detail.vertices[i].x
vertices[i * 3 + 1] = detail.vertices[i].y
vertices[i * 3 + 2] = detail.vertices[i].z
}
mesh.geometry.attributes.position.needsUpdate = true
// Update vertex normals.
for (let i = 0; i < detail.normals.length; ++i) {
normals[i * 3] = detail.normals[i].x
normals[i * 3 + 1] = detail.normals[i].y
normals[i * 3 + 2] = detail.normals[i].z
}
mesh.geometry.attributes.normal.needsUpdate = true
// make it so frustum doesn't cull mesh when hand is close to camera
mesh.frustumCulled = false
mesh.visible = true
}
const hide = () => {
mesh.visible = false
}
return {
mesh,
show,
hide,
}
}
const handMeshComponent = {
schema: {
'material-resource': {type: 'string'},
'wireframe': {type: 'boolean', default: true},
'uv-orientation': {type: 'string', default: 'right'},
},
init() {
this.handMesh = null
const beforeRun = ({detail}) => {
let material
if (this.el.getAttribute('material')) {
material = this.el.components.material.material
} else if (this.data['material-resource']) {
material = this.el.sceneEl.querySelector(this.data['material-resource']).material
} else {
material = new THREE.MeshBasicMaterial(
{color: '#7611B6', opacity: 0.5, transparent: true}
)
}
this.handMesh = handMesh(detail, material, this.data.wireframe, this.data['uv-orientation'])
this.el.setObject3D('mesh', this.handMesh.mesh)
this.el.emit('model-loaded')
}
const show = (event) => {
this.handMesh.show(event)
this.el.object3D.visible = true
}
const hide = () => {
this.handMesh.hide()
this.el.object3D.visible = false
}
this.el.sceneEl.addEventListener('xrhandloading', beforeRun)
this.el.sceneEl.addEventListener('xrhandfound', show)
this.el.sceneEl.addEventListener('xrhandupdated', show)
this.el.sceneEl.addEventListener('xrhandlost', hide)
},
update() {
if (!this.handMesh) {
return
}
let material
if (this.el.getAttribute('material')) {
material = this.el.components.material.material
} else if (this.data['material-resource']) {
material = this.el.sceneEl.querySelector(this.data['material-resource']).material
} else {
material = new THREE.MeshBasicMaterial({color: '#7611B6', opacity: 0.5, transparent: true})
}
this.handMesh.mesh.material = material
},
}
export {handMeshComponent}
Thanks, @joshmao, itβs working.
We do this automatically in our Hand Mesh primitive in A-Frame
Is there a flag to disable the automatic adding of hand controls, so that users can add their own setup using a-frame?
When I use a-frame directly and do stuff like parent items to the camera ( headset ) by adding an <a-entity within the <a-camera element, it works great by default. When I do this in 8th Wall, weird stuff happens like the items get parented to the controller instead of the headset.
Having the hands / controller set up is a great default option, but if anyone wants to do a custom setup, this needs to be disabled in order for them to make the required changes.