www.pudn.com > roam.rar > Patch.cpp


// Patch.cpp: implementation of the Patch class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "ROAM.h" 
#include "Patch.h" 
#include "Landscape.h" 
#include "math.h" 
#include "ncgcolor.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
int gNumTrisRendered=0; 
int eyeX; 
int eyeY; 
 
GLfloat gClipAngle=0; 
int m_eyeX; 
int m_eyeY; 
 
 
////////////////////////////////////////////////////////////////////// 
// Landscape Class 
////////////////////////////////////////////////////////////////////// 
// --------------------------------------------------------------------- 
// Definition of the static member variables 
// 
int         Landscape::m_NextTriNode; 
TriTreeNode Landscape::m_TriPool[POOL_SIZE]; 
 
// Beginning frame varience (should be high, it will adjust automatically) 
float gFrameVariance = 1000; 
 
// Desired number of Binary Triangle tessellations per frame. 
// This is not the desired number of triangles rendered! 
// There are usually twice as many Binary Triangle structures as there are rendered triangles. 
int gDesiredTris = 150000; 
float m_zmin; 
float m_zmax; 
 
Landscape::Landscape() 
{ 
 
} 
 
Landscape::~Landscape() 
{ 
 
} 
 
void Landscape::Init(double *hMap) 
{ 
	Patch *patch; 
	int X, Y; 
 
	// Initialize all terrain patches 
	for ( Y=0; Y < NUM_PATCHES_PER_SIDE; Y++) 
		for ( X=0; X < NUM_PATCHES_PER_SIDE; X++ ) 
		{ 
			patch = &(m_Patches[Y][X]); 
			patch->Init( X*PATCH_SIZE, Y*PATCH_SIZE, X*PATCH_SIZE, Y*PATCH_SIZE, hMap ); 
			patch->ComputeVariance(); 
		} 
} 
 
// --------------------------------------------------------------------- 
// 重置所有地形块,重新计算误差判断准则 
// 
void Landscape::Reset(int viewPosX,int viewPosY) 
{ 
	// 根据视角范围,判断地形是否可视 
	const float PI_DIV_180 = M_PI / 180.0f; 
	const float FOV_DIV_2 = 90/2; 
 
	m_eyeX = (int)(viewPosX + PATCH_SIZE * sinf( gClipAngle * PI_DIV_180 ));//eyeX; 
	m_eyeY = (int)(viewPosY + PATCH_SIZE * cosf( gClipAngle * PI_DIV_180 ));//eyeY; 
 
	int leftX  = (int)(m_eyeX + 100.0f * sinf( (gClipAngle-FOV_DIV_2) * PI_DIV_180 )); 
	int leftY  = (int)(m_eyeY - 100.0f * cosf( (gClipAngle-FOV_DIV_2) * PI_DIV_180 )); 
 
	int rightX = (int)(m_eyeX + 100.0f * sinf( (gClipAngle+FOV_DIV_2) * PI_DIV_180 )); 
	int rightY = (int)(m_eyeY - 100.0f * cosf( (gClipAngle+FOV_DIV_2) * PI_DIV_180 )); 
 
	int X, Y; 
	Patch *patch; 
 
	// Set the next free triangle pointer back to the beginning 
	SetNextTriNode(0); 
 
	// Reset rendered triangle count. 
	gNumTrisRendered = 0; 
 
	// Go through the patches performing resets, compute variances, and linking. 
	for ( Y=0; Y < NUM_PATCHES_PER_SIDE; Y++ ) 
		for ( X=0; X < NUM_PATCHES_PER_SIDE; X++) 
		{ 
			patch = &(m_Patches[Y][X]); 
			 
			// Reset the patch 
			patch->Reset(); 
			patch->m_isVisible =1; 
			 
			// Check to see if this patch has been deformed since last frame. 
			// If so, recompute the varience tree for it. 
			if ( patch->isDirty() ) 
				patch->ComputeVariance(); 
 
			if ( patch->isVisibile() ) 
			{ 
				// Link all the patches together. 
				if ( X > 0 ) 
					patch->GetBaseLeft()->LeftNeighbor = m_Patches[Y][X-1].GetBaseRight(); 
				else 
					patch->GetBaseLeft()->LeftNeighbor = NULL;		// Link to bordering Landscape here.. 
 
				if ( X < (NUM_PATCHES_PER_SIDE-1) ) 
					patch->GetBaseRight()->LeftNeighbor = m_Patches[Y][X+1].GetBaseLeft(); 
				else 
					patch->GetBaseRight()->LeftNeighbor = NULL;		// Link to bordering Landscape here.. 
 
				if ( Y > 0 ) 
					patch->GetBaseLeft()->RightNeighbor = m_Patches[Y-1][X].GetBaseRight(); 
				else 
					patch->GetBaseLeft()->RightNeighbor = NULL;		// Link to bordering Landscape here.. 
 
				if ( Y < (NUM_PATCHES_PER_SIDE-1) ) 
					patch->GetBaseRight()->RightNeighbor = m_Patches[Y+1][X].GetBaseLeft(); 
				else 
					patch->GetBaseRight()->RightNeighbor = NULL;	// Link to bordering Landscape here.. 
			} 
		} 
 
} 
 
