A "precalculated" animation done by interpolating between a number of 3D model states.
After constructing an object of this class, you must actually load it's animation by calling Load or LoadFromFile or LoadFromEvents etc.
When loading you must provide one or more X3D models with their associated times. Animation will show a transition from the first model to the last. If models are "structurally equal" then the transition between two successive models will be smooth, otherwise a sudden change will be shown. "Structurally equal" means the same nodes hierarchy, the same names of nodes, the same values of all fields (with the exception of fields that are floating-point based and so can be interpolated, for example SFFloat, SFVec3f and equivalent MFXxx fields). For multi-valued fields (MFXxx) that can be interpolated: note that values of items may differ, but still the counts of items must be equal.
the first scene on the list is exactly the 1st object
the last scene on the list is exactly the last object
intermediate scenes are accordingly interpolated between the two surrounding "predefined" by you scenes
For example, first object may be a small sphere with blue color, the other object may be a larger sphere with white color, and the simplest times are 0.0 for the 1st scene and 1.0 for the 2nd scene. The animation will show the blue sphere growing larger and fading into the white color. Of course, any kind of models is allowed — e.g. it can be a walking man at various stages, so in effect you get an animation of walking man.
A special case when you pass only one scene to this class is allowed (it may be handy in some situations). This will obviously produce just a still result, i.e. resulting TCastlePrecalculatedAnimation will be just a wrapper around single TCastleScene instance.
Internal version of Load routines, feasible to load from both ready RootNodes array and to automatically generate RootNodes on the fly.
GetRootNodeWithTime will be called with indexes from 0 to RootNodesCount - 1. It's guaranteed that it will be called in this order (from 0 upwards to RootNodesCount - 1) and will be called exactly once for each index. So it's safe to e.g. create RootNode with some costly operation there.
Note that RootNode passed to GetRootNodeWithTime becomes owned by this class. Well, you can get control over only the first one, by AOwnsFirstRootNode, but you cannot free it anyway while this is loaded.
See Load for more information, including the meaning of EqualityEpsilon.
function HeightCollision(const Position, GravityUp: TVector3Single; const TrianglesToIgnoreFunc: T3DTriangleIgnoreFunc; out AboveHeight: Single; out AboveGround: P3DTriangle): boolean; override;
Load the animation scenes. Must be called (this or some other loading routine like LoadFromFile) before you do almost anything with this object. Loaded changes to True after calling this.
Parameters
RootNodes
Models describing the "predefined" frames of animation. They must descend from TX3DRootNode.
For all nodes except the first: They are always owned by this class — that's needed, because actually we may do some operations on these models when building animation (including even freeing some RootNodes, if we will find that they are equivalent to some other RootNodes). They all must point to different objects.
You must supply at least one item here (you cannot make an animation from 0 items).
Times
Array specifying the point of time for each "predefined" frame. Length of this array must equal to length of RootNodes array.
ScenesPerTime
This says how many scenes will be used for period of time equal to 1.0. This will determine Scenes.Count. RootNodes[0] always takes Scenes[0] and RootNodes[High(RootNodes)] always takes Scenes[Scenes.High].
Note that if we will find that some nodes along the way are exactly equal, we may drop scenes count between — because if they are both equal, we can simply render the same scene for some period of time. This is an optimization, and you shouldn't notice it at all, since rendeting will be the same (but less memory-consuming).
Special value ScenesPerTime = 0 means that you want to have only the RootNodes you explicitly passed in the scene, not more. No more intermediate scenes will ever be created. This creates a trivial animation that suddenly jumps from one RootNode to the next at specified times. It may be useful if you already have generated a lot of RootNodes, densely distributed over time, and you don't need TCastlePrecalculatedAnimation to insert any more scenes.
EqualityEpsilon
This will be used for comparing fields, to decide if two fields (and, consequently, nodes) are equal. It will be simply passed to TX3DField.Equals.
You can pass here 0 to use exact comparison, but it's advised to use here something > 0. Otherwise we could waste display list memory (and loading time) for many frames of the same node that are in fact equal.
Load precalculated animation by playing a single VRML file with events (interpolators, TimeSensor and such working). Conceptually, this "records" interactive animation stored in VRML file into TCastlePrecalculatedAnimation precalculated animation.
ATimeBegin, ATimeEnd tell what time slice should be recorded. They will also set TimeBegin and TimeEnd properties.
Parameters
ScenesPerTime
tells with what density should the animation be recorded. See Load for ScenesPerTime, EqualityEpsilon precise documentation. Note that special value ScenesPerTime = 0 is interpreted here as "record only one, initial frame".
ProgressTitle
When <> '' we will use Progress.Init, Step, Fini to display nice progress of operation.
If you need more control over loading, for example you want to change some parameters at loading (for example, ScenesPerTime and EqualityEpsilon of kanim files), you should use more flexible (and less comfortable to use) LoadFromFileToVars class procedure (specialized for kanim files) or Load3DSequence (if you want to handle any files).
Loaded property changes to True after calling this.
Parameters
AllowStdIn
If True, then URL = '-' is understood as "standard input".
LoadTime
If True then loading changes current TimeLoop and TimeBackwards properties. Sometimes this is sensible (you want to allow control over them from the file), sometimes not (e.g. you set suitable values for them by code).
Note that, independent of this, you can always change TimeLoop and TimeBackwards properties later, since these properties are writeable at any time.
Smoothness
Scales the number of scenes created per second. Values > 1 make better quality but also use more memory. If this parameter isn't given, we use global AnimationSmoothness (which is by default 1, but may be globally changed).
Prepare all scenes for rendering. Basically, this calls PrepareResources(...) for all Scenes.
There's also a special memory (and prepare time) optimization used for prManifoldAndBorderEdges: we use the fact that animation scenes are "structurally equal", and so prepare and share one manifold edges information for all scenes.
ProgressStep = True is especially useful with this: we'll call Progress.Step then after preparing each scene. For portability, always check PrepareResourcesSteps, but for now this is just always equal ScenesCount.
function PrepareResourcesSteps: Cardinal; override;
Free resources for all scenes, it's useful if you know that you will not need some allocated resources anymore and you want to conserve memory use.
See TCastleSceneCore.FreeResource documentation for a description of what are possible resources to free.
procedure GLContextClose; override;
Close anything associated with current OpenGL context in this class. This calls GLContextClose on every Scenes[], and additionally may close some other internal things here.
This is TimeDuration * 2 if TimeBackwards, otherwise it's just TimeDuration. In other words, this is the time of the one "full" (forward + backward) animation.
If Time is < TimeBegin, always the first scene will be returned. If Time is between TimeEnd and TimeEnd + TimeDuration, then the animation will be played backwards. When Time is > TimeEnd + TimeDuration, again always the first scene will be returned.
Overloaded version with explicit Loop parameter ignores the TimeLoop property. This way you can force looping (or force not looping), regardless of the TimeLoop property, so also regardless of loop setting in kanim file.
function Scene(const Time: Single; const Loop: boolean): TCastleScene;
You can change properties of this object at any time, but beware that some changes may force time-consuming regeneration of some things (like OpenGL display lists) in the nearest Render of the scenes. So explicitly calling PrepareResources may be useful after changing these Attributes.
Note that Attributes may be accessed and even changed when the scene is not loaded (e.g. before calling Load / LoadFromFile). Also, Attributes are preserved between various animations loaded.
The sum of bounding boxes of all animation frames.
Result of this function is cached, which means that it usually returns very fast. But you have to call ChangedAll when you changed something inside Scenes[] using some direct Scenes[].RootNode operations, to force recalculation of this box.
procedure BeforeNodesFree;
Call this before directly freeing some VRML nodes in animation scenes.
procedure ChangedAll;
Call this when you changed something inside Scenes[] using some direct Scenes[].RootNode operations. This calls TCastleScene.ChangedAll on all Scenes[] and invalidates some cached things inside this class.
function Info( ATriangleVerticesCounts, ABoundingBox, AManifoldAndBorderEdges: boolean): string;
Returns some textual info about this animation. Similar to TCastleScene.Info.
We pass key and mouse events only if there's exactly one scene (ScenesCount = 1), as there's no sensible way of activating VRML/X3D events when TCastlePrecalculatedAnimation contains more than one scene. (Precalculated animation of this class, and interactive animation by TCastleSceneCore.ProcessEvents do not mix sensibly.)
So when ScenesCount = 1, we simply pass key and mouse events to the only Scene[0]. Be sure to turn on Scene[0].ProcessEvents := true if you want to make actual use of it.
Is the RootNode in first scene owned by this TCastlePrecalculatedAnimation instance? If yes, it will be freed at closing the animation. Otherwise, you are responsible for freeing it yourself (but you cannot do this while animation is loaded, anyway).
Initial world time, set by the ResetTimeAtLoad call. This can be useful for showing user time like "Animation Time: LoadTime + %f" on status bar.
0 means that starting Time was TimeBegin of the animation (0.0 in case of normal VRML files, usually 0.0 in case of Kanim). Note that even when TimeBegin <> 0 (for Kanim), we still set TimeAtLoad to 0, this is nicer to show to user.
Current time of the animation. Although you do not have to use it: you can always acccess any point in time of the animation by Scene. But sometimes tracking the current time here is most natural and comfortable.
When we have exactly one scene in Scenes, our methods (ResetTime, ResetTimeAtLoad and Update) will synchronize Scenes[0].Time always to the same value as our own Time. This makes time-dependent nodes (like TimeSensor, MovieTexture etc.) inside this scene work Ok.
Turn this on to treat specially the case when a single scene (Scenes.Count = 1) is loaded: we will set this scene's Static = False. This allows you to enable VRML/X3D events and dynamically change the scene in this very special case. The normal behavior, when we load many scenes (or when this property is False), is to set all children scenes Static = True.
Practically, this is useful only for tools like view3dscene, that want to have full VRML/X3D events when possible, and at the same time they want to load everything as TCastlePrecalculatedAnimation, for ease of coding.
To put it simply, just don't use this in normal programs – it's a hack.
Although Static can be later changed, but changing it (after loading) to False is expensive (needs ChangedAll, that also recalculates shape tree, forces shape octree and other recalculations). That's why this property is needed, it sets Static correctly before loading the contents.
Note that Scenes[0].TimePlaying, Scenes[0].TimePlayingSpeed do not matter when you're operating on the TCastlePrecalculatedAnimation level. They will not affect our Time, or even Scenes[0].Time, and they will not be synchronized with our values.
property TimePlayingSpeed: Single read FTimePlayingSpeed write FTimePlayingSpeed default 1.0;
Should collision checking check also last animation frame.
Regardless of this value, we always check collision with the first animation frame (FirstScene), of course only when FirstScene.OctreeCollisions is initialized, and only if GetCollides (which includes GetExists).
When CollisionUseLastScene is True, we will also check collision with the last animation frame's octree, i.e. LastScene.OctreeCollisions. (Of course, only if it's initialized, e.g. by adding ssDynamicCollisions to the LastScene.Spatial property.) So when CollisionUseLastScene, collision checking sees the animation as a sum of first and last frames geometry. CollisionUseLastScene is useful if the object is moving, but the move is very slight, so that the sum of first and last scenes geometry is good enough approximation of the whole geometry at any point of the animation.
Although it seems like a totally dumb way to check for collisions, it's suitable for many purposes (see e.g. uses on "castle hall" level), it's simple and not memory-consuming, and you don't have to take any action when animation frame changes (because Time changes don't change the colliding geometry, so the animation is static from the point of view of collision checking routines).
TODO: In the future other collision methods may be available. First of all, checking with sum of all bounding boxes, or with particular scene time box, should be available.