I've succesfully implemented this algorythm to load a Terrain out of a Heightmap:
#include "Terrain.h"
CTerrain::CTerrain()
{
m_lpDevice = NULL;
m_VBuffer = NULL;
m_IBuffer = NULL;
m_Width = 0;
m_Length = 0;
m_Type = 1;
}
// Freigeben der Objekte
CTerrain::~CTerrain()
{
if(NULL != m_VBuffer)
{
m_VBuffer->Release();
m_VBuffer = NULL;
}
if(NULL != m_IBuffer)
{
m_IBuffer->Release();
m_IBuffer = NULL;
}
}
void CTerrain::Create(LPDIRECT3DDEVICE9 lpDevice, LPCSTR lpHeightmap)
{
// Zaehlvariablen fuer die Schleifen und Indizes
int i, j, k;
m_lpDevice = lpDevice;
LPDIRECT3DSURFACE9 lpSurface = NULL;
D3DXIMAGE_INFO ImgInfo;
// Informationen ueber die Heightmap holen
D3DXGetImageInfoFromFile(lpHeightmap, &ImgInfo);
// Breite und Laenge der Landschaft speichern
m_Width = ImgInfo.Width;
m_Length = ImgInfo.Height;
// Oberflaeche fuer die Heightmap anlegen
m_lpDevice->CreateOffscreenPlainSurface(m_Width,
m_Length,
D3DFMT_L8,
D3DPOOL_SCRATCH,
&lpSurface, 0);
// Heightmap laden
D3DXLoadSurfaceFromFile(lpSurface, 0, 0, lpHeightmap, 0, D3DX_DEFAULT, 0, 0);
// Anzahl der Vertices und Indizes berechnen
int SizeVertices = m_Width * m_Length;
int SizeIndices = (m_Width-1)*(m_Length-1)*2*3;
// Vertex und Index Buffer anlegen
m_lpDevice->CreateVertexBuffer(SizeVertices * sizeof(TERRAIN_VERTEX),
0, D3DFVF_TERRAINVERTEX,
D3DPOOL_DEFAULT,
&m_VBuffer,
0);
m_lpDevice->CreateIndexBuffer(SizeIndices * 2,
0,
D3DFMT_INDEX16,
D3DPOOL_DEFAULT,
&m_IBuffer,
0);
// Oberflaeche sperren
D3DLOCKED_RECT LockedRect;
lpSurface->LockRect(&LockedRect, 0, 0);
// und einen Zeiger auf die Pixel anlegen
BYTE* lpHeights = (BYTE*) LockedRect.pBits;
int nPitch = LockedRect.Pitch;
// die Vertices anlegen
TERRAIN_VERTEX* Vertices = new TERRAIN_VERTEX[SizeVertices];
// und mit Werten belegen
for(i=0;i<m_Length;i++)
{
// der Farbwert des Pixels bestimmt die Hoehe
for(j=0;j<m_Width;j++)
{
Vertices[i*m_Width+j].x = static_cast<float>(j)*0.5f;
Vertices[i*m_Width+j].y = static_cast<float>(lpHeights[i*nPitch+j])*0.1f;
Vertices[i*m_Width+j].z = static_cast<float>(m_Length - i)*0.5f;
// je hoeher der Vertex, umso heller die Farbe
Vertices[i*m_Width+j].Color = D3DCOLOR_XRGB(lpHeights[i*nPitch+j],
lpHeights[i*nPitch+j],
lpHeights[i*nPitch+j]);
}
}
// Array fuer die Indizes anlegen
unsigned short* Indices = new unsigned short[SizeIndices];
// und die Indizes der Dreiecke erzeugen
k = 0;
for(i=0;i<m_Length-1;i++)
{
for(j=0;j<m_Width-1;j++)
{
Indices[k++] = m_Width * (i + 1) + j;
Indices[k++] = m_Width * i + j;
Indices[k++] = m_Width * (i + 1) + j + 1;
Indices[k++] = m_Width * i + j;
Indices[k++] = m_Width * i + j + 1;
Indices[k++] = m_Width * (i + 1) + j + 1;
}
}
// Vertices in den Vertex Buffer kopieren
void* pVertices;
m_VBuffer->Lock(0, SizeVertices * sizeof(TERRAIN_VERTEX), &pVertices, 0);
memcpy(pVertices, Vertices, SizeVertices*sizeof(TERRAIN_VERTEX));
m_VBuffer->Unlock();
// Indizes in den Index Buffer kopieren
void* pIndices;
m_IBuffer->Lock(0, SizeIndices * sizeof(unsigned short), &pIndices, 0);
memcpy(pIndices, Indices, SizeIndices*sizeof(unsigned short));
m_IBuffer->Unlock();
// Speicher freigeben
delete[] Vertices;
delete[] Indices;
// Oberflaeche freigeben
lpSurface->Release();
}
void CTerrain::Render(D3DXVECTOR3 Position, D3DXVECTOR3 Rotation, D3DXVECTOR3 Scale)
{
// Vertex-Format setzen
m_lpDevice->SetFVF(D3DFVF_TERRAINVERTEX);
// Vertex Buffer angeben
m_lpDevice->SetStreamSource(0, m_VBuffer, 0, sizeof(TERRAIN_VERTEX));
//SetWorldTransformation(Position, Rotation, Scale);
// Index Buffer angeben
m_lpDevice->SetIndices(m_IBuffer);
// Landschaft rendern
m_lpDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
m_lpDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
0, 0,
m_Length * m_Width,
0,
(m_Width-1)*(m_Length-1) * 2);
m_lpDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
}
Thats my terrain class, the algorythim is in the Create-function. Everyting works fine for any heightmap under 256*256-size. Thats how it looks:
Now i've got 2 problems:
1. If I use any map with a size greater than 512*512 it starts glitching up. First off all part of the terrain isn't shown, although the framerate is dropping to a very low amount (didn't do any render optimizations yet). This is how it looks:
Can anyone please take a look and tell me if theres anything wrong with my algorythm? Here is the header file of my class:
#pragma once
// Diese Datei enthaelt die Definition der Terrain-Klasse
#include <d3d9.h>
#include <d3dx9.h>
// Vertex-Format definieren
// momentan werden nur Koordinaten und Farben als
// Elemente fuer die Vertices verwendet
#define D3DFVF_TERRAINVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
struct TERRAIN_VERTEX
{
float x, y, z;
D3DCOLOR Color;
};
// Klasse Terrain
class CTerrain
{
public:
CTerrain();
virtual ~CTerrain();
// Terrain aus der uebergebenen Heightmap erzeugen
void Create(LPDIRECT3DDEVICE9 lpDevice, LPCSTR lpHeightmap);
// Terrain rendern
void Render(D3DXVECTOR3 Position, D3DXVECTOR3 Rotation, D3DXVECTOR3 Scale);
protected:
// Breite und Laenge des Terrain
int m_Width;
int m_Length;
// der Vertexbuffer enthaelt die Vertices
LPDIRECT3DVERTEXBUFFER9 m_VBuffer;
LPDIRECT3DDEVICE9 m_lpDevice;
// der Indexbuffer beschreibt die Dreiecke
LPDIRECT3DINDEXBUFFER9 m_IBuffer;
};
If you'd like to use it, just create an object of CTerrain class, call create, pass in a valid D3D-Device, and for render simply pass 3 vectors: (0, 0, 0), (0, 0,0), (1, 1, 1). Should be self-explaining but you never know.. hopefully someone figures out whats wrong!
2. I want to illuminate the terrain, and therefor I need the normals of the vertices. It would be no problem to change my FVF-declaration, but how do I calculate the normals? From what I read I need the 8 vertices nearby, but how do I get them, and how do I use these data to calculate the normals? Are there any tutorials/sample algorythm or can someone explain to me (with some pseudo-code-example would be best).
3. How do I "paint" that terrain? All my books just use a already drawn texture to lay over the terrain, but I'd like to do something a little more practical. What would be a "modern" approach to paint a terrain if I want to build my own level editor? Should I just create such a texture and modify the texture if I paint the terrain in the editor, or is there a better way to store the textures for terrain (using a whole texture for every terrain would cause a lot of hard disk space and probably memory too).
4. Is Heightmaps even a good approach to do terrain? For some parts I'd want to have terrain thats "over" another terrain (you know, like a cliff that doesn't go down vertically but a bit offset into the ground). Is this possible with heightmaps or do I have to take a differnet approach?