terrain.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,186 行 · 第 1/3 页

CPP
1,186
字号
			// find the nabours, and make sure at least them or their parents are drawn
			TQuad *parent = q->parent;
			QuadMap *qm = qmaps[parent->depth];
			if (parent->qmPos.x>0) CheckNabourLod (parent, -1, 0);
			if (parent->qmPos.y>0) CheckNabourLod (parent, 0, -1);
			if (parent->qmPos.x<qm->w-1) CheckNabourLod (parent, 1, 0);
			if (parent->qmPos.y<qm->w-1) CheckNabourLod (parent, 0, 1);
		}
	}

	// Handle visibility with the frustum, and select LOD based on distance to camera and LOD setting
	void Terrain::QuadVisLod (TQuad *q)
	{
		if (q->InFrustum (&frustum)) {
			// Node is visible, now determine if this LOD is suitable, or that a higher LOD should be used.
			float lod = config.detailMod * q->CalcLod (curRC->cam->pos);
			if (q->depth < quadTreeDepth-config.maxLodLevel || (lod > 1.0f && !q->isLeaf())) {
				q->drawState=TQuad::Parent;
				for (int a=0;a<4;a++)
					QuadVisLod (q->childs[a]);
			} else q->drawState=TQuad::Queued;
			updatequads.push_back (q);

			// update max lod value
			if (q->maxLodValue < lod)
				q->maxLodValue = lod;
		} else {
			q->drawState=TQuad::Culled;
			culled.push_back (q);
		}
	}

	static inline bool QuadSortFunc (const QuadRenderInfo& q1, const QuadRenderInfo& q2)
	{
		return q1.quad->textureSetup->sortkey < q2.quad->textureSetup->sortkey;
	}


	// update quad node drawing list
	void Terrain::Update ()
	{
		nodeUpdateCount = 0;

		renderDataManager->ClearStat();

		// clear LOD values of previously used renderquads
		for (int a=0;a<contexts.size();a++)
		{
			RenderContext *rc = contexts[a];
			for (int n = 0; n < rc->quads.size();n++) {
				TQuad *q = rc->quads [n].quad;

				q->maxLodValue = 0.0f;
				if (q->renderData)
					q->renderData->used = true;
			}
		}

		for (int ctx=0;ctx<contexts.size();ctx++)
		{
			RenderContext *rc = curRC = contexts[ctx];

			// Update the frustum based on the camera, to cull away TQuad nodes
			//Camera *camera = rc->cam;
		//	frustum.CalcCameraPlanes (&camera->pos, &camera->right, &camera->up, &camera->front, camera->fov, camera->aspect);
			//assert (camera->pos.x == 0.0f || frustum.IsPointVisible (camera->pos + camera->front * 20)==Frustum::Inside);

			// determine the new set of quads to draw
			QuadVisLod (quadtree);

			// go through nabours to make sure there is a maximum of one LOD difference between nabours
			UpdateLodFix (quadtree);

			if (debugQuad)
				ForceQueue (debugQuad);

			// update lod state and copy the rendering list to the render context...
			rc->quads.clear();
			for (int a=0;a<updatequads.size();a++)
			{
				if (updatequads[a]->drawState == TQuad::Queued)
				{
					TQuad *q = updatequads[a];
					// calculate lod state
					QuadMap *qm = qmaps[q->depth];
					int ls=0;
					if (q->qmPos.x>0 && qm->At (q->qmPos.x-1,q->qmPos.y)->drawState == TQuad::NoDraw) ls |= left_bit;
					if (q->qmPos.y>0 && qm->At (q->qmPos.x,q->qmPos.y-1)->drawState == TQuad::NoDraw) ls |= up_bit;
					if (q->qmPos.x<qm->w-1 && qm->At (q->qmPos.x+1,q->qmPos.y)->drawState == TQuad::NoDraw) ls |= right_bit;
					if (q->qmPos.y<qm->w-1 && qm->At (q->qmPos.x,q->qmPos.y+1)->drawState == TQuad::NoDraw) ls |= down_bit;

					rc->quads.push_back (QuadRenderInfo());
					rc->quads.back().quad = q;
					rc->quads.back().lodState = ls;
					if (q->renderData) q->renderData->used = true;
				}
			}
			// sort rendering quads based on sorting key
			sort (rc->quads.begin(), rc->quads.end(), QuadSortFunc);

			// the active set of quads is determined, so their draw-states can be reset for the next context
			for (int a=0;a<culled.size();a++)
				culled[a]->drawState = TQuad::NoDraw;
			culled.clear();

			// clear the list of queued quads
			for (int a=0;a<updatequads.size();a++)
				updatequads[a]->drawState = TQuad::NoDraw;
			updatequads.clear ();
		}

		renderDataManager->FreeUnused ();

		for (int ctx=0;ctx<contexts.size();ctx++)
		{
			RenderContext *rc = curRC = contexts[ctx];

			// allocate required vertex buffers
			for (int a=0;a<rc->quads.size();a++)
			{
				TQuad *q = rc->quads[a].quad;

				if (!q->renderData) {
					renderDataManager->InitializeNode (q);

					if (config.terrainNormalMaps && rc->needsNormalMap)
						renderDataManager->InitializeNodeNormalMap(q, config.normalMapLevel);

					nodeUpdateCount ++;
				}
			}
		}

		if (logUpdates) {
			if (nodeUpdateCount) d_trace ("NodeUpdates: %d, NormalDataAllocs:%d, RenderDataAllocs:%d\n",
				nodeUpdateCount, renderDataManager->normalDataAllocates, renderDataManager->renderDataAllocates);
		}
		curRC = 0;
	}

	void Terrain::DrawSimple ()
	{
		glEnable(GL_CULL_FACE);

		for (int a=0;a<activeRC->quads.size();a++)
		{
			QuadRenderInfo *q = &activeRC->quads[a];
			q->quad->Draw (indexTable, true, q->lodState);
		}
	}

	void Terrain::DrawOverlayTexture (uint tex)
	{
		glEnable (GL_TEXTURE_2D);
		glBindTexture (GL_TEXTURE_2D, tex);
		glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_MODULATE);
        SetTexGen (1.0f / (heightmap->w * SquareSize));

		DrawSimple ();

		glDisable (GL_TEXTURE_GEN_S);
		glDisable (GL_TEXTURE_GEN_T);
		glDisable (GL_TEXTURE_2D);
	}

	void DrawNormals (TQuad *q, Heightmap *hm)
	{
		glBegin (GL_LINES);
		for (int y=q->hmPos.y;y<q->hmPos.y+QUAD_W;y++)
			for (int x=q->hmPos.x;x<q->hmPos.x+QUAD_W;x++)
			{
				Vector3 origin (x * hm->squareSize, hm->HeightAt (x,y), y * hm->squareSize);

				Vector3 tangent, binormal;
				CalculateTangents(hm, x,y, tangent, binormal);

				Vector3 normal = binormal.cross(tangent);
				normal.Normalize();
				binormal.Normalize ();
				tangent.Normalize ();

				glColor3f (1,1,0);
				glVertex3fv(&origin[0]);
				glVertex3fv(&(origin + normal * 7)[0]);
				glColor3f (1,0,0);
				glVertex3fv(&origin[0]);
				glVertex3fv(&(origin + binormal * 7)[0]);
				glColor3f (0,0,1);
				glVertex3fv(&origin[0]);
				glVertex3fv(&(origin + tangent * 7)[0]);
			}
		glEnd();
		glColor3f (1,1,1);

	}

	void Terrain::Draw ()
	{
		if (!fill)
			glPolygonMode (GL_FRONT_AND_BACK,GL_LINE);
		else {
			glPolygonMode (GL_FRONT_AND_BACK,GL_FILL);

			const float diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
			glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuse);
		}
		glEnable(GL_CULL_FACE);
		glColor3f(1.0f,1.0f,1.0f);

		if (config.cacheTextures)
		{
			// draw all terrain nodes using their cached textures
			glEnable (GL_TEXTURE_GEN_S);
			glEnable (GL_TEXTURE_GEN_T);
			glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

			glEnable (GL_TEXTURE_2D);
			glDisable (GL_LIGHTING);

			for (int a=0;a<activeRC->quads.size();a++)
			{
				TQuad *q = activeRC->quads[a].quad;

				// set quad texture
				glBindTexture (GL_TEXTURE_2D, q->cacheTexture);

				// set texture gen
				float ht = (q->end.x-q->start.x) / ( 2 * config.cacheTextureSize );

				float v[4] = {0.0f, 0.0f, 0.0f, 0.0f};
				v[0] = 1.0f / (q->end.x - q->start.x + ht * 2);
				v[3] = -v[0] * (q->start.x - ht);
				glTexGenfv (GL_S, GL_OBJECT_PLANE, v);
				v[0] = 0.0f;
				v[2] = 1.0f / (q->end.z - q->start.z + ht * 2);
                v[3] = -v[2] * (q->start.z - ht);
				glTexGenfv (GL_T, GL_OBJECT_PLANE, v);

				// draw
				q->Draw (indexTable, false, activeRC->quads[a].lodState);
			}

			glDisable (GL_TEXTURE_2D);
			glDisable (GL_TEXTURE_GEN_S);
			glDisable (GL_TEXTURE_GEN_T);
		}
		else  // no texture caching, so use the texturing system directly
		{
			int numPasses=texturing->NumPasses();

			glEnable(GL_DEPTH_TEST);

			if (fill) texturing->BeginTexturing();

			if (!fill) numPasses=1;
			for (int pass=0;pass<numPasses;pass++)
			{
				bool skipNodes=false;

				if (fill)
					texturing->BeginPass(pass);

				for (int a=0;a<activeRC->quads.size();a++)
				{
					TQuad *q = activeRC->quads[a].quad;

					// Setup node texturing
					if (fill)
						skipNodes = !texturing->SetupNode (q, pass);

					assert (q->renderData);

					if (!skipNodes) {
						// Draw the node
						q->Draw (indexTable, false, activeRC->quads[a].lodState);
					}
				}

				if (fill) texturing->EndPass();
			}

			glDisable (GL_BLEND);
			glDepthMask (GL_TRUE);

			if (fill)
				texturing->EndTexturing ();
		}

		if (debugQuad)
			DrawNormals (debugQuad, lowdetailhm->GetLevel (debugQuad->depth));

		glPolygonMode (GL_FRONT_AND_BACK,GL_FILL);
	}

	void Terrain::CalcRenderStats (RenderStats& stats, RenderContext *ctx)
	{
		if (!ctx)
			ctx = activeRC;

		stats.cacheTextureSize = 0;
		stats.renderDataSize = 0;
		stats.tris = 0;

		for (int a=0;a<ctx->quads.size();a++) {
			TQuad *q = ctx->quads[a].quad;
			if (q->cacheTexture)
				stats.cacheTextureSize += config.cacheTextureSize * config.cacheTextureSize * 3;
			stats.renderDataSize += q->renderData->GetDataSize();
			stats.tris += MAX_INDICES/3;
		}
		stats.passes = texturing->NumPasses ();
	}