////////////////////////////////////////// 
///////       分配内存 /////////////////// 
////////////////////////////////////////// 
TriTreeNode * Landscape::AllocateTri() 
{ 
	TriTreeNode *pTri; 
 
	// IF we've run out of TriTreeNodes, just return NULL (this is handled gracefully) 
	if ( m_NextTriNode >= POOL_SIZE ) 
		return NULL; 
 
	pTri = &(m_TriPool[m_NextTriNode++]); 
	pTri->LeftChild = pTri->RightChild = NULL; 
 
	return pTri; 
} 
 
/////////////////////////////////////////// 
/////////   建立lod      ////////////////// 
/////////////////////////////////////////// 
void Landscape::Tessellate() 
{ 
	// Perform Tessellation 
	int nCount; 
	Patch *patch = &(m_Patches[0][0]); 
	for (nCount=0; nCount < NUM_PATCHES_PER_SIDE*NUM_PATCHES_PER_SIDE; nCount++, patch++ ) 
	{ 
		if (patch->isVisibile()) 
			patch->Tessellate( ); 
	} 
 
} 
 
 
/////////////////////////////////////////// 
/////////   绘制地形     ////////////////// 
/////////////////////////////////////////// 
void Landscape::Render() 
{ 
	int nCount; 
	Patch *patch = &(m_Patches[0][0]); 
 
	for (nCount=0; nCount < NUM_PATCHES_PER_SIDE*NUM_PATCHES_PER_SIDE; nCount++, patch++ ) 
	{ 
		if (patch->isVisibile()) 
			patch->Render(); 
	} 
 
	// Check to see if we got close to the desired number of triangles. 
	// Adjust the frame variance to a better value. 
//	if ( GetNextTriNode() != gDesiredTris ) 
//		gFrameVariance += ((float)GetNextTriNode() - (float)gDesiredTris) / (float)gDesiredTris; 
 
	// Bounds checking. 
//	if ( gFrameVariance < 0 ) 
//		gFrameVariance = 0; 
} 
 
 
 
 
////////////////////////////////////////////////////////////////////// 
// Patch Class 
////////////////////////////////////////////////////////////////////// 
 
Patch::Patch() 
{ 
 
} 
 
Patch::~Patch() 
{ 
 
} 
 
 
void Patch::Init(int heightX, int heightY, int worldX, int worldY, double *hMap) 
{ 
	// Clear all the relationships 
	m_BaseLeft.RightNeighbor = m_BaseLeft.LeftNeighbor = m_BaseRight.RightNeighbor = m_BaseRight.LeftNeighbor = 
	m_BaseLeft.LeftChild = m_BaseLeft.RightChild = m_BaseRight.LeftChild = m_BaseLeft.LeftChild = NULL; 
 
	// Attach the two m_Base triangles together 
	m_BaseLeft.BaseNeighbor = &m_BaseRight; 
	m_BaseRight.BaseNeighbor = &m_BaseLeft; 
 
	// Store Patch offsets for the world and heightmap. 
	m_WorldX = worldX; 
	m_WorldY = worldY; 
 
	// Store pointer to first byte of the height data for this patch. 
	m_HeightMap = &hMap[heightY * MAP_SIZE + heightX]; 
 
	// Initialize flags 
	m_VarianceDirty = 1;//假设不满足精度,需要做进一步细化 
	m_isVisible = 0;    //设不可见 
} 
 
 
void Patch::Reset() 
{ 
	// 预先假设地形是不可见的. 
	m_isVisible = 0; 
 
	// Reset the important relationships 
	m_BaseLeft.LeftChild = m_BaseLeft.RightChild = m_BaseRight.LeftChild = m_BaseLeft.LeftChild = NULL; 
 
	// Attach the two m_Base triangles together 
	m_BaseLeft.BaseNeighbor = &m_BaseRight; 
	m_BaseRight.BaseNeighbor = &m_BaseLeft; 
 
	// Clear the other relationships. 
	m_BaseLeft.RightNeighbor = m_BaseLeft.LeftNeighbor = m_BaseRight.RightNeighbor = m_BaseRight.LeftNeighbor = NULL; 
 
} 
 
