Skip to content Skip to sidebar Skip to footer

Create an AR Blooming Flower

Spark AR Notes: Custom SamplerFactoryCreating Infinite Blooming Flower

You’ll learn how to create an Infinite blooming flower in Spark AR. We will use JavaScript and Reactive Programming style. With this example you can deep dive into many SamplerFactory methods. You can find the sample project in the end of this tutorial.

1. Knowledge requirements

To complete this tutorial, you have to know some basics of Spark AR and JavaScript. If you’ve never been working with Spark AR, try to complete these official tutorials.

2. Preparations steps:

We will set up a blank scene with target tracking. Then we import the 3D asset aka single-pedal which was rigged inside a null object named “placer” (inside “planeTracking0”).

In this tutorial, I import the “pedal-rigged” was made in Blender, and duplicated into 72 times.

3. Variations

Importing module AnimationScene to access objects inside the scene, Reactive for reactive programming.

const Animation = require('Animation');
const Scene = require('Scene');
const Reactive = require('Reactive');
const sceneRoot = Scene.root;

Create a promise function and ThreeDObjects Array named ‘pedal’

Promise.all([sceneRoot.findByPath('planeTracker0/placer/pedal-rigged*');])
.then(function(objects) {const pedal = objects[0];});

Create variable T equal total flower blooming time.

T = delayTime + Transform Area Time : flower blooming period (Milliseconds)

const T = 30000; //Flower blooming period
const n = Array[pedal].length; //pedals number = 72

4. Pedals Number per Corolla & Angles Between

In this example, we have 6 pedals per corolla and total 72 pedals in this flower. So we have 72/6 = 12 corollas = 12 blooming periods.

var pedalNumberPerCorolla = 6; //INPUT Pedal Number Per Corolla

Angle between pedals: degreePedalBetween = 360/ pedalNumberPerCorolla;// 60 degrees

Set degreePedalBetween value in an array pedals rotation Y axis.

var pedalRotateY = new Array(n).fill(null).map((_,i) => ({
    degrees : 360*i/pedalNumberPerCorolla,}));

Assign rotationY to pedals array:

for (var d=0; d < n; d++) {pedal[d].transform.rotationY = radian(pedalRotateY[d].degrees);};

5. Pedals Vertical Number

By this order of pedals angles, we have pedal 1st, 7th, 13th, 19th, 25th, 31th, 37th, 43th, 49th, 55th, 61th, 67th align straight vertical orders.

Continue to create a Time Driver Parameter

var pedalDriverParameters = {durationMilliseconds : T, loopCount : Infinity, mirror: false};

Create a Time Driver

const pedalDriver = Animation.timeDriver(pedalDriverParameters);
pedalDriver.start();

Create samplers: The sampler allows you to specify the beginning and end values of the animation as well as the easing function applied to it, altering the rate of change. In this tutorial, we learn about modifying the function behind this. After that, we also create the Animation, which is created by combining the driver and sampler.

