I wanted to preserve use of the original texture images with their vertically stacked animation frames, but also to have the animation work in a standalone x3d file that is inlined into the web page.
V-Chat geometry is stored in world.wdb files which very usefully include the names of animated textures in the form AnimXXYY, where XX is the number of animation frames in the texture and YY is the frame duration in 100ths of a second e.g. Anim0430 indicates a 4 frame animation, each frame held for 0.3 seconds.
The first step is to add a TextureTransform node to the animated texture image that scales it by a factor of XX on the y axis (which means a scale factor of 1/XX):
<TextureTransform DEF='ttAnim0430' scale='1 0.25' translation='0 0' />
Next a TimeSensor node is added that repeats at the right rate. In this case 4 frames at 0.3 seconds needs a 1.2 second cycleInterval:
<TimeSensor DEF='tsAnim0430' cycleInterval='1.2' loop='true' />
The TimeSensor output gets turned into the required texture translation coordinates by using a PositionInterpolator. This outputs a three dimensional coordinate (an SFVec3f), but it turns out that this can be routed to the two dimensional translation attribute (an SFVec2F) with no ill effects provided each keyValue is given as an x y z triple. (Lesson learned: much time wasted messing around with a CoordinateInterpolator, which doesn’t work). The example here needs:
<PositionInterpolator id='piAnim0430' DEF='ciAnim0430' key='0 0.25 0.25 0.5 0.5 0.75 0.75 1' keyValue='0 0 0 0 0 0 0 1 0 0 1 0 0 2 0 0 2 0 0 3 0 0 3 0'/>
Finally, a couple of ROUTE nodes plumb everything together:
<ROUTE fromNode='tsAnim0430' fromField='fraction_changed' toNode='piAnim0430' toField='set_fraction'/> <ROUTE fromNode='piAnim0430' fromField='value_changed' toNode='ttAnim0430' toField='translation'/>