让前面的程序成为一个全景图浏览器还要
1。一个命令行载入图
2。一个打开图像的对话框(右键菜单)
命令行加载:
init();
if(argc==2){
// 全路径读入一个纹理
PathBuildTexture(argv[1], SkyTexture[0]);
m_sky.InitSky(0,0,0,R,SkyTexture[0]);//初始化天空球
}
打开对话框:
void MenuProc(int id)
{
if(id==1){
string fn=myGetOpenFile();
printf("%s\n",fn.c_str());
char f[260];
strcpy(f, fn.c_str());
PathBuildTexture(f, SkyTexture[0]);
m_sky.InitSky(0,0,0,R,SkyTexture[0]);//初始化天空球
}
glutPostRedisplay();
}
完整cpp:
//55 全景球浏览器
//左键(+ 移动)旋转,右键打开对话框,回车键(enter)全屏切换,按w、a、s、d、f、x、切换视角
#pragma comment( lib, "opengl32.lib" )
#pragma comment( lib, "glut32.lib")
#include <GL/glut.h>
#include <GL/glu.h>
#include <math.h>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include "ArcBall.h"
#include "Sky.h"
CSky m_sky;
void setWidthHeight(GLint width ,GLint height);
void screenshot(char* FileName); //全窗口
int BuildTexture(char *szPathName, GLuint &texid);
// 全路径读入一个纹理
int PathBuildTexture(char *szPathName, GLuint &texid);
//初始化,必须用全局变量的方式,不能用new
ArcBallT arcBall(600.0f,400.0f);
ArcBallT* ArcBall =&arcBall;// new ArcBallT(600.0f,400.0f);//&arcBall;
#define PI 3.141592654
GLuint SkyTexture[10];//纹理
int width,height;//屏幕宽高
int R=100;//地图球半径
std::string direction;//方向
bool Shot=false;
void reshape(int w, int h){
glViewport(0,0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//GLdouble zNear=R*cos(PI/2);
gluPerspective(90.0f, (GLfloat)w / (GLfloat)h, 6.5f, R*2);//zNear
glMatrixMode(GL_MODELVIEW);
ball
ArcBall->setBounds((GLfloat)w, (GLfloat)h);//1. 设置窗口边界
}
void init(){
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
/* 启用纹理 */
glEnable(GL_TEXTURE_2D);
/* 初始化天空 */
m_sky.InitSky(0,0,0,R,SkyTexture[0]);//初始化天空球
direction="前";
}
void display (void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
glLoadIdentity();
gluLookAt(0.0, 0.0, 0.0,//m_sky.GetSkyR()*ArcBall->zoomRate+5.0,//视点跟踪球大小
0.0, 0.0,R,
0.0, 1.0, 0.0);
//glTranslatef(0,0,-3);
glScalef(ArcBall->zoomRate, ArcBall->zoomRate, ArcBall->zoomRate);//2. 缩放
glScalef(-1.0, -1.0, -1.0);//2. 缩放
glMultMatrixf(ArcBall->Transform.M); //3. 旋转
//位置转到...
if(direction=="后")
glRotatef(180, 0.0, 1.0, 0.0);
else if(direction=="左")
glRotatef(90, 0.0, 1.0, 0.0);
else if(direction=="右")
glRotatef(-90, 0.0, 1.0, 0.0);
else if(direction=="上")
glRotatef(90, 1.0, 0.0, 0.0);
else if(direction=="下")
glRotatef(-90, 1.0, 0.0, 0.0);
/* 绘制天空 */
m_sky.ShowSky() ;
glFlush ();
}
//移动
void move(int x, int y)
{
ArcBall->MousePt.s.X = x;
ArcBall->MousePt.s.Y = y;
ArcBall->upstate();
glutPostRedisplay();
}
//点击
void mouse(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON && state==GLUT_DOWN){
ArcBall->isClicked = true;
move(x,y);
}
else if(button == GLUT_LEFT_BUTTON && state==GLUT_UP)
ArcBall->isClicked = false;
else if(button == GLUT_RIGHT_BUTTON && state==GLUT_DOWN){
ArcBall->isRClicked = true;
move(x,y);
}
else if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP)
ArcBall->isRClicked = false;
ArcBall->upstate();
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
case '1':
m_sky.T=SkyTexture[1];
break;
case '2':
m_sky.T=SkyTexture[2];
break;
case '3':
m_sky.T=SkyTexture[3];
break;
case '4':
m_sky.T=SkyTexture[4];
break;
case '5':
m_sky.T=SkyTexture[0];
break;
case 13://按回车切换全屏
{
static bool full=false;
if (full)
{
glutReshapeWindow(width,height);//窗口
full=false;
}
else
{
glutFullScreen();//全屏
full=true;
}
}
break;
case 'w':
{
direction="上";
glutPostRedisplay();
Shot=true;
}
break;
case 'f':
{
direction="后";
glutPostRedisplay();
Shot=true;
}
break;
case 'x':
{
direction="下";
glutPostRedisplay();
Shot=true;
}
break;
case 'a':
{
direction="左";
glutPostRedisplay();
Shot=true;
}
break;
case 'd':
{
direction="右";
glutPostRedisplay();
Shot=true;
}
break;
case 's'://前
{
direction="前";
glutPostRedisplay();
Shot=true;
}
break;
}
glutPostRedisplay();
}
//VC++:打开、保存文件对话框和浏览文件夹对话框
//2。API实现
string myGetOpenFile()
{
OPENFILENAMEA ofn;
char szFile[260];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "全景图 (*.bmp,*.jpg)\0*.bmp;*.jpg;\0\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = 0;
string f;
if (GetOpenFileNameA(&ofn)!=FALSE)
f=ofn.lpstrFile;
return f;
}
void MenuProc(int id)
{
if(id==1){
string fn=myGetOpenFile();
printf("%s\n",fn.c_str());
char f[260];
strcpy(f, fn.c_str());
PathBuildTexture(f, SkyTexture[0]);
m_sky.InitSky(0,0,0,R,SkyTexture[0]);//初始化天空球
}
glutPostRedisplay();
}
int main(int argc, char** argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
width=800;height=640;
R=width/(2*sin(45*PI/180));
glutInitWindowSize(width,height);
glutCreateWindow("全景浏览器");
init();
if(argc==2){
// 全路径读入一个纹理
PathBuildTexture(argv[1], SkyTexture[0]);
m_sky.InitSky(0,0,0,R,SkyTexture[0]);//初始化天空球
}
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse); //注册鼠标事件。
glutMotionFunc(move); //注册移动事件
glutKeyboardFunc(keyboard);
glutCreateMenu(MenuProc);
//菜单
glutAddMenuEntry("打开全景图",1);
glutAttachMenu(GLUT_RIGHT_BUTTON);//关连到右键
glutMainLoop();
return 0;
}
全路径加载图函数(我是放在别的文件中的):
// 全路径读入一个纹理
int PathBuildTexture(char *szPathName, GLuint &texid)
{
HDC hdcTemp; // 用于保存位图的 DC
HBITMAP hbmpTemp; // 暂时保存位图
IPicture *pPicture; // 图接口
OLECHAR wszPath[MAX_PATH+1]; // 图片的完整路径 (WCHAR)
//char szPath[MAX_PATH+1]; // 图片的完整路径
long lWidth; // 逻辑单元宽度
long lHeight; // 逻辑单元高度
long lWidthPixels; // 以像素为单位的宽度
long lHeightPixels; // 以像素为单位的高度
GLint glMaxTexDim ; // 保持最大纹理大小
//printf("图片路径:%s\n",szPath);
MultiByteToWideChar(CP_ACP, 0, szPathName, -1, wszPath, MAX_PATH); // 从ASCII转换为Unicode
HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);
if(FAILED(hr)) { // 如果加载失败
printf("装入纹理出错,可能是文件 %s 不存在!\n",szPathName);
return FALSE; // 返回假
}
hdcTemp = CreateCompatibleDC(GetDC(0)); // 创建与Windows兼容的设备上下文
if(!hdcTemp) // 创造失败了吗?
{
pPicture->Release(); // 递减图片参考计数
return FALSE; //返回假(失败)
}
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim); // 获取支持的最大纹理大小
pPicture->get_Width(&lWidth); // 获取图宽度(转换为像素)
lWidthPixels = MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);
pPicture->get_Height(&lHeight); // 获取图高度(转换为像素)
lHeightPixels = MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);
// 将图像调整为最接近的2次方
if (lWidthPixels <= glMaxTexDim) // 图像宽度是否小于或等于卡限制
lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f);
else // 否则设置宽度为 "最大功率的两个" 卡可以处理
lWidthPixels = glMaxTexDim;
if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
else // Otherwise Set Height To "Max Power Of Two" That The Card Can Handle
lHeightPixels = glMaxTexDim;
// 创建临时位图
BITMAPINFO bi = {0}; // 我们要的位图类型
DWORD *pBits = 0; // 指向位图位的指针
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // 设定结构尺寸
bi.bmiHeader.biBitCount = 32; // 32 位
bi.bmiHeader.biWidth = lWidthPixels; // Power Of Two Width
bi.bmiHeader.biHeight = lHeightPixels; // 使图像向上(正Y轴)
bi.bmiHeader.biCompression = BI_RGB; // RGB 编码
bi.bmiHeader.biPlanes = 1; // 1 Bitplane
// 以这种方式创建位图允许我们指定颜色深度,并允许我们立即访问位
hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);
if(!hbmpTemp) // Did Creation Fail?
{
DeleteDC(hdcTemp); // 删除设备描述表(上下文)
pPicture->Release(); // 递减图片参考计数
return FALSE; // Return False (Failure)
}
SelectObject(hdcTemp, hbmpTemp); // 选择处理我们的 temp DC 和我们的 temp 位图对象
// 将图片渲染到位图上
pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);
// 将BGR转换为RGB格式,并添加255的α值
for(long i = 0; i < lWidthPixels * lHeightPixels; i++) // 循环通过所有像素
{
BYTE* pPixel = (BYTE*)(&pBits[i]); // 获取当前像素
BYTE temp = pPixel[0]; // 将第一种颜色存储在 temp 变量(蓝色)中
pPixel[0] = pPixel[2]; // 将红色值移到正确位置(第一个)
pPixel[2] = temp; // 将 temp 值移到正确的蓝色位置(第3个)
// 这将使任何黑色像素完全透明(如果需要,可以硬编码该值)
if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0)) // 像素是完全黑色的吗?
pPixel[3] = 0; // 将α值设置为0
else // Otherwise
pPixel[3] = 255; // Set The Alpha Value To 255
}
glGenTextures(1, &texid); // 创建纹理
// 使用位图中的数据生成典型纹理
glBindTexture(GL_TEXTURE_2D, texid); // 绑定到纹理id
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // (针对所需的筛选类型修改此选项)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // (针对所需的筛选类型修改此选项)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits); // (Modify This If You Want Mipmaps)
DeleteObject(hbmpTemp); // Delete The Object
DeleteDC(hdcTemp); // Delete The Device Context
pPicture->Release(); // 递减图片参考计数
return TRUE; // Return True (都很好)
}
效果图: