// WinMain.cpp : 定义应用程序的入口点。
//
#pragma region 头文件,宏定义
//include
#include "stdafx.h"
#include "WinMain.h"
#include <GL/glew.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <GL/GLAUX.H>
#include <gl/glut.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap.h"
//define
#define MAX_LOADSTRING 100
#define BITMAP_ID 0X4D42 //位图标识ID
#define MAP_X 32 //地图X轴的尺寸
#define MAP_Z 32
#define MAP_SCALE 20.0f
#define PI 3.14159
#pragma endregion over
#pragma region 变量定义
// 全局变量:
HDC g_HDC;
bool fullScreen = false;
bool keyPressd[256]; //被按下的键,其值为true
float angle = 0.0f; //视点角度
float radians = 0.0f; //以弧度表示的视点角度
float waterHeight = 154.0f; //水面高度
bool waterDir = true; //用于水的动画,true-向上,false-向下
//鼠标,视点变量
int mouseX, mouseY; //鼠标坐标
float cameraX, cameraY, cameraZ; //视点坐标
float lookX, lookY, lookZ ; //观察点坐标
//纹理信息
BITMAPINFOHEADER bitmapInfoHeader; //临时位图信息头
BITMAPINFOHEADER landInfo; //陆地纹理信息头
BITMAPINFOHEADER waterInfo; //水纹理信息头
unsigned char* imageData; //地形图图像数据
unsigned char* landTexture;//陆地纹理数据
unsigned char* waterTexture;//水纹理数据
unsigned int land; //陆地纹理对象,使用纹理对象,加速纹理的处理过程
unsigned int water; //水纹理对象
//地形数据
float terrain[MAP_X][MAP_Z][3]; //高程地形数据
#pragma endregion 变量定义完毕
#pragma region old code
#pragma endregion old over
void GLInitialize()
{
glEnable(GL_DEPTH_TEST);//被遮挡的表面剔除掉
glEnable(GL_SMOOTH);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_CULL_FACE); //不计算多边形背面
glFrontFace(GL_CCW);//多边形逆时针方向为正面
glEnable(GL_LIGHTING); //启用光照
将背景清理为黑色
glClearColor(.0f,.0f,.0f,.0f);
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void InitializeTerrain()
{
//遍历所有的高程点,并为每个点计算坐标
for (int z = 0; z < MAP_Z ; z++)
{
for (int x = 0; x < MAP_X; x++)
{
terrain[x][z][0] = float(x) * MAP_SCALE;
terrain[x][z][1] = (float)imageData[(z*MAP_Z + x ) * 3];
terrain[x][z][2] = -float(z) * MAP_SCALE;
}
}
}
bool LoadTextures()
{
//载入地形纹理数据
landTexture = LoadBitmapFile("green.bmp",&landInfo);
if (!landTexture)
{
return false;
}
//载入水纹理数据
waterTexture = LoadBitmapFile("water.bmp",&waterInfo);
if (! waterTexture)
{
return false;
}
//生成陆地纹理为mipmap
glGenTextures(1,&land);
glBindTexture(GL_TEXTURE_2D,land);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB,landInfo.biWidth,landInfo.biHeight,GL_RGB,GL_UNSIGNED_BYTE, landTexture);
//生成水纹理为mipmap
glGenTextures(1,&water);
glBindTexture(GL_TEXTURE_2D, water);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复或者夹持处理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, waterInfo.biWidth, waterInfo.biHeight, GL_RGB, GL_UNSIGNED_BYTE, waterTexture);
return true;
}
void Render()
{
radians = float(PI * (angle - 90.0f) / 180.0f);
//计算视点位置
cameraX = lookX + sin(radians) * mouseY;
cameraZ = lookZ + cos(radians) * mouseY;
cameraY = lookY + mouseY / 2.0f;
//将视点的观察点设为地形图的中央
lookX = (MAP_X * MAP_SCALE) / 2.0F;
lookY = 150.0f;
lookZ = -(MAP_Z * MAP_SCALE) / 2.0F;
//清理屏幕和深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//设置视点位置
gluLookAt(cameraX,cameraY,cameraZ,
lookX, lookY, lookZ,
0.0f, 1.0f, 0.0f);
//将当前纹理设为陆地纹理
glBindTexture(GL_TEXTURE_2D, land);
//虽然我们将遍历所有的地形数据点
//但是我们只会为沿着x轴的每一行数据点绘制一条三角形带
for (int z = 0; z < MAP_Z - 1; z++)
{
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < MAP_X - 1; x++)
{
//对于每个顶点,我们为其计算灰度颜色
//设置纹理坐标绘制顶点
//蛇形绘制顺序
//绘制顶点0
glColor3f( terrain[x][z][1] / 255.0f, terrain[x][z][1] / 255.0f, terrain[x][z][1] / 255.0f);
glTexCoord2f(0.0f,1.0f);
glVertex3f( terrain[x][z][0], terrain[x][z][1], terrain[x][z][2] );
//绘制顶点1
glTexCoord2f(1.0f, 1.0f);
glColor3f( terrain[x + 1][z][1] / 255.0f, terrain[x + 1][z][1] / 255.0f, terrain[x + 1][z][1] / 255.0f);
glVertex3f( terrain[x + 1][z][0], terrain[x + 1][z][1], terrain[x + 1][z][2] );
//绘制顶点2
glTexCoord2f(0.0f, 0.0f);
glColor3f( terrain[x][z + 1][1] / 255.0f, terrain[x][z + 1][1] / 255.0f, terrain[x][z + 1][1] / 255.0f);
glVertex3f( terrain[x][z + 1][0], terrain[x][z + 1][1], terrain[x][z + 1][2] );
//绘制顶点3
glTexCoord2f(0.0f, 1.0f);
glColor3f( terrain[x + 1][z + 1][1] / 255.0f, terrain[x + 1][z + 1][1] / 255.0f, terrain[x + 1][z + 1][1] / 255.0f);
glTexCoord2f(1.0f, .0f);
glVertex3f( terrain[x + 1][z + 1][0], terrain[x + 1][z + 1][1], terrain[x + 1][z + 1][2] );
}
glEnd();
}
//启用混合
glEnable(GL_BLEND);
//将深度缓存设为只读状态
glDepthMask(GL_FALSE);
//设置用于透明效果的混合因子
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
//将颜色设置为透明的蓝色
glColor4f(0.5f,0.5f,1.0f,0.7f);
glBindTexture(GL_TEXTURE_2D,water);
//将一个大的四边形表面绘制水,该四边形就是最下面的地,然后因为地有高低,所以水比地低的地方因为被遮挡,所以就看不到了。
glBegin(GL_QUADS);
//左下角
glTexCoord2f(.0f, .0f);
glVertex3f(terrain[0][0][0] , waterHeight, terrain[0][0][2]);
//右下角
glTexCoord2f(10.0f, .0f);
glVertex3f(terrain[MAP_X - 1][0][0] , waterHeight, terrain[MAP_X - 1][0][2]);
//右上角
glTexCoord2f(10.0f, 10.0f);
glVertex3f(terrain[MAP_X - 1][MAP_Z - 1][0] , waterHeight, terrain[MAP_X - 1][MAP_Z - 1][2]);
//左上角
glTexCoord2f(.0f, 10.0f);
glVertex3f(terrain[0][ MAP_Z - 1 ][0] , waterHeight, terrain[0][ MAP_Z - 1 ][2]);
glEnd();
//将深度缓存设置回正常的模式
glDepthMask(GL_TRUE);
//禁用混合
glDisable(GL_BLEND);
//动画水
if (waterHeight > 155.0f)
{
waterDir = false;
}
else if(waterHeight < 154.0f)
{
waterDir = true;
}
if(waterDir)
waterHeight += 0.01f;
else
waterHeight -= 0.01f;
glFlush();
SwapBuffers(g_HDC);//交换前后缓存
}
// 此代码模块中包含的函数的前向声明:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//此函数用于应用程序的初始化和设置
void Initilize()
{
glClearColor(.0f,.0f,.0f,.0f); //背景色清理为黑色
glShadeModel(GL_SMOOTH); //使用平滑底纹
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW); //多边形逆时针方向为正面
// glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE );
glEnable(GL_TEXTURE_2D);//启用2D纹理
imageData = LoadBitmapFile("terrain2.bmp", &bitmapInfoHeader);
//初始化地形数据载入纹理
InitializeTerrain();
LoadTextures();
}
void SetupPixelFormat(HDC hDC)
{
int nPixelFormat; //像素格式变量
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1, //版本号,总为1
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0,0,0,0,0,0,
0,
0,
0,
0,0,0,0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
//选择最匹配的像素格式,返回索引值
nPixelFormat = ChoosePixelFormat(hDC,&pfd);
//设置设备环境的像素格式
SetPixelFormat(hDC,nPixelFormat,&pfd);
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//第三和第四个参数实际是第二个参数的扩展,它们提供message无法提供的更多的信息细节
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HGLRC hRC; //绘制环境
static HDC hDC; //设备环境
int width,height; //窗口宽高
int oldMouseX, oldMouseY; //旧的鼠标坐标
switch (message)
{
case WM_CREATE: //创建窗口
hDC = GetDC(hwnd);
g_HDC = hDC;
SetupPixelFormat(hDC);
//创建绘制环境,并将其设置为当前绘制环境
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC,hRC);
return 0;
break;
case WM_CLOSE:
//取消对绘图环境的选定并将其删除
wglMakeCurrent(hDC,NULL);
wglDeleteContext(hRC);
//发送消息到消息队列
PostQuitMessage(0);
return 0;
break;
case WM_SIZE:
height = HIWORD(lParam); //得到窗口宽度和高度
width = LOWORD(lParam);
if (height == 0)
{
height = 1;
}
//重置窗口尺寸
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION); //设定投影矩阵
glLoadIdentity(); //复位投影矩阵
//计算窗口尺寸比例
gluPerspective(54.0f,(GLfloat)width/(GLfloat)height,1.0f,1000.0f);
glMatrixMode(GL_MODELVIEW); //设定模型视图矩阵
glLoadIdentity(); //复位模型视图矩阵
return 0;
break;
case WM_KEYDOWN:
keyPressd[wParam] = true;
return 0;
break;
case WM_KEYUP:
keyPressd[wParam] = false;
return 0 ;
break;
case WM_MOUSEMOVE:
oldMouseX = mouseX;
oldMouseY = mouseY;
//从windows系统得到鼠标坐标
mouseX = LOWORD(lParam);
mouseY = HIWORD(lParam);
//限定视点的活动范围
if(mouseY < 200)
mouseY = 200;
if (mouseY > 450)
{
mouseY = 450;
}
//鼠标向右移动
if ( ( mouseX - oldMouseX) > 0)
{
angle += 3.0f;
}
else if( ( mouseX - oldMouseX) < 0 )
angle -= 3.0f;
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
//return 0;
}
//计算三个点形成的面的法向量
//入口函数
int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow
)
{
MSG msg;
//HACCEL hAccelTable;
bool done; //应用程序退出标记
HWND hWnd;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINMAIN));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground =NULL;// (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_WINMAIN);
wcex.lpszClassName = "MyClass";//szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
if (!RegisterClassEx(&wcex))
{
return 0;
}
// 初始化全局字符串
// LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
// LoadString(hInstance, IDC_WINMAIN, szWindowClass, MAX_LOADSTRING);
hWnd = CreateWindowEx
(
NULL,
"MyClass",
"The OpenGL Game",
WS_OVERLAPPEDWINDOW,
100,100,
800,800,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,"create error","Game",NULL);
return FALSE;//创建窗口失败
}
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
// 执行应用程序初始化:
// 主消息循环:
//这里最好还是用peekmessage函数。该函数虽然与getmessage功能相似,但是更适合于游戏程序
//如果只是消息队列中没有消息,getmessage函数会使应用程序挂起直到出现一个消息。而使用运行时函数peekmessage时,即使
//消息队列中没有消息,也会让应用程序继续运行。这对于游戏程序来说很有用。
done = false;
Initilize();
while( !done )
{
PeekMessage(&msg,hWnd,NULL,NULL,PM_REMOVE);
if (msg.message == WM_QUIT)
{
done = true;
}
else
{
Render();
TranslateMessage(&msg); //翻译消息并分发到事件队列中
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}