Lightradio
tested with
+
+
This tutorial was inspired by an online discussion
about radiobuttons. A few people shared their implementations with the
VAG/VRML community so now it is my time to share.
The radiobutton is a nice candidate for a PROTO. A PROTO
allows you to write your own nodes. VRML2.0 gives you the possibility to
extend the language from within a VRML file. Like any other node, a
PROTO has eventIns, eventOuts, exposedFields,
and regular fields, only you define them yourself!
This radiobutton has an eventIn ButtonReleaseP to deactivate a
button, an eventOut Button_is_ActiveP and an eventOut
Button_isnot_ActiveP to interact with other nodes. Let's start
with defining the header of the PROTO:
PROTO RADIOBUTTON
[
eventIn SFBool Button_releaseP
eventOut SFBool Button_isActiveP
eventOut SFBool Button_isnot_ActiveP
]
PROTOs are declared at the beginning of a file but can also be
inlined as EXTERNALPROTOs (if your favorite browser supports them!).
The next step provides the geometry of a radiobutton. To keep it simple I
use just a cylinder:
DEF radioButton Transform {
center 0 -0.1 0
children [
Shape {
appearance DEF buttonAppearance Appearance {
material Material {
diffuseColor 1 1 0
}
}
geometry Cylinder {
height .2
radius .1
}
}
DEF TCHS TouchSensor { }
]
}
The Transform and the Appearance nodes are DEFed because I need
to ROUTE to them later. The cylinder is centered at its bottom
because it will be downscaled each time it gets clicked. A TouchSensor
is neccessary to provide usefullness to our button. A PROTO will
always take the first node and derive its type from it. Right now the
PROTO will think that it is a Transform node. If I move the
TouchSensor node to the first line, the PROTO will behave like
a TouchSensor node. When in doubt about the nature of your
PROTO you should always embed your nodes within a Group node.
Now it's time for Scripting: when the button is clicked it has to a)
shrink, b) change its color, and c) notify its enviroment about its
activation.
A function will help us:
function Button_was_pressed(pressed) {
if (pressed) {
shrinkButton[0] = 1;
shrinkButton[1] = 0.5;
shrinkButton[2] = 1;
activeMaterial = onMaterial;
Button_isActiveS = TRUE;
}
}
The vector shrinkButton has the value 0.5 in its y-direction field. When
the vector is ROUTEd to the button's center field it will
scale it down to half of its original value. The field
activeMaterial will give the
active button a visual emphasis. The field Button_isActiveS is
the only field which will actually leave the PROTO node and trigger
other events.
A second function is needed once the button is released:
function Button_releaseS(released) {
if (released) {
shrinkButton[0] = 1;
shrinkButton[1] = 1;
shrinkButton[2] = 1;
passiveMaterial = offMaterial;
Button_isnot_ActiveS = FALSE;
}
}
The scale vector shrinkButton is reset, the field
emissiveColor is turned off and the original color of the button is
restored. Both functions reside in different Scripts. For reasons
unknown to the author they would not work in one Script node ;( .
The PROTO is completed with the appropriate ROUTing.
The TouchSensor triggers the function Button_was_pressed:
ROUTE TCHS.isActive TO RADIO.Button_was_pressed
The active node properties are ROUTEd:
ROUTE RADIOactive.shrinkButton TO radioButton.set_scale
ROUTE RADIOactive.activeMaterial TO buttonAppearance.set_material
The passive node properties are ROUTEd:
ROUTE RELEASE.shortButton TO radioButton.set_scale
ROUTE RELEASE.passiveMaterial TO buttonAppearance.set_material
The final PROTO
lightradio source sums it all
up and also includes the eventIns, eventOuts, and fields of
the Scripts. You will discover an unknown keyword in both Script nodes:
IS. If you want Script events to be available outside
of the PROTO you need to map between the PROTO events and the
Script events. Look at the Script RELEASE:
DEF RELEASE Script {
eventIn SFBool Button_releaseS IS Button_releaseP
...
}
I am using the field Button_releaseS inside the Script and
associate it with the field Button_releaseP which makes the Script
field available outside of the PROTO.
With the PROTO declared we can use it in the following way:
Transform {
children [
DEF middle RADIOBUTTON { }
]
}
middle is now a RADIOBUTTON. The empty parenthesis reveal that
there is more to a PROTO: you can pass fields to a PROTO
during initialization and thus customize each implementation.
To demonstrate today's lesson I let the radiobuttons turn on/off
SpotLights to illuminate primitives.
Transform {
translation 0 1 -1
children [
DEF middleL SpotLight {
location 0 0 1
direction 0 0 -1
color 0 1 0
on FALSE
}
Shape {
appearance Appearance {
material Material {
diffuseColor 0 0.2 0
}
}
geometry Sphere { radius 0.2 }
}
]
}
The final ROUTing comes in two steps: Once a button is clicked the
other buttons have to be turned off:
ROUTE first.Button_isActiveP TO second.Button_releaseP
ROUTE first.Button_isActiveP TO third.Button_releaseP
The function Button_releaseS does its clean-up (resets color and size)
and sends out the event Button_isnot_activeP=FALSE which is used to
end the action of the previous active button.
The next step turns on the active light and turns off the previous active
light:
ROUTE left.Button_isActiveP TO leftL.on
ROUTE middle.Button_isnot_ActiveP TO middleL.on
ROUTE right.Button_isnot_ActiveP TO rightL.on
For each radiobutton you have to provide ROUTEs to ensure that only
one is active and to turn on/off their assigned functionalities. The final
lightradio source shows you how
all these pieces work together.(
lightradio.wrl)

Copyright © 1996-98
Markus Roskothen. All rights reserved.