Overview of enabling animation in your app

Configuring the build

Configuring the build to enable animation in your app involves modifying the build.gradle file and importing animations into your project.

  1. Update your app's build.gradle to support an imported animation model renderable, by adding the Sceneform animation dependency:

     dependencies {
         
         // Support for animated model renderables.
         implementation "com.google.ar.sceneform:animation:1.15.0"
         }
    
  2. Import and preview your *.fbx animation files to obtain an *.sfb file containing the imported model.

Using animations at runtime

Use runtime operations to:

Creating an animation renderable

At runtime, use ModelRenderable.Builder to load the *.sfb and attach it to a node in the scene, as you would do with any ModelRenderable:

  // Create the ModelRenderable.
  ModelRenderable.builder()
      .setSource(this, R.raw.andy)
      .build()
      .thenAccept(renderable -> andyRenderable = renderable)
      .exceptionally(
          throwable -> {
          Log.e(TAG, "Unable to load Renderable.", throwable);
          return null;
      });

  // Attach the ModelRenderable to the node in the scene.
  Node andyNode = new Node();
  andyNode.setParent(arFragment.getArSceneView().getScene());
  andyNode.setRenderable(andyRenderable);

Get access to the animation data

// Get the animation data called "andy_dance" from the `andyRenderable`.
AnimationData danceData = andyRenderable.getAnimationData("andy_dance");

Accessing animations based on different metadata

     // Get the animation name.
     danceData.getName();

To retrieve an instance of animation data, use the ModelRenderable.getAnimationData() methods:

     // Access animations by index.
     numAnimations = andyRenderable.getAnimationDataCount();
     danceData = andyRenderable.getAnimationData(0);

Control playback

Create a ModelAnimator to control playback.

ModelAnimator andyAnimator = new ModelAnimator(danceData, andyRenderable);

Use start() to play back the animation. It will stop automatically at the end.

andyAnimator.start();

To loop the animation, use setRepeatCount()

andyAnimator.setRepeatCount(<number of repeats>)

(Optional) Add property animation operations

The ModelAnimator extends the Android Animator class, which allows more rich interactions such as looping, responding to events, and non-linear interpolators.

Using SkeletonNode to identify and attach models to bones

When you’re working with a renderable containing bones, use the SkeletonNode class to get access to individual bones in the skeleton by attaching nodes to the bones. This lets you “attach” objects to bones or control their position.

While the animation is playing, The position, scale, and orientation of the attached node is updated by the SkeletonNode each frame. Setting the position, scale, or rotation of the attached node will override the bone until the next time the bone is updated by the animation.

In the Animation sample, this is done by attaching a Node containing a model of a hat to the bone for Andy’s “head.” When Andy is animated, the hat stays on his head.

Accessing information about bones

To access information about bones in a ModelRenderable, use the getBoneCount(), getBoneName(), or getBoneParent() methods:

// Get the number of bones in the model’s skeleton.
andyRenderable.getBoneCount();

// Get the names of the bones in the model’s skeleton.
andyRenderable.getBoneName();

// Get the hierarchy of the bones in the model’s skeleton.
andyRenderable.getBoneParent();

Working with SkeletonNodes

The SkeletonNode class exposes the skeleton of a model in order to attach nodes to specific bones.

To use SkeletonNode, create a new instantiation of it and set the renderable to the renderable that contains a model with a skeleton.

 andyWithSkeleton = new SkeletonNode();
 andyWithSkeleton.setRenderable(andyRenderable);
 andyWithSkeleton.setParent(scene);

To attach a renderable to a specific bone, first create a new node and attach it to the bone. Add the node containing the renderable as a child of the first node. To ensure that the scale and rotation of the bone isn't used to set the relative transform of the nodes, make sure to reset the scale and position of the second node.

 hatNode = new Node();
 Node boneNode = new Node();
 boneNode.setParent(andy);
 andy.setBoneAttachment(HAT_BONE_NAME, boneNode);
 hatNode.setRenderable(hatRenderable);
 hatNode.setParent(boneNode);
 hatNode.setWorldScale(Vector3.one());
 hatNode.setWorldRotation(Quaternion.identity());
 Vector3 pos = hatNode.getWorldPosition();