Help - Search - Members - Calendar
Full Version: The VRML Site Viewer
Dumpshock Forums > Discussion > Community Projects
Omega Skip
Been quite some time since I last posted here, but I think this might be of interest to some of you guys and gals.

A few sessions back, one of the players in my group said the following:
QUOTE
This is the day I've been dreaming of ever since I started playing this game.

What happened was this: They were about to infiltrate a corporate facility, and instead of giving them the usual aerial photographs, floorplans, etc., I took out my laptop, placed it on the table, and opened a browser window showing them a fully 3D model of the complex. I built the model using the VRMLanguage, which most of you should have at least heard about.
Although the introduction of 3D models has helped me a lot to improve the gaming experience, I've found that the standard VRML interface is absolutely not suited for this kind of application. There are three native navigation modes in VRML, namely "WALK", "FLY", and "EXAMINE", and neither one of them can provide the kind of navigation that I have in mind. So now I'm programming a custom interface, called the Site Viewer, which I'd like to tell you about.

What it is
Right now, the Site Viewer allows a user to navigate a VRML scene in either Internet Explorer or Netscape (or any other web browser capable of displaying VRML scenes) by clicking on any building / structure that he wants to examine. Upon clicking on a building, the view will focus on that building, and place the viewer at a predefined distance from the selected geometry. Now, the user can orbit around the building, zoom in, zoom out, and also select another building at any time. The advantage over the standard interface is that when using the Site Viewer, you always retain a preset horizontal viewing angle (usually 30° down) which prevents you from getting lost or finding yourself at an odd viewing angle. (which happens A LOT when using the standard FLY or EXAMINE modes)

What it isn't
The Site Viewer is not a tool to build your own sites from scratch, sorry. But, and this is a big but, once you do have some kind of VRML model, you can use the Site Viewer infrastructure to assemble your models into a scene which can be viewed comfortably. Also, the Site Viewer is not intended as a replacement for floorplans; paper is just so much more flexible than a PC, it would require obscene amounts of programming to achieve that level of flexibility on a computer. Besides, in the end you'd end up with Neverwinter Nights anyway...

What it will be
In its current state, the Site Viewer infrastructure allows exterior viewing of separate buildings, but nothing more. I'm planning to enhance its functionality by adding the ability to select single floor, and then viewing the interior of that single floor. Imagine clicking on a multi-storied building, viewing the exterior, then selecting one floor from a list, phasing out the rest of the building, and then viewing only the selected floor.

Bottom Line
I do realize that 3D modeling isn't exactly everybody's bag, but I'm more than willing to share my ideas on the topic with anybody who's interested. Personally, I've found that the introduction of 3D models has been a huge improvement for my game, especially since you'd expect this kind of thing to be commonplace in the sixth world. I'll probably throw together a Proof Of Concept scene later, and post the code for it here. Relax, VRML code is very small - usually less than 200 lines of code total.

Okay, that's it for now. Expect the POC scene later today.
Omega Skip
Remember when I said, 200 lines tops? Well, I decided to put a little effort into it, and modeled the brawl zone as seen in the adventure "A killing glare". I made a zip archive that's about 30kb in size containing all the necessary files. You can download it from my homesite here. Once you've unzipped the files, you can open the file named Sceneview.wrl with your web browser. In case the scene doesn't display, you probably need a VRML plugin; I highly recommend Parallelgraphics' Cortona Player Plugin, since it's the most powerful one available. (The beast even supports bumpmaps, reflective mapping, and lots more amazing stuff you'd never expect to see outside of a videogame)

About the presented scene:
I tried to keep things as simple as possible with this scene, using only standard primitives (in this case, only boxes) and no textures. Still, the scene should illustrate pretty well where I'm going with this. You can open all the files with a text editor and take a look under the hood to see how I arranged the setting. The files titled "BaseXX.wrl" are the single buildings you see, and they are all inlined at the end of the "Sceneview.wrl" file. Like I said, you're invited to examine the files closely; after all, that's why I started this thread.
And for all those of you who are too paranoid to open .js files, here's the contents of the SVIII.js file:
QUOTE

function zoomIn() { zoom_in = true; }

function zoomOut() { zoom_in = false; }

function zoom()
{
    if(zoom_in)
    {
        local_at[0] = local_at[1] = old_local[0] = old_local[1] = 0;
        local_at[2] = old_local[2] -= 2;
    }
    else
    {
        local_at[0] = local_at[1] = old_local[0] = old_local[1] = 0;
        local_at[2] = old_local[2] += 2;
    }
}

function switchView(new_view)
{
    interp_vector = new_view.subtract(old_view);
    zoom_distance = 50 - old_local[2];
}