// --------------------------------------------------------------------- 
// Discover the orientation of a triangle's points: 
// 
// Taken from "Programming Principles in Computer Graphics", L. Ammeraal (Wiley) 
// 
inline int orientation( int pX, int pY, int qX, int qY, int rX, int rY ) 
{ 
	int aX, aY, bX, bY; 
	float d; 
 
	aX = qX - pX; 
	aY = qY - pY; 
 
	bX = rX - pX; 
	bY = rY - pY; 
 
	d = (float)aX * (float)bY - (float)aY * (float)bX; 
	return (d < 0) ? (-1) : (d > 0); 
} 
 
 
void Patch::SetVisibility(int eyeX, int eyeY, int leftX, int leftY, int rightX, int rightY) 
{ 
	// Get patch's center point 
	int patchCenterX = m_WorldX + PATCH_SIZE / 2; 
	int patchCenterY = m_WorldY + PATCH_SIZE / 2; 
	 
	// Set visibility flag (orientation of both triangles must be counter clockwise) 
	m_isVisible = true;//(orientation( eyeX,  eyeY,  rightX, rightY, patchCenterX, patchCenterY ) < 0) && 
				  //(orientation( leftX, leftY, eyeX,   eyeY,   patchCenterX, patchCenterY ) < 0); 
 
} 
////////////////////////////////////// 
/////    计算图像空间误差    ///////// 
////////////////////////////////////// 
void Patch::ComputeVariance() 
{ 
	// 计算每个三角形的误差 
 
	m_CurrentVariance = m_VarianceLeft; 
	RecursComputeVariance(	0,          PATCH_SIZE, m_HeightMap[PATCH_SIZE * MAP_SIZE], 
							PATCH_SIZE, 0,          m_HeightMap[PATCH_SIZE], 
							0,          0,          m_HeightMap[0], 
							1); 
 
	m_CurrentVariance = m_VarianceRight; 
	RecursComputeVariance(	PATCH_SIZE, 0,          m_HeightMap[ PATCH_SIZE], 
							0,          PATCH_SIZE, m_HeightMap[ PATCH_SIZE * MAP_SIZE], 
							PATCH_SIZE, PATCH_SIZE, m_HeightMap[(PATCH_SIZE * MAP_SIZE) + PATCH_SIZE], 
							1); 
 
	// Clear the dirty flag for this patch 
	m_VarianceDirty = 0; 
} 
 