for (var i=0; i < n; i++) {const pedalSamplers = [Animation.samplers.polybezier(rotationZPolybezier[i]), Animation.samplers.polybezier(scalePolyline[i])];

With the animation created we can now apply it to each pedal of this flower, we’ll be using it to change the rotationZ and scale.

const pedalAnimation = Animation.animate(pedalDriver, pedalSamplers);
const pedalTransform = pedal[i].transform;
pedalTransform.rotationZ = pedalnimation[0];
pedalTransform.scaleX = pedalAnimation[1];
pedalTransform.scaleY = pedalAnimation[1];
pedalTransform.scaleZ = pedalAnimation[1];

6. Custom Sample Factory Method: polyline()

polyline(config: {keyframes?: Array | Array | Array, knots?: Array}): ScalarSampler | ArrayOfScalarSamplers | RotationSampler
Meta Spark Studio / Learn

Returns a sampler object that generates values that goes piecewise linearly through specified keyframes as the attached driver’s progress goes from 0.0 to 1.0 through normalized knots points.

The dimensions of the config.keyframes and config.knots arrays, if specified, must be equal and be not less than 2.

The first element of config.knots, if specified, must be zero.

If config.knots is not specified then the knot sequence defaults to [0, 1, 2, …, config.keyframes.length – 1].

In case using an stand alone animation without delay, we can use a animation sampler like linear() or easeInQuint() method.

The sampler allows you to specify the beginning and end values of the animation as well as the applied to it, altering the rate of change.

The following code creates a sampler.

const pedalSampler = Animation.samplers.linear(0,1);

The samplers property of the animation module gives us access to the SamplerFactory class, which we use to set the easing function via the linear() method. We pass in 0 and 1 to the method to specify the start and end values of the animation.

Modify SamplerFactory methods: polyline()

const scalePolyline = {
    keyframes: [0, 0, 1, 0, 0],
    knots: [0, i*T/n, (i+0.5)*T/n, (i+1)*T/n, 2*T],};

Or like this

const scalePolyline = {
    keyframes: [0, 0, 1, 0, 0],
    knots: [0, i*T, (i+0.5n)*T, (i+n)*T, 2*n*T],};

7. Custom Sample Factory Method: polybezier()

polybezier(config: {keyframes?: Array | Array, knots?: Array, tangents?: Array | Array}): ScalarSampler | ArrayOfScalarSamplers
Meta Spark Studio / Learn

Returns a sampler object that generates values of a piecewise cubic Bezier spline that goes through specified keyframes as the attached driver’s progress goes from 0.0 to 1.0 through normalized knots points.

When tangents is specified, the curve is C1-smooth, otherwise the curve is C2-smooth and the second derivatives at the begin and end points are zero.

The dimensions of config.keyframes and config.knots, if specified, and config.tangents, if specified, arrays must be equal and have no less than 2 elements.

The first element of config.knots, if specified, must always be zero.

If config.knots is not specified then the knot sequence is defaulted to [0, 1, 2, …, config.keyframes.length – 1].

Modify SamplerFactory methods: polybezier() instead of bezier()

const rotationZPolybezier = {
    keyframes: [0, 3.2, 6.1, 0],
    knots: [0, i*T/n, (i+1)*T/n, 2*T],};
    tangents: [0, 0, 0, 0],};

Or like this

const rotationZPolybezier = {
    keyframes: [0, 3.2, 6.1, 0],
    knots: [0, i*T, (i+n)*T, 2*n*T],};
    tangents: [0, 0, 0, 0],};

Your code will now look like this

const Animation = require('Animation');
const Scene = require('Scene');
const Reactive = require('Reactive');

const sceneRoot = Scene.root;

Promise.all([
    sceneRoot.findByPath('planeTracker0/placer/pedal-rigged*');
])

.then(function(objects) {
    const pedal = objects[0];

    const T = 30000; //Flower blooming period
    const n = Array[pedal].length; //pedals number = 72
    var pedalNumberPerCorolla = 6; //INPUT Pedal Number Per Corolla

    var pedalRotateY = new Array(n).fill(null).map((_,i) => ({
        degrees : 360*i/pedalNumberPerCorolla,}));

    //Function convert radius degrees to Radian
    function radian(degrees) {
        const result = Math.PI*degrees/180;
        return result;
    };

    for (var d = 0; d < n; d++) {
        pedal[d].transform.rotationY = radian(pedalRotateY[d].degrees)};

    const scalePolyline = {
        keyframes: [0, 0, 1, 0, 0],
        knots: [0, i*T, (i+0.5n)*T, (i+n)*T, 2*n*T],};

    const rotationZPolybezier = {
        keyframes: [0, 3.2, 6.1, 0],
        knots: [0, i*T, (i+1)*T, 2*n*T],};
        tangents: [0, 0, 0, 0],};

    var pedalDriverParameters = {
        durationMilliseconds : T,
        loopCount : Infinity,
        mirror: false
    };

    const pedalDriver = Animation.timeDriver(pedalDriverParameters);
    pedalDriver.start();

    for (var i = 0; i < n; i++) {
    const pedalSamplers = [Animation.samplers.polybezier(rotationZPolybezier[i]), Animation.samplers.polybezier(scalePolyline[i])];

    const pedalAnimation = Animation.animate(pedalDriver, pedalSamplers);
    const pedalTransform = pedal[i].transform;
    pedalTransform.rotationZ = pedalnimation[0];
    pedalTransform.scaleX = pedalAnimation[1];
    pedalTransform.scaleY = pedalAnimation[1];
    pedalTransform.scaleZ = pedalAnimation[1];
    };
}

Read the official document of Spark AR/ Learn for a completely project that animate an 3D object by script. (link below)

Thank you for reading!

8. Resources

Feel free to send me a message.

References