function rotateZ()
{
    if (rotate_plus_Z)
    {
        rotate_view_Z[1] = 1;
        rotate_view_Z[0] = rotate_view_Z[2] = 0;
        rotate_view_Z[3] += 0.02;
    }
    else if (rotate_minus_Z)
    {
        rotate_view_Z[1] = 1;
        rotate_view_Z[0] = rotate_view_Z[2] = 0;
        rotate_view_Z[3] -= 0.02;
    }
}

function activateRotationPlusZ(over_mouse) { rotate_plus_Z = over_mouse; }
function activateRotationMinusZ(over_mouse) { rotate_minus_Z = over_mouse; }

function interpolateView(fract)
{
    viewer_at = old_view.add(interp_vector.multiply(fract));
//  viewer_at[0] = old_view[0] + interp_vector[0] * fract;
//  viewer_at[1] = old_view[1] + interp_vector[1] * fract;
//  viewer_at[2] = old_view[2] + interp_vector[2] * fract;
    local_at[0] = local_at[1] = 0;
    local_at[2] = old_local[2] + zoom_distance * fract;
}

function timerStopped(is)
{
    if (!is)
    {
        old_view = viewer_at;
        old_local = local_at;
    }
}

This will make a lot more sense if you compare it with the script node and ROUTEs contained in Sceneview.wrl.

