You have designed a great VRML world with a lot of animations and interaction
for your visitors, but …
… nobody else sees what they are doing. Someone hits a homerun in the
virtual baseball stadium but nobody cheers.
Therefore you want to share events with other visitors. Everybody should see what's happening. blaxxun Contact provides an easy way of routing VRML events to all visitors of a multi-user place.
Designing shared events in VRML 2.0
First of all, you have to declare the EXTERNPROTOs BlaxxunZone
and SharedEvent. For every type of eventOut you want to share you
have to use the eventIn set_type and the eventOut
typeToServer. If you want to get an event from
the server, you must use the eventOut type_changed and the eventIn
typeFromServer. type is one of the following
strings: bool, color, float, int32, rotation, string, time, vec2f, vec3f.
Then you have to instantiate a BlaxxunZone
named SharedZone into your VRML world. For every event that you
want to share, you must add an instance of a SharedEvent as a field
to the SharedZone.
At last you have to change the routes in your
VRML file:
Instead of routing directly to the target
node in your VRML world, you route to the eventIn set_type
of the SharedEvent and from the eventOut type_changed
of the SharedEvent to your target node. That's it!
Events routed to the SharedEvent nodes are automatically routed
to the server by the typeToServer eventOuts and from
there to all visitors of the place, including yourself. The SharedEvent
PROTO has input and output fields for the following VRML event types:
SFBool, SFColor, SFFloat, SFInt32, SFRotation,
SFString, SFTime, SFVec2f, SFVec3f.
Please note that SFTime events are NOT converted to the
local time on other clients ! If you are not interested in the actual local time the event was sent,
but want to use it to start animations you should use a VRMLScript function to receive the time_changed
EventIn and then use the time stamp of the call instead of the original value.
You don't have to define all types of events in your EXTERNPROTO declaration, just the ones that you need for your shared events.
Shared.wrl ( display it ) contains the definition of these 2 prototypes.
Attention! The names of the typeToServer and typeFromServer events must be the same as in the definition of the Shared.wrl !!
Let's look at a small example ( display it ) that illustrates the use. The world just contains three objects, a box, a sphere and a cone. If you click on the sphere, the box changes its color, if you click on the cone, it changes the rotation. Please check shared.wrl ( display it ) for other event types. shareddebug ( display it ) contains a debug version of the PROTOs: Every time a shared event is sent or received, a trace is written to the VRML console.
First of all, we have to declare the EXTERNPROTOs:
EXTERNPROTO BlaxxunZone [
eventIn
MFNode addEvents
eventIn
MFNode removeEvents
exposedField MFNode
events
] "http://www.blaxxun.com/vrml/protos/shared.wrl#BlaxxunZone"
EXTERNPROTO SharedEvent [
exposedField SFString name
eventIn SFColor
set_color
eventIn SFRotation
set_rotation
eventOut SFColor
color_changed
eventOut SFRotation rotation_changed
eventIn SFColor
colorFromServer
eventIn SFRotation
rotationFromServer
eventOut SFColor
colorToServer
eventOut SFRotation rotationToServer
] "http://www.blaxxun.com/vrml/protos/shared.wrl#SharedEvent"
We want to share the new color and the
new rotation with all other members. So for the color we need to quote
the following events
eventIn SFColor
set_color
eventOut SFColor
colorToServer
eventOut SFColor
color_changed
eventIn SFColor
colorFromServer
Remember to name the typeToServer
and typeFromServer events exactly the same
as in the definition of the SharedEvent Proto!
The "name" field in the SharedEvent
can be used to identify the shared event, e.g. to send an event to the
VRML world from a CSbot. The use of the name field is optional. The typeToServer
and typeFromServer events are used internally
by blaxxun Contact.
Then we have to add the SharedZone with one instance of the SharedEvent for the color and one for the translation.
DEF SharedZone BlaxxunZone {
events [
DEF SharedColor SharedEvent
{name "newColor" }
DEF SharedRotation SharedEvent
{ }
]
}
These are the three objects:
DEF BoxRotation Transform {
children [
Shape { # the red box
appearance Appearance
{ material DEF BoxColor Material { diffuseColor 1 0 0 } }
geometry
Box { }
}
]
}
# the sphere is used to change the
color
Transform {
position -4 0 0
children [
Shape { geometry Sphere {}
},
DEF ColorSensor TouchSensor
{},
DEF ColorScript Script {
eventIn SFTime
clicked
eventOut SFColor color_changed
url "vrmlscript:
function clicked (value,
time) {
color_changed =
new SFColor(Math.random(),Math.random(),Math.random());
}
"
}
]
}
# the cone is used to change the rotation
Transform {
position 4 0 0
children [
Shape { geometry Cone {} },
DEF RotationSensor TouchSensor
{},
DEF RotationScript Script {
eventIn SFTime
clicked
eventOut SFRotation rotation_changed
url "vrmlscript:
function clicked (value,
time) {
angle = Math.random()*6.283;
rotation_changed
=new SFRotation( 0,1,0,angle);
}
"
}
]
}
At last we have to add the ROUTEs between the sensors, the SharedEvent nodes and the geometry:
ROUTE RotationSensor.touchTime TO RotationScript.clicked
ROUTE RotationScript.rotation_changed
TO SharedRotation.set_rotation
ROUTE SharedRotation.rotation_changed
TO BoxRotation.rotation
ROUTE ColorSensor.touchTime TO ColorScript.clicked
ROUTE ColorScript.color_changed TO
SharedColor.set_color
ROUTE SharedColor.color_changed TO
BoxColor.diffuseColor
sample1.wrl
( display it )
contains the source for that example.
Hint:
To make sure that the prototypes
are loaded at the same as the other scripts of your world, use PROTOs instead
of EXTERNPROTOs and copy the definition of the PROTO from shared.wrl to
your VRML world!
Though the usage of the BlaxxunZone and SharedEvent is highly recommended, you may chose to use your own implementation. However you may not change the naming of the SharedZone node, the type and naming of its fields events and avatars and the naming of the typeFromServer EventIns and typeToServer EventOuts of your shared event.
As mentioned before it is not necessary to use the complete definition of a SharedEvent or SharedZone, you just instantiate the things you need.
It is also not required to use one SharedEvent per field you want to share. You can use all field types in one SharedEvent. But if you need a field type twice, you have to instantiate another SharedEvent.
The possibility to use different VRML worlds for the same blaxxun Contact scene enables you to use SharedEvents to guide or administrate visitors to your VRML world:
The VRML world your guide is accessing contains the possibility to send SharedEvents
( events are routed to a set_type EventIn of a SharedEvent) whereas the VRML
world of your visitors just contains the routing of the EventOuts. Thus they just receive the events but
are not able to send them themselves.
For instance you could share the Viewpoints in your guide world, and thus if your guide
goes to another viewpoint, all visitors would join him there. But no visitor could do the same thing !
This is especially convenient for guided tours with CDBots. Another usage is to open doors,
remove walls, show images or even to take your visitors to another VRML world. You are limited
only by your imagination.
Note: to use this feature you must give every SharedEvent a name !
The blaxxun Object Manager provides a simple mechanism to store shared information. There are 3 different lifetime modes for shared events:
You just have to name the shared events differently to use a different lifetime. Event names starting with F_ will be stored forever (Forever), event names starting with P_ are stored while someone is visiting the place (Populated).
All stored events of a place are sent to a client after he enters the place.
Additionally you can request all stored events explicitly by sending a shared event named get_events to the server.
The shared events can have different access types that affect the distribution to other clients. These are:
Again you just have to name the events differently to set an access type.
Lock
Write lock means that as soon as someone sends this event, it can't be modified by others
unless the locking client releases the lock again or leaves the place. If he leaves the place,
the shared event is unlocked automatically. This can be used e.g. in a ball game to lock the ball
as soon as someone plays the ball to guarantee that only one person plays the ball at the same time.
The event must have a _LOCK in its name to set this access type. An event can be locked by sending
any content and released again by sending an empty string.
A shared event with a name like P_S_LOCK_... is treated as a lock event.
Setting it to the empty string '' releases the lock.
Setting it to any! other string requests the lock from the server.
The server answers with the name of the avatar that has the lock.
This example ( display it ) illustrates the use.
If the lock is not set, you can set it by clicking on the red sphere.
If you have the lock, you can release it by clicking on the green sphere.
Click
( display it )
here
to load the example vrml file.
Distribution
Distribution means that the event is only send to the server and it isn't distributed to other clients.
You just have to add _S_ to the name, e.g. P_S_ will be send only to the server and stored there
while someone is visiting the place.
Pilot
Pilot is a special lock that will be passed to the next user whenever the "pilot" user leaves the place.
It is marked by adding a _PILOT to the name.
In the following
example
( display it ),
the first user to enter receives the Pilot Lock. If this user leaves,
another user receives the Pilot Lock. If the user releases the lock
by clicking on the green sphere, another user receives the lock.
If there is no other user, the lock will go back to the user that had it.
Click ( display it ) here to load the example vrml file.
A shared event with a name like P_S_PILOT_... is treated as a pilot event.
Setting it to the empty string '' releases the lock.
Setting it to any other string requests the lock from the server.
The server answers with the name of the avatar that has the lock.
Group
Usually SharedEvents are distributed to everyone in the scene, even though they are only of
interest to some clients (e.g. the ones being close enough to see the effects of the event
or the clients playing the game). Normally this is no problem but in some cases you might want
to restrict the distribution to a selected group of users e.g. when
History
Additionally special _HIST_ and _RESET extensions are available in 4.1.
The _HIST_ extension tells the Object manager to store the history of this
shared event, that is all values set for it during its lifetime. The _HIST_
extension can be combined with the different access types.
A user will receive the queue of all values set for that shared event, not only
the last one (as normal). For group events he receives the queue
after subscribing.
As the shared event queues may become quite big you need a possibility to reset it:
Setting a Reset shared event (with _RESET as suffix) to 'eventname' resets the
event queue for the shared event named 'eventname', setting a _GRP_RESET event to 'groupname'
resets the shared event queue for the group groupname (see the subscribe event),
that is the history of ALL grouped shared events.
Please note that history shared events require a proper application logic
and enough system resources for the blaxxun Community Server. Especially persistent shared events
need to be used very careful.
There is no inherent right or security mechanism on _RESET events, so
your application should ensure that only authorized clients operate on them.
You will usually use a PILOT or LOCK event to decide which client
resets certain shared event queues.
We recommend to use _HIST_ events only where unavoidable
e.g. where you cannot store the current status in a single shared event (see limitations below)
or where the time line of the event is absolutely necessary. Note that due to the nature
of the net, we cannot absolutely ensure that the user will receive the SharedEvent
queue in the same order as it is sent (though for TCP connections it is highly likely).
This example uses groups and history
shared events.
Hint:
The blaxxun Server SDK can be used to modify the standard behavior
of the Object Manager and to implement your own logic.
You may not send strings of a length exceeding 200 bytes.
Network limitations
It's a big difference whether events are
routed inside the VRML browser, just requiring some program code to execute
on your PC, or whether they are send over a (usually slow) internet connection
to all other visitors of a multi-user place. So special care has to be
taken in the design of shared events. E.g. you should never route fraction_changed
events of a TimeSensor or Interpolator!
The best way is to send either the initial
event, e.g. the touchTime event of a TouchSensor or the final result, e.g
the end position from a PlaneSensor. And to perform the action on all clients
separately, e.g. interpolate to the end position.
You might have noticed that the BlaxxunZone contains EventIns named addEvents and removeEvents. These are supported only by blaxxun Contact plug in, release 4.0 or higher. Additionally an "addEvent" event is NOT send to other clients. It's only performed locally. So make sure that the event that triggers adding a new event is defined as shared event.
Joining or leaving a chat group in 3D
blaxxun Contact supports 2 specially named SharedEvents groupJoin and groupLeave,
providing the possibility to join or leave a chatgroup ( identified via their String field ) via VRML.
To use this feature add 2 SharedEvents to your VRML scene, where one name field reads 'groupJoin'
the other one 'groupLeave'. Once you send the name of the chat group you want to join to the
set_string(stringToServer) EventIn, the client will automatically join respectively create
this chat group if it did not exist.
To create a localized chat groups ie. a chat group defined by a space in VRML
put a proximity sensor in your world which once activated sends the 'group name'
to the groupJoin SharedEvent. Or you might use a TouchSensor which does the same thing.
Note that sending this event is NOT distributed by Contact over the server ( to avoid enforcement of everybody joining a chat group). Please use blaxxun Agents to send enforced chat invitations to selected visitors
You can test it using the small example. If you click on the cone, you join a chatgroup, if you click on the sphere, you leave it again. The chat panel will be automatically activated. So if you activate the Public chat panel again and click on the cone, the Vrmlgroup chat panel will become active.
You can get all the information about the chat in 3D. So you can think of a completely different chat interface, showing all chat on a board in 3D or displaying the chat on top of each avatar. You can even send chat from 3D.
The BlaxxunZone and the Avatar PROTO contain special fields to support it.
PROTO BlaxxunZone [
exposedField MFString sendToChat ""
exposedField MFString groupChatName ""
exposedField MFString groupChat ""
]
If you set a string in sendToChat, it will be sent to the Public chat group.
Incoming chat will be written to the exposedField groupChat. groupChatName
will reflect the name of the chat group that received the chat.
Example4
( display it )
demonstrates the use.
The last 5 chat output lines will be displayed in 3D. The example also shows how to use
the Script EAI. You can send chat from an HTML form and receive it in an HTML frame.
The Avatar proto contains a field chatGesture. If that field exists, Contact will write all chat from that avatar into that field. http://www.blaxxun.com/vrml/avatars/chat.wrl ( display it ) is an avatar that displays all his chat over its head.
You want to put a mirror in your world and display the names of the visitors on a blackboard ?
blaxxun Contact 3D and the blaxxunZone ( for compatibility with older blaxxun Contact versions ) both contain
exposedFields named myAvatarUrl and myAvatarName. blaxxun Contact sets both once you enter
a VRML world. Note that the myAvatarName field is for information only, changing
it via the corresponding eventIn will have no consequences in blaxxun Contact.
However changing myAvatarUrl will replace the visitors avatar once he reloads
or goes to another scene ( see Avatar pages in the Developer guide ).
So its best use is in locker rooms where you get dressed before entering
the main world. blaxxun Contact 4.1 or higher has another field: myAvatarSize, which contains the avatar
size/NavigationInfo as calculated by Contact.
Please note that
Your own avatars nickname:
You can retrieve the users nickname directly from
Contact3d via the EventOut Browser.myAvatarName_changed or from the
exposedField myAvatarName in the BlaxxunZone.
Read that field e.g. to display the name somewhere in 3D.
PROTO BlaxxunZone [
exposedField SFString myAvatarName ""
]
Whenever blaxxun Contact adds an avatar to
the VRML world, it sends an event to the addAvatars eventIn of
the BlaxxunZone. And a removeAvatars
event if the avatar is deleted.
Let's look at a small example
that illustrates the use. The world adds a "shadow" for every avatar in
2 meters distance whenever an avatar is added. It uses the avatar_added
eventOut of the BlaxxunZone and routes it to the children field of
a Transform node:
EXTERNPROTO BlaxxunZone [
eventIn
MFNode addAvatars
eventOut
MFNode avatars_added
eventIn
MFNode removeAvatars
eventOut
MFNode avatars_removed
] "http://www.blaxxun.com/vrml/protos/shared.wrl#BlaxxunZone"
DEF SharedZone BlaxxunZone{}
DEF AvatarShadow Transform
{
translation 0 0 -2
children []
}
ROUTE SharedZone.avatars_added TO AvatarShadow.addChildren
ROUTE SharedZone.avatars_removed TO AvatarShadow.removeChildren
sample2.wrl ( display it ) contains the source for that example.
If the avatars use the standard blaxxun
Avatar PROTO, the VRML world can check the fields of the avatar node, e.g.
to get the current position of an avatar.
Note: this requires node parsing via the EAI or VRMLScript.
sample2.wrl contains an example for finding the touch sensor that is part of the avatar url.
If you beam to another avatar, Contact adds a Viewpoint into the world and positions it 3 meters in front of the other avatar. But you can define your own Viewpoint and specify a different distance.
The BlaxxunZone contains the following fields:
PROTO BlaxxunZone [
exposedField SFNode beamToViewpoint []
exposedField SFFloat beamToDistance 3
]
You can use beamToDistanceto change the distance and beamToViewpoint
to use your own viewpoint, e.g.
DEF VIEW1 Viewpoint {}
DEF SharedZone BlaxxunZone {
beamToViewpoint USE VIEW1
beamToDistance 2
}
Contact uses an efficient method of displaying only the nearest avatars and culling avatars that aren't visible. Additionally you can use LODs to display the avatars with a different level of detail. Of course the avatars can have an LOD built in. But you can also define an LOD for your world that will affect all avatars.
The BlaxxunZone contains the following fields:
PROTO BlaxxunZone [
exposedField MFNode avatarLOD []
exposedField MFFloat avatarRange []
]
The following example displays a grey box whenever an avatar is more than 20 meters away:
DEF SharedZone BlaxxunZone
{
avatarRange [ 20 ]
avatarLOD [
Transform { translation 0 -1 0
children [
Shape {
appearance Appearance { material Material { diffuseColor 0.5 0.5 0.5 } }
geometry Box { size 0.3 1.85 0.3 }
}
]
}
]
}
You can send avatar gestures using the set_myAvatarGesture eventIn of the SharedZone. Contact sets the myAvatarGesture_changed eventOut whenever a gesture is performed whether by the method above or any user action triggering a gesture ( e.g. body language menu ). These gestures are distributed to other clients. If you simply want the users avatar to perform a gesture e.g. in a locker room use the set_myAvatarGesture eventIn of Contact3d (Browser.set_myAvatarGesture). Note this makes sense only in 3rd person mode.
The BlaxxunZone contains the following fields:
PROTO BlaxxunZone [
eventIn SFInt32 set_myAvatarGesture
eventIn SFInt32 myAvatarGestureFromServer
eventOut SFInt32 myAvatarGesture_changed
eventOut SFInt32 myAvatarGestureToServer
]
The chat example
( display it )
contains also a
"send gesture 1" button in HTML
that sends out the gesture1. You can visit the example with 2 clients or activate the third
person mode (type A in the 3D window) to see at the gesture.
Limitations
Only avatars are added that are displayed
in 3D. For performance and bandwidth reasons, blaxxun Contact adds only avatars that
are in your neighborhood. The other avatars are only visible in the people
list and chat.
If you want to get events from all avatars
in the scene, you could use a bot that sends a shared event whenever an
avatar enters the scene.
Right now, you get 3 events
for each avatar ( 3 adds and 2 removes), as first the default avatar is added, then replaced
with the users avatar and in step 3 (unless the users avatar implements our
avatar interface)
replaced with a wrapped version of the users avatar.
We recommend highly to include the BlaxxunZone as a PROTO into the world (no EXTERNPROTO)
to guarantee that it is loaded when blaxxun Contact starts adding avatars.
This way you can additionally improve performance by removing all events and fields
you do not need in your VRML world. E.g. the add/remove avatars/objects are rarely used,
but due to their structure ( complete VRML files/scene graphs added) cost performance.
Shared Objects
Please note that SharedObjects and SharedObjectEvents are only available/useful in a community context.
To fully understand their possibilities you should have a good understanding of
our community concepts.
We will describe only those events and fields, that are useful for your world design.
Overriding the implementation of other events is not recommended (but possible).
For the full implementation see shared.wrl
EXTERNPROTO SharedObject [
exposedField SFVec3f translation
exposedField SFRotation rotation
exposedField SFString name
exposedField SFString id
exposedField MFNode children
eventIn SFBool startMove
eventIn MFString attributesFromServer
exposedField MFString attributes
eventOut MFString attributes_changed
eventOut SFTime touchTime
eventOut SFBool isOver
eventOut SFVec3f newPosition
eventOut SFRotation newRotation
] "http://www.blaxxun.com/vrml/protos/shared.wrl#SharedObject"
Override this implementation if you want to implement a world specific SharedObject handling.
E.g. you could use the attributes field to read a 2D image of the shared object, initially
just load this image and load the VRML representation on an user action. This will decrease download
size for worlds with a lot of SharedObjects.
In the blaxxun Contact configuration file (bxx file) you can specify the attributes that should be read
automatically for all objects. Per default, these are the prize and the count of objects.
[World Params]objectpucolumnvalue TPR,CNT,IMG,CLA
will read the class and image attribute additionally.
Of course you could design your own object movement handling too.
Usually you will not use world specific SharedObject handling, but rather want to
create special SharedObjects. This is done if you implement the SharedObject as a:
EXTERNPROTO SharedObjectEvent [
#shared object fields
eventIn SFString set_name
eventOut SFString name_changed
eventIn SFString set_action
eventOut SFString action_changed
eventIn MFString set_attributes
eventOut MFString attributes_changed
eventIn MFString attributesFromServer
eventOut MFString attributesToServer
eventIn SFTime set_touchTime
eventOut SFTime touchTime
eventIn SFBool set_isOver
eventOut SFBool isOver
] "http://www.blaxxun.com/vrml/protos/shared.wrl#SharedObjectEvent"
Note that the SharedObjectEvent additionally contains all fields of a sharedevent. Usage of those will make the object behave like a sharedevent. However there are some restrictions:
Similar to Avatar LODS Contact provides an LOD mechanism
for objects (blaxxun Contact 4.3 and higher). Note that Contact 4.3 introduces a default
LOD for objects, that is an empty shape displayed for distances
greater than 30 units. Only if you want to overwrite or remove this handling you need
to use the following fieldsin the BlaxxunZone :
PROTO BlaxxunZone [
exposedField MFNode objectLOD []
exposedField MFFloat objectRange []
]
The following example displays a grey box whenever an object is more than 20 meters away:
DEF SharedZone BlaxxunZone
{
objectRange [ 20 ]
objectLOD [
Transform { translation 0 -1 0
children [
Shape {
appearance Appearance { material Material { diffuseColor 0.5 0.5 0.5 } }
geometry Box { size 0.3 1.85 0.3 }
}
]
}
]
}
VRML
97 Specification
VRML
97 Node Reference
The
Annotated VRML97 Reference Manual
General blaxxun
Developer Information