OpenGL天空地形,场景漫游



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(lParam);
   
   /// 获得程序实例的指针
   GLApplication * appl = reinterpret_cast(creation->lpCreateParams);
   
   /// 保存程序实例的指针为用户自定义的程序附加值
   SetWindowLong(hWnd, GWL_USERDATA, reinterpret_cast(appl));
   appl->m_IsVisible = true;         /**< 设置程序可见 */
   return 0;             /**< 返回 */
  }
 }
 else
 { 
  /// 如果不是第一次窗口,返回程序实例的指针
  GLApplication * appl = reinterpret_cast(user_data);
  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               /**< 包含OpenGL头文件 */
#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(test);
}


/** 构造函数 */
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_pHeightMap[i] = 0;

 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;
}


















你可能感兴趣的:(OpenGL,C++)