2009年5月4日星期一

不是不想写而是实在没啥可写的了

最近实在是有太多事情了,五一玩过去了,于是乎事情堆得更多了,列举一下吧:
近期任务:
1) 魔方
2) Java词法分析器
3) 专业外语的Presentation
4) 准备亚信杯
5) VHDL实验环境熟悉
6) 计算机网络看书!!!!!!!
7) 复习编译原理!!!!!!!!
8) 看胡事民老师的论文!!!!!
估计在这里面的东西完成几个之前是不会有什么突破了!学习啊是无止境的~

2009年4月13日星期一

约翰 卡马克

昨天看了《DOOM启示录》,准确的说是今天凌晨,看得我是汹涌澎湃,热血燃烧,相信每一个热爱游戏的人看完了都会有同样的感觉。约翰 卡马克,成为了神一般的存在。我多么想成为他那样的人,所以路还很长,很长。。。只有追求,还没有行动。

2009年4月10日星期五

OpenGL (1) 入门

前几天跟着师兄做东西,“被迫”学习了一下OpenGl,这几天又要开始忙着作业啊,考试啊,什么的,赶紧抓个空把学到的东西记录下来,不然一个不小心就“丢”了。

首先讨论一下开发环境配置问题,需要一些DLL、头文件、LIB文件,这些东西网上应该都有:

1) GLU.DLL GLUT.DLLGLUT32.DLL

2) GL.HGLAUX.HGLEXT.HGLU.HGLUT.HWGLEXT.H

3) GLAUX.LIBGLU32.LIBGLUT32.LIBOPENGL32.LIB

由于OpenGL是跨平台的,因此上面的是Win平台下需要的东西。其实GLUT是一个工具包,是Nate Robin开发的,他的主页上有非常详细的教程。GLUTWin的窗口、事件相关的API进行了封装,至少对于初学的我来说用起来十分舒服,而且它也被大家广泛使用的。

当然,我更喜欢原滋原味的东西,因此还是要看看Nehe的教程的。

不说废话了,就以我目前做的一个小例子来说说OpenGL的基本框架吧,当然这里面没有用到光照、雾、纹理等高级的东西,不过有涉及到拾取。

VS2008新建一个控制台程序,源代码如下:

#include

#include

#include

#include

注意这里只需要有GL/GLUT.H就够了,因为其内部包含了对OPENGL的头文件的正确引用。

#define BLACK_F 0.0, 0.0, 0.0

#define RED_F 1.0, 0.0, 0.0

#define GREEN_F 0.0, 1.0, 0.0

#define YELLOW_F 1.0, 1.0, 0.0

#define BLUE_F 0.0, 0.0, 1.0

#define WHITE_F 1.0, 1.0, 1.0

定义了浮点数形式的RGB数值,只是为了编程方便~ *_F”这种表示方法是模仿OpenGL的,因为OpenGL里的很多函数是根据参数的数据类型来命名最后一个的譬如glColor3f(),glColor3d(),分别表示参数为GLfloatGLdouble类型,但是其功能是一模一样的。

#define CUBE_NAME 1

#define PI 3.1415926

#define CUBE_SIZE 2.0

#define HEIGHT 600

#define WIDTH 800

#define SELECT_BUFF_SIZE 512

GLint wndWidth, wndHeight;

全局的变量用来记录当前的窗口宽和高

GLfloat angle = 0.0;

//

// Rotate Axis

//

GLfloat axis[ 3 ] = { 0.0, 0.0, 0.0 };

//

// Mouse location

//

GLfloat lastPos[ 3 ], currentPos[ 3 ];

//

// Camera Properties

//

GLfloat eye[3] = { 0.0, 0.0, 4.0 };

GLfloat at[3] = { 0.0, 0.0, 0.0 };

GLfloat up[3] = { 0.0, 1.0, 0.0 };

GLfloat lastMatrix[ 16 ] =

{1.0, 0.0, 0.0, 0.0,

0.0, 1.0, 0.0, 0.0,

0.0, 0.0, 1.0, 0.0,

0.0, 0.0, 0.0, 1.0};

GLfloat selectCopy[ 16 ] =

{1.0, 0.0, 0.0, 0.0,

0.0, 1.0, 0.0, 0.0,

0.0, 0.0, 1.0, 0.0,

0.0, 0.0, 0.0, 1.0};

unsigned char btnState;

GLboolean isSelected;

//

// Project a 2D mouse coordinates to a 3D sphere

//

int ProjectSphere( GLint x, GLint y, GLint r, GLfloat v[ 3 ] )

{

GLfloat z;

v[0] = ( GLfloat ) x * 2.0 - ( GLfloat ) r;

v[1] = ( GLfloat ) r - ( GLfloat ) y * 2.0;

z = r * r - v[0] * v[0] - v[1] * v[1];

if (z <>

{

return 0;

}

v[2] = sqrt( z );

//

// Normalise

//

v[0] /= ( GLfloat ) r;

v[1] /= ( GLfloat ) r;

v[2] /= ( GLfloat ) r;

return !0;

}

这个函数是将二维的鼠标坐标映射到一个单位球面上,通过它可以实现鼠标控制摄像机。

void Init( void )

{

glClearColor( WHITE_F, 0.0 );

glShadeModel( GL_FLAT );

//glEnable( GL_DEPTH_TEST );

}

做一些初始化的操作glClearColor()设置了清除颜色缓冲区时所试用的颜色,glShadeModel()则指明了着色方案,你只有两种选择GL_FLAT 或者是GL_SMOOTH,默认是后者

void Display( void )

{

glClear( GL_COLOR_BUFFER_BIT );

glClear( GL_DEPTH_BUFFER_BIT );

清除颜色缓冲区和深度缓冲区

glColor3f( RED_F );

设置了我们画东西需要用什么颜色来画

glMatrixMode( GL_MODELVIEW );

调整模型视图矩阵,这里需要特殊说明一下:

OpenGL在完成绘制前一般有几个步骤:

1) 调整投影矩阵, 简洁来说就是把照相机放好,放到哪个位置,朝哪个方向。

2) 调整模型视图矩阵,对物体进行的一系列变换,譬如我们要让它旋转、平移等。

3) 调整视口, 摄像机看到的东西要映射到屏幕上,需要调整映射到哪里的问题。

4) 绘制

glLoadIdentity();

//

// Apply the new transform with the old one

//

glRotatef( angle, axis[ 0 ], axis[ 1 ], axis[ 2 ] );

glMultMatrixf( lastMatrix );

首先进行了旋转,然后乘上了之前的变换矩阵,然而由于矩阵乘法顺序的问题,实际上我们等于先做了lastMatrix的变换,然后才有旋转的效果。

//

// Save as last Matrix

//

glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);

glutSolidCube( CUBE_SIZE );

绘制函数,这里我们只是调用了一个GLUT的一个工具函数,画了一个实心的立方体,其实很多时候要绘制什么需要我们自己定义的,调用glDraw***一类的函数,然后放到glBegin()和glEnd()之间就可以了。。。

glutSwapBuffers();

为了实现旋转效果的平滑过渡,我们用了双缓冲,在后面的初始化里有这样的设置,当然你也可以设置成单缓冲,那么这里一定要有glFlush()

}

以上就是画好我们想要的东西,然后把这个函数设置为回调函数就可以了。那么对于用户输入的响应或者一些变换需要在下面实现:

void Reshape( GLsizei w, GLsizei h )

{

glViewport( 0, 0, w, h );

glMatrixMode( GL_PROJECTION );

glLoadIdentity();

gluPerspective( 60.0, ( GLfloat ) w / ( GLfloat ) h, 0.1, 8.0 );

gluLookAt( eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2] );

对投影矩阵进行变换,之所以没有把它放到Display函数里,是因为当我们改变窗口大小的时候,会导致投影矩阵做相应的变换,同样视口也一样

//

// New window size

//

wndHeight = h;

wndWidth = w;

}

GLboolean JudgeSelect( GLint x, GLint y )

{

GLuint selectBUf[ SELECT_BUFF_SIZE ];

GLint hitNum;

GLint viewPort[ 4 ];

//

// Judge if select the cube

//

glGetIntegerv( GL_VIEWPORT, viewPort );

glSelectBuffer( SELECT_BUFF_SIZE ,selectBUf );

glRenderMode( GL_SELECT );

glInitNames();

glPushName( 0 );

glMatrixMode( GL_PROJECTION );

glPushMatrix();

glLoadIdentity();

//

// Set a small area to be hitted

//

gluPickMatrix( ( GLdouble ) x, ( GLdouble ) ( viewPort[ 3 ] - y ), 5.0, 5.0, viewPort );

gluPerspective( 60.0, ( GLfloat ) wndWidth / ( GLfloat ) wndHeight, 0.1, 8.0 );

gluLookAt( eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2] );

glMatrixMode( GL_MODELVIEW );

glPushMatrix();

memcpy( selectCopy, lastMatrix, 16 * sizeof( lastMatrix[ 0 ] ) );

glLoadIdentity();

//

// Apply the new transform with the old one

//

glRotatef( angle, axis[ 0 ], axis[ 1 ], axis[ 2 ] );

glMultMatrixf( lastMatrix );

//

// Save as last Matrix

//

glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);

glLoadName( CUBE_NAME );

glutSolidCube( CUBE_SIZE );

glPopMatrix();

memcpy( lastMatrix, selectCopy, 16 * sizeof( lastMatrix[ 0 ] ) );

glMatrixMode( GL_PROJECTION );

glPopMatrix();

glFlush( );

hitNum = glRenderMode( GL_RENDER );

if( hitNum <= 0 ) {

return FALSE;

} else {

return TRUE;

}

}

这一部分是用来实现选择的,即如果用户没有选择立方体,那么拖动鼠标就不会旋转,选择的机制这里先略过,因为我还没有彻底搞懂。。。,从结果上看,就是hitNum来记录点击的次数。

void Mouse( GLint button, GLint state, GLint x, GLint y)

{

//

// Record mouse state

//

btnState = (unsigned char) button;

btnState <<= 4;

btnState |= (unsigned char)state;

switch ( button )

{

case GLUT_LEFT_BUTTON:

//

// Initial the lastPos

//

ProjectSphere( x, y, wndHeight, lastPos );

isSelected = JudgeSelect( x, y );

break;

}

}

这个函数也是一个回调函数,用来处理鼠标事件,当点击鼠标之后我们就要准备进行旋转了,但是也不一定,isSelected会告诉我们是否点到了立方体上,是否需要旋转。

void Motion( GLint x, GLint y )

{

GLfloat d, dx, dy, dz;

if( ! isSelected ) {

return;

}

if ( ( ( btnState >> 4 ) & 0x0F) == GLUT_LEFT_BUTTON )

{

if( ! ProjectSphere( x, y, wndHeight, currentPos ) )

{

return;

}

dx = currentPos[ 0 ] - lastPos[ 0 ];

dy = currentPos[ 1 ] - lastPos[ 1 ];

dz = currentPos[ 2 ] - lastPos[ 2 ];

//

// If mouse has moved

//

if (dx || dy || dz)

{

//

// Moving angle

//

d = sqrt( dx * dx + dy * dy + dz * dz );

angle= d * 180.0;

//

// The nomal vector of the move plane

//

axis[0] = lastPos[1] * currentPos[2] - lastPos[2] * currentPos[1];

axis[1] = lastPos[2] * currentPos[0] - lastPos[0] * currentPos[2];

axis[2] = lastPos[0] * currentPos[1] - lastPos[1] * currentPos[0];

//

// Record the old coordinates

//

lastPos[0] = currentPos[0];

lastPos[1] = currentPos[1];

lastPos[2] = currentPos[2];

}

glutPostRedisplay();

}

}

鼠标移动的回调函数,这里我们实现了旋转需要的数学运算,最关键的是记得最后要有glutPostRedisplay()这个函数,它告诉窗体要重绘。

GLint main( GLint argc, char** argv )

{

glutInit( &argc, argv );

glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );/* | GLUT_DEPTH );*/

glutInitWindowSize( WIDTH, HEIGHT );

glutInitWindowPosition( 100, 100 );

glutCreateWindow( "BLIZZARD" );

同样是一些初始化的工作,可以都一起扔到Init()函数里面。。。

Init();

glutDisplayFunc( Display );

glutReshapeFunc( Reshape );

glutMouseFunc( Mouse );

glutMotionFunc( Motion );

glutMainLoop();

这里就是对上面所有的回调函数进行绑定。GlutMainLoop则进入消息循环,里面会有一些默认的处理机制,譬如点窗口的X,就会关掉窗口等。。。

return 0;

}

使用glut工具包,确实非常方便,简化了和系统相关的编程部分。。。另外尽量使用GL开头的数据类型,这个是跨平台的问题了。。。

正则表达式 读书笔记 (一)

正则表达式是一种用来匹配和处理文本的字符串,主要就是实现字符串的搜索和替换功能。Firefox里有一个Regular Expression的插件,可以用来做一些简单的正则表达式的练习。

1) 匹配纯文本

2) . 字符可以匹配任何一个单个的字符(包括字符、字母、数字甚至是 . 字符本身)

3) 匹配特殊字符,需要使用转义字符 \

4) 匹配多个字符中的某一个,使用字符集合, 例如 [0-9]

5) 取非匹配,例如 [^0-9]

6) \f 换页符

\n 换行符

\r 回车符

\t 制表符

\v 垂直制表符

\d 任何一个数字字符

\D 任何一个非数字字符

\w 任何一个字母数字字符或者是下划线字符

\W 任何一个非字符数字且非下划线字符

\s 任何一个空白字符

\S 任何一个非空白字符

7) 匹配一个字符一次或多次重复,只需要加上后缀 + 例子:\w+

(未完)

创建一个窗口 -- From GameTutorials, LLC

#include // 必须的头文件
#include

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
// LRESULT 是一个32位的数值,对于一个回调函数其返回类型是LRESULT。
// 其定义为:
// typedef LONG_PTR LRESULT;
// 关于回调函数(CALLBACK),它可以和一个窗口类相关联。
//MSDN中的解释是:CALLBACK Calling convention for callback functions.
//
//This type is declared in WinDef.h as follows:
//
//
//#define CALLBACK __stdcall

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow )
// 同CALLBACK一样 WINAPI 也是 Calling convention for the Win32 API,
// 个人目前无法理解。。。
// WinMain各个参数的解释:
// HINSTANCE hInstance -- 应用程序的实例信息,一个程序可以有两个实例同时运行(开两个QQ)
// HINSTANCE hPrevInstance -- 一般情况下为NULL,没有实际用处。
// PSTR szCmdLine -- 传给应用程序的参数,PSTR就是字符串。
// int iCmdShow -- 保存关于窗口的信息,诸如最大化、最小化等等。
{
HWND hwnd; // HWND -- 窗口句柄,用它来追踪窗口,应用程序可以有多个窗口。
MSG msg; // MSG -- 消息,传递给窗口的。
WNDCLASSEX wndclass; // WNDCLASSEX -- 窗口类,存有窗口的常用信息如图标、标题、大小等。

// 设置窗口属性。
wndclass.cbSize = sizeof( wndclass );
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc; // 指定窗口过程,告知窗口去调用哪个回调函数来处理窗口消息。
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0; // 通常都赋值为0
wndclass.hInstance = hInstance; // ?不解啊
// 设置图标和箭头,空值为HINSTANCE类型。
wndclass.hIcon = LoadIcon( NULL, IDI_WINLOGO );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = ( HBRUSH ) GetStockObject( WHITE_BRUSH );
// The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes.
// 返回值是VOID* 因此需要类型转换。
wndclass.lpszMenuName = NULL;
// 为窗口类起的名字,在创建窗口时将会用到。
wndclass.lpszClassName = TEXT( "Window Class" );
wndclass.hIconSm = LoadIcon( NULL, IDI_WINLOGO );

RegisterClassEx( &wndclass ); // 注册窗口类。

hwnd = CreateWindow( TEXT( "Window Class" ),
TEXT( "BLIZZARD Window" ), // 窗口标题。
WS_OVERLAPPEDWINDOW, // 窗口类型。
CW_USEDEFAULT, // 初始X位置。
CW_USEDEFAULT, // 初始Y位置。
CW_USEDEFAULT, // 初始X大小。
CW_USEDEFAULT, // 初始Y大小。
NULL, // 父窗口句柄。
NULL, // 菜单句柄。
hInstance, // 实例句柄。
NULL // 可以利用最后一个参数给WndProc传递变量,当然也可以用全局变量的方式。
);

ShowWindow( hwnd, iCmdShow ); // MSDN中的注释:The ShowWindow function sets the specified window's show state.
// 因此不能从翻译上理解ShowWindow函数调用后会显示窗口。
UpdateWindow( hwnd ); // 绘制窗口,会向窗口发送一个WM_PAINT消息。

// 消息循环。
// 如果收到WM_QUIT消息将返回0。
while( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg ); // 将消息发送给窗口过程。
}

UnregisterClass( TEXT( "Window Class" ), hInstance );

return msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{
switch( iMsg ) {
case WM_CREATE : // 创建窗口。
break;
case WM_SIZE : // 改变窗口大小。
break;
case WM_PAINT : // 窗口重绘。
break;
case WM_DESTROY :
PostQuitMessage( 0 ); // 必须调用PostQuitMessage。
break;
}
return DefWindowProc( hwnd, iMsg, wParam, lParam );
// 调用DefWindowProc采用默认方式处理未处理消息。
}



学习WIN 32离不开MSDN,这点深有体会,许多东西必须查!

WIN 32 编程的一般步骤是:

1) 初始化窗口类。

2) 注册窗口类。

3) 创建窗口。

4) 从窗口过程获取消息。

5) 处理消息。