www.pudn.com > DialogOpenGL.rar > OpenGL.cpp


// OpenGL.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "DialogOpenGL.h" 
#include "OpenGL.h" 
#include  
#include  
#include  
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
GLuint texureId; 
 
///////////////////////////////////////////////////////////////////////////// 
// COpenGL 
 
COpenGL::COpenGL():m_bInit(FALSE),m_bFullScreen(FALSE), 
		m_hDC(NULL),m_hRC(NULL),m_parent(NULL) 
{ 
	doRotate = false; 
	mouseDown = false; 
	firstSpot = secondSpot = false; 
	dx = 0.0f, dy = 0.0f, dz = -5.0f; 
	deep = 0.9f; 
} 
 
COpenGL::~COpenGL() 
{ 
	KillGLWindow();	// Shutdown 
} 
 
 
BEGIN_MESSAGE_MAP(COpenGL, CWnd) 
	//{{AFX_MSG_MAP(COpenGL) 
	ON_WM_CREATE() 
	ON_WM_PAINT() 
	ON_WM_SIZE() 
	ON_WM_RBUTTONDOWN() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_LBUTTONUP() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
///////////////////////////////////////////////////////////////////////////// 
// COpenGL message handlers 
 
void COpenGL::Create(CRect rect, CWnd *parent) 
{ 
	if (m_bInit) return; 
	ASSERT(rect); 
	ASSERT(parent); 
	m_rect = rect; 
	m_parent = parent; 
 
	CString className = AfxRegisterWndClass( 
		CS_HREDRAW | CS_VREDRAW | CS_OWNDC, 
		NULL, 
		(HBRUSH)GetStockObject(BLACK_BRUSH), 
		NULL); 
	 
	CreateEx( 
		0, 
		className, 
		"OpenGL", 
		WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 
		rect, 
		parent, 
		0); 
} 
 
void COpenGL::Load3dsModel(CString filePath) 
{ 
	model.Load(filePath.GetBuffer(filePath.GetLength())); 
} 
 
void COpenGL::LoadTexture(CString filePath) 
{ 
	char strMessage[128] = {0}; 
 
	model.addTexture = true; 
 
	// 对有纹理的材质载入该纹理 
	if (!BuildTexture(filePath.GetBuffer(filePath.GetLength()), texureId)) 
	{ 
		// 纹理载入失败 
		model.addTexture = false; 
		sprintf(strMessage, "3DS纹理文件载入失败: %s !", filePath); 
		MessageBox(strMessage, "Error", MB_OK); 
	} 
} 
 
int COpenGL::OnCreate(LPCREATESTRUCT lpCreateStruct)  
{ 
	if (CWnd::OnCreate(lpCreateStruct) == -1) 
		return -1; 
	 
	// TODO: Add your specialized creation code here 
	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &m_DMsaved); 
 
 
	GLuint	PixelFormat;												// Holds The Results After Searching For A Match 
	static	PIXELFORMATDESCRIPTOR pfd=									// pfd Tells Windows How We Want Things To Be 
	{																		 
		sizeof(PIXELFORMATDESCRIPTOR),									// Size Of This Pixel Format Descriptor 
			1,																// Version Number 
			PFD_DRAW_TO_WINDOW |											// Format Must Support Window 
			PFD_SUPPORT_OPENGL |											// Format Must Support OpenGL 
			PFD_DOUBLEBUFFER,												// Must Support Double Buffering 
			PFD_TYPE_RGBA,													// Request An RGBA Format 
			m_DMsaved.dmBitsPerPel,												// Select Our Color Depth 
			0, 0, 0, 0, 0, 0,												// Color Bits Ignored 
			0,																// No Alpha Buffer 
			0,																// Shift Bit Ignored 
			0,																// No Accumulation Buffer 
			0, 0, 0, 0,														// Accumulation Bits Ignored 
			16,																// 16Bit Z-Buffer (Depth Buffer)   
			0,																// No Stencil Buffer 
			0,																// No Auxiliary Buffer 
			PFD_MAIN_PLANE,													// Main Drawing Layer 
			0,																// Reserved 
			0, 0, 0															// Layer Masks Ignored 
	};																		 
	 
	 
	if ( !( m_hDC = ::GetDC ( m_hWnd ) ) )	{							// Did We Get A Device Context?																	 
		KillGLWindow ();													// Reset The Display 
		TRACE ( "Can't Create A GL Device Context." ); 
		return FALSE;												 
	} 
	 
	if ( !( PixelFormat = ChoosePixelFormat ( m_hDC, &pfd ) ) )	{		// Did Windows Find A Matching Pixel Format?																		 
		KillGLWindow ();												// Reset The Display 
		TRACE ( "Can't Find A Suitable PixelFormat." ); 
		return FALSE;												 
	} 
	 
	if ( !SetPixelFormat ( m_hDC, PixelFormat, &pfd ) ){				// Are We Able To Set The Pixel Format?																		 
		KillGLWindow ();												// Reset The Display 
		TRACE ( "Can't Set The PixelFormat." ); 
		return FALSE;												 
	} 
	 
	if ( !( m_hRC = wglCreateContext ( m_hDC ) ) ) {					// Are We Able To Get A Rendering Context?																	 
		KillGLWindow ();												// Reset The Display 
		TRACE( " Can't Create A GL Rendering Context." ); 
		return FALSE;													 
	} 
	 
	if ( !wglMakeCurrent ( m_hDC, m_hRC ) )	{							// Try To Activate The Rendering Context																		 
		KillGLWindow ();												// Reset The Display 
		TRACE ( "Can't Activate The GL Rendering Context." ); 
		return FALSE;													 
	} 
	 
	if ( !InitGL () ) {													// Initialize Our Newly Created GL Window																		 
		KillGLWindow ();												// Reset The Display 
		TRACE ( "Initialization Failed." ); 
		return FALSE;													 
	} 
	m_bInit = TRUE; 
	 
	return 0; 
} 
 
