=========
Animation
=========

Beginning with version 2021 SP1, HOOPS Visualize has the ability to perform simple animations. These animations can be used to demonstrate how assemblies fit together, better show complex parts within a single view, demonstrate how an object changes over time, and more. 

	As the developer, you have a certain amount of control over the complexity and structure of your own scenes, and HOOPS Visualize will render frames as fast as the hardware is able. However, please note that while Visualize can perform simple animations reasonably well, it is first and foremost an engineering visualization engine. High performance, computationally intensive animation involving a large amount of geometry and post-processing is theoretically possible, but is not the goal of the animation API. It follows that such performance should not necessarily be expected. If you require this capability, please contact support to determine the best options for your project.

HOOPS Visualize has several different properties that can be animated, including scale, rotation, translation, camera, color, and opacity.

All animations are associated with ``HPS::View`` and ``HPS::Model``. These objects are part of the :doc:`view hierarchy <0301_core>`, which must be used when working with animation.

Programmatically speaking, animations are composed of a number of objects which describe how the animation changes over time. From lowest level to highest, these objects are arranged as follows:

Keyframes > KeyframeArray > Sampler > Animation > AnimationControl

* **Keyframes.** Like in traditional animation, a keyframe defines start and end points of an animation, or portion of an animation. In HOOPS Visualize, there are a number of different keyframe types, depending on what kind of animation you're creating. These are further explained below.
* **KeyframeArray.** A collection of keyframes the sampler will use to create the animation.
* **Sampler.** Samples an animation based on keyframes in order to interpolate between the scene's start and end state.
* **Animation.** Uses a sampler to create one or more *animation channels*, which describe what kind of animation will be occurring in the scene. Scale operations will use a scale channel, rotation animations will use a rotation channel, etc. Created from a ``HPS::Model`` object.
* **AnimationControl.** Used to conduct playback of animations. Created by and associated with the HPS::View.

In the next section, we'll look at how these parts fit together.


Linear Translation Example
==========================

Let's take a look at a short code example. This snippet creates a translation animation which moves the model along the X axis.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [translation_animation]
		   :end-before: //! [translation_animation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [translation_animation]
		   :end-before: //! [translation_animation]

Looking over this code, you may infer by the class names that we are preparing a linear animation using two vector objects. This means the keyframes will be timed in a linear fashion along a straight line. Let's break this code down to explain more about what's happening.

**View hierarchy references.** The first part of the code is simply getting references to the various view hierarchy objects we'll need for the subsequent sections - ``HPS::Canvas``, ``HPS::View``, ``HPS::Model``. Depending on your window framework, this might be slightly different from what is shown, but the concept is the same.

**Building keyframes.** The first piece of the animation, creating the keyframes, is simple. We have two keyframes - a start and an end - and we want to translate uniformly between them. The first parameter indicates the "tick" at which that keyframe will be reached (more on ticks later). The Vector parameters are describing the translation amount. So in this code, we are starting at a position of [0, 0, 0] and ending at a position of [-5, 0, 0]. If, for example, you wanted to translate in a third direction, simply add another keyframe with a new position. 

**Collecting keyframes.** The next code block is simply adding all keyframes of our animation into a collection which will be used in the next step.

**Sampler.** The next task is to run the keyframe array through the sampler. Notice that we are using ``VectorKeyframe``, so we must use ``HPS::VectorSampler`` (other animation types will use different keyframe and sampler objects). Here is where we set the animation to ``Linear`` (other interpolation options include ``Constant``, ``SphericalLinear``, and ``Spline``).

**Animation.** The ``HPS::Animation`` object is created using the ``HPS::Model`` that you want to animate (for more on Models and Views, see :doc:`this page <0301_core>`). In this example, we are only animating the model's translation, so we add a single translation channel using ``AddTranslationChannel``. The name "translationChannel" is only an identifier - you can use any name you find convenient.

