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 + -
显示快捷键?