I think this example illustrates pretty well that even if you're not 100% familiar with VRML, you can work with the basic framework of the Sceneviewer (which is, basically, the SVIII script file and everything in Sceneview.wrl up to the start of the actual scene geometry) and produce some helpful models.
Grey
This sounds like some interesting stuff. I don't have the time right now, but I'll give it a look over and let you know what I think of it (giving you a laymen's POV).
Spookymonster
Omega, any suggestions on a Mozilla-compatable VRML plug-in?
Omega Skip
@Spooky: To be honest, I've never personally tried any non-IE/NS plugins, so this list contains, on the one hand, a lot of alternative browser plug-ins, but on the other hand I can't really give you any first-hand recommendations. Sorry. But I hope you'll find one on the list that suits you. =)

Please note also that, depending on what plugin you use, your mileage may vary. Overall, Cortona provides the highest quality, but unfortunately, it's only compatible with the two most common browsers. (afaik)
Connor
The plug-in from that cortona sight works fine in Mozilla. Remember, mozilla *is* netscape. If mozilla doesn't see the plug-in just make sure it's in the mozilla plug-in dir. It's easy enough to copy.

For any mac users out there, they have a mac version you can download here.

And I have to say I think I'm going to look into this VRML stuff for my games.


[edit]

Looking around at VRML stuff and I came across this site...

American University of Beirut

The site is a campus tour with plain photography, VRML walk through, a standard map, etc....

Imagine the possiblities!
Spookymonster
Didn't see the Netscape mention at first; my bad. Cortona works fine under Mozilla Firebird 0.7 in Windows, in case anyone's interested.

Very promising, Omega. I'll definitely have to look for some shareware modelling tools to play wit smile.gif.
Connor
White Dune is a freeware editor I ran across earlier today. I've played around with it a bit and it seems to be quite usable.

They've even got a "4kids" build with less options and a more straightforward interface, which is kind of nice.

This is something I'm definately going to try and capitalize upon for my games. Omega, thanks for bringing it to our attention.
DV8
Omega, this idea is awesome. How hard is it to model something like this for someone with a bit of programming knowledge and experience?
Omega Skip
@DV8: Not hard at all if you've got some practical and theoretical programming experience. VRML uses, at its core, a scene graph to display a scene, with nodes and leaves just like any other old graph. It's a very simple and easy to understand concept.

My main goal with the Site Viewer is to let others use the interface without having to delve too deeply into the higher mysteries of VRML. I structured the SceneView.wrl file so that everything up to where it says "Begin Scene Geometry" is just the infrastructure which implements the interface's functionality. This means that at the very end of the file, you can inline your own VRML models using the TransformSensor node I prototyped at the beginning of the file, without having to worry what exactly all that means.

So all you really need is
(1) some sort of editor with which you make your models - personally, I use 3DStudio Max, but I imagine most of you will be better off with a simpler, and not so goddang expensive, software - there's a few really good freeware editors out there. If you really want to get your hands dirty, you can use a text editor to make VRML models, but I assure you that this is a pain.
(2) A VRML capable browser
(3) A text editor that lets you edit SceneView.wrl

Time permitting, I'll post a more lengthy tutorial on basic VRML modeling on the weekend. If you'd like to see what else can be done using VRML, you may take a look at my website (which I haven't updated in ages)

www.virtuality.gmxhome.de
DV8
Omega: Eagerly awaiting your tutorial.
Omega Skip
Allrighty then, here it goes...



Scope

The goal of this tutorial is to familiarize you with some of the basics behind VRML, to give you a general understanding of how this language describes a 3-dimensional world, and ultimately, to let you create some simple models which you can then inline into the Site Viewer. I will introduce you to some concepts such as scene graphs, nodes, fields, vectors, and 3 dimensional coordinate systems. Programming skills and a little bit of mathematical knowledge (especially analytical geometry) help, but I'll try to keep things simple so that the less technically-inclined can understand what the hell I'm talking about.
What you'll need:
- A little time
- A VRML capable browser + plugin
- A text editor (we will be doing all our work in plain VRML code; don't panic, it ain't hard)


Part One: Nodes and Fields

VRML (in case you're wondering, Virtual Reality Modeling Language) uses a scene graph to describe 3d scenes and models. Every graph consists of nodes and leaves: Your family tree, for example, is a graph that has your parents as a node and you and your siblings as the node's leaves. What this means for VRML is this: Each element in a VRML file is either a NODE or a FIELD. You could say that nodes are "the stuff that happens", while fields are "how the stuff happens". Nodes are, for example, the four basic primitves called Box, Sphere, Cylinder, and Cone. A Box node has a field that contains the box's height, width, and length. So, if you were to include a box that's 2 units wide, 3 units high, and 5 units long somewhere in your file, then you'd write it like this:

Box { size 2.0 3.0 5.0 }

When adding an element to your file, you first write the name of the node (in this case, Box), followed by a {, then the name of each field you want to specify, followed by the data you want to assign to the field, and finally a closing }. Like I said, nodes are "the stuff", and fields are the stuff's attributes. A node can have multiple fields, like for example the Cylinder node:

Cylinder { radius 2.0 height 6.0 }

or, if you want your code to look more tidy:

Cylinder
{
radius 2.0
height 6.0
}

Now, there's two interesting differences between the Box and Cylinder nodes: Box only has one field, and the two fields of the Cylinder node accept a different kind of data. Like this:

size 2.0 3.0 5.0 VS radius 2.0

Each field accepts a different kind of data. The most important data types in VRML are: 3-dimensional vectors, floating point numbers, boolean values, text strings, time, and nodes. Yes, some fields accept nodes as data. This is a very important detail! For example, the Transform node, which specifies translations, rotations, and scale changes in a 3-d coordinate system, also has a fourth field, like this:

Transform
{
translation 0.0 0.0 0.0
rotation 0.0 0.0 0.0 0.0
scale 1.0 1.0 1.0
children
[
...
]
}

The node's children field can contain any number of nodes, and the changes specified in the translation, rotation, and scale fields are applied to all these nodes as a group - which is why the Transform node is called a grouping node. Of course, a Transfrom node could have another Transform node as a child, and so on; you can nest as many nodes as you want.

You should now know the following things about VRML:
- Everythings is either a node or a field;
- Nodes are things like boxes, cylinders, geometric transformations, etc;
- Fields contain data to specifiy these boxes, cylinders, etc;
- Nodes can be nested.




Part Two: Coordinate Systems

If you look at the above example, you'll see that the Transform's translation field has, as its value, a triplet of numbers. These triplets are called 3-dimensional vectors. You probably all remember two dimensional coordinate systems from calculus 101. Like, when you had to plot f(x) = x˛, and other functions, you had a coordinate system with the horizontal x axis and the vertical y axis. Every point in this coordinate system can be described as a pair of numbers, like (2,10), meaning "go right 2 units, go up 10 units". Or (-2,-10), "go left 2 units, go down 10 units". VRML uses the exact same method, but in order to describe a three dimensional world properly, we need another dimension, called the z axis.

Make a fist with your left hand. Now, extend the thumb, the index, and the middle finger. Point your index at the ceiling; Your middle finger should be pointing right, and your thumb should point towards you. Your middle finger now represents VRML's x axis, your index is the y axis, and your thumb is the z axis. Keep this in mind when you think of the coordinate system.

Remember the Box's size field? Take a look at what we specified above: 2 3 5. This means: 2 units on the x axis (width), 3 units on the y axis (height), and 5 units on the z axis (length). Likewise, you can specify a translation by assigning a vector to the Transform node's translation field.
Maybe we should now talk a little bit about dimensions: How does the coordinate system apply to VRML nodes, and how do we make use of the coordinate system?




Part Three: Our first shape

Ok, now we're going to apply what we learned so far. Open up the text editor of your choice. (Personally, I prefer the standard windows editor) Make a new file. Now, we'll give the file a header that specifies it as a VRML file, like so:

#VRML V2.0 utf8

Type in the header EXACTLY like this. VRML is case sensitive. Wait, maybe I wasn't loud enough: VRML IS CASE SENSITIVE. If you write something like transform { ... } instead of Transform { ... }, then your scene won't display.

Now that you've got the header, you might want to add a comment that says what this file is, like so:
QUOTE

#VRML V2.0 utf8
#Arglebarglegooblegoop

If you want to add a comment to your file, just add a #, followed by your comment. Now, let's add something to the scene. How about a box?
QUOTE

#VRML V2.0 utf8
#Arglebarglegooblegoop

Box { size 2.0 3.0 5.0 }

view this scene

Now, save this file as, oh, I don't know, whatever.wrl and open it with your browser.
...
Yuck! This box looks like ass! This is because the Box node specifies only geometry, but not material properties. The node that specifies things like color, transparency, etc. is called:

Material
{
diffuseColor 0.3 0.9 0.1 # This is an RGB value; 0 0 0 is Black, 1 1 1 is White, and so on
emissiveColor 0.1 0.1 0.1 # This is also an RGB value specifying a material's self-illumination
}

The Material node is usually contained by an Appearance node, like so:

Appearance
{
material Material { ... } # material is another field that has a node as its data type: Material
}

Now, all we need is a node that combines geometry and material. This node is called, reasonably enough, Shape:
QUOTE

#VRML V2.0 utf8
#Arglebarglegooblegoop

Shape
{
    appearance Appearance  # again, the datatype of the appearance field is a node
    {
        material Material
        {
            diffuseColor 0.3 0.9 0.1    # Green
            emissiveColor 0.1 0.1 0.1
        }
    }
    geometry Box  # geometry is yet another field that accepts nodes as data
    {
        size 2.0 3.0 5.0
    }
}

view this scene

Ok, now we've got a nice, green box. Let's expand our Scene a little by adding one of each of the other standard primitives (cone, sphere, cylinder). For that, we'll need three more shape nodes, and for each we'll specify a new diffuseColor. Like so:
QUOTE

#VRML V2.0 utf8
#Arglebarglegooblegoop

Shape
{
    appearance Appearance
    {
        material Material
        {
            diffuseColor 0.3 0.9 0.1    # Green
            emissiveColor 0.1 0.1 0.1
        }
    }
    geometry Box
    {
        size 2.0 3.0 5.0
    }
}

# Copy, paste =)

Shape
{
    appearance Appearance
    {
        material Material
        {
            diffuseColor 0.9 0.3 0.1 # Red
            emissiveColor 0.1 0.1 0.1
        }
    }
    geometry Cylinder
    {
            radius 1.5
            height 4
    }
}

view this scene

Now we've added a cylinder, but wait: I smell BS - The cylinder and the Box occupy the same space! What gives? This is so lame! Ok, so let's fix this by translating the box and the cylinder a little, like so:
QUOTE

#VRML V2.0 utf8
#Arglebarglegooblegoop

Transform
{
    translation 2 0 0      # Two units to the right
    children
    [
        Shape
        {
            appearance Appearance
            {
                material Material
                {
                    diffuseColor 0.3 0.9 0.1    # Green
                    emissiveColor 0.1 0.1 0.1
                }
            }
            geometry Box
            {
                size 2.0 3.0 5.0
            }
        }
    ]
}

# Copy, paste =)

Transform
{
    translation -2 0 0      # Two units to the left
    children
    [
        Shape
        {
            appearance Appearance
            {
                material Material
                {
                    diffuseColor 0.9 0.3 0.1 # Orange
                    emissiveColor 0.1 0.1 0.1
                }
            }
            geometry Cylinder
            {
                    radius 1.5
                    height 4
            }
        }
    ]
}

# Ok, cool, now here's the other two primitves

Transform
{
    translation -6 0 0      # Six units to the left
    children
    [
        Shape
        {
            appearance Appearance
            {
                material Material
                {
                    diffuseColor 0.1 0.3 0.9 # Red
                    emissiveColor 0.1 0.1 0.1
                }
            }
            geometry Cone
            {
                    bottomRadius 1.5
                    height 4
            }
        }
    ]
}

Transform
{
    translation 6 0 0      # Six units to the right
    children
    [
        Shape
        {
            appearance Appearance
            {
                material Material
                {
                    diffuseColor 0.9 0.1 0.3 # Red
                    emissiveColor 0.1 0.1 0.1
                }
            }
            geometry Sphere
            {
                    radius 2
            }
        }
    ]
}

view this scene

Allright. Now go and experiment a little. Nodes you should know by now:
- Transform
- Shape
- Appearance
- Material
- Box
- Cylinder
- Cone
- Sphere

In the next tutorial, I'll go into more detail about the Transform node, individual coordinate systems, and object dimensions. Expect the next part to be up sometime early on monday.

If you've got any questions / suggestions / complaints, please don't keep them to yourself! Let me know what you think, and don't be afraid to post your own .wrl's if you've got a specific question.
DV8
Fucking awesome. I know what I'm doing tonight. Omega, you have hereby earned the title of "Awesome DSF'er." If I manage to squeeze anything sensible or worthwhile out of your tutorial I'll make sure to upload it for your viewing pleasure.
Omega Skip
Wait till you see the next part of the tutorial... smile.gif
Omega Skip
In the last tutorial, I briefly touched on some key concepts behind VRML: What does a VRML file look like, what kind of elements exist in this language, what's a node or a field. In this tutorial, I'll go into a little more detail on the Transform and Shape nodes, and explain a little more about the coordinate system(s) used by VRML.

Part Four: Translating Geometry

If you remember, last time we used the Transform node to arrange four Shapes in a scene. Before we applied a translation to them, each Shape occupied roughly the same space. Every Shape you create is placed at the coordinate system's origin by default. The origin is at the coordinates 0.0 0.0 0.0. This leads to a new question: When geometry is placed at the origin, how do I know what space it occupies exactly? For example, you create a box that's 2 units wide, 3 units high, and 5 units wide - what coordinates is the box's top at, where is its bottom?

QUOTE

#VRML V2.0 utf8
#Transforming Geometry

Shape
{
    appearance Appearance
    {
        material Material { diffuseColor 0.3 0.9 0.1 }  # Greenish
    }
    geometry Box { size 2 3 5 }
}

view this scene

The answer to the above question is actually quite simple. Geometry is placed in a scene so that its center coincides with the origin. Look at the box. Its center is at half width, half height, half depth - basically, right in the middle. So at what height is the box's top? To determine this, simply go up on the Y axis for 1.5 units. (which is the box's specified height, divided by two) Likewise, its bottom is at -1.5 units on the Y axis. You can determine the box's front, back, and side positions the same way: The sides are at +/- 1 on the X axis, while front and back are at +/- 2.5 on the Z axis.

Ok. Now let's place another box on top of this box. The second box shall be 1 unit wide, 0.5 units high, and 1 unit long. We can put this box on top of our first box by applying a translation to it - which means that we'll put our second box inside a Transform node. You should remember Transform nodes from the last tutorial, but just so that we all know what we're talking about, here it is again:

Transform
{
translation 0 0 0
rotation 0 0 0 0
scale 1 1 1
children [ ... ]
}

Look at the node's fields. What a Transform node does, is that it applies a local coordinate system to all it's child nodes. All geometry contained in a Transform node's children field is placed at the local coordinate system's origin - and that local origin is specified by the Transform node's translation field. Let's see how this works in our current example.

The box we wanted to add is supposed to sit on top of our first box. This means that we want our second box's bottom to be at the same height as our first box's top. Our first box's top is at a height of 1.5 units on the Y axis, and our second box's bottom is at -0.25 units. This means we need to move our second box up 1.75 units on the Y axis, like so:

QUOTE

#VRML V2.0 utf8
#Transforming Geometry

Shape    # Our first Box
{
    appearance Appearance
    {
        material Material { diffuseColor 0.3 0.9 0.1 }  # Greenish
    }
    geometry Box { size 2 3 5 }
}

Transform
{
    translation 0 1.75 0    # up 1.75 units on the Y axis
    # leaving the other fields unspecified makes VRML assign default values to them
    children
    [
        Shape    # The second box
        {
            appearance Appearance
            {
                material Material { diffuseColor 0.1 0.3 0.9 }    # Blueish
            }
            geometry Box { size 1 0.5 1 }
        }
    ]
}

view this scene

Voila, a blue box on top of a green box. Let's get fancy, and place the second box so that its front and right side coincide with the lower box's front and right side. The first box's right side was at 1 units, our second box's right side is currently at 0.5 units, so we need to move it 0.5 units to the right. To make the fronts coincide, we need to move the second box 2 units along the Z axis. The scene now looks like this:

QUOTE

#VRML V2.0 utf8
#Transforming Geometry

Shape    # Our first Box
{
    appearance Appearance
    {
        material Material { diffuseColor 0.3 0.9 0.1 }  # Greenish
    }
    geometry Box { size 2 3 5 }
}

Transform
{
    translation 0.5 1.75 2    # right 0.5 units, up 1.75, forward 2 units
    # leaving the other fields unspecified makes VRML assign default values to them
    children
    [
        Shape    # The second box
        {
            appearance Appearance
            {
                material Material { diffuseColor 0.1 0.3 0.9 }    # Blueish
            }
            geometry Box { size 1 0.5 1 }
        }
    ]
}

view this scene

The mechanics to determine your geometry's boundaries apply to the other primitves as well: A cylinder with a height of 10 and a radius of 3.5 with its local origin at 3 9 21 would have its top at 3 14 21, its bottom at 3 4 21, and so on.




Part Five: Rotating Geometry

You may have already noticed that the rotation field has a standard value of 0.0 0.0 0.0 0.0, which isn't a 3-d vector since it's got four elements instead of just three. This kind of data is called a rotaional value: Its first three elements specifiy an axis of rotation, and the fourth element specifies an angle, using radians for its unit. (Degree to Radian conversion: (x°/180) * PI, so 90° = 1.5708, and so on) Personally, I've found using more than one value for the axis of rotation to be a pain. That's why I recommend using only a "pure" axis, like 1 0 0 (X axis), or 0 1 0 (Y axis), or 0 0 1 (Z axis). Most of the time you won't need to rotate geometry around more than one axis anyway; but if you do, you may find the technique I'm about to describe helpful. Also, I'll explain some more small details about VRML that will be of help to you.

Allright. I can smell that y'all are getting tired of boxes, so we'll be using some other kind of geometry now. Our next project is to create a model that displays the three axes of the standard coordinate system. To do this, we'll create three arrows out of standard primitves, and then rotate them to align with the corresponding axis. Let's get started.

The first step is to create an arrow. We'll take a Cylinder, and add a Cone at it's top using a Transform node. Since the Cylinder and the Cone of each arrow should look alike, it would make sense to define an Appearance node that can then be shared by the two Shape nodes. VRML allows this through the DEF statement. You can use a DEF(ine) anywhere where you'd normally put a node. The syntax for a DEF statement goes like this:

DEF [valid name] [node] { ... }

Anytime you want to use the predefined node, you simply use a USE statement wherever you'd normally put your node. Valid names begin with a letter, use only letters, digits, _'s and -'s, and don't use any already existing names, like Box. Remember: VRML IS CASE SENSITIVE!

QUOTE

#VRML V2.0 utf8
#The Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Shape
{
    appearance USE App_Blue
    geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
}
Transform
{
    translation 0 2.5 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
        }
    ]
}

