three.jsblenderreact-three-fiber3d-modelpose

How to maintain threejs model pose during consecutive animations


Hi I am working on a 3d avatar model to replicate sign language poses. I have created two separate actions in blender for my avatar hampalmd & hamshouldertop. In blender it is possible to create keyframes for certain joints instead of the full body. So by blending both actions I am able to create my sign in blender. However in React-three-fiber where I have uploaded my model I encountered the problem where my model resets to the rest position when transitioning to new animation. How do I save the endstate / pose of the 3d model as the beginning state for the next animation?Example


import React, { useEffect, useRef } from "react";
import { useGLTF, useAnimations } from "@react-three/drei";
import { useCharacterAnimations } from "../contexts/CharacterAnimations";
import * as THREE from "three";

const Man = (props) => {
  const group = useRef();
  const { nodes, materials, animations } = useGLTF("./models/man.glb");
  const { setAnimations, animationIndex } = useCharacterAnimations();
  const { actions, names } = useAnimations(animations, group);
  console.log(names);

  useEffect(() => {
    setAnimations(names);
  }, [names]);

  useEffect(() => {
    const currentAction = actions[names[animationIndex]];

    // Reset, fade in, and play the animation
    currentAction.reset().fadeIn(0.5).play();
    // Ensure animation plays once
    currentAction.setLoop(THREE.LoopOnce, 1);

    // Pause the animation at the end of the last frame
    currentAction.clampWhenFinished = true;

    // Clean up function to fade out the animation when component unmounts
    return () => {
      currentAction.fadeOut(0.5);
    };
  }, [animationIndex]);


  return (
    <group ref={group} {...props} dispose={null}>
      <group name="Scene">
        <group name="Armature001" rotation={[1.829, 0, 0]}>
          <primitive object={nodes.root} />
          <skinnedMesh
            name="rp_manuel_animated_001_dancing_geo"
            geometry={nodes.rp_manuel_animated_001_dancing_geo.geometry}
            material={materials["rp_manuel_animated_001_mat.005"]}
            skeleton={nodes.rp_manuel_animated_001_dancing_geo.skeleton}
            castShadow
          />
        </group>
      </group>
    </group>
  );
};
export default Man;

useGLTF.preload("./models/man.glb");

I tried setting the new rest pose of the 3d model after animation ends but encounter an error

// Clean up function to fade out the animation when component unmounts
return () => {
  // Fade out the animation
  currentAction.fadeOut(0.5);

  // Apply the skeleton's pose to the skinned mesh
  group.current.skeleton.applySkeleton(group.current);
};

But i encountered the error

TypeError: Cannot read properties of null (reading 'skeleton')


Solution

  • I have solved the problem, it was a simple fix. When exporting the model to selected format (fbx, glb ...) you have to uncheck the "Reset pose bones" under the "Armature" tab

    Answer

    // Clean up function to fade out the animation when component unmounts 
    return () => {   
        // Fade out the animation   
        currentAction.fadeOut(0.5);
    };
    

    Above is the updated return function. There is no need to modify the skeleton's pose in the code