Thread: Difficult time understanding generating terrain (from file)

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

    Difficult time understanding generating terrain (from file)

    I downloaded this program terragen and have generated random terrains and saved them as raw image data. I understand that each byte of the raw data file corresponds to a height ranging from 0-255 and those are my vertex coordinates for thet triangles/trianglestrip. The thing I have a difficult time understanding is do I plot the triangles in rows of two starting with the first point on the first row, then the first on the second row, then second on the second row etc? Or do I plot them each row at a time (first one on first row, then second, then third, etc)?

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Your terrain grid should be the same size as your heightmap.

    The following then can be used:

    Code:
    void CTerrain::PrepareTerrain(int iCellSize)
    {
      //Setup variables
      m_vecPos=D3DXVECTOR3(0.0f,0.0f,0.0f);
      m_iCellSize=iCellSize;
      m_iMapSize=(m_iMapWidth)*(m_iMapDepth);
      m_iNumVertsPerRow=m_iMapWidth;
      m_iNumVertsPerCol=m_iMapDepth;
      m_iNumCellsPerRow=m_iNumVertsPerRow-1;
      m_iNumCellsPerCol=m_iNumVertsPerCol-1;
      m_iWorldWidth=m_iNumCellsPerRow*m_iCellSize;
      m_iWorldDepth=m_iNumCellsPerCol*m_iCellSize;
      m_iWorldWidth2=m_iWorldWidth>>1;
      m_iWorldDepth2=m_iWorldDepth>>1;
      m_iNumVerts=m_iNumVertsPerRow*m_iNumVertsPerCol;
      m_iNumTris=m_iNumCellsPerRow*m_iNumCellsPerCol*2;
      m_iNumIndices=m_iNumTris*3;
      
      
      //Create vertex buffer
      HRESULT hr=m_pDevice->CreateVertexBuffer(m_iNumVerts * sizeof(TerrainVertex),
                                    D3DUSAGE_WRITEONLY,
                                    TerrainVertex::FVF,
                                    D3DPOOL_MANAGED,
                                    &m_pVB,
                                    NULL);
                                    
      if (FAILED(hr))
      {
        ::MessageBox(0,"Failed to create vertex buffer",0,0);
      }
                                    
      //Create index buffer
      hr=m_pDevice->CreateIndexBuffer(m_iNumIndices * sizeof(WORD),
                                   D3DUSAGE_WRITEONLY,
                                   D3DFMT_INDEX16,
                                   D3DPOOL_MANAGED,
                                   &m_pIB,
                                   NULL);
      if (FAILED(hr))
      {
        ::MessageBox(0,"Failed to create index buffer",0,0);
      }
                             
      
      //Compute vertices
      TerrainVertex *pVerts=0;
      m_pVB->Lock(0,0,(void **)&pVerts,0);
      CreateVertices(pVerts);
      m_pVB->Unlock();
      
      
      //Compute indices  
      WORD *pIndices=0;
      m_pIB->Lock(0,0,(void **)&pIndices,0);
      CreateIndices(pIndices);
      m_pIB->Unlock(); 
        
      //Compute normals 
      CreateNormals();
         
    }
    void CTerrain::CreateVertices(TerrainVertex *pVerts)
    {
     
      int iStartX=-m_iWorldWidth2;
      int iStartZ=m_iWorldDepth2;
      int iEndX=m_iWorldWidth2;
      int iEndZ=-m_iWorldDepth2;
        
       int iNumVerts=0;
      
      float fUInc=1.0f/(float)m_iNumCellsPerRow;
      float fVInc=1.0f/(float)m_iNumCellsPerCol;
      
      int r=0,c=0;
      int iOffset=0;
      
      for (int z=iStartZ;z>=iEndZ;z-=m_iCellSize)
      {
        c=0;
    
        for (int x=iStartX;x<=iEndX;x+=m_iCellSize)
        {
          int index=r*m_iNumVertsPerCol+c;
          float fHeight=(float)m_pHeightMap[index];
                
          //fScaleHeight=128.0f;
                      
          pVerts[index]=TerrainVertex((float)x,fHeight,(float)z,(float)c*fUInc*64.0f,
                                       (float)r*fVInc*64.0f);
     
          //Detail texture coords
          pVerts[index].du=(float)c*fUInc*256.0f;
          pVerts[index].dv=(float)r*fVInc*256.0f;      
          c++;
                
        }
        r++;
        
      }
               
    }
    
    void CTerrain::CreateIndices(WORD *pIndices)
    {
      
      int iOffset=0;
      int iVert=0;
      for (int i=0;i<m_iNumCellsPerCol;i++)
      {
        for (int j=0;j<m_iNumCellsPerRow;j++)
        {
               
          pIndices[iVert]=i*m_iNumVertsPerRow+j;
          pIndices[iVert+1]=i*m_iNumVertsPerRow+j+1;
          pIndices[iVert+2]=(i+1)*m_iNumVertsPerRow+j;
        
          pIndices[iVert+3]=(i+1)*m_iNumVertsPerRow+j;
          pIndices[iVert+4]=i*m_iNumVertsPerRow+j+1;
          pIndices[iVert+5]=(i+1)*m_iNumVertsPerRow+j+1;
          iVert+=6;
        }
      }
      
    }
    I highly recommend you place this into a quadtree and pass the constraints of each node of the tree to a similar CreateVertices() function which will create the vertices and compute the bounding volume for the patch. This will create patches of terrain. At render time you simply traverse the quadtree and do a frustrum cull down the tree. Here are the rules.

    • If the bounding volume of the patch is out, all it's children are also out and do not need to be checked. This patch and all of it's children are not rendered.
    • If the bounding volume of the patch is partially in, you must check it's children. If one of the children's bounding volume is out, then all of it's children are also out. Render only the patches that are in or partially in. At this point you may choose to also render the patches that are partially in. It will take more time for you to code an algo to split the meshes along the frustum than it will for the graphic card to simply cull those triangles that are out.
    • If the bounding volume of the patch is completely in, then all of it's children are also completely in. Therefore you do NOT check the children and you simply render the entire patch and all of it's children.


    This is an extremely fast process and rendered terrain with no LOD on my GeForce 3 64MB card at a whopping 90 FPS (good for that card) with over 3 million vertices in the grid. A quadtree structure may look like this:

    Code:
    struct TerrainPatch
    {
      CTerrain *pParent;
      CTerrain *pUL;  //Upper left
      CTerrain *pUR;  //Upper right
      CTerrain *pLL;  //Lower left
      CTerrain *pLR;  //Lower right
      CBoundingVolume *pBounds;  //Bounding volume for this patch
    };
    The parent node is so you can easily traverse the tree from anywhere in the tree. It does not have to be included for this to work as you will always be drilling down the tree and never up during rendering. I use it for other items in my system.


    If you want to implement terrain LOD I recommend talking to Perspective as I do not yet have a working implementation.
    Last edited by VirtualAce; 06-07-2007 at 10:37 AM.

  3. #3
    Ethernal Noob
    Join Date
    Nov 2001
    Posts
    1,901
    I thought the raw terrain file was my heightmap.

    And I'm just starting with the terrain stuff so I understood barely a subset of that. I'm looking for something the concept rather than just one implementation.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    It is.

    Code:
    ...
    ...
          //Retrieve height from array
          float fHeight=(float)m_pHeightMap[index];
                
         //Scale here if you want 
         //fScaleHeight=128.0f;
                      
          pVerts[index]=TerrainVertex((float)x,fHeight,(float)z,(float)c*fUInc*64.0f,
                                       (float)r*fVInc*64.0f);
    ...
    ...
    The height or Y component for each vertex is retrieved from the terrain array. The array is filled by doing a simple block read from disk. You can store both height and color information in the same file using this:

    Code:
    #pragma pack(1)
    struct RGBH
    {
      BYTE Red;
      BYTE Green;
      BYTE Blue;
      BYTE Height;
    };
    #pragma
    
    RGBH *pTerrainArray=new RGBH[Height*Width];
    int handle=_open(TerrainFile,_O_BINARY,_S_IREAD);
    if (handle==-1) 
    {
      //File open failed
    }
    _read(handle,pTerrainArray,Width*Height*sizeof(RGBH));
    _close(handle);
    It's been some time since I did this so check my code but the concept is there. The color information is in the RGB portion and the height is in the H member of RGBH. You can then scale the height by any factor you want to get good results on screen. So the answer to your question is the height values in the heightmap or array correspond exactly to the individual vertices in your vertex buffer or grid. So if you have a 256x256 heightmap then you will have 256x256 vertices.

    Vertex[0].y=HeightMap[0]*fScaleFactor;

    And so on. You can use a 2D array if it's easier for you conceptually and the speed difference will be minimal if any at all. Keep working on this and post your results here. I guarantee you your terrain will be working before the end of this thread.
    Last edited by VirtualAce; 06-07-2007 at 11:42 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. File transfer- the file sometimes not full transferred
    By shu_fei86 in forum C# Programming
    Replies: 13
    Last Post: 03-13-2009, 12:44 PM
  2. gcc link external library
    By spank in forum C Programming
    Replies: 6
    Last Post: 08-08-2007, 03:44 PM
  3. C++ std routines
    By siavoshkc in forum C++ Programming
    Replies: 33
    Last Post: 07-28-2006, 12:13 AM
  4. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  5. simulate Grep command in Unix using C
    By laxmi in forum C Programming
    Replies: 6
    Last Post: 05-10-2002, 04:10 PM