view this scene

Ok, now we've got our first arrow. It's pointing up, which is fine, but the arrow's bottom is 2 units below the origin. We want the arrow to start from the origin, so we need to modifiy our scene like this:

QUOTE

#VRML V2.0 utf8
#The Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Transform # this node contains the Arrow_Line and the Transform node containing the Arrow_Head
{
    translation 0 2 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
        },    # Note that I added a comma, to tell the Transform node that after this child follows another
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Blue
                    geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
                }
            ]
        }
    ]
}

view this scene

I feel like I should explain a few things here. The first Transform node speficies a local coordinate system for both the Shape node and the second Transform node. The second (nested) Transform node in turn specifies its local coordinate system in relation to the coordinate system that this node itself is placed in. I could have just as well used one Transform for the Arrow_Line, moving it up 2 units, and one separate Transform for the Arrow_Head, moving it up 4.5 units. But this method is more elegant, since it groups elements that belong together.
Now, we'll add a second arrow, using the geometry nodes we DEF'ed earlier, but with another appearance.

QUOTE

#VRML V2.0 utf8
#The Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Transform # this node contains the Arrow_Line and the Transform node containing the Arrow_Head
{
    translation 0 2 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
        },    # Note that I added a comma, to tell the Transform node that after this child follows another
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Blue
                    geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
                }
            ]
        }
    ]
}

