Camera.h camera.cpp
CBMPLoader.h CBMPLoader.cpp
Font.h Font.cpp
GLFrame.h GLFrame.cpp
GLWindow.h GLWindow.cpp
SkyAndTerrain.h SkyAndTerrain.cpp
SkyBox.h SkyBox.cpp
stdafx.h stdafx.cpp
Terrain.h Terrain.cpp
Vector.h Vector.cpp
//========================================================
/**
* @file Camera.h
*
* 项目描述: 构造天空和地面
* 文件描述: 摄像机类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#ifndef __CAMERA_H__
#define __CAMERA_H__
#include "stdafx.h"
#include "Vector.h" /**< 包含向量类头文件 */
#include "GLFrame.h"
/** 摄像机类 */
class Camera
{
public:
/** 构造函数和析构函数 */
Camera();
~Camera();
/** 获得摄像机状态方法 */
Vector3 getPosition() { return m_Position; }
Vector3 getView() { return m_View; }
Vector3 getUpVector() { return m_UpVector; }
float getSpeed() { return m_Speed; }
/** 设置速度 */
void setSpeed(float speed)
{
m_Speed = speed;
}
/** 设置摄像机的位置, 观察点和向上向量 */
void setCamera(float positionX, float positionY, float positionZ,
float viewX, float viewY, float viewZ,
float upVectorX, float upVectorY, float upVectorZ);
/** 旋转摄像机方向 */
void rotateView(float angle, float X, float Y, float Z);
/** 根据鼠标设置摄像机观察方向 */
void setViewByMouse();
/** 左右摄像机移动 */
void yawCamera(float speed);
/** 前后移动摄像机 */
void moveCamera(float speed);
/** 放置摄像机 */
void setLook();
//得到摄像机指针
static Camera* GetCamera(void) { return m_pCamera;}
private:
/** 摄像机属性 */
static Camera *m_pCamera; /**< 当前全局摄像机指针 */
Vector3 m_Position; /**< 位置 */
Vector3 m_View; /**< 朝向 */
Vector3 m_UpVector; /**< 向上向量 */
float m_Speed; /**< 速度 */
};
#endif //__CAMERA_H__
//======================================================================
/**
* @file CBMPLoader.h
*
* 项目描述: 构造天空和地面
* 文件描述: 载入位图类
* 适用平台: Windows98/2000/NT/XP
*
*/
//======================================================================
#ifndef __CBMPLOADER_H__
#define __CBMPLOADER_H__
#include "stdafx.h"
#define BITMAP_ID 0x4D42 /**< 位图文件的标志 */
/** 位图载入类 */
class CBMPLoader
{
public:
CBMPLoader();
~CBMPLoader();
bool LoadBitmap(const char *filename); /**< 装载一个bmp文件 */
void FreeImage(); /**< 释放图像数据 */
unsigned int ID; /**< 生成纹理的ID号 */
int imageWidth; /**< 图像宽度 */
int imageHeight; /**< 图像高度 */
unsigned char *image; /**< 指向图像数据的指针 */
};
#endif //__CBMPLOADER_H__
//========================================================
/**
* @file Font.h
*
* 项目描述: 构造天空和地面
* 文件描述: 字体类
* 适用平台: Windows98/2000/NT/XP
*/
//========================================================
#ifndef __GLFONT_H__
#define __GLFONT_H__
#include "stdafx.h"
/** 定义字体类 */
class GLFont
{
public:
/** 构造函数和析构函数 */
GLFont();
~GLFont();
///成员方法
bool InitFont(); /**< 初始化字体 */
void PrintText(char *string, float x, float y); /**< 在(x,y)处输出string内容 */
protected:
HFONT m_hFont; /**< 字体句柄 */
};
#endif // __GLFONT_H__
//======================================================================
/**
* @file GLFrame.h
*
* 项目描述: 构造天空和地面
* 文件描述: 键盘类和程序框架类
* 适用平台: Windows98/2000/NT/XP
*
* 你必须在你的继承类中完成以下函数的实现
*
* GLApplication * GLApplication::Create(const char * class_name)
* 创建你的子类的一个实例
*
* bool Init();
* 执行所有的初始化工作,如果成功函数返回true
*
* void Uninit();
* 执行所有的卸载工作
*
* void Update(DWORD milliseconds);
* 执行所有的更新操作,传入的参数为两次操作经过的时间,以毫秒为单位
*
* void Draw();
* 执行所有的绘制操作
*/
//======================================================================
#ifndef __GLFRAME_H__
#define __GLFRAME_H__
#include "GLWindow.h" /**< 包含GLWindow.h头文件 */
/** 定义键盘类 */
class Keys
{
public:
/** 构造函数 */
Keys() { Clear(); }
/** 清空所有的按键信息 */
void Clear() { ZeroMemory(&m_KeyDown, sizeof(m_KeyDown)); }
/** 判断某个键是否按下 */
bool IsPressed(unsigned int key) { return (key < MAX_KEYS) ? (m_KeyDown[key] == true) : false; }
/** 设置某个键被按下 */
void SetPressed(unsigned int key) { if (key < MAX_KEYS) m_KeyDown[key] = true; }
/** 设置某个键被释放 */
void SetReleased(unsigned int key) { if (key < MAX_KEYS) m_KeyDown[key] = false; }
private:
static const unsigned int MAX_KEYS = 256;
bool m_KeyDown[MAX_KEYS]; /**< 保存256个按键的状态 */
};
/** 基本的程序类,继承它用来创建OpenGL程序 */
class GLApplication
{
public:
/** 创建一个全局的Create函数,这个函数必须被继承类实现 */
static GLApplication * Create(const char * class_name); /**< 创建你自己的子类 */
/** 虚析构函数 */
virtual ~GLApplication() {};
protected:
/** 下面的函数必须被继承类实现,完成基本的OpenGL渲染过程 */
virtual bool Init() = 0; /**< OpenGL的初始化 */
virtual void Uninit() = 0; /**< OpenGL的卸载 */
virtual void Update(DWORD milliseconds) = 0; /**< 执行OpenGL程序的更新 */
virtual void Draw() = 0; /**< 绘制OpenGL场景 */
/** 通用的函数 */
void ToggleFullscreen(); /**< 切换 全屏/窗口模式 */
void TerminateApplication(); /**< 结束程序 */
void ResizeDraw(bool enable) { m_ResizeDraw = enable; } /**< 设置在窗口改变大小的时候,可以绘制 */
Keys m_Keys; /**< 按键类 */
/** 构造函数 */
GLApplication(const char * class_name);
private:
/** 程序的主循环 */
friend int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
int Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
/** 消息处理回调函数 */
friend LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT Message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static const UINT WM_TOGGLEFULLSCREEN = (WM_USER + 1); /**< 自定义消息,在切换窗口模式的时候发送 */
GLWindow m_Window; /**< Window类 */
const char* m_ClassName; /**< 程序名 */
bool m_IsProgramLooping; /**< 程序循环标记,如果为false,则退出程序 */
bool m_CreateFullScreen; /**< 若为true,则创建全屏模式 */
bool m_IsVisible; /**< 窗口是否可见 */
bool m_ResizeDraw; /**< 是否在改变大小时,调用了绘制函数 */
DWORD m_LastTickCount; /**< 上一次计时器的值 */
};
#endif // __GLFRAMEWORK_H__
//========================================================
/**
* @file GLWindows.h
*
* 项目描述: 构造天空和地面
* 文件描述: 程序窗口类
* 适用平台: Windows98/2000/NT/XP
*/
//========================================================
#ifndef __GLWINDOW_H__
#define __GLWINDOW_H__
#include "stdafx.h" /**< 包含stdafx.h头文件 */
/** windows窗口类 */
class GLWindow
{
public:
/** 构造函数 */
GLWindow();
/** 创建windows窗口 */
bool Create(const char * window_title, const char * class_name, bool fullscreen,HINSTANCE h_instance, LPVOID lpParam);
/** 删除OpenGL窗口 */
void Destroy();
/** 改变窗口的显示设置 */
bool ChangeScreenSetting();
/** 当窗口大小改变时,通知OpenGL调整大小 */
void ReshapeGL();
/** Swap Buffers (Double Buffering) */
void SwapBuffers() { ::SwapBuffers(m_hDC); }
/** 设置窗口左上角的位置 */
void SetPosX(int x);
void SetPosX(unsigned short x) { SetPosX((signed short)x); }
void SetPosY(int y);
void SetPosY(unsigned short y) { SetPosY((signed short)y); }
/** 返回窗口的大小 */
int GetWidth();
int GetHeight();
/** 设置窗口的大小 */
void SetWidth(int width);
void SetHeight(int height);
/** 返回窗口左上角的位置 */
int GetPosX();
int GetPosY();
/** 设置窗口的颜色位深 */
void SetHiColor() { m_BitsPerPixel = 16; }
void SetTrueColor() { m_BitsPerPixel = 32; }
/** 重载运算符,可以让GL_Window m_Window声明后的m_Window作为窗口句柄使用 */
operator HWND() { return m_hWnd; }
private:
HWND m_hWnd; /**< 窗口句柄 */
HDC m_hDC; /**< 设备描述表 */
HGLRC m_hRC; /**< OpenGL渲染描述表 */
int m_WindowPosX; /**< 窗口的左上角的X位置 */
int m_WindowPosY; /**< 窗口的左上角的Y位置 */
int m_WindowWidth; /**< 窗口的宽度 */
int m_WindowHeight; /**< 窗口的高度 */
int m_ScreenWidth; /**< 全屏的宽度 */
int m_ScreenHeight; /**< 全屏的高度 */
int m_BitsPerPixel; /**< 颜色位深 */
bool m_IsFullScreen; /**< 是否全屏 */
};
#endif // __GLWINDOW_H__
//=========================================================================
/**
* @file SkyAndTerrain.h
*
* 项目描述: 构造天空和地面
* 文件描述: 具体实例类
* 适用平台: Windows98/2000/NT/XP
*
* 在这个类中您必须重载如下几个虚函数
*
* virtual bool Init();
* 执行所有的初始化工作,如果成功函数返回true
*
* virtual void Uninit();
* 执行所有的卸载工作
*
* virtual void Update(DWORD milliseconds);
* 执行所有的更新操作,传入的参数为两次操作经过的时间,以毫秒为单位
*
* virtual void Draw();
* 执行所有的绘制操作
*/
//=========================================================================
#ifndef __SKY_AND_TERRAIN_H__
#define __SKY_AND_TERRAIN_H__
#include "stdafx.h"
#include "CBMPLoader.h"
#include "GLFrame.h" /**< 包含基本的框架类 */
#include "Font.h"
#include "Camera.h"
#include "SkyBox.h"
#include "Terrain.h"
/** 从GL_Application派生出一个子类 */
class SkyTerrain : GLApplication
{
public:
bool Init(); /**< 执行所有的初始化工作,如果成功函数返回true */
void Uninit(); /**< 执行所有的卸载工作 */
void Update(DWORD milliseconds); /**< 执行所有的更新操作,传入的参数为两次操作经过的时间,以毫秒为单位 */
void Draw(); /**< 执行所有的绘制操作 */
void UpdateCamera(); /**< 更新摄像机 */
void CaculateFrameRate(); /**< 计算帧速 */
void PrintText(); /**< 输出文字信息 */
private:
friend class GLApplication; /**< 父类为它的一个友元类,可以用来创建程序的实例,见函数GL_Application * GL_Application::Create(const char * class_name) */
SkyTerrain(const char * class_name); /**< 构造函数 */
/** 用户自定义的程序变量 */
CTerrain m_Terrain; /**< 地形类 */
CSkyBox m_SkyBox; /**< 天空类 */
GLFont m_Font; /**< 字体类 */
Camera m_Camera; /**< 摄像机类 */
float m_Fps; /**< 帧速 */
bool m_RenderMode; /**< 绘制模式 */
bool sp; /**< 空格键是否释放 */
};
#endif // __SKY_AND_TERRAIN_H__
//========================================================
/**
* @file SkyBox.h
*
* 项目描述: 构造天空和地面
* 文件描述: 天空盒类
* 适用平台: Windows98/2000/NT/XP
*
*
*/
//========================================================
#ifndef __SKYBOX_H__
#define __SKYBOX_H__
#include "stdafx.h"
#include "CBMPLoader.h"
#include "Vector.h"
#include "Camera.h"
#define GL_CLAMP_TO_EDGE 0x812F
/** 天空盒类 */
class CSkyBox
{
public:
/** 构造函数 */
CSkyBox();
~CSkyBox();
/** 初始化 */
bool init();
/** 渲染 */
void render();
private:
CBMPLoader m_texture[5]; /**< 天空盒纹理 */
Vector3 m_CameraPos; /**< 当前摄像机位置 */
float length; /**< 长度 */
float width; /**< 宽度 */
float height; /**< 高度 */
float yRot; /**< 绕Y轴旋转 */
};
#endif ///__SKYBOX_H__
/*===================================================
@file stdafx.h
*/
#ifndef __STDAFX_H__
#define __STDAFX_H__
/** 包含常用的头文件 */
#include
#include
#include
#include
/** 包含gl头文件 */
#include
#include
#include
/** 包含OpenGL链接库文件 */
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
/**< 禁止编译器出现类型转换的警告 */
#pragma warning(disable: 4311)
#pragma warning(disable: 4312)
#pragma warning(disable: 4244)
#pragma warning(disable: 4018)
#pragma warning(disable: 4267)
/** 定义地面网格 */
const unsigned int MAP_WIDTH = 1024;
const unsigned int CELL_WIDTH = 16;
#endif
//========================================================
/**
* @file Terrain.h
*
* 项目描述: 构造天空和地面
* 文件描述: 地形类
* 适用平台: Windows98/2000/NT/XP
* 修改日期: 2007-04-02 加入了细节纹理,采用了多重纹理技术,使地表更富有细节感
* 2007-05-19 给地形加入了简单雾效
*
*/
//========================================================
#ifndef __TERRAIN_H__
#define __TERRAIN_H__
#include "stdafx.h"
#include "CBMPLoader.h"
#include "camera.h"
/** 地形类 */
class CTerrain
{
public:
/** 构造函数 */
CTerrain();
~CTerrain();
/** 初始化地形 */
bool init();
/** 渲染地形 */
void render();
/** 设置是否使用细节纹理标志 */
void setDetail(bool _bDetail) { m_bDetail = _bDetail;}
/** 获得地面高度 */
float getAveHeight(float x, float z);
/** 得到当前Terrain指针 */
static CTerrain* GetTerrainPointer() { return m_pTerrain;}
private:
/** 设置地形的大小 */
void setSize(unsigned int width, unsigned int cell);
/** 载入'.raw'高度图 */
bool loadRawFile(const char* fileName);
/** 装载纹理 */
bool loadTexture();
/** 获得点(x,y)的高度信息 */
int getHeight(int x, int y);
/** 设置纹理坐标 */
void setTexCoord(float x, float z);
/** 设置雾效 */
void initFog();
public:
static CTerrain* m_pTerrain; /**< 当前Terrain指针 */
unsigned int m_nWidth; /**< 地形网格数 */
unsigned int m_nCellWidth; /**< 每一格宽度 */
BYTE* m_pHeightMap; /**< 存放高度信息 */
CBMPLoader m_texture[2]; /**< 地面纹理和细节纹理 */
bool m_bDetail; /**< 是否使用细节纹理标志 */
int m_DetailScale; /**< 缩放比例 */
};
#endif //__TERRAIN_H__
//========================================================
/**
* @file Vector.h
*
* 项目描述: 构造天空和地面
* 文件描述: 向量类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#ifndef __VECTOR_H__
#define __VECTOR_H__
#include "stdafx.h"
/** 向量类 */
class Vector3
{
public:
/** 构造函数 */
Vector3() { x = 0.0; y = 0.0; z = 0.0; }
Vector3( float xx, float yy, float zz)
{
x = xx;
y = yy;
z = zz;
}
Vector3(const Vector3& vec)
{
x = vec.x;
y = vec.y;
z = vec.z;
}
/** 成员函数 */
inline float length(); /**< 计算向量长度 */
Vector3 normalize(); /**< 单位化向量 */
float dotProduct(const Vector3& v); /**< 计算点积 */
Vector3 crossProduct(const Vector3& v); /**< 计算叉积 */
/** 重载操作符 */
Vector3 operator + (const Vector3& v);
Vector3 operator - (const Vector3& v);
Vector3 operator * (float scale);
Vector3 operator / (float scale);
Vector3 operator - ();
public:
float x,y,z;
};
#endif //__VECTOR_H__
//========================================================
/**
* @file Camera.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 摄像机类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#include "stdafx.h"
#include "Camera.h" /**< 包含摄像机头文件 */
#include "Vector.h" /**< 包含向量类 */
#include "GLFrame.h"
Camera* Camera::m_pCamera = NULL;
/** 构造函数 */
Camera::Camera()
{
/** 初始化向量值 */
Vector3 zero = Vector3(0.0, 0.0, 0.0);
Vector3 view = Vector3(0.0, 1.0, 0.5);
Vector3 up = Vector3(0.0, 0.0, 1.0);
/** 初始化摄像机 */
m_Position = zero;
m_View = view;
m_UpVector = up;
m_Speed = 0.1f;
m_pCamera = this;
}
Camera::~Camera()
{
}
/** 设置摄像机的位置,朝向和向上向量 */
void Camera::setCamera( float positionX, float positionY, float positionZ,
float viewX, float viewY, float viewZ,
float upVectorX, float upVectorY, float upVectorZ)
{
/** 构造向量 */
Vector3 Position = Vector3(positionX, positionY, positionZ);
Vector3 View = Vector3(viewX, viewY, viewZ);
Vector3 UpVector = Vector3(upVectorX, upVectorY, upVectorZ);
/** 设置摄像机 */
m_Position = Position;
m_View = View;
m_UpVector = UpVector;
}
/** 旋转摄像机方向 */
void Camera::rotateView(float angle, float x, float y, float z)
{
Vector3 newView;
/** 计算方向向量 */
Vector3 view = m_View - m_Position;
/** 计算 sin 和cos值 */
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);
/** 计算旋转向量的x值 */
newView.x = (cosTheta + (1 - cosTheta) * x * x) * view.x;
newView.x += ((1 - cosTheta) * x * y - z * sinTheta) * view.y;
newView.x += ((1 - cosTheta) * x * z + y * sinTheta) * view.z;
/** 计算旋转向量的y值 */
newView.y = ((1 - cosTheta) * x * y + z * sinTheta) * view.x;
newView.y += (cosTheta + (1 - cosTheta) * y * y) * view.y;
newView.y += ((1 - cosTheta) * y * z - x * sinTheta) * view.z;
/** 计算旋转向量的z值 */
newView.z = ((1 - cosTheta) * x * z - y * sinTheta) * view.x;
newView.z += ((1 - cosTheta) * y * z + x * sinTheta) * view.y;
newView.z += (cosTheta + (1 - cosTheta) * z * z) * view.z;
/** 更新摄像机的方向 */
m_View = m_Position + newView;
}
/** 用鼠标旋转摄像机 */
void Camera::setViewByMouse()
{
POINT mousePos; /**< 保存当前鼠标位置 */
int middleX = GetSystemMetrics(SM_CXSCREEN) >> 1; /**< 得到屏幕宽度的一半 */
int middleY = GetSystemMetrics(SM_CYSCREEN) >> 1; /**< 得到屏幕高度的一半 */
float angleY = 0.0f; /**< 摄像机左右旋转角度 */
float angleZ = 0.0f; /**< 摄像机上下旋转角度 */
static float currentRotX = 0.0f;
/** 得到当前鼠标位置 */
GetCursorPos(&mousePos);
ShowCursor(TRUE);
/** 如果鼠标没有移动,则不用更新 */
if( (mousePos.x == middleX) && (mousePos.y == middleY) )
return;
/** 设置鼠标位置在屏幕中心 */
SetCursorPos(middleX, middleY);
/**< 得到鼠标移动方向 */
angleY = (float)( (middleX - mousePos.x) ) / 1000.0f;
angleZ = (float)( (middleY - mousePos.y) ) / 1000.0f;
static float lastRotX = 0.0f; /**< 用于保存旋转角度 */
lastRotX = currentRotX;
/** 跟踪摄像机上下旋转角度 */
currentRotX += angleZ;
/** 如果上下旋转弧度大于1.0,我们截取到1.0并旋转 */
if(currentRotX > 1.0f)
{
currentRotX = 1.0f;
/** 根据保存的角度旋转方向 */
if(lastRotX != 1.0f)
{
/** 通过叉积找到与旋转方向垂直的向量 */
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize();
///旋转
rotateView( 1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
/** 如果旋转弧度小于-1.0,则也截取到-1.0并旋转 */
else if(currentRotX < -1.0f)
{
currentRotX = -1.0f;
if(lastRotX != -1.0f)
{
/** 通过叉积找到与旋转方向垂直的向量 */
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize();
///旋转
rotateView( -1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
/** 否则就旋转angleZ度 */
else
{
/** 找到与旋转方向垂直向量 */
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize();
///旋转
rotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);
}
/** 总是左右旋转摄像机 */
rotateView(angleY, 0, 1, 0);
}
/** 左右移动摄像机 */
void Camera::yawCamera(float speed)
{
Vector3 yaw;
Vector3 oldPos,oldView;
Vector3 cross = m_View - m_Position;
oldPos = m_Position;
oldView = m_View;
cross = cross.crossProduct(m_UpVector);
///归一化向量
yaw = cross.normalize();
m_Position.x += yaw.x * speed;
m_Position.z += yaw.z * speed;
m_View.x += yaw.x * speed;
m_View.z += yaw.z * speed;
/** 进行边界检查和限定 */
if(m_View.x > MAP_WIDTH - 20 || m_View.x < 2*CELL_WIDTH)
{
m_Position.x = oldPos.x ;
m_View.x = oldView.x;
}
if(m_View.z > MAP_WIDTH - 20 || m_View.z < 2*CELL_WIDTH)
{
m_Position.z = oldPos.z ;
m_View.z = oldView.z;
}
}
/** 前后移动摄像机 */
void Camera::moveCamera(float speed)
{
/** 计算方向向量 */
Vector3 vector = m_View - m_Position;
vector = vector.normalize(); /**< 单位化 */
Vector3 oldPos,oldView;
oldPos = m_Position;
oldView = m_View;
/** 更新摄像机 */
m_Position.x += vector.x * speed; /**< 根据速度更新位置 */
m_Position.z += vector.z * speed;
m_Position.y += vector.y * speed;
m_View.x += vector.x * speed; /**< 根据速度更新方向 */
m_View.y += vector.y * speed;
m_View.z += vector.z * speed;
/** 进行边界检查和限定 */
if(m_View.x > MAP_WIDTH - 20 || m_View.x < 2*CELL_WIDTH)
{
m_Position.x = oldPos.x ;
m_View.x = oldView.x;
}
if(m_View.z > MAP_WIDTH - 20 || m_View.z < 2*CELL_WIDTH)
{
m_Position.z = oldPos.z ;
m_View.z = oldView.z;
}
}
/** 设置视点 */
void Camera::setLook()
{
/** 设置视口 */
gluLookAt(m_Position.x, m_Position.y, m_Position.z,
m_View.x, m_View.y, m_View.z,
m_UpVector.x, m_UpVector.y, m_UpVector.z);
}
//======================================================================
/**
* @file CBMPLoader.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 载入位图类
* 适用平台: Windows98/2000/NT/XP
*/
//======================================================================
#include"CBMPLoader.h" /**< 包含头文件 */
/** 构造函数 */
CBMPLoader::CBMPLoader()
{
/** 初始化成员值为0 */
image = 0;
imageWidth = 0;
imageHeight = 0;
}
/** 析构函数 */
CBMPLoader::~CBMPLoader()
{
FreeImage(); /**< 释放图像数据占据的内存 */
}
/** 装载一个位图文件 */
bool CBMPLoader::LoadBitmap(const char *file)
{
FILE *pFile = 0; /**< 文件指针 */
/** 创建位图文件信息和位图文件头结构 */
BITMAPINFOHEADER bitmapInfoHeader;
BITMAPFILEHEADER header;
unsigned char textureColors = 0;/**< 用于将图像颜色从BGR变换到RGB */
/** 打开文件,并检查错误 */
pFile = fopen(file, "rb");
if(pFile == 0) return false;
/** 读入位图文件头信息 */
fread(&header, sizeof(BITMAPFILEHEADER), 1, pFile);
/** 检查该文件是否为位图文件 */
if(header.bfType != BITMAP_ID)
{
fclose(pFile); /**< 若不是位图文件,则关闭文件并返回 */
return false;
}
/** 读入位图文件信息 */
fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
/** 保存图像的宽度和高度 */
imageWidth = bitmapInfoHeader.biWidth;
imageHeight = bitmapInfoHeader.biHeight;
/** 确保读取数据的大小 */
if(bitmapInfoHeader.biSizeImage == 0)
bitmapInfoHeader.biSizeImage = bitmapInfoHeader.biWidth *
bitmapInfoHeader.biHeight * 3;
/** 将指针移到数据开始位置 */
fseek(pFile, header.bfOffBits, SEEK_SET);
/** 分配内存 */
image = new unsigned char[bitmapInfoHeader.biSizeImage];
/** 检查内存分配是否成功 */
if(!image) /**< 若分配内存失败则返回 */
{
delete[] image;
fclose(pFile);
return false;
}
/** 读取图像数据 */
fread(image, 1, bitmapInfoHeader.biSizeImage, pFile);
/** 将图像颜色数据格式进行交换,由BGR转换为RGB */
for(int index = 0; index < (int)bitmapInfoHeader.biSizeImage; index+=3)
{
textureColors = image[index];
image[index] = image[index + 2];
image[index + 2] = textureColors;
}
fclose(pFile); /**< 关闭文件 */
return true; /**< 成功返回 */
}
/** 释放内存 */
void CBMPLoader::FreeImage()
{
/** 释放分配的内存 */
if(image)
{
delete[] image;
image = 0;
}
}
/**
* @file GLFont.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 字体类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#include "Font.h"
GLFont::GLFont()
{
}
/** 析构函数 */
GLFont::~GLFont()
{
if(m_hFont)
DeleteObject(m_hFont); /**< 删除字体句柄 */
}
/** 初始化字体 */
bool GLFont::InitFont()
{
/**< 创建字体 */
m_hFont = CreateFont(-16, /**< 字体高度 */
0, /**< 字体宽度 */
0, /**< 字体的旋转角度 Angle Of Escapement */
0, /**< 字体底线的旋转角度Orientation Angle */
FW_BOLD, /**< 字体的重量 */
FALSE, /**< 是否使用斜体 */
FALSE, /**< 是否使用下划线 */
FALSE, /**< 是否使用删除线 */
GB2312_CHARSET, /**< 设置字符集 */
OUT_TT_PRECIS, /**< 输出精度 */
CLIP_DEFAULT_PRECIS, /**< 裁剪精度 */
ANTIALIASED_QUALITY, /**< 输出质量 */
FF_DONTCARE|DEFAULT_PITCH, /**< Family And Pitch */
"宋体"); /**< 字体名称 */
if(!m_hFont)
return false; /**< 创建字体失败则返回 */
/** 初始化成功则返回true */
return true;
}
/** 在指定位置输出字符串 */
void GLFont::PrintText(char *string, float x, float y)
{
HBITMAP hBitmap,hOldBmp; /**< 定义两个位图句柄 */
BITMAP bm; /**< 位图结构变量 */
SIZE size; /**< 位图尺寸 */
GLboolean lp,tp;
HDC hDC = ::CreateCompatibleDC(0); /**< 暂存设备场景 */
glGetBooleanv(GL_LIGHTING,&lp); /**< 查看场景中是否有光照 */
glGetBooleanv(GL_TEXTURE_2D,&tp);/**< 查看场景中是否启用纹理 */
/** 保存和设置一些属性 */
glLoadIdentity();
glPushMatrix();
glTranslatef(0,0,-10.0f);
glDisable(GL_LIGHTING); /**< 关闭光照 */
glDisable(GL_TEXTURE_2D); /**< 关闭纹理 */
glDisable(GL_DEPTH_TEST); /**< 关闭深度测试 */
SelectObject(hDC, m_hFont); /**< 选择字体 */
::GetTextExtentPoint32(hDC, string, strlen(string), &size);/**< 获取字符位图大小 */
hBitmap = CreateBitmap(size.cx, size.cy,1, 1, NULL); /**< 创建与hDC相关单色位图 */
hOldBmp = (HBITMAP)SelectObject(hDC,hBitmap); /**< 选择位图 */
SetBkColor(hDC, RGB(0, 0, 0)); /**< 背景色为黑色 */
SetTextColor(hDC, RGB(255, 255, 255)); /**< 字体颜色白色 */
SetBkMode(hDC, OPAQUE); /**< 用当前的背景颜色填充背景 */
TextOut(hDC, 0, 0, string, strlen(string)); /**< 输出文字到暂存hDC */
/** 获得相关位图数据结构 */
GetObject(hBitmap, sizeof(bm), &bm);
size.cx = (bm.bmWidth + 31) & (~31); /**< 边缘对齐 */
size.cy = bm.bmHeight;
int bufsize = size.cx * size.cy/8; /**< 图形数据长度 */
/** 定义单色位图结构 */
struct {
BITMAPINFOHEADER bih;
RGBQUAD col[2];
}bic;
/** 获取单色位图结构信息 */
BITMAPINFO *binf = (BITMAPINFO *)&bic;
binf->bmiHeader.biSize = sizeof(binf->bmiHeader); /**< 修改结构信息 */
binf->bmiHeader.biWidth = bm.bmWidth;
binf->bmiHeader.biHeight = bm.bmHeight;
binf->bmiHeader.biPlanes = 1;
binf->bmiHeader.biBitCount = 1; /**< 单色 */
binf->bmiHeader.biCompression = BI_RGB; /**< 颜色方式 */
binf->bmiHeader.biSizeImage = bufsize;
binf->bmiHeader.biXPelsPerMeter = 1;
binf->bmiHeader.biYPelsPerMeter = 1;
binf->bmiHeader.biClrUsed = 0;
binf->bmiHeader.biClrImportant = 0;
/** 定义图形数据块 */
UCHAR* pBmpBits = new UCHAR[bufsize];
memset(pBmpBits, 0, sizeof(UCHAR)*bufsize);
/** 将设备无关数据保存在pBmpBits指向的数据块中 */
::GetDIBits(hDC, hBitmap, 0, bm.bmHeight, pBmpBits, binf,DIB_RGB_COLORS);
/** 显示字符串 */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /**< 指定像素存储模式 */
glRasterPos2f(x,y); /**< 定位 */
glBitmap(size.cx, size.cy, 0.0, 0.0, 0.0, 0.0, pBmpBits); /**< 位图显示 */
delete pBmpBits; /**< 删除指针 */
SelectObject(hDC, hOldBmp); /**< 恢复原来位图信息 */
DeleteObject(hBitmap);
::DeleteDC(hDC);
/** 恢复一些属性 */
if(lp)
glEnable(GL_LIGHTING); /**< 启用光照 */
if(tp)
glEnable(GL_TEXTURE_2D); /**< 启用纹理 */
glEnable(GL_DEPTH_TEST); /**< 启用深度测试 */
glPopMatrix();
}
//======================================================================
/**
* @file GLFrame.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 键盘类和程序框架类
* 适用平台: Windows98/2000/NT/XP
*
*
* 你必须在你的继承类中完成以下函数的实现
*
* GLApplication * GLApplication::Create(const char * class_name)
* 创建你的子类的一个实例
*
* bool Init();
* 执行所有的初始化工作,如果成功函数返回true
*
* void Uninit();
* 执行所有的卸载工作
*
* void Update(DWORD milliseconds);
* 执行所有的更新操作,传入的参数为两次操作经过的时间,以毫秒为单位
*
* void Draw();
* 执行所有的绘制操作
*/
//======================================================================
#include "stdafx.h"
#include "GLFrame.h" /**< 包含GLFrame.h头文件 */
/** 主程序入口 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int ret = -1;
GLApplication * appl = GLApplication::Create("OpenGL"); /**< 创建程序类 */
if (appl != 0)
{
ret = appl->Main(hInstance, hPrevInstance, lpCmdLine, nCmdShow);/**< 执行程序主循环 */
delete appl; /**< 删除程序类(在继承类中,使用GL_Example * example = new GL_Example(class_name);分配了一块内存)*/
}
else
{ /**< 创建程序出错 */
MessageBox(HWND_DESKTOP, "创建程序出错", "Error", MB_OK | MB_ICONEXCLAMATION);
}
return ret;
}
/** 处理窗口消息 */
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LONG user_data = GetWindowLong(hWnd, GWL_USERDATA); /**< 返回用户自定义的32位的程序附加值 */
if (user_data == 0)
{
/// 如果程序第一次运行
if (uMsg == WM_CREATE) /**< 处理窗口创建消息 */
{
/// 返回窗口结构的指针,它保存刚创建的程序实例的类
CREATESTRUCT * creation = reinterpret_cast
/// 获得程序实例的指针
GLApplication * appl = reinterpret_cast
/// 保存程序实例的指针为用户自定义的程序附加值
SetWindowLong(hWnd, GWL_USERDATA, reinterpret_cast
appl->m_IsVisible = true; /**< 设置程序可见 */
return 0; /**< 返回 */
}
}
else
{
/// 如果不是第一次窗口,返回程序实例的指针
GLApplication * appl = reinterpret_cast
return appl->Message(hWnd, uMsg, wParam, lParam); /**< 调用程序实例自己的消息处理函数 */
}
return DefWindowProc(hWnd, uMsg, wParam, lParam); /**< 调用默认的窗口消息处理函数 */
}
/** 构造函数 */
GLApplication::GLApplication(const char * class_name)
{
m_ClassName = class_name; /**< 保存类名 */
m_IsProgramLooping = true; /**< 设置程序循环为true */
m_CreateFullScreen = true; /**< 使用全屏模式 */
m_IsVisible = false; /**< 不可见 */
m_ResizeDraw = false; /**< 在窗口改变大小的时候,不可绘制 */
m_LastTickCount = 0;
}
void GLApplication::ToggleFullscreen() /**< 切换 全屏/窗口模式 */
{
PostMessage(m_Window, WM_TOGGLEFULLSCREEN, 0, 0); /**< 发送自定的切换消息 */
}
void GLApplication::TerminateApplication() /**< 结束程序 */
{
PostMessage(m_Window, WM_QUIT, 0, 0); /**< 发送退出消息 */
m_IsProgramLooping = false; /**< 停止程序循环 */
}
/** 消息循环 */
LRESULT GLApplication::Message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) /**< 处理不同的Windows消息 */
{
case WM_SYSCOMMAND: /**< 截获系统命令 */
switch (wParam)
{
case SC_SCREENSAVE: /**< 截获屏幕保护启动命令 */
case SC_MONITORPOWER: /**< 截获显示其省电模式启动命令 */
return 0; /**< 不启用这两个命令 */
break;
}
break; /**< 退出 */
case WM_CLOSE: /**< 关闭窗口 */
TerminateApplication(); /**< 调用TerminateApplication函数 */
return 0;
break;
case WM_EXITMENULOOP:
case WM_EXITSIZEMOVE:
m_LastTickCount = GetTickCount(); /**< 更新计数器的值 */
return 0;
break;
case WM_MOVE:
m_Window.SetPosX(LOWORD(lParam)); /**< 更新鼠标的坐标 */
m_Window.SetPosY(HIWORD(lParam));
return 0;
break;
case WM_PAINT:
if (m_ResizeDraw == true) /**< 如果需要重绘 */
{
m_Window.ReshapeGL(); /**< 重新设置窗口的大小 */
Draw(); /**< 重新绘制 */
m_Window.SwapBuffers(); /**< 交换前后帧缓存 */
}
break;
case WM_SIZING: /**< 窗口正在改变大小 */
{
RECT * rect = (RECT *)lParam;
m_Window.SetWidth(rect->right - rect->left); /**< 设置窗口宽度 */
m_Window.SetHeight(rect->bottom - rect->top); /**< 设置窗口高度 */
return TRUE;
}
break;
case WM_SIZE: /**< 窗口改变大小后 */
switch (wParam) /**< 处理不同的窗口状态 */
{
case SIZE_MINIMIZED: /**< 是否最小化? */
m_IsVisible = false; /**< 如果是,则设置不可见 */
return 0;
break;
case SIZE_MAXIMIZED: /**< 窗口是否最大化? */
case SIZE_RESTORED: /**< 窗口被还原? */
m_IsVisible = true; /**< 设置为可见 */
m_Window.SetWidth(LOWORD(lParam)); /**< 设置窗口宽度 */
m_Window.SetHeight(HIWORD(lParam)); /**< 设置窗口高度 */
m_Window.ReshapeGL(); /**< 改变窗口大小 */
m_LastTickCount = GetTickCount(); /**< 更新计数器的值 */
return 0;
break;
}
break;
case WM_KEYDOWN: /**< 更新按键信息 */
m_Keys.SetPressed(wParam);
return 0;
break;
case WM_KEYUP: /**< 更新释放键信息 */
m_Keys.SetReleased(wParam);
return 0;
break;
case WM_TOGGLEFULLSCREEN: /**< 切换 全屏/窗口模式 */
m_CreateFullScreen = !m_CreateFullScreen;
PostMessage(hWnd, WM_QUIT, 0, 0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam); /**< 调用默认的窗口消息处理函数 */
}
/** 程序的主循环 */
int GLApplication::Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
/// 注册一个窗口
WNDCLASSEX windowClass; /**< 窗口类 */
ZeroMemory(&windowClass, sizeof(WNDCLASSEX)); /**< 清空结构为0 */
windowClass.cbSize = sizeof(WNDCLASSEX); /**< 窗口结构的大小 */
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; /**< 设置窗口类型为,移动时重画,并为窗口取得DC */
windowClass.lpfnWndProc = (WNDPROC)(WindowProc); /**< WndProc处理消息 */
windowClass.hInstance = hInstance; /**< 设置实例 */
windowClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE); /**< 设置背景 */
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); /**< 载入光标 */
windowClass.lpszClassName = m_ClassName; /**< 设置类名 */
if (RegisterClassEx(&windowClass) == 0) /**< 尝试注册窗口类 */
{ /**< NOTE: Failure, Should Never Happen */
MessageBox(HWND_DESKTOP, "注册窗口失败!", "Error", MB_OK | MB_ICONEXCLAMATION);
return -1; /**< 退出并返回FALSE */
}
// 询问是否在全屏状态下运行?
if (MessageBox(HWND_DESKTOP, "你想在全屏状态下运行么 ?", "设置运行模式", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
m_CreateFullScreen = false; /**< m_CreateFullScreen记录当前的显示模式为窗口 */
}
while (m_IsProgramLooping) /**< 循环直到WM_QUIT退出程序 */
{
/// 创建一个窗口
if (m_Window.Create("OpenGL 3D游戏编程——构造天空和地面", m_ClassName,m_CreateFullScreen, hInstance, this) == true)
{
/// 如果初始化失败,则退出
if (Init() == false) /**< 调用自定义的初始化函数 */
{ /**< 失败 */
TerminateApplication(); /**< 关闭窗口退出程序 */
}
else /**< 成功开始消息循环 */
{
MSG msg; /**< Window消息结构 */
bool isMessagePumpActive = true; /**< 当消息不为空时,处理消息循环 */
m_LastTickCount = GetTickCount(); /**< 返回当前的计时器的值 */
m_Keys.Clear(); /**< 清空所有的按键信息 */
while (isMessagePumpActive == true) /**< 当消息不为空时,处理消息循环 */
{ /**< 成功创建窗口,检测窗口消息 */
if (PeekMessage(&msg, m_Window, 0, 0, PM_REMOVE) != 0)
{
/// 检测是否为WM_QUIT消息
if (msg.message != WM_QUIT)
{
DispatchMessage(&msg); /**< 如果不是发送消息到消息回调函数中处理 */
}
else
{
isMessagePumpActive = false; /**< 如果是,则退出 */
}
}
/// 如果没有消息
else
{
if (m_IsVisible == false) /**< 如果窗口不可见 */
{
WaitMessage(); /**< 暂停程序,等待消息 */
}
else /**< 如果窗口可见 */
{ /**< 执行消息循环 */
DWORD tickCount = GetTickCount(); /**< 返回计时器的当前值 */
Update(tickCount - m_LastTickCount); /**< 调用用户自定义的更新函数 */
m_LastTickCount = tickCount; /**< 重新设置上一次,计数器的值 */
Draw(); /**< 调用用户自定义的绘制函数 */
m_Window.SwapBuffers(); /**< 交换前后帧缓存 */
}
}
} /**< 如果isMessagePumpActive == true,则循环 */
}
/**< 程序结束 */
Uninit(); /**< 用户自定义的卸载函数 */
m_Window.Destroy(); /**< 删除窗口 */
}
else /**< 如果创建窗口失败 */
{
MessageBox(HWND_DESKTOP, "创建OpenGL窗口错误", "Error", MB_OK | MB_ICONEXCLAMATION);
m_IsProgramLooping = false; /**< 停止程序循环 */
}
}
UnregisterClass(m_ClassName, hInstance); /**< 取消注册的窗口 */
return 0;
}
//========================================================
/**
* @file GLWindows.h
*
* 项目描述: 使用OO的OpenGL程序框架演示
* 文件描述: 程序窗口类
* 适用平台: Windows98/2000/NT/XP
*
*
*/
//========================================================
#include "GLWindow.h" /**< 包含GLwindows.h头文件 */
#include
#include
/** 构造函数 */
GLWindow::GLWindow()
{
m_WindowPosX = 0; /**< 窗口的左上角的X位置 */
m_WindowPosY = 0; /**< 窗口的左上角的Y位置 */
m_WindowWidth = 800; /**< 窗口的宽度 */
m_WindowHeight = 600; /**< 窗口的高度 */
m_ScreenWidth = 1024; /**< 全屏的宽度 */
m_ScreenHeight = 768; /**< 全屏的高度 */
m_BitsPerPixel = 16; /**< 颜色位深 */
m_IsFullScreen = false; /**< 不使用全屏 */
m_hWnd = 0;
m_hDC = 0;
m_hRC = 0;
}
/** 返回窗口的大小 */
int GLWindow::GetWidth()
{
if (m_IsFullScreen == true)
{
return m_ScreenWidth;
}
else
{
return m_WindowWidth;
}
}
int GLWindow::GetHeight()
{
if (m_IsFullScreen == true)
{
return m_ScreenHeight;
}
else
{
return m_WindowHeight;
}
}
/** 设置窗口的大小 */
void GLWindow::SetWidth(int width)
{
if (m_IsFullScreen == true)
{
m_ScreenWidth = width;
}
else
{
m_WindowWidth = width;
}
}
void GLWindow::SetHeight(int height)
{
if (m_IsFullScreen == true)
{
m_ScreenHeight = height;
}
else
{
m_WindowHeight = height;
}
}
/** 返回窗口左上角的位置 */
int GLWindow::GetPosX()
{
if (m_IsFullScreen == false)
{
return m_WindowPosX;
}
return 0;
}
int GLWindow::GetPosY()
{
if (m_IsFullScreen == false)
{
return m_WindowPosY;
}
return 0;
}
/** 设置窗口左上角的位置 */
void GLWindow::SetPosX(int x)
{
if (m_IsFullScreen == false)
{
m_WindowPosX = x;
}
}
void GLWindow::SetPosY(int y)
{
if (m_IsFullScreen == false)
{
m_WindowPosY = y;
}
}
/** 当窗口大小改变时,通知OpenGL调整大小 */
void GLWindow::ReshapeGL()
{
GLsizei width = GetWidth();
GLsizei height = GetHeight();
glViewport(0, 0, width, height); /**< 重新设置视口 */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 1.0f, 4000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/** 改变窗口的显示设置 */
bool GLWindow::ChangeScreenSetting()
{
DEVMODE dmScreenSettings; /**< 设备模式 */
ZeroMemory(&dmScreenSettings, sizeof(DEVMODE)); /**< 清零结构 */
dmScreenSettings.dmSize = sizeof(DEVMODE); /**< 结构大小 */
dmScreenSettings.dmPelsWidth = GetWidth(); /**< 设置宽度 */
dmScreenSettings.dmPelsHeight = GetHeight(); /**< 设置高度 */
dmScreenSettings.dmBitsPerPel = m_BitsPerPixel; /**< 设置位深度 */
//dmScreenSettings.dmDisplayFrequency = 75; /**< 设置屏幕刷新率 */
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT ;//| DM_DISPLAYFREQUENCY;
/// 改变显示模式
if (ChangeDisplaySettings(&dmScreenSettings, 0) != DISP_CHANGE_SUCCESSFUL)
{
return false; /**< 如果失败,返回false */
}
return true; /**< 成功返回 */
}
/** 创建windows窗口 */
bool GLWindow::Create(const char * window_title,const char * class_name,bool fullscreen, HINSTANCE h_instance, LPVOID lpParam)
{
m_IsFullScreen = fullscreen;
int nX=0;
int nY=0;
PIXELFORMATDESCRIPTOR pfd = /**< 设置像素描述结构 */
{
sizeof(PIXELFORMATDESCRIPTOR), /**< 像素描述结构的大小 */
1, /**< 版本号 */
PFD_DRAW_TO_WINDOW | /**< 缓存区的输出显示在一个窗口中 */
PFD_SUPPORT_OPENGL | /**< 缓存区支持OpenGL绘图 */
PFD_STEREO | /**< 颜色缓存区是立体缓存 */
PFD_DOUBLEBUFFER, /**< 颜色缓存区是双缓存 */
PFD_TYPE_RGBA, /**< 使用RGBA颜色格式 */
m_BitsPerPixel, /**< 颜色缓存区中颜色值所占的位深 */
0, 0, 0, 0, 0, 0, /**< 使用默认的颜色设置 */
0, /**< 无Alpha缓存 */
0, /**< 颜色缓存区中alpha成分的移位计数 */
0, /**< 无累计缓存区 */
0, 0, 0, 0, /**< 累计缓存区无移位 */
32, /**< 32位深度缓存 */
0, /**< 无蒙版缓存 */
0, /**< 无辅助缓存区 */
PFD_MAIN_PLANE, /**< 必须为PFD_MAIN_PLANE,设置为主绘图层 */
0, /**< 表示OpenGL实现所支持的上层或下层平面的数量 */
0, 0, 0 /**< 过时,已不再使用 */
};
DWORD windowStyle = WS_OVERLAPPEDWINDOW & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX; /**< 定义我们窗口类型,使用常规设定,去掉最大化按钮,并不能改变窗体大小 */
DWORD windowExtendedStyle = WS_EX_APPWINDOW;
if (m_IsFullScreen == true) /**< 如果为全屏模式,尝试转化为全屏模式 */
{
if (ChangeScreenSetting() == false)
{ /**< 全屏模式转换失败,弹出对话框提示,并尝试窗口模式 */
MessageBox(HWND_DESKTOP, "模式转换失败.\n在窗口模式下运行.", "Error", MB_OK | MB_ICONEXCLAMATION);
m_IsFullScreen = false; /**< 设置为窗口模式 */
}
else /**< 如果为窗口模式 */
{
ShowCursor(false); /**< 隐藏鼠标 */
windowStyle = WS_POPUP; /**< 设置窗口模式为顶层窗口 */
windowExtendedStyle |= WS_EX_TOPMOST;
}
}
/// 调整我们窗口的大小,使其客户区的大小为我们设置的大小
RECT windowRect = {GetPosX(), GetPosY(), GetPosX() + GetWidth(), GetPosY() + GetHeight()};
if (m_IsFullScreen == false) /**< 在窗口模式下使用 */
{
windowExtendedStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; /**< 使窗口具有3D外观 */
int wid = GetSystemMetrics(SM_CXSCREEN); /**< 获取当前屏幕宽 */
int hei = GetSystemMetrics(SM_CYSCREEN); /**< 获取当前屏幕高 */
nX = (wid - GetWidth()) / 2; /**< 计算窗口居中用 */
nY = (hei - GetHeight()) / 2;
/// 调整我们窗口的大小,使其客户区的大小为我们设置的大小
AdjustWindowRectEx(&windowRect, windowStyle, 0, windowExtendedStyle);
/// 判断窗口的左上角是否隐藏在桌面外
if (windowRect.left < 0) /**< 如果窗口X坐标为负,移动坐标到0处,并调整窗口的位置 */
{
windowRect.right -= windowRect.left;
windowRect.left = 0;
}
if (windowRect.top < 0) /**< 如果窗口Y坐标为负,移动坐标到0处,并调整窗口的位置 */
{
windowRect.bottom -= windowRect.top;
windowRect.top = 0;
}
}
/// 创建窗口
m_hWnd = CreateWindowEx(windowExtendedStyle, /**< 窗口的扩展风格 */
class_name, /**< 窗口的类名 */
window_title, /**< 窗口标题 */
windowStyle, /**< 窗口的风格 */
nX,nY, /**< 窗口的左上角位置 */
windowRect.right - windowRect.left, /**< 窗口的宽度 */
windowRect.bottom - windowRect.top, /**< 窗口的高度 */
HWND_DESKTOP, /**< 窗口的父窗口为桌面 */
0, /**< 无菜单 */
h_instance, /**< 传入窗口的实例句柄 */
lpParam); /**< 传入程序类参数 */
while (m_hWnd != 0) /**< 窗口是否创建成功 */
{
m_hDC = GetDC(m_hWnd); /**< 返回窗口的设备描述表 */
if (m_hDC == 0) /**< 如果为空 */
{ /**< 失败 */
break;
}
GLuint PixelFormat = ChoosePixelFormat(m_hDC, &pfd); /**< 查找一个兼容的像素格式 */
if (PixelFormat == 0) /**< 如果没找到 */
{ /**< 失败 */
break;
}
if (SetPixelFormat(m_hDC, PixelFormat, &pfd) == false) /**< 设置像素格式 */
{ /**< 失败 */
break;
}
m_hRC = wglCreateContext(m_hDC); /**< 创建OpenGL的渲染描述表 */
if (m_hRC == 0) /**< 如果为空 */
{ /**< 失败 */
break;
}
if (wglMakeCurrent(m_hDC, m_hRC) == false) /**< 设置当前的OpenGL的渲染对象为当前的窗口 */
{ /**< 失败 */
break;
}
ShowWindow(m_hWnd, SW_NORMAL); /**< 显示窗口 */
ReshapeGL(); /**< 告诉OpenGL调整窗口大小 */
return true; /**< 成功返回 */
}
Destroy(); /**< 释放资源 */
return false; /**< 返回失败 */
}
/** 删除OpenGL窗口 */
void GLWindow::Destroy()
{
if (m_hWnd != 0) /**< 窗口句柄是否存在 */
{
if (m_hDC != 0) /**< 窗口设备描述表是否存在 */
{
wglMakeCurrent(m_hDC, 0); /**< 设置当前的OpenGL的渲染对象为空NULL */
if (m_hRC != 0) /**< OpenGL的渲染描述表是否存在 */
{
wglDeleteContext(m_hRC); /**< 释放OpenGL的渲染描述表 */
m_hRC = 0; /**< 设置OpenGL的渲染描述表为0 */
}
ReleaseDC(m_hWnd, m_hDC); /**< 释放窗口的设备描述表 */
m_hDC = 0; /**< 设置窗口的设备描述表为0 */
}
DestroyWindow(m_hWnd); /**< 删除窗口 */
m_hWnd = 0; /**< 设置窗口句柄为0 */
}
if (m_IsFullScreen) /**< 如果为全屏模式,在程序结束后,变换到窗口模式,并显示鼠标 */
{
ChangeDisplaySettings(NULL, 0); /**< 变换到窗口模式 */
ShowCursor(true); /**< 显示鼠标 */
}
}
//=========================================================================
/**
* @file SkyAndTerrain.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 具体实例类
* 适用平台: Windows98/2000/NT/XP
*
*
* 在这个类中您必须重载如下几个虚函数
*
* virtual bool Init();
* 执行所有的初始化工作,如果成功函数返回true
*
* virtual void Uninit();
* 执行所有的卸载工作
*
* virtual void Update(DWORD milliseconds);
* 执行所有的更新操作,传入的参数为两次操作经过的时间,以毫秒为单位
*
* virtual void Draw();
* 执行所有的绘制操作
*/
//=========================================================================
#include "stdafx.h"
#include "SkyAndTerrain.h"
/** 创建一个程序的实例 */
GLApplication * GLApplication::Create(const char * class_name)
{
SkyTerrain * test = new SkyTerrain(class_name);
return reinterpret_cast
}
/** 构造函数 */
SkyTerrain::SkyTerrain(const char * class_name) : GLApplication(class_name)
{
/// 初始化用户自定义的程序变量
m_Fps = 0;
m_RenderMode = true;
sp = false;
}
/** 初始化OpenGL */
bool SkyTerrain::Init()
{
/** 用户自定义的初始化过程 */
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
ResizeDraw(true); /**< 改变OpenGL窗口大小,直接调用子类的函数 */
/** 初始化字体 */
if(!m_Font.InitFont())
MessageBox(NULL,"初始化字体失败!","错误",MB_OK);
/** 初始化地形 */
if(!m_Terrain.init())
{
MessageBox(NULL,"初始化地形失败!","错误",MB_OK);
exit(0);
}
/** 初始化天空 */
if(!m_SkyBox.init())
{
MessageBox(NULL,"初始化天空失败!","错误",MB_OK);
exit(0);
}
/** 设置摄像机 */
m_Camera.setCamera( 500, 35, 400, 501, 35, 400, 0, 1, 0);
return true; /**< 成功返回 */
}
/** 用户自定义的卸载函数 */
void SkyTerrain::Uninit()
{
}
/** 更新摄像机 */
void SkyTerrain::UpdateCamera()
{
m_Camera.setViewByMouse();
/** 键盘按键响应 */
if(m_Keys.IsPressed(VK_SHIFT)) /**< 按下SHIFT键时加速 */
{
m_Camera.setSpeed(0.2f);
}
if(!m_Keys.IsPressed(VK_SHIFT))
{
m_Camera.setSpeed(0.1f);
}
if(m_Keys.IsPressed(VK_UP) || m_Keys.IsPressed('W')) /**< 向上方向键或'W'键按下 */
m_Camera.moveCamera(m_Camera.getSpeed()); /**< 移动摄像机 */
if(m_Keys.IsPressed(VK_DOWN) || m_Keys.IsPressed('S')) /**< 向下方向键或'S'键按下 */
m_Camera.moveCamera(-m_Camera.getSpeed()); /**< 移动摄像机 */
if(m_Keys.IsPressed(VK_LEFT) || m_Keys.IsPressed('A')) /**< 向左方向键或'A'键按下 */
m_Camera.yawCamera(-m_Camera.getSpeed()); /**< 移动摄像机 */
if(m_Keys.IsPressed(VK_RIGHT) || m_Keys.IsPressed('D')) /**< 向右方向键或'D'键按下 */
m_Camera.yawCamera(m_Camera.getSpeed()); /**< 移动摄像机 */
/** 根据地形高度更新摄像机 */
Vector3 vPos = m_Camera.getPosition(); /**< 得到当前摄像机位置 */
Vector3 vNewPos = vPos;
/** 设置摄像机高度为 地形高度 + 10 */
vNewPos.y = (float)m_Terrain.getAveHeight(vPos.x,vPos.z ) + 10;
/** 得到高度差值 */
float temp = vNewPos.y - vPos.y;
/** 更新摄像机方向 */
Vector3 vView = m_Camera.getView();
vView.y += temp;
/** 设置摄像机 */
m_Camera.setCamera(vNewPos.x, vNewPos.y, vNewPos.z,
vView.x, vView.y, vView.z,
0, 1, 0);
}
/** 程序更新函数 */
void SkyTerrain::Update(DWORD milliseconds)
{
if (m_Keys.IsPressed(VK_ESCAPE) == true) /**< 按ESC退出 */
{
TerminateApplication();
}
/** 用户自定义的更新过程 */
/** 更新摄像机 */
UpdateCamera();
/** 空格键切换绘制模式 */
if(m_Keys.IsPressed(VK_SPACE) && !sp)
{
sp = true;
m_RenderMode = !m_RenderMode;
if(m_RenderMode)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
if(!m_Keys.IsPressed(VK_SPACE))
sp = false;
if (m_Keys.IsPressed(VK_F1) == true) /**< 按F1切换窗口/全屏模式 */
{
ToggleFullscreen();
}
}
/** 计算帧速 */
void SkyTerrain::CaculateFrameRate()
{
static float framesPerSecond = 0.0f; /**< 保存显示帧数 */
static float lastTime = 0.0f; /**< 记录上次时间 */
float currentTime = GetTickCount() * 0.001f; /**< 获得当前时间 */
framesPerSecond++; /**< 显示帧数递增1 */
/** 如果时间差大于1.0秒 */
if( currentTime - lastTime > 1.0f )
{
lastTime = currentTime; /**< 保存当前时间 */
m_Fps = framesPerSecond; /**< 当前帧数传给m_Fps */
framesPerSecond = 0; /**< 将帧数置零 */
}
}
/** 输出文字信息 */
void SkyTerrain::PrintText()
{
char string[128]; /**< 用于保存输出字符串 */
glPushAttrib(GL_CURRENT_BIT); /**< 保存现有颜色属性信息 */
glColor3f(0.0f,1.0f,1.0f); /**< 设置文字颜色 */
sprintf(string,"当前位置:X=%3.1f Y=%3.1f Speed =%3.1f ",
m_Camera.getPosition().x,m_Camera.getPosition().z ,m_Camera.getSpeed()); /**< 字符串赋值 */
m_Font.PrintText(string,-5.0f,3.5f);
/** 输出帧速 */
CaculateFrameRate(); /**< 计算帧速 */
sprintf(string,"FPS:%d",(int)m_Fps); /**< 字符串赋值 */
m_Font.PrintText(string, -5.0f,3.0f); /**< 输出字符串 */
glPopAttrib();
}
/** 绘制函数 */
void SkyTerrain::Draw()
{
/** 用户自定义的绘制过程 */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
/** 放置摄像机 */
m_Camera.setLook();
/** 绘制天空 */
m_SkyBox.render();
/** 渲染地形 */
m_Terrain.render();
/** 输出屏幕信息 */
PrintText();
glFlush(); /**< 强制执行所有的OpenGL命令 */
}
//========================================================
/**
* @file SkyBox.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 天空盒类
* 适用平台: Windows98/2000/NT/XP
*
*
*/
//========================================================
#include "SkyBox.h"
CSkyBox::CSkyBox():length(750.0f),width(550.0f),height(400.0f),yRot(0.01f)
{
}
CSkyBox::~CSkyBox()
{
/** 删除纹理对象及其占用的内存 */
for(int i =0 ;i< 5; i++)
{
m_texture[i].FreeImage();
glDeleteTextures(1,&m_texture[i].ID);
}
}
/** 天空盒初始化 */
bool CSkyBox::init()
{
char filename[128] ; /**< 用来保存文件名 */
char *bmpName[] = { "back","front","top","left","right"};
for(int i=0; i< 5; i++)
{
sprintf(filename,"data/%s",bmpName[i]);
strcat(filename,".bmp");
if(!m_texture[i].LoadBitmap(filename)) /**< 载入位图文件 */
{
MessageBox(NULL,"装载位图文件失败!","错误",MB_OK); /**< 如果载入失败则弹出对话框 */
exit(0);
}
glGenTextures(1, &m_texture[i].ID); /**< 生成一个纹理对象名称 */
glBindTexture(GL_TEXTURE_2D, m_texture[i].ID); /**< 创建纹理对象 */
/** 控制滤波 */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
/** 创建纹理 */
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, m_texture[i].imageWidth,
m_texture[i].imageHeight, GL_RGB, GL_UNSIGNED_BYTE,
m_texture[i].image);
}
return true;
}
void CSkyBox::render()
{
/** 获得场景中光照状态 */
GLboolean lp;
glGetBooleanv(GL_LIGHTING,&lp);
m_CameraPos = Camera::GetCamera()->getPosition();
glDisable(GL_LIGHTING); /**< 关闭光照 */
glEnable(GL_TEXTURE_2D);
/** 开始绘制 */
glPushMatrix();
glTranslatef(m_CameraPos.x,m_CameraPos.y,m_CameraPos.z);
glRotatef(yRot,0.0f,1.0f,0.0f);
/** 绘制背面 */
glBindTexture(GL_TEXTURE_2D, m_texture[0].ID);
glBegin(GL_QUADS);
/** 指定纹理坐标和顶点坐标 */
glTexCoord2f(1.0f, 0.0f); glVertex3f( width, -height, -length);
glTexCoord2f(1.0f, 1.0f); glVertex3f( width, height, -length);
glTexCoord2f(0.0f, 1.0f); glVertex3f( -width, height, -length);
glTexCoord2f(0.0f, 0.0f); glVertex3f( -width, -height, -length);
glEnd();
/** 绘制前面 */
glBindTexture(GL_TEXTURE_2D, m_texture[1].ID);
glBegin(GL_QUADS);
/** 指定纹理坐标和顶点坐标 */
glTexCoord2f(1.0f, 0.0f); glVertex3f( -width, -height, length);
glTexCoord2f(1.0f, 1.0f); glVertex3f( -width, height, length);
glTexCoord2f(0.0f, 1.0f); glVertex3f( width, height, length);
glTexCoord2f(0.0f, 0.0f); glVertex3f( width, -height, length);
glEnd();
/** 绘制顶面 */
glBindTexture(GL_TEXTURE_2D, m_texture[2].ID);
glBegin(GL_QUADS);
/** 指定纹理坐标和顶点坐标 */
glTexCoord2f(0.0f, 1.0f); glVertex3f( width, height, -length);
glTexCoord2f(0.0f, 0.0f); glVertex3f( width, height, length);
glTexCoord2f(1.0f, 0.0f); glVertex3f( -width, height, length);
glTexCoord2f(1.0f, 1.0f); glVertex3f( -width, height, -length);
glEnd();
/** 绘制左面 */
glBindTexture(GL_TEXTURE_2D, m_texture[3].ID);
glBegin(GL_QUADS);
/** 指定纹理坐标和顶点坐标 */
glTexCoord2f(1.0f, 1.0f); glVertex3f( -width, height, -length);
glTexCoord2f(0.0f, 1.0f); glVertex3f( -width, height, length);
glTexCoord2f(0.0f, 0.0f); glVertex3f( -width, -height, length);
glTexCoord2f(1.0f, 0.0f); glVertex3f( -width, -height, -length);
glEnd();
/** 绘制右面 */
glBindTexture(GL_TEXTURE_2D, m_texture[4].ID);
glBegin(GL_QUADS);
/** 指定纹理坐标和顶点坐标 */
glTexCoord2f(0.0f, 0.0f); glVertex3f( width, -height, -length);
glTexCoord2f(1.0f, 0.0f); glVertex3f( width, -height, length);
glTexCoord2f(1.0f, 1.0f); glVertex3f( width, height, length);
glTexCoord2f(0.0f, 1.0f); glVertex3f( width, height, -length);
glEnd();
glPopMatrix(); /** 绘制结束 */
if(lp) /** 恢复光照状态 */
glEnable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
yRot += 0.01f;
if(yRot > 360.0f)
yRot = 0.0f;
}
/*======================================
* @file stdafx.cpp
*
*
*/
#include "stdafx.h"
//========================================================
/**
* @file Terrain.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 地形类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#include "Terrain.h"
#include "CBMPLoader.h"
/** 当前CTerrain指针 */
CTerrain* CTerrain::m_pTerrain = NULL;
/** 多重纹理函数指针 */
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL;
/** 构造函数 */
CTerrain::CTerrain():m_bDetail(true),m_DetailScale(128)
{
/** 设置地形大小 */
setSize(MAP_WIDTH,CELL_WIDTH);
/** 为地形高程分配内存,并初始化 */
m_pHeightMap = new BYTE[m_nWidth * m_nWidth];
for(int i=0; i
m_pTerrain = this;
}
/** 析构函数 */
CTerrain::~CTerrain()
{
/** 删除内存和指针 */
if(m_pHeightMap)
{
delete[] m_pHeightMap;
m_pHeightMap = 0;
}
/** 删除纹理对象及其占用内存 */
for(int i=0; i<2; i++)
{
m_texture[i].FreeImage();
glDeleteTextures(1,&m_texture[i].ID);
}
}
/** 初始化雾效 */
void CTerrain::initFog()
{
float fogColor[4] = { 0.8f,0.8f,0.8f,1.0f };
glEnable(GL_FOG);
glFogi(GL_FOG_MODE,GL_EXP); /** 设置雾效的模式 */
glFogfv(GL_FOG_COLOR,fogColor); /** 指定雾的颜色 */
glFogf(GL_FOG_DENSITY,0.001f); /** 设置雾的浓度 */
glFogf(GL_FOG_START,1.0); /** 设置线性雾的开始位置 */
glFogf(GL_FOG_END,1000.0); /** 设置线性雾的结束位置 */
glHint(GL_FOG_HINT,GL_DONT_CARE); /** 规定雾化效果的质量 */
}
/** 初始化地形 */
bool CTerrain::init()
{
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB");
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress("glMultiTexCoord2fARB");
if(!glActiveTextureARB || !glMultiTexCoord2fARB)
{
//输出错误信息
MessageBox(NULL, "不支持多重纹理!", "错误", MB_OK);
exit(0);
//setDetail(false);
}
/** 载入高度文件 */
loadRawFile("data/terrain.raw");
/** 载入纹理 */
loadTexture();
/** 初始化雾效 */
initFog();
return true;
}
/** 设置地形大小 */
void CTerrain::setSize(unsigned int width, unsigned int cell)
{
m_nWidth = width;
m_nCellWidth = cell;
}
/** 获得高度图中高度值 */
int CTerrain::getHeight(int x,int y)
{
if(!m_pHeightMap)
return 0;
int xx = x % m_nWidth;
int yy = y % m_nWidth;
/** 返回当前高度 */
return m_pHeightMap[(xx + yy * m_nWidth)];
}
/** 获得地面高度 */
float CTerrain::getAveHeight(float x,float z)
{
float CameraX, CameraZ;
CameraX = x / m_nCellWidth;
CameraZ = z / m_nCellWidth;
/** 计算高程坐标(Col0, Row0),(Col1, Row1) */
int col0 = int(CameraX);
int row0 = int(CameraZ);
unsigned int col1 = col0 + 1;
unsigned int row1 = row0 + 1;
/** 确保单元坐标不超过高程以外 */
if (col1 > m_nWidth) col1 = 0;
if (row1 > m_nWidth) row1 = 0;
/** 获取单元的四个角的高度 */
float h00 = (float)(m_pHeightMap[col0*m_nCellWidth + row0*m_nCellWidth*m_nWidth]);
float h01 = (float)(m_pHeightMap[col1*m_nCellWidth + row0*m_nCellWidth*m_nWidth]);
float h11 = (float)(m_pHeightMap[col1*m_nCellWidth + row1*m_nCellWidth*m_nWidth]);
float h10 = (float)(m_pHeightMap[col0*m_nCellWidth + row1*m_nCellWidth*m_nWidth]);
/** 计算机摄像机相对于单元格的位置 */
float tx = CameraX - int(CameraX);
float ty = CameraZ - int(CameraZ);
/** 进行双线性插值得到地面高度 */
float txty = tx * ty;
float final_height = h00 * (1.0f - ty - tx + txty)
+ h01 * (tx - txty)
+ h11 * txty
+ h10 * (ty - txty);
return final_height;
}
/** 载入高度图 */
bool CTerrain::loadRawFile(const char* fileName)
{
FILE *pFile = NULL;
/** 打开文件 */
pFile = fopen( fileName, "rb" );
/** 错误检查 */
if ( pFile == NULL )
{
/** 输出错误信息,并返回false */
MessageBox(NULL, "打开高度图文件失败!", "错误", MB_OK);
exit(0);
}
/** 读取高度图文件 */
fread( m_pHeightMap, 1, m_nWidth*m_nWidth, pFile );
/** 获取错误代码 */
int result = ferror( pFile );
/** 检查错误代码 */
if (result)
{
MessageBox(NULL, "无法获取高度数据!", "错误", MB_OK);
exit(0);
}
/** 关闭文件,成功返回 */
fclose(pFile);
return true;
}
/** 设置纹理坐标 */
void CTerrain::setTexCoord(float x,float z)
{
float u = (float)x / (float)m_nWidth;
float v = -(float)z / (float)m_nWidth;
///设置地面纹理的纹理坐标
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u, v);
///设置细节纹理的纹理坐标
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, u, v);
}
/** 载入地面纹理 */
bool CTerrain::loadTexture()
{
char* fileName[] = {"data/terrain.bmp","data/detail.bmp"};
for(int i=0; i < 2; i++)
{
if(!m_texture[i].LoadBitmap(fileName[i]) ) /**< 载入位图文件 */
{
MessageBox(NULL,"装载位图文件失败!","错误",MB_OK); /**< 如果载入失败则弹出对话框 */
exit(0);
}
glGenTextures(1, &m_texture[i].ID); /**< 生成一个纹理对象名称 */
glBindTexture(GL_TEXTURE_2D, m_texture[i].ID); /**< 创建纹理对象 */
/** 控制滤波 */
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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, m_texture[i].imageWidth,
m_texture[i].imageHeight, GL_RGB, GL_UNSIGNED_BYTE,
m_texture[i].image);
}
return true;
}
/** 渲染地形 */
void CTerrain::render()
{
int X = 0, Y = 0;
float x, y, z;
bool bSwitchSides = false;
/** 检查高度图是否有效 */
if(!m_pHeightMap)
return;
/** 绑定纹理 */
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_texture[0].ID);
/** 渲染细节纹理 */
if(m_bDetail)
{
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
glBindTexture(GL_TEXTURE_2D, m_texture[1].ID);
/** 变换纹理矩阵 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef((float)m_DetailScale, (float)m_DetailScale, 1);
glMatrixMode(GL_MODELVIEW);
}
/** 绘制三角形带 */
glBegin( GL_TRIANGLE_STRIP );
/** 从行(X)开始循环 */
for ( X = 0; X <= m_nWidth; X += m_nCellWidth )
{
/** 检查该列是否需要从相反顺序绘制 */
if(bSwitchSides)
{
/** 绘制地形的一列 */
for ( Y = m_nWidth; Y >= 0; Y -= m_nCellWidth )
{
/** 顶点(X, Y, Z) */
x = X;
y = getHeight( X, Y );
z = Y;
/** 指定纹理坐标,并发送顶点 */
//setFogCoord(m_FogDepth, (float)y);
setTexCoord( (float)x, (float)z );
glVertex3f(x, y, z);
/** 顶点(X + m_nCellWidth, Y, Z) */
x = X + m_nCellWidth;
y = getHeight( X + m_nCellWidth, Y );
z = Y;
/** 指定纹理坐标,并发送顶点 */
//setFogCoord(m_FogDepth, (float)y);
setTexCoord( (float)x, (float)z );
glVertex3f(x, y, z);
}
}
else
{
/** 绘制地形的一列 */
for ( Y = 0; Y <= m_nWidth; Y += m_nCellWidth )
{
/** 顶点(X + m_nCellWidth, Y, Z) */
x = X + m_nCellWidth;
y = getHeight( X + m_nCellWidth, Y );
z = Y;
/** 指定纹理坐标,并发送顶点 */
//setFogCoord(m_FogDepth, (float)y);
setTexCoord( (float)x, (float)z );
glVertex3f(x, y, z);
/** 顶点 (X, Y, Z) */
x = X;
y = getHeight( X, Y );
z = Y;
/** 指定纹理坐标,并发送顶点 */
//setFogCoord(m_FogDepth, (float)y);
setTexCoord( (float)x, (float)z );
glVertex3f(x, y, z);
}
}
/** 变换开关 */
bSwitchSides = !bSwitchSides;
}
/** 绘制结束 */
glEnd();
///关闭纹理单元1
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
//关闭纹理单元0
glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
}
//========================================================
/**
* @file Vector.cpp
*
* 项目描述: 构造天空和地面
* 文件描述: 向量类
* 适用平台: Windows98/2000/NT/XP
*
*/
//========================================================
#include "Vector.h" /**< 包含头文件 */
/** 计算向量的长度 */
inline float Vector3::length()
{
return (float)( x * x + y * y + z * z );
}
/** 单位化一向量 */
Vector3 Vector3::normalize()
{
float len = length(); /**< 计算向量长度 */
if( len == 0 )
len = 1;
x = x / len;
y = y / len;
z = z / len;
return *this;
}
/** 点积 */
float Vector3::dotProduct(const Vector3& v)
{
return ( x * v.x + y * v.y + z * v.z );
}
/** 叉积 */
Vector3 Vector3::crossProduct(const Vector3& v)
{
Vector3 vec;
vec.x = y * v.z - z * v.y;
vec.y = z * v.x - x * v.z;
vec.z = x * v.y - y * v.x;
return vec;
}
/** 操作符 + */
Vector3 Vector3::operator +(const Vector3& v)
{
Vector3 vec;
vec.x = x + v.x;
vec.y = y + v.y;
vec.z = z + v.z;
return vec;
}
/** 操作符 - */
Vector3 Vector3::operator -(const Vector3& v)
{
Vector3 vec;
vec.x = x - v.x;
vec.y = y - v.y;
vec.z = z - v.z;
return vec;
}
/** 操作符 * */
Vector3 Vector3::operator *(float scale)
{
x = x * scale;
y = y * scale;
z = z * scale;
return *this;
}
/** 操作符 / */
Vector3 Vector3::operator /(float scale)
{
if(scale != 0.0)
{
x = x / scale;
y = y / scale;
z = z / scale;
}
return *this;
}
/** 负号 */
Vector3 Vector3::operator -()
{
Vector3 vec( - x,- y, - z);
return vec;
}