Interpolation component - extensions

Extensions introduced in Castle Game Engine related to the interpolation, which allows to animate various X3D fields.

See also documentation of supported nodes of the Interpolation component and X3D specification of the Interpolation component.

Contents:

1. ColorSetInterpolator

As an extension, we add the ColorSetInterpolator (Pascal API: TColorSetInterpolatorNode) node, that generates MFColor values.

ColorSetInterpolator : X3DInterpolatorNode {
  SFNode     [in,out]      metadata         NULL    # [X3DMetadataObject]; defined by X3DNode
  SFFloat    [in]          set_fraction             # defined by X3DInterpolatorNode
  MFFloat    [in,out]      key              []      # defined by X3DInterpolatorNode
  MFColor    [in,out]      keyValue         []    
  MFColor    [out]         value_changed          
}

The number of items in the "keyValue" field should be a multiple of the number of items in the "key" field, that is keyValue.count = key.count * singleValueChangedCount. When the "set_fraction" input event is received, we linearly interpolate the colors, and the "value_changed" event is generated with a set of singleValueChangedCount colors.

This works and looks exactly like other interpolation nodes. It is similar to CoordinateInterpolator, but generates colors. It is similar to ColorInterpolator, but generates many values. Colors are interpolated in HSV space.

Useful to interpolate e.g. Background.skyColor values, or Color.color values.

2. VectorInterpolator

As an extension, we add the VectorInterpolator (Pascal API: TVectorInterpolatorNode) node, that generates MFFloat values. This is compatible with InstantReality.

VectorInterpolator : X3DInterpolatorNode {
  SFNode     [in,out]      metadata         NULL    # [X3DMetadataObject]; defined by X3DNode
  SFFloat    [in]          set_fraction             # defined by X3DInterpolatorNode
  MFFloat    [in,out]      key              []      # defined by X3DInterpolatorNode
  MFFloat    [in,out]      keyValue         []    
  MFFloat    [out]         value_changed          
}

The number of items in the "keyValue" field should be a multiple of the number of items in the "key" field, that is keyValue.count = key.count * singleValueChangedCount. When the "set_fraction" input event is received, we linearly interpolate the floats, and the "value_changed" event is generated with a set of singleValueChangedCount floats.

Useful to interpolate e.g. by ElevationGrid.set_height.

3. CubicBezierPositionInterpolator and CubicBezier2DOrientationInterpolator

CubicBezierPositionInterpolator : X3DInterpolatorNode {
  SFNode     [in,out]      metadata         NULL    # [X3DMetadataObject]; defined by X3DNode
  SFFloat    [in]          set_fraction             # defined by X3DInterpolatorNode
  MFFloat    [in,out]      key              []      # defined by X3DInterpolatorNode
  MFVec3f    [in,out]      keyValue         []    
  MFVec4f    [in,out]      controlPoints    []    
  SFVec3f    [out]         value_changed          
}
CubicBezier2DOrientationInterpolator : X3DInterpolatorNode {
  SFNode     [in,out]      metadata         NULL    # [X3DMetadataObject]; defined by X3DNode
  SFFloat    [in]          set_fraction             # defined by X3DInterpolatorNode
  MFFloat    [in,out]      key              []      # defined by X3DInterpolatorNode
  MFFloat    [in,out]      keyValue         []    
  MFVec4f    [in,out]      controlPoints    []    
  SFVec3f    [in,out]      axis             []    
  SFRotation [out]         value_changed          
}

These nodes interpolate using cubic Bezier curves. They are similar to standard X3D interpolator nodes (that use linear interpolation between values) and to X3D Spline*Interpolator nodes (that use Catmull-Rom splines), but these ones use cubic Bezier curves.

  1. CubicBezierPositionInterpolator (Pascal API: TCubicBezierPositionInterpolatorNode) is equivalent to standard PositionInterpolator(Pascal API: TPositionInterpolatorNode), except using cubic Bezier curve instead of linear interpolation.
  2. CubicBezier2DOrientationInterpolator (Pascal API: TCubicBezier2DOrientationInterpolatorNode) is equivalent to standard OrientationInterpolator(Pascal API: TOrientationInterpolatorNode), except using cubic Bezier curve instead of linear interpolation, and simplifying parameters for rotations in 2D.

These nodes are particularly useful when converting Spine animations with curve interpolation to X3D. That is because Spine uses cubic Bezier curves for interpolation too, with controls points defined in a similar way. See Spine JSON docs, paragraphs The Bézier curve array has 4 elements.... Our Spine reading code automatically uses these nodes where necessary.

Every CubicBezier*Interpolator node has an additional field controlPoints (number of 4D vectors) describing the Bezier curves between they key values. Between every 2 values (on keyValue field) there are 2 additional 2D points (Bezier control points), packed together as 4D vector. So the count of controlPoints must be n - 1 (additional values are silently ignored, and when missing we assume linear interpolation) where n is the count of keys (and keyValues). Let us call every 4 numbes as CX1, CY1, CX2, CY2. Values CX1 and CX2 determine the position of handle between previous (0) and next (1) key. Values CY1 and CY2 determine the output value, where 0 is the value at previous key and 1 is the value at next key.

For CubicBezier2DOrientationInterpolator, the way we handle 2D rotation interpolation requires additional explanation. The final rotation (expressed as SFRotation) is always calculated as a rotation around constant vector (in axis field), with rotation angle calculated by interpolating (with cubic Bezier curves) the angles defined in keyValue field (as radians). This way CubicBezier2DOrientationInterpolator is very efficient for 2D rotations.

Note that you could also use NurbsPositionInterpolator and NurbsOrientationInterpolator to interpolate using Bezier curves (see NURBS nodes), since NURBS equations already allow to specify Bezier curves. However, this would be less efficient to calculate, as we don't know then that the NURBS "knot" represents a Bezier curve. We can calculate resuls faster knowing that it's a Bezier cubic curve, not anything more generic. Additionally, CubicBezier2DOrientationInterpolator makes extra optimization, knowing that rotation is in 2D.

4. OrientationInterpolator2D

OrientationInterpolator2D : X3DInterpolatorNode {
  SFNode     [in,out]      metadata         NULL    # [X3DMetadataObject]; defined by X3DNode
  SFFloat    [in]          set_fraction             # defined by X3DInterpolatorNode
  MFFloat    [in,out]      key              []      # defined by X3DInterpolatorNode
  MFFloat    [in,out]      keyValue         []    
  SFVec3f    [in,out]      axis             0 0 1 
  SFRotation [out]         value_changed          
}

Like a standard OrientationInterpolator(Pascal API: TOrientationInterpolatorNode), but with simplified parameters for rotations in 2D. The axis (in axis field) is constant, only the amount of rotation (single float) changes (according to keyValue). This makes it both more efficient, and the interpolation more obvious (no need for "slerp").