///////////////////////////////////////////////////// 
//////////// 递归求解各层三角形的变形误差  ////////// 
///////////////////////////////////////////////////// 
unsigned char Patch::RecursComputeVariance( int leftX,  int leftY,  double leftZ, 
										    int rightX, int rightY, double rightZ, 
											int apexX,  int apexY,  double apexZ, 
											int node) 
{ 
	//        /|\ 
	//      /  |  \ 
	//    /    |    \ 
	//  /      |      \ 
	//  ~~~~~~~*~~~~~~~  <-- Compute the X and Y coordinates of '*' 
	// 
	int centerX = (leftX + rightX) >>1;		// Compute X coordinate of center of Hypotenuse 
	int centerY = (leftY + rightY) >>1;		// Compute Y coord... 
	unsigned char myVariance; 
 
	// Get the height value at the middle of the Hypotenuse 
	double centerZ  = m_HeightMap[(centerY * MAP_SIZE) + centerX]; 
 
	// Variance of this triangle is the actual height at it's hypotenuse midpoint minus the interpolated height. 
	// Use values passed on the stack instead of re-accessing the Height Field. 
	myVariance = abs((int)centerZ - (((int)leftZ + (int)rightZ)>>1)); 
 
	// Since we're after speed and not perfect representations, 
	//    only calculate variance down to an 8x8 block 
	if ( (abs(leftX - rightX) >= 8) || 
		 (abs(leftY - rightY) >= 8) ) 
	{ 
		// Final Variance for this node is the max of it's own variance and that of it's children. 
		myVariance = MAX( myVariance, RecursComputeVariance( apexX,   apexY,  apexZ, leftX, leftY, leftZ, centerX, centerY, centerZ,    node<<1 ) ); 
		myVariance = MAX( myVariance, RecursComputeVariance( rightX, rightY, rightZ, apexX, apexY, apexZ, centerX, centerY, centerZ, 1+(node<<1)) ); 
	} 
 
	// Store the final variance for this node.  Note Variance is never zero. 
	if (node <= (1<>1; // Compute X coordinate of center of Hypotenuse 
	int centerY = (leftY + rightY)>>1; // Compute Y coord... 
	float distance; 
	if ( node < (1<= (1< gFrameVariance))	// OR if we are not below the variance tree, test for variance. 
//	((float)m_CurrentVariance[node] * MAP_SIZE * 0.08) > distance) 
	{ 
		Split(tri);														// Split this triangle. 
		 
		if (tri->LeftChild &&											// If this triangle was split, try to split it's children as well. 
			((abs(leftX - rightX) >= 8) || (abs(leftY - rightY) >= 8)))	// Tessellate all the way down to one vertex per height field entry 
		{ 
			RecursTessellate( tri->LeftChild,   apexX,  apexY, leftX, leftY, centerX, centerY,    node<<1  ); 
			RecursTessellate( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY, 1+(node<<1) ); 
		} 
	} 
} 
 
 
////////////////////////////////////// 
/////////    绘制地形    ///////////// 
////////////////////////////////////// 
void Patch::Render() 
{ 
	// Store old matrix 
	glPushMatrix(); 
	glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 
	 
	// Translate the patch to the proper world coordinates 
	glTranslatef( (GLfloat)m_WorldX, (GLfloat)m_WorldY,0  ); 
	glBegin(GL_TRIANGLES); 
		 
		RecursRender (	&m_BaseLeft, 
			0,				PATCH_SIZE, 
			PATCH_SIZE,		0, 
			0,				0); 
		 
		RecursRender(	&m_BaseRight, 
			PATCH_SIZE,		0, 
			0,				PATCH_SIZE, 
			PATCH_SIZE,		PATCH_SIZE); 
	 
	glEnd(); 
	 
	// Restore the matrix 
	glPopMatrix(); 
} 
 
 
 
// --------------------------------------------------------------------- 
// Render the tree.  Simple no-fan method. 
// 
void Patch::RecursRender( TriTreeNode *tri, int leftX, int leftY, int rightX, int rightY, int apexX, int apexY ) 
{ 
	if ( tri->LeftChild )					// All non-leaf nodes have both children, so just check for one 
	{ 
		int centerX = (leftX + rightX)>>1;	// Compute X coordinate of center of Hypotenuse 
		int centerY = (leftY + rightY)>>1;	// Compute Y coord... 
 
		RecursRender( tri->LeftChild,  apexX,   apexY, leftX, leftY, centerX, centerY ); 
		RecursRender( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY ); 
	} 
	else									// A leaf node!  Output a triangle to be rendered. 
	{ 
		// Actual number of rendered triangles... 
		gNumTrisRendered++; 
 
		GLdouble leftZ  = m_HeightMap[(leftY *MAP_SIZE)+leftX ]; 
		GLdouble rightZ = m_HeightMap[(rightY*MAP_SIZE)+rightX]; 
		GLdouble apexZ  = m_HeightMap[(apexY *MAP_SIZE)+apexX ]; 
		int col = MAP_SIZE; 
 
		FCOLOR color; 
		meGetPseudoColor(leftZ,m_zmax,m_zmin,color,1); 
		glColor3f(color.red,color.green,color.blue); 
		glVertex3f(		(GLfloat) leftX, 
						(GLfloat) leftY, 
						(GLfloat) leftZ/20); 
 
		meGetPseudoColor(rightZ,m_zmax,m_zmin,color,1); 
		glColor3f(color.red,color.green,color.blue); 
		glVertex3f(		(GLfloat) rightX, 
						(GLfloat) rightY, 
						(GLfloat) rightZ/20); 
 
		meGetPseudoColor(apexZ,m_zmax,m_zmin,color,1); 
		glColor3f(color.red,color.green,color.blue); 
		glVertex3f(		(GLfloat) apexX, 
						(GLfloat) apexY, 
						(GLfloat) apexZ/20); 
	} 
} 
 
 
// --------------------------------------------------------------------- 
// Split a single Triangle and link it into the mesh. 
// Will correctly force-split diamonds. 
// 
void Patch::Split(TriTreeNode *tri) 
{ 
	// We are already split, no need to do it again. 
	if (tri->LeftChild) 
		return; 
 
	// If this triangle is not in a proper diamond, force split our base neighbor 
	if ( tri->BaseNeighbor && (tri->BaseNeighbor->BaseNeighbor != tri) ) 
		Split(tri->BaseNeighbor); 
 
	// Create children and link into mesh 
	tri->LeftChild  = Landscape::AllocateTri(); 
	tri->RightChild = Landscape::AllocateTri(); 
 
	// If creation failed, just exit. 
	if ( !tri->LeftChild ) 
		return; 
 
	// Fill in the information we can get from the parent (neighbor pointers) 
	tri->LeftChild->BaseNeighbor  = tri->LeftNeighbor; 
	tri->LeftChild->LeftNeighbor  = tri->RightChild; 
 
	tri->RightChild->BaseNeighbor  = tri->RightNeighbor; 
	tri->RightChild->RightNeighbor = tri->LeftChild; 
 
	// Link our Left Neighbor to the new children 
	if (tri->LeftNeighbor != NULL) 
	{ 
		if (tri->LeftNeighbor->BaseNeighbor == tri) 
			tri->LeftNeighbor->BaseNeighbor = tri->LeftChild; 
		else if (tri->LeftNeighbor->LeftNeighbor == tri) 
			tri->LeftNeighbor->LeftNeighbor = tri->LeftChild; 
		else if (tri->LeftNeighbor->RightNeighbor == tri) 
			tri->LeftNeighbor->RightNeighbor = tri->LeftChild; 
		else 
			;// Illegal Left Neighbor! 
	} 
 
	// Link our Right Neighbor to the new children 
	if (tri->RightNeighbor != NULL) 
	{ 
		if (tri->RightNeighbor->BaseNeighbor == tri) 
			tri->RightNeighbor->BaseNeighbor = tri->RightChild; 
		else if (tri->RightNeighbor->RightNeighbor == tri) 
			tri->RightNeighbor->RightNeighbor = tri->RightChild; 
		else if (tri->RightNeighbor->LeftNeighbor == tri) 
			tri->RightNeighbor->LeftNeighbor = tri->RightChild; 
		else 
			;// Illegal Right Neighbor! 
	} 
 
	// Link our Base Neighbor to the new children 
	if (tri->BaseNeighbor != NULL) 
	{ 
		if ( tri->BaseNeighbor->LeftChild ) 
		{ 
			tri->BaseNeighbor->LeftChild->RightNeighbor = tri->RightChild; 
			tri->BaseNeighbor->RightChild->LeftNeighbor = tri->LeftChild; 
			tri->LeftChild->RightNeighbor = tri->BaseNeighbor->RightChild; 
			tri->RightChild->LeftNeighbor = tri->BaseNeighbor->LeftChild; 
		} 
		else 
			Split( tri->BaseNeighbor);  // Base Neighbor (in a diamond with us) was not split yet, so do that now. 
	} 
	else 
	{ 
		// An edge triangle, trivial case. 
		tri->LeftChild->RightNeighbor = NULL; 
		tri->RightChild->LeftNeighbor = NULL; 
	} 
} 
 
 
void SetMinMaxElevation(float zmin,float zmax) 
{ 
	m_zmin = zmin; 
	m_zmax = zmax; 
}