Extensible 3D (X3D)
Part 1: Architecture and base components
39 Followers component
The name of this component is "Followers". This name shall be used when referring to this component in the COMPONENT statement (see 7.2.5.4 Component statement).
This clause describes the Followers component of this part of ISO/IEC 19775. This includes how Followers are specified and how they behave. Table 39.1 provides links to the major topics in this clause.
The Follower nodes collectively enable the easy creation of smooth, natural transitions by simply setting a destination value at any time. On receiving a new destination value, the Follower generates a transition from the current value to the destination value.
In case a transition triggered by reception of a previous destination value is not yet finished while the new destination is received, both, the new and old transition is merged, so that a smooth animation is created, where the previous movement degrades and gradually becomes a movement towards the new destination which is then eventually being reached.
Follower nodes accomplish this by implementing finite impulse response (FIR) filters and infinite impulse response (IIR) filters from the field of system theory. Due to this filter distinction the Follower nodes are divided into Chaser nodes (FIR) and Damper nodes (IIR).
Like TimeSensor nodes Follower nodes usually send events at times where they do not receive input events. Their behaviour is completely determined by the events they receive from the scene graph itself at earlier times.
Follower nodes are not affected by their position in the transformation hierarchy nor are they affected by the state of containing Switch nodes, LOD nodes and other nodes that affect the visibility of their children.
In this node hierarchy diagram, abstract nodes are formatted in italic.
X3DFollowerNode
|
+--- X3DChaserNode
| +--- PositionChaser
| +--- OrientationChaser
| +--- PositionChaser2D
| +--- ScalarChaser
|
+--- X3DDamperNode
+--- PositionDamper
+--- OrientationDamper
+--- ColorDamper
+--- PositionDamper2D
+--- CoordinateDamper
+--- TexCoordDamper
X3DFollowerNode : X3DChildNode {
[S|M]F<type> [in] set_destination
[S|M]F<type> [out] value_changed
[S|M]F<type> [] initialDestination
[S|M]F<type> [] initialValue
[S|M]F<type> [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
}
The abstract node X3DFollowerNode forms the basis for all nodes specified in this clause. The data type place holder [S|M]F<type> evaluates to the same data type for all fields of a specialization of the abstract node class X3DFollowerNode.
An X3DFollowerNode maintains an internal state that consists of a current value and a destination value. Both values are of the same data type the term [S|M]F<type> evaluates into for a given specialization. It is the 'data type of the node'. In certain cases of usage the terms input and output fit better for destination value and current value, respectively.
Whenever the current value differs from the destination value, the current value gradually changes until it reaches the destination value producing a smooth transition. It generally moves towards the destination value but if a transition triggered by a prevous destination value is still in progress, for most parameter sets it takes a short while until the movement becomes a movement towards the new destination value.
The value_changed outputOnly field outputs the current value of the internal state.
The set_destination inputOnly field receives new destination values, resulting in the value_changed field sending values in most cases.
The initializeOnly fields initialDestination and initialValue initialize the internal state of the X3DFollowerNode. The current value receives the value of initialValue and the destination value receives the value of initialDestination. If both fields have the same values, the X3DFollowerNode sends that value through the value_changed field in a single event upon initialization. If both fields have different values, the X3DFollowerNode creates an animation from the value of initialValue towards the value of initialDestination. The shape of that transition is the same as if the current value internal state had always been at the value of initialValue and the node had just received the destination value.
With the set_value inputOnly field one can immediately force the current value towards a certain value. When the X3DFollowerNode receives a value on set_value, any current transition is stopped and the current value assumes that value. The value_changed field outputs that value and then moves towards the value currently set for the destination value. This animation has the same shape as if the current value had already been at the newly received value for a long time and the node had just received an event on set_destination carrying the value of the currently set destination value.
One can acheive various results by sending certain values to set_value, set_destination or both at the same time:
The isActive outputOnly field indicates the beginning and end of a transition. It sends TRUE before set_value begins animating and it sends FALSE after set_value has reached the destination or has been stoped by another means. When set_value receives an event while isActive is TRUE, isActive sends FALSE after value_changed has output the received value. If isActive is FALSE at that moment, it does not send anything.
Fields inherited from the X3DFollowerNode are formatted in grey color for clarity.
X3DChaserNode: X3DFollowerNode {
[S|M]F<type> [in] set_destination
[S|M]F<type> [out] value_changed
[S|M]F<type> [] initialDestination
[S|M]F<type> [] initialValue
[S|M]F<type> [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [] duration [0..∞]
}
An ideal X3DChaserNode calculates the output on value_changed as a finite impulse response (FIR) based on the events received on set_destination in the following way:
Each time an event is received on set_destination a transition An from the prevously received destination to the new destination is created according to the following equation. The data types of all variables are floating point numbers or integers in case of indices, except for dn, dn-1, An(t) and O(t). These variables have the data type of the node, which is SFVec3f, SFColor, and so on.
Here Tn is the point in time where the event has been recieved, D is the value of the duration field, dn is the new destination value received with the event, dn-1 is the value that was the destination before the event and R(x) is the core function of the filter:
All the transitions created for every event on set_destination are added together to form the output on value_changed.
l (ell) is the number of events received so far on set_destination. If k is set to 0, d-1 is the value of the initialValue field and d0 is the value of initialDestination. This way the initial transition determined by these two fields is produced.
Theoretically the start index k could be always set to 0 (zero) meaning that all set_destination events since initialization need to be stored. However, k can be increased without changing the result O(t) as long as the time stamp Tk-1 is more than D seconds before the current time stamp. This is due to the facts a) that after a period of D seconds (the duration field) the transitions An(t) are constantly dn - dn-1 and b) that dk-1 is the sum of all differences dn - dn-1 so far.
This way the ideal X3DChaserNode implementation has to remember the values and time stamps of all set_destination events received in the last period of duration seconds, plus the value received latest before that period. For calculating the current value of value_changed it uses that latest received value as a starting point (dk-1) and adds to it all transitions An(t) generated by the stored events.
The proposed, more optimal implementation could divide the tim-line into equidistant time-slots and store only the latest set_destination event received for each time-slot. This way a fixed length array could be used for describing the input during the period of the last duration seconds. This however could create little jumps in the animation created at value_changed because a set_destination event may cause the beginning of a transition being produced and may then be replaced by a later event recieved in the same time-slot. To avoid this, events should be associated with the end of the time-slot rather than with the time-stamp they are received at.
This of course causes the output reach the value received at set_destination up to the length of a time-slot later than is dictated by the duration field. To compensate, an implementation should subtract the length of a time-slot from duration and use the result for D. It might be bad finishing a bit too late, but finishing too early should never be a problem.
It is proposed that the implementation uses (about) 10 time-slots per duration duration.
The above diagram illustrates how an implementation calculates the output at an arbitrary point in time. The diagram uses only 4 time-slots per duration D. The period goes from the current time-stamp Now back by D seconds, not necessarily matching the grid of the time slots. The events d1 and d2 have happened before this period and are therefore summarized by the value of d2. The event d3 however falls into the period of D seconds. It is moved towards the end of the time-slot it falls into and generates the transition A3(t) with the amplitude d3 - d2. The event d4 gets ignored because it is followed by d5 in the same time-slot. Therefore only d5 generates a transition, which is A5(t). The amplitude of A5(t) is d5 - d3 because d4 got ignored. The output O(t) is thus calculated by adding:
When the current time-stamp has advanced until after the end of curve A3(t), which is when the time-slot containing event d3 is no longer part of the last D seconds, the start value for the addition d2 is replaced with d3 and the curve A3(t) is removed from the addition, so that O(t) = d3 + A5(t).
The above diagram uses 4 time slots per duration D. With the above recommendations of making D one time-slot shorter than the duration field specifies, this means that a time-slot is a 5th of what is specified by duration.
An X3DDamperNode abstract node uses the following signature:
X3DDamperNode: X3DFollowerNode {
[S|M]F<type> [in] set_destination
[S|M]F<type> [out] value_changed
[S|M]F<type> [] initialDestination
[S|M]F<type> [] initialValue
[S|M]F<type> [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The X3DDamperNode creates an IIR response that approches the destination value according to the shape of the e-function only assymptotically but very quickly.
An X3DDamperNode node is parametrized by the tau, order and tolerance fields. Internally it consists of a set of linear first-order filters that each process the output of the previous filter. The input of the first filter is fed by the values received on set_destination and the output of the last filter goes to the value_changed field.
The calculations of the output for the current time-stamp Tn for each filter are based on the output of that filter from the previous time-stamp Tn-1 and the current input using the following equation:
The field order specifies the number of such internal filters. Specifying 0 (zero) for order means that no filter is used. In this case the events received on set_destination are forwarded directly to output_changed. The larger the value for order the smoother the output on value_changed will be, but the more delay will be introduced. Since values larger than 5 don't introduce any more smoothing the range for order is limitted to a maximum of 5.
The field tau specifies the time-constant of the internal filters, and thus the speed the output of an X3DDamperNode responds to the input. Its value is assigned to the variable τ in the above equation. A value of 0 for tau means immediate response and the events received on set_destination are forwarded directly to output_changed. The field tau specifies how long it takes the output of an internal filter to reach the value of its input by 63 % (1 - 1/e). The remainder after that period is reduced by 63 % during another period of tau seconds and so on, provided that the input of the filter does not change. This behavior can be exposed if order is set to 1 (one).
Since the output of an X3DDamperNode approaches the input value only asymptotically, there must be a means to determine when the destination value can be assumed to be reached and the node can stop emitting values and set isActive to FALSE. This is governed by the tolerance field. if tolerance is left at its default value -1 then it's up to the browser implementation to find a good way for detecting the end of a transition. Browsers that do not have an elaborate algorithm can just use .001 as the tolerance value instead. If a value larger than 0 (zero) is specified for tolerance then the browser must calculate the difference between output and input for each internal filter being used and stop the animation only when all filters fall below that limit or are equal to it. If 0 (zero) is specified for tolerance then a transition should be stopped only if input and output match exactly for all internal filters. This can for example happen if set_value receives an event.
Implementations shall do the test for end of transition before they calculate the new output value. If the difference between current value and destination value is below the tolerance they shall output the destination value at value_changed and then set isActive to FALSE. Otherwise they calculate an update for value_changed.
PositionChaser: X3DChaserNode {
SFVec3f [in] set_destination
SFVec3f [out] value_changed
SFVec3f [] initialDestination
SFVec3f [] initialValue
SFVec3f [in] set_value
SFBool [out] isActive
SFTime [] duration [0..+∞]
SFNode [in,out] metadata NULL [X3DMetadataObject]
}
The PositionChaser animates transitions for 3D vectors. If the value_changed field is routed to a translation field of a Transform node that contains an object, then whenever the set_destination field receives a 3D position the PositionChaser node moves the object from its current position to the newly set position. It creates a smooth transition that ends duration seconds after the last position has been received.
When set_value receives a position, any possibly performed transition is stopped and the object jumps directly to the given position. With the field initialValue the initial position of the object can be set. The field initialDestination should be set to the same value unless a transition to a certain position is to be created right after the scene is loaded or right after the PositionChasernode is created dynamically.
OrientationChaser: X3DChaserNode {
SFRotation [in] set_destination
SFRotation [out] value_changed
SFRotation [] initialDestination
SFRotation [] initialValue
SFRotation [in] set_value
SFBool [out] isActive
SFTime [] duration [0..+∞]
SFNode [in,out] metadata NULL [X3DMetadataObject]
The OrientationChaser animates transitions for orientations. If the value_changed field is routed to a rotation field of a Transform node that contains an object, then whenever the set_destination field receives an orientation the OrientationChaser node rotates the object from its current orientation to the newly set orientation. It creates a smooth transition that ends duration seconds after the last orientation has been received.
When set_value receives an orientation, any possibly performed transition is stopped and the object jumps directly to the given orientation. With the field initialValue the initial orientation of the object can be set. The field initialDestination should be set to the same value unless a transition to a certain orientation is to be created right after the scene is loaded or right after the OrientationChasernode is created dynamically.
In order to implement the OrientationChaser node equations 1, 2 and 3 can be combined to equation 6:
It leads to the following loop denoted in peseudo code:
var Result= dk-1;
for(var n from k to l) {
var Delta= dn - dn-1;
Result+= Delta * R(...);
}
O(t)= Result;
Since dk-1, dn, dn-1 and thus Result contain rotation values (SFRotation), the above code must be converted to use operations availabe for rotations. This can be acheived using the slerp operation. For the following let slerp(A, B, t) be a function that calculates the linear spherical interpolation from A to B by the amout t. Let also Core(.) be a function that calculates R(...) and let Buffer be an array so that Buffer[i] evaluates to di. Then the above loop can be implemented as:
var Result= Buffer[k-1];
for(var n from k to l) {
var Delta= Buffer[n-1].inverse().multiply(Buffer[n]);
Result= slerp(Result, Result.multiply(Delta), Core(...));
}
O(t)= Result;
PositionChaser2D: X3DChaserNode {
SFVec2f [in] set_destination
SFVec2f [out] value_changed
SFVec2f [] initialDestination
SFVec2f [] initialValue
SFVec2f [in] set_value
SFBool [out] isActive
SFTime [] duration [0..+∞]
SFNode [in,out] metadata NULL [X3DMetadataObject]
The PositionChaser2D animates transitions for 2D vectors. Whenever the set_destination field receives a 2D vector the value_changed creates a transition from its current 2D vector value to the newly set value. It creates a smooth transition that ends duration seconds after the last 2D vector has been received.
When set_value receives a 2D vector, any possibly performed transition is stopped and value_changed sends this value immediately, creating a jump. With the field initialValue the initial value of value_changed can be set. The field initialDestination should be set to the same value unless a transition to a certain 2D vector value is to be created right after the scene is loaded or right after the PositinChaser2Dnode is created dynamically.
ScalarChaser: X3DChaserNode {
SFFloat [in] set_destination
SFFloat [out] value_changed
SFFloat [] initialDestination
SFFloat [] initialValue
SFFloat [in] set_value
SFBool [out] isActive
SFTime [] duration [0..+∞]
SFNode [in,out] metadata NULL [X3DMetadataObject]
}
The ScalarChaser animates transitions for single float values. Whenever the set_destination field receives a floating point number the value_changed creates a transition from its current value to the newly set number. It creates a smooth transition that ends duration seconds after the last number has been received.
When set_value receives a floating point number, any possibly performed transition is stopped and value_changed sends this value immediately, creating a jump. With the field initialValue the initial value of value_changed can be set. The field initialDestination should be set to the same value unless a transition to a certain value value is to be created right after the scene is loaded or right after the ScalarChasernode is created dynamically.
PositionDamper: X3DDamperNode {
SFVec3f [in] set_destination
SFVec3f [out] value_changed
SFVec3f [] initialDestination
SFVec3f [] initialValue
SFVec3f [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The PositionDamper animates transitions for 3D vectors. If the value_changed field is routed to a translation field of a Transform node that contains an object, then whenever the set_destination field receives a 3D position the PositionDamper node moves the object from its current position to the newly set position. It creates a transition that approaches the newly set position asymptotically during a time period of approximately three to four times the value of the field tau depending on the desired acuracy and the value of order. Through this asymptotic approach of the destination value a very smooth transition is created. The order field specifies the smoothness of the transition.
When set_value receives a position, any possibly performed transition is stopped and the object jumps directly to the given position. With the field initialValue the initial position of the object can be set. The field initialDestination should be set to the same value unless a transition to a certain position is to be created right after the scene is loaded or right after the PositionDampernode is created dynamically.
OrientationDamper: X3DDamperNode {
SFRotation [in] set_destination
SFRotation [out] value_changed
SFRotation [] initialDestination
SFRotation [] initialValue
SFRotation [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The OrientationDamper animates transitions for orientations. If the value_changed field is routed to a orientation field of a Transform node that contains an object, then whenever the set_destination field receives a 3D position the PositionDamper node moves the object from its current position to the newly set position. It creates a transition that approaches the newly set position asymptotically during a time period of approximately three to four times the value of the field tau depending on the desired acuracy and the value of order. Through this asymptotic approach of the destination value a very smooth transition is created. The order field specifies the smoothness of the transition.
When set_value receives a position, any possibly performed transition is stopped and the object jumps directly to the given position. With the field initialValue the initial position of the object can be set. The field initialDestination should be set to the same value unless a transition to a certain position is to be created right after the scene is loaded or right after the PositionDampernode is created dynamically.
In order to implement the OrientationDamper node equation 5 has to be calculated for each internal filter. For SFRotation values the equation is equivalent with the following term:
output= input.slerp(output, alpha);
where:
output: on or on-1, respectively
input: dn
alpha: e-ΔT / τ
ColorDamper: X3DDamperNode {
SFColor [in] set_destination
SFColor [out] value_changed
SFColor [] initialDestination
SFColor [] initialValue
SFColor [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The ColorDamper node creates transitions for color values. Calculations shall take place in HSV space.
The ColorDamper animates color values. Whenever the set_destinationfield receives a color the ColorDamper node creates a transition from the current color to the newly set color. The transition created approaches the newly set position asymptotically during a time period of approximately three to four times the value of the field tau depending on the desired acuracy and the value of order. The order field specifies the smoothness of the transition.
When set_value receives a color, any possibly performed transition is stopped and value_changed sends this value immediately, creating a jump to the new color. With the field initialValue the initial color can be set. The field initialDestination should be set to the same value unless a transition to a certain color is to be created right after the scene is loaded or right after the PositionDampernode is created dynamically.
PositionDamper2D: X3DDamperNode {
SFVec2f [in] set_destination
SFVec2f [out] value_changed
SFVec2f [] initialDestination
SFVec2f [] initialValue
SFVec2f [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The PositionDamper2D animates transitions for 2D vectors. Whenever the set_destination field receives a 2D vector the value_changed creates a transition from its current 2D vector value to the newly set value. It creates a transition that approaches the newly set 2D vector asymptotically during a time period of approximately three to four times the value of the field tau depending on the desired acuracy and the value of order. The order field specifies the smoothness of the transition.
When set_value receives a 2D vector, any possibly performed transition is stopped and value_changed sends this value immediately, creating a jump. With the field initialValue the initial value of value_changed can be set. The field initialDestination should be set to the same value unless a transition to a certain 2D vector value is to be created right after the scene is loaded or right after the PositinChaser2Dnode is created dynamically.
CoordinateDamper: X3DDamperNode {
MFVec3f [in] set_destination
MFVec3f [out] value_changed
MFVec3f [] initialDestination
MFVec3f [] initialValue
MFVec3f [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The CoordinateDamper animates transitions for an array of 3D vector. These could be the coordinates of a mesh. Whenever the set_destination field receives an array of 3D vectors, value_changed begins sending an array of the same length, where each element moves from its current value towards the value at the same position in the array received. Each element approaches its destination value asymptotically during a time period of approximately three to four times the value of the field tau depending on the desired acuracy and the value of order. The order field specifies the smoothness of the transition. The transition endes when all elements have reached their destination.
When set_value receives an event, any possibly performed transition is stopped and value_changed sends this array immediately, creating a jump. With the field initialValue the initial value of value_changed can be set. The field initialDestination should be set to the same value unless a transition to a certain 2D vector value is to be created right after the scene is loaded or right after the CoordinateDampernode is created dynamically.
The MFVec3f arrays that are sent to the set_destination or set_value field must have the same length (number of elements). This length of the arrays must not change over time. Values assigned to initialDestination or initialValue must either be an empty array or an array with the same number of elements as is sent to the set_destination or set_value fields. In any other case the behavior is not defined.
TexCoordDamper: X3DDamperNode {
MFVec2f [in] set_destination
MFVec2f [out] value_changed
MFVec2f [] initialDestination
MFVec2f [] initialValue
MFVec2f [in] set_value
SFBool [out] isActive
SFNode [in,out] metadata NULL [X3DMetadataObject]
SFTime [in,out] tau [0..+∞]
SFInt32 [] order [0..5]
SFFloat [in,out] tolerance -1 or [0..∞]
}
The TexCoordDamper node creates transitions for arrays of 2D vectors. The same description applies as is given for the CoordinateDamper node, except that 2D vectors are animated instead of 3D vectors.
![]()