Wednesday 27 June 2012

Engine Design - Terrain

Well this is my version of a terrain class for the Engine, it is a very simple class. I took the initial idea and I guess framework from Riemers Series 4 tutorial and got it to fit my engine. I added a few methods to it to make it a bit more user friendly, for instance there is a method to tell if an object has collided with the terrain object, there is a method that will return the given height of the terrain at a given Vector3 and another method that I use to raise and lower the terrain with a mouse click. This terrain picker is derived from my picking method found in the last post, it was also inspired (again) by Riemers post on his forum concerning terrain picking, what he was doing was very similar to my object picker and so gave me the inspiration to come up with my method. I also have to add that if it was not for Chr0n1x I would not have gone looking for examples to do terrain picking.
I also created my own shader for the terrain as I found that Riemers, although very good as it had great detail, slowed my engine a little. The shader I came up with, while not being as pretty gives a faster render, I will put that up here to.
So on to the code. What I will do is break it down into chunks again and then offer the whole class at the end. I am not going to go over the basic terrain code as this is all covered in Riemers tutorial, for this please go to his site and read though the tutorial to get a better understanding, saves me repeating his tutorial. Also there are some methods I have commented out as they are unfinished, for example I am working on a random map generator, this is not complete so has been removed, also there is the color map generator for the terrain, this I have left as it is completed, but it was more of a folly rather than for any real purpose, I have left it in as a matter of completeness.
 
Terrain Position
private Vector3 basePosition;
public new Vector3 Position
{
    get { return basePosition; }
    set { basePosition = value; }
}
This field and property were added so that I could have my terrain centered on the position rather than the position vector be the bottom left hand corner of the terrain mesh.


GetHeightAt
public Vector3 GetHeightAt(Vector3 objPosition)
{
    Vector3 retVal = new Vector3();
    int x, y;
    y = (int)objPosition.X + (myWidth / 2);
    x = (int)objPosition.Z + (myHeight / 2);
    if ((x >= 0 && y >= 0) && (x < myWidth && y < myHeight))
        retVal = new Vector3(x, y, myPosition.Y + myHeightData[x, y]);
    return retVal;
}
This method basicaly returns the height value of the terrain at a given position. I used this for debugging my collision method using it to display the height of the terrain as I drove my camera over it.


Collision



public bool Collision(Vector3 objPosition, float heightBuffer)
{
    bool retVal = false;
    int x, y, objHeight;
    y = (int)objPosition.X + (myWidth / 2);
    x = (int)objPosition.Z + (myHeight / 2);
    objHeight = (int)(objPosition.Y - heightBuffer);
    if ((x >= 0 && y >= 0) && (x < myWidth && y < myHeight))
    {
        if (objHeight < myPosition.Y + myHeightData[x, y])
            retVal = true;
    }
    return retVal;
}
This method returns true if the object is colliding with the terrain and false if it is not, simple enough. The heightBuffer is there as your models position may well be taked from it's center and so you need to specify the distance from the center to the base of the model. I guess you could use the radius of the objects native BoundingSphere, I did not do this as this as just occured to me as I write this.
Here is an example of this in action, this code placed in the Update of your Game will stop your camera from going through the terrain and give it the impression of being solid.
// Terrain collision.
RCTerrain terrain = (RCTerrain)(((RCObjectNode)game.Scene.GetObject("terrain")).Object);
game.WriteText(game.Font, 10, 100, terrain.GetHeightAt(RCCameraManager.ActiveCamera.Position).ToString(), Color.White);
if (terrain.Collision(RCCameraManager.ActiveCamera.Position, 2.5f))
    RCCameraManager.ActiveCamera.Position = new Vector3(RCCameraManager.ActiveCamera.Position.X, RCCameraManager.ActiveCamera.Position.Y + .2f, RCCameraManager.ActiveCamera.Position.Z);


PickTerrain
Before Mouse Clicks


After wasfting the mouse, clicking left and right mouse buttons