#ifndef TERRAINRENDERERLIB_EXPORTS
	void Terrain::DebugPrint (IFontRenderer *fr)
	{
		const float s=16.0f;
		if (debugQuad) {
			fr->printf (0, 30, s, "Selected quad: (%d,%d) on depth %d. Lod=%3.3f",
				debugQuad->qmPos.x, debugQuad->qmPos.y, debugQuad->depth, config.detailMod * debugQuad->CalcLod (activeRC->cam->pos));
		}
		RenderStats stats;
		CalcRenderStats(stats);
		fr->printf (0, 46, s, "Rendered nodes: %d, tris: %d, VBufSize: %d(kb), TotalRenderData(kb): %d, DetailMod: %g, CacheTextureMemory: %d",
			activeRC->quads.size(), stats.tris, VertexBuffer::TotalSize()/1024, stats.renderDataSize/1024, config.detailMod, stats.cacheTextureSize);
		fr->printf (0, 60, s, "NodeUpdateCount: %d, RenderDataAlloc: %d, #RenderData: %d",
			nodeUpdateCount, renderDataManager->normalDataAllocates, renderDataManager->QuadRenderDataCount ());
		texturing->DebugPrint(fr);
	}
#endif

	TQuad* FindQuad (TQuad *q, const Vector3& cpos)
	{
		if (cpos.x >= q->start.x && cpos.z >= q->start.z &&
			cpos.x < q->end.x && cpos.z < q->end.z)
		{
			if (q->isLeaf ()) return q;

			for (int a=0;a<4;a++)  {
				TQuad *r = FindQuad (q->childs[a], cpos);
				if (r) return r;
			}
		}
		return 0;
	}

	void Terrain::RenderNodeFlat (int x,int y,int depth)
	{
		RenderNode (qmaps[depth]->At (x,y));
	}

	void Terrain::RenderNode (TQuad *q)
	{
		// setup projection matrix, so the quad is exactly mapped onto the viewport
		glMatrixMode (GL_PROJECTION);
		glPushMatrix ();
		glLoadIdentity ();
		glOrtho (q->start.x, q->end.x, q->start.z, q->end.z, -10000.0f, 100000.0f);
		glColor3f(1.f,1.f,1.f);

		if (!q->renderData)
			renderDataManager->InitializeNode (q);

		texturing->BeginTexturing();
		// render to the framebuffer
		for (int p=0;p<q->textureSetup->renderSetup [0]->passes.size();p++)
		{
			texturing->BeginPass(p);
			if (texturing->SetupNode (q, p))
			{
				glBegin(GL_QUADS);
				glVertex3f (q->start.x, 0.0f, q->start.z);
				glVertex3f (q->end.x, 0.0f, q->start.z);
				glVertex3f (q->end.x, 0.0f, q->end.z);
				glVertex3f (q->start.x, 0.0f, q->end.z);
				glEnd();
			}
			texturing->EndPass();
		}
		texturing->EndTexturing();
		glMatrixMode (GL_PROJECTION);
		glPopMatrix ();
	}

	void Terrain::CacheTextures ()
	{
		if (!config.cacheTextures)
			return;

		glPushAttrib (GL_VIEWPORT_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport (0, 0, config.cacheTextureSize, config.cacheTextureSize);

		glMatrixMode (GL_MODELVIEW);
		glPushMatrix ();
		glLoadIdentity ();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?