Transform # this node contains the second Arrow
{
    translation 2 0 0 # This arrow will be our X-Axis
    rotation 0 0 1 -1.5708  # Counter-clockwise Rotation along the Z-Axis
    children
    [
        Shape
        {
            appearance USE App_Red
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Red
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

view this scene

This time, I added a rotation to the second root Transform node. This rotation specifies (a) a rotation along the Z-axis, and (b) a counter-clockwise rotation of 90°, or 1.5708 radians. If you want to find out along which axis you want to rotate your geometry, the left-hand rule I introduced last time comes in extremely handy. Also, positive rotational values specifiy clockwise rotations along the specified axis, while negative values mean counter-clockwise rotation.
Ok, now it's time to add the last of our three arrows, like so:

QUOTE

#VRML V2.0 utf8
#The Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Transform # this node contains the Arrow_Line and the Transform node containing the Arrow_Head
{
    translation 0 2 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
        },    # Note that I added a comma, to tell the Transform node that after this child follows another
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Blue
                    geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
                }
            ]
        }
    ]
}

Transform # this node contains the second Arrow
{
    translation 2 0 0 # This arrow will be our X-Axis
    rotation 0 0 1 -1.5708  # Counter-clockwise Rotation along the Z-Axis
    children
    [
        Shape
        {
            appearance USE App_Red
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Red
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

Transform # this node contains the third Arrow
{
    translation 0 0 2 # This arrow will be our Z-Axis
    rotation 1 0 0 1.5708  # Left-Hand rule: rotate clockwise along X-Axis
    children
    [
        Shape
        {
            appearance USE App_Green
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Green
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

view this scene

Looks nice, doesn't it? Let me go back a little and talk about rotations along more than one axis. I said above that it's a pain, and it totally is, if you try to rotate along multiple axes at once. But, just like we nested translations, we can also nest rotations. For example, in the last example we could have made life a little harder by using two instead of one rotations for the last arrow, like so:

QUOTE

Transform
{
    rotation 0 1 0 -1.5708
    children
    [
        Transform
        {
            translation 2 0 0
            rotation 0 0 1 -1.5708
            children [ ... ] # Arrow
        }
    ]
}


This is the same Transform as for the second arrow, but rotated 90° ccw along the Y axis.
Now, let's take a look at scaling geometry.




Part Six: Scaling Geometry

So far, we've translated and rotated geometry like mad, and had lots of fun doing it. Right? Next we'll take a look at the Transform node's scale field. The standard value for scale is 1 1 1 - which means that every axis of the local coordinate system specified by the Transform node is multiplied with 1. Just for giggles, let's take the last example, but specify scales for the X and Z axis arrows.

QUOTE

#VRML V2.0 utf8
#The Scaled Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Transform # this node contains the Arrow_Line and the Transform node containing the Arrow_Head
{
    translation 0 2 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
        },    # Note that I added a comma, to tell the Transform node that after this child follows another
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Blue
                    geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
                }
            ]
        }
    ]
}

Transform # this node contains the second Arrow
{
    translation 2 0 0 # This arrow will be our X-Axis
    rotation 0 0 1 -1.5708  # Counter-clockwise Rotation along the Z-Axis
    scale 1 2 1  # stretch along the LOCAL Y-Axis
    children
    [
        Shape
        {
            appearance USE App_Red
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Red
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

Transform # this node contains the third Arrow
{
    translation 0 0 2 # This arrow will be our Z-Axis
    rotation 1 0 0 1.5708  # Left-Hand rule: rotate clockwise along X-Axis
    scale 1 0.5 1 # shrink along the LOCAL Y-Axis
    children
    [
        Shape
        {
            appearance USE App_Green
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Green
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

view this scene

You'll notice that the ends of the scaled arrows no longer coincide with the standard coordinate system. That's because scale changes are applied at the center of the local coordinate system. So, if we wanted to fix our arrows so that the ends again meet at the origin, we would have to proceed like this: First, we need to determine the new length of our arrows, and then we need to adjust the root transforms accordingly. Our scaled X-Axis arrow is now 8 units high instead of 4 (since the scale change is applied at the LOCAL Y axis, it affects the height value, but not the radius), so we need to translate it right 4 units instead of two. That way, the bottom of this arrow again lies at the origin of the standard coordinate system. Likewise, since the Z-Axis arrow now is only 2 units long, we need to adjust the translation to 1 instead of two units, like so:

QUOTE

#VRML V2.0 utf8
#The Scaled Coordinate System

DEF App_Red Appearance { material Material { diffuseColor 0.9 0.2 0.1 } }
DEF App_Green Appearance { material Material { diffuseColor 0.2 0.9 0.1 } }
DEF App_Blue Appearance { material Material { diffuseColor 0.1 0.2 0.9 } }

Transform # this node contains the Arrow_Line and the Transform node containing the Arrow_Head
{
    translation 0 2 0
    children
    [
        Shape
        {
            appearance USE App_Blue
            geometry DEF Arrow_Line Cylinder { radius 0.15 height 4 }
        },    # Note that I added a comma, to tell the Transform node that after this child follows another
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Blue
                    geometry DEF Arrow_Head Cone { bottomRadius 0.4 height 1 }
                }
            ]
        }
    ]
}

Transform # this node contains the second Arrow
{
    translation 4 0 0 # This arrow will be our X-Axis
    rotation 0 0 1 -1.5708  # Counter-clockwise Rotation along the Z-Axis
    scale 2 1 1  # stretch along the X-Axis
    children
    [
        Shape
        {
            appearance USE App_Red
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Red
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

Transform # this node contains the third Arrow
{
    translation 0 0 1 # This arrow will be our Z-Axis
    rotation 1 0 0 1.5708  # Left-Hand rule: rotate clockwise along X-Axis
    scale 1 1 2
    children
    [
        Shape
        {
            appearance USE App_Green
            geometry USE Arrow_Line
        },
        Transform
        {
            translation 0 2.5 0
            children
            [
                Shape
                {
                    appearance USE App_Green
                    geometry USE Arrow_Head
                }
            ]
        }
    ]
}

view this scene

That fixed it. To tell the truth, most of the time you won't be needing to change the scale of stuff, except when you want special kinds of geometry. The real strength of the scale field is that you can specify non-uniform scale changes, which let you easily create flat arrows (try shrinking the Y-Axis arrow from the above example along its local Z-Axis, for example), or elipsoids (by scaling a sphere to shrink along one axis while stretching it along another).

This wraps up this tutorial. You should now know a little about the following things:
- Translating, rotating, and scaling geometry
- How coordinate systems are applied
- How to find a shape's boundaries
- How DEF / USE statements work




Outlook

In the next installment, I'll introduce you to the Inline node, run a few examples of how you can combine standard primitves to create simple objects like trees, buildings, bridges, cars, etc., and finally explain in detail how you can inline your stuff into the Site Viewer.
I can't give you any exact date when the next part'll be up, but you can expect it before Friday.
Fahr
this reminds me of PovRay....

Looks good, any chance of getting building libraries made? like if I make an office building, someplace permanaent to leave the model so others can use it?

that way we could eventually just grab a bunch of peices and transform them where we wanted them...

-Mike R.
Omega Skip
The model library idea sounds intriguing, definitely something to look into.

Oh, and while we're at it, two days ago I showed my girlfriend the latest build of the viewer interface, and she complained. And you know, when my girlfriend complains, then something must be amiss - so I upgraded the interface.

Improvements:
- added two additional rotation buttons so that you can tilt your view up / down
- capped tilt to 0° < tilt° < 90° (you'll see)
- capped "zoom in" to 25 units minimum distance to target
- added a speed meter where you can set the rotation / zoom speed anywhere from 0 - 200 % (drag the indicator to where you want it on the meter - you'll see)
- rearranged the buttons so that it looks good when switching to fullscreen mode (hitting F11 in Explorer or similar method in other browser recommended over using Cortona's "Fullscreen" option)

You can grab the zip file from my homepage here.

Also, I included a 95% finished Site that I'll be using in my campaign: An abandoned temple ruin in the Sahara desert. Enjoy!
Connor
What about adding in a panning interface so the camera can be moved left/right up/down instead of just the zoom and rotation?

And the temple complex looks quite awesome. Hopefully I can get caught up on my work for the week and get some time to play around with this.
Omega Skip
I've been thinking about that (panning), but to tell the truth, I've always felt like the interface would become too crowded if I tried to squeeze in four more buttons. Technically it's not a problem, but if you find a good place for the viewer's pivot points, then usually you won't want to pan.

But if you really wanted panning, would you rather have a vertical or horizontal plane of movement, or both?
Connor
Well, if only one was there I'd be annoyed at the loss of the other... But what about making the interface buttons a little smaller? They're quite large as it is and it wouldn't hurt to reduce the size a bit to make room.
Omega Skip
Allrighty then, I updated the interface as requested. You can download it, as usual, from my hpage here.

I'm semihappy with the result; kinda sucks that pan-changes are applied locally, maybe I'll fix that later this week. But the fact that re-clicking a building restores local translation makes this much less of an issue, methinks.
Connor
That's much better. Thanks for the improvements!
Omega Skip
I said "before Friday", didn't I? Err, yeah, I meant... "Friday" - as in, "Saturday". This week was a little bit more busy than I though it'd be, which is no excuse - I know. Tutorial will be up Saturday. If not, you can revoke the "awesome DSFer" status. smile.gif
DV8
I've been toying with this a little bit, but I haven't been able to squeeze out anything worth showing to you guys. Right now it's just a plane with a bunch of geometric shapes. I've downloaded White_Dunes, which doesn't seem to work so well at home - probably because of the dual monitors - as it does at work. I think it's very handy, especially for transforming basic shapes.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Dumpshock Forums © 2001-2012