terrain.cpp
来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,186 行 · 第 1/3 页
CPP
1,186 行
float m[16];
std::fill(m,m+16,0.0f);
m[0]=m[6]=m[9]=m[15]=1.0f;
glLoadMatrixf (m);
glDepthMask (GL_FALSE);
glDisable (GL_DEPTH_TEST);
glDisable (GL_CULL_FACE);
glDisable(GL_LIGHTING);
// Make sure every node that might be drawn, has a cached texture
for (int ctx=0;ctx<contexts.size();ctx++)
{
RenderContext *rc = contexts[ctx];
if (!rc->needsTexturing)
continue;
for (int a=0;a<rc->quads.size();a++)
{
TQuad *q = rc->quads[a].quad;
if (q->cacheTexture)
continue;
RenderNode (q);
// copy it to the texture
glGenTextures(1, &q->cacheTexture);
glBindTexture (GL_TEXTURE_2D, q->cacheTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
if (config.anisotropicFiltering > 0.0f)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, config.anisotropicFiltering);
glCopyTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, 0,0, config.cacheTextureSize, config.cacheTextureSize, 0);
}
}
glMatrixMode (GL_MODELVIEW);
glPopMatrix ();
// restore viewport and depth buffer states
glPopAttrib ();
}
void Terrain::DebugEvent (const std::string& event)
{
if (event == "t_toggle_fill") {
fill=!fill;
return;
}
if (event == "t_detail_inc")
config.detailMod *= 1.2f;
if (event == "t_detail_dec")
config.detailMod /= 1.2f;
if (event == "t_debugquad") {
if (debugQuad)
debugQuad=0;
else
debugQuad=FindQuad(quadtree, activeRC->cam->pos);
return;
}
// if (key == 'l')
// logUpdates=!logUpdates;
texturing->DebugEvent (event);
}
void Terrain::Load(TdfParser& tdf, LightingInfo *li, ILoadCallback *cb)
{
string basepath = "MAP\\TERRAIN\\";
// validate configuration
if (config.cacheTextures)
config.useBumpMaps = false;
if (config.anisotropicFiltering != 0) {
if (!GLEW_EXT_texture_filter_anisotropic)
config.anisotropicFiltering = 0.0f;
else {
float maxAnisotropy=0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&maxAnisotropy);
if (config.anisotropicFiltering > maxAnisotropy)
config.anisotropicFiltering = maxAnisotropy;
}
}
// load heightmap
string heightmapName;
tdf.GetDef(heightmapName, string(), basepath + "Heightmap");
if (heightmapName.empty ())
throw content_error("No heightmap given");
if (cb) cb->PrintMsg ("loading heightmap (%s)...", heightmapName.c_str());
string extension = heightmapName.substr (heightmapName.length ()-3,heightmapName.length ());
if (extension == "raw") heightmap = LoadHeightmapFromRAW (heightmapName, cb);
else heightmap = LoadHeightmapFromImage (heightmapName,cb);
if (!heightmap)
throw content_error("Failed to load heightmap " + heightmapName);
d_trace("heightmap size: %dx%d\n", heightmap->w, heightmap->h);
if (heightmap->w-1 != atoi(tdf.SGetValueDef("","MAP\\GameAreaW").c_str()) ||
heightmap->h-1 != atoi(tdf.SGetValueDef("","MAP\\GameAreaH").c_str()))
{
char hmdims[32];
SNPRINTF(hmdims,32,"%dx%d", heightmap->w, heightmap->h);
throw content_error("Map size (" + string(hmdims) + ") should be equal to GameAreaW and GameAreaH");
}
float hmScale = atof (tdf.SGetValueDef ("1000", basepath + "HeightScale").c_str());
float hmOffset = atof (tdf.SGetValueDef ("0", basepath + "HeightOffset").c_str());
// apply scaling and offset to the heightmap
for (int y=0;y<heightmap->w*heightmap->h;y++)
heightmap->data[y]=heightmap->data[y]*hmScale + hmOffset;
if (cb) cb->PrintMsg ("initializing heightmap renderer...");
// create a set of low resolution heightmaps
int w=heightmap->w-1;
Heightmap *prev = heightmap;
quadTreeDepth = 0;
while (w>QUAD_W) {
prev = prev->CreateLowDetailHM();
quadTreeDepth++;
w/=2;
}
lowdetailhm = prev;
assert((1<<quadTreeDepth)*(lowdetailhm->w-1) == heightmap->w-1);
// set heightmap squareSize for all lod's
heightmap->squareSize = SquareSize;
for (Heightmap *lod = heightmap->lowDetail; lod; lod = lod->lowDetail)
lod->squareSize = lod->highDetail->squareSize * 2.0f;
// build a quadtree and a vertex buffer for each quad tree
// prev is now the lowest detail heightmap, and will be used for the root quad tree node
quadtree = SAFE_NEW TQuad;
quadtree->Build (lowdetailhm, int2(), int2(), int2(), heightmap->w-1, 0);
// create a quad map for each LOD level
Heightmap *cur = prev;
QuadMap *qm = 0;
while (cur) {
if (qm) {
qm->highDetail = SAFE_NEW QuadMap;
qm->highDetail->lowDetail = qm;
qm = qm->highDetail;
} else {
qm = SAFE_NEW QuadMap;
}
qm->Alloc ((cur->w-1)/QUAD_W);
qmaps.push_back(qm);
cur = cur->highDetail;
}
// fill the quad maps (qmaps.front() is now the lowest detail quadmap)
qmaps.front()->Fill (quadtree);
// generate heightmap normals
if (cb) cb->PrintMsg (" generating terrain normals for shading...");
for (Heightmap *lod = heightmap; lod; lod = lod->lowDetail)
lod->GenerateNormals ();
if (cb) cb->PrintMsg ("initializing texturing system...");
// load textures
texturing = SAFE_NEW TerrainTexture;
texturing->Load (&tdf, heightmap, quadtree, qmaps, &config, cb, li);
renderDataManager = SAFE_NEW RenderDataManager (lowdetailhm, qmaps.front());
// calculate index table
indexTable=SAFE_NEW IndexTable;
d_trace ("Index buffer data size: %d\n", VertexBuffer::TotalSize ());
}
void FindRAWProps(int len, int& width, int& bytespp, ILoadCallback* cb)
{
// test for 16-bit
int w = 3;
while (w*w*2 < len)
w = (w-1)*2+1;
if (w*w*2 != len) {
w = 3; // test for 8-bit
while (w*w < len)
w = (w-1)*2+1;
if (w*w != len) {
cb->PrintMsg ("Incorrect raw file size: %d, last comparing size: %d", len, w*w*2);
return;
} else bytespp=1;
} else bytespp=2;
width = w;
}
Heightmap* Terrain::LoadHeightmapFromRAW (const std::string& file, ILoadCallback *cb)
{
CFileHandler fh (file);
if (!fh.FileExists ()) {
cb->PrintMsg ("Failed to load %s", file.c_str());
return 0;
}
int len = fh.FileSize();
int w=-1, bits;
FindRAWProps(len, w, bits, cb);
bits*=8;
if (w<0) return 0;
Heightmap *hm = SAFE_NEW Heightmap;
hm->Alloc (w,w);
if (bits==16) {
ushort *tmp=SAFE_NEW ushort[w*w];
fh.Read (tmp, len);
for (int y=0;y<w*w;y++) hm->data[y]=float(tmp[y]) / float((1<<16)-1);
#ifdef SWAP_SHORT
char *p = (char*)hm->data;
for (int x=0;x<len;x+=2, p+=2)
std::swap (p[0],p[1]);
#endif
} else {
uchar *buf=SAFE_NEW uchar[len], *p=buf;
fh.Read(buf,len);
for (w=w*w-1;w>=0;w--)
hm->data[w]=*(p++)/255.0f;
delete[] buf;
}
return hm;
}
Heightmap* Terrain::LoadHeightmapFromImage(const std::string& heightmapFile, ILoadCallback *cb)
{
const char *hmfile = heightmapFile.c_str();
CFileHandler fh(heightmapFile);
if (!fh.FileExists())
throw content_error(heightmapFile + " does not exist");
ILuint ilheightmap;
ilGenImages (1, &ilheightmap);
ilBindImage (ilheightmap);
int len=fh.FileSize();
assert(len >= 0);
char *buffer=SAFE_NEW char[len];
fh.Read(buffer, len);
bool success = !!ilLoadL(IL_TYPE_UNKNOWN, buffer, len);
delete [] buffer;
if (!success) {
ilDeleteImages (1,&ilheightmap);
cb->PrintMsg ("Failed to load %s", hmfile);
return false;
}
int hmWidth = ilGetInteger (IL_IMAGE_WIDTH);
int hmHeight = ilGetInteger (IL_IMAGE_HEIGHT);
// does it have the correct size? 129,257,513
int testw = 1;
while (testw < hmWidth) {
if (testw + 1 == hmWidth)
break;
testw <<= 1;
}
if (testw>hmWidth || hmWidth!=hmHeight) {
cb->PrintMsg ("Heightmap %s has wrong dimensions (should be 129x129,257x257...)", hmfile);
ilDeleteImages (1,&ilheightmap);
return false;
}
// convert
if (!ilConvertImage (IL_LUMINANCE, IL_UNSIGNED_SHORT)) {
cb->PrintMsg ("Failed to convert heightmap image (%s) to grayscale image.", hmfile);
ilDeleteImages (1,&ilheightmap);
return false;
}
// copy the data into the highest detail heightmap
Heightmap *hm = SAFE_NEW Heightmap;
hm->Alloc (hmWidth,hmHeight);
ushort* data=(ushort*)ilGetData ();
for (int y=0;y<hmWidth*hmHeight;y++)
hm->data[y]=data[y]/float((1<<16)-1);
// heightmap is copied, so the original image can be deleted
ilDeleteImages (1,&ilheightmap);
return hm;
}
Vector3 Terrain::TerrainSize ()
{
return Vector3 (heightmap->w, 0.0f, heightmap->h) * SquareSize;
}
int Terrain::GetHeightmapWidth ()
{
return heightmap->w;
}
void Terrain::SetShaderParams (Vector3 dir, Vector3 eyevec)
{
texturing->SetShaderParams (dir, eyevec);
}
// heightmap blitting
void Terrain::HeightmapUpdated (int sx,int sy,int w,int h)
{
// clip
if (sx < 0) sx = 0;
if (sy < 0) sy = 0;
if (sx + w > heightmap->w) w = heightmap->w - sx;
if (sy + h > heightmap->h) h = heightmap->h - sy;
// mark for vertex buffer update
renderDataManager->UpdateRect(sx,sy,w,h);
// update heightmap mipmap chain
heightmap->UpdateLower(sx,sy,w,h);
}
void Terrain::GetHeightmap (int sx,int sy,int w,int h, float *dest)
{
// clip
if (sx < 0) sx = 0;
if (sy < 0) sy = 0;
if (sx + w > heightmap->w) w = heightmap->w - sx;
if (sy + h > heightmap->h) h = heightmap->h - sy;
for (int y=sy;y<sy+h;y++)
for (int x=sx;x<sx+w;x++)
*(dest++) = heightmap->HeightAt (x,y);
}
float* Terrain::GetHeightmap()
{
return heightmap->data;
}
//TODO: Interpolate
float Terrain::GetHeight (float x, float z)
{
const float s = 1.0f / SquareSize;
int px = (int)(x*s);
if (px < 0) px=0;
if (px >= heightmap->w) px=heightmap->w-1;
int pz = (int)(z*s);
if (pz < 0) pz=0;
if (pz >= heightmap->h) pz=heightmap->h-1;
return heightmap->HeightAt (px,pz);
}
void Terrain::SetShadowParams(ShadowMapParams *smp)
{
texturing->SetShadowMapParams(smp);
}
RenderContext* Terrain::AddRenderContext (Camera* cam, bool needsTexturing)
{
RenderContext *rc = SAFE_NEW RenderContext;
rc->cam = cam;
rc->needsTexturing = needsTexturing;
contexts.push_back (rc);
return rc;
}
void Terrain::RemoveRenderContext (RenderContext *rc)
{
contexts.erase (find(contexts.begin(),contexts.end(),rc));
delete rc;
}
void Terrain::SetActiveContext (RenderContext *rc)
{
activeRC = rc;
}
};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?