/*
* This code was writen by zhaojian (ken2006)
* The motivation of this code is to show how to set up a viewing system in opengl
* If You've found this code useful or have something to optimize, Please mail to
* me. My mail add is : [email protected]
* further more ,我将尽可能的注释每行代码,以便你阅读,呵呵,我很好吧!
*/
#define WIN32_LEAN_AND_MEAN // 不许加入MFC 的内容
#include
//---- 以下为 opengl 头文件,不用多说
#include
#include
#include
#include
#include
#include
//加入opengl 的一些连接库
#pragma comment( lib, "opengl32.lib")
#pragma comment( lib, "glu32.lib")
#pragma comment( lib, "glaux.lib")
// 键盘处理宏
#define KEY_DOWN(vk_code)((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
HDC hDC=NULL; // 全局设备描述表句柄(handle)
HGLRC hRC=NULL; // 渲染描述表(Render Context)句柄
HWND hWnd=NULL; // 窗口(window) 句柄
HINSTANCE hInstance; // 应用程序句柄
bool fullscreen = true; // 全屏标识
GLfloat speed = 1.0f; //移动速度
int SCREEN_WIDTH =800; //屏幕的宽高
int SCREEN_HEIGHT =600;
GLuint texture[1]; // 存储一个纹理
GLfloat MAP_SIZE = 200; //地图的大小
GLfloat theta = 0.0f; //旋转的角度
GLfloat viewUp = 0.0f; //向上和向下看的程度
// 3D向量结构
struct Vector3{
GLfloat x;
GLfloat y;
GLfloat z;
};
Vector3 eyePosition ={0.0f, 4.0, 0.0f}; //视点坐标
Vector3 viewAtPosition ={0.0f, 4.0,-1.0}; // 参考点的坐标
//函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 消息处理函数
GLvoid DrawScene(); //画周围场景
GLvoid SetViewByMouse(); //通过鼠标控制旋转
GLvoid ReSizeGL(GLsizei width, GLsizei height) // 设置opengl窗口
{
if (height==0) // 防止除零
{
height=1;
}
glViewport(0,0,width,height); // 设置视口
glMatrixMode(GL_PROJECTION); // 设当前变换为投影变换
glLoadIdentity(); // 当前矩阵初始为单位阵
// 设置透视投影取景器
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,1000.0f);
glMatrixMode(GL_MODELVIEW); // 设当前变换为模型变换
glLoadIdentity();
}
// 读取纹理文件,并返回 AUX_RGBImageRec *
AUX_RGBImageRec *LoadBMP(char *Filename)
{
FILE *File=NULL; // File 句柄
//注意一定要判断文件是否存在,因为auxDIBImageLoad() 错误处理能力很差
if (!Filename)
{
return NULL;
}
File=fopen(Filename,"r"); // 打开文件
if (File) // 如果存在
{
fclose(File); // 关闭文件
return auxDIBImageLoad(Filename); // 并返回AUX_RGBImageRec指针
}
return NULL; // 失败了,返回NULL
}
//以下为载入纹理
int LoadGLTextures()
{
int Status=FALSE; // 状态标志
AUX_RGBImageRec *TextureImage[1]; // 用来保存纹理数据
memset(TextureImage,0,sizeof(void *)*1); // 数据内容清零
//载入位图 并判断成功否?
if (TextureImage[0]=LoadBMP("floor2.bmp"))
{
Status=TRUE; // 成功
glGenTextures(1, &texture[0]); // 创建一个纹理
// 纹理设置
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
if (TextureImage[0]) // 释放TextureImage 空间
{
if (TextureImage[0]->data)
{
free(TextureImage[0]->data);
}
free(TextureImage[0]);
}
return Status; //返回
}
//opengl 初始化
int InitGL(GLvoid)
{
if (!LoadGLTextures()) //载入纹理
{
return FALSE;
}
glEnable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
// 加入光照
GLfloat light0Pos[4] = {1.0,1.0,1.0,0.0};
GLfloat light1Pos[4] = {-1.0f,1.0f,-1.0f,0.0f};
GLfloat lightAmbient[4] ={0.0,0.5,1.0,1.0};
glLightfv(GL_LIGHT0,GL_POSITION,light0Pos);
glLightfv(GL_LIGHT1,GL_POSITION,light1Pos);
glLightfv(GL_LIGHT0,GL_AMBIENT,lightAmbient);
glLightfv(GL_LIGHT1,GL_AMBIENT,lightAmbient);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
return TRUE;
}
/核心所在///
GLvoid Camera(){
SetViewByMouse(); //通过鼠标进行旋转变换
if (KEY_DOWN(VK_LEFT)||KEY_DOWN('A')) { // 向左走
eyePosition.x+=(viewAtPosition.z-eyePosition.z)*speed;
eyePosition.z+= -(viewAtPosition.x-eyePosition.x)*speed;
}
if (KEY_DOWN(VK_RIGHT)||KEY_DOWN('D')){ //向右走
eyePosition.x-=(viewAtPosition.z-eyePosition.z)*speed;
eyePosition.z-= -(viewAtPosition.x-eyePosition.x)*speed;
}
if(theta>360) //显然角度不能大于360度
theta=0.0f;
if (KEY_DOWN(VK_PRIOR)) viewUp +=0.02f; //抬头看
if (KEY_DOWN(VK_NEXT)) viewUp -=0.02f; //低头看
if (viewUp> 0.5f) viewUp = 0.5f; //不要抬得太高
if (viewUp<-0.6f) viewUp =-0.6f;
if (KEY_DOWN(VK_UP)||KEY_DOWN('W')) //前进
{
eyePosition.x+= (viewAtPosition.x-eyePosition.x)*speed;
eyePosition.z+= (viewAtPosition.z-eyePosition.z)*speed;
}
if(KEY_DOWN(VK_DOWN)||KEY_DOWN('S')) //后退
{
eyePosition.x-=(viewAtPosition.x-eyePosition.x)*speed;
eyePosition.z-=(viewAtPosition.z-eyePosition.z)*speed;
}
if (eyePosition.x<-(MAP_SIZE-10)) //别走出边界了
eyePosition.x= -(MAP_SIZE-10);
if (eyePosition.x> (MAP_SIZE-10))
eyePosition.x= (MAP_SIZE-10);
if (eyePosition.z<-(MAP_SIZE-10))
eyePosition.z= -(MAP_SIZE-10);
if (eyePosition.z> (MAP_SIZE-10))
eyePosition.z= (MAP_SIZE-10);
// 新的参考点的位置
viewAtPosition.x = float(eyePosition.x + cos(theta));
viewAtPosition.z = float(eyePosition.z + sin(theta));
viewAtPosition.y = eyePosition.y;
gluLookAt( eyePosition.x,eyePosition.y ,eyePosition.z, // 视点位置
viewAtPosition.x, viewAtPosition.y + viewUp, viewAtPosition.z , //参考点位置
0.0,1.0,0.0); //向上
return ;
}
GLvoid SetViewByMouse(){ //通过此函数来通过鼠标控制旋转和上下观察
POINT mousePos; //鼠标位置
POINT middlePos; //屏幕中心位置
middlePos.x =SCREEN_WIDTH/2; // SCREEN_WIDHT 为屏幕宽度,全局变量,可改。
middlePos.y =SCREEN_HEIGHT/2;
GetCursorPos(&mousePos); //得到鼠标当前位置
if(mousePos.x==middlePos.x&&mousePos.y==middlePos.y) //如果鼠标没有动,返回。
return ;
SetCursorPos(middlePos.x, middlePos.y); //如果鼠标动了,则恢复到屏幕中心
theta += GLfloat(-middlePos.x +mousePos.x)/500; //旋转改变量
viewUp +=GLfloat( middlePos.y - mousePos.y)/500; //上下改变量
return ;
}
int RenderScene(GLvoid) // 画图的主函数
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清理屏幕和深度缓冲区
glLoadIdentity(); // 使当前矩阵为单位矩阵
Camera(); //视点变换函数
DrawScene(); //画环境
return TRUE; //返回
}
//画场景
GLvoid DrawScene()
{
glBegin(GL_QUADS); //地面 ,加入纹理坐标
glTexCoord2f(0.0f,0.0f); glVertex3f(-MAP_SIZE, 0.0f, -MAP_SIZE);
glTexCoord2f(1.0f,0.0f); glVertex3f(MAP_SIZE, 0.0f, -MAP_SIZE);
glTexCoord2f(1.0f,1.0f); glVertex3f(MAP_SIZE, 0.0f, MAP_SIZE);
glTexCoord2f(0.0f,1.0f); glVertex3f(-MAP_SIZE, 0.0F, MAP_SIZE);
glEnd();
//画四根柱子,作为边界(天边)
glPushMatrix() ;
glTranslatef(MAP_SIZE,59,MAP_SIZE);
//auxSolidSphere(0.2);
auxSolidCylinder(1.0f,60);
glTranslatef(-MAP_SIZE*2,0.0,0.0F);
auxSolidCylinder(1.0f,60);
glTranslatef(0.0f,0.0f,-MAP_SIZE*2);
auxSolidCylinder(1.0,60.0);
glTranslatef(2*MAP_SIZE,0.0f,0.0);
auxSolidCylinder(1.0f,60.0f);
glPopMatrix();
}
BOOL SetPFormat(HDC hDC)//检测安装OpenGL
{ int nPixelFormat; // 象素点格式
//hDC=hDC0;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图
PFD_SUPPORT_OPENGL | // 支持 OpenGL
PFD_DOUBLEBUFFER, // 双缓存模式
PFD_TYPE_RGBA, // RGBA 颜色模式
16, // 24 位颜色深度
0, 0, 0, 0, 0, 0, // 忽略颜色位
0, // 没有非透明度缓存
0, // 忽略移位位
0, // 无累加缓存
0, 0, 0, 0, // 忽略累加位
16, // 32 位深度缓存
0, // 无模板缓存
0, // 无辅助缓存
PFD_MAIN_PLANE, // 主层
0, // 保留
0, 0, 0 // 忽略层,可见性和损毁掩模
};
if (!(nPixelFormat = ChoosePixelFormat(hDC, &pfd)))
{ MessageBox(NULL,"没找到合适的显示模式","Error",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
SetPixelFormat(hDC,nPixelFormat,&pfd);//设置当前设备的像素点格式
hRC = wglCreateContext(hDC); //获取渲染描述句柄
wglMakeCurrent(hDC, hRC); //激活渲染描述句柄
return TRUE;
}
void CleanGL(){
wglMakeCurrent(hDC, NULL); //清除OpenGL
wglDeleteContext(hRC); //清除OpenGL
}
//以下copy自 NEHE TUTORIAL,用来创建窗口
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
// GLuint PixelFormat; // Holds The Results After Searching For A Match
WNDCLASS wc; // Windows Class Structure
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
WindowRect.left=(long)0; // Set Left Value To 0
WindowRect.right=(long)width; // Set Right Value To Requested Width
WindowRect.top=(long)0; // Set Top Value To 0
WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height
fullscreen=fullscreenflag; // Set The Global Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(hInstance,"crosshair"); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = "OpenGL"; // Set The Class Name
if (!RegisterClass(&wc)) // Attempt To Register The Window Class
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (fullscreen) // Attempt Fullscreen Mode?
{
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 = bits; // 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)
{
// If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode.
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By/nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Windowed Mode Selected. Fullscreen = FALSE
}
else
{
// Pop Up A Message Box Letting User Know The Program Is Closing.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; // Return FALSE
}
}
}
if (fullscreen) // Are We Still In Fullscreen Mode?
{
dwExStyle=WS_EX_APPWINDOW; // Window Extended Style
dwStyle=WS_POPUP; // Windows Style
ShowCursor(FALSE); // Hide Mouse Pointer
}
else
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle=WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size
// Create The Window
if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window
"OpenGL", // Class Name
title, // Window Title
dwStyle | // Defined Window Style
WS_CLIPSIBLINGS | // Required Window Style
WS_CLIPCHILDREN, // Required Window Style
0, 0, // Window Position
WindowRect.right-WindowRect.left, // Calculate Window Width
WindowRect.bottom-WindowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
hInstance, // Instance
NULL))) // Dont Pass Anything To WM_CREATE
{
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
ShowWindow(hWnd,SW_SHOW); // Show The Window
SetForegroundWindow(hWnd); // Slightly Higher Priority
SetFocus(hWnd); // Sets Keyboard Focus To The Window
ShowCursor(true);
return TRUE; // Success
}
// 消息处理函数
LRESULT CALLBACK WndProc( HWND hWnd, //窗口句柄
UINT Msg, // 消息
WPARAM wParam, // 附加信息1
LPARAM lParam) // 附加信息2
{
switch (Msg) //检测是何消息
{
case WM_CREATE:
hDC = GetDC(hWnd);
SetPFormat(hDC);
return 0;
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
case WM_SIZE:
{
ReSizeGL(LOWORD(lParam),HIWORD(lParam));
return 0;
}
}
return DefWindowProc(hWnd,Msg,wParam,lParam);
}
/程序入口
int WINAPI WinMain( HINSTANCE hInstance, //程序句柄
HINSTANCE hPrevInstance, // 上一个程序句柄(已经没用了)
LPSTR lpCmdLine, // 控制行参数
int nCmdShow) // 窗口显示模式
{
MSG msg;
if(MessageBox(NULL,"全屏模式吗?" ,"full screen mode", MB_YESNO)==IDNO)
fullscreen = false;
if (!CreateGLWindow("viewing system",800,600,32,fullscreen)) //创建窗口
{
return 0;
}
InitGL(); //初始化opengl
while(true) //主循环
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // 有消息吗?
{
if (msg.message==WM_QUIT) // 退出消息
break;
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // 调用处理函数WndProc()
}
if(KEY_DOWN(VK_ESCAPE)) //如果按ESC则退出程序
SendMessage(hWnd,WM_CLOSE,0,0);
RenderScene(); // 进行render
SwapBuffers(hDC); // 双缓冲
} //结束主循环
CleanGL(); //清理OpenGL
return (msg.wParam);
} // 结束程序
/