The second parameter is a ``HPS::KeyPath``, which points to the segment that will be affected by this animation. In this code snippet, we only have a single unambiguous hierarchy: Canvas > Layout > View > Model, so we can simply animate the entire canvas as a ``HPS::SprocketPath``. However, if you have multiple views, models, or parts of models that you want to animate independently, you will need to be more specific with your ``HPS::KeyPath``, specifying your keys down to the individual segment (see the API docs for SprocketPath). Lastly, we add the sampler from the previous step.

	Additional channels can be created to animate different aspects of the model, or to combine multiple animations in a single model. See the `Other Animation Types`_ section for an example of combining two animations.

**AnimationControl.** The ``HPS::AnimationControl`` object is referenced from the ``HPS::View`` object you're working with and is used to conduct playback of this animation.

Before playback is conducted you must first supply the animation(s) to the AnimationControl. In our example, we are running the animation over 4000 milliseconds. We are also setting the milliseconds-per-tick value to 1000. This means that each "tick" of the animation will take one second, although this can be adjusted to your requirements. The ``HPS::AnimationControl`` object also allows you to start, stop, resume, and wait on the animation.

The result of the animation is as follows:

.. image:: images/translation-animation.gif

Please note the code sample above does not load a model. If you intend to run the sample code in this section, be sure to load a model before you execute.


Rotation Animations
===================

Animated rotations rely on quaternions to describe the rotation. As such, we'll need to use animation objects that can handle quaternion rotation. Although the objects are built in slightly different ways, the fundamental structure of the animation remains the same. We'll get a reference to the view hierarchy, specify keyframes, specify a rotation, and add this data to an HPS::Animation object.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [rotation_animation]
		   :end-before: //! [rotation_animation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [rotation_animation]
		   :end-before: //! [rotation_animation]

Again, you'll have to load the model in a separate step. The result of this animation is a 360 degree rotation:

.. image:: images/rotation-animation.gif

You may have noticed that the model isn't quite spinning around the center of the model. By default, rotations will rotate around the target segment's origin point, which might not be inside the model itself. If you want to rotate about something other than the segment center point, you must make use of both a translation and rotation. There are many ways to create that transformation, but let's imagine you have a matrix that represents the full transformation you want to occur for some target at tick X. You could use ``HPS::MatrixKit::Decompose`` to get a translation, rotation, and scale from that matrix, then make a keyframe for each of those values that occur at tick X and append them to their respective channel's keyframe arrays. The transformation variables like scale, translation, and rotation are decomposed in the animation channels so that they can be directly interpolated using the preferences of the sampler.


Combining Animations
====================

We've looked at how animations work in general, and we've discussed how simple translation and rotation animations work. Since we already have the code for both animations, combining the two can be easily done. All that is needed is to add a rotation channel with the new rotation sampler to the original ``HPS::Animation`` object. The complete code sample is as follows:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [combined_animation]
		   :end-before: //! [combined_animation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [combined_animation]
		   :end-before: //! [combined_animation]

The combined animation:

.. image:: images/combined-animation.gif


Animating Color
===============

Other animations types are created in a similar way - you would just need to use different objects to build the animation. For example, to animate color, you would use a set of ``HPS::RGBColorKeyframe``, ``HPS::RGBColorKeyframeArray``, and ``HPS::RGBColorSampler``. The same ``HPS::Animation`` class from previous examples accepts the color keyframes and will work based on the sampler you provide.

You can also use a color channel to animate subentities. Faces, lines, edges, and markers may be animated by using the ``HPS::Animation::AddColorChannel`` function and specifying a ``HPS::ChannelColorUsageArray`` value. If no color channel entity value is specified, it will default to ``Faces``.

Below is an example of animating the face color. This sample changes the face color gradually from black to red to purple:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [color_animation]
		   :end-before: //! [color_animation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [color_animation]
		   :end-before: //! [color_animation]
		   
Multiple subentities may also be animated using the same ``Animation``. If, for example, you wanted to animate faces, lines, edges, markers, or any combination of these, simply specify that in the ``ChannelColorUsageArray``. For example:

.. tabs::

	.. group-tab:: C++
	
		.. code-block:: cpp
		
			ccua->push_back(HPS::ChannelColorUsage::Edges);
			ccua->push_back(HPS::ChannelColorUsage::Lines);
			ccua->push_back(HPS::ChannelColorUsage::Markers);
		   
	.. group-tab:: C#

		.. code-block:: csharp
		
			ccua[0] = HPS.ChannelColorUsage.Edges;
			ccua[1] = HPS.ChannelColorUsage.Lines;
			ccua[2] = HPS.ChannelColorUsage.Markers;