public void PickTerrain(GraphicsDevice device, Point mousecoords, bool Up)
{
    RCCameras.RCCamera camera = RCCameras.RCCameraManager.ActiveCamera;
    Vector3 nearSource = camera.Viewport.Unproject(new Vector3(mousecoords.X, mousecoords.Y, camera.Viewport.MinDepth), camera.Projection, camera.View, Matrix.Identity);
    Vector3 farSource = camera.Viewport.Unproject(new Vector3(mousecoords.X, mousecoords.Y, camera.Viewport.MaxDepth), camera.Projection, camera.View, Matrix.Identity);
    Vector3 direction = farSource - nearSource;
    float zFactor = nearSource.Y / direction.Y;
    Vector3 zeroWorldPoint = nearSource + direction * zFactor;
    Ray ray = new Ray(nearSource, direction);
    for (int x = 0; x < myWidth; x++)
        for (int y = 0; y < myHeight; y++)
        {
            BoundingBox tmp = new BoundingBox(myVertices[x + y * myWidth].Position + myPosition, (myVertices[x + y * myWidth].Position + myPosition) + new Vector3(1f, 1f, 1f));
            if (ray.Intersects(tmp) != null)
            {
                float val = 0;
                if (Up)
                    val += .5f;
                else
                    val -= .5f;
                if (x > 0)
                    myHeightData[x - 1, y] += val;
                myHeightData[x, y] += val;
                if (x < myWidth)
                    myHeightData[x + 1, y] += val;
                if (x > 0 && y > 0)
                    myHeightData[x - 1, y - 1] += val;
                if (y > 0)
                    myHeightData[x, y - 1] += val;
                if (x < myWidth && y > 0)
                    myHeightData[x + 1, y - 1] += val;
                if (x > 0 && y < myHeight)
                    myHeightData[x - 1, y + 1] += val;
                if (y < myHeight)
                    myHeightData[x, y + 1] += val;
                if (x < myWidth && y < myHeight)
                    myHeightData[x + 1, y + 1] += val;
                break;
            }
        }
    BuildTerrain(device);
}
This is basicaly my Picking code, but what I am doing here is checking to see if my ray intersects a vertex of the terrain. I do this by moving through each of the terrains verts and using their possition Vector3 build a bounding box around them to check if my ray intersects it, if it does then I do what I want to do, in this case raise or lower the trrain depending on which mouse button was clicked, a bit like POPULOUS, a very old game I used to love. What I need to add to this method is the check to see if the intersecting vertex is the nearest to the camera like in my scene object picking method. Oh well at least this gives you an idea of how it can be done.

[Class and shader was here, but can't post it, please see original blog post]

Well I guess my Water class is next, here is an image of it in action:
 
First some nice calm water, waves at an amplitude of .1 


Now at amplitude 1

"lets go surfing now, everybody's learning how..."



Posted Mon, Oct 15 2007 2:59 PM by Charles Humphrey | Add post to favorites | Add blog to favorites
Filed under: Randomchaos 3D Engine, Terrain, XNA 1.0 [Edit Tags]


Comments

Zandman26 wrote re: Engine Design - Terrain
on Thu, Dec 13 2007 2:39 PM
Hi again:)
Time to bother you again with a question, as I started out about a year ago with Riemers tutorials for normal DirectX I went back there yesterday to build my terrain, and when I did his DX tutorial on the matter it was clear and simple but XNA seams to have a more complex way of doing some things. So the result of me finishing his tutorial was that my mesh object gets half hidden behind the height map and  the height map in it self is also messed up . The height map in it self has something that looks like black squares at a certain distance from the camera and if you pan over the map you can see right trough the mountains.
Know anything about this problem?
Charles Humphrey wrote re: Engine Design - Terrain
on Thu, Dec 13 2007 4:31 PM
OK, I have seen the transparent mesh thing before this it to do with render states, I am sure I have answered this in another Terrain post (xna-uk.net/.../simple-bumped-terrain-with-water.aspx)
You can fix this in either the shader or before the draw call to the terrain.
As to your modes appearing half way through the terrain?? Don't really know, Riemers tutorial does not come with any collision detection, so I guess it's where you are placing the models. Also Riemers tutorial uses the Z axis as the Up so this could also give you issues if you don't expect it.
If you are using my terrain projects (modified from Riemers great tuts) then I have altered the creation of the terrain and shaders to use the Y axis as the Up/Down plane.
Hope this helps.
Zandman26 wrote re: Engine Design - Terrain
on Fri, Dec 14 2007 12:09 PM
Well, I did the changes to the effect file and also tried to use the CPU to make it work when the result of changing the effect code did not work. But I get the same result ether way, so I made a new project copy/paste Riemers code and ran the app and in that code nothing was wrong with the rendering, so I could stop doubting that the problem was my graphics card, later copy/pasted the code in to my project got rid of all his device creation,camera settings and input stuff and started running my own engine, and it looked as terrible as before. Cant figure this out (this was much easier to  do in DX9 code) if your up to it I can send you the code, else if you have any other tip on what to do I'm glad for the help:)
Charles Humphrey wrote re: Engine Design - Terrain
on Fri, Dec 14 2007 2:56 PM
hmmm, sure mail me the project, can't promise anything but will try and see what the issue is.
ShaneSimpkins wrote re: Engine Design - Terrain
on Fri, Jan 25 2008 12:11 AM
I was just wondering if you would be able to send me your player model you use in the screen shots in the tutorial.
I have been searching everywhere for a .x model (free) that i can stick in the game engine I am making so that I may be able to test it nicely and get a 3rd person camera working.
If you could then please email me the model stuff (zipped or something) to ShaneSimpkins[at]gmail[dot]com
--------------------------------------
"Replacing the [at] and [dot]" - "@ and ." ;P
Charles Humphrey wrote re: Engine Design - Terrain
on Fri, Jan 25 2008 9:33 AM
Shane,
The model is from the DirectX SDK and it is called Tiny.x
It should be included in the animation sample for this engine located here: xna-uk.net/.../entry1070.aspx
If you still have trouble getting this model, give me a shout again and I will post it.
ShaneSimpkins wrote re: Engine Design - Terrain
on Sat, Jan 26 2008 10:19 AM
Thank you very much, I know have it and am putting it into the game now. Thank you very much :D

No comments:

Post a Comment