# Thread: Terrain Screenshot and Discussion

1. I found this on a board search. Perhaps you could use this to perform some frustrum culling on your terrian.

Code:
void Frustum::Calculate( D3DXMATRIX *lpMatView, D3DXMATRIX *lpMatProj )
{
D3DXMATRIX matClip;

D3DXMatrixMultiply( &matClip, lpMatView, lpMatProj);
D3DXMatrixInverse( &matClip, NULL, &matClip );

// Use unit extents at first
vertices[0] = D3DXVECTOR3(-1.0f, -1.0f,  0.0f);
vertices[1] = D3DXVECTOR3( 1.0f, -1.0f,  0.0f);
vertices[2] = D3DXVECTOR3(-1.0f,  1.0f,  0.0f);
vertices[3] = D3DXVECTOR3( 1.0f,  1.0f,  0.0f);
vertices[4] = D3DXVECTOR3(-1.0f, -1.0f,  1.0f);
vertices[5] = D3DXVECTOR3( 1.0f, -1.0f,  1.0f);
vertices[6] = D3DXVECTOR3(-1.0f,  1.0f,  1.0f);
vertices[7] = D3DXVECTOR3( 1.0f,  1.0f,  1.0f);

// Transform unit extents by inverse clip matrix
for( int i = 0; i < 8; ++i )
D3DXVec3TransformCoord( &vertices[i], &vertices[i], &matClip );

// Calculate the planes of the frustum from the given extents
D3DXPlaneFromPoints( &planes[0], &vertices[0], &vertices[1], &vertices[2] );
D3DXPlaneFromPoints( &planes[1], &vertices[6], &vertices[7], &vertices[5] );
D3DXPlaneFromPoints( &planes[2], &vertices[2], &vertices[6], &vertices[4] );
D3DXPlaneFromPoints( &planes[3], &vertices[7], &vertices[3], &vertices[5] );
D3DXPlaneFromPoints( &planes[4], &vertices[2], &vertices[3], &vertices[6] );
D3DXPlaneFromPoints( &planes[5], &vertices[1], &vertices[0], &vertices[4] );
}
This code was originally posted by Mr. Wizard.
Hope it helps.

I'm coding a terrain system as well just to test some new theories and perhaps be more on the same page as you are so we can continue this discussion. Terrain is very cool and an integral part of most games. Doing it right is very hard.

2. Thanks for the resources Bubba, I'll definitely be sure to check them all out. A friend of mine happened to have the first book, and for anybody looking to start in on this topic, it was fantastic. My biggest problem was realizing the material was covered in the book AFTER I tackled it on my own and created a crude solution (luckily, it was often very similar, and the material was invaluable for tweaking).

Doing it right is very hard.
Amen to that. And especially the first time. Like I mentioned before, we're in the middle of a complete re-write, and this is exactly the reason why. But one thing I learned is that you just can't get discouraged. The problems to solutions ratio is massive, but you just gotta keep working through it.

You can kill that code that mixes the textures and pre-mix it using the FFP prior to setting the texture for the Device.
Indeed? Well, part of the problems we're having with this whole project is not knowing the full capabilities of what we're working with. When a problem crops up, that's when we start looking for solutions. Of course there're things we can plan ahead for, but we really are learning this as we go. So any comments and suggestions are always appreciated. I'll look into the Fixed Function Pipeline (I'll start with what it actually is...then work my way from there)

Depending on how the FFP (See how quickly I pick up the lingo?) handles it all, we might go that way. But our mixing function took a solid chunk of work, so there's some sentimental value to keeping it But of course, if it's not as efficient, powerful, or fast, then goodbye.

Sometimes I really do feel like I'm trying to build an airplane and I've never even seen a wrench. But one bolt and screw at a time...I've finally gotten a wheel on. And that wheel may or may not be in the shape of a triangle.

I'm also currently reading through a chapter from "Focus on 3D Terrain Programming" by Trent Polack, which heavily focusses on Roettger's Algorithm and how to approach it. Currently it's only light reading, so I'm not sure how well it will explain everything, but I'm hoping for the best. Once this re-write's back to where the previous version was, we'll begin looking ahead once again to implementing new features.

3. Here is a sample of some detail mapping.

There are two textures being blended together here. One is extremely resolute and the other is wrapped over the grid.

This is using the FFP.