Animating Scale
===============

To animate scale, you would build the animation in the same way as the translation example (see above), using ``HPS::VectorKeyframe`` and associated classes. In this case, the keyframe values represent scale rather than position. And instead of adding a translation channel, you would add a scale channel using ``HPS::Animation::AddScaleChannel``. Here's an example of code that scales a model by 300%:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [scale_animation]
		   :end-before: //! [scale_animation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [scale_animation]
		   :end-before: //! [scale_animation]
		   
	
Other Animation Types
=====================
		   
Supported animation types, keyframes, and the samplers they work with are listed in the following table:

.. csv-table::
	:header: "Animation type", "Associated keyframe", "Associated sampler"
	
	"Camera position", "HPS::PointKeyframe", "HPS::PointSampler"
	"Camera target", "HPS::PointKeyframe", "HPS::PointSampler"
	"Camera up vector", "HPS::VectorKeyframe", "HPS::VectorSampler"
	"Color", "HPS::RGBColorKeyframe", "HPS::RGBColorSampler"
	"Opacity", "HPS::FloatKeyframe", "HPS::FloatSampler"
	"Rotation", "HPS::QuaternionKeyframe", "HPS::QuaternionSampler"
	"Scale", "HPS::VectorKeyframe", "HPS::VectorSampler"
	"Translation", "HPS::VectorKeyframe", "HPS::VectorSampler"
	
	
Interpolation Types
===================

So far, all of our examples have used linear interpolation, however, there are a few other options available if you require them:

.. csv-table::
	:header: "Interpolation type", "Description"
	
	"``HPS::Sampler::InterpolationType::Constant``", "Will not interpolate your model between keyframes. Instead, the model abruptly changes state to the next keyframe."
	"``HPS::Sampler::InterpolationType::Linear``", "Interpolates between keyframes in a steady manner."
	"``HPS::Sampler::InterpolationType::SphericalLinear``", "Currently used just for rotation animations. Can be used as a smoother way to interpolate quaternions - commonly referred to as 'slerp'."
	"``HPS::Sampler::InterpolationType::Spline``", "HOOPS Visualize will compute a spline between keyframes that will be used to affect the animation."

The next section illustrates how the interpolation types change the result. It will also demonstrate how to animate specific segments rather than the entire scene.


Animating Specific Segments
===========================

Below is an example showing the difference between constant and linear interpolation of a color animation. It's also an example of how to animate different segments in the same scene. We've created some sample geometry - two cylinders which each exist in separate segments. These cylinders are using the same keyframes and colors, but use different interpolation types. Also note how the ``HPS::KeyPath`` objects are built from leaf to root, specifying two individual segments that we want to animate independent of the rest of the scene. This is different from previous examples, where we used the ``HPS::KeyPath`` parameter to simply animate all geometry in the canvas as a whole:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_animation.cpp
		   :start-after: //! [animating_segments]
		   :end-before: //! [animating_segments]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_animation.cs
		   :start-after: //! [animating_segments]
		   :end-before: //! [animating_segments]

Note there is nothing to load in a separate step - you can run this example as-is in one of the :doc:`sandboxes </general/sandboxes>` included with HOOPS Visualize. The result of this code is shown below. The linearly interpolated cylinder is at the top, the constant interpolation is below:

.. image:: images/color-animation.gif


Other Notes
===========

**Rendering speed.** HOOPS Visualize will do its best to keep your animation running according to your specification. However, updates scheduled by the animation API are not interrupted if they cannot be completed before the next frame is scheduled. Therefore, if the hardware is unable to keep up with the rendering demands of the scene, frames will be skipped. For example, let's say you set a "milliseconds-per-tick" value of 1000 with ten keyframes which occur at ticks 1-10 (keyframe value changes occur every second), but the hardware needs two seconds to complete each update. In this case, you would see the animation render every other tick.
