Avatar system for three.js.
- Loads VRM/VRoid, Ready Player Me
- Play mixamo animation
- Head sync for VR
- Replaceable collision detection
- Replaceable lip sync
- Replaceable IK (Inverse Kinematics)
- Show hand mirror
npm run example
npm install @verseengine/three-move-controller @verseengine/three-touch-controller @verseengine/three-xr-controller @verseengine/three-avatar
<script
async
src="https://cdn.jsdelivr.net/npm/es-module-shims@1.6.2/dist/es-module-shims.min.js"
></script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.153.0/build/three.module.js",
"three-avatar": "https://cdn.jsdelivr.net/npm/@verseengine/three-avatar/dist/esm/index.js",
"@pixiv/three-vrm": "https://cdn.jsdelivr.net/npm/@pixiv/three-vrm@2/lib/three-vrm.module.min.js",
"@verseengine/three-touch-controller": "https://cdn.jsdelivr.net/npm/@verseengine/three-touch-controller/dist/esm/index.js",
"@verseengine/three-move-controller": "https://cdn.jsdelivr.net/npm/@verseengine/three-move-controller/dist/esm/index.js",
"@verseengine/three-xr-controller": "https://cdn.jsdelivr.net/npm/@verseengine/three-xr-controller/dist/esm/index.js"
}
}
</script>
import * as THREE from "three";
import * as THREE_VRM from "@pixiv/three-vrm";
import {
createAvatarIK,
isAnimationDataLoaded,
preLoadAnimationData,
createAvatar,
Lipsync,
} from "three-avatar";
// Animation fbx files downloaded from mixamo.com
const ANIMATION_MAP = {
idle: "path/to/idle.fbx",
walk: "path/to/walk.fbx",
dance: "path/to/dance.fbx",
};
async function loadAvatar(avatarURL) {
...
if (!isAnimationDataLoaded()) {
await preLoadAnimationData(ANIMATION_MAP);
}
const collisionObjects:THREE.Object3D[] = [wall0, wall1, ...];
const teleportTargetObjects:THREE.Object3D[] = [ground0, ...];
const collisionBoxes = [];
[...collisionObjects, ...teleportTargetObjects].map((el) => {
el.traverse((c) => {
if (!c.isMesh) {
return;
}
collisionBoxes.push(new THREE.Box3().setFromObject(c));
});
});
let resp = await fetch(avatarURL);
const avatarData = new Uint8Array(await resp.arrayBuffer());
const avatar = await createAvatar(
avatarData,
renderer,
false,
avatarContainer,
{
getCollisionBoxes: () => collisionBoxes,
isInvisibleFirstPerson: true,
}
);
avatarContainer.add(avatar.object3D);
const touchController = new TouchController(avatarContainer);
}
const clock = new THREE.Clock();
function animate() {
_avatar?.tick(clock.getDelta());
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);