4. Here is another screenshot with the height map blurred a lot and the detail texture is now a 512x512 greyscale planar bitmap. See how much detail you can get. Now add this to your already impressive system and it would look extremely good. Notice you can see the 'edges' of my detail map. If you fix this, it will look very good. And this is with no lighting at all.

5. And finally this is detail mapping and light mapping.

This requires 3 texture stages. For bump mapping I would need to add a 4th texture stage and a 4th set of texture coordinates.

Again Direct3D lighting is off. No normals here or anything.

Code:
void CTerrain::Render(D3DXMATRIX trans,float fTimeDelta)
{

m_pDevice->SetTransform(D3DTS_WORLD, &trans);

m_pDevice->SetStreamSource(0,m_pVB,0,sizeof(TerrainVertex));
m_pDevice->SetIndices(m_pIB);

m_pDevice->SetFVF(TerrainVertex::FVF);

m_pDevice->SetTexture(0,m_pLand);
m_pDevice->SetTexture(1,m_pDetail);
m_pDevice->SetTexture(2,m_pLight);

m_pDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
m_pDevice->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
m_pDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);

m_pDevice->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_CURRENT);
m_pDevice->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_TEXTURE);

m_pDevice->SetTextureStageState(1,D3DTSS_ALPHAARG1,D3DTA_CURRENT);
m_pDevice->SetTextureStageState(1,D3DTSS_ALPHAARG2,D3DTA_TEXTURE);

m_pDevice->SetTextureStageState(2,D3DTSS_COLORARG1,D3DTA_CURRENT);
m_pDevice->SetTextureStageState(2,D3DTSS_COLORARG2,D3DTA_TEXTURE);
m_pDevice->SetTextureStageState(2,D3DTSS_COLOROP,D3DTOP_MODULATE);
m_pDevice->SetTextureStageState(2,D3DTSS_ALPHAARG1,D3DTA_CURRENT);
m_pDevice->SetTextureStageState(2,D3DTSS_ALPHAARG2,D3DTA_TEXTURE);
m_pDevice->SetTextureStageState(2,D3DTSS_ALPHAOP,D3DTOP_MODULATE);

m_pDevice->SetTextureStageState(3,D3DTSS_COLOROP,D3DTOP_DISABLE);
m_pDevice->SetTextureStageState(3,D3DTSS_ALPHAOP,D3DTOP_DISABLE);

m_pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
0,0,m_iNumVerts,
0,m_iNumTris);
}

6. Another one with the detail texture the same as, but far smaller and dense then the land texture. Check out the detail. Obviously you would not want the detail everywhere as mine shows, but you can do a lot with detail mapping.

I'll show another shot with bump mapping here soon.

7. Here is a screenshot with vertex lighting, light mapping, detail mapping, pixel fog, and a texture stretched across the grid. There is no skybox in this shot. Skyboxes don't lend themselves well to fog effects in the FFP.

8. This engine is producing rather nice looking terrains.

I'm going to implement the texture blending that Perspective and you have used. Quickly I think I'm going to run out of texture stages for this terrain system.

It would look fantastic with a pixel shader but I haven't gotten that far yet. It needs some trees, water, etc. Going to add a sky plane and water plane soon.

9. terrains are the very beginning of a 3d game. though im using opengl. i have no idea how to make a terrain...
they look very nice btw

10. Well you need lots of textures.

1. Greyscale heightmap
2. High res color textures for all portions of land (mine are 512x512)
3. Blend maps for each type of texture.
4. Detail maps if needed. (Mine is 512x512)
5. Bump maps if needed.
6. Specular environment maps if needed.
7. Light maps if needed.

Then you create a grid of vertices based on the value in the heightmap. Display the y component of the vector by the value in the heightmap.

11. >I'm going to implement the texture blending that Perspective and you have used

Its right here if you want it:

12. I'm having a big problem with the terrain. I'm trying to dynamically generate the terrain around the camera and it works so far. I'm only using 64x64 vertices at any one time. the problem is that the vertices need to be recycled, yet appear to move as well. I'm not quite sure how to do this.

Right now as the camera moves, the heights of the vertices change according to the part of the map inside of the frustrum, but the vertices do not move toward you.

When I attempt to translate, the vertices move, but the grid moves out of sight, all the while still accurately updating the position.

How do i get the terrain to stay around the camera, yet still move in relation to it?

Here is the code for GenerateVertices(). It does not use a quadtree yet but the code is done for it.

Code:
void CTerrain::GenerateVertices(float fViewAngle,D3DXVECTOR3 Pos)
{
HRESULT hr=0;

TerrainVertex *Verts=0;
m_pVB->Lock(0,0,(void **)&Verts,0);

float fLeftAngle=fViewAngle-m_fHFOV2;
if (fLeftAngle<0) fLeftAngle+=(D3DX_PI*2);

float fRightAngle=fLeftAngle+m_fHFOV;
if (fRightAngle>(D3DX_PI*2)) fRightAngle-=(D3DX_PI*2);

//Find out frustrum
float fRange2=m_fViewRange*.5f;

float fLeft=Pos.x-(cosf(fLeftAngle)*fRange2);
float fRight=Pos.x+(cosf(fRightAngle)*fRange2);
float fFront=Pos.z+m_fViewRange;           //(sinf(fViewAngle)*m_fViewRange);
float fBack=Pos.z;

char txt[100];
sprintf(txt,"%f %f %f ",fLeftAngle,fRightAngle,fViewAngle);

float uInc=2.0f/(float)m_iNumVertsPerRow;
float vInc=2.0f/(float)m_iNumVertsPerCol;

int index=0;

float x=-fRange2;
float z=-fRange2;

for (float i=fLeft;i<fRight;i+=m_iCellSize)
{
for (float j=fBack;j<fFront;j+=m_iCellSize)
{
int MapX=i/m_iCellSize;
int MapZ=j/m_iCellSize;

MapX %=m_iWidth;
MapZ %=m_iDepth;

int MapIndex=abs(MapZ)*m_iWidth+abs(MapX);
int iHeight=m_puHeightMap[MapIndex];

Verts[index]=TerrainVertex(D3DXVECTOR3(i,(float)iHeight,j),
(float)x*uInc,
(float)z*vInc);
index++;
x+=(float)m_iCellSize;

}
x=-fRange2;
z+=(float)m_iCellSize;

}

//char txt[100];
//sprintf(txt,"Index:%u ",index);
//::MessageBox(0,txt,0,0);

m_pVB->Unlock();

}

13. Are you rendering the terrain before any movement translations are done on the view matrix? Sounds like your moving the terrain as well as the camera translating it.

14. I'm sending the identity matrix as the translation for the terrain.

What should the vertices be created as? Equidistant from 0.0f,0.0f,0.0f or is it

CameraPos-FrustrumPos

This:
Code:
Verts[index]=TerrainVertex(D3DXVECTOR3(i,(float)iHeight,j),
(float)x*uInc,
(float)z*vInc);
Should this be x and z instead of i and j?

The point is that I do not want the terrain to ever end or look like it falls off the face of the planet. I want to simply repeat terrain according to the heightmap much like I did with raycasting and voxel heightfields. Even though the map was 256x256 I could just clamp the map values to the size of the map and get the height values. This caused a repeating terrain that stretched everywhere.

Ok, I'm going to re-write the code completely. Here is my plan:

Frustrum computation
1. Retrieve view angle from camera
2. Find left and right extents of frustrum based on HFOV
3. Subtract left from cameraPos and right to get two basis vectors for frustrum.
4. Basically scan convert the triangle only I'm not using pixels, I'm using cells. Any cells that fall inside the triangle are added to the vertex buffer. I can't use indexes into the buffer since that would require them to interconnect. So each quad will be 4 vertices drawn as a triangle strip.

This allows me to draw quads anywhere I need since they are not part of a regular vertex grid. This means I could draw only those quads that lie in the frustrum cone.

I'm not sure how to handle the pitch component of the frustrum since that would cause issues with my algo.

Code:
...
D3DXVECTOR3 UL,UR,LL,LR,CameraPos,Look,Right;

pCamera->GetPosition(&CameraPos);
pCamera->GetLook(&Look);
pCamera->GetRight(&Right);

LR.x=CameraPos.x+(Right.x*m_fViewRange2);
LR.y=0.0f;
LR.z=CameraPos.z+(Right.z*m_fViewRange2);

UR.x=LR.x+(Look.x*m_fViewRange);
UR.y=0.0f;
UR.z=LR.z+(Look.z*m_fViewRange);

UL.x=UR.x-(Right.x*m_fViewRange);
UL.y=0.0f;
UL.z=UR.z-(Right.z*m_fViewRange);

LL.x=UL.x-(Look.x*m_fViewRange);
LL.y=0.0f;
LL.z=UL.z-(Look.z*m_fViewRange);
...

This should retrieve the current frustrum in front of the player right? It's not a pyramid, but it's a a square area.