Animating Flowing Water

One of the things you can't do in Spazz is an animated TextureTransform. For that you will need to venture into the world of hand coding.

Before we start, let me say that the people who created the VRML 2.0 specification never intended that people should code VRML by hand. The standard is to guide software companies as they create VRML editors.

However, if you understand how to read and write native VRML code, you can make little tweaks or add behaviors that your VRML editor doesn't support. This is one of those cases. So, make your fountain, add a transparent surface for the water, then use this technique to add the ripples.

You can go take a look at the finished sample here: water.wrl



#VRML V2.0 utf8



### Don't be afraid of this code. It won't byte. :-) It looks long, but it's

### mostly my comments. The actual working code is about half a page.

###

### It's a little daunting at first to look at raw HTML code, but when you

### read through it a line at a time, you'll find it's actually pretty simple

### to follow. You'll recognize a lot of the numbers and words in context, because 

### they're the same values you use to make objects in Spazz 3D (for example,

### when you look at the Cylinder description below, it has values for height

### and radius, the same as the Cylinder Properties page in Spazz3D.

###

### You can look at any of your VRML models in code similar to this. 

### In Spazz3D, go to the File Menu and choose VRML Export Options...

### Turn off (un-check) the box that says GZip WRL File.

### Now, when you Export the Spazz model as VRML, the file will be a text file

### like this one. You can open the file with notepad and read through the code.

### You can copy this file and paste it to the end to put this water object

### into your world.

###

### The Mall Checkpage has a utility that will GZip the file for you when you're

### finished making your changes. You always want to GZip a copy of your final

### version (and keep the original as text so you can edit it!). A GZip'd world

### file is 90% smaller than one that hasn't been GZip'd (i.e., a 40K file ends

### up being only 4K).



### Here's the code, with comments.

###

### Define the shape that the water texture will flow across



DEF waterTransform Transform {

  rotation 1 0 0 .2

  children [

    DEF waterShape Shape {

      appearance Appearance {

        texture ImageTexture {

          url "waterripples.jpg"

        }



# Give the textureTransform node a name so that we can refer to it

#   with a ROUTE statement (at the end of this example). TextureTransform

#   is similar to the Transform node for objects, but the values are set

#   in only two dimensions, x and z. There's no need to set a y coordinate,

#   because the image is flat. Here, I'm setting the scale to 10 10, which

#   means that the texture will be repeated ten times on the x axis and ten

#   times on the z axis. This gives me lots of water ripples from a very

#   small image.



        textureTransform DEF waterTextureTransform TextureTransform {

          scale 10 10

        }

      }

      geometry Cylinder {

        height .001

        radius 1

      }

    }

  ]

}



# This creates a timer that runs in a 10 second loop. It starts automatically

#    when the file is opened.



DEF waterTimer TimeSensor {

	cycleInterval 10

	loop TRUE

}



# This script is an Interpolator - it receives information in one form,

#   such as a fraction of time, and returns a value that can be used by

#   it's target object. In this case, it sends back two numbers that set

#   the X and Z positions of the texture image on the cylinder.

#   We have to use our own handmade Interpolator, because the VRML spec

#   doesn't include one that is composed of two floating point numbers.

#   We'll send it a fraction between 0 and 1 that represents what percentage

#   of time has elapsed on the timer. We use that value to create a Single

#   Field Vector with Two Floating point values (SF2Vec2f). That means it's

#   one variable that holds two numbers. The numbers are the current fraction

#   of time that's elapsed on the sensor, and the number 0.



DEF textureInterpolatorScript Script {

      eventIn SFFloat currentFraction

      eventOut SFVec2f new_translation

      directOutput TRUE

      url "javascript:

        function currentFraction() {

         new_translation = new SFVec2f(currentFraction, 0);

        }

      "

}



# When the timer advances by a fraction of the total time loop, send the current fraction

#   value to the interpolator script.



ROUTE waterTimer.fraction_changed TO textureInterpolatorScript.currentFraction



# Send the new translation (x = elapsed time on the timer, z = 0)

#   to the waterTextureTransform. Move (i.e., translate) the image by that fraction for x.

#   For example, if the timer has run halfway through its cycle, it will send the value

#   .5 to the interpolator. The interpolator returns the value (.5, 0).

#   The TextureTransform value is set to x=.5, z=0. This moves the image to the halfway

#   point on the x axis of the surface of the item to which the texture is applied 

#   (halfway across the cylinder, in this example.



ROUTE textureInterpolatorScript.new_translation TO waterTextureTransform.set_translation


Back to Creating Objects for Cybertown

Send your comments on this page to Epistomolus.

Updated February 23, 2007