This approach means that at loading time we "fix" the whole the animation. Animation becomes something like a 3D movie.
A downside is that loading time (and other resources usage, like memory) is larger, especially for longer animations. That's because we store the whole animation in memory.
Huge advantage of this method: once loaded, animation can be played ultra-fast. Actually, it's as fast a displaying a still model (currently, this is exactly what is done under the hood: at each time, we simply display one chosen frame of animation; in the future this may change, but still will be lighting fast). That's the reward for long loading time and "fixing" the animation.
We generally do not recommend this method anymore.
castle-anim-frames (Castle Game Engine animations) format was specifically created to describe precalculated animations.
We also load MD3 (Quake3 engine) animations to this form, as they are especially suitable for it.
TNodeInterpolator
class is used to build
and render precalculated animations.
For each provided frame we have an associated time.
The resulting animation will change the first model to the last one,
such that at any time point we will either use one of the predefined models
(if point in time is close to the model's associated time) or a new
model created by interpolating between two successive models in time.
Under the hood, we have quite intelligent algorithm that checks
each pair of two successive models for structural equality.
“Structural equality” simply means that the two models
are equal, with the exception of various floating-point fields,
on which they may differ. The idea is that we can define linear
interpolation between two models that are structurally equal.
So when you specify two structurally equal models for an animation,
we can generate many intermediate scenes (this is
the ScenesPerTime
parameter to loading method) that smoothly show one model changing into
the other. This can interpolate any floating-point field value, like
SFColor
,
SFFloat
, SFMatrix
,
SFRotation
, SFVec2f
and
SFVec3f
an all equivalent multi-valued fields
(they can differ in values, but still must have the same number of items).
For example, the first model may be a small sphere with blue color, and the second model may be a larger sphere with white color. The resulting animation depicts a growing sphere with color fading from blue to white. More examples:
Moving, rotating, scaling objects may be expressed by changing transformation values.
Any kind of morphing (mesh deformation) may be expressed by changing values of
IndexedFaceSet
coordinates.Materials, colors, lights may change. Even such properties like a material transparency, or a light position or direction.
Texture coordinates may change to achieve effects like a moving water surface.
Another advantage of structural equality is that we will perform aggressive merging of two structurally equal models. This means that when two nodes are detected to be exactly equal, one of them will be removed (and pointers rearranged to both point to the same node in memory). If the nodes are not exactly equal, we still check their children and possibly merge them. This is a huge saving in terms of memory, as practically all the non-animated parts of the model will only be kept once in memory. It's implemented quite intelligently, so it's actually a relatively fast process done during the model loading.
All the models of the animation do not actually have to be structurally equal. You can even change one model into something completely different. But in these cases we cannot generate smooth transition from one model to the other, and the animation will just show a sudden change into new version at it's time.
If you're concerned that possibly some parts of your animation
are not structurally equal, you can always load them into
view3dscene
run with --debug-log
command-line option.
Then, at loading time, you will get messages on console if two
successive models were not detected as structurally equal
(and so a sharp change from one to the other will be shown in animation).
The message will also describe exactly where the difference is found.
First of all, the scenes are not interpolated when
rendering. Instead, at loading time, we create
a number of new interpolated models and save them
(along with the models that were specified explicitly).
The parameter ScenesPerTime
says with what granularity the intermediate scenes
are constructed for a time unit.
If you specify too large
ScenesPerTime
your animations will take a lot
of time to prepare and will require a lot of memory.
On the other hand too small ScenesPerTime
value will
result in an unpleasant jagged animation. Ideally, ScenesPerTime
should be >= than the number of frames you will render in your time unit,
but this is usually way too large value.
Special value of 0
for ScenesPerTime
means that you want only the explicitly passed nodes in the scene,
nothing more. No more intermediate scenes will ever be created.
This creates a trivial animation that suddenly jumps from
one still model to the next at specified times. It may be useful if you
already have generated a lot of models, densely distributed
over time, and you don't need TNodeInterpolator
to insert any more
scenes. Structural equality (or it's lack)
doesn't change the look of such animation, as no additional interpolation is done
when loading, but still structurally equal models may be merged
to conserve memory use.
Internally, the TNodeInterpolator
wraps
each model (that was specified explicitly or created by interpolation)
in a new node.
This means that we have all the features
of our static OpenGL rendering available when doing animations too.
We have a special file format to express precalculated animations: castle-anim-frames, Castle Game Engine animations. It references a number of static 3D model files and their corresponding times, describing the animation.
If you want to experiment with castle-anim-frames format,
view3dscene
can load and play animations in castle-anim-frames format.
You can find example castle-anim-frames animations in
VRML/X3D demo models
(see directory castle-anim-frames/
),
the sources of our engine also contain simple examples in
directory castle_game_engine/examples/
(like resource_animations
that plays animations
specified in resource.xml
files for game creatures/items).
Also “The
Castle” uses such animations for all creatures and weapons.
In general, using a single glTF, X3D, VRML, Spine file is a much better approach than castle-anim-frames files.
Also, castle-anim-frames files may waste a lot of disk space if your animation tries to change two pieces of your model with drastically different speeds. Consider this:
It's OK to create an animation with a box that blinks (changes color) 100 times per time unit. Just 2 model files are needed for this, with castle-anim-frames file specifying to loop them over a short period of time.
It's also OK to create an animation with a sphere that blinks only once for a given time unit.
But if you want to create an animation that contains both the box (blinking 100 times/time unit) and the sphere (blinking once for a time unit), you will have to prepare 100 still 3D files to express this!
VRML interpolators don't have this problem, since every interpolator has it's own set of keys. So both can be placed within the same file, without the need to explicitly write 100 values anywhere.
Despite this, there remains one practical advantage of using castle-anim-frames file format: you can design your animations using any authoring software that can export static VRML files. If your modeler can design animations, but doesn't save them to VRML interpolator nodes, all you have to do is to export your models a couple of times from a couple of different points in time.
In the old days, this allowed us to use Blender do design animations and export them. Nowadays, we export from Blender to glTF using standard Blender exporter, and there's no need for castle-anim-frames.