void COpenGL::KillGLWindow() 
{ 
	if (m_bFullScreen)															// Are We In Fullscreen Mode? 
	{ 
		if (!ChangeDisplaySettings(NULL,CDS_TEST)) {						// if the shortcut doesn't work 
			ChangeDisplaySettings(NULL,CDS_RESET);							// Do it anyway (to get the values out of the registry) 
			ChangeDisplaySettings(&m_DMsaved,CDS_RESET);						// change it to the saved settings 
		} else { 
			ChangeDisplaySettings(NULL,CDS_RESET); 
		} 
		 
		ShowCursor(TRUE);													// Show Mouse Pointer 
	}																		 
	 
	if ( m_hRC ) {															// Do We Have A Rendering Context?																		 
		if ( !wglMakeCurrent ( NULL, NULL ) )	{							// Are We Able To Release The DC And RC Contexts? 
			TRACE ( "Release Of DC And RC Failed." ); 
		} 
		 
		if ( !wglDeleteContext ( m_hRC ) ) {								// Are We Able To Delete The RC? 
			TRACE ( "Release Rendering Context Failed." ); 
		} 
		m_hRC = NULL;														// Set RC To NULL 
	} 
	 
	if ( m_hDC && !::ReleaseDC ( m_hWnd, m_hDC ) ) {						// Are We Able To Release The DC 
		TRACE ( "Release Device Context Failed." ); 
		m_hDC = NULL;														// Set DC To NULL 
	} 
	 
	if ( m_hWnd && !::DestroyWindow ( m_hWnd ) )	{							// Are We Able To Destroy The Window? 
		TRACE( "Could Not Release m_hWnd." ); 
		m_hWnd = NULL;														// Set m_hWnd To NULL 
	} 
} 
 
int COpenGL::InitGL() 
{ 
	glShadeModel(GL_SMOOTH);												// Enable Smooth Shading 
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);									// Black Background 
	glClearDepth(1.0f);														// Depth Buffer Setup 
	glEnable(GL_DEPTH_TEST);												// Enables Depth Testing 
	glDepthFunc(GL_LEQUAL);													// The Type Of Depth Testing To Do 
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);						// Really Nice Perspective Calculations 
	glEnable(GL_TEXTURE_2D);												// Enable Texture Mapping 
 
	return TRUE;															// Initialization Went OK	 
} 
 
