1/10

ARCore

Interaction & Animation

2/10

Agenda

3/10

Animating Objects

In order to animate an object we can make use of the Android Property Animation API.

The most easy way is to use an ObjectAnimator to animate the properties of a Node. A Node has multiple things that can be animated:

The following slides will give you a short introduction to the Property Animation API and after this we learn how this can be applied on Sceneform Nodes.

4/10

The Property Animation API

The Property Animation API can be used for animating (almost) anything in Android. All you need is to create an Animator object and define the characteristics of the animation. There are two main types of Animators available:

5/10

Defining the characteristics of your animation

You can define the following characteristics:

The Animator will use this defined characteristics to calculate the new values of the properties of your object for each fragment of a timeframe. For example in the below graphic, the value of x at the beginning of the animation with a duration of 40 ms will be x = 0. At the end the value of x will be x = 40. The Animator will now calculate the value for every frame that shall be rendered. How often is defined by the refresh delay (in this case 10 ms). How the value changes within each fraction can be linear or otherwise defined by a function based on the current elapsed time (non-linear animation).

center 60%

6/10

Type Interpolation

To create a non-linear interpolation, the TimeInterpolator class can be used.

There are a number of predefined TimeInterpolators provided, but you can always write your own. Some examples:

7/10

Type Evaluation

The class TypeEvaluator defines how the values for the property have to be calculated. For example, if a property has integer values, the IntEvaluator is used. If a properties requires floats the FloatInterpolator is used. And for a color property the ArgbInterpolator can be used.

The problem with animation 3D objects (nodes) is, that the use properties that require special types such as a Vector3 for scale and location and a Quaternion for rotation.

Fortunately the Sceneform.Math package provides us with a QuaternionEvaluator and a Vector3Evaluator.

Animating the light of a node

The following example code shows how to create an ObjectAnimator that animates the intensity of the Light of a node.

To get access to the Light of the node use getLight() and the animate the property intensity like shown here:

final int durationInMilliseconds = 8000;
final float minimumIntensity = 1000.0f;
final float maximumIntensity = 6000.0f;
ObjectAnimator intensityAnimator =
              ObjectAnimator.ofFloat(
                      node.getLight(), "intensity", minimumIntensity, maximumIntensity);
      intensityAnimator.setDuration(durationInMilliseconds);
      intensityAnimator.setRepeatCount(ValueAnimator.INFINITE);
      intensityAnimator.setRepeatMode(ValueAnimator.REVERSE);
      intensityAnimator.start();
8/10

Rotating a node

For rotation we need to animate the localRotation property of the node. This properties is of type Quaternion. As a rotation can be maximum 180 degrees in one direction, the first value of the animation is 0, the second 180 and the third 360 degrees.

By using setObjectValues() the animations interpolation values are defined. setPropertyName() defines to alter the localRotation of the node. We are using a LinearInterpolation here.

Quaternion orientation1 = Quaternion.axisAngle(new Vector3(0.0f, 1.0f, 0.0f), 0);
Quaternion orientation2 = Quaternion.axisAngle(new Vector3(0.0f, 1.0f, 0.0f), 180);
Quaternion orientation3 = Quaternion.axisAngle(new Vector3(0.0f, 1.0f, 0.0f), 360);


ObjectAnimator rotateAnimator = new ObjectAnimator();
rotateAnimator.setObjectValues(orientation1, orientation2, orientation3);

// Next, give it the localRotation property.
rotateAnimator.setPropertyName("localRotation");

// Use Sceneform's QuaternionEvaluator.
rotateAnimator.setEvaluator(new QuaternionEvaluator());

//  Allow orbitAnimation to repeat forever
rotateAnimator.setRepeatCount(ObjectAnimator.INFINITE);
rotateAnimator.setRepeatMode(ObjectAnimator.RESTART);
rotateAnimator.setInterpolator(new LinearInterpolator());
rotateAnimator.setAutoCancel(true);
rotateAnimator.setTarget(node);
rotateAnimator.setDuration(8000);
rotateAnimator.start();

Scaling a node

Scaling of a node is basically done in the same way as with the rotating. This time we need to use the Vector3Evaluator instead of the QuaternionEvaluator as the localScale property requires this.

Vector3 maxSize = new Vector3(3,3,3);

ObjectAnimator scaleAnimation = new ObjectAnimator();
scaleAnimation.setObjectValues(maxSize);

scaleAnimation.setPropertyName("localScale");
scaleAnimation.setEvaluator(new Vector3Evaluator());
scaleAnimation.setDuration(8000);
scaleAnimation.setRepeatCount(ValueAnimator.INFINITE);
scaleAnimation.setRepeatMode(ValueAnimator.REVERSE);
scaleAnimation.setInterpolator(new LinearInterpolator());
scaleAnimation.setTarget(node);
scaleAnimation.start();

Scaling a TransformableNode

When using the TransformableNode instead of a normal Node class you will notice a flickering when using the above method to scale its size. This is due to the ScaleController that is part of the TransformableNode. The ScaleController will always try to rezise to node to its normal size and that cause the flickering. So in this case it is better to animate the MaxScale property of the ScaleController instead. You can get the ScaleController instance by calling getScaleController() on the TransformableNode.

private void animateNode(TransformableNode node){
      final int durationInMilliseconds = 8000;
      final float minimumSize = 1.0f;
      final float maximumSize = 6.0f;
      
      sizeAnimator = ObjectAnimator.ofFloat(node.getScaleController(),"MaxScale", minimumSize,maximumSize);
      sizeAnimator.setDuration(durationInMilliseconds);
      sizeAnimator.setRepeatCount(ValueAnimator.INFINITE);
      sizeAnimator.setRepeatMode(ValueAnimator.REVERSE);
      sizeAnimator.start();
  }
9/10

Changing the Texture of the Plane Renderer

To make the app more individual, you can change the texture of the plane that is shown when ARCore detects it. This is done by creating a Texture.Sample instance and then use this to build a Texture and set it on the ARSceneView PlaneRenderer as Material. This code snipped shows, how this is done:


Texture.Sampler sampler =
              Texture.Sampler.builder()
                      .setMagFilter(Texture.Sampler.MagFilter.LINEAR)
                      .setWrapMode(Texture.Sampler.WrapMode.REPEAT)
                      .build();

      Texture.builder()
              .setSource(this, R.drawable.ic_launcher)
              .setSampler(sampler)
              .build()
              .thenAccept(texture -> {
                  arSceneView
                          .getPlaneRenderer().getMaterial().thenAccept(material -> material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture));
              });
10/10

Conclusion

Now you should be able to create a simple ARCore app that displays 3D objects. You have learned how to place the objects in space and handling tap events by using a TranformableNode to manipulate the objects. Next you have learned how to animate nodes using the ObjectAnimator. Last but not least you have learned how to create a Texture and replace the standard PlaneRenderer texture with your own. This should make it possible to create your own simple IKEA App clone or create your own app idea. Have fun!