Well I took out everything that is not part of the problem. Here's the complete source code of terrain:
#ifndef TERRAIN_H
#define TERRAIN_H
#include "Heightmap.h"
#include "d3dUtil.h"
#include "Vertex.h"
#include "Camera.h"
class Terrain
{
public:
Terrain(UINT vertRows, UINT vertCols, float dx, float dz,
char * heightmap, char * tex0, char *tex1,
char * tex2, char * blendMap, float heightScale,
float yOffset, Camera & cam);
~Terrain();
DWORD getNumTriangles();
DWORD getNumVertices();
float getWidth();
float getDepth();
void onLostDevice();
void onResetDevice();
// (x, z) relative to terrain's local space.
float getHeight(float x, float z);
void setDirToSunW(const D3DXVECTOR3& d);
void draw(D3DXMATRIX& view, D3DXMATRIX& proj);
private:
void buildGeometry();
void buildSubGridMesh(RECT& R, VertexPNT* gridVerts);
void buildEffect();
Camera & mCamera;
struct SubGrid
{
ID3DXMesh* mesh;
AABB box;
// For sorting.
bool operator<(const SubGrid& rhs)const;
const static int NUM_ROWS = 33;
const static int NUM_COLS = 33;
const static int NUM_TRIS = (NUM_ROWS-1)*(NUM_COLS-1)*2;
const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
};
private:
Heightmap mHeightmap;
std::vector<SubGrid> mSubGrids;
DWORD mVertRows;
DWORD mVertCols;
float mWidth;
float mDepth;
float mDX;
float mDZ;
IDirect3DTexture9* mTex0;
IDirect3DTexture9* mTex1;
IDirect3DTexture9* mTex2;
IDirect3DTexture9* mBlendMap;
ID3DXEffect* mFX;
D3DXHANDLE mhTech;
D3DXHANDLE mhViewProj;
D3DXHANDLE mhDirToSunW;
D3DXHANDLE mhTex0;
D3DXHANDLE mhTex1;
D3DXHANDLE mhTex2;
D3DXHANDLE mhBlendMap;
};
#endif // TERRAIN_HI marked the errors down below.
#include "Terrain.h"
#include <list>
Terrain::Terrain(UINT vertRows, UINT vertCols, float dx, float dz,
char * heightmap, char *tex0, char *tex1,
char * tex2, char *blendMap, float heightScale,
float yOffset, Camera & cam):mCamera(cam) // <--- Like this?
{
mVertRows = vertRows;
mVertCols = vertCols;
mDX = dx;
mDZ = dz;
mWidth = (mVertCols-1)*mDX;
mDepth = (mVertRows-1)*mDZ;
mHeightmap.loadRAW(vertRows, vertCols, heightmap, heightScale, yOffset);
tbTextureManager& tm = tbTextureManager::Instance();
mBlendMap = tm.GetTexture(blendMap);
mTex0 = tm.GetTexture(tex0);
mTex1 = tm.GetTexture(tex1);
mTex2 = tm.GetTexture(tex2);
buildGeometry();
buildEffect();
}
Terrain::~Terrain()
{
for(UINT i = 0; i < mSubGrids.size(); ++i)
ReleaseCOM(mSubGrids[i].mesh);
ReleaseCOM(mFX);
ReleaseCOM(mTex0);
ReleaseCOM(mTex1);
ReleaseCOM(mTex2);
ReleaseCOM(mBlendMap);
}
DWORD Terrain::getNumTriangles()
{
return (DWORD)mSubGrids.size()*mSubGrids[0].mesh->GetNumFaces();
}
DWORD Terrain::getNumVertices()
{
return (DWORD)mSubGrids.size()*mSubGrids[0].mesh->GetNumVertices();
}
float Terrain::getWidth()
{
return mWidth;
}
float Terrain::getDepth()
{
return mDepth;
}
void Terrain::onLostDevice()
{
HR(mFX->OnLostDevice());
}
void Terrain::onResetDevice()
{
HR(mFX->OnResetDevice());
}
float Terrain::getHeight(float x, float z)
{
// Transform from terrain local space to "cell" space.
float c = (x + 0.5f*mWidth) / mDX;
float d = (z - 0.5f*mDepth) / -mDZ;
// Get the row and column we are in.
int row = (int)floorf(d);
int col = (int)floorf(c);
// Grab the heights of the cell we are in.
// A*--*B
// | /|
// |/ |
// C*--*D
float A = mHeightmap(row, col);
float B = mHeightmap(row, col+1);
float C = mHeightmap(row+1, col);
float D = mHeightmap(row+1, col+1);
// Where we are relative to the cell.
float s = c - (float)col;
float t = d - (float)row;
// If upper triangle ABC.
if(t < 1.0f - s)
{
float uy = B - A;
float vy = C - A;
return A + s*uy + t*vy;
}
else // lower triangle DCB.
{
float uy = C - D;
float vy = B - D;
return D + (1.0f-s)*uy + (1.0f-t)*vy;
}
}
void Terrain::setDirToSunW(const D3DXVECTOR3& d)
{
HR(mFX->SetValue(mhDirToSunW, &d, sizeof(D3DXVECTOR3)));
}
bool Terrain::SubGrid::operator<(const SubGrid& rhs) const
{
D3DXVECTOR3 d1 = box.center() - mCamera->getPos().vD3DVector; // ERROR
D3DXVECTOR3 d2 = rhs.box.center() - mCamera->getPos().vD3DVector; // ERROR
return D3DXVec3LengthSq(&d1) < D3DXVec3LengthSq(&d2);
}
void Terrain::draw(D3DXMATRIX& view, D3DXMATRIX& proj)
{
// Frustum cull sub-grids.
std::list<SubGrid> visibleSubGrids;
for(UINT i = 0; i < mSubGrids.size(); ++i)
{
if( mCamera->isVisible(mSubGrids[i].box) ) // ERROR
visibleSubGrids.push_back(mSubGrids[i]);
}
// Sort front-to-back from camera.
visibleSubGrids.sort();
HR(mFX->SetMatrix(mhViewProj, &(view*proj)));
HR(mFX->SetTechnique(mhTech));
UINT numPasses = 0;
HR(mFX->Begin(&numPasses, 0));
HR(mFX->BeginPass(0));
for(std::list<SubGrid>::iterator iter = visibleSubGrids.begin(); iter != visibleSubGrids.end(); ++iter)
HR(iter->mesh->DrawSubset(0));
HR(mFX->EndPass());
HR(mFX->End());
}
void Terrain::buildGeometry()
{
//===============================================================
// Create one large mesh for the grid in system memory.
DWORD numTris = (mVertRows-1)*(mVertCols-1)*2;
DWORD numVerts = mVertRows*mVertCols;
ID3DXMesh* mesh = 0;
D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE];
UINT numElems = 0;
HR(VertexPNT::Decl->GetDeclaration(elems, &numElems));
// Use Scratch pool since we are using this mesh purely for some CPU work,
// which will be used to create the sub-grids that the graphics card
// will actually draw.
HR(D3DXCreateMesh(numTris, numVerts,
D3DPOOL_SCRATCH|D3DXMESH_32BIT, elems, tbDirect3D::Instance().GetDevice(), &mesh));
//===============================================================
// Write the grid vertices and triangles to the mesh.
VertexPNT* v = 0;
HR(mesh->LockVertexBuffer(0, (void**)&v));
std::vector<D3DXVECTOR3> verts;
std::vector<DWORD> indices;
GenTriGrid(mVertRows, mVertCols, mDX, mDZ, D3DXVECTOR3(0.0f, 0.0f, 0.0f), verts, indices);
float w = mWidth;
float d = mDepth;
for(UINT i = 0; i < mesh->GetNumVertices(); ++i)
{
// We store the grid vertices in a linear array, but we can
// convert the linear array index to an (r, c) matrix index.
int r = i / mVertCols;
int c = i % mVertCols;
v[i].pos = verts[i];
v[i].pos.y = mHeightmap(r, c);
v[i].tex0.x = (v[i].pos.x + (0.5f*w)) / w;
v[i].tex0.y = (v[i].pos.z - (0.5f*d)) / -d;
}
// Write triangle data so we can compute normals.
DWORD* indexBuffPtr = 0;
HR(mesh->LockIndexBuffer(0, (void**)&indexBuffPtr));
for(UINT i = 0; i < mesh->GetNumFaces(); ++i)
{
indexBuffPtr[i*3+0] = indices[i*3+0];
indexBuffPtr[i*3+1] = indices[i*3+1];
indexBuffPtr[i*3+2] = indices[i*3+2];
}
HR(mesh->UnlockIndexBuffer());
// Compute Vertex Normals.
HR(D3DXComputeNormals(mesh, 0));
//===============================================================
// Now break the grid up into subgrid meshes.
// Find out the number of subgrids we'll have. For example, if
// m = 513, n = 257, SUBGRID_VERT_ROWS = SUBGRID_VERT_COLS = 33,
// then subGridRows = 512/32 = 16 and sibGridCols = 256/32 = 8.
int subGridRows = (mVertRows-1) / (SubGrid::NUM_ROWS-1);
int subGridCols = (mVertCols-1) / (SubGrid::NUM_COLS-1);
for(int r = 0; r < subGridRows; ++r)
{
for(int c = 0; c < subGridCols; ++c)
{
// Rectangle that indicates (via matrix indices ij) the
// portion of grid vertices to use for this subgrid.
RECT R =
{
c * (SubGrid::NUM_COLS-1),
r * (SubGrid::NUM_ROWS-1),
(c+1) * (SubGrid::NUM_COLS-1),
(r+1) * (SubGrid::NUM_ROWS-1)
};
buildSubGridMesh(R, v);
}
}
HR(mesh->UnlockVertexBuffer());
ReleaseCOM(mesh); // Done with global mesh.
}
void Terrain::buildSubGridMesh(RECT& R, VertexPNT* gridVerts)
{
//===============================================================
// Get indices for subgrid (we don't use the verts here--the verts
// are given by the parameter gridVerts).
std::vector<D3DXVECTOR3> tempVerts;
std::vector<DWORD> tempIndices;
GenTriGrid(SubGrid::NUM_ROWS, SubGrid::NUM_COLS, mDX, mDZ,
D3DXVECTOR3(0.0f, 0.0f, 0.0f), tempVerts, tempIndices);
ID3DXMesh* subMesh = 0;
D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE];
UINT numElems = 0;
HR(VertexPNT::Decl->GetDeclaration(elems, &numElems));
HR(D3DXCreateMesh(SubGrid::NUM_TRIS, SubGrid::NUM_VERTS,
D3DXMESH_MANAGED, elems, tbDirect3D::Instance().GetDevice(), &subMesh));
//===============================================================
// Build Vertex Buffer. Copy rectangle of vertices from the
// grid into the subgrid structure.
VertexPNT* v = 0;
HR(subMesh->LockVertexBuffer(0, (void**)&v));
int k = 0;
for(int i = R.top; i <= R.bottom; ++i)
{
for(int j = R.left; j <= R.right; ++j)
{
v[k++] = gridVerts[i*mVertCols+j];
}
}
//===============================================================
// Compute the bounding box before unlocking the vertex buffer.
AABB bndBox;
HR(D3DXComputeBoundingBox((D3DXVECTOR3*)v, subMesh->GetNumVertices(),
sizeof(VertexPNT), &bndBox.minPt, &bndBox.maxPt));
HR(subMesh->UnlockVertexBuffer());
//===============================================================
// Build Index and Attribute Buffer.
WORD* indices = 0;
DWORD* attBuff = 0;
HR(subMesh->LockIndexBuffer(0, (void**)&indices));
HR(subMesh->LockAttributeBuffer(0, &attBuff));
for(int i = 0; i < SubGrid::NUM_TRIS; ++i)
{
indices[i*3+0] = (WORD)tempIndices[i*3+0];
indices[i*3+1] = (WORD)tempIndices[i*3+1];
indices[i*3+2] = (WORD)tempIndices[i*3+2];
attBuff[i] = 0; // All in subset 0.
}
HR(subMesh->UnlockIndexBuffer());
HR(subMesh->UnlockAttributeBuffer());
//===============================================================
// Optimize for the vertex cache and build attribute table.
DWORD* adj = new DWORD[subMesh->GetNumFaces()*3];
HR(subMesh->GenerateAdjacency(EPSILON, adj));
HR(subMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE|D3DXMESHOPT_ATTRSORT,
adj, 0, 0, 0));
delete[] adj;
//===============================================================
// Save the mesh and bounding box.
SubGrid g;
g.mesh = subMesh;
g.box = bndBox;
mSubGrids.push_back(g);
}
void Terrain::buildEffect()
{
ID3DXBuffer* errors = 0;
HR(D3DXCreateEffectFromFile(tbDirect3D::Instance().GetDevice(), "Terrain.fx",
0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors));
if( errors )
MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);
mhTech = mFX->GetTechniqueByName("TerrainTech");
mhViewProj = mFX->GetParameterByName(0, "gViewProj");
mhDirToSunW = mFX->GetParameterByName(0, "gDirToSunW");
mhTex0 = mFX->GetParameterByName(0, "gTex0");
mhTex1 = mFX->GetParameterByName(0, "gTex1");
mhTex2 = mFX->GetParameterByName(0, "gTex2");
mhBlendMap = mFX->GetParameterByName(0, "gBlendMap");
HR(mFX->SetTexture(mhTex0, mTex0));
HR(mFX->SetTexture(mhTex1, mTex1));
HR(mFX->SetTexture(mhTex2, mTex2));
HR(mFX->SetTexture(mhBlendMap, mBlendMap));
}Main (beware of German commentary! InitTerrain plays a key role here.)
// ===================
// Hier werden die Direct3D-Klassen der TriBase-Engine angewandt.
#include <TriBase.h>
#include "Resource.h"
#include "Config.h"
#include "Vertex.h"
#include "Camera.h"
#include "GameObject.h"
#include "Terrain.h"
// ******************************************************************
// Globale Variablen
tbConfig g_Config; // Konfigurationsstruktur
PDIRECT3DCUBETEXTURE9 g_pEnvMap = NULL; // Umgebungstextur
tbEffect* g_pSkyBoxEffect = NULL; // Sky-Box-Effekt
tbVertexBuffer* g_pSkyBoxVB = NULL; // Sky-Box-Vertex-Buffer
tbIndexBuffer* g_pSkyBoxIB = NULL; // Sky-Box-Index-Buffer
tbFont* g_pFont1 = NULL; // Erste Schriftart
float g_fTime = 0.0f; // Globaler Zeitzähler
Camera g_Camera; /// Kamera
GameObject* g_Cube; /// Würfel
Terrain* g_Terrain; /// Landschaft
tbModel* g_pDuckModel; /// Platzhaltermodell
// ******************************************************************
// Die Move-Funktion
tbResult MoveProc(float fNumSecsPassed)
{
tbVector3 vCameraDir;
// Den Zeitzähler aktualisieren
g_fTime += fNumSecsPassed;
// Input
//---------------------------------------------------------------
g_Cube->UpdateInput(fNumSecsPassed);
g_Camera.moveCamera(g_Cube->Position, fNumSecsPassed);
// Physics
// --------------------------------------------------------------
//g_Terrain->setDirToSunW(D3DXVECTOR3(cos(fNumSecsPassed), sin(fNumSecsPassed) , 0.0f)); // looks nice
g_Cube->UpdatePhysics(g_Terrain, fNumSecsPassed);
return TB_OK;
}
// ******************************************************************
// Die Render-Funktion
tbResult RenderProc(float fNumSecsPassed)
{
tbMatrix mWorld;
int iNumPasses;
tbDirect3D& D3D = tbDirect3D::Instance();
//Z-Buffer clearen, Skybox überschreibt das Bild
D3D->Clear(0,NULL, D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0, 0,0), 1.0f, 0);
// Die Szene beginnen
D3D->BeginScene();
// ------------------------------------------------------------------
g_Camera.setUpCamera();
// ------------------------------------------------------------------
// Z-Buffer deaktivieren, Dithering aktivieren
D3D.SetRS(D3DRS_ZENABLE, FALSE);
D3D.SetRS(D3DRS_DITHERENABLE, TRUE);
// Die Umgebungstextur setzen
D3D.SetTexture(0, g_pEnvMap);
D3D.SetTSS(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
// Weltmatrix erstellen, die die Sky-Box zur Kamera hin schiebt
mWorld = tbMatrixTranslation(g_Camera.getPos());
D3D.SetTransform(D3DTS_WORLD, mWorld);
// Vertex- und Index-Buffer aktivieren und das Vertexformat setzen
D3D->SetStreamSource(0, g_pSkyBoxVB->GetVB(), 0, sizeof(SSkyBoxVertex));
D3D->SetIndices(g_pSkyBoxIB->GetIB());
D3D.SetFVF(SSkyBoxVertex::dwFVF);
// Zeichnen!
iNumPasses = g_pSkyBoxEffect->Begin();
for(int iPass = 0; iPass < iNumPasses; iPass++)
{
g_pSkyBoxEffect->Pass(iPass);
D3D->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
}
g_pSkyBoxEffect->End();
//-------------------------------------------------------------------
D3D.SetRS(D3DRS_ZENABLE, TRUE);
g_Terrain->draw((D3DXMATRIX&) g_Camera.getViewMatrix(), (D3DXMATRIX&) g_Camera.getProjMatrix() );
//-------------------------------------------------------------------
g_Cube->UpdateGraphics(g_fTime);
//-------------------------------------------------------------------
///ENTE
D3DLIGHT9 Light;
// Ein Richtungslicht erstellen mit der Richtung der Kamera
ZeroMemory(&Light, sizeof(D3DLIGHT9));
Light.Type = D3DLIGHT_DIRECTIONAL;
Light.Diffuse = tbColor(0.5f, 0.5f, 0.5f);
Light.Ambient = tbColor(0.5f, 0.5f, 0.5f);
Light.Specular = tbColor(0.5f, 0.5f, 0.5f);
Light.Direction = g_Camera.getTarget();
D3D->SetLight(0, &Light);
D3D->LightEnable(0, TRUE);
// Weltmatrix zurücksetzen
D3D.SetTransform(D3DTS_WORLD, tbMatrixIdentity());
D3D.SetRS(D3DRS_LIGHTING, TRUE);
g_pDuckModel->Render(-1, -1, TRUE, FALSE);
//-------------------------------------------------------------------
// Debug-Hilfen
char acText[256]; // Speichert die Nachricht
tbVector3 camPos = g_Camera.getPos();
g_pFont1->Begin();
sprintf(acText, "Camera: %.2f | %.2f | %.2f", camPos.x, camPos.y, camPos.z);
g_pFont1->DrawTextA(tbVector2(10.0f,10.0f), acText);
sprintf(acText, "Player: %.2f | %.2f | %.2f", g_Cube->Position.x, g_Cube->Position.y, g_Cube->Position.z);
g_pFont1->DrawTextA(tbVector2(10.0f,30.0f), acText);
switch(g_Camera.getMode())
{
case FREE_FLYING: {sprintf(acText, "Camera mode: Free flying"); break;}
case BIRDS_EYE_PLAYER: {sprintf(acText, "Camera mode: Bird's eye on player"); break;}
case FOLLOW_PLAYER: {sprintf(acText, "Camera mode: Follow player"); break;}
case FOLLOW_PLAYER_LEFT: {sprintf(acText, "Camera mode: Follow left of player"); break;}
case FOLLOW_PLAYER_RIGHT: {sprintf(acText, "Camera mode: Follow Right of player"); break;}
}
g_pFont1->DrawTextA(tbVector2(10.0f,50.0f), acText);
g_pFont1->End();
// ------------------------------------------------------------------
// Szene beenden
D3D->EndScene();
return TB_OK;
}
// ******************************************************************
// Initialisierung der Sky-Box
tbResult InitSkyBox()
{
SSkyBoxVertex aVertex[8];
// Vertex- und Index-Buffer erstellen. 8 Vertizes, 36 Indizes.
g_pSkyBoxVB = new tbVertexBuffer;
if(g_pSkyBoxVB->Init(8 * sizeof(SSkyBoxVertex), sizeof(SSkyBoxVertex), SSkyBoxVertex::dwFVF,
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DPOOL_DEFAULT))
{
// Fehler!
return TB_ERROR;
}
// Nun der Index-Buffer (16 Bits pro Index)
g_pSkyBoxIB = new tbIndexBuffer;
if(g_pSkyBoxIB->Init(36 * sizeof(WORD), sizeof(WORD), D3DFMT_INDEX16))
{
// Fehler!
return TB_ERROR;
}
// ------------------------------------------------------------------
// Die Vertizes erstellen
aVertex[0].vPosition = tbVector3(-1.0f, 1.0f, 1.0f);
aVertex[1].vPosition = tbVector3( 1.0f, 1.0f, 1.0f);
aVertex[2].vPosition = tbVector3( 1.0f, 1.0f, -1.0f);
aVertex[3].vPosition = tbVector3(-1.0f, 1.0f, -1.0f);
aVertex[4].vPosition = tbVector3(-1.0f, -1.0f, 1.0f);
aVertex[5].vPosition = tbVector3( 1.0f, -1.0f, 1.0f);
aVertex[6].vPosition = tbVector3( 1.0f, -1.0f, -1.0f);
aVertex[7].vPosition = tbVector3(-1.0f, -1.0f, -1.0f);
// Die Texturkoordinaten entsprechen den Vertexpositionen
for(int iVertex = 0; iVertex < 8; iVertex++)
{
// Texturkoordinate eintragen
aVertex[iVertex].vTexture = aVertex[iVertex].vPosition;
}
// Alle Vertizes eintragen und den Vertex-Buffer aktualisieren
g_pSkyBoxVB->AddVertices(8, aVertex);
if(g_pSkyBoxVB->Update()) return TB_ERROR;
// ------------------------------------------------------------------
// Den Index-Buffer ausfüllen
WORD awIndex[36] = {7, 3, 0, 4, 7, 0, // Vorderseite
5, 1, 2, 6, 5, 2, // Hinterseite
4, 0, 1, 5, 4, 1, // Linke Seite
6, 2, 3, 7, 6, 3, // Rechte Seite
2, 1, 0, 3, 2, 0, // Oberseite
4, 5, 6, 7, 4, 6}; // Unterseite
g_pSkyBoxIB->AddIndices(36, awIndex);
if(g_pSkyBoxIB->Update()) return TB_ERROR;
// ------------------------------------------------------------------
// Die Textur der Sky-Box laden
g_pEnvMap = tbTextureManager::Instance().GetCubeTexture("_GRAPHICS/EnvMap.dds");
if(g_pEnvMap == NULL) return TB_ERROR;
// Effekt laden
g_pSkyBoxEffect = new tbEffect;
if(g_pSkyBoxEffect == NULL) return TB_ERROR;
if(g_pSkyBoxEffect->Init("_GRAPHICS/SkyBox.fx")) return TB_ERROR;
return TB_OK;
}
// ******************************************************************
// Initialisierung der Schriftarten
tbResult InitFonts()
{
// Erste Schriftart...
g_pFont1 = new tbFont;
if(g_pFont1->Init("_GRAPHICS/Futura_8_22.tga", "_GRAPHICS/Futura_8_22.tbf"))
{
// Fehler...
return TB_ERROR;
}
return TB_OK;
}
// ******************************************************************
// Initialisierung des Terrains
tbResult InitTerrain()
{
InitVertexDeclaration();
g_Terrain = new Terrain(257, 257, 0.5f, 0.5f,
"heightmap17_257.raw",
"_GRAPHICS/grass.dds",
"_GRAPHICS/dirt.dds",
"_GRAPHICS/stone.dds",
"_GRAPHICS/blend_hm17.dds",
0.2f, 0.0f, g_Camera);
g_Terrain->setDirToSunW(D3DXVECTOR3(0.0f, 1.0f, 0.0f));
if (g_Terrain == NULL)
return TB_ERROR;
else
return TB_OK;
}
// ******************************************************************
// Aufräumen
tbResult CleanUp()
{
///
tbDirect3D& D3D = tbDirect3D::Instance();
// Textur deaktivieren und löschen
D3D->SetTexture(0, NULL);
// Vertex- und Index-Buffer deaktivieren und löschen
D3D->SetStreamSource(0, NULL, 0, 0);
D3D->SetIndices(NULL);
// Terrain benutzt Declaration anstatt FVF
DestroyVertexDeclaration();
g_Terrain->~Terrain();
// Alles löschen
tbDirect3D::Instance().Exit();
tbTextureManager::Instance().Exit();
tbDirectInput::Instance().Exit();
TB_SAFE_DELETE(g_pDuckModel);
TB_SAFE_DELETE(g_pSkyBoxEffect);
TB_SAFE_DELETE(g_pSkyBoxVB);
TB_SAFE_DELETE(g_pSkyBoxIB);
TB_SAFE_DELETE(g_pFont1);
delete g_Cube;
// Die TriBase-Engine herunterfahren
tbExit();
return TB_OK;
}
// ******************************************************************
// Windows-Hauptfunktion
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
char* pcCommandLine,
int iShowCommand)
{
// TriBase-Engine initialisieren
tbInit();
tbResult r;
// Konfiguration abfragen
if (tbFileExists("Config.txt"))
{
if (r = loadConfig(&g_Config, "Config.txt"))
{
// Fehler!
MessageBox(NULL, "Fehler beim Laden der Konfiguration!", "Fehler",
MB_OK | MB_ICONEXCLAMATION);
return 1;
}
}
else
{
if(r = tbDoConfigDialog(&g_Config)) // Hier wird der Dialog gestartet.
{
if(r == TB_CANCELED) return 0;
else
{
// Fehler!
MessageBox(NULL, "Fehler im Konfigurationsdialog!", "Fehler",
MB_OK | MB_ICONEXCLAMATION);
return 1;
}
}
if (r = saveConfig(&g_Config, "Config.txt")) // ausgewählte Config wird automatisch gespeichert.
{
// Fehler!
MessageBox(NULL, "Fehler beim Speichern der Konfiguration!", "Fehler",
MB_OK | MB_ICONEXCLAMATION);
return 1;
}
}
// Direct3D initialisieren
if(tbDirect3D::Instance().Init(&g_Config, // Hier wird die Konfiguration weitergegeben.
"Game. Work in progress.",
NULL,
LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1))))
{
// Fehler!
MessageBox(NULL, "Fehler bei der Direct3D-Initialisierung!", "Fehler",
MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Den Texturmanager initialisieren
if(tbDirectInput::Instance().Init())
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Initialisieren von tbDirectInput!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Den Texturmanager initialisieren
if(tbTextureManager::Instance().Init())
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Initialisieren des Texturmanagers!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Die Sky-Box initialisieren
if(InitSkyBox())
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Initialisieren der Sky-Box!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Die Schriftarten initialisieren
if (InitFonts())
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Initialisieren der Schriftarten!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
g_Camera = Camera(tbVector3(0.0f,30.0f,0.0f));
// Das Terrain initialisieren
if (InitTerrain())
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Initialisieren des Terrains!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Das Stadtmodell laden
g_pDuckModel = new tbModel;
if(g_pDuckModel->Init("_GRAPHICS/Ente.tbm"))
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Laden der Entenmodelldatei!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
/// GameObjects initialisieren
g_Cube = createCube(tbVector3(10.0f, 24.0f,0.0f));
if(tbDoMessageLoop(MoveProc, RenderProc))
{
MessageBox(tbDirect3D::Instance().GetWindow(), "Fehler beim Zeichnen!",
"Fehler", MB_OK | MB_ICONEXCLAMATION);
CleanUp();
return 1;
}
// Aufräumen
CleanUp();
return 0;
}
#ifndef CAMERA_H
#define CAMERA_H
#include "TriBase.h"
#include "d3dUtil.h"
enum CameraMode {FREE_FLYING, FOLLOW_PLAYER, FOLLOW_PLAYER_LEFT, FOLLOW_PLAYER_RIGHT, BIRDS_EYE_PLAYER};
class Camera
{
private:
tbVector3 m_vCameraPos; // Kameraposition
float m_fCameraRot; // Rotation der Kamera
float m_fCameraUpDown; // Schaut die Kamera hoch oder runter?
tbVector3 m_vCameraDir; // Wohin schaut die Kamera?
int m_iCameraMode; // Modus der Kamera
tbMatrix mProjection; // Für die Projektions-Matrix
tbMatrix mView; // Für die View-Matrix
// Frustum Planes
D3DXPLANE mFrustumPlanes[6]; // [0] = near
// [1] = far
// [2] = left
// [3] = right
// [4] = top
// [5] = bottom
public:
Camera()
{
m_vCameraPos = (0.0f, 30.0f, 0.0f);
m_iCameraMode = FOLLOW_PLAYER;
m_fCameraUpDown = 0.0f;
m_fCameraRot = 0.0f;
m_vCameraDir = tbVector3(sinf(m_fCameraRot) * cosf(m_fCameraUpDown),
sinf(m_fCameraUpDown),
cosf(m_fCameraRot) * cosf(m_fCameraUpDown));
}
Camera(tbVector3 pos)
{
m_vCameraPos = pos;
m_iCameraMode = FOLLOW_PLAYER;
m_fCameraUpDown = 0.0f;
m_fCameraRot = 0.0f;
m_vCameraDir = tbVector3(sinf(m_fCameraRot) * cosf(m_fCameraUpDown),
sinf(m_fCameraUpDown),
cosf(m_fCameraRot) * cosf(m_fCameraUpDown));
}
tbVector3 getPos(){ return m_vCameraPos; }
tbVector3 getTarget() { return m_vCameraDir; }
int getMode(){ return m_iCameraMode; }
tbMatrix getProjMatrix() { return mProjection; }
tbMatrix getViewMatrix() { return mView; }
void moveCamera(tbVector3 vTarget, float fTime);
void setUpCamera();
void buildWorldFrustumPlanes();
// Box coordinates should be relative to world space.
bool isVisible(const AABB& box) const;
};
#endif CAMERA_H#include "Camera.h"
void Camera::moveCamera(tbVector3 vTarget, float fTime)
{
switch (m_iCameraMode)
{
case FREE_FLYING:
{
// Tastatursteuerung...
m_vCameraDir = tbVector3(sinf(m_fCameraRot) * cosf(m_fCameraUpDown),
sinf(m_fCameraUpDown),
cosf(m_fCameraRot) * cosf(m_fCameraUpDown));
if(GetAsyncKeyState(VK_A)) m_fCameraRot -= 1.0f * fTime;
if(GetAsyncKeyState(VK_D)) m_fCameraRot += 1.0f * fTime;
if(GetAsyncKeyState(VK_UP)) m_fCameraUpDown -= 1.0f * fTime;
if(GetAsyncKeyState(VK_DOWN)) m_fCameraUpDown += 1.0f * fTime;
if(GetAsyncKeyState(VK_W)) m_vCameraPos += m_vCameraDir * 25.0f * fTime;
if(GetAsyncKeyState(VK_S)) m_vCameraPos -= m_vCameraDir * 25.0f * fTime;
break;
}
case BIRDS_EYE_PLAYER:
{
m_vCameraPos = vTarget + tbVector3(0.0f, 40.0f, 0.0f);
m_fCameraRot = 0.0f;
m_fCameraUpDown = -1.55f; // ~90° da PI / 2
break;
}
case FOLLOW_PLAYER:
{
m_vCameraPos = vTarget + tbVector3(0.0f, 20.0f, -20.0f);
m_fCameraRot = 0.0f;
m_fCameraUpDown = -0.78f; // ~45° da PI / 4
break;
}
case FOLLOW_PLAYER_LEFT:
{
m_vCameraPos = vTarget + tbVector3(20.0f, 20.0f, 0.0f);
m_fCameraRot = -1.55f;
m_fCameraUpDown = -0.78f; // ~45° da PI / 4
break;
}
case FOLLOW_PLAYER_RIGHT:
{
m_vCameraPos = vTarget + tbVector3(-20.0f, 20.0f, 0.0f);
m_fCameraRot = 1.55f;
m_fCameraUpDown = -0.78f; // ~45° da PI / 4
break;
}
}
// Kamera-Modus via NumPad
if(GetAsyncKeyState(VK_NUMPAD2)) m_iCameraMode = FOLLOW_PLAYER;
if(GetAsyncKeyState(VK_NUMPAD4)) m_iCameraMode = FOLLOW_PLAYER_LEFT;
if(GetAsyncKeyState(VK_NUMPAD6)) m_iCameraMode = FOLLOW_PLAYER_RIGHT;
if(GetAsyncKeyState(VK_NUMPAD5)) m_iCameraMode = FREE_FLYING;
if(GetAsyncKeyState(VK_NUMPAD8)) m_iCameraMode = BIRDS_EYE_PLAYER;
}
void Camera::setUpCamera()
{
tbDirect3D& D3D = tbDirect3D::Instance();
// Projektionsmatrix erstellen und einsetzen
mProjection = tbMatrixProjection(TB_DEG_TO_RAD(90.0f), D3D.GetAspect(), 0.1f, 100.0f);
D3D.SetTransform(D3DTS_PROJECTION, mProjection);
//switch (m_iCameraMode)
//{
// case FREE_FLYING:
// {
// Kameramatrix erstellen und einsetzen
m_vCameraDir = tbVector3(sinf(m_fCameraRot) * cosf(m_fCameraUpDown),
sinf(m_fCameraUpDown),
cosf(m_fCameraRot) * cosf(m_fCameraUpDown));
// break;
// }
//}
mView = tbMatrixCamera(m_vCameraPos, m_vCameraPos + m_vCameraDir);
buildWorldFrustumPlanes();
D3D.SetTransform(D3DTS_VIEW, mView);
}
void Camera::buildWorldFrustumPlanes()
{
// Note: Extract the frustum planes in world space.
tbMatrix VP = mView * mProjection;
D3DXVECTOR4 col0(VP(0,0), VP(1,0), VP(2,0), VP(3,0));
D3DXVECTOR4 col1(VP(0,1), VP(1,1), VP(2,1), VP(3,1));
D3DXVECTOR4 col2(VP(0,2), VP(1,2), VP(2,2), VP(3,2));
D3DXVECTOR4 col3(VP(0,3), VP(1,3), VP(2,3), VP(3,3));
// Planes face inward.
mFrustumPlanes[0] = (D3DXPLANE)(col2); // near
mFrustumPlanes[1] = (D3DXPLANE)(col3 - col2); // far
mFrustumPlanes[2] = (D3DXPLANE)(col3 + col0); // left
mFrustumPlanes[3] = (D3DXPLANE)(col3 - col0); // right
mFrustumPlanes[4] = (D3DXPLANE)(col3 - col1); // top
mFrustumPlanes[5] = (D3DXPLANE)(col3 + col1); // bottom
for(int i = 0; i < 6; i++)
D3DXPlaneNormalize(&mFrustumPlanes[i], &mFrustumPlanes[i]);
}
bool Camera::isVisible(const AABB& box)const
{
// Test assumes frustum planes face inward.
D3DXVECTOR3 P;
D3DXVECTOR3 Q;
// N *Q *P
// | / /
// |/ /
// -----/----- Plane -----/----- Plane
// / / |
// / / |
// *P *Q N
//
// PQ forms diagonal most closely aligned with plane normal.
// For each frustum plane, find the box diagonal (there are four main
// diagonals that intersect the box center point) that points in the
// same direction as the normal along each axis (i.e., the diagonal
// that is most aligned with the plane normal). Then test if the box
// is in front of the plane or not.
for(int i = 0; i < 6; ++i)
{
// For each coordinate axis x, y, z...
for(int j = 0; j < 3; ++j)
{
// Make PQ point in the same direction as the plane normal on this axis.
if( mFrustumPlanes[i][j] >= 0.0f )
{
P[j] = box.minPt[j];
Q[j] = box.maxPt[j];
}
else
{
P[j] = box.maxPt[j];
Q[j] = box.minPt[j];
}
}
// If box is in negative half space, it is behind the plane, and thus, completely
// outside the frustum. Note that because PQ points roughly in the direction of the
// plane normal, we can deduce that if Q is outside then P is also outside--thus we
// only need to test Q.
if( D3DXPlaneDotCoord(&mFrustumPlanes[i], &Q) < 0.0f ) // outside
return false;
}
return true;
}
Code is originally (partly) from Frank D. Luna and David Scherfing.