Terrain following help needed

Hey everyone,

I’d been working on a game engine for a few days, and then I ran into a bit of a problem: how to stop my character from walking through the terrain. My terrain is just a simple Shape3D constructed from a simple 2D heightmap array, and my character is also just a simple Shape3D. It was all looking good, and I was getting speeds of about 40 FPS. Then I tried to add some simple terrain following, to always position my character 1 unit above the ground directly below him/her. All I’ve done is cast a ray directly downwards, then I check through the collisions, and then I reposition my character’s height, like so:


 if (moved)
            {
                pickTool.setShapeRay(character.getLocation(), DOWN);

                pickResult = pickTool.pickClosest(); // This doesn't actually work either.... it only does when I use pickAny() or pickAll(), then call the first one in the array.

                   
                    if ((pickResult.numIntersections() > 0) && (pickResult.getObject() instanceof Terrain))
                    {
                character.setAltitude(character.getLocation().y - pickResult.getIntersection(0).getDistance() + 1);
                    }
            }

Now, when I use this code, which is run whenever the character has moved in any direction, the FPS drops to like 4!!!.

Does anybody have any idea what I can do to speed it up, or a better method of doing the same thing?

Thanks,
James

I’m working on exactly the same thing right now - keeping my heightmap array, locating the player position within that array and then working out the height at this location. As you can see from this thread I’m not there yet, but I think if rearrange it slightly so I’m manually setting the triangles rather than using quads it should be possible to find the height at any point on a triangle using methods discussed here.

Thanks a lot Breakfast! I’m going to try and work on that idea a bit, and see if I can get it working.

James.

This is a work in progress, but here is how I am handling it at the MiTUEN project (mituen.sourceforge.net) : There are also extra dynamics in how the camera follows the terrain in other classes at the project.
public class TerrainFollower
{
Vector3d diffVec = new Vector3d();
Vector3d downVector = new Vector3d();
Vector3d Y_DOWN = new Vector3d(0, -1, 0);
PickResult pr = null;
PickIntersection pi;
Transform3D viewTx = new Transform3D();

public boolean terrainFollow(TransformGroup tg, Vector3d pos, BranchGroup terrain)
{
    tg.getTransform(viewTx);
    Vector3d locationVector = new Vector3d();
    Transform3D worldEyeTransform = new Transform3D();
    tg.getLocalToVworld(worldEyeTransform);
    worldEyeTransform.mul(viewTx); // viewTx);
    worldEyeTransform.get(locationVector);
    Point3d locationPoint = new Point3d();
    //  locationPoint.set(locationVector);
    worldEyeTransform.transform(Y_DOWN, downVector);
    PickRay terrainPicker = new PickRay();
    locationPoint.add(locationVector, pos);
    locationPoint.y += 10000; // This seems to be needed for some reason to raise above terrain
    System.out.println(locationVector.toString());
    terrainPicker.set(locationPoint, Y_DOWN); //downVector);
    // System.out.println("locationPoint: " + locationPoint);
    //  System.out.println("downVector: " + downVector);
    //        System.out.println("terrain children: "+terrain.numChildren());
    if (terrain == null)
    {
        System.out.println("no terrain to choose");


        return false;

    }
    SceneGraphPath ground[] = terrain.pickAll(terrainPicker);
    //(terrainPicker);
    if (ground == null)
    {
        System.out.println("tf:no terrain");
        return false;
    }

    boolean ret_val = true;
    double shortest_length = -1;

    /*
    for (int i = 0; (i < ground.length); i++) {
        PickResult pr = new PickResult(ground[i], terrainPicker);
               PickIntersection pi = pr.getClosestIntersection(locationPoint);
        if (pi != null) {
            Point3d intpt = pi.getPointCoordinates();
            Point3d intpt1 = pi.getPointCoordinatesVW();
            if (intpt != null) {
                System.out.println("Terrain Intersection point: "+intpt);
                System.out.println("Terrain intersection point1: "+intpt1);
                diffVec.sub(locationPoint, intpt);
                if (shortest_length == -1 || (diffVec.lengthSquared() < shortest_length)) {
                    shortest_length = diffVec.lengthSquared();
                    intersectionPoint.set(intpt1);
                }
            }
        } else {System.out.println("null pi"); }
    } */

    if (ground.length < 1)
    {
        System.out.println("Empty ground array :( ");
        return false;
    }
    pr = new PickResult(ground[0], terrainPicker);
    //  pr.setFirstIntersectOnly(true);
    // pr.setFirstIntersectOnly(true);
    //  pr.getFirstPickEnable();
    // System.out.println("Num intersection: " + pr.numIntersections());
    if (pr.numIntersections() < 1)
    {
        System.out.println("Picked but no intersections");
        return false;
    }
    pi = pr.getIntersection(0);
    //  System.out.println("pr: "+ pr);
    Point3d intersectionPoint = pi.getPointCoordinatesVW();
    // Point3d inpt1 = pi.getPointCoordinatesVW();

    /*       if (shortest_length == -1) {
               System.out.println("no real terrain");
               return false;
           }*/

    //IMPORTANT
    //terrain_transform.transform(intersectionPoint);

    /*      if(lastTerrainHeight == -1){
            lastTerrainHeight = intersectionPoint.y;
        }

        double terrain_step = intersectionPoint.y - lastTerrainHeight;
        double height_above_terrain = locationPoint.y - intersectionPoint.y;

        if(height_above_terrain != ainfo.amodel.height){
            if(terrain_step > ainfo.speed){
                ret_val = false;
            }
            oneFrameTranslation.y = ainfo.amodel.height - height_above_terrain;
        }

*/

    /*
    System.out.println("intersectionPoint.y = "+intersectionPoint.y);
    System.out.println("locationPoint.y = "+locationPoint.y);
    System.out.println("terrain_step = "+terrain_step);
    System.out.println("height_above_terrain = "+height_above_terrain);
    System.out.println("oneFrameTranslation.y = "+oneFrameTranslation.y);
    System.out.println("avatarHeight = "+avatarHeight);
    System.out.println("lastTerrainHeight = "+lastTerrainHeight);
    */

    //   Quat4d quat = new Quat4d();
    float lastTerrainHeight = (float) intersectionPoint.y;
    double terrain_step = intersectionPoint.y - lastTerrainHeight;
    double height_above_terrain = locationPoint.y - intersectionPoint.y;
    System.out.println("tf: height above terrain:" + height_above_terrain);

    doMoveNoTerrain(new Vector3d(0.0, -(height_above_terrain - 10002), 0.0), tg);


    return ret_val;
}

public void doMoveNoTerrain(Vector3d theMove, TransformGroup transformGroup)
{
    Transform3D transform3D = new Transform3D();
    transformGroup.getTransform(transform3D);
    Transform3D toMove = new Transform3D();
    toMove.setTranslation(theMove);
    transform3D.mul(toMove);
    transformGroup.setTransform(transform3D);
    TerrainMeshControl.uglyGlobal.viewerPositionUpdated(transform3D);
    
    //  if (sml != null)
    //      sml.viewerPositionUpdated(toMove);
}

}

Thanks for the reply.

I haven’t really had time to work on it lately, but this weekend I’m going to try and figure it out completely. Thanks for showing how you did it, I’ll see if I can see what’s missing in mine.

Thanks,
James