And now here's Program no. 8.

b8.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       translation 0.406737 0.913545 0
        children
        [       Shape
                {       appearance Appearance
                        {       material Material
                                {       diffuseColor     1 0.956773 0.5
                                }
                        }
                        geometry Cube { }
                },
        ]
}

Program no. 8 will display a colored cube on the screen. There is a great difference between the way you write VRML 1.0 and VRML 2.0 code. While in VRML 1.0 we simply typed in Cube to display it on the screen, you can't do that in VRML 2.0. In VRML 2.0 you can do more than just type in Cube and create one, you can flesh out the program by adding more features, things that a simple Cube geometry can't manage. Cube is a part of the geometry. The node Shape has two fields, appearance and geometry. geometry could represent anything , a Cube, a Sphere, a Cylinder, a Cone or even your own bitmaps. In our program we have said that the geometry is equal to Cube. appearance doesn't have to be present. The field appearance has to look a lot like the node Appearance ( Is this confusing or what ?! ). Just remember , the one with the capital A is the node, the other one is a field in Shape. An appearance in turn contains other fields ( members ). In this case we have material ( with a small m ) as a field in appearance . Material is a node in which we're initializing just one member, i.e. diffuseColor. To understand this better you can remove the entire Appearance and just keep the geometry. What we want is a colorful and vivid Cube. In this program you can have multiple shapes (geometry's ) which will be children. We'll explain all this later, so don't worry yourself about it. In this program the Transform is named uuu. You can also remove the translation (the third line) and try out the program like that. The more you experiment, the faster you'll learn.

b88.wrl

#VRML V2.0 utf8
DEF uuu Transform 
{       translation 0.406737 0.913545 0
        children [
                        Shape 
                        {       appearance Appearance 
                                {       material Material 
                                        {       diffuseColor     1 0.956773 0.5
                                        }
                                        texture ImageTexture 
                                        {       url     "evening.jpg"
                                        }
                                }
                                geometry Cube { }
                        },
                    ]
}

In this example we wanted things to improve in Appearance, so we added another member, texture, which stands for the image evening.jpg and which looks like the node imageTexture. Instead of evening.jpg you could have made the url equal to any other .gif file, or a .bmp file or a .jpg file. When you load the file you'll see a cube appear with the image evening.jpg wrapped around it. These are techniques that you can use to make your applications work better.

Had a programmer for dinner once. Tasted like chicken......

Take a deep breath before you start Program no. 9 .

b9.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       translation 0.406737 0.913545 0
        children
        [       Shape
                {       appearance Appearance
                        {       material Material
                                {       diffuseColor     1 0.956773 0.5
                                }
                        }
                        geometry Cube { }
                },
        ]
}

DEF t1 TimeSensor
{       discrete    TRUE
        loop        TRUE
        enabled     TRUE
        cycleInterval       0.08
}    

DEF s1 Script
{       url "b9.class"
        scriptType "javabc"
        eventIn  SFTime  fly
        field   SFNode  uu USE uuu
}

