转眼间,2014年快过去了,数了数代码,封装后的代码也有17000行了,但是,还有很多工作量,继续进行最为敬畏的地形生成。
这个DEMO的意思是说,加载256色位图,以位图颜色索引乘以一个缩放因子为高程,将每个单元格分割为三角形,并插入到物体的顶点列表中;当然,还要有一个彩色纹理映射到高程场上。
生成地形模式:
#define POLY4DV2_ATTR_8BITCOLOR 0x0004 8位
#define POLY4DV2_ATTR_RGB16 0x0008 16位
创建网格后,用两个函数分别计算两种法线:
1,计算多边形法线,(供背面消除和多边形光照计算使用)
Compute_OBJECT4DV2_Poly_Normals( ddraw_mathmath, OBJECT4DV2_PTRobj );
2,计算顶点法线(供Gouraud着色使用)
Compute_OBJECT4DV2_Vertex_Normals( ddraw_mathmath, OBJECT4DV2_PTRobj );
要设置着色模式,可以用下述常量之一。
#define POLY4DV2_ATTR_SHADE_MODE_EMISSIVE 0x0020
#define POLY4DV2_ATTR_SHADE_MODE_FLAT 0x0040
#define POLY4DV2_ATTR_SHADE_MODE_GOURAUD 0x0080
如果要使纹理映射到地形表面,则用POLY4DV2_ATTR_SHADE_MODE_TEXTURE,此时,只能用固定和恒定着色,不能采用Gouraud着色。
采用的方法是:计算输入纹理图的纹理坐标,然后将纹理映射到地形图上。
加载函数类似于加载PLG文件
int DDRAW_LIUSHUIXIAN_TEXTURE::Generate_Terrain_OBJECT4DV2( ddraw_math math,
OBJECT4DV2_PTR obj, //指向物体的指针
float twidth, //世界坐标系中的长度(X轴)
float theight, //世界坐标系中的高度(Z轴)
float vscale, //最大可能高度值
char * height_map_file, //256色高程图的文件名
char * texture_map_file, //纹理图的文件名
int rgbcolor, //没有纹理时地形的颜色
VECTOR4D_PTR pos, //初始位置
VECTOR4D_PTR rot, //初始旋转角度
int poly_attr) //着色属性
{
char buffer[256]; //工作缓冲区
float col_tstep,row_tstep;
float col_vstep,row_vstep;
int columns,rows;
int rgbwhite;
BITMAP_FILE height_bitmap; //存储高程图
BITMAP_FILE bitmap16bit;
//第一步清空和初始化OBJ
memset(obj, 0, sizeof(OBJECT4DV2) );
//将物体状态设置为可见和活动的
obj->state =OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_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;
//以后只用色的模式
rgbwhite = _RGB16BIT565( 255, 255, 255 );
//设置物体包含的帧数
obj->num_frames =1;
obj->curr_frame =0;
obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
//清空位图
memset(& height_bitmap, 0, sizeof( BITMAP_FILE) );
//第步,加载高程图
ddraw_bitmap bm;
bm.Load_Bitmap_File( & height_bitmap,height_map_file );
//计算基本信息
columns = height_bitmap.bitmapinfoheader.biWidth;
rows = height_bitmap.bitmapinfoheader.biHeight;
col_vstep = twidth / ( float )( columns - 1 );
row_vstep = theight / ( float) ( rows - 1 );
sprintf(obj->name,"Terrains:%s%s", height_map_file, texture_map_file);
obj->num_vertices =columns * rows;
obj->num_polys =( ( columns - 1 ) * ( rows - 1 ) ) * 2;
//存储一些结果,供地形追踪算法使用
obj->ivar1 =columns;
obj->ivar2 =rows;
obj->fvar1 =col_vstep;
obj->fvar2 =row_vstep;
//为存储顶点数和多边形数的变量分配内存
Init_OBJECT4DV2(obj, obj->num_vertices, obj->num_polys, obj->num_frames );
//加载纹理图(如果有的话)
if(( poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE) && texture_map_file )
{
//从磁盘里加载纹理图
bm.Load_Bitmap_File( & bitmap16bit,texture_map_file );
//创建一个大小和位深合适的位图
obj->texture =( BITMAP_IMAGE_PTR ) malloc( sizeof( BITMAP_IMAGE ) );
bm.Create_Bitmap( obj->texture, 0, 0, bitmap16bit.bitmapinfoheader.biWidth,bitmap16bit.bitmapinfoheader.biHeight,
bitmap16bit.bitmapinfoheader.biBitCount);
//加载位图图像
bm.Load_Image_Bitmap16( obj->texture, & bitmap16bit,0, 0, BITMAP_EXTRACT_MODE_ABS );
//计算纹理图的步进因子
col_tstep = ( float ) ( bitmap16bit.bitmapinfoheader.biWidth- 1 ) / ( float ) ( columns- 1 );
row_tstep = ( float ) ( bitmap16bit.bitmapinfoheader.biHeight- 1 ) / ( float ) ( rows- 1 );
//指出物体带纹理
SET_BIT(obj->attr,OBJECT4DV2_ATTR_TEXTURES );
//卸载位图
bm.Unload_Bitmap_File( & bitmap16bit);
}
//第步:按先行后列的顺序加载顶点列表和纹理坐标列表
for(int curr_row= 0; curr_row < rows;curr_row ++ )
{
for(int curr_col= 0; curr_col < columns;curr_col ++ )
{
int vertex = ( curr_row* columns ) + curr_col;
//计算顶点坐标
obj->vlist_local[vertex].x =curr_col * col_vstep- ( twidth / 2 );
obj->vlist_local[vertex].y =vscale * ( ( float) height_bitmap.buffer[curr_col + ( curr_row* columns )] )/ 255;
obj->vlist_local[vertex].z =curr_row * row_vstep- ( theight / 2 );
obj->vlist_local[vertex].w =1;
//设置顶点的D点
SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT );
//需要纹理坐标
if(( poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE) && texture_map_file )
{
//计算纹理坐标
obj->tlist[vertex].x = curr_col* col_tstep;
obj->tlist[vertex].y = curr_row* row_tstep;
}
}
}
//计算平均半径和最大半径
Compute_OBJECT4DV2_Radius(obj);
//第步:加载多边形列表
for(int poly =0; poly < obj->num_polys / 2; poly++)
{
//每个单元格有两个三角形,单元格顶点按先行后列的顺序排列
//如果顶点数组大小为m*n,则多边形列表大小为*(m-1)*(n-1)
int base_poly_index = ( poly% ( columns - 1 ) ) + ( columns* ( poly / ( columns- 1 ) ) );
//当前单元格的左下多边形
obj->plist[poly*2].vert[0] =base_poly_index;
obj->plist[poly*2].vert[1] =base_poly_index + columns;
obj->plist[poly*2].vert[2] =base_poly_index + columns+ 1;
//当前单元格的右上多边形
obj->plist[poly*2+1].vert[0] =base_poly_index;
obj->plist[poly*2+1].vert[1] =base_poly_index + columns+ 1;
obj->plist[poly*2+1].vert[2] =base_poly_index + 1;
//将多边形顶点列表指向物体的顶点列表
obj->plist[poly*2].vlist =obj->vlist_local;
obj->plist[poly*2+1].vlist =obj->vlist_local;
//设置多边形的颜色
obj->plist[poly*2].color =rgbcolor;
obj->plist[poly*2+1].color =rgbcolor;
//检查着色方法是否是Gouraud或phong,如果是,则需要顶点法线
if(( obj->plist[poly*2].attr& POLY4DV2_ATTR_SHADE_MODE_GOURAUD ) ||( obj->plist[poly * 2].attr& POLY4DV2_ATTR_SHADE_MODE_PHONG ) )
{
//设置顶点的属性,指出它包含法线
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[0]].attr, VERTEX4DTV1_ATTR_NORMAL);
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[1]].attr, VERTEX4DTV1_ATTR_NORMAL);
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[2]].attr, VERTEX4DTV1_ATTR_NORMAL);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[0]].attr, VERTEX4DTV1_ATTR_NORMAL);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[1]].attr, VERTEX4DTV1_ATTR_NORMAL);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[2]].attr, VERTEX4DTV1_ATTR_NORMAL);
}
//如果启用了纹理映射,则计算纹理坐标
if(poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE)
{
//指定多边形使用的纹理
obj->plist[poly *2].texture =obj->texture;
obj->plist[poly * 2+ 1].texture =obj->texture;
//设置纹理坐标
//左下三角形
obj->plist[poly*2].text[0] =base_poly_index;
obj->plist[poly*2].text[1] =base_poly_index + columns;
obj->plist[poly*2].text[2] =base_poly_index + columns+ 1;
//当前单元格的右上多边形
obj->plist[poly*2+1].text[0] =base_poly_index;
obj->plist[poly*2+1].text[1] =base_poly_index + columns+ 1;
obj->plist[poly*2+1].text[2] =base_poly_index + 1;
//重新设置多边形颜色,使其反射率更高
obj->plist[poly*2].color =rgbwhite;
obj->plist[poly*2+1].color =rgbwhite;
//设置纹理坐标属性
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[0]].attr, VERTEX4DTV1_ATTR_TEXTURE);
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[1]].attr, VERTEX4DTV1_ATTR_TEXTURE);
SET_BIT(obj->vlist_local[obj->plist[poly * 2].vert[2]].attr, VERTEX4DTV1_ATTR_TEXTURE);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[0]].attr, VERTEX4DTV1_ATTR_TEXTURE);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[1]].attr, VERTEX4DTV1_ATTR_TEXTURE);
SET_BIT(obj->vlist_local[obj->plist[poly * 2 + 1].vert[2]].attr, VERTEX4DTV1_ATTR_TEXTURE);
}
//将材质模式设置为不使用材质
SET_BIT(obj->plist[poly * 2].attr,POLY4DV2_ATTR_DISABLE_MATERIAL );
SET_BIT(obj->plist[poly * 2 + 1].attr,POLY4DV2_ATTR_DISABLE_MATERIAL );
//将三角形的状态设置为活动的
obj->plist[poly *2].state =POLY4DV2_STATE_ACTIVE;
obj->plist[poly * 2+ 1].state =POLY4DV2_STATE_ACTIVE;
//将多边形顶点列表指向物体的顶点列表
obj->plist[poly *2].vlist =obj->vlist_local;
obj->plist[poly * 2+ 1].vlist =obj->vlist_local;
//设置纹理坐标列表
obj->plist[poly *2].tlist =obj->tlist;
obj->plist[poly * 2+ 1].tlist =obj->tlist;
}
//计算多边形法线的长度
Compute_OBJECT4DV2_Poly_Normals(math, obj);
//计算使用GOURAUD着色的多边形,计算其顶点法线
Compute_OBJECT4DV2_Vertex_Normals(math, obj);
return( 1 );
}
演示程序是个沙地汽车,但是我不想去做这个,我只想看看引擎的运用,而不是逻辑,
在初始化时加载地形
VECTOR4D terrain_pos = { 0, 0, 0, 0 };
liushuixian_texture.Generate_Terrain_OBJECT4DV2( * math, & obj_work,4000, 3000, 700, "earthheightmap01.bmp","earthcolormap01.bmp",
_RGB16BIT565( 255, 255, 255), & terrain_pos, NULL,POLY4DV2_ATTR_RGB16 | POLY4DV2_ATTR_SHADE_MODE_FLAT | POLY4DV2_ATTR_SHADE_MODE_TEXTURE );
摄像机位置改变
POINT4D cam_pos ={0,500,-200,1};
截图如下: