上个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; //文件指针
char buffer[256]; //缓冲区
char * token_string; //指向要分析的物体数据文本的指针
//先找到物体描述符
//第一步清空和初始化OBJ
memset( obj, 0, sizeof(OBJECT4DV1) );
//将物体状态设置为可见和活动的
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步,读取文件
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步:加载顶点列表
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; //当前多边形得顶点数(这里设定为3)
char tmp_string[8]; //存储多边形描述符的字符串
//第5步:加载多边形列表
for( int poly = 0; poly < obj->num_polys; poly++ )
{
token_string = Get_Line_PLG( buffer, 255,fp );
//假定所有模型都是由三角形组成,则每个多边形都有3个顶点,将面描述符、顶点数和顶点列表存储到变量中
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开头的16进制进行检测
if ( tmp_string[0] == '0' && toupper( tmp_string[1]) == 'X' )
{
sscanf( tmp_string, "%x", & poly_surface_desc );
}
else
poly_surface_desc = atoi( tmp_string );
//让多边形顶点列表指向物体的顶点列表
obj->plist[poly].vlist = obj->vlist_local;
//存储顶点列表和多边形顶点索引值后,分析多边形描述符,并据此相应地设置多边形
//提取多边形描述符中的每个位字段
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
{
//使用8位颜色索引
SET_BIT( obj->plist[poly].attr, POLY4DV1_ATTR_8BITCOLOR );
//提取最后的8位即可得到颜色索引
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;
}
//最后将多边形设置为活动状态
obj->plist[poly].state = POLY4DV1_STATE_ACTIVE;
//关闭文件
fclose( fp );
return ( 1 );
}