terrain.cpp
来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,186 行 · 第 1/3 页
CPP
1,186 行
/*
---------------------------------------------------------------------
Terrain Renderer using texture splatting and geomipmapping
Copyright (c) 2006 Jelmer Cnossen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Jelmer Cnossen
j.cnossen at gmail dot com
---------------------------------------------------------------------
*/
#include "StdAfx.h"
#define GLEW_STATIC
#include <GL/glew.h>
#include "TerrainBase.h"
#include "Terrain.h"
#include "TerrainVertexBuffer.h"
#include <cstdarg>
#include "TdfParser.h"
#include <deque>
#include <fstream>
#include "TerrainTexture.h"
#include "TerrainNode.h"
#include "FileSystem/FileHandler.h"
// define this for big endian machines
//#define SWAP_SHORT
namespace terrain {
using namespace std;
// debug stuff
bool fill=true;
Config::Config()
{
cacheTextures=false;
useBumpMaps=false;
terrainNormalMaps=false;
detailMod=2.0f;
normalMapLevel=2;
cacheTextureSize = 128;
useShadowMaps = false;
anisotropicFiltering = 0;
forceFallbackTexturing = false;
maxLodLevel = 4;
}
void ILoadCallback::PrintMsg (const char *fmt, ...)
{
char buf[256];
va_list l;
va_start (l, fmt);
VSNPRINTF(buf, sizeof(buf), fmt, l);
va_end (l);
Write (buf);
}
//-----------------------------------------------------------------------
// Quad map - stores a 2D map of the quad nodes,
// for quick access of nabours
//-----------------------------------------------------------------------
void QuadMap::Alloc (int W)
{
w=W;
map = SAFE_NEW TQuad*[w*w];
memset (map, 0, sizeof(TQuad*) * w*w);
}
void QuadMap::Fill (TQuad *q)
{
At (q->qmPos.x, q->qmPos.y) = q;
if (!q->isLeaf ()) {
assert (highDetail);
for (int a=0;a<4;a++)
highDetail->Fill (q->childs[a]);
}
}
//-----------------------------------------------------------------------
// Terrain Quadtree Node
//-----------------------------------------------------------------------
TQuad::TQuad ()
{
parent=0;
for (int a=0;a<4;a++)
childs[a]=0;
depth=width=0;
drawState=NoDraw;
renderData=0;
textureSetup=0;
cacheTexture=0;
normalData=0;
}
TQuad::~TQuad ()
{
if (!isLeaf()) {
for (int a=0;a<4;a++)
delete childs[a];
}
// delete cached texture
if (cacheTexture) {
glDeleteTextures(1,&cacheTexture);
cacheTexture = 0;
}
}
void TQuad::Build (Heightmap *hm, int2 sqStart, int2 hmStart, int2 quadPos, int w, int d)
{
// create child nodes if necessary
if (hm->highDetail) {
for (int a=0;a<4;a++) {
childs[a] = SAFE_NEW TQuad;
childs[a]->parent = this;
int2 sqPos (sqStart.x + (a&1)*w/2, sqStart.y + (a&2)*w/4); // square pos
int2 hmPos (hmStart.x*2 + (a&1)*QUAD_W, hmStart.y*2 + (a&2)*QUAD_W/2); // heightmap pos
int2 cqPos (quadPos.x*2 + (a&1), quadPos.y*2 + (a&2)/2);// child quad pos
childs[a]->Build (hm->highDetail, sqPos, hmPos, cqPos, w/2, d + 1);
}
}
float minH, maxH;
hm->FindMinMax (hmStart, int2(QUAD_W,QUAD_W), minH, maxH);
start = Vector3 (sqStart.x, 0.0f, sqStart.y) * SquareSize;
end = Vector3 (sqStart.x+w, 0.0f, sqStart.y+w) * SquareSize;
start.y = minH;
end.y = maxH;
hmPos = hmStart;
qmPos = quadPos;
sqPos = sqStart;
depth = d;
width = w;
}
int TQuad::GetVertexSize ()
{
// compile-time assert
typedef int vector_should_be_12_bytes [(sizeof(Vector3) == 12) ? 1 : -1];
int vertexSize = 12;
uint vda = textureSetup->vertexDataReq;
if (vda & VRT_Normal)
vertexSize += 12;
if (vda & VRT_TangentSpaceMatrix)
vertexSize += 12 * 3;
return vertexSize;
}
void TQuad::Draw (IndexTable *indexTable, bool onlyPositions, int lodState)
{
uint vda = textureSetup->vertexDataReq;
int vertexSize = GetVertexSize();
// Bind the vertex buffer components
Vector3 *vbuf = (Vector3 *)renderData->vertexBuffer.Bind();
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_FLOAT, vertexSize, vbuf ++);
if (vda & VRT_Normal)
{
if (!onlyPositions) {
glEnableClientState (GL_NORMAL_ARRAY);
glNormalPointer (GL_FLOAT, vertexSize, vbuf);
}
vbuf ++;
}
if (vda & VRT_TangentSpaceMatrix)
{
if (!onlyPositions) {
assert (textureSetup->currentShaderSetup);
textureSetup->currentShaderSetup->BindTSM (vbuf, vertexSize);
}
vbuf += 3;
}
// Bind the index buffer and render
IndexBuffer& ibuf = indexTable->buffers [lodState];
glDrawElements (GL_TRIANGLES, indexTable->size [lodState], indexTable->IndexType(), ibuf.Bind());
ibuf.Unbind ();
// Unbind the vertex buffer
if (!onlyPositions) {
if (vda&VRT_Normal) glDisableClientState (GL_NORMAL_ARRAY);
if (vda&VRT_TangentSpaceMatrix) textureSetup->currentShaderSetup->UnbindTSM();
}
glDisableClientState (GL_VERTEX_ARRAY);
renderData->vertexBuffer.Unbind ();
}
bool TQuad::InFrustum (Frustum *f)
{
Vector3 boxSt=start, boxEnd=end;
return f->IsBoxVisible (boxSt, boxEnd) != Frustum::Outside;
}
// Calculates the exact nearest point, not just one of the box'es vertices
void NearestBoxPoint(const Vector3 *min, const Vector3 *max, const Vector3 *pos, Vector3 *out)
{
Vector3 mid = (*max + *min) * 0.5f;
if(pos->x < min->x) out->x = min->x;
else if(pos->x > max->x) out->x = max->x;
else out->x = pos->x;
if(pos->y < min->y) out->y = min->y;
else if(pos->y > max->y) out->y = max->y;
else out->y = pos->y;
if(pos->z < min->z) out->z = min->z;
else if(pos->z > max->z) out->z = max->z;
else out->z = pos->z;
}
float TQuad::CalcLod (const Vector3& campos)
{
Vector3 nearest;
NearestBoxPoint (&start, &end, &campos, &nearest);
float nodesize = end.x - start.x;
// float sloped = 0.01f * (1.0f + end.y - start.y);
nearest -= campos;
//return sqrtf(sloped) * nodesize / (nearest.length () + 0.1f);
return nodesize / (nearest.Length () + 0.1f);
}
void TQuad::CollectNodes(std::vector<TQuad*>& quads)
{
if (!isLeaf()) {
for (int a=0;a<4;a++)
childs[a]->CollectNodes(quads);
}
quads.push_back(this);
}
void TQuad::FreeCachedTexture()
{
if (cacheTexture) {
glDeleteTextures(1,&cacheTexture);
cacheTexture = 0;
}
}
TQuad *TQuad::FindSmallestContainingQuad2D(const Vector3& pos, float range, int maxdepth)
{
if (depth < maxdepth)
{
for (int a=0;a<4;a++) {
TQuad *r = childs[a]->FindSmallestContainingQuad2D(pos,range,maxdepth);
if (r) return r;
}
}
if (start.x <= pos.x - range && end.x >= pos.x + range &&
start.z <= pos.z - range && end.z >= pos.z + range)
return this;
return 0;
}
//-----------------------------------------------------------------------
// Terrain Main class
//-----------------------------------------------------------------------
Terrain::Terrain()
{
heightmap = 0;
quadtree = 0;
debugQuad = 0;
indexTable = 0;
texturing = 0;
lowdetailhm = 0;
shadowMap = 0;
renderDataManager = 0;
logUpdates=false;
nodeUpdateCount=0;
curRC = 0;
activeRC = 0;
quadTreeDepth = 0;
}
Terrain::~Terrain()
{
delete renderDataManager;
delete texturing;
while (heightmap) {
Heightmap *p = heightmap;
heightmap = heightmap->lowDetail;
delete p;
}
for (int a=0;a<qmaps.size();a++)
delete qmaps[a];
qmaps.clear();
delete quadtree;
delete indexTable;
}
void Terrain::SetShadowMap (uint shadowTex)
{
shadowMap = shadowTex;
}
// used by ForceQueue to queue quads
void Terrain::QueueLodFixQuad (TQuad *q)
{
if (q->drawState == TQuad::Queued)
return;
if (q->drawState == TQuad::NoDraw) {
// make sure the parent is drawn
assert (q->parent);
QueueLodFixQuad (q->parent);
// change the queued quad to a parent quad
q->parent->drawState = TQuad::Parent;
for (int a=0;a<4;a++) {
TQuad *ch = q->parent->childs [a];
updatequads.push_back(ch);
ch->drawState = TQuad::Queued;
UpdateLodFix (ch);
}
}
}
void Terrain::ForceQueue (TQuad *q)
{
// See if the quad is culled against the view frustum
TQuad *p = q->parent;
while (p) {
if (p->drawState == TQuad::Culled)
return;
if (p->drawState == TQuad::Queued)
break;
p = p->parent;
}
// Quad is not culled, so make sure it is drawn
QueueLodFixQuad (q);
}
inline void Terrain::CheckNabourLod (TQuad *q, int xOfs, int yOfs)
{
QuadMap *qm = qmaps[q->depth];
TQuad *nb = qm->At (q->qmPos.x+xOfs, q->qmPos.y+yOfs);
// Check the state of the nabour parent (q is already the parent),
if (nb->drawState == TQuad::NoDraw) {
// a parent of the node is either culled or drawn itself
ForceQueue (nb);
return;
}
// Parent: a child node of the nabour is drawn, which will take care of LOD gaps fixing
// Queued: drawn itself, so the index buffer can fix gaps for that
// Culled: no gap fixing required
}
// Check nabour parent nodes to see if they need to be drawn to fix LOD gaps of this node.
void Terrain::UpdateLodFix (TQuad *q)
{
if (q->drawState==TQuad::Culled)
return;
if (q->drawState==TQuad::Parent) {
for(int a=0;a<4;a++)
UpdateLodFix(q->childs [a]);
}
else if (q->parent) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?