I new to Animation and Three js .
I have 2 file :
- VRM file(having mesh,texture,lighting ,skeletons etc) character file.
- GLB file having (animation but no mesh) exported from unreal.
What i am trying to achieve is play animation of GLB file on VRM file . (this way user can swap character and can able to play same animation on different character’s).
Here is my minimal code to achieve this.
So what’s the issue ?
animation is not playing properly i.e there is issue with look and animations it doesn’t look as intended.
Here is code
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
var camera, controls, scene, renderer;
let mixer, model;
const clock = new THREE.Clock(); // For animation timing
let currentVrm = undefined;
let currentMixer = undefined;
let currentMixer2 = undefined;
let mesh1, mesh2, clipp;
let animation_demo;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
//renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
console.log(" Info:", renderer.info.memry);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x3232323);
const ambientLight = new THREE.AmbientLight(0x000000, 0.2); // Soft white light
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, 1, -2);
scene.add(directionalLight);
const cloudTexture = new THREE.TextureLoader().load("images.jpeg");
const material = new THREE.MeshBasicMaterial({
map: cloudTexture,
side: THREE.DoubleSide,
});
const sphereGeometry = new THREE.SphereGeometry(50, 3, 3);
sphereGeometry.scale(-1, 1, 1); // Invert the geometry
// Apply the material to the sphere
const sphere = new THREE.Mesh(sphereGeometry, material);
// scene.add(sphere);
const groundGeometry = new THREE.PlaneGeometry(10, 10); // Adjust size as needed
const grassTexture = new THREE.TextureLoader().load(
"istockphoto-1200569039-612x612.jpg"
); // Load grass texture
grassTexture.wrapS = THREE.RepeatWrapping;
grassTexture.wrapT = THREE.RepeatWrapping;
grassTexture.repeat.set(10, 10); // Repeat texture on the ground
const grassMaterial = new THREE.MeshStandardMaterial({ map: grassTexture });
const groundMesh = new THREE.Mesh(groundGeometry, grassMaterial);
groundMesh.rotation.x = -Math.PI / 2; // Rotate to make it horizontal
// scene.add(groundMesh);
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.01
);
camera.position.set(0, 1.5, -1.5);
camera.position.x = camera.position.x;
camera.position.y =
(camera.position.y - camera.position.y - camera.position.y) / 2 + 1.5;
// camera.lookAt(1000, 1000, 1000);
var gridHelper = new THREE.GridHelper(10, 10);
var axesHelper = new THREE.AxesHelper(5);
scene.add(gridHelper);
scene.add(axesHelper);
controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 1; // Minimum distance camera can be from the center
controls.maxDistance = 4; // Maximum distance camera can be from the center
controls.maxPolarAngle = Math.PI / 2; // Limit camera rotation angle to avoid looking upside down
controls.enablePan = false; // Disable panning
controls.target.set(0, 0.75 * 1.5, 0);
const vrmLoader = new GLTFLoader();
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// var vrmLoader = new VRMLoader();
vrmLoader.register((parser) => {
return new VRMLoaderPlugin(parser);
});
vrmLoader.crossOrigin = "anonymous";
vrmLoader.crossOrigin = "anonymous";
vrmLoader.load("testAnim.glb", function (gltf) {
//mesh1 = gltf.scene.children[0];
// gltf.scene.scale.set(101,101,101);
mesh2 = gltf.scene;
animation_demo = gltf.animations;
vrmLoader.load(
"Vroid_Character_1.vrm",
(vrm) => {
model = vrm.scene;
const humanoid = vrm.userData.vrm;
currentVrm = humanoid;
scene.add(model);
currentMixer = new THREE.AnimationMixer(mesh2);
animation_demo.forEach((clip) => {
// console.log("Clip");
// console.log(clip.tracks);
clip.tracks.forEach((track) => {
const targetObject = track.name.includes("position") ?? null;
// const targetObject2 = track.name.includes('scale') ?? null;
// const targetObject3 = track.name.includes('quaternion') ?? null;
// console.log(track.name.includes('quaternion'));
const bindingProp = mesh2;
if (targetObject) {
const property = track.name.split(".").pop(); // Extract the property name (e.g., "position")
const trackBinding = new THREE.PropertyMixer(
bindingProp,
property
);
currentMixer._bindings.push({
track: track,
binding: trackBinding,
});
currentMixer._bindingsByRootAndName[
bindingProp.uuid + "." + property
] = trackBinding;
bindingProp.position.set(...track.values); // Assuming track.values contains [x, y, z]
}
const clip2 = new THREE.AnimationClip("Animation", 1.0);
currentMixer.clipAction(clip, model).play();
});
});
},
function (progress) {
console.log(
"Loading model...",
100 * (progress.loaded / progress.total),
"%"
);
},
function (error) {
console.error(error);
}
);
});
}
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (currentVrm) {
currentVrm.update(delta);
}
if (currentMixer) {
// console.log("currentMixer",currentMixer);
currentMixer.update(delta);
}
controls.update();
renderer.render(scene, camera);
}
Currently I want to know if
- I am in right direction or not ?
- There were no errors in console , but can’t able debug these animation’s issue.What i can’t do resolve issue. Guidance in right direction would be helpful.
Here is small demo what i output i am getting in browser
Here is my glb file that i am using
testAnim.glb (110.9 KB)