ROUTE  t1.time TO s1.fly
b9.java
import vrml.*;
import java.io.*;
public class b9 extends Script
{       SFNode u = (SFNode)getField("uu");
        Transform ut = (Transform) u.getValue();
        DataOutputStream d;
        public b9()
        {       try
                {       FileOutputStream f = new FileOutputStream("b9.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");
        }
        public void fly(SFTime t, SFTime now)
        {       float m[] = new float[ 3 ];
                m[ 0 ] = 0.025f;
                m[ 1 ] = 0.025f;
                m[ 2 ] = 0.025f;
                ut.translate( m, ut.modeRelative );
                abc("In Fly\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();
                } catch ( Exception e ) {}
        }
}

Program no. 9 is a bit on the large size, but stick with us and we'll see you through this !!

In it we have a TimeSensor. Now because the enabled is TRUE, the function fly will be called constantly. The most notable thing about program no. 9 is the Script. Here uu (you can name it anything you like) is a field, which is an SFNode. Which node it represents is decided by what it is followed by e.g. if you say USE uuu, the field uu will stand for the node uuu, which in turn stands for Transform. This implies that you can have as many fields as your heart desires.

Whenever you see a field in the Script, it means that we want to access a node from a Java program. In this program, the Transform is a node called uuu which we want to call from our Java program.

In a nutshell, whenever you want to access any of the nodes from your Java program, you have to set them as fields in the script

Now follow closely our every move, as we unravel the mysteries of the (s)crypt !!

In the file b9. java , we create an object u that looks like SFNode. Remember that every object that looks like a Script has a member called getField. In the node Script, present in the .wrl file , we've called the field uu. Now because we've named the field uu, we place it in double inverted commas and supply uu to the getField function. u in my Java program seems to stand for the Transform, uuu, but in actuality u does not actually stand for a Transform ( An object unique to the SONYJava API ) it actually stands for a SFNode. So we use the SFNode u and say u.getValue which will return a transform because uu itself stands for a Transform.

By now, you'll have probably realized that accessing a field in the Script is a two stage process in our Java program.

The first thing you need is a handle to a field, you can get your hands on this by using the getField function. Since the field is an SFNode, it's handle should also be declared as an SFNode. After going through this procedure, you use getValue to get the handle to the node that the field represents. For example, if the field stood for the Transform, it would return a handle to the Transform, if the field stands for a Shape, it'll return a handle to the Shape. If you still don't get what we're talking about, re-read and re-re-read this explanation, it's important.

Once you've understood this, the rest becomes very easy. In the function fly, we create an object which is an array of 3 floats. We initialize the 3 elements of the array to any value, and use the function translate. Now because translate is a function in a Transform we use the Transform variable ut and type in ut.translate. This function asks for an array that contains the values of x, y and z axis along which you want to the movement of the Cube to take place. mode Relative is a static variable in a Transform class. In this variable, the values of the position of the Cube are relative and not static, i.e., the values of the position of the cube keep increasing, until finally the Cube just waltzes off the screen.

Now once again remember Transform is not a part of the original specification. Its only present in the SONY extensions, though we hope it's adopted by everyone.

This was an example of the usefulness of the extensions added by Sony to make our lives easier.

These are the steps we need to follow to get a handle to a Node in a .wrl file.

  1. ) The first thing you need to do is decide which Node you want to change in your Java program. So you use DEF to give a name to your node, in this case, our node is a Transform, named uuu. ( Why ? because we want to move our geometry, that's why. ).

  2. ) Next, you have to use field in the node Script and create a variable, the data type of which should be SFNode. We have used the reserved word field and have named uu. This means that the field uu, will now represent the Transform ( refer to line no.5 in the Script ).

  3. ) Then in your Java program, you first access the field, and from the field you access the required node, i.e. Transform.

  4. ) Then you use functions in the class by means of which you can manipulate the objects in VRML 2.0.

If you think this is complicated, then you ain't seen nothing yet !!.

Now lets take a nice long look at Program no. 10.

b10.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       translation 0.406737 0.913545 0
        children
        [       Shape
                {       appearance Appearance
                        {       material Material
                                {       diffuseColor     1 0.956773 0.5
                                }
                        }
                        geometry Cube { }
                },
        ]
}
DEF t1 TimeSensor
{       discrete    TRUE
        loop        TRUE
        enabled     TRUE
        cycleInterval       0.08
}    

DEF s1 Script
{       url "b10.class"
        scriptType "javabc"
        eventIn  SFTime  fly
        field   SFNode  uu USE uuu
}

ROUTE  t1.time TO s1.fly
b10.java
import vrml.*;
import java.io.*;
public class b10 extends Script
{       SFNode u = (SFNode)getField("uu");
        Transform ut = (Transform) u.getValue();
        DataOutputStream d;
        float i = 0;
        public b10()
        {       try
                {       FileOutputStream f = new FileOutputStream("b10.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");
        }
        public void fly(SFTime t, SFTime now)
        {       float r[],o[];
                r = new float[4];
                r[0] = 1.0f;r[1] = 1.0f;r[2] = 1.0f;
                r[3]=i;
                i=i+0.2f;
                o = new float[3];
                o[0] = 0.0f;o[1] = 0.0f;o[2] = 0.0f;
                ut.rotateAroundLineDegree(r, o,ut.modeAbsolute);
                abc("In Fly\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();
                } catch ( Exception e ) {}
        }
}

Program no. 10 is almost exactly the same as its predecessor, the only difference is that in fly ( which is called constantly ) we have created an array of floats. The array is 4 members large because now we want to rotate the Cube. You have to specify the exact amount by which the geometry should be rotated, so you have to give the values of x,y,z and the angle of rotation. We've used a function named rotateAroundLinedegree ( try saying that in one breath !! ) and in it we've specified how much you want to rotate the image, the original point from which you want to rotate the cube (0,0,0). modeAbsolute is a static variable like modeRelative and it's been used because we want to Cube to pirouette in the same position, not dance all over the screen. Try out this program in its original form first, then make some alterations of your own and you'll understand what we're talking about. This is another of the many functions in Transform. If you want to know more, go check out one of the HTML files.

So far we have restricted ourselves to testing and using the time sensor. Not lets take a look at another type of sensor, the touch sensor.

b11.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       children
        [       DEF ts TouchSensor {}
                children
                [       Shape
                        {       appearance Appearance
                                {        material Material {}
                                }
                                geometry Cylinder {}
                        },
                ]
        ]
}
DEF s1 Script
{       eventIn SFBool  c1
        url     "b11.class"
        scriptType      "javabc"
}

ROUTE ts.isActive TO s1.c1
b11.java
import vrml.*;
import java.io.*;
public class b11 extends Script
{       DataOutputStream d;
        public b11()
        {       try
                {       FileOutputStream f = new FileOutputStream("b11.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");      
        }
        public void c1(SFBool ev, SFTime now)
        {       abc("In c1\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();
                } catch ( Exception e ) {}
        }
}

The alpha version of the browser has only implemented two sensors as of now, the time sensor and the touch sensor. In this program we have a TouchSensor named, of all things, ts. We also have a Transform named uuu. We've also created a Cylinder around which we can wrap a picture, if you so desire.

In this program we have two children (we'll explain the whole concept on 'children' later, for now it's enough for you to know that the stork brought these in !!!). In the children I have two fields, one is a TouchSensor and the other is a Cylinder. Now, in the node Script, c1 is an eventIn, which means that it will receive an event ( all explained before).

The eventIn is of data type SFBool and therefore it can only hold a true or a false. Next we've typed in ROUTE ts.isActive that means the touch sensor will now generate the event is.Active. This event is generated whenever you click with the mouse on whatever object the TouchSensor is presently monitoring. In this case it is the Cylinder. So when you click and release the mouse button, the TouchSensor will generate an is.Active event which we are routing to c1 in our script. This is positive proof that c1 is called twice. There functions are the reason behind the assertion that VRML 2.0 will let you create applications impossible or extremely difficult to create otherwise. Even if the object was jumping all over the screen, you won't have to write any more code, the Touch Sensor will figure out where the object is and act accordingly.

On to the next example.

b111.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       children
        [       DEF ts TouchSensor {}
                children
                [       Shape
                        {       appearance Appearance
                                {        material Material {}
                                }
                                geometry Cylinder {}
                        },
                ]
        ]
}
DEF s1 Script
{       eventIn SFBool  c1
        url     "b11.class"
        scriptType      "javabc"
}

ROUTE ts.isOver TO s1.c1
b111.java
import vrml.*;
import java.io.*;
public class b11 extends Script
{       DataOutputStream d;
        public b11()
        {       try
                {       FileOutputStream f = new FileOutputStream("b111.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");      
        }
        public void c1(SFBool ev, SFTime now)
        {       abc("In c1\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();
                } catch ( Exception e ) {}
        }
}

In this program we've substituted is.Active with is.Over. Now each time your mouse pointer goes over the object, the function named c1 is called. This one little word opens up a whole new world of interaction. For example, on your Web page, you could have a rectangle with your sponsors name on it, that starts to rotate every time the users mouse moves over it.

b12.wrl

#VRML V2.0 utf8
DEF uuu1 Transform
{       children
        [       DEF ts1 TouchSensor {}
                children
                [       Shape
                        {       appearance Appearance
                                {       material Material {}
                                }
                                geometry Cylinder {}
                        },
                ]
        ]
}

DEF uuu Transform
{       translation 10 0 0
        children
        [       DEF ts TouchSensor {}
                children
                [       Shape
                        {       appearance Appearance
                                {       material Material {}
                                }
                                geometry Cube {}
                        },
                ]
        ]
}
DEF s1 Script
{       eventIn SFBool  c2
        eventIn SFBool  c1
        url     "b12.class"
        scriptType      "javabc"
}

ROUTE ts.isActive TO s1.c1
ROUTE ts1.isActive TO s1.c2
b12.java
import vrml.*;
import java.io.*;
public class b12 extends Script
{       DataOutputStream d;
        public b12()
        {       try
                {       FileOutputStream f = new FileOutputStream("b12.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");
        }
        public void c1(SFBool ev, SFTime now)
        {       abc("In c1\r\n");
        }
        public void c2(SFBool ev, SFTime now)
        {       abc("In c2\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();                      
                } catch ( Exception e ) {}
        }
}

In this here program, we have not one but two Transforms, named quite aptly, uuu and uuu1. Each of the two transforms have their own personal TouchSensors and each of them calls two different functions. This means that when you click on one object, it calls one function and when you click on the other , it calls another function.

In b13, we set the code so that c1 is called twice each time you press the left mouse button.

b13.wrl

#VRML V2.0 utf8
DEF uuu Transform
{       translation 10 0 0
        children
        [       DEF ts TouchSensor {}
                children
                [       Shape
                        {       appearance Appearance
                                {       material Material {}
                                }
                                geometry Cube {}
                        },
                ]
        ]
}
DEF s1 Script
{       eventIn SFBool  c1
        url     "b13.class"
        scriptType      "javabc"
}

ROUTE ts.isActive TO s1.c1
b13.java
import vrml.*;
import java.io.*;
public class b13 extends Script
{       DataOutputStream d;
        public b13()
        {       try
                {       FileOutputStream f = new FileOutputStream("b13.txt");
                        BufferedOutputStream b = new BufferedOutputStream(f,128);
                        d = new DataOutputStream(b);
                }
                catch ( Exception e ) {}
                abc("In Constructor\r\n");
        }
        public void c1(SFBool ev, SFTime now)
        {       if ( ev.getValue() == true )
                        abc("In c1\r\n");
                else
                        abc("In c2\r\n");
        }
        public void abc( String a)
        {       try
                {       d.writeChars(a);
                        d.flush();
                } catch ( Exception e ) {}
        }
}

The function c1 has two parameters, ev is an SFBool and now, which is an SFTime. The second parameter nowc1 (SFBool) is very important because it tells you whether the user has clicked the mouse button or released it. Depending upon why the function c1 is being called, the first parameter (ev) will change. Now every time you click and release the mouse button, getValue returns a True or a False. In other words this function is called twice with every click of the mouse. This means that you can have an if statement for each of the values. So depending upon the data type of the events , this parameter can have different values.

The beauty of VRML 2.0 lies in its sensors because it is the sensor that senses the dynamic changes in the environment. For example, you can have a moving car with a sensor associated with it. When the users mouse moves over the car, the sensor is alerted and it carries out the duties you've assigned it. This means that VRML 2.0 is the head honcho, it detects the action and then passes them to the Java program so that the related code may be executed. This means less headaches for the programmer and better applications for the user.

Continued ..........


This is the tutorial that we have compiled and will keep on updating as and when we keep cracking the code. Have any suggestions, comments, ideas, cracked code, feed back ? Feel free to Get in touch with us.
Back to the Vijay Mukhi's Technology Cornucopia or the main VRML page.


Vijay Mukhi's Computer Institute
B-13, Everest Building, Tardeo, Mumbai 400 034, India.
http://www.vijaymukhi.com
E-mail : vmukhi@giasbm01.vsnl.net.in
Tel : 91-22-496 4335 /6/7/9
Fax : 91-22-307 28 59