Up Hill and Down Dale

Now we are walking, but as you have probably noticed, as soon as we get to the bumpy areas at the side of the world we start sinking into them- we are not walking up or down the hills, we are just ploughing through them as though they weren’t there at all. Which is, if you think about it, fair enough because we haven’t done anything to change the vertical position of the purple dude, we have just set up controls that allow him to move around on the flat. So next up we need to make him handle the ups and downs of the world.

The way we do this will be to use the picking capabilities of Java3D. These are based on the creation of a PickTool that enables us to create a picking shape. There are various shapes available to us, all extending javax.media.j3d.PickShape and each one offering a different shape that we can create and that will tell us what objects in the world intersect with it. For this task we will use a PickRay, which creates a straight line from a given point in a given direction. We can create this from the centre of our character straight down and then move him up or down according to where the ground is. This will require a few changes to the behaviour and the calling code –the PickRay will have to be created outside the behaviour and passed to it and we will have to set pickability so that the landscape can be picked but the character can’t.

So looking at ClimbMorph you will notice that only a couple of places have changed. First up we now have a private PickTool picker; declared and handed to the Behaviour when it is created. We also have a whole lot of changes to the moveStep method, which we will look at now:


if (fwDown)
{
upSum++;

Point3d pickStart=new Point3d();
Vector3d down = new Vector3d(0.0, -2.0, 0.0);
Transform3D newT3d = new Transform3D();
newT3d.setTranslation(new Vector3d(0.0, 1.0, stepper));
newT3d.mul(ourTrans);
newT3d.transform(pickStart);

So, first we have incremented upSum as before, then we have created a new point3d then transformed it to match our starting point but be 2 metres above it. This point will be the starting point for our picking ray, which will point straight down so that if the ground is anything less than 2 metres above our current position we will locate it.


We also create a Vector3d called “down”- I have said this before and I’ll say it again –3D graphics has a really strong element of performance tuning about it- a Vector3d that will never do anything but point down should never be created every frame- it should be a static class level variable. The only reason it is happening this way here is that I want to have as many code changes as possible in one place as it’s easier to see what is going on. You could probably go through most of the classes here as an exercise and see how you could make them more efficient.


picker.setShapeRay(pickStart, down);

The PickRay has a starting point and a direction, both of which we just defined.


PickResult picked = picker.pickClosest();
PickIntersection intersect = picked.getIntersection(0);
Point3d nextpoint = intersect.getPointCoordinates();

Now we have used pickClosest to find the first intersection the PickRay finds and return it’s PickIntersection object. We then get it’s point co-ordinates and load them into our point.


if (nextpoint != null)
{
double pickY = (pickStart.y);
pickY = pickY-2.0;
if (nextpoint.y != pickY)
{
Vector3d translate = new Vector3d();
ourTrans.get(translate);
translate.y=nextpoint.y;
ourTrans.setTranslation(translate);
}
}
tempTrans.setTranslation(new Vector3d(0.0d, 0.0d, stepper));
}

So if our point has been created properly we create a double to contain the position of our starting point ( we subtract 2 because we started 2 metres off the ground ) and make sure that the point we have just picked is not the same as the previous y position –no point doing all this work if it is.

Assuming it is different, we create a new Vector3d, copy the translation from OurTrans into it, set it’s y value to be the new one we have just picked and loaded it back into ourTrans.

Finally we add the step to tempTrans. After this last bracket the method is exactly the same as the previous version.

That’s all there is to the Behaviour but we need to change a few things in the calling method to make it work properly:

SimpleLand s = new SimpleLand(10, 10, texturePath);

PickTool p = new PickTool(objRoot);
p.setCapabilities(s, PickTool.INTERSECT_FULL);

ClimbMorph mBeh = new ClimbMorph(morphAlpha, ourMorph, dudeGroup, p);
mBeh.setSchedulingBounds(bounds);

dudeGroup.addChild(mBeh);
dudeGroup.setPickable(false);

Here we have created the land as before then created the PickTool and informed it that it is allowed to read everything about the land object. We create our ClimbMorph behaviour handing it the picktool as well. When we have added it to the dudeGroup we set the pickability of that node to false –if we don’t do that the pickray will run into our character rather than the ground which typically results in the character shooting vertically up in the air when you ask it to go forwards. Not, strictly speaking, the desired effect, but one of the funnier errors to see in action…

The other major change is that we have added a background group to our world. This doesn’t interact with the scene at all, it just sits there looking better than a plain black space.

BranchGroup bgGeometry = new BranchGroup();

Appearance App = new Appearance();
try {
Texture tex = new TextureLoader(new java.net.URL(filePath+"skysec.jpg"), this).getTexture();
App.setTexture(tex);
}
catch (Exception e)
{
e.printStackTrace();
}

Sphere outerWorld = new Sphere( 1.0f, Primitive.GENERATE_TEXTURE_COORDS | Primitive.GENERATE_NORMALS_INWARD, App);
bgGeometry.addChild(outerWorld);

As you can probably tell, this is just creating a new BranchGroup and appearance and populating it with a sphere that displays it’s appearance inwards.

Background bg = new Background();
bg.setApplicationBounds(bounds);
bg.setGeometry(bgGeometry);
objRoot.addChild(bg);

Finally we have created a Background node, applied the Geometry we just created and added it to the root of the world tree. So now, we should have something that looks a bit like this:

Previous: Now We're Getting Somewhere | Back to the Index | Next: Finally in Followcam Footage