Thread: My terrain Generation function's fubar.

  1. #1
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901

    My terrain Generation function's fubar.

    I'm trying to generate terrain from a terragen file. I spent a bit of time creating a *.ter file class that works quite well. The only problem I'm having is when it comes down to me rendering the heights and processing the rows and columns of the actual height data I fall short. I bolded the specific function that handles the generation of the heights and as you see in the pictures (first two are using GL_TRIANGLES, and the last one is using GL_POINTS) they are not coming out anywhere near nice looking. The heights are there, and it looks like, from a stone's throw, terrain, but something must be wrong with how I'm processing the columns.

    Code:
    #include "globj.h"
    
    //=========================================================================
    float rotateAngle1 = 0.0;
    float rotateAngle2 = 0.0;
    
    GLuint texture1;
    //=========================================================================
    
    GLObj::GLObj() : moveX(0), moveY(0), moveZ(0), rotX(0) {}
    
    GLObj::~GLObj() {}
    
    bool GLObj::init()
    {
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
    
        glEnable(GL_STENCIL_TEST);
        glEnable(GL_TEXTURE_2D);
    
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    
        //glEnable(GL_COLOR_MATERIAL);
    
        glClearColor(0.0, 0.0, 0.0, 0.0);
    
        //setupLighting();
    
        if(!setupTerrain())
            return false;
    
        return true;
    }
    
    void GLObj::setupProjection(int width, int height, double fov)
    {
        if(height == 0)
            height = 1;
    
        float ratio = static_cast<float>(width)/ static_cast<float>(height);
        glViewport(0, 0, width, height);
    
        glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(fov, ratio, 1.0, 1000.0);
    
        glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
    
        screenWidth = width;
        screenHeight = height;
    }
    
    void GLObj::setupLighting()
    {
        glEnable(GL_LIGHTING);
        glEnable(GL_COLOR_MATERIAL);
    
        //distant white light
        glEnable(GL_LIGHT0);
            float light0Pos[] = {0, 0, 100, 0.0};
            float light0Col[] = {1.0, 1.0, 1.0, 1.0};
            float light0Spc[] = {1.0, 1.0, 1.0, 1.0};
            glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
            glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, light0Col);
            glLightfv(GL_LIGHT0, GL_SPECULAR, light0Spc);
    
    
        glEnable(GL_LIGHT1);
            float light1Pos[] = {100, 100, 100, 0.0};
            float light1Col[] = {0.2, 0.2, 0.4, 1.0};
            float light1Spc[] = {1.0, 1.0, 1.0, 1.0};
            glLightfv(GL_LIGHT1, GL_POSITION, light1Pos);
            glLightfv(GL_LIGHT1, GL_AMBIENT_AND_DIFFUSE, light1Col);
            glLightfv(GL_LIGHT1, GL_SPECULAR, light1Spc);
    }
    
    bool GLObj::setupTerrain()
    {
        FileError msg = terrain.load("Data/Surface1.ter");
        if(msg == FILE_TYPE_MISMATCH || msg == FILE_READ_ERROR)
                return false;
    
        return true;
    }
    
    
    void GLObj::prepare(double t) {}
    
    void GLObj::render()
    {
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
        glTranslatef(-26 - moveX, -15 + moveY, -255 + moveZ);
        glRotatef(15, 1, 0, 0);
        glRotatef(rotX, 0, 1, 0);
    
        TerragenFile::index row, col;
    
        glColor3f(0.5, 0.5, 0.5);
        glDisable(GL_CULL_FACE);
    
        short int b = terrain.getBHeight();
        short int s = terrain.getHScale();
    
        for(row = 0; row < terrain.getY() - 1; ++row)
        {
            glBegin(GL_TRIANGLE_STRIP);
            for(col = 0; col < terrain.getX(); ++col)
            {
                float height =
                    (b +  terrain[(row * terrain.getX()) + col] * s )/ 65536;
                float  nextHeight =
                    (b +  terrain[((row + 1) * terrain.getX()) + col] * s )/ 65536;
    
                float x = static_cast<float>(row - terrain.getX()/2);
                float z1 = static_cast<float>(col - terrain.getX()/2);
                float z2 = static_cast<float>((col + 1) - (terrain.getX()/2));
    
                glVertex3d(x, height, z1);
                glVertex3d(x, nextHeight, z2);
            }
            glEnd();
        }
    }
    the float height and float nextheight values are calculated from the terragen file specification where it is said to calculate the absolute height of a point in the file you use the formula
    BaseHeight + Elevation * HeightScale / 65536.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You are over-running the vertex grid. In other words your creation loops are probably going 1 over the edge of the map. You've got several vertices that are set at 0.0f,0.0f,0.0f. Because you are drawing triangles, the first two vertices of the triangle are probably correct but the third is not. This is true for all quads that lie on the edge of the map. The lines you are seeing are extremely extended hypotenuses of your triangles.
    Check your loops and make sure they are not overrunning the bounds of the vertex grid.

    If you are using strips and it looks like you are, you must stop the strip at the edge of the grid. Otherwise the next triangle will not look correct because the next vertex will be on the next row, but at the beginning of that row. You will have a huge messed up triangle stretching from the end of one row to the start of another row. Once you get past this point, it works fine because the vertices are lined up so that a strip works correctly.

    So when you get to the edge of the grid, the triangles stop and so should your strips. Each row of triangles is a strip but you cannot just set the primitive type to strip and render the whole thing because of the edge issues.

    To alleviate this problem I just went with a triangle list. Strips were not much faster in rendering and proved to be a pain. It will be moreso when I add the geometry clipmap algo to it.

    Code:
    void CTerrain::CreateVertices(TerrainVertex *pVerts)
    {
     
      int iStartX=-m_iWorldWidth2;
      int iStartZ=m_iWorldDepth2;
      int iEndX=m_iWorldWidth2;
      int iEndZ=-m_iWorldDepth2;
        
      //Lock vertex buffer
      
     
      int iNumVerts=0;
      
      float fUInc=1.0f/(float)m_iNumCellsPerRow;
      float fVInc=1.0f/(float)m_iNumCellsPerCol;
      
      int r=0,c=0;
      int iIndex=0;
      for (int z=iStartZ;z>=iEndZ;z-=m_iCellSize)
      {
        c=0;
    
        for (int x=iStartX;x<=iEndX;x+=m_iCellSize)
        {
          iIndex++;
          float fHeight=(float)m_pHeightMap[iIndex];
                
          pVerts[iIndex]=TerrainVertex((float)x,fHeight,(float)z,(float)c*fUInc*64.0f,
                                       (float)r*fVInc*64.0f);
          pVerts[iIndex].du=(float)c*fUInc*256.0f;
          pVerts[iIndex].dv=(float)r*fVInc*256.0f;      
          c++;
                
        }
        r++;
        
      }
      
               
    }
    I pre-scale my heightmap so don't worry I'm doing the same exact thing you are just in a diff order.

    u and v are color texture coords
    du and dv are detail texture coords
    Last edited by VirtualAce; 06-20-2007 at 11:37 AM.

  3. #3
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    OK I was able to rid the code of the lins by changing the following
    Code:
    float x = static_cast<float>(row + terrain.getX()/2);
                float z1 = static_cast<float>(col + terrain.getX()/2);
                float z2 = static_cast<float>((col + 1) + (terrain.getX()/2));
    I had to add the terragen.get_()/2 instead of subtracting it. That also restored the larger portion of the terrain where I was wondering went to.

    But As you see in the first picture which was rendered with GL_TRIANGLE_STRIPS it has no definition as an object. There are gaps in between the strips for some reason. The second pics are points.
    Last edited by indigo0086; 06-20-2007 at 11:55 AM.

  4. #4
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    You need to overlap your rendering by one... ie. render 1-2, 2-3, 3-4.. instead of 1-2, 3-4, 5-6...

  5. #5
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    I thought that is what it does. It processes the points for the first row, then the next row, then it increments to the next row, processes that one, then the next.

  6. #6
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    hmmm, yes, the code looks ok. I made the suggestion based on the description of the problem. Can you post a screen shot that is zoomed in closer?

  7. #7
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    Well I first DISABLED GL_BLEND lol, and then I changed the code so that it positioned the points relative to the center of the terrain like it was in my original code Now it's working fine. Latest shots.
    ;b

    Man I love this terrain stuff, I learned so much about data representation, terrain generation, all that stuff. I love this, I wrote a neat handler for terragen files if anyone is interested in looking aand maybe showing me what to improve. Here's the revised code and screens.

    Code:
    void GLObj::render()
    {
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
    
        //set up camera
        glRotatef(20, 1, 0, 0);
        glTranslatef(moveX, -50 + moveY, -200 + moveZ);
        glRotatef(rotX, 0, 1, 0);
        glRotatef(rotY, 1, 0, 0);
    
        //variables for rendering
        TerragenFile::index row, col, n1, n2;
        float x, z1, z2;
    
        short int b = terrain.getBHeight();
        short int s = terrain.getHScale();
    
        float halfX = static_cast<float>(terrain.getX()) / 2.0;
        float halfY = static_cast<float>(terrain.getY()) / 2.0;
    
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    
        for(row = 0; row < terrain.getY() - 1; ++row)
        {
            glBegin(GL_TRIANGLE_STRIP);
            for(col = 0; col < terrain.getX(); ++col)
            {
                //indexes of current and next row
                n1 = row * terrain.getX() + col;
                n2 = (row + 1) * terrain.getX() + col;
    
                //calculates absolute altitude
                float height = (b +  terrain[n1] * s )/ 65536;
                float nextHeight = (b +  terrain[n2] * s )/ 65536;
    
                //altitude color
                float color = 0.5 + 0.5 * height / MAX_HEIGHT;
                float nextColor = 0.5 + 0.5 * nextHeight / MAX_HEIGHT;
    
                x = static_cast<float>(col - halfX);
                z1 = static_cast<float>(row - halfY);
                z2 = static_cast<float>((row + 1) - halfY);
    
                glColor3f(color, color, color);
                glTexCoord2f((float)x/terrain.getX()*8, (float)row/terrain.getY()*8);
                glVertex3d(x, height, z1);
    
                glColor3f(nextColor, nextColor, nextColor);
                glTexCoord2f((float)x/terrain.getX()*8, (float)(row + 1)/terrain.getY()*8);
                glVertex3d(x, nextHeight, z2);
            }
            glEnd();
        }
    }
    now I just need to figure out why the texture coordinates are the way they are because the logic escapes me at the moment. Then on to normals.
    Last edited by indigo0086; 06-20-2007 at 08:24 PM.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Very nice work. Glad you got it working. The only reason we could help is because when we first set out on terrain we achieved about the exact same renders as you.

    The very cool thing about terrain is that it is extremely challenging. You are trying to present an enormous amount of triangles from an equally enormous data set all while texturing, lighting, all while maintaining interactive framerates. It requires some extremely good coding and some intuitive approaches to the problem. The grid is easy but doing the other cool things like real-time terrain deformation, continuous level of detail, quad-tree frustum culling, landscapes that can represent an entire world, ROAM, etc, etc, is not so easy.

  9. #9
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    yeah, this was hard and I was just trying to replicate what I saw in the book from scratch. I try to look at the code once, then understand the logic behind it, then replicate it. Now I need to work on the skybox.


    Know of any place that offers free textures?
    Last edited by indigo0086; 06-21-2007 at 02:38 AM.

  10. #10
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    Also, would display lists improve rendering for terrain given that this one in particular is 257 * 257 points in size. I haven't really played with display lists yet but I hear they dramatically improve the rendering time of repeating display algorithms like this.

  11. #11
    Amazingly beautiful user.
    Join Date
    Jul 2005
    Location
    If you knew I'd have to kill you
    Posts
    254
    Display lists are extremely specific to the graphics card's implementation of them.
    On same cards, they provide a drastic boost, while on others, you won't notice a difference.
    It also depends on the amount of work you are doing to render your terrain.

    OpenGL Display Lists FAQ
    Programming Your Mom. http://www.dandongs.com/

  12. #12
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    I tried a display list based terrain renderer once, it was comparable (but slightly slower) than just rendering the triangle strips themselves on my card. Granted it was a radeon 9000 mobile...

  13. #13
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    I have a radeon X1950 Pro but a single core Athlon 64 XP 3500 (Desperately trying to find a dual core for my board that isn't sold out) and I don't really notice a difference, but I don't know of a way to really benchmark the program but I'm not doing anything big with it yet.

    I am thinking of a way of adding in my TerragenFile Library a way to dynamically resize the terrain. By default it's whatever the terragen specifies, but I'm thinking there could be a way to resize the terrain and still have it retain it's features without simply skipping points on either plane. That's for another day though.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. calling functions within functions
    By edd1986 in forum C Programming
    Replies: 3
    Last Post: 03-29-2005, 03:35 AM
  2. Drawing only what you can see (terrain)
    By Eber Kain in forum Game Programming
    Replies: 8
    Last Post: 07-05-2004, 12:19 AM
  3. Terrain algos
    By VirtualAce in forum Game Programming
    Replies: 1
    Last Post: 04-10-2004, 02:50 PM
  4. OpenGL terrain demo (EDITOR)
    By Jeremy G in forum Game Programming
    Replies: 2
    Last Post: 03-30-2003, 08:11 PM
  5. Passing data/pointers between functions #2
    By TankCDR in forum C Programming
    Replies: 1
    Last Post: 11-02-2001, 09:49 PM