void COpenGL::RenderGLScene() 
{ 
	if(!m_bInit) return; 
 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);			// Clear Screen And Depth Buffer 
	glLoadIdentity(); 
	 
	 
	// EXAMPLE OPENGL CODE START //////////////////////////////////////////////////////// 
	//////////////////////////////////////////////////////////////////////////////  
	static GLfloat	yrot;											// Y Rotation 
	 
	glPushMatrix();													// Push Matrix Onto Stack (Copy The Current Matrix) 
	glLoadIdentity();												// Reset The Current Modelview Matrix 
	glTranslatef(dx, dy, dz);										// Move Left 1.5 Units And Into The Screen 6.0 
	 
	glRotatef(yrot,0.0f,1.0f,0.0f); 
 
	// 从窗口坐标到OpenGL坐标的转换 
	double wx, wy, wz; 
 
	if(mouseDown){ 
		CString strZ, strW; 
 
		this->GetParent()->GetDlgItemText(IDC_Z, strZ); 
		this->GetParent()->GetDlgItemText(IDC_W, strW); 
		deep = (float)atof(strZ.GetBuffer(strZ.GetLength ())); 
		model.width = (float)atof(strW.GetBuffer(strW.GetLength ())); 
 
		GLint viewport[4]; 
		glGetIntegerv(GL_VIEWPORT, viewport); 
		GLdouble mvmatrix[16], projmatrix[16]; 
		glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); 
		glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); 
		GLint realy = viewport[3] -  mouse_y; 
 
		gluUnProject((GLdouble)mouse_x, (GLdouble)realy, deep, mvmatrix, projmatrix, viewport, &wx, &wy, &wz); 
 
		if(!firstSpot){ 
			model.SetFirstTextureSpot((float)wx, (float)wy, (float)wz); 
			firstSpot = true; 
			secondSpot = false; 
		} 
		else if(!secondSpot){ 
			model.SetSecondTextureSpot((float)wx, (float)wy, (float)wz); 
			secondSpot = true; 
			firstSpot = false; 
		} 
		mouseDown = false; 
	} 
 
	model.Render(); 
	glPopMatrix();														// Pop Matrix Off The Stack 
 
	if(doRotate) 
		yrot+=1.2f;														// Decrease The Rotation Variable For The Quad 
	////////////////////////////////////////////////////////////////////////////// 
	// EXAMPLE OPENGL CODE END ////////////////////////////////////////////////////////// 
	// Swap our scene to the front 
	 
	SwapBuffers(m_hDC); 
	 
	Invalidate(FALSE); 
} 
 
void COpenGL::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
	 
	// TODO: Add your message handler code here 
	::ValidateRect ( m_hWnd, NULL ); 
	 
	// Do not call CWnd::OnPaint() for painting messages 
} 
 
 
void COpenGL::OnSize(UINT nType, int cx, int cy)  
{ 
	CWnd::OnSize(nType, cx, cy); 
	 
	// TODO: Add your message handler code here 
	if ( cy==0) {														// Prevent A Divide By Zero By																	 
		cy=1;															// Making Height Equal One 
	}																		 
	 
	glViewport(0,0,cx,cy);											// Reset The Current Viewport 
	 
	glMatrixMode(GL_PROJECTION);											// Select The Projection Matrix 
	glLoadIdentity();														// Reset The Projection Matrix 
	 
	gluPerspective(45.0f,(GLfloat)cx/(GLfloat)cy,0.1f,100.0f);		// Calculate The Aspect Ratio Of The Window 
	 
	glMatrixMode(GL_MODELVIEW);												// Select The Modelview Matrix 
	glLoadIdentity();														// Reset The Modelview Matrix	 
	 
} 
 
void COpenGL::OnRButtonDown(UINT nFlags, CPoint point)  
{ 
	doRotate = !doRotate; 
	CWnd::OnRButtonDown(nFlags, point); 
} 
 
void COpenGL::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	mouse_x = point.x; 
	mouse_y = point.y; 
	mouseDown = true; 
	CWnd::OnLButtonDown(nFlags, point); 
} 
 
BOOL COpenGL::SetFullScreen(int width, int height, int depth) 
{ 
	if(!m_bInit) return FALSE; 
	if (m_bFullScreen) return TRUE; 
	 
	DEVMODE dmScreenSettings;												// Device Mode 
	memset(&dmScreenSettings,0,sizeof(dmScreenSettings));					// Makes Sure Memory's Cleared 
	dmScreenSettings.dmSize=sizeof(dmScreenSettings);						// Size Of The Devmode Structure 
	dmScreenSettings.dmPelsWidth	= width;								// Selected Screen Width 
	dmScreenSettings.dmPelsHeight	= height;							// Selected Screen Height 
	dmScreenSettings.dmBitsPerPel	= depth;								// Selected Bits Per Pixel 
	dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; 
	 
	// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar. 
	 
	if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) 
	{ 
		return m_bFullScreen = FALSE; 
	} 
 
	SetParent(NULL); 
	SetWindowPos(&CWnd::wndTop, 
		          0, 0,  
				  GetSystemMetrics(SM_CXSCREEN),  
				  GetSystemMetrics(SM_CYSCREEN),  
				  SWP_SHOWWINDOW); 
 
	ShowCursor(FALSE); 
	SetFocus(); 
	return m_bFullScreen = TRUE; 
} 
 
BOOL COpenGL::SetNormScreen() 
{ 
	if(!m_bInit) return FALSE; 
 
	if (m_bFullScreen)														// Are We In Fullscreen Mode? 
	{ 
		if (!ChangeDisplaySettings(NULL,CDS_TEST)) {						// if the shortcut doesn't work 
			ChangeDisplaySettings(NULL,CDS_RESET);							// Do it anyway (to get the values out of the registry) 
			ChangeDisplaySettings(&m_DMsaved,CDS_RESET);					// change it to the saved settings 
		} else { 
			ChangeDisplaySettings(NULL,CDS_RESET); 
		} 
		SetParent(m_parent); 
		SetWindowPos(&CWnd::wndTop, 
		          m_rect.left, m_rect.top,  
				  m_rect.Width(),  
				  m_rect.Height(),  
				  SWP_SHOWWINDOW);		 
		ShowCursor(TRUE);	// Show Mouse Pointer 
		m_bFullScreen = FALSE; 
	} 
	return TRUE; 
} 
 
 
// 构造函数 
C3DSModel::C3DSModel() 
{ 
	g_ViewMode = GL_TRIANGLES; 
	addTexture = false; 
	spotNum = 0; 
	width = 0.02f; 
 
	// 初始化文件指针 
	m_FilePtr = NULL; 
 
	// 定义一个默认的材质(灰色) 
	tMaterial defaultMat; 
	defaultMat.isTexMat = false; 
	strcpy(defaultMat.matName.string, "5DG_Default"); 
	defaultMat.color[0] = 192; 
	defaultMat.color[1] = 192; 
	defaultMat.color[2] = 192; 
	m_3DModel.pMaterials.push_back(defaultMat); 
 
	// 初始化保存3DS模型的结构体 
	m_3DModel.numOfMaterials = 1; 
	m_3DModel.numOfObjects = 0; 
} 
 
// 析构函数 
C3DSModel::~C3DSModel() 
{ 
	m_3DModel.pMaterials.clear(); 
	m_3DModel.pObject.clear(); 
} 
 
// 载入3ds文件 
BOOL C3DSModel::Load(char *strFileName) 
{ 
	char strMessage[128] = {0}; 
	tChunk chunk = {0}; 
 
	// 打开文件 
	m_FilePtr = fopen(strFileName,"rb"); 
 
	// 如果文件打开失败 
	if (!m_FilePtr) 
	{ 
		sprintf(strMessage, "3DS文件 %s 不存在!", strFileName); 
		MessageBox(NULL, strMessage, "Error", MB_OK); 
		return false; 
	} 
 
	// 读取3ds文件的第一个Chunk 
	ReadChunk(&chunk); 
 
	// 检查是否是3ds文件 
	if (chunk.ID != PRIMARY) 
	{ 
		sprintf(strMessage, "读取文件 %s 失败!", strFileName); 
		MessageBox(NULL, strMessage, "Error", MB_OK); 
		fclose(m_FilePtr); 
		return false; 
	} 
 
	// 开始读取3ds文件 
	ReadPrimary(chunk.length-6); 
 
	// 计算每个顶点的法线量 
	ComputeNormals(); 
 
	// 关闭打开的文件 
	fclose(m_FilePtr); 
	m_FilePtr = NULL; 
 
	return true; 
} 
 
// 从文件中读取1个字节 
BYTE C3DSModel::ReadByte(void) 
{ 
	BYTE result = 0; 
	fread(&result, 1, 1, m_FilePtr); 
	return result; 
} 
 
// 从文件中读取2个字节 
WORD C3DSModel::ReadWord(void) 
{ 
	return ReadByte() + (ReadByte()<<8); 
} 
 
// 从文件中读取4个字节 
UINT C3DSModel::ReadUint(void) 
{ 
	return ReadWord() + (ReadWord()<<16); 
} 
 
// 从文件中读取浮点数 
float C3DSModel::ReadFloat(void) 
{ 
	float result; 
	fread(&result, sizeof(float), 1, m_FilePtr); 
	return result; 
} 
 
// 从文件中读取字符串(返回字符串长度) 
UINT C3DSModel::ReadString(STRING *pStr) 
{ 
	int n=0; 
	while ((pStr->string[n++]=ReadByte()) != 0) 
		; 
	return n; 
} 
 
// 读取3ds的一个Chunk信息 
void C3DSModel::ReadChunk(tChunk *pChunk) 
{ 
	fread(&pChunk->ID, 1, 2, m_FilePtr); 
	fread(&pChunk->length, 1, 4, m_FilePtr); 
} 
 
// 读取3ds文件主要Chunk 
UINT C3DSModel::ReadPrimary(UINT n) 
{ 
	UINT count = 0;				// 该Chunk内容已读取的字节计数 
	tChunk chunk = {0};			// 用以保存子Chunk的内容 
	while (count < n) 
	{ 
		ReadChunk(&chunk); 
		switch (chunk.ID) 
		{ 
		case PRIM_EDIT: 
			ReadEdit(chunk.length-6); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 读取3ds物体主编辑Chunk 
UINT C3DSModel::ReadEdit(UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	while(count < n) 
	{ 
		ReadChunk(&chunk); 
		switch(chunk.ID) 
		{ 
		case EDIT_MAT: 
			ReadMaterial(chunk.length-6); 
			break; 
		case EDIT_OBJECT: 
			ReadObject(chunk.length-6); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 读取3ds对象 
UINT C3DSModel::ReadObject(UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	// 新的3ds对象 
	t3DObject newObject = {0}; 
	count += ReadString(&newObject.objName); 
	m_3DModel.numOfObjects ++; 
 
	while (count < n) 
	{ 
		ReadChunk(&chunk); 
		switch (chunk.ID) 
		{ 
		case OBJECT_INFO: 
			ReadObjectInfo(&newObject, n-count -6); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	// 保存3ds对象 
	m_3DModel.pObject.push_back(newObject); 
	return count; 
} 
 
// 读取3ds对象信息 
UINT C3DSModel::ReadObjectInfo(t3DObject *pObj, UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
 
	while (count < n) 
	{ 
		ReadChunk(&chunk); 
		switch (chunk.ID) 
		{ 
		case OBJECT_VERTEX: 
			{ 
				pObj->numOfVerts = ReadWord(); 
				pObj->pVerts = new Vector3[pObj->numOfVerts]; 
				memset(pObj->pVerts, 0, sizeof(Vector3) * pObj->numOfVerts); 
				// 按块读取顶点坐标值 
				fread(pObj->pVerts, 1, chunk.length - 8, m_FilePtr); 
				// 调换y、z坐标值(由于3dMAX坐标系方向与OpenGL不同) 
				float fTempY; 
				for (int i = 0; i < pObj->numOfVerts; i++) 
				{ 
					fTempY = pObj->pVerts[i].y; 
					pObj->pVerts[i].y = pObj->pVerts[i].z; 
					pObj->pVerts[i].z = -fTempY; 
				} 
				break; 
			} 
		case OBJECT_FACET: 
			ReadFacetInfo(pObj,chunk.length-6); 
			break; 
		case OBJECT_UV: 
			pObj->numTexVertex = ReadWord(); 
			pObj->pTexVerts = new Vector2[pObj->numTexVertex]; 
			memset(pObj->pTexVerts, 0, sizeof(Vector2) * pObj->numTexVertex); 
			// 按块读取纹理坐标值 
			fread(pObj->pTexVerts, 1, chunk.length - 8, m_FilePtr); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 读取面信息 
UINT C3DSModel::ReadFacetInfo(t3DObject *pObj, UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	pObj->numOfFaces = ReadWord(); 
	pObj->pFaces = new tFace[pObj->numOfFaces]; 
	memset(pObj->pFaces, 0, sizeof(tFace) * pObj->numOfFaces); 
	// 读取面索引值(第4个值为3dMAX使用的参数,舍弃) 
	for (int i=0; inumOfFaces; i++) 
	{ 
		pObj->pFaces[i].vertIndex[0] = ReadWord(); 
		pObj->pFaces[i].vertIndex[1] = ReadWord(); 
		pObj->pFaces[i].vertIndex[2] = ReadWord(); 
		ReadWord(); 
	} 
	count +=2+pObj->numOfFaces*8; 
 
	STRING matName; 
	int t; 
	int matID = 0; 
	while (count < n) 
	{ 
		ReadChunk(&chunk); 
		switch (chunk.ID) 
		{ 
		case FACET_MAT: 
			{ 
				ReadString(&matName);			// 材质名称 
				t=ReadWord();					// 材质对应的面个数 
				// 查找对应的材质 
				for (int i=1;i<=m_3DModel.numOfMaterials;i++) 
				{ 
					if (strcmp(matName.string, m_3DModel.pMaterials[i].matName.string) == 0) 
					{ 
						matID = i; 
						break; 
					} 
				} 
				// 依据面索引给每个面绑定材质ID 
				while (t>0) 
				{ 
					pObj->pFaces[ReadWord()].matID = matID; 
					t--; 
				} 
				break; 
			} 
		default: 
			{ 
				fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
				break; 
			} 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 读取材质 
UINT C3DSModel::ReadMaterial(UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	// 新的材质 
	tMaterial newMaterial = {0}; 
	m_3DModel.numOfMaterials ++; 
	while (count < n) 
	{ 
		ReadChunk(&chunk); 
		switch (chunk.ID) 
		{ 
		case MAT_NAME: 
			ReadString(&newMaterial.matName); 
			break; 
		case MAT_DIF: 
			ReadMatDif (&newMaterial, chunk.length-6); 
			break; 
		case MAT_MAP: 
			ReadMatMap(&newMaterial, chunk.length-6); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	// 保存新的材质 
	m_3DModel.pMaterials.push_back(newMaterial); 
	return count; 
} 
 
// 读取材质的漫反射属性 
UINT C3DSModel::ReadMatDif (tMaterial *pMat, UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	while (countcolor[0] = ReadByte(); 
			pMat->color[1] = ReadByte(); 
			pMat->color[2] = ReadByte(); 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 读取材质的纹理 
UINT C3DSModel::ReadMatMap(tMaterial *pMat, UINT n) 
{ 
	UINT count = 0; 
	tChunk chunk = {0}; 
	while (countmapName); 
			pMat->isTexMat = true; 
			break; 
		default: 
			fseek(m_FilePtr, chunk.length-6, SEEK_CUR); 
			break; 
		} 
		count += chunk.length; 
	} 
	return count; 
} 
 
// 绘制3ds模型 
void C3DSModel::Render(void) 
{ 
	tMaterial *mat; 
	t3DObject *obj; 
	int		  *index; 
	 
	for (int nOfObj=0; nOfObjnumOfFaces; nOfFace++) 
		{ 
			index = obj->pFaces[nOfFace].vertIndex; 
			mat  = &m_3DModel.pMaterials[obj->pFaces[nOfFace].matID]; 
 
			glEnable(GL_TEXTURE_2D); 
			if(addTexture) glBindTexture(GL_TEXTURE_2D, texureId);		// 选择该材质的纹理 
			glColor3ub(mat->color[0], mat->color[1], mat->color[2]); 
			// 绘制三角形面 
			glBegin(g_ViewMode); 
				glNormal3f(obj->pNormals[index[0]].x,obj->pNormals[index[0]].y,obj->pNormals[index[0]].z); 
				glVertex3f(obj->pVerts[index[0]].x, obj->pVerts[index[0]].y, obj->pVerts[index[0]].z); 
 
				glNormal3f(obj->pNormals[index[1]].x,obj->pNormals[index[1]].y,obj->pNormals[index[1]].z); 
				glVertex3f(obj->pVerts[index[1]].x, obj->pVerts[index[1]].y, obj->pVerts[index[1]].z); 
 
				glNormal3f(obj->pNormals[index[2]].x,obj->pNormals[index[2]].y,obj->pNormals[index[2]].z); 
				glVertex3f(obj->pVerts[index[2]].x, obj->pVerts[index[2]].y, obj->pVerts[index[2]].z); 
			glEnd(); 
 
			glColor3f(1.0f, 1.0f, 1.0f); 
 
			float cx1, cy1, cz1, cx2, cy2, cz2, lx1, ly1, lz1, lx2, ly2, lz2, 
				rx3, ry3, rz3, rx4, ry4, rz4; 
			bool horizon; 
 
			for(int i = 0; i < spotNum;) 
			{ 
				cx1 = textureSpots[i++]; 
				cy1 = textureSpots[i++]; 
				cz1 = textureSpots[i++]; 
				cx2 = textureSpots[i++]; 
				cy2 = textureSpots[i++]; 
				cz2 = textureSpots[i++]; 
 
				if(fabs(cx1-cx2)>fabs(cy1-cy2)){  
					lx1 = lx2 = cx1; 
					rx3 = rx4 = cx2; 
					ly1 = cy1 + width/2; ly2 = cy1 - width/2; 
					ry3 = cy2 - width/2; ry4 = cy2 + width/2; 
					lz1 = lz2 = cz1; 
					rz3 = rz4 = cz2; 
					horizon = true; 
				} 
				else{ 
					ly1 = ry4 = cy1; 
					ly2 = ry3 = cy2; 
					lx1 = cx1 - width/2; lx2 = cx2 - width/2; 
					rx3 = cx2 + width/2; rx4 = cx1 + width/2;  
					lz1 = rz4 = cz1; 
					lz2 = rz3 = cz2; 
					horizon = false; 
				} 
 
				glBegin(GL_QUADS); 
					if(addTexture) glTexCoord2f(0.05f, 0.05f); 
					glVertex3f(lx1, ly1, lz1); 
					if(addTexture) horizon? glTexCoord2f(0.0f, 0.95f) : glTexCoord2f(0.95f, 0.0f); 
					glVertex3f(lx2, ly2, lz2); 
					if(addTexture) glTexCoord2f(0.95f, 0.95f); 
					glVertex3f(rx3, ry3, rz3); 
					if(addTexture) horizon? glTexCoord2f(0.95f, 0.0f) : glTexCoord2f(0.0f, 0.95f); 
					glVertex3f(rx4, ry4, rz4); 
				glEnd(); 
			} 
		} 
	} 
} 
 
// 释放3ds模型资源 
void C3DSModel::Release(void) 
{ 
	m_3DModel.numOfMaterials = 1; 
	while (m_3DModel.pMaterials.size() != 0) 
		m_3DModel.pMaterials.pop_back(); 
	m_3DModel.numOfObjects = 0; 
	for (int nOfObj=0; nOfObjnumOfFaces]; 
		Vector3 *pTempNormals	= new Vector3 [obj->numOfFaces]; 
		obj->pNormals			= new Vector3 [obj->numOfVerts]; 
 
		for(int nOfFace=0; nOfFacenumOfFaces; nOfFace++) 
		{ 
			index = obj->pFaces[nOfFace].vertIndex; 
			// 三角形的3个顶点 
			vPoly[0] = obj->pVerts[index[0]]; 
			vPoly[1] = obj->pVerts[index[1]]; 
			vPoly[2] = obj->pVerts[index[2]]; 
			// 计算这个三角形的法线量 
			v1 = vPoly[0]-vPoly[1]; 
			v2 = vPoly[2]-vPoly[1]; 
			vNormal  = Cross(v1, v2); 
 
			pTempNormals[nOfFace] = vNormal;					// 保存未单位化的法向量 
			vNormal  = Normalize(vNormal);						// 单位化法向量 
			pNormals[nOfFace] = vNormal;						// 增加到法向量数组列表 
		} 
		Vector3 vSum(0.0, 0.0, 0.0); 
		Vector3 vZero(0.0, 0.0, 0.0); 
		int shared=0; 
 
		for (int nOfVert = 0; nOfVert < obj->numOfVerts; nOfVert++)			// 遍历所有顶点 
		{ 
			for (int nOfFace = 0; nOfFace < obj->numOfFaces; nOfFace++)		// 遍历包含该顶点的面 
			{ 
				if (obj->pFaces[nOfFace].vertIndex[0] == nOfVert ||  
					obj->pFaces[nOfFace].vertIndex[1] == nOfVert ||  
					obj->pFaces[nOfFace].vertIndex[2] == nOfVert) 
				{ 
					vSum = vSum+pTempNormals[nOfFace]; 
					shared++; 
				} 
			}       
			 
			obj->pNormals[nOfVert] = vSum/float(-shared); 
 
			obj->pNormals[nOfVert] = Normalize(obj->pNormals[nOfVert]);	 
 
			vSum = vZero; 
			shared = 0; 
		} 
	 
		delete [] pTempNormals; 
		delete [] pNormals; 
	} 
 
} 
 
 
/////////////////////////////////////////////////////////////////////// 
//Load Texture 
 
#include 														// 标准输入输出头文件 
#include 														// OLE控制库头文件 
#include 														// 数学函数头文件 
#include 														// OpenGL32库的头文件 
 
BOOL BuildTexture(char *szPathName, GLuint &texid)						// 载入图片并转换为纹理 
{ 
	HDC			hdcTemp;												// DC用来保存位图 
	HBITMAP		hbmpTemp;												// 保存临时位图 
	IPicture	*pPicture;												// 定义IPicture Interface 
	OLECHAR		wszPath[MAX_PATH+1];									// 图片的完全路径 
	long		lWidth;													// 图像宽度 
	long		lHeight;												// 图像高度 
	long		lWidthPixels;											// 图像的宽带(以像素为单位) 
	long		lHeightPixels;											// 图像的高带(以像素为单位) 
	GLint		glMaxTexDim ;											// 保存纹理的最大尺寸 
 
	MultiByteToWideChar(CP_ACP, 0, szPathName, -1, wszPath, MAX_PATH);		// 把ASCII码转化为Unicode标准码 
	HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture); 
 
	if(FAILED(hr))														// 如果导入失败 
	{ 
		// 图片载入失败出错信息 
		MessageBox (HWND_DESKTOP, "图片导入失败!\n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 
		return FALSE;													// 返回 FALSE 
	} 
 
	hdcTemp = CreateCompatibleDC(GetDC(0));								// 建立窗口设备描述表 
	if(!hdcTemp)														// 建立失败? 
	{ 
		pPicture->Release();											// 释放IPicture 
		// 图片载入失败出错信息 
		MessageBox (HWND_DESKTOP, "图片导入失败!\n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 
		return FALSE;													// 返回 FALSE 
	} 
 
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);					// 取得支持的纹理最大尺寸 
	 
	pPicture->get_Width(&lWidth);										// 取得IPicture 宽度 (转换为Pixels格式) 
	lWidthPixels	= MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540); 
	pPicture->get_Height(&lHeight);										// 取得IPicture 高度 (转换为Pixels格式) 
	lHeightPixels	= MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540); 
 
	// 调整图片到最好的效果 
	if (lWidthPixels <= glMaxTexDim)									// 图片宽度是否超过显卡最大支持尺寸 
		lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f);  
	else																// 否则,将图片宽度设为显卡最大支持尺寸 
		lWidthPixels = glMaxTexDim; 
  
	if (lHeightPixels <= glMaxTexDim)									// 图片高度是否超过显卡最大支持尺寸 
		lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f); 
	else																// 否则,将图片高度设为显卡最大支持尺寸 
		lHeightPixels = glMaxTexDim; 
 
	// 建立一个临时位图 
	BITMAPINFO	bi = {0};												// 位图的类型 
	DWORD		*pBits = 0;												// 指向位图Bits的指针 
 
	bi.bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);				// 设置结构大小 
	bi.bmiHeader.biBitCount		= 32;									// 32 位 
	bi.bmiHeader.biWidth		= lWidthPixels;							// 宽度像素值 
	bi.bmiHeader.biHeight		= lHeightPixels;						// 高度像素值 
	bi.bmiHeader.biCompression	= BI_RGB;								// RGB 格式 
	bi.bmiHeader.biPlanes		= 1;									// 一个位平面 
 
	// 建立一个位图这样我们可以指定颜色和深度 并访问每位的值 
	hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0); 
	 
	if(!hbmpTemp)														// 建立失败? 
	{ 
		DeleteDC(hdcTemp);												// 删除设备描述表 
		pPicture->Release();											// 释放IPicture 
		// 图片载入失败出错信息 
		MessageBox (HWND_DESKTOP, "图片导入失败!\n(TextureLoad Failed!)", "Error", MB_OK | MB_ICONEXCLAMATION); 
		return FALSE;													// 返回 FALSE 
	} 
 
	SelectObject(hdcTemp, hbmpTemp);									// 选择临时DC句柄和临时位图对象 
 
	// 在位图上绘制IPicture 
	pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0); 
 
	// 将BGR转换为RGB 将ALPHA值设为255 
	for(long i = 0; i < lWidthPixels * lHeightPixels; i++)				// 循环遍历所有的像素 
	{ 
		BYTE* pPixel	= (BYTE*)(&pBits[i]);							// 获取当前像素 
		BYTE  temp		= pPixel[0];									// 临时存储第一个颜色像素(蓝色) 
		pPixel[0]		= pPixel[2];									// 将红色值存到第一位 
		pPixel[2]		= temp;											// 将蓝色值存到第三位 
		pPixel[3]		= 255;											// ALPHA值设为255 
	} 
 
	glGenTextures(1, &texid);											// 创建纹理 
 
	// 使用来自位图数据生成 的典型纹理 
	glBindTexture(GL_TEXTURE_2D, texid);								// 绑定纹理 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// 线形滤波 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   // 线形滤波 
 
	// 生成纹理 
	glTexImage2D(GL_TEXTURE_2D, 0, 3, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits); 
 
	DeleteObject(hbmpTemp);												// 删除对象 
	DeleteDC(hdcTemp);													// 删除设备描述表 
 
	pPicture->Release();												// 释放 IPicture 
 
	return TRUE;														// 返回 TRUE 
}