2014年1月14日星期二(DEMO7-2,加载3D线框立方体物体模型)
上个DEMO,是渲染列表,这个DEMO,进行了加载PLG模型,仍然是一步步地进行。
PLG模型首行包含了物体名称、顶点数和多边形数3部分组成。
加载模型时可以每次读取一行,并对其中的数字进行分析。
现在开始进行代码。
先设置摄像机坐标和位置,朝向,放缩比例,这里用的是物体,不是渲染列表。
// initialize camera position and direction
POINT4D cam_pos = {0,0,0,1};
VECTOR4D cam_dir = {0,0,0,1};
// all your initialization code goes here...
VECTOR4D vscale={5.0,5.0,5.0,1},
vpos = {0,0,0,1},
vrot = {0,0,0,1};
OBJECT4DV1 obj;
读取一行数据换行的辅助函数
Char * ddraw_liushuixian::Get_Line_PLG(char * buffer,int maxlength,FILE * fp )
{
//跳过PLG文件中的注释和空行,返回一整行数据,如果文件为空,返回NULL
int index = 0; //索引
int length = 0; //长度
//进入分析循环
while ( 1 )
{
//读取下一行
if ( ! fgets(buffer, maxlength, fp ))
{
return NULL;
}
//计算空格数
for ( length = strlen(buffer),index = 0; isspace(buffer[index]);index ++ );
//检查是否是空行或注释
if ( index >= length || buffer[index] == '#')
{
continue;;
}
//此时得到一个数据行
return ( &buffer[index]);
}
}
定义一些物体状态
#define OBJECT4DV1_STATE_ACTIVE 0x0001
#define OBJECT4DV1_STATE_VISIBLE 0x0002
#define OBJECT4DV1_STATE_CULLED 0x0004
加上一个函数计算平均半径和最大半径
void ddraw_liushuixian::Compute_OBJECT3DV1_RADIUS(OBJECT4DV1_PTR obj )
{
obj->avg_radius = 0;
obj->max_radius = 0;
for ( int vertex = 0; vertex < obj->num_vertices;vertex++ )
{
float dist_to_vertex = sqrt( obj->vlist_local[vertex].x *obj->vlist_local[vertex].x +
obj->vlist_local[vertex].y *obj->vlist_local[vertex].y +
obj->vlist_local[vertex].z *obj->vlist_local[vertex].z );
obj->avg_radius +=dist_to_vertex;
if ( dist_to_vertex > obj->max_radius )
{
obj->max_radius =dist_to_vertex;
}
}
obj->avg_radius /=obj->num_vertices;
}
就是一般的算法。
单双面标志
#define PLX_1SIDED_FLAG 0
#define PLX_2SIDED_FLAG 1
//顶点属性
#define POLY4DV1_ATTR_2SIDED 0x0001
#define POLY4DV1_ATTR_TRANSPARENT 0x0002
#define POLY4DV1_ATTR_8BITCOLOR 0x0004
#define POLY4DV1_ATTR_RGB16 0x0008
#define POLY4DV1_ATTR_RGB24 0x0010
//颜色模式
#define PLX_COLOR_MODE_RGB_FLAG 0x8000
#define PLX_COLOR_MODE_INDEXED_FLAG 0x0000
//各种掩码
#define PLX_RGB_MASK 0x8000
#define PLX_SHADE_MODE_MASK 0x6000
#define PLX_2SIDED_MASK 0x1000
#define PLX_COLOR_MASK 0x0fff
//模型着色模式
#define PLX_SHADE_MODE_PURE_FLAG 0x0000
#define PLX_SHADE_MODE_CONSTANT_FLAG 0x0000
#define PLX_SHADE_MODE_FLAT_FLAG 0x2000
#define PLX_SHADE_MODE_GOURAUD_FLAG 0x4000
#define PLX_SHADE_MODE_PHONG_FLAG 0x6000
#define PLX_SHADE_MODE_FASTPHONG_FLAG 0x6000
与之对应的,是顶点着色模式
#define POLY4DV1_ATTR_SHADE_MODE_PURE 0x0020
#define POLY4DV1_ATTR_SHADE_MODE_CONSTANT 0x0020
#define POLY4DV1_ATTR_SHADE_MODE_FLAT 0x0040
#define POLY4DV1_ATTR_SHADE_MODE_GOURAUD 0x0080
#define POLY4DV1_ATTR_SHADE_MODE_PHONG 0x0100
#define POLY4DV1_ATTR_SHADE_MODE_FASTPHONG 0x0100
#define POLY4DV1_ATTR_SHADE_MODE_TEXTURE 0x0200
下面开始加载PLG模型
int ddraw_liushuixian::Load_OBJECT4DV1_PLG( OBJECT4DV1_PTR obj,char * filename, VECTOR4D_PTR scale, VECTOR4D_PTR pos, VECTOR4D_PTR rot )
{
FILE * fp; //文?件t指?针?
char buffer[256]; //缓o冲?区?
char * token_string; //指?向¨°要°a分¤?析?的Ì?物?体¬?数ºy据Y文?本À?的Ì?指?针?
//先¨¨找¨°到Ì?物?体¬?描¨¨述º?符¤?
//第̨²一°?步?清?空?和¨ª初?始º?化¡¥OBJ
memset( obj, 0, sizeof(OBJECT4DV1) );
//将?物?体¬?状Á¡ä态¬?设¦¨¨置?为a可¨¦见?和¨ª活?动¡¥的Ì?
obj->state = OBJECT4DV1_STATE_ACTIVE | OBJECT4DV1_STATE_VISIBLE;
//设¦¨¨置?物?体¬?的Ì?位?置?
obj->world_pos.x = pos->x;
obj->world_pos.y = pos->y;
obj->world_pos.z = pos->z;
obj->world_pos.w = pos->w;
//第̨²2步?,ê?读¨¢取¨?文?件t
fp = fopen( filename,"r" );
//第̨²3步?,ê?读¨¢取¨?物?体¬?描¨¨述º?符¤?
token_string = Get_Line_PLG(buffer, 255, fp );
//分¤?析?物?体¬?描¨¨述º?符¤?
sscanf(token_string, "%s %d %d", obj->name, & obj->num_vertices, & obj->num_polys );
//第̨²4步?:êo加¨®载?顶£¤点Ì?列¢D表À¨ª
for ( int vertex = 0; vertex < obj->num_vertices; vertex++ )
{
token_string = Get_Line_PLG( buffer, 255, fp );
//分¤?析?顶£¤点Ì?
sscanf( token_string, "%f %f %f", & obj->vlist_local[vertex].x, & obj->vlist_local[vertex].y, & obj->vlist_local[vertex].z );
obj->vlist_local[vertex].w = 1;
//缩?放¤?顶£¤点Ì?坐Á?标À¨º
obj->vlist_local[vertex].x *= scale->x;
obj->vlist_local[vertex].y *= scale->y;
obj->vlist_local[vertex].z *= scale->z;
}
//计?算?平?均¨´半ã?径?和¨ª最Á?大䨮半ã?径?
Compute_OBJECT3DV1_RADIUS(obj);
int poly_surface_desc = 0; //PLG/PLX多¨¤边À?形?描¨¨述º?符¤?
int poly_num_verts = 0; //当Ì¡À前¡ã多¨¤边À?形?得Ì?顶£¤点Ì?数ºy(这a里¤?设¦¨¨定¡§为a3)ê?
char tmp_string[8]; //存ä?储ä¡é多¨¤边À?形?描¨¨述º?符¤?的Ì?字Á?符¤?串ä?
//第̨²5步?:êo加¨®载?多¨¤边À?形?列¢D表À¨ª
for( int poly = 0; poly < obj->num_polys; poly++ )
{
token_string = Get_Line_PLG( buffer, 255,fp );
//假¨´定¡§所¨´有®D模¡ê型¨ª都?是º?由®¨¦三¨y角?形?组Á¨¦成¨¦,ê?则¨°每?个?多¨¤边À?形?都?有®D3个?顶£¤点Ì?,ê?将?面?描¨¨述º?符¤?、¡é顶£¤点Ì?数ºy和¨ª顶£¤点Ì?列¢D表À¨ª存ä?储ä¡é到Ì?变À?量¢?中D
sscanf( token_string, "%s %s %d %d %d", tmp_string, & poly_num_verts, & obj->plist[poly].vert[0],
& obj->plist[poly].vert[1], & obj->plist[poly].vert[2]);
//对?于®¨²0x开a头ª¡¤的Ì?16进?制?进?行D检¨¬测a
if ( tmp_string[0] == '0' && toupper( tmp_string[1]) == 'X' )
{
sscanf( tmp_string, "%x", & poly_surface_desc );
}
else
poly_surface_desc = atoi( tmp_string );
//让¨?多¨¤边À?形?顶£¤点Ì?列¢D表À¨ª指?向¨°物?体¬?的Ì?顶£¤点Ì?列¢D表À¨ª
obj->plist[poly].vlist = obj->vlist_local;
//存ä?储ä¡é顶£¤点Ì?列¢D表À¨ª和¨ª多¨¤边À?形?顶£¤点Ì?索¡Â引°y值¦Ì后¨®,ê?分¤?析?多¨¤边À?形?描¨¨述º?符¤?,ê?并¡é据Y此ä?相¨¤应®|地Ì?设¦¨¨置?多¨¤边À?形?
//提¬¨¢取¨?多¨¤边À?形?描¨¨述º?符¤?中D的Ì?每?个?位?字Á?段?
if( poly_surface_desc & PLX_2SIDED_FLAG )
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_2SIDED );
}
//设¦¨¨置?颜?色¦?模¡ê式º?
//如¨?果?是º?16位?
if ( poly_surface_desc & PLX_COLOR_MODE_RGB_FLAG)
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_RGB16 );
//提¬¨¢取¨?RGB值¦Ì
int red = ( ( poly_surface_desc & 0x0f00) >> 8 );
int greeen = ( ( poly_surface_desc & 0x00f0) >> 4 );
int blue = ( poly_surface_desc & 0x000f );
obj->plist[poly].color = _RGB16BIT565( red * 16, greeen * 16, blue * 16 );
}
else
{
//使º1用®?8位?颜?色¦?索¡Â引°y
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_8BITCOLOR );
//提¬¨¢取¨?最Á?后¨®的Ì?8位?即¡ä可¨¦得Ì?到Ì?颜?色¦?索¡Â引°y
obj->plist[poly].color = ( poly_surface_desc & 0x00ff );
}
//处ä|理¤¨ª着Á?色¦?模¡ê式º?
int shade_mode = ( poly_surface_desc & PLX_SHADE_MODE_MASK );
//设¦¨¨置?多¨¤边À?形?的Ì?着Á?色¦?模¡ê式º?
switch ( shade_mode )
{
case PLX_SHADE_MODE_PURE_FLAG:
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PURE );
}
break;
case PLX_SHADE_MODE_FLAT_FLAG:
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_FLAT );
}
break;
case PLX_SHADE_MODE_GOURAUD_FLAG:
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD );
}
break;
case PLX_SHADE_MODE_PHONG_FLAG:
{
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PHONG );
}
break;
default:
break;
}
//最Á?后¨®将?多¨¤边À?形?设¦¨¨置?为a活?动¡¥状Á¡ä态¬?
obj->plist[poly].state = POLY4DV1_STATE_ACTIVE;
}
//关?闭À?文?件t
fclose( fp );
return ( 1 );
}
2014年1月20日星期一(继续DEMO7_2)
继续进行,设置摄像机。摄像机不变,在Game_Init()中加载模型
liushuixian.Load_OBJECT4DV1_PLG( & obj, "cube1.plg", & vscale, & vpos, & vrot );
//设置模型在世界坐标系的位置
obj.world_pos.x = 0;
obj.world_pos.y = 0;
obj.world_pos.z = 100;
下一步就是在Game_main()中帧循环了
先重置模型,这里只是与背面消除有关,
背面消除在世界空间进行,只是看观察方向(物体->摄像机)与物体面法线夹角,小于90度则看到,大于90度则看不到,则可以消除,或者根据两向量的点乘,>0则看到,<0则看不到
void ddraw_liushuixian::Reset_OBJECT4DV1( OBJECT4DV1_PTR obj )
{
RESET_BIT( obj->state, OBJECT4DV1_STATE_CULLED);
for( int poly = 0; poly < obj->num_polys; poly ++ )
{
POLY4DV1_PTR curr_poly = & obj->plist[poly];
if ( ! curr_poly->state & POLY4DV1_STATE_ACTIVE)
{
continue;
}
RESET_BIT( curr_poly->state, POLY4DV1_STATE_CLIPPED );
RESET_BIT( curr_poly->state, POLY4DV1_STATE_BACKFACE );
}
}
下一步对物体进行放缩和旋转,和渲染列表类似。只是对物体的局部顶点进行放缩和变换,并设置了是否考虑朝向,其实就是局部坐标轴的变换
void ddraw_liushuixian::Transform_OBJECT4DV1(OBJECT4DV1_PTR obj,
MATRIX4X4_PTR mt, //变换矩阵
int coord_select,
int transform_basis,
ddraw_math math2 ) //指定要变换的坐标
{
switch( coord_select )
{
case TRANSFORM_LOCAL_ONLY:
{
//对?物?体¬?的Ì?每?个?局?部?/模¡ê型¨ª顶£¤点Ì?坐Á?标À¨º进?行D变À?换?
for( int vertex = 0; vertex < obj->num_vertices; vertex++ )
{
POINT4D presult; //用®?于®¨²暂Y时º¡À存ä?储ä¡é变À?换?结¨¢果?
math2.Mat_Mul_VECTOR4D_4X4( & obj->vlist_local[vertex], mt, & presult );
math2.VECTOR4D_COPY( & obj->vlist_local[vertex], & presult );
}
}
break;
case TRANSFORM_TRANS_ONLY:
{
for(int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
//使º1用®?矩?阵¨®mt对?顶£¤点Ì?进?行D变À?换?
POINT4D presult; //用®?于®¨²暂Y时º¡À存ä?储ä¡é变À?换?结¨¢果?
math2.Mat_Mul_VECTOR4D_4X4( & obj->vlist_trans[vertex], mt, & presult );
math2.VECTOR4D_COPY( & obj->vlist_trans[vertex], & presult );
}
}
break;
case TRANSFORM_LOCAL_TO_TRANS:
{
for( int vertex = 0; vertex < obj->num_vertices; vertex++ )
{
//使º1用®?矩?阵¨®mt对?顶£¤点Ì?进?行D变À?换?
math2.Mat_Mul_VECTOR4D_4X4( & obj->vlist_local[vertex], mt, & obj->vlist_trans[vertex] );
}
}
break;
default:
break;
}
//最Á?后¨®检¨¬查¨¦是º?否¤?对?朝¡¥向¨°向¨°量¢?进?行D变À?换?
if ( transform_basis)
{
//旋y转Áa物?体¬?的Ì?朝¡¥向¨°向¨°量¢?
VECTOR4D vresult; //用®?于®¨²存ä?储ä¡é旋y转Áa结¨¢果?
//旋y转Áaux
math2.Mat_Mul_VECTOR4D_4X4( & obj->ux, mt, & vresult );
math2.VECTOR4D_COPY( & obj->ux, & vresult );
//旋y转Áauy
math2.Mat_Mul_VECTOR4D_4X4( & obj->uy, mt, & vresult );
math2.VECTOR4D_COPY( & obj->uy, & vresult );
//旋y转Áa uz
math2.Mat_Mul_VECTOR4D_4X4( & obj->uz, mt, & vresult );
math2.VECTOR4D_COPY( & obj->uz, & vresult );
}
}
在每帧中,进行有朝向
liushuixian.Transform_OBJECT4DV1( &obj, &mrot, TRANSFORM_LOCAL_ONLY, 1, * math );
下一步进行模型从模型坐标系到世界坐标系的转换,(即平移),只是不用列表中的多个物体,只有一个而已。
void ddraw_liushuixian::Model_To_World_OBJECT4DV1( OBJECT4DV1_PTR obj, ddraw_math math2,int coord_select )
{
if( coord_select == TRANSFORM_LOCAL_TO_TRANS )
{
//满足条件对其进行变换
for( int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
//平移顶点 math2.VECTOR4D_ADD( & obj->vlist_local[vertex], & obj->world_pos, & obj->vlist_trans[vertex] );
}
}
else
{
for( int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
//平移顶点
math2.VECTOR4D_ADD( & obj->vlist_trans[vertex], & obj->world_pos, & obj->vlist_trans[vertex] );
}
}
每帧改为
liushuixian.Model_To_World_OBJECT4DV1( & obj,* math );
相机变换矩阵一样
下面看下世界坐标到相机坐标的变换
void ddraw_liushuixian::World_To_Camera_OBJECT4DV1( ddraw_math math2, OBJECT4DV1_PTR obj, CAM4DV1_PTR cam )
{
for( int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
//使用相机对象中的矩阵mcma对顶点进行变换
POINT4D presult; //用于存储每次变换的结果
//对顶点进行变换
math2.Mat_Mul_VECTOR4D_4X4( &obj->vlist_trans[vertex], &cam->mcam, &presult );
//将结果存回去
math2.VECTOR4D_COPY( & obj->vlist_trans[vertex], &presult );
}
}
下面进行摄像机到透视坐标转换
void ddraw_liushuixian::Camera_To_Perspective_OBJECT4DV1( OBJECT4DV1_PTR obj, CAM4DV1_PTR cam )
{
for( int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
float z = obj->vlist_trans[vertex].z;
//根据相机的观察参数对顶点进行变换
obj->vlist_trans[vertex].x = cam->view_dist * obj->vlist_trans[vertex].x / z;
obj->vlist_trans[vertex].y = cam->view_dist * obj->vlist_trans[vertex].y * cam->aspect_ratio / z;
}
}
下面进行透视坐标到屏幕坐标的转换
void ddraw_liushuixian::Perspective_To_Screen_OBJECT4DV1( OBJECT4DV1_PTR obj, CAM4DV1_PTR cam )
{
float alpha = ( 0.5 * cam->viewport_width - 0.5 );
float beta = ( 0.5 * cam->viewport_height - 0.5 );
//满足条件,对其进行变换
for( int vertex = 0; vertex < obj->num_vertices; vertex ++ )
{
//顶点的透视坐标是归一化的,取值范围为-1到1,对坐标进行缩放,并反转Y轴
obj->vlist_trans[vertex].x = alpha + alpha * obj->vlist_trans[vertex].x;
obj->vlist_trans[vertex].y = beta - beta * obj->vlist_trans[vertex].y;
}
}
现在看每一帧
liushuixian.Perspective_To_Screen_OBJECT4DV1( &obj, & cam );
最后需要绘制,线框模式+着色,基本类似,每个三角形面三个顶点,连线加色。代码如下所示
void ddraw_liushuixian::Draw_OBJECT4DV1_Wire16( ddraw_math math2, OBJECT4DV1_PTR obj, UCHAR * video_buffer,int lpitch )
{
for( int poly = 0; poly < obj->num_polys; poly++ )
{
//当且仅当多边形没有被剔除或者裁剪掉,同时处于活动状态且可见时,才对其进行变换)
if( !( obj->plist[poly].state & POLY4DV1_STATE_ACTIVE ) ||
( obj->plist[poly].state & POLY4DV1_STATE_CLIPPED ) ||
( obj->plist[poly].state & POLY4DV1_STATE_BACKFACE ) )
continue; //进入下一个多边形
int vindex_0 = obj->plist[poly].vert[0];
int vindex_1 = obj->plist[poly].vert[1];
int vindex_2 = obj->plist[poly].vert[2];
//绘制三角形的边
//2d初始化过程中已经设置好裁剪,位于2D屏幕/窗口外的多边形都将被裁剪掉
math2.Draw_Clip_Line16(
obj->vlist_trans[vindex_0].x,
obj->vlist_trans[vindex_0].y,
obj->vlist_trans[vindex_1].x,
obj->vlist_trans[vindex_1].y,
obj->plist[poly].color,
video_buffer, lpitch );
math2.Draw_Clip_Line16(
obj->vlist_trans[vindex_1].x,
obj->vlist_trans[vindex_1].y,
obj->vlist_trans[vindex_2].x,
obj->vlist_trans[vindex_2].y,
obj->plist[poly].color,
video_buffer, lpitch );
math2.Draw_Clip_Line16(
obj->vlist_trans[vindex_2].x,
obj->vlist_trans[vindex_2].y,
obj->vlist_trans[vindex_0].x,
obj->vlist_trans[vindex_0].y,
obj->plist[poly].color,
video_buffer, lpitch );
}
}
在每帧中绘制
liushuixian.Draw_OBJECT4DV1_Wire16( *math, & obj, ddraw->getbackbuffer(), ddraw->getbacklpitch() );
OK,试试看吧。