关于cgal的使用就不介绍了。安装了也很麻烦,但是可以直接使用库,可以去我的上一篇文章下载,cgal下载
这里只简单的说说实现切割模型的方法。
判断所有的顶点在面的哪一面,如果一个面的所有点都在切割面的正面,那么这个面就是在切割面的positive部分,反之,如果一个面的所有点都在切割面的负面,那么这个面就是在切割面的negative部分,其余的面就是和切割面相交了。根据这个简单的原理,我们就可以用切割面把模型分为三部分。以下是我用cgal实现的效果。


从图片来看,效果基本还行,只是切割处不光滑,但是对于这么简单的算法能实现的效果,已经不错了,性价比很高。
如果想进一步平滑分割处,有什么办法了?我们既然已经把模型内部的面分成三部分了,那么我们只需要计算会和切割面相交的面和切割面的交点,面和面的交点应该是一条直线,那么应该有2个交点,我们再沿着这条相交线,对模型内部的面进行分割就行了。如果分割后出现非三角面,我们还可以进行三角面片化。具体的实现也不会难如上青天。

作为在学习图形学的苦逼,刚开始弄这些库也不简单。即使会弄,也麻烦得要死。装cgal就得去找教程,按照教程安装。
但是如果有安装好的库之后,只需要配置vs的路径就行了。而且库可以带走,不用多次安装了。我还是有一点分享精神的,我就把我的库都分享出来吧。
CGAL是4.1的,Boost是1.47的,OpenGL里面有glew,还有个freeimage,不过没有glm。glm可以去opengl超级宝典的源代码里面找,不过感觉不是很需要glm,即使需要几何计算,还不如找成熟的几何计算的类。

由于这篇文章访问量较大,而且不少同学留言问过我链接失效的事情,因此再次更新百度云盘的分享链接:
点此下载,密码:15j6。

编译一个程序的时候发现glui一直链接不上去,也许是我的glui版本太低了,结果到最后发现作者设置了lib的附加路径。也许glui2.35真的不能在vs2010下使用了。我下载过好几次,都还是发现glui2.36编译不过去,最后把一个模板实例化移到全局命名空间里面才编译过去了。这里打算讲一下怎么编译对应开发平台下的代码库和编译glui2.36。
我下载过cximage,opencv2.2,cgal,glui等开发库,有些开发库可能一下载下来就有头文件和lib,dll了。但是大多的是下载下来一个工程。而且,这个工程的原始环境还可能和你所用的环境不一样。比如,你下载下来的是vs2005或者vs2008的工程,但是你的环境是vs2010。如果,你在vs2010下面用别人用vs2005编译好的lib就可以链接不成功,这个时候你就需要用vs2010打开工程,重新编译vs2010下的lib。一般情况是打开包含所有工程的workspace,然后点击批生成,把所有需要的lib都生成出来。具体步骤就不用细说了。
但是还是可能遇到其它问题,比如说glui2.36就是编译不过去,因为出现了语法问题。那么就需要发挥你的聪明才智了,经验越多,语法了解的越多,就能够编译过去。我这里再上传,我修改过的glui.236的工程,里面已经包含vs2010下面的各种lib和dll了,放心使用吧。工程和lib,dll都在msvc目录下面,头文件在include里面。
glui2.36

如果是用c,c++这当然是相当熟悉的操作了,也不需要做个记录了。但是现在用的是vs2010版本的vb.net。而现在我有一个大对象,里面存储了整形,浮点,字符串,还有图片。我想将数据归档到文件里面,还有从文件中恢复出来。所以,只能使用二进制的读写方法,顺便记录图片的长度。至于要不要记录字符串的长度,只能问VB.NET了。
读取文件的使用BinaryReader,写文件的使用BinaryWriter。这2个对.net的string也支持二进制读写,所以只需要存储图片的长度了。图片本身当做byte[]存储。这2个类在Imports System.IO空间,都需要从FileStream创建。
虽说对VB.NET不熟悉,但是实现之后还是发现这个东西很好用的。以后还可以用来参考下。

存储的对象的类定义如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Imports System.IO

Public Class Project

'建筑,供水,供电,结构,供暖,环境
Public oldValueFactor(0 To 5) As Double
Public oldValue(0 To 5) As Integer
Public newValueFactor(0 To 5) As Double
Public newValue(0 To 5) As Integer

Public idLen As Integer
Public id As String
Public nameLen As Integer
Public name As String
Public placeLen As Integer
Public place As String
Public situationLen As Integer
Public situation As String

Public oldTotal As Integer '总分
Public newTotal As Integer

Public oldLen As Integer
Public oldPic() As Byte
Public newLen As Integer
Public newPic() As Byte '图片

Public Sub New()
oldLen = 0
newLen = 0
Dim j As Integer
For j = 0 To 5
oldValueFactor(j) = New Double()
oldValue(j) = New Double()
newValueFactor(j) = New Double()
newValue(j) = New Double()
Next
End Sub

End Class

对象类的集合类定义如下。该类支持归档到文件,从文件恢复,查找,添加,删除操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
Imports System.IO

Public Class ProjectSet
Public nNum As Integer
'1000000
Public pros(0 To 100000) As Project

Public Sub New()
nNum = 0
End Sub

Public Sub LoadFromFile(ByVal fileName As String)

Dim Stream As FileStream
Dim BinaryStreamReader As BinaryReader
Try
Stream = New FileStream(fileName, FileMode.Open)
BinaryStreamReader = New BinaryReader(Stream)
nNum = BinaryStreamReader.ReadInt32()
Catch E As Exception
Return
End Try

Dim i As Integer
For i = 0 To nNum - 1
pros(i) = New Project
Dim j As Integer
For j = 0 To 5
pros(i).oldValueFactor(j) = BinaryStreamReader.ReadDouble()
pros(i).oldValue(j) = BinaryStreamReader.ReadInt32()
pros(i).newValueFactor(j) = BinaryStreamReader.ReadDouble()
pros(i).newValue(j) = BinaryStreamReader.ReadInt32()
Next

pros(i).id = BinaryStreamReader.ReadString()
pros(i).name = BinaryStreamReader.ReadString()
pros(i).place = BinaryStreamReader.ReadString()
pros(i).situation = BinaryStreamReader.ReadString()

pros(i).oldTotal = BinaryStreamReader.ReadInt32()
pros(i).newTotal = BinaryStreamReader.ReadInt32()

pros(i).oldLen = BinaryStreamReader.ReadInt32()
pros(i).oldPic = BinaryStreamReader.ReadBytes(pros(i).oldLen)

pros(i).newLen = BinaryStreamReader.ReadInt32()
pros(i).newPic = BinaryStreamReader.ReadBytes(pros(i).newLen)

Next

BinaryStreamReader.Close()
Stream.Close()
End Sub

Public Sub SaveToFile(ByVal fileName As String)

Dim Stream = New FileStream(fileName, FileMode.Create)
Dim BinaryStream As New BinaryWriter(Stream)

BinaryStream.Write(nNum)

Dim i As Integer
For i = 0 To nNum - 1

Dim j As Integer
For j = 0 To 5
BinaryStream.Write(pros(i).oldValueFactor(j))
BinaryStream.Write(pros(i).oldValue(j))
BinaryStream.Write(pros(i).newValueFactor(j))
BinaryStream.Write(pros(i).newValue(j))
Next

BinaryStream.Write(pros(i).id)
BinaryStream.Write(pros(i).name)
BinaryStream.Write(pros(i).place)
BinaryStream.Write(pros(i).situation)

BinaryStream.Write(pros(i).oldTotal)
BinaryStream.Write(pros(i).newTotal)
BinaryStream.Write(pros(i).oldLen)
BinaryStream.Write(pros(i).oldPic)
BinaryStream.Write(pros(i).newLen)
BinaryStream.Write(pros(i).newPic)

Next

BinaryStream.Close()
Stream.Close()

End Sub

Public Sub AddProject(ByVal project As Project)
pros(nNum) = project
nNum += 1
End Sub

Public Sub DeleteProject(ByVal nIndex As Integer)

Dim i As Integer
For i = nIndex To nNum - 2
pros(i) = pros(i + 1)
Next

nNum -= 1

End Sub

Public Function FindProject(ByVal id As String) As Integer

Dim i As Integer
For i = 0 To nNum - 1
If id = pros(i).id Then
Return i
End If
Next
Return -1

End Function

End Class

上面的集合类里面定义了个大数组,是因为我对.NET不熟悉,不知道这样的对象数组定义,产生的是对象集合,还是虚的引用集合。我觉得是引用集合就不用声明数据的大小了吧,结果还是需要声明。而且,读取文件的时候还需要给每个数据项new一个Project。反正不清楚为什么是这样的原因,因为对.NET真的一点不熟悉。

过程基本分四步或者三步走。第一步,加载图片。第二步,灰度化图片。第三步,Canny边缘化,第四步,检测直线或者圆。
首先,说明下OpenCv表示图片的结构是IplImage。这个东西代表了很多有用的东西。接下来的几步,都有该结构的指针对应图片。
第一步,加载图片。可以直接加载成灰度图,或者加载成彩色图,或者加载为原有图片的格式,第二步再灰度化。
直接加载为灰度图,
IplImage* imgZero = cvLoadImage(szZero, CV_LOAD_IMAGE_GRAYSCALE);
加载为彩色图,则把cvLoadImage函数的第二个参数改为CV_LOAD_IMAGE_COLOR,改为CV_LOAD_IMAGE_ANYCOLOR则保持图片原有格式。
第二步,如果第一步不是按灰度图加载,那么在这一步需要灰度化。代码如下,
IplImage *imgGray = NULL;imgGray = cvCreateImage(cvGetSize(imgZero),IPL_DEPTH_8U,1);cvCvtColor(imgZero, imgGray ,CV_BGR2GRAY);

这几句代码的意思是根据原图片大小创建单通道位深为8的图片,然后把原有图片转换为创建的单通道8位深灰度图片。
第三步,canny处理。代码如下,
IplImage *imgCanny = cvCreateImage(cvGetSize(imgZero ),IPL_DEPTH_8U,1);cvCanny(imgGray , imgCanny, 50, 100);
这几句代码的意思是根据原图片大小创建单通道位深为8的图片,然后对灰度图片进行canny处理,处理结果存储在新建立的imgCanny图片中。
第四步,使用识别函数进行识别直线或者圆。
识别直线的函数是cvHoughLines2。识别圆的函数是cvHoughCircles。进行识别之前,先创建存储区。
CvMemStorage *storage = cvCreateMemStorage(0);

识别直线的代码如下,

1
2
3
4
5
6
7
8
9
10
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgZero, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[1].fX = line[0].x;
pts[1].fY = line[0].y;

识别圆的代码如下,
1
2
3
4
5
6
7
8
9
10
11
12
cvClearMemStorage(storage);
CvSeq * cir=NULL;
cir = cvHoughCircles(imgCanny, storage, CV_HOUGH_GRADIENT, 1, imgRecog->width/10 ,80,40, 50);
float * p=(float *)cvGetSeqElem(cir, 0);
CvPoint pt = cvPoint(cvRound(p[0]),cvRound(p[1]));
cvCircle(imgRecog,pt,cvRound(p[2]),CV_RGB(0,255,0));
cvCircle(imgRecog, pt, 5, CV_RGB(0,255,0), -1, 8, 0 );//绘制圆心
cout<<"圆心:"<<pt.x<<","<<pt.y<<endl;
cout<<"半径:"<<p[2]<<endl;
pts[0].fX = pt.x;
pts[0].fY = pt.y;
fR = p[2];

下面再提供一个识别圆和直线的程序的完整代码。识别直线和圆的参数设置,需要自己调节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>
#include <iostream>
#include <fstream>
using namespace std;

#pragma comment(lib, "opencv_core220.lib")
#pragma comment(lib, "opencv_highgui220.lib")
#pragma comment(lib, "opencv_imgproc220.lib")

struct Point
{
double fX;
double fY;
};

Point pts[4];
double fR;

int main()
{
//打开输出文件
ofstream outf("out.txt");
//获取cout默认输出
streambuf *default_buf=cout.rdbuf();
//重定向cout输出到文件
cout.rdbuf( outf.rdbuf() );

char* szZero = "仪表盘0.bmp";
char* szOne = "仪表盘1.bmp";
char* szRecog = "仪表盘.bmp";
IplImage* imgZero = cvLoadImage(szZero, 1);
if (!imgZero) return -1;
IplImage* imgOne = cvLoadImage(szOne, 1);
if (!imgOne) return -1;
IplImage* imgRecog = cvLoadImage(szRecog, 1);
if (!imgRecog) return -1;

IplImage *imgEdge = NULL;
imgEdge = cvCreateImage(cvGetSize(imgRecog),IPL_DEPTH_8U,1);
IplImage *imgCanny = cvCreateImage(cvGetSize(imgRecog),IPL_DEPTH_8U,1);

CvMemStorage *storage = cvCreateMemStorage(0);//内存采用默认大小

cvCvtColor(imgZero, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化检测刻度0直线
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgZero, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[1].fX = line[0].x;
pts[1].fY = line[0].y;

cvCvtColor(imgOne, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化检测刻度1直线
cvClearMemStorage(storage);
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgOne, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[2].fX = line[0].x;
pts[2].fY = line[0].y;

cvCvtColor(imgRecog, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化圆检测
cvClearMemStorage(storage);
CvSeq * cir=NULL;
cir = cvHoughCircles(imgCanny, storage, CV_HOUGH_GRADIENT, 1, imgRecog->width/10 ,80,40, 50);
float * p=(float *)cvGetSeqElem(cir, 0);
CvPoint pt = cvPoint(cvRound(p[0]),cvRound(p[1]));
cvCircle(imgRecog,pt,cvRound(p[2]),CV_RGB(0,255,0));
cvCircle(imgRecog, pt, 5, CV_RGB(0,255,0), -1, 8, 0 );//绘制圆心
cout<<"圆心:"<<pt.x<<","<<pt.y<<endl;
cout<<"半径:"<<p[2]<<endl;
pts[0].fX = pt.x;
pts[0].fY = pt.y;
fR = p[2];

//hough变化检测指针直线
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgRecog, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[3].fX = line[0].x;
pts[3].fY = line[0].y;

static const double PI = atan(1.0) * 4;
double fKZero = (pts[1].fY - pts[0].fY) / (pts[1].fX - pts[0].fX);
double fThetaZero = 180.0 / PI * atan(fKZero);
if (pts[1].fX + 10 < pts[0].fX)//左半部分
{
fThetaZero += 180.0;
}
fThetaZero += 360.0;
if (fThetaZero > 360.0)
{
fThetaZero -= 360.0;
}

double fKOne = (pts[2].fY - pts[0].fY) / (pts[2].fX - pts[0].fX);
double fThetaOne = 180.0 / PI * atan(fKOne);
if (pts[2].fX + 10 < pts[0].fX)//左半部分
{
fThetaOne += 180.0;
}
fThetaOne += 360.0;
if (fThetaOne > 360.0)
{
fThetaOne -= 360.0;
}

double fKRecog = (pts[3].fY - pts[0].fY) / (pts[3].fX - pts[0].fX);
double fThetaRecog = 180.0 / PI * atan(fKRecog);
if (pts[3].fX + 10 < pts[0].fX)//左半部分
{
fThetaRecog += 180.0;
}
fThetaRecog += 360.0;
if (fThetaRecog > 360.0)
{
fThetaRecog -= 360.0;
}

char szAns[100];
sprintf(szAns, "0刻度的角度为:%f,1刻度的角度为:%f,识别刻度的角度为:%f,识别结果为:%.2f",
fThetaZero, fThetaOne, fThetaRecog, (fThetaRecog - fThetaZero) / (fThetaOne - fThetaZero));
cout << szAns << endl;

cvNamedWindow("检测", CV_WINDOW_AUTOSIZE);
cvShowImage("检测", imgRecog);
cvNamedWindow("刻度0", CV_WINDOW_AUTOSIZE);
cvShowImage("刻度0", imgZero);
cvNamedWindow("刻度1", CV_WINDOW_AUTOSIZE);
cvShowImage("刻度1", imgOne);
cvWaitKey(0);

cvDestroyWindow("检测");
cvDestroyWindow("刻度0");
cvDestroyWindow("刻度1");
cvReleaseImage(&imgRecog);
cvReleaseImage(&imgZero);
cvReleaseImage(&imgOne);
cvReleaseImage(&imgEdge);
cvReleaseImage(&imgCanny);
cvReleaseMemStorage(&storage);
}

再给出一个识别图片内所有直线的代码,当然不可能有这么好的效果,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>

#pragma comment(lib, "opencv_core220.lib")
#pragma comment(lib, "opencv_highgui220.lib")
#pragma comment(lib, "opencv_imgproc220.lib")

int main()
{
char* szPic = "pic_6.jpg";
IplImage* imgOrigin = cvLoadImage(szPic, 1);
if (!imgOrigin) return -1;

IplImage *imgEdge = NULL;
imgEdge = cvCreateImage(cvGetSize(imgOrigin),IPL_DEPTH_8U,1);
IplImage *imgCanny = cvCreateImage(cvGetSize(imgOrigin),IPL_DEPTH_8U,1);

CvMemStorage *storage = cvCreateMemStorage(0);//内存采用默认大小

cvCvtColor(imgOrigin, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);

//hough变化检测刻度0直线
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 10, 10 );
for (int i = 0; i < lines->total; ++i)
{
line = (CvPoint*)cvGetSeqElem(lines, i);
cvLine(imgOrigin, line[0], line[1], CV_RGB(255,0,0), 1, CV_AA, 0 );
}

cvNamedWindow("检测", CV_WINDOW_AUTOSIZE);
cvShowImage("检测", imgOrigin);
cvWaitKey(0);
cvDestroyWindow("检测");
cvReleaseImage(&imgOrigin);
cvReleaseImage(&imgEdge);
cvReleaseImage(&imgCanny);
cvReleaseMemStorage(&storage);
}

写这个程序的目的最初是给别人做个作业,最后那个人也不知道要不要,很郁闷。本来不怎么想写的,去网上下了别人的代码,后面发现完全不对头,就拿来做了大幅度的修改,不过使用了下载的图片作为纹理。
这个例子最大的用处是使用OpenGL怎么自动产生球体纹理,自己计算球体的纹理坐标毕竟麻烦了点,虽然也不难。当然还有加载纹理,以及光照,视角设定,特殊键处理等的方法。可以使用上下键放缩和左右键移动。由于使用的是别人的代码修改的,自己感觉一坨糟,也不想去改好了。总之,要模拟出个漂亮的太阳系没那么容易,我的这个比网上有些下载的貌似稍微强点。
先上张图片,再把代码和纹理都贡献了,不过得传到百度网盘了,提供链接。

对于靠近太阳的行星,可以按上键靠近,对于贴了地球纹理那个球体,还可以看到地球的样子了。有兴趣的完全可以把这个太阳系改得再漂亮点。
百度网盘下载

这应该是用vc在程序中添加视频音频播放器最方便的方法了。但是,你如果只是用vs2010或者其它版本工具插入activex控件,然后生成代码,那就不一定能够得到你想要的全部功能。
wmplayer貌似还有个对应的wm sdk。这个可以用com接口的形式更加底层的控制播放器。但是,如果我们能够得到相关的实现播放器功能的代码,我们也能够得到相同程度的控制。前段时间,在网上找了很久,终于找到一大堆有个wmplayer的类代码。用其中的CWMPPlayer4类绑定播放器就能够得到很强大的控制了,用这个类的成员函数可以得到其他控制和设置类的接口等。
用这个东西最大的缺陷是无法播放内存数据,如果是内存数据,必须生成临时文件。最大的优点是简单方便,功能强大,还可以在不同版本的windows平台兼容,只要上面有默认的wmplayer播放器。这个东西还可以播放网络数据,因为文件其实是通过url索引的。因此,在要求简单的时候,还可以用这个东西快速实现一个网络播放器
下面提供wmplayer相关类的代码,WMP

屏蔽右键和全屏都可以在PreTranslateMessage这个虚函数里面处理,比如if (pMsg->message == WM_LBUTTONDBLCLK)
return TRUE;返回TRUE就不会对这个消息进行处理了。屏蔽右键也可以通过调用m_mediaPlayer.put_enableContextMenu(FALSE);处理。
最奇葩的是屏蔽当前播放的是哪个文件的信息,居然有人奇葩到提出这样的需求。找来找去都没有这样的接口,我都直接看wmsdk的文档了,最后想出了个奇葩的办法,就是用一个static控件遮住。
到现在觉得,最好的操作wmplayer的方式是下载微软的wmsdk,从而直接用接口操作,可以提供最大限度的控制。

至于vc内嵌浏览器的方式有很多种,vs2008就可以使用htmldialog了,使用htmlview也行,但是个人觉得还是插入activex控件,然后再绑定变量来的舒服。我就是用插入activex控件再绑定变量的方式的,记得使用vc6就会生产类CWebBrowser2,但是这个类使用起来并不是那么的舒服,所以我对它再进行了一次继承。
注意到,CWebBrowser2继承自CWnd,所以我们继承自CWebBrowser2得类CxExplorer就必须支持mfc里面的动态创建机制,总之这个也很简单,加几句宏就行了,原理其实也不需要弄得太清楚,把mfc的原理弄得太清楚实在太费时间了。。。
我创建的这个浏览器类,除了提供基类的接口外,还提供了几个方便自动填表之类操作的接口。比如,获取text的值,写text的值,点击按钮或者复选框,执行js函数等的功能,由于我基本也就只用过这些功能,所以也就弄了几个这样的接口,也没过多区关注其原理等。
其实,我也没学过太多的com知识,只看过一本com技术内幕的书,更别说com的模板ATL了。所以啊,很多知识也不是很了解。用内嵌浏览器也玩过几次,以前没怎么发掘它的强大应用了。。。
其实,内嵌浏览器很多很好的应用,比如说可以作为客户端,由于内嵌了浏览器,所以不需要实现太多的客户端逻辑,而且服务端直接写web页面就行了,不过速度要求不能太高。还有自动填写表之类的应用,还有写网页游戏的自动玩的功能应该都能发掘出来吧。。。
关于如何判断网页是否加载完成的方法,网上一直说用OnDocumentComplete事件,其实还可能出现网页离线的状态,但是我试了几个方法都检测不出来。所以,最好还是用OnTimer检测和设置你想要的东西。

下面提供这几个类的下载,我用的使用方式是DDX,不使用vs生成的类还可以保持代码的一致性。explorer

使用位图纹理主要需要注意2个地方。1个是非二次方纹理的处理,这个可以参考我上一篇文章的。另外一个是需要注意位图数据的格式,24位的是BGR而不是RGB,32位的是BGRA,而不是RGBA,所以用glTexImage2D之类的函数需要注意把format改成GL_BGR_EXT或者GL_BGRA_EXT,但是internalformat必须是GL_RGB或者GL_RGBA,如果没有进行这个处理的话,颜色效果会变掉的。
下面我给出我的一个任务里面所实现的bitmap位图类,我加载数据的时候使用了以前实现的类CxBitmap,这个也可以从我的另一篇文章中得到。整个类还是比较简单的,你也可以修改加载文件的代码,从而不需要使用CxBitmap类。

CBitmapTex类的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef XPC_YX_BITMAPTEX_H
#define XPC_YX_BITMAPTEX_H
#include

#define RGB16(r,g,b) ( ((r>>3) << 10) + ((g>>3) << 5) + (b >> 3) )
#define RGB24(r,g,b) ( ((r) << 16) + ((g) << 8) + (b) )

class CBitmapTex
{
public:
CBitmapTex() { m_byData = 0; m_szTexName[0] = 0; }
CBitmapTex(const char* pcszFileName) { m_byData = 0; LoadBitmapTex(pcszFileName); }
~CBitmapTex() { if (m_byData) free(m_byData); }
int LoadBitmapTex(const char* pcszFileName);
void InitTex();
void SetTexture();

private:
int m_nWidth;
int m_nHeight;
int m_nBytesPerPixel;
BYTE* m_byData;
char m_szTexName[MAX_PATH];
unsigned m_uTexName;
double m_fTexScaleX;
double m_fTexScaleY;
};

#endif

CBitmapTex的实现如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "bitmapTex.h"
#include "CxBitmap.h"
#include "tool.h"
#include
#include
#include

//该函数只处理了没有压缩的格式
int CBitmapTex::LoadBitmapTex(const char* pcszFileName)
{
CxBitmap* pBitmap = new CxBitmap;
int i;
int nBytesPerLine;

strcpy(m_szTexName, pcszFileName);
if (pBitmap->LoadBitmap(pcszFileName) == NULL)
{
return 0;
}
m_nWidth = pBitmap->GetWidth();
m_nHeight = pBitmap->GetHeight();
m_nBytesPerPixel = pBitmap->GetBytesPerPixel();
m_byData = (BYTE*)malloc(m_nWidth * m_nHeight * m_nBytesPerPixel);
if (m_byData == NULL)
{
return 0;
}
nBytesPerLine = m_nWidth * m_nBytesPerPixel;
if (pBitmap->GetBytesPerLine() == nBytesPerLine)
{
//拷贝数据
memcpy(m_byData, pBitmap->GetBuffer(), m_nHeight * m_nWidth * m_nBytesPerPixel);
}
else//注意文件内部的bitmap数据已经对齐,但是opengl纹理的数据不能对其,所以必须一行行拷贝
{ //否则会出现纹理变形
for (i = 0; i < m_nHeight; ++i) { memcpy(m_byData + i * nBytesPerLine, pBitmap->GetBuffer()
+ i * pBitmap->GetBytesPerLine(), nBytesPerLine);//拷贝数据
}
}

delete pBitmap;

return 1;
}

void CBitmapTex::InitTex()
{
int nFormat;
int nWidthPowerOfTwo = ::Pow2Big(m_nWidth);
int nHeightPowerOfTwo = ::Pow2Big(m_nHeight);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, m_uTexName);
glBindTexture(GL_TEXTURE_2D, m_uTexName);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

if (m_nBytesPerPixel == 3)
{
nFormat = GL_BGR_EXT;
}
else if (m_nBytesPerPixel == 4)
{
nFormat = GL_BGRA_EXT;//bitmap内部数据是bgra格式,所以输入格式必须变化下
}

if (nWidthPowerOfTwo == m_nWidth nHeightPowerOfTwo == m_nHeight)
{
//输出格式必须是GL_RGB,写成GL_BGR_EXT会出现空白
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nWidthPowerOfTwo, nHeightPowerOfTwo,
0, nFormat, GL_UNSIGNED_BYTE, m_byData);
m_fTexScaleX = m_fTexScaleY = 1.0;
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidthPowerOfTwo, nHeightPowerOfTwo,
0, nFormat, GL_UNSIGNED_BYTE, NULL);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_nWidth, m_nHeight,
nFormat, GL_UNSIGNED_BYTE, m_byData);
m_fTexScaleX = 1.0 * m_nWidth / nWidthPowerOfTwo;
m_fTexScaleY = 1.0 * m_nHeight / nHeightPowerOfTwo;
}
}

void CBitmapTex::SetTexture()
{
glBindTexture(GL_TEXTURE_2D, m_uTexName);
//设置纹理堆栈
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScaled(m_fTexScaleX, m_fTexScaleY, 1.0);
}

注意到我的实现中使用了CxBitmap类,你可以从这篇文章中得到。还使用tool.h中的一个函数Pow2Big,该然后是返回一个刚好比输入参数大的2的次方大小,实现很简单。
该类只需要先调用LoadBitmapTex读入数据之后,再调用InitTex初始化opengl的一些纹理设置,最后需要使用哪个纹理,就用哪个纹理对象调用SetTexture函数就行了,非常方便。

也许在某些显卡上面,直接用glTexImage2D是可以使用非2次方纹理的。但是在我的机子上,直接用出现运行错误了。有没有办法让我们的程序同时支持2次方纹理和非二次方纹理了,并且我们还不需要改变纹理坐标了。
有,我们可以判断纹理的宽和高是不是都为2次方的。如果都是,那么直接使用就行了。如果不是,我们先用glTexImage2D申请2次方的纹理,然后用glTexSubImage2D把我们的数据放进去,再计算纹理坐标的变化因子,最后用glMatrixMode进入纹理堆栈操作,用glScaled设置坐标变化因子,这一切做完之后就可以按照非二次方纹理使用了。
下面提供部分代码,大致可以看出操作方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (m_nBytesPerPixel == 3)
{
nFormat = GL_BGR_EXT;
}
else if (m_nBytesPerPixel == 4)
{
nFormat = GL_BGRA_EXT;//bitmap内部数据是bgra格式,所以输入格式必须变化下
}

if (nWidthPowerOfTwo == m_nWidth nHeightPowerOfTwo == m_nHeight)
{
//输出格式必须是GL_RGB,写成GL_BGR_EXT会出现空白
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nWidthPowerOfTwo, nHeightPowerOfTwo,
0, nFormat, GL_UNSIGNED_BYTE, m_byData);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidthPowerOfTwo, nHeightPowerOfTwo,
0, nFormat, GL_UNSIGNED_BYTE, NULL);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_nWidth, m_nHeight,
nFormat, GL_UNSIGNED_BYTE, m_byData);
fTexScaleX = 1.0 * m_nWidth / nWidthPowerOfTwo;
fTexScaleY = 1.0 * m_nHeight / nHeightPowerOfTwo;
//设置纹理堆栈
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScaled(fTexScaleX, fTexScaleY, 1.0);
}