This tutorial gives an overview of VRML syntax, as well as a overview of how 3D graphics is done at a lower level than you can see in 3D modeling programs. VRML code is simple text which is interpreted by a VRML browser and rendered in realtime into 3D objects. It is important for both programmers and graphic artists to understand VRML syntax, as this is the point where the two areas meet.
You can use 3D modeling programs like 3DS Max to produce complicated geometric shapes and texture mapping, and can even add many VRML nodes via the VRML Helpers functions, but a certain amount of text editing is still necessary.
Open file main.wrl in a browser. This is an extremely simple VRML file with one object in it.
Navigation:
Put mouse cursor in middle of screen.
Click-and-drag in any direction to move in that direction.
Right-click
menu:
The right-click menu allows you to change settings for your VRML browser. Please do the following:
1. Right click in VRML window to get menu.
2. Select “Bewegung” and look at alternatives for movement. Select “Betrachten” = “Examine” or press the key combination Strg/Umschalt/E (Ctrl/Shift/E) to get into Examine mode. Click and drag in the window and see how your movement has changed.
3.
Select
“Settings/Voreinstellungen.”
4.
Direct3D:
Ideally, all boxes should be checked for the best visual quality. Depending on
your graphic card and operating system this may or may not be possible.
5.
General:
Select Menue-Umfang: “Expert” for more options in the right-click menu.
(CAREFUL! “Trade Show” gives you an extremely
restricted menu. If you choose this one by accident, press F9 to call up the
Settings/
Voreinstellungen dialog box and change back to
“Expert.”
6.
Caching:
Make sure "Internet VRML-Dateien auf Festplatte speichern" is checked
and set the field " Internet VRML-Dateien speichern in" to the path
name of your local working folder or to your server account. Then copies of all
the Internet vrml files you view will be stored in this folder.
7. Click “OK” to close menu and save new settings.
If you selected the Expert menu you should
now have more options in your right-click menu.
1. Select “Grafik/Drahtgitter” and look at how the vrml image changes. These are the polygons that make up the shape. Set it back to smooth textures by selecting “Grafik/Weich.”
2.
“Speed” gives you options for the
speed of your movement, if it seems to be too fast or too slow.
Besides the material in this tutorial, please also see Floppy’s tutorials, from
which much text was shamelessly stolen. Sections of this tutorial will
reference many of his tutorials for more information.
http://web3d.vapourtech.com/ ->Tutorials -> VRML97
Let’s start by opening the file main.wrl in a text editor (VRMLPad.) You should see the following:
#VRML V2.0 utf8
# simple node
DEF defaultBox Shape {
appearance Appearance {
material Material {}
}
geometry Box { size 1 1 1}
} # end Shape
Header line:
All VRML files start with a header line, which is:
#VRML V2.0 utf8
This tells the browser that it's looking at a VRML file, and the version it's using. In this case, it's version 2.0. VRML is case-sensitive, so make sure that the capitalisation is exactly the same as you see here. The “utf8” part tells the browser which text string standard to use.
Comment lines:
Any line that starts with a # character is a comment. Notice that some of the code is commented out -–this is a great way to play around with a file while testing things out or debugging.
Scene graph
- Nodes and Fields:
A VRML world is made up of “nodes,” which are types of objects. Inside these nodes, you find “fields,” which are properties of the object. Fields can be anything, from the size of a box, to another node inside the first.
Nodes:
All official VRML 2.0 nodes and fields are described in chapter 6 of the Virtual Reality Modeling Language International Standard ISO/IEC 14772-1:1997 website at:
http://www.web3d.org/technicalinfo/specifications/vrml97/index.htm
Floppy also has a quick node reference in Appendix A at the end of his tutorial.
Fields:
Please read “Fields & Types” in Floppy’s tutorial 1.7 for the names and descriptions of the values that are used in VRML.
Scene
Graph:
Nodes can be nested inside one another, giving a of hierarchy of nodes called the “Scene Graph.” For instance in main.wrl we have:
DEF defaultBox Shape {
appearance
Appearance {
material Material { }
}
geometry Box { size 1 1 1 }
} #end defaultBox Shape
“DEF defaultBox” declares the name of this Shape node to be “defaultBox.” The node is written in the file by writing the node name “Shape” (capitalized) and then a pair of curly braces. Inside these braces, we have the 2 fields “appearance” and “geometry” (lower case.) The appearance field contains an “Appearance” node which in its turn contains the fields “material” and “texture.”
The material field contains the “Material” node which is empty, which means that the default value of “0.8 0.8 0.8” ( = grey) will be used. (The Node Reference lists defaults for each field.)
The geometry field contains the “Box” node, which in turn contains the size field with values “1 1 1,” which creates a box with size 1meter x 1meter x 1meter.
·
Note: all VRML 2.0 dimensions are in
meters; all rotations in radians.
·
See Floppy’s tutorial 1.3 “Co-ordinate
Systems & Axes” for
a list of radian equivalents for commonly used degrees.
Dropped
brackets:
It is easy to forget or delete a bracket when editing the file. VrmlPad gives you a couple of helpers:
Syntax checking:
Try deleting one of the brackets. Notice that many words are now underlined and at the bottom of the file several colored fields light up.
Ctrl M to find closing bracket:
Put the cursor in front of a bracket and
press Ctrl/M. The cursor
will jump to what it thinks is the closing bracket.
(See
also Floppy’s tutorial 2.2)
To make it easier to see objects, let’s insert a Background node (it can be anywhere, but it is good for to put it before the Shape node since it “contains” the Shape node.) Type in the following and save the file as main_bkgd.wrl.
Background
{
skyAngle [1.3, 1.571]
skyColor [
0 0 1 #blue
1 1 0 #yellow
0.0 0.5 0.8 #sea blue
]
groundAngle [1.5, 1.571]
groundColor [
0 0 0, #black
0.8 0.5 0.2, #orange-brown
0 0.5 0 #green
]
}
The field “skyColor” is a list of SFColor values corresponding to the sequence of colours to be displayed on a hemisphere forming the background. The first value is displayed directly above, the next below, etc. The field “skyAngle” is the angle (down from the top) at which each color will be displayed. The first color is automatically displayed at an angle of 0 (straight up), so you don't need to include that one. Therefore, there will always be one fewer angles than colors. The browser interpolates between the colours on the background.
The fields “groundColor” and “groundAngle” function similarly, except they start with an angle of 0 being straight down instead of straight up. The groundColor is a hemisphere inside of the skyColor. Try increasing the second groundAngle value and see what happens. To have a single colour as background, simply specify a single skyColor and no other fields.
Other fields in the Background node enable you to add “infinite backgrounds” – texture maps which are mapped onto a cube around the world. This cube is always centered on the viewer and the sides always remain at a constant distance from the viewer, no matter how far they go in any direction. The fields are bottomUrl, topUrl, frontUrl, backUrl, leftUrl, rightUrl.
Look at the file front.png in Photoshop: it is a solid color, and uses the alpha channel to create the cutout mountains. Look as well at right.png – here I have made the background white. In both files you can see how the middle of the file is at the horizon line. This unfortunately means that the files have to be very large in order to get a very high quality background image. (An alternative in order to use smaller images is to create a large box around your entire scene – but then you have to make sure that the user can’t run into it.)
Add the following four highlighted fields to the Background node as shown (at the same level of indentation as the other fields). Save and look at the file:
Background {
skyAngle [1.3, 1.571]
skyColor [
0 0 1 #blue
1 1 0 #yellow
0.0 0.5 0.8 #sea blue
]
groundAngle [1.5, 1.571]
groundColor [
0 0 0, #black
0.8 0.5 0.2, #orange-brown
0 0.5 0 #green
]
frontUrl
"media/front.png"
rightUrl
"media/right.png"
backUrl
"media/back.png"
leftUrl
"media/left.png"
}
In the browser notice how:
· There’s a thin line between background colors and background images, even though both should be exactly at the same horizon line – the middle of the screen. Increase the second groundAngle a tiny bit over 1.571 in order to cover up this line.
· On the top edge of the mountain in right.png there’s a white outline where the anti-aliased white background shows through. This is hard to avoid without having a hard edge, which is why it is good to use a background that is the same color as the foreground object.
(See
also floppy’s tutorial 1.4.)
Now let’s start playing around with the Shape node:
Add a
diffuseColor field to the Material node:
Add a diffuseColor field to the Material node as shown below. Save the file as main_color.wrl and look at it in the browser. The box is now red.
DEF defaultBox Shape {
appearance Appearance {
material Material {
diffuseColor 1 0 0
}
}
geometry Box { size 1 1 1}
} # end Shape
Adding .jpg or
a .png texture field to the Appearance node:
Add a texture field to the Appearance node as shown below with the file linux.jpg in the “media” folder. Notice how you have to give the path name, since the file is in a subfolder. Save the file as main_texture.wrl and look at it in the browser. The box now has a texture on it. Note that the color is not visible – it is hidden by the texture.
DEF defaultBox Shape {
appearance Appearance {
material Material {
diffuseColor 0 1 0
}
texture ImageTexture {
url "media/linux.jpg"
}
}
geometry
Box { size 1 1 1}
} # end Shape
Change the file name to linux.png and see how that looks. Rotate the box to see how the texture is mapped on all sides. (Look at the channels of the png file in Photoshop – an alpha channel is set to make the background transparent.)
Try setting the Z-dimension of the box to 0.1 (i.e. Box { size 1 1 0.1} ) and look at what happens. (You can set it to zero as a quick and dirty way to make a two-sided images – although this isn’t quite spec conform.)
repeatT and
repeatS:
Notice however that there are funny thin lines at the top and the bottom of the penguin. This is an artifact that occurs because the default setting is for the browser to repeat the texture, so even if you’ve mapped it only once, it picks up a bit of the color that would be there if it repeated.
To get rid of this, add the repeatT and repeatS fields to the ImageTexture node and set them to FALSE as follows. (For repeated textures see Floppy’s tutorial 2.7.)
DEF defaultBox Shape {
appearance Appearance {
material Material {
diffuseColor 0 1 0
}
texture ImageTexture {
url "media/linux.png"
repeatT FALSE
repeatS FALSE
}
}
geometry Box { size 1 1 0}
} # end Shape
Optimizing
textures:
·
Use ONLY jpgs, gifs and pngs. Use
pngs ONLY when you need graduated transparency.
If your texture is completely opaque, use jpgs. If you need an image with fully
transparent and fully opaque parts, use gifs.
·
All texture maps should be powers of two, e.g. 64x64, 64x128, 128x128,
128x256, etc.
The browser has to scale up each dimension of a texture
map to a power of two. You can save the browser time (thereby making your world
faster) by using Photoshop to make all your textures dimensions that are powers
of two.
Do this by taking your
correctly proportioned image into Photoshop. Go to Image/Image size, and
setting it to 256x256 pixels, 128x128 pixels or whatever size you can get away
with. When you map this squared image onto a shape with the correct
proportions, it will stretch to fit.
·
Maximum size shouldn’t exceed 256 x
256 pixels.
We might need to make a couple of exceptions, such as for the Background
images, but the exceptions should be a rarity. If you really think you need to
do this, speak to me about it first.
·
Use texture coordinates or repeated
textures when possible (see
Floppy’s tutorial 2.7.)
Whenever possible,
such as on a building wall, repeat a small texture over an entire surface. To get
better visual quality with smaller textures, use texture coordinates to map
smaller textures to different parts of an object.
·
Example:
Hard core optimization, for instance for online games, uses a large number of
very small texture maps that are all part of a single large texture map.The
vrml code then uses texture coordiates to map it to the correct geometry. Look
at the example in the “avatar” folder: tomoe.jpg and tomoe_unzipped.wrl. The
texture map is 512x512 but will contain all the texture maps the avatar ever
needs. Texture coordinates are used to map different parts of the image to
different parts of the avatar’s body.
(See
also “Object Reuse” in Floppy’s tutorial 1.2 and the entire tutorial 1.3 on
coordinate systems, axes and transforms.)
There are several ways of reusing nodes in vrml. Here are two: USE and Inline.
Transform
node, re-using nodes with DEF/USE:
· You can reuse any node by defining a name for the first occurance of that node with DEF and then invoking that name with USE.
· You need to add a Transform node to move the USEd box to the side. Otherwise both objects will be in the same place and it will look as if you only have one. Type this code into your file and rename it main_use.wrl. Look at it in the browser.
Transform {
translation 2 0 0
children [ USE defaultBox ]
}
#end transform
Note: In VRML coordinates the X-Axis is horizontal, Y is vertical, and Z is coming out of the screen. (See Floppy’s tutorial 1.3 for a diagram and for more description of the Transform node.)
Inline node
for including separate .wrl files:
You can also use the Inline node to include a completely separate .wrl file in the same world. You need of course to transform it so it doesn’t sit in the same place as the other geometry in your world.
Look in the folder “media” at the files “palm_tree.png” (not the world’s best, but hey, I didn’t make it, I stole it ) and “palm_tree.wrl”
Exercise:
The palm tree is created at (0,0,0) so you have to move it up in the y-axis by half its height. Add the following code to your file, and build a Transform node around it as needed so the penguin is sitting in front of it at its base.
Inline { url "media/palm_tree.wrl" }
Besides the geometry primitives, there are several ways to create arbitrary geometry.
Please read about IndexedFaceSets in Floppy’s tutorial 2.4. There is additional material in this tutorial on ElevationGrids and Extrusions as well.
Normals:
Please read “Normals” in Floppy’s tutorial 2.5.
· Normals are important to understand why not all faces of 3D geometry are visible.
· The crease angle is a quick and effective way to increase the apparent smoothness of a surface without adding more polygons. In 3DSMax you will need to use smoothing groups to get the same effect.
Texture
Mapping:
Please read Floppy’s tutorial 2.7, especially “A trip into texture space” and “Texture coordinates.”
Exercise1 –
add single-sided island:
Add an island in the x-z plane (i.e. y=0 plane) by created a flat IndexedFaceSet. Make it a single square polygon and map the file “media/island.png” onto it. Set solid TRUE, so it is one sided, and ccw TRUE, which is the default. How do you have to arrange the point order so the texture shows on the top surface? Save the file as main_island.wrl.
#
island texture mapped to IndexedFaceSet
appearance Appearance {
material Material { }
texture ImageTexture { url
"media/island.png" }
}
geometry IndexedFaceSet {
solid FALSE
ccw TRUE
coord Coordinate { point [ #coordinates to
define 20x20 rectangle in y=0 plane
10 0 -10,
-10 0 -10,
-10 0 10,
10 0 10
] }
coordIndex [ 0, 1, 2, 3, -1] #order in which geometry points are to be
drawn
texCoord TextureCoordinate { point [ #coordinates to define that entire
texture map should be used
1 1,
0 1,
0 0,
1 0
] }
texCoordIndex [ 0, 1, 2, 3, -1] #order in which texture
points are to be drawn
}
}
#end island Shape
There are probably a couple of problems with your file:
· You have just added a y=0 plane, but remember that primitives – your penguins - are created centered at (0,0,0), so you need to adjust their positions by adding the appropriate Transforms.
· You enter the world centered at y=0 also, but the browser puts you far enough away so that you can see ALL the geometry. To place yourself where you want to be, add a Viewpoint node to set your initial position and eye level to a desired position. The standard used is a height above ground of y=1.6 meters, which is eye height for a human 1.8 meters tall. (Add this to your file just under the Background node.)
Viewpoint {
orientation 0 1 0 0 # looking down the z-axis.
position 0 1.6 10 # y-value is
same as that of avatarSize
description "entry" # this name appears in “Standorte” menu of
the right-click menu.
}
Note:
§ There are other problems as well: the island seems to be black. We’ll take care of this in the next section on lighting.
§ Also, If your penguins are .pngs with partial transparency you might also see the one off to the side is flipping in front and behind the palm tree, and the one by the tree flips in front and behind it. This is because the renderer has trouble deciding which of the images should where. We’ll address this problem further below in the section on draw groups.
Exercise2 –
turn penguins into IndexedFaceSets:
The trick we are using with the Box primitive is slightly illegal – let’s replace the penguin with an IndexedFaceSet.
1. Copy the code for the island.
2. Reducing the size of the IndexedFaceSet to 1x1and construct it in the z=0 plane by changing the Coordinate points.
3. To make the penguin texture show on both sides, set solid FALSE.
Until now we have had no light nodes in our VRML world. The browser automatically turned on the virtual headlamp so we could see the geometry, but it only illuminates objects whose planes are at right angles to the headlamp beam. The island is parallel to our headlamp beam and therefore is not illuminated until we rotate it in Examine mode. Turn off the headlamp via the right-click menu: select “Grafik” and then unclick “Beleuchtung.”
Notice that now ALL the geometry is dark, but that the Background is completely unaffected. You would have to alter the fields of the Background directly if you want to change it (see Floppy’s tutorial 3.5 on bindable nodes.) For now however let’s leave the Background alone and install lights in the scene.
NavigationInfo:
First, we need to get rid of the headlight. Read Floppy’s tutorial 2.8 about NavigationInfo and then put the following code in your file just under the Background node. Save the file as main_light.wrl.
NavigationInfo
{
headlight FALSE
}
Lighting:
Read Floppy’s tutorial 2.6 on lighting. Lights are “expensive” i.e. take up a lot of processor time, so use as few lights as possible.
Exercise1 – front light:
Insert a DirectionalLight into your scene as a “sun” (put it under the Viewpoint and before any of the geometry.)
# "sun" light
DirectionalLight {
intensity 1
color 1 1 1
direction -0.5 -0.5 -0.5 #coming from behind you from the right
ambientIntensity 1
on TRUE
}
Look at the back side of the penguins as well – they are in shadow. But why are the penguins so red, when your light is pure white and at maximum intensity? Remember that your diffuseColor is red – it combines with the texture map. You didn’t see this before because the headlight washes out all these subtle effects. Set the diffuseColor to white (1 1 1) so we can see the effects of the lights.
Exercise2 –
fill light:
Now add a second “fill light” and see how it looks. Play around with the parameters of the light and see what effect they have on the scene. Note you can turn each light on or off with the “on” field.
# fill light
DirectionalLight {
intensity 1
color 1 1 0
direction 0.5 -0.5
0.5 #coming from in front to your left
ambientIntensity 1
on TRUE
}
Note: Should you want one object in a scene to be much brighter than the rest, you can also add an emissiveColor field to its Material node. A value of 1 1 1 (= white) gives you the maximum brightness.
Exercise3 –
more IndexedFaceSets:
Now that we’ve used the white front of the penguin to see the effects of lights on front and back, let’s make the penguin more realistic, with a different texture for its back side.
1. Make a copy of the penguin’s Shape node.
2. In both Shape nodes change solid FALSE to solid TRUE so that each polygon is one-sided.
3. In the second Shape node change the texture from media/linux.png to media/linux_back.png and ccw TRUE to ccw FALSE. So now you have two one sided IndexedFaceSets, each with a different texture, each facing in a different direction.
So how about that disappearing penguin? When two images with partial transparency are very close to each other, it is very difficult for the renderer to know which one is “on top.” It always draws the one whose center is closest to you on top of the other one. When you first enter the world you are closer to the center of the island than to the center of the far penguin. When you approach the penguin, its center is now closer to you so it pops out on top.
We can take care of this problem with a blaxxun node extension called DrawGroup. This node allows you to define the exact order in which objects should be drawn. As this is an extension to the VRML standard, for compatibility with other vrml browsers we need to include an EXTERNPROTO node that defines DrawGroup. Let’s worry about what an EXTERNPROTO is later – for now please just copy it into your file.
Exercise:
1. Add the EXTERNPROTO DrawGroup code near the top of your file, before any geometry but after the Background, Viewpoint, NavInfo and the lights.
EXTERNPROTO
DrawGroup[
exposedField SFVec3f bboxSize
exposedField SFVec3f bboxCenter
exposedField SFBool sortedAlpha
exposedField MFNode children
eventIn MFNode addChildren
eventIn MFNode removeChildren
]
["urn:inet:blaxxun.com:node:DrawGroup",
"http://www.blaxxun.com/vrml/protos/nodes.wrl#DrawGroup"]
2. Then copy in the following code directly underneath the EXTERNPROTO.
3. Insert your penguins, the palm tree and the island inside the “children” field in that order. Indent them so it is clear that they are children of the DrawGroup. (In VrmlPad you can do this by highlighting the entire block of code, then selecting Edit/Selection/Indent.) Save the file as main_draw.wrl.
# DrawGroup to specify
rendering order
DrawGroup{
sortedAlpha FALSE # set FALSE to define the drawing order
yourself
children [
# Replace this comment with the
penguin, tree and island nodes in the exact order to be drawn
]
}
Note:
DrawGroup and other extensions to blaxxun
Contact are described at http://developer.blaxxun.com/,
then follow the links -> Documentation -> 3d Authoring Manual -> Extensions -> Node Extensions.
OK, let’s make a penguin hop. To animate objects in your world you need to understand how events are routed between nodes, and how timeSensors and Interpolators work. Please read:
·
“Wiring It Up” in Floppy’s tutorial 3.1.
·
“TimeSensor” in Floppy’s tutorial 3.2.
·
“Interpolators and Animation” in Floppy’s tutorial 3.4
Exercise:
Make just the USEd penguin hop. This gets sticky – we’ve got the penguin in a hierarchy of Transforms. I advise that you put each penguin in a separate transform and add a second transform specifically for the Interpolater. Save the file as main_anim.wrl
Notes:
· You need to DEF a name for the penguin2 Transform in order to route to it.
· Remember that the penguin2 has to be within the DrawGroup. The TimeSensor and PositionInterpolator can go after the DrawGroup and before the ROUTES.
· VrmlPad wants you to put all the ROUTEs at the end of the file, and will complain if you don’t.
#penguin2
transform for animation
DEF penguin2Trans Transform {
translation 0 0 0
children [
#internal transform for starting position
Transform {
translation 2 0.5 0
children [
USE defaultBox ]
} #end internal transform
]}
#end animation transform
#
hopping penguin2 clock
DEF hopClock TimeSensor {
enabled TRUE
loop TRUE
cycleInterval 1.0
}
#
hopping penguin2 Interpolator
DEF hopInterp PositionInterpolator {
key
[0, 0.5, 1]
keyValue
[
0 0 0,
0 1 0,
0 0 0
]
}
#
all routes go at the very end of the file
ROUTE
hopClock.fraction_changed TO
hopInterp.set_fraction
ROUTE
hopInterp.value_changed TO penguin2Trans.set_translation
OK, that penguin is going to drive us nuts. Let’s set up a proximity sensor on it, so it only hops when we come close.
Please
read “ProximitySensor” and “Collision” in Floppy’s tutorial 3.2.
Exercise1:
Add the following to your file and save it as main_sensor.wrl:
· A ProximitySensor within the internal penguin transform.
· Since it is difficult for us to see where the proximity sensor is actually located, add a dummy half-transparent box in the same location. To allow you to enter the dummy box, put it in a Collision node with collide FALSE. (When you’re happy with its position, comment out the entire dummy box and collision node to save the renderer time.)
· To make the penguin wait for your click, add startTime 0 and stopTime 1 to the TimeSensor. In the time scale used in VRML, time = 1 second was back in 1970, and therefore your animation “has already stopped,” so to speak. When you send a new startTime to the TimeSensor, the stopTime is reset to infinity because loop = TRUE.
· Route the ProximitySensor’s enterTime event to the TimeSensor set_ startTime field.
# group
for touch sensor and penguin
DEF
penguin2Trans Transform {
translation 0 0 0
children [
#internal transform for starting position
Transform {
translation 2 0.5 0
children [
USE defaultBox
DEF
proxPenguin ProximitySensor { size 6 4 8 }
#dummy
collision box
Collision
{
collide
FALSE
children
[
Shape
{
appearance Appearance {
material Material {transparency 0.5 }
}
geometry Box { size 6 4 8}
} # end
Shape
]} #end
collision
]} #end internaltransform
]} #end
animation transform
# hopping penguin2 clock
DEF hopClock TimeSensor {
enabled TRUE
loop TRUE
cycleInterval 1.0
# startTime
0
# stopTime
1
}
# all routes go at the end of the file – outside of the DrawGroup
ROUTE
proxPenguin. enterTime TO hopClock.set_startTime
What if you want to stop the penguin from hopping? You could add a TouchSensor that stops the animation, but you probably have to then also disable the proximity sensor, otherwise it’ll turn the animation on if you move slightly and reenter the sensor area. Let’s leave that for now, however.
Exercise2:
OK, enough hopping. Let’s set the TimeSensor so the penguin only hops once. This is easy – just change the TimeSensor loop field to FALSE. Notice it’ll hop once every time you come near it.
# hopping penguin2 clock
DEF hopClock TimeSensor {
enabled TRUE
loop FALSE
cycleInterval 1.0
startTime 0
stopTime 1
}
Let's also get rid of the dummy box that depicts the proximity sensor and its surrounding Collision node. Put comment signs (the number sign "#") in front of every line. In VrmlPad you can do this easily by highlighting the entire block of lines and then clicking the number sign button in the row of editing icons at the top of the editing window.
#dummy
collision box
# Collision {
# collide FALSE
# children [
# Shape {
# appearance Appearance {
# material Material {transparency 0.5
}
# }
# geometry Box { size 6 4 8}
# } # end Shape
# ]} #end collision
For information
on other sensors:
Floppy’s tutorial 3.2 describes the ProximitySensor and other environmental sensors; Floppy’s tutorial 3.3 has information on other pointing sensors. We will implement a touch sensor below.
Please read Floppy’s tutorial 2.3 on sound.
Sound requires two nodes:
· A Sound node to control where the sound is located, how it falls off and how the sound is spatialized in the 3D space.
· An AudioClip node to specify the sound files. blaxxun Contact can play all .wav files and small (up to 135 Kbyte as of Oct.2002) .mp3 files in an AudioClip node.
Notes:
· wav files are extremely large. Keep them short and loop whenever possible to get the effect of a longer sound. If at all possible, keep wav files especially to 8 bit sampling at 11 kHz.
· As of October 2002, Real Audio files and large (over 200 Kbyte) mp3 files will not play within an AudioClip node. They have to be put in a MovieTexture node that is in a texture field of a Shape node – which means they cannot be localized at all and will always be played at maximum volume, no matter where they are located and how far you are from them.
Exercise1 – ambient sound:
Add the (very quiet) “media/water.wav” file
as an ambient sound. For ambient sound
(i.e. constant volume wherever you are in the space) you need to set:
· maxBack = minBack = maxFront = minFront
· make sure the max values are large enough to cover the entire space in which the user will move.
· Set spatialize FALSE
Save the vrml file as main_sound.wrl.
#
water - ambient
maxBack
500 # sound inaudible
outside of this very large radius of 500 meters
minBack
500 # inside of radius
of 500 meters, sound is at constant volume
maxFront 500
minFront 500
spatialize FALSE # No spatialization for
ambient sound
intensity 1
source DEF waterClip AudioClip {
url "media/water.wav"
loop TRUE
}
}
#end water - ambient sound
Exercise2 –
spatialized, localized sound:
Now we want to add the file “media/bird.wav” as a sound centered at the palm tree and spatialized so it dies off as you go away from the tree. Remember that the tree is in media/palm_tree.wrl and is Inlined into the main file. The Sound node has a location field, so theoretically you can put it anywhere within the file, but if you don’t have the Sound node code located next to the object to which it is associated, it is easy to loose track of where it is located. Let’s put it inside the Tranform node that is wrapped around the palm_tree Inline node.
Let’s throw in a TouchSensor on the palm tree as well so we can turn off the bird sound with the mouse.
Please
read “TouchSensor” in Floppy’s tutorial 3.3.
Note: A TouchSensor will affect all geometry at the same level of the scene graph. Also, the TouchSensor doesn't know anything about the alpha transparency of the palm_tree bitmap, so a large area to either side of the palm tree will be touch sensitive as well.
Add the following to your main file and save it as main_sound2.wrl.
# palm tree
Transform {
translation 0 2.5 -0.25
children [
Inline { url "media/palm_tree.wrl" }
# bird - spatialized
Sound {
direction 0 0 1 # positive Z-axis
location 0 2 0 # locate the sound slighty above the user
maxBack 20 # inaudible outside this radius
minBack 1 # full volume within this radius
maxFront 20 # inaudible outside this radius
minFront 1 # full volume within this radius
spatialize TRUE
intensity 1
source DEF birdClip AudioClip {
url
"media/bird.wav"
loop TRUE
}
} #end sound
DEF palmTouch TouchSensor { }
] } #end palm tree transform
# all routes go at the end of the file - outside of the DrawGroup
Notice that the effect of the code is that:
· The sound is looping endlessly when you first open the file.
· If you click on the palm tree, the sound turns off when you release the mouse button.
· If you click on the palm tree again, the sound runs while you hold the mouse button depressed, and stops when you let go. This is because palmTouch.isActive sends a TRUE event to the birdClip loop field while you are pressing the mouse button, and a FALSE event when you let go.
As your objects get more complicated, your interactivity more complex and your main file longer and longer, it is advisable to split your code into several different files.
Setting up a
modular file hierarchy:
· The main file should contain the nodes that set the scene and define how the user acts: background, NavigationInfo, Viewpoints, and later on, Scripts. It is important that the flow of command and activity be kept in this file, so you understand how things fit together and where where to find the code.
· Large chunks of geometry or geometry with large chunks of data should be kept in sub-files. You have seen how to inline the palm_tree.wrl file into your scene, but the Inline node doesn’t allow you affect the palm tree at all. Protos and ExternProtos give you many more features. Read on!
Please
read “Proto” and “ExternProto” in Floppy’s tutorial 1.7.
Exercise1
- Turn geometry into Proto:
Take a look at the file basic_plant.wrl in the media folder. It consists of two leaves – the original leaf and a transformed, USEd leaf.
We want to use this file to add more vegetation to the island – in different scales, locations and colors. The file is long enough that it would make our main file unreadable. We could inline it inside a transform, but then we couldn’t change its colors. We will turn it into a prototype – a Proto node - in a separate file and then read it into our main file as an external prototype – an ExternProto node.
· Add the diffuseColor IS plantColor statement within the originalLeaf Material node.
· Wrap the entire geometry - originalLeaf transform plus the USEd instance - in a second Transform node, using IS statements to link internel fields to PROTO fields.
· Wrap that Transform node in a PROTO node with fields as shown below. Save the file in the media folder as proto_ plant.wrl and look at it in the browser.
PROTO leafPlant [
field
SFVec3f plantTrans 0 0 0
field
SFRotation plantRotate 0 0 0 0
field
SFVec3f plantScale 1 1 1
field
SFColor plantColor 0 0.5 0.1
#default green
]
{
# Transforms to be set when instancing leafPlant
Transform {
translation
IS plantTrans
rotation IS plantRotate
scale IS plantScale
children [
# add
geometry here -> including diffuseColor
IS plantColor
] } #end instancing transform
} #
end proto
The screen was probably black. You need to reference a PROTO in order to see it. For testing purposes, add the following at the end of the file, save it and look at it again. You should be able to see the leaves.
#
debugging reference - comment out when finished.
leafPlant{
}
OK – now lets use the characteristics of proto within this file – add the following plant reference as well as the reference with no fields. Save the file and look at it again. You should have two plants of different sizes and colors.
#
debugging reference - comment out when finished.
leafPlant{
plantTrans
1 0 0
plantScale
0.5 2 0.5
plantColor 0.8 0.8 0.2 #
yellow
}
Please comment out the
leafPlant references now, so we
don’t have extra plants lying around, and we’ll use the proto_plant.wrl file as
an external prototype in our main file.
Exercise2
- EXTERNPROTOs:
Open up your last main file, main_sound2.wrl. Save it out as main_extern.wrl.
·
Insert the EXTERNPROTO definition
given below near the top of the file next to the other EXTERNPROTO you put in –
the one for the DrawGroup code. Notice
that the EXTERNPROTO declaration
does NOT include default values for its fields! Also note that I’ve called the
externproto leafyPlant instead of leafPlant – if they are in separate files you
can do either, but it’s better to give the externproto a different name so you
don’t get mixed up.
Note: By reading the details on EXTERNPROTOs in Floppy’s tutorial, you
should now understand that the DrawGroup EXTERNPROTO was created in the same
way, but references files that are on the blaxxun site in the Internet!
· Insert the reference to leafyPlant in the correct order within the DrawGroup. Notice I’ve named the EXTERNPROTO leafyPlant instead of leafPlant – either one works, but I like to distinguish them so I can keep track of which reference is coming from what file.
EXTERNPROTO
leafyPlant [
field SFVec3f plantTrans
field SFRotation plantRotate
field SFVec3f plantScale
field SFColor plantColor
]
"media/proto_plant.wrl" #this is how the externproto knows where to
find the code.
# default everything
leafyPlant
{ }
#
using all fields
leafyPlant{
plantTrans 1
0 0
plantScale
0.5 2 0.5
plantColor
0.8 0.8 0.2 # yellow
}
Add or change the fields of leafyPlant as desired. You might want to add a Collision node to the PROTO file, otherwise it’ll get hard to walk around the island as you add more plants.
Exercise –
EXTERNPROTOs and path names:
In proto_plant.wrl, add a texture map to the plant. Warning: This gets tricky. First add the texture map and use the debugging code in proto_plant.wrl to make sure that the texture is mapping to the plant. Afterwards, comment out the debugging statements. Now comes the tricky part:
When you view proto_plant.wrl directly, the texture map is in the same folder (media) and therefore needs no path name. When you reference proto_plant.wrl via an EXTERNPROTO in the main file, however, the externproto node thinks that proto_plant.wrl is actually in the same folder as it is. This means that within proto_plant.wrl you now have to specify the path name for the texture map as if it were in the top level folder along with the main program.
(Once you’ve adjusted the path name, to test the proto_plant.wrl file alone you’ll have to save a copy of it into the top level folder – I always call this something like test_proto_plant.wrl to keep them clearly separate.)
So what if we add a touch sensor to increase the scale of the plants each time we click on them? To do this we need to write scripts – real programming within VRML. We can’t teach you to program here, but if you already know how, here’s the syntax you need:
· always need to end vrmlscript statements with semicolons.
Exercise –
TouchSensor scaling of leafyPlant:
In proto_plant.wrl,
§ Add an eventIn called plantGrow to the PROTO definition. EventIns aren’t initialized, since they are set in the main program.
§ To keep this scaling transform separate from the field that is already being used in your leafPlant PROTO, add a third transform that will connect the scale to an. Make sure it is inside the curly brackets of the PROTO.
PROTO leafPlant [
eventIn SFVec3f plantGrow
field SFVec3f plantTrans 0 0 0
field SFRotation plantRotate 0 0 0 0
field SFVec3f plantScale 1 1 1
field SFColor plantColor 0 0.5 0.1 #default green
]
{
Transform {
scale IS plantGrow
children [
# Transforms to be set when instancing leafPlant
Transform {
translation IS plantTrans
rotation IS plantRotate
scale IS plantScale
children [
# originalLeaf transform and geometry here
] } #end instancing transform
]} #end transform for touch sensor
} # end proto
In your main file (let’s call it main_script.wrl):
§ Add the eventIn plantGrow to the EXTERNPROTO definition.
§ Add an instance of leafyPlant in a Group node together with a TouchSensor.
EXTERNPROTO leafyPlant [
eventIn SFVec3f plantGrow
field SFVec3f plantTrans
field SFRotation plantRotate
field SFVec3f plantScale
field SFColor plantColor
] "media/proto_plant.wrl" #this is how the externproto knows where to find the code.
#
leaf group w/touch sensor
Group
{
children [
DEF plantGrow TouchSensor { }
plantTrans -1 0 1
plantRotate 0 1 0 1.571
plantColor 0.8 0.2 0.8
}
]}
#end leaf group w/touch sensor
At the end of the file right before the ROUTEs, add this Script node, and the ROUTEs needed to connect plantGrow to growMe1 via the Script node allScripts.
Note: Within the “vrmlscript ...” apostrophes, comments are indicated with // rather than #.
# eventIns
eventIn
SFBool scaleUp
#eventOuts
eventOut SFVec3f scale_changed
# fields
field SFVec3f incr 1 1 1
field SFFloat i 0
url "vrmlscript:
function initialize () // parentheses, not curly brackets!
{
scale_changed = incr;
}
function scaleUp (value, timestamp )
{ if (value)
{
i++;
var newScale = new SFVec3f(incr.x, incr.y+i, incr.z);
scale_changed = newScale;
}
}
"
# don't forget these quotation marks!
} # end Scripts node
#
growing plant
ROUTE
plantGrow.isActive TO allScripts.scaleUp
ROUTE
allScripts.scale_changed TO
growMe1.plantGrow
When there are too many polygons in a scene at one time, movement in the scene becomes very slow. There are various nodes that can be used to turn geometry on and off as needed when the user walks through the scene. blaxxun’s 3ds max VRML exporter supports some of these nodes (see documentation under Help/Additional Help.)
§ Switches (Floppy’s tutorial 3.7) can be used to turn on or off groups of VRML nodes depending on eventIns from sensors.
§ Level of Detail (Floppy’s tutorial 3.8) automatically swaps out different versions of geometry depending on its distance from the user.
§ Nurb objects similarly change their polygon count depending on the distance of the object from the user.
§ Particle systems
THE END