渲染命令列表
不断的切换渲染线程者也,
例如设置颜色,
对于渲染器方面,使用控制命令来控制这个渲染状态者也,渲染2d,设置颜色者也
渲染3d
这几个状态,分别在ui界面时,渲染器切换到2D渲染,
进入游戏后,切换到3D渲染,
前端,
初始化命令行者也 记录总数者也
如渲染线程的状态分为场景电影,2D,3D,Buffer,画surface,画缓冲区等等
在播放游戏开始的画面视频时,在切换到视频的状态,每个状态对应的执行过程不同,例如3D的渲染3D空间的物体,2D渲染的平面的物体,场景的电影,我们把它剪成一幁一幁地画上去,
交换缓存,来进行渲染者也
画缓存,
画多边形等等的处理者也
后置阶段的所有的多边形者也
线程的处理等等
2d部分的显示者也
渲染2d
字体
渲染2d的处理,,
要渲染了字体和图像了者也
freetype这个opengl的显示纹理乃是使用的这个纹理的2次方了者也,
这个乃是调用的这个把字体保存为了纹理图片,然后来显示纹理的
拉伸纹理,拉伸这个字体的处理等等
载入了这个字体的轮廓,把轮廓保存为了位图,然后来进行处理了者也!
调用显示列表来处理
保存位图,把这个转化为了这个opengl能够使用的这个纹理东西了者也
8位的亮度表示这个alpha?
绑定了字体的纹理
计算位图中字符图像的宽度
绘制一个正方形,显示字符
然后绑定了这个纹理的宽度和这个长度,来显示的这个字体的处理了者也
// 创建FreeType库
创建一个freetype这个字体的库?
在FreeType库中保存字体信息的类叫做face
在FreeType中使用1/64作为一个像素的高度所以我们需要缩放h来满足这个要求
设置这个face这个的尺寸了者也
然后再来创建了这个显示列表,
释放了这个freetype类,
释放了这个face
最后释放显示列表
最后显示这个字体的处理
字体的使用者也,使用的freetype来绘制
图像
StretchPic();//拉宽页面
显示一幅图像者也
设置颜色
设置2d式的状态等等
在显示这个文字时候,拉宽图像,文字在制定的地方渲染
对于2d的东西,例如生命值,中心的交叉点,还有弹药值等等2d的东西要不断的切换线程的状态,
从而来进行渲染者也
1
加载了dmap文件了者也
dmap文件的格式
列表
Lump Index块索引 Lump Name块名 Description描述
1 Texture纹理 表现面的性质,转换为GL纹理
2 Planes平面地形 被map包括了进去
3 Nodes BSP树节点
4 Leaves BSP树叶
5 Leafface face面索引列表,每个Leaf都有
7 Models模型 描述了一个场景骨架
10 Vertex顶点 不用多说,构造每一个面
11 Meshvertes 偏移列表,每个mesh都有
13 Faces面 也不要多说
14 Lightmaps光照 打包的光照数据,转换为GL纹理
16 Visdata Cluster-cluster可以看到的一簇一簇数据
每一个部分的结构如下表示的
从网上基于quake3引擎的大致影响之bsp格式直接转过来的者也
typedef struct {
int fileofs, filelen;
} lump_t;
用来
typedef struct {
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;//
typedef struct {
char shader[MAX_QPATH];
int surfaceFlags;//绝对经典的东西,还是和q3map一起说比较有趣,
int contentFlags;//绝对经典的东西,还是和q3map一起说比较有趣
} dshader_t; // lump1
// planes x^1 is allways the opposite of plane x
typedef struct {
float normal[3];
float dist;
} dplane_t; // lump2
typedef struct {
int planeNum;
int children[2]; // negative numbers are -(leafs+1), not nodes
int mins[3]; // for frustom culling
int maxs[3];
} dnode_t; // lump3
typedef struct {
int cluster; // -1 = opaque cluster (do I still store these?)
int area;
int mins[3]; // for frustum culling
int maxs[3];
int firstLeafSurface;
int numLeafSurfaces;
//用于碰撞检测,不用于渲染模块
int firstLeafBrush;
int numLeafBrushes;
} dleaf_t; // lump4
int leafsurfaces; // lump5
int leafbrushes; //lump6
typedef struct {
float mins[3], maxs[3];
int firstSurface, numSurfaces;
//下面的变量用于碰撞检测用
int firstBrush, numBrushes;
} dmodel_t;// lump7
typedef struct {
int firstSide;
int numSides;
int shaderNum; // the shader that determines the contents flags
} dbrush_t;// lump8
typedef struct {
int planeNum; // positive plane side faces out of the leaf
int shaderNum;
} dbrushside_t;// lump9
typedef struct {
vec3_t xyz;
float st[2];
float lightmap[2];
vec3_t normal;
byte color[4];
} drawVert_t;// lump10
int drawIndexes; // lump11
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;// lump12
//对表面类型进行总结,具体见下面
typedef enum {
MST_BAD,
MST_PLANAR,//很重要的,说明该表面是一个世界的静态表面,例如墙面,地板等,可以通
//过brushside计算出来
MST_PATCH,//二次贝塞尔表面,要进行相应三角型化,要求速度的话,可以使用前向差分
//算法,二次贝塞尔使用9个控制点插值计算
MST_TRIANGLE_SOUP,//用于BMODEL的表面,可以进行三角形扇或带化或顶点索引三角形
//如果要了解具体算法,可以参考一些计算几何的算法,如果有足够
//深厚的功力,建议参考nvstriper相关代码,还有关于计算几何或
//拓拔方面的知识,网络上有一个很好的库ttl,里面有篇实现的论
//文,关于gmap概念以及使用半边结构进行各种拓拔查找以及修改,
//绝对经典的东西
MST_FLARE //实际上就是公告版,因该都会使用吧
} mapSurfaceType_t;
typedef struct {
int shaderNum;//索引指向shaderlump
int fogNum;//索引指向foglump
int surfaceType;// mapSurfaceType_t,具体说明见上
int firstVert;//索引指向drawVert_tlump
int numVerts;
int firstIndex;//索引指向顶点索引lump
int numIndexes;
//下面一些变量和静态lightmap相关,事实上现在的图形硬件足够快,静态光照图相关算
//法已有没落的趋势,事实上现在比较先进的引擎都是全动态光照,通过BSP进行场景管理
//可以非常高效的实现,使渲染效果大幅度提高。这部分是我最感兴趣的部分,以后有机会
//可以探讨一下,但是必须要对BSP相关操作有非常的了解才可以深入
int lightmapNum;
int lightmapX, lightmapY;
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds
//下面两个变量用于贝塞尔曲面
int patchWidth;
int patchHeight;
} dsurface_t;// lump13
byte lightBytes; // lump14
byte lightgridData;// lump15
byte visBytes;// lump16
bsp地图格式的处理了者也
This document describes the Quake 3 BSP file format. This is an unofficial document. Quake 3 is a registered trademark of id Software, which does not sponsor, authorize, or endorse this document.
This document describes the Quake 3 BSP file format as the author understands it. While every effort has been made to ensure that the contents of this document are accurate, the author does not guarantee that any portion of this document is actually correct. In addition, the author cannot be held responsible the consequences of the any use or misuse of the information contained in this document.
Copyright © 2000 Kekoa Proudfoot. All rights reserved.
File structure
Quake 3 BSP files are IBSP files, and therefore have a structure similar to previous BSP files from id Software. Every IBSP file begins with a header, which in turn contains a lump directory. The lump directory describes the layout of the rest of the file, which contains some number of lumps. Each lump stores a particular kind of map data.
Header / Directory |
Lump |
Lump |
Lump |
... |
|
|
The layout of an IBSP file. An IBSP file consists of a header followed by a number of lumps. The header contains a directory which identifies the locations and sizes of the lumps. |
Data types
Quake 3 BSP files contains only four basic data types. They are:
Type |
Description |
|
ubyte |
unsigned byte |
int |
4-byte integer, little-endian |
float |
4-byte IEEE float, little-endian |
string[n] |
string of n ASCII bytes, not necessarily null-terminated |
|
All data in a BSP file is organized into records composed of these four data types.
Header and Directory
The header record looks like this:
header
string[4] magic |
Magic number. Always "IBSP". |
int version |
Version number. 0x2e for the BSP files distributed with Quake 3. |
direntry[17] direntries |
Lump directory, seventeen entries. |
Each direntry locates a single lump in the BSP file:
direntry
int offset |
Offset to start of lump, relative to beginning of file. |
int length |
Length of lump. Always a multiple of 4. |
Lumps
There are 17 lumps in a Quake 3 BSP file. In the order that they appear in the lump directory, they are:
Index |
Lump Name |
Description |
|
0 |
Entities |
Game-related object descriptions. |
1 |
Textures |
Surface descriptions. |
2 |
Planes |
Planes used by map geometry. |
3 |
Nodes |
BSP tree nodes. |
4 |
Leafs |
BSP tree leaves. |
5 |
Leaffaces |
Lists of face indices, one list per leaf. |
6 |
Leafbrushes |
Lists of brush indices, one list per leaf. |
7 |
Models |
Descriptions of rigid world geometry in map. |
8 |
Brushes |
Convex polyhedra used to describe solid space. |
9 |
Brushsides |
Brush surfaces. |
10 |
Vertexes |
Vertices used to describe faces. |
11 |
Meshverts |
Lists of offsets, one list per mesh. |
12 |
Effects |
List of special map effects. |
13 |
Faces |
Surface geometry. |
14 |
Lightmaps |
Packed lightmap data. |
15 |
Lightvols |
Local illumination data. |
16 |
Visdata |
Cluster-cluster visibility data. |
Entities
The entities lump stores game-related map information, including information about the map name, weapons, health, armor, triggers, spawn points, lights, and .md3 models to be placed in the map. The lump contains only one record, a string that describes all of the entities:
entities
string[length] ents |
Entity descriptions, stored as a string. |
The length of the entity string is given by the size of the lump itself, as specified in the lump directory.
The meanings, formats, and parameters of the various entity descriptions are currently outside the scope of this document. For more information about entity descriptions, see the documentation to Q3Radiant, the Quake 3 level editor.
Textures
The textures lump stores information about surfaces and volumes, which are in turn associated with faces, brushes, and brushsides. There are a total of length / sizeof(texture) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
texture
string[64] name |
Texture name. |
int flags |
Surface flags. |
int contents |
Content flags. |
Planes
The planes lump stores a generic set of planes that are in turn referenced by nodes and brushsides. There are a total of length / sizeof(plane) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
plane
float[3] normal |
Plane normal. |
float dist |
Distance from origin to plane along normal. |
Note that planes are paired. The pair of planes with indices i and i ^ 1 are coincident planes with opposing normals.
Nodes
The nodes lump stores all of the nodes in the map's BSP tree. The BSP tree is used primarily as a spatial subdivision scheme, dividing the world into convex regions called leafs. The first node in the lump is the tree's root node. There are a total of length / sizeof(node) records in the lump, wherelength is the size of the lump itself, as specified in the lump directory.
node
int plane |
Plane index. |
int[2] children |
Children indices. Negative numbers are leaf indices: -(leaf+1). |
int[3] mins |
Integer bounding box min coord. |
int[3] maxs |
Integer bounding box max coord. |
Leafs
The leafs lump stores the leaves of the map's BSP tree. Each leaf is a convex region that contains, among other things, a cluster index (for determining the other leafs potentially visible from within the leaf), a list of faces (for rendering), and a list of brushes (for collision detection). There are a total of length / sizeof(leaf) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
leaf
int cluster |
Visdata cluster index. |
int area |
Areaportal area. |
int[3] mins |
Integer bounding box min coord. |
int[3] maxs |
Integer bounding box max coord. |
int leafface |
First leafface for leaf. |
int n_leaffaces |
Number of leaffaces for leaf. |
int leafbrush |
First leafbrush for leaf. |
int n_leafbrushes |
Number of leafbrushes for leaf. |
If cluster is negative, the leaf is outside the map or otherwise invalid.
Leaffaces
The leaffaces lump stores lists of face indices, with one list per leaf. There are a total of length / sizeof(leafface) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
leafface
Leafbrushes
The leafbrushes lump stores lists of brush indices, with one list per leaf. There are a total of length / sizeof(leafbrush) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
leafbrush
Models
The models lump describes rigid groups of world geometry. The first model correponds to the base portion of the map while the remaining models correspond to movable portions of the map, such as the map's doors, platforms, and buttons. Each model has a list of faces and list of brushes; these are especially important for the movable parts of the map, which (unlike the base portion of the map) do not have BSP trees associated with them. There are a total of length / sizeof(models) records in the lump, wherelength is the size of the lump itself, as specified in the lump directory.
model
模型
float[3] mins |
Bounding box min coord. |
float[3] maxs |
Bounding box max coord. |
int face |
First face for model. |
int n_faces |
Number of faces for model. |
int brush |
First brush for model. |
int n_brushes |
Number of brushes for model. |
Brushes
笔刷
The brushes lump stores a set of brushes, which are in turn used for collision detection. Each brush describes a convex volume as defined by its surrounding surfaces. There are a total of length / sizeof(brushes) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
brush
int brushside |
First brushside for brush. |
int n_brushsides |
Number of brushsides for brush. |
int texture |
Texture index. |
Brushsides
笔刷的边
The brushsides lump stores descriptions of brush bounding surfaces. There are a total of length / sizeof(brushsides) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
brushside
int plane |
Plane index. |
int texture |
Texture index. |
Vertexes
The vertexes lump stores lists of vertices used to describe faces. There are a total of length / sizeof(vertex) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
vertex
float[3] position |
Vertex position. |
float[2][2] texcoord |
Vertex texture coordinates. 0=surface, 1=lightmap. |
float[3] normal |
Vertex normal. |
ubyte[4] color |
Vertex color. RGBA. |
Meshverts
The meshverts lump stores lists of vertex offsets, used to describe generalized triangle meshes. There are a total of length / sizeof(meshvert) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
meshvert
int offset |
Vertex index offset, relative to first vertex of corresponding face. |
Effects
The effects lump stores references to volumetric shaders (typically fog) which affect the rendering of a particular group of faces. There are a total of length / sizeof(effect) records in the lump, wherelength is the size of the lump itself, as specified in the lump directory.
effect
string[64] name |
Effect shader. |
int brush |
Brush that generated this effect. |
int unknown |
Always 5, except in q3dm8, which has one effect with -1. |
Faces
The faces lump stores information used to render the surfaces of the map. There are a total of length / sizeof(faces) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
face
int texture |
Texture index. |
int effect |
Index into lump 12 (Effects), or -1. |
int type |
Face type. 1=polygon, 2=patch, 3=mesh, 4=billboard |
int vertex |
Index of first vertex. |
int n_vertexes |
Number of vertices. |
int meshvert |
Index of first meshvert. |
int n_meshverts |
Number of meshverts. |
int lm_index |
Lightmap index. |
int[2] lm_start |
Corner of this face's lightmap image in lightmap. |
int[2] lm_size |
Size of this face's lightmap image in lightmap. |
float[3] lm_origin |
World space origin of lightmap. |
float[2][3] lm_vecs |
World space lightmap s and t unit vectors. |
float[3] normal |
Surface normal. |
int[2] size |
Patch dimensions. |
There are four types of faces: polygons, patches, meshes, and billboards.
Several components have different meanings depending on the face type.
For type 1 faces (polygons), vertex and n_vertexes describe a set of vertices that form a polygon. The set always contains a loop of vertices, and sometimes also includes an additional vertex near the center of the polygon. For these faces, meshvert and n_meshverts describe a valid polygon triangulation. Every three meshverts describe a triangle. Each meshvert is an offset from the first vertex of the face, given by vertex.
For type 2 faces (patches), vertex and n_vertexes describe a 2D rectangular grid of control vertices with dimensions given by size. Within this rectangular grid, regions of 3×3 vertices represent biquadratic Bezier patches. Adjacent patches share a line of three vertices. There are a total of (size[0] - 1) / 2 by (size[1] - 1) / 2 patches. Patches in the grid start at (i, j) given by:
i = 2n, n in [ 0 .. (
size[0] - 1) / 2 ), and
j = 2m, m in [ 0 .. (
size[1] - 1) / 2 ).
For type 3 faces (meshes), meshvert and n_meshverts are used to describe the independent triangles that form the mesh. As with type 1 faces, every three meshverts describe a triangle, and each meshvert is an offset from the first vertex of the face, given by vertex.
For type 4 faces (billboards), vertex describes the single vertex that determines the location of the billboard. Billboards are used for effects such as flares. Exactly how each billboard vertex is to be interpreted has not been investigated.
The lm_ variables are primarily used to deal with lightmap data. A face that has a lightmap has a non-negative lm_index. For such a face, lm_index is the index of the image in the lightmaps lump that contains the lighting data for the face. The data in the lightmap image can be located using the rectangle specified by lm_start and lm_size.
For type 1 faces (polygons) only, lm_origin and lm_vecs can be used to compute the world-space positions corresponding to lightmap samples. These positions can in turn be used to compute dynamic lighting across the face.
None of the lm_ variables are used to compute texture coordinates for indexing into lightmaps. In fact, lightmap coordinates need not be computed. Instead, lightmap coordinates are simply stored with the vertices used to describe each face.
Lightvols
The lightvols lump stores a uniform grid of lighting information used to illuminate non-map objects. There are a total of length / sizeof(lightvol) records in the lump, where length is the size of the lump itself, as specified in the lump directory.
Lightvols make up a 3D grid whose dimensions are:
nx = floor(models[0].maxs[0] / 64) - ceil(models[0].mins[0] / 64) + 1
ny = floor(models[0].maxs[1] / 64) - ceil(models[0].mins[1] / 64) + 1
nz = floor(models[0].maxs[2] / 128) - ceil(models[0].mins[2] / 128) + 1
lightvol
ubyte[3] ambient |
Ambient color component. RGB. |
ubyte[3] directional |
Directional color component. RGB. |
ubyte[2] dir |
Direction to light. 0=phi, 1=theta. |
Visdata
The visdata lump stores bit vectors that provide cluster-to-cluster visibility information. There is exactly one visdata record, with a length equal to that specified in the lump directory.
visdata
int n_vecs |
Number of vectors. |
int sz_vecs |
Size of each vector, in bytes. |
ubyte[n_vecs * sz_vecs] vecs |
Visibility data. One bit per cluster per vector. |
Cluster x is visible from cluster y if the (1 << y % 8) bit of vecs[x * sz_vecs + y / 8] is set.
Note that clusters are associated with leaves.
Known Issues and Missing Items |
[top] |
|
This document is very brief. I have gathered more information, but have not had time to write it up. Occasionally, I add more information to this document.
At some point I put together a page that describes triangle meshes and other q3 leaf elements. I forget the exact reason I created that page, but you might find it interesting.
Feel free to ask for clarification, but please accept my apologies if I can't find the time to answer.
对于bsp文件的这个加载以及处理方案,
对于bsp文件中得到了这个光源,entity来抽取出来进行显示了者也
R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] );
这个说什么都想象不到的者也
// we are about to upload textures
R_SyncRenderThread();
R_LoadShaders( &header->lumps[LUMP_SHADERS] );
R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] );、
加载光照贴图的处理了者也!
R_LoadShaders( &header->lumps[LUMP_SHADERS] );
R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] );
R_LoadPlanes (&header->lumps[LUMP_PLANES]);
R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] );
R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header-
>lumps[LUMP_DRAWINDEXES] );
R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]);
R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]);
R_LoadSubmodels (&header->lumps[LUMP_MODELS]);
R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] );
R_LoadEntities( &header->lumps[LUMP_ENTITIES] );
R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] );
加载整个quake3整个的这个流程的处理等等了者也
对于这个分析face,分析光照贴图的分析等等
case MST_PATCH:
ParseMesh ( in, dv, out );
numMeshes++;
break;
case MST_TRIANGLE_SOUP:
ParseTriSurf( in, dv, out, indexes );
numTriSurfs++;
break;
case MST_PLANAR:
ParseFace( in, dv, out, indexes );
numFaces++;
break;
case MST_FLARE:
ParseFlare( in, dv, out, indexes );
numFlares++;
break;
out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight,
shader->fogParms.color[1] * tr.identityLight,
shader->fogParms.color[2] * tr.identityLight, 1.0 );
R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] );
R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] );
对于光照这个偏移了
根据视口的高度,宽度等等来生成
2根据bsp文件中存储的玩家的位置,
来决定玩家的照相机的位置,根据配置文件的存储的这个view视口的
长,宽,远,近平面来生成这个平截头体,来判断这个与
bsp树中的这个
视锥体的六个平面与这个照相机的处理了这个,然后这个六个平面和这个pvs的处理者也
照相机的,这个六个视锥平面,来判断这个与bsp文件的相交处理,
然后相交来进行判断,
来裁剪了这个叶子节点,看不见的model了者也
对于照相机与平面的判定,如果在平面之前,那么渲染前面平面,
在渲染当前的平面
在渲染后面的平面
如果照相机在平面的后面,那么闲渲染这个后面的节点,在渲染中间的节点了,
再渲染前面的节点了,
5、 按从后到前的顺序渲染透明的面列表
然后bsp file->winding
model->mesh
flare->addflare者也
然后历经这个view的各项裁剪,例如box裁剪,sphere裁剪,等等各项裁剪者也
render->根据配置文件的来配置opengl->设置视口大小->加载bsp文件
->根据 玩家位置,计算pvs潜在可视集->来找到这个brush的处理等等
+md3 model所有的这个多边形者也!
这里还可以加上了这个玩家武器发出的这个粒子系统者也!
找到玩家所在的房间及邻居房间+敌人模型或表示物体的模型->
(1)表示房间多边形 数目(2)表示物体的多 边 形,(3)光源数目等等->
(1)将表示房间的多边形与表示物体或人物模型的多边形组合到一起
(2)在确定完玩家潜在能看到的多边形后->
对于玩家生成平截头体,根据平截头体,再进一步裁剪pvs中看不见的多边形,
剩下的多边形其实就是一幁所要处理的东西了->
将多边形进行排序,对不透明的多边形从前往后渲染,从后往前对透明的多边形先渲染->
当对于多边形阶段完了后,对多边形进行细分->
然后对于这一部分的顶点,进行细分者也
winding细分,mesh细分,网格的细分者也
后端阶段
这个裁剪的处理了,对于在平面的处理
裁剪的curve的处理,对于brush的处理了
R_AddMarkFragments
把这些子弹的痕迹做成了这个段的处理了
R_BoxSurfaces_r
R_ColorShiftLightingBytes
这个颜色挪移的位了者也
渲染器后端
// all of the information needed by the back end must be
// contained in a backEndData_t. This entire structure is
// duplicated so the front and back end can run in parallel
// on an SMP machine
typedef struct {
drawSurf_t drawSurfs[MAX_DRAWSURFS];
dlight_t dlights[MAX_DLIGHTS];
trRefEntity_t entities[MAX_ENTITIES];
srfPoly_t *polys;//[MAX_POLYS];
polyVert_t *polyVerts;//[MAX_POLYVERTS];
renderCommandList_t commands;
} backEndData_t;
后置处理的这个多边形数量,
还有这个动态灯的数量
还有这个实体的数量等等
这个渲染命令列表者也
RE_AddDynamicLightToScene
增加动态灯光给场景中
RE_AddAdditiveLightToScene
渲染场景的处理了
然后渲染了场景
主要是更新的这个brush model
还有这个md3 model模型
还有光源部分的更新处理等等
我明白了这个后置阶段以后,把模型还有场景的信息全部加载进来之后
然后再进行这个细分的处理,细分到了一定的程度
最后到达这个后端阶段者也
多边形数目
光源数目
实体数目、多边形顶点数目
这就是每一幁所能看见的数目者也
每一次都要更新这个三者,最后消除时,直接把构成这个三个数量清0即可者也
在更新的过程中,
对于动态的灯光的更新和对于运动物体的更新等等的处理
MD3
md3文件的处理
/*
========================================================================
.MD3 triangle model file format
========================================================================
*/
#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I')
#define MD3_VERSION 15
// limits
#define MD3_MAX_LODS 4
#define MD3_MAX_TRIANGLES 8192 // per surface
#define MD3_MAX_VERTS 4096 // per surface
#define MD3_MAX_SHADERS 256 // per surface
#define MD3_MAX_FRAMES 1024 // per model
#define MD3_MAX_SURFACES 32 // per model
#define MD3_MAX_TAGS 16 // per frame
// vertex scales
#define MD3_XYZ_SCALE (1.0/64)
typedef struct md3Frame_s {
vec3_t bounds[2];
vec3_t localOrigin;
float radius;
char name[16];
} md3Frame_t;
typedef struct md3Tag_s {
char name[MAX_QPATH]; // tag name
vec3_t origin;
vec3_t axis[3];
} md3Tag_t;
/*
** md3Surface_t
**
** CHUNK SIZE
** header sizeof( md3Surface_t )
** shaders sizeof( md3Shader_t ) * numShaders
** triangles[0] sizeof( md3Triangle_t ) * numTriangles
** st sizeof( md3St_t ) * numVerts
** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames
*/
typedef struct {
int ident; //
char name[MAX_QPATH]; // polyset name
int flags;
int numFrames; // all surfaces in a model should have the same
int numShaders; // all surfaces in a model should have the same
int numVerts;
int numTriangles;
int ofsTriangles;
int ofsShaders; // offset from start of md3Surface_t
int ofsSt; // texture coords are common for all frames
int ofsXyzNormals; // numVerts * numFrames
int ofsEnd; // next surface follows
} md3Surface_t;
typedef struct {
char name[MAX_QPATH];
int shaderIndex; // for in-game use
} md3Shader_t;
typedef struct {
int indexes[3];
} md3Triangle_t;
typedef struct {
float st[2];
} md3St_t;
typedef struct {
short xyz[3];
short normal;
} md3XyzNormal_t;
typedef struct {
int ident;
int version;
char name[MAX_QPATH]; // model name
int flags;
int numFrames;
int numTags;
int numSurfaces;
int numSkins;
int ofsFrames; // offset for first frame
int ofsTags; // numFrames * numTags
int ofsSurfaces; // first surface, others follow
int ofsEnd; // end of file
} md3Header_t;
我怀疑这个乃是使用的这个添加的lod的多边形的处理了者也
MD3模型的格式、导入与骨骼概念动画
MD3模型就是MD2模型的升级版,是ID公司的游戏引擎Quake3中使用的主模型格式。它的特点主要集中在骨骼概念的引入吧。相关的模型载入类文章:
言归正传吧。其实我觉得MD3模型是一种颇麻烦的模型格式。也许当时骨骼动画还没有大行其道吧,制定模型格式的时候对骨骼概念有了借鉴,但是也只是仅仅限于比较表层的概念:譬如说上层骨骼的运动会影响其下层的骨骼的运动,这种父子关系应该说是骨骼动画中比较核心的东西。MD3实现了这层概念,但是表现出来的却是颇粗糙的:一个怪物模型(MD系列模型主要就是一些人体和怪物)被分成三部分:头部模型、上身模型、下身模型(当然可以说还有一些武器模型)。这三个模型是分别独立的,分别对应一个.md3后缀二进制模型文件和一个.skin后缀文本的材质文件(这些文件名包含head、upper、lower来作区分,显得随意性很大)。
三个模型之间用tag来标记连接点,下身模型(lower)作为基本的父模型,上身模型(upper)则是它的子模型,下身的运动会相应地传递到上身,同样头部模型(head)是上身模型的子模型。怎么样,很简朴的骨骼吧。我们其实很难说MD3它不是骨骼模型,但是说它是又好像觉得太勉强了点——嘛,就是这种状态。动画信息存储在一个以.animation后缀名的文本文件中:
- sex f
- headoffset -15 0 0
- 0 30 0 20
- 29 1 0 20
- .....
- 90 40 0 20
- ...
- 153 8 8 20
- ....
动画只有下身和上身具备,头部和武器等都是连接到上身模型上,跟着它运动的而已。每个数字的意义在开头的注释行说得很清楚了。而其每一行所代表的动画则用后注释的方式标明——是的,即使不标也没关系,不过是隐藏了动画的信息难为使用者而已。(本ZWModelMD3类是根据这些字眼来判断动画归属上半身还是下半身模型的,所以不标则很麻烦。)还好MD系列模型所属的ID公司不是那么小家子的,毕竟连模型格式都那么公开了。MD3模型,即使那么不方便,也算是标记了一个小时代了吧。
说它不方便,首先当然是一个模型就包含那么多个文件:最基本的3个.md3和3个.skin、1个.animation还有纹理(与MD2不同,不再是一个模型一个纹理那样简洁的了),有时候还附加一个或2个.md3的武器模型和对应的材质文件(用.shader文件)。所以标准的做法是把它们集合到一个以模型名字为名字的文件夹中。这里给我的模型管理类引入了路径问题,已经勉强解决了我就不在此提了。
理应MD3的载入类ZWModelMD3与ZModelMD2很相似,但是事实完全8是这样的。我还是得几乎重新写载入部分的代码。其中一个理由很容易理解,存储格式变化颇大(后面慢慢道来);还有一个理由则是现在一个模型要包含三个子部分模型和一个武器模型的集合(可以为0),也就是很多东西都要至少三份了。当然导入上就只是加个循环的简单问题,但是识别文件和连接模型也得花点心思了。
- int nPartType = 0;
- WIN32_FIND_DATA FindData;
- memset(szPathFileName, 0, sizeof(szPathFileName));
- wcscat_s(szPathFileName, sizeof(szPathFileName) / sizeof(wchar_t), szResDirectory);
- wcscat_s(szPathFileName, sizeof(szPathFileName) / sizeof(wchar_t), L"*.md3");
- HANDLE hFindHandle = ::FindFirstFile(szPathFileName, &FindData);
- if(INVALID_HANDLE_VALUE != hFindHandle)
- {
- do
- {
- nPartType = -1;
- _wcslwr_s(FindData.cFileName, sizeof(FindData.cFileName) / sizeof(wchar_t));
- if(wcsstr(FindData.cFileName, L"head"))
- {
- nPartType = MD3_MAINPART_HEAD;
- }
- else if(wcsstr(FindData.cFileName, L"upper"))
- {
- nPartType = MD3_MAINPART_UPPER;
- }
- else if(wcsstr(FindData.cFileName, L"lower"))
- {
- nPartType = MD3_MAINPART_LOWER;
- }
- if(nPartType >= 0 && nPartType < MD3_MAINPART_COUNT)
- {
- memset(szPathFileName, 0, sizeof(szPathFileName));
- wcscat_s(szPathFileName, sizeof(szPathFileName) / sizeof(wchar_t), szResDirectory);
- wcscat_s(szPathFileName, sizeof(szPathFileName) / sizeof(wchar_t), FindData.cFileName);
- if(0 == _wfopen_s(&m_FilePointer, szPathFileName, L"rb"))
- {
- if(!ImportSubModel(&m_ModelMainMD3[nPartType], szPathFileName,
- (MD3_MAINPART_UPPER == nPartType || MD3_MAINPART_LOWER == nPartType), usage))
- {
- return false;
- }
- fclose(m_FilePointer);
- m_FilePointer = NULL;
- }
- }
- } while (::FindNextFile(hFindHandle, &FindData));
- }
可以的话我都不想这样做,加了这么多平台相关的代码不说,实在很麻烦(skin文件也是类似的):轮询文件夹下的文件(m_ModelMainMD3[MD3_MAINPART_COUNT]是那三个SubModel),找文件名的关键字(是的,如果你的头部模型文件里没有head这四个字符或者文件夹里有两个以上包含head字符的.md3文件,会把问题弄得很复杂)。ImportSubModel函数与以往一样,导入文件后初始化帧信息和生成VBO。还是从MD3模型格式开始吧:
- typedef struct tagMd3Header
- {
- char szMagic[4];
- int32 nVersion;
- char szFileName[MNAME];
- int32 nFlag;
- int32 nNumFrames;
- int32 nNumTags;
- int32 nNumMeshes;
- int32 nNumMaxSkin;
- int32 nOffsetFrames;
- int32 nOffsetTags;
- int32 nOffsetMeshes;
- int32 nFileSize;
- }Md3Header;
- typedef struct tagBoneFrameInfo
- {
- float32 fMinPos[3];
- float32 fMaxPos[3];
- float32 fPos[3];
- float32 fScale;
- char szCreator[16];
- }BoneFrameInfo;
- typedef struct tagTagInfo
- {
- char szName[MNAME];
- float32 fTranslation[3];
- float32 fRotation[3][3];
- }TagInfo;
- typedef struct tagMeshHeaderInfo
- {
- char szMeshID[4];
- char szMeshName[MNAME];
- int32 nFlag;
- int32 nNumMeshFrames;
- int32 nNumSkins;
- int32 nNumVertices;
- int32 nNumFaces;
- int32 nOffsetFaces;
- int32 nOffsetSkins;
- int32 nOffsetTexcoord;
- int32 nOffsetVertices;
- int32 nMeshInfoSize;
- }MeshHeaderInfo;
- typedef struct tagFaceInfo
- {
- int32 nVertIndex[3];
- }FaceInfo;
- typedef struct tagSkinInfo
- {
- char szName[MNAME];
- int32 nIndex;
- }SkinInfo;
- typedef struct tagTexcoordInfo
- {
- float32 u;
- float32 v;
- }TexcoordInfo;
- typedef struct tagFrameVertInfo
- {
- int16 vertSrc[3];
- unsigned char normalInfo[2];
- }FrameVertInfo;
与MD2格式模型一样,一切从文件头Md3Header开始,每个MD3文件开头的sizeof(Md3Header)大小的都是一个这样的文件头,注释部分我参考了网上注解,应该没错吧(如果有错请指出)。与MD2的不同貌似信息少了不少——别担心,后面还有个MeshHeaderInfo呢。Md3Header除了用于标识的MagicWord(IDP3),version(15),没啥用的maxSkin外,最主要的就是三个东西:boneFrame、tag、mesh。在“导入结构定义”部分的头三个,就是这部分信息的数据结构,BoneFrameInfo基本可以不理会,除非你想把骨骼控制点(建模软件里弄骨骼的那些)也渲染出来。TagInfo就是骨骼信息的核心了,每个tag作为一个连接标记,决定了所连接的子模型的位置和运动形式——一个变换矩阵。而MeshHeaderInfo就是我们模型的Base数据了,一个模型由一个或多个网格对象(Mesh)组成,每个网格对象对应单一纹理和VBO信息组,这个与3DS、OBJ类似(参见[用Indexed-VBO渲染3DS模型] [OBJ模型文件的结构、导入与渲染Ⅱ])。每个Mesh段也是开头一个信息头,指涉其所在区域内数据的组织:顶点位置、顶点法线、顶点纹理坐标、顶点索引(Skin信息不用理会,我们使用的是用mesh的名字标识去检索.skin文件内容)。顶点信息(FrameVertInfo)有点怪异,接下来会说说。如果你这时惊讶于为什么还是每帧都一堆顶点属性(这不还是逐帧动画么),你还没弄清楚“骨骼概念”动画与真正的骨骼动画的差距。
再来看看我自定义的保存模型信息的数据结构(再提醒一次,如果你只打算用传统的glVertex打点来绘制而不用VBO,你大可不必这么麻烦地转换,从上述读入的文件"raw信息"已经基本足够提供个给你绘制了,网上也有很多这样的例子吧):
- typedef struct tag3DTagFrameInfo
- {
- Vector3 fTranslation;
- float fRotation[3][3];
- }t3DTagFrameInfo;
- typedef struct tag3DTagLink
- {
- char szTagName[MNAME];
- t3DTagFrameInfo *pTagFrameInfos;
- std::vector<void *> LinkingModels;
- }t3DTagLink;
- typedef struct tag3DAnimation
- {
- unsigned int nStartFrame;
- unsigned int nEndFrame;
- unsigned int nFramesPerSec;
- }t3DAnim;
- typedef struct tag3DFrameInfo
- {
- unsigned int nCurFrame;
- unsigned int nNextFrame;
- unsigned int nStartFrame;
- unsigned int nEndFrame;
- float fSecPerKeyFrame;
- float fCurBlendValue;
- DWORD DStartPlot;
- }t3DFrameInfo;
- typedef struct tag3DObject
- {
- char szName[MNAME];
- GLuint nDiffuseMap;
- Vector3 *pPosVerts;
- Vector3 *pNormals;
- TexCoord *pTexcoords;
- unsigned short *pIndexes;
- unsigned int nNumIndexes;
- unsigned int nNumVerts;
- unsigned int nNumPosVBOs;
- GLuint *pPosVBO;
- GLuint *pNormVBO;
- GLuint nTexcoordVBO;
- GLuint nIndexVBO;
- }t3DObject;
- typedef struct tag3DModel
- {
- bool bVisable;
- bool bIsTextured;
- Md3Header MD3Header;
- t3DObject *pObjects;
- t3DTagLink *pLinkModels;
- std::vector Animations;
- t3DFrameInfo *pFrameInfo;
- GLuint TexObjDiffuseMap;
- }t3DModel;
网格对象t3DObject这个我就不多解释了,结合[OBJ模型文件的结构、导入与渲染Ⅰ]中类似的数据定义,或者看名字都知道了。t3DModel这里除了保存一堆网格对象外,还有一个帧信息t3DFrameInfo,只有上身模型和下身模型才有必要new一个出来:因为它就是用于动画的信息啊(结合一个模型所具有的动画t3DAnim列表[由.animation文件导入])。然后比较新颖的就是这个t3DTagLink结构了吧,它就是保存上述的tag信息了,它保存了tag名字、这个tag在每一动画帧下的用于子模型的变换矩阵信息(位置和旋转),还保存着连接到这个tag的子模型列表(LinkingModels)。
数据的转换应该来说关键只是这些数据结构的定义,实际的转换代码也就那么些赋值转换之类了。这里特别注意的是FrameVertInfo(上面提及过),3个short和2个字节可以表示一个顶点位置和一个法线?ID公司的人就是那么有点犀利——把short除以64来得到位置信息,把两个BYTE归一成(0,1)再乘以360度的弧度来标识球体的经度维度——原点指向该经纬位置的向量,就是我们需要的法线啦!绝!这个我是从GPU GEMS1某个例子里导入MD3模型的代码中发现奇怪的,网上查到解析信息才知道的,不然我可能就忽略它自己去计算粗糙的法线了(毕竟MD2就只能这样)。以下清单的解压缩部分是重点哈~~
- typedef struct tagFrameVertInfo
- {
- int16 vertSrc[3];
- unsigned char normalInfo[2];
- }FrameVertInfo;
- void ZWModelMD3::ProcessFileInfo(t3DModel *pModel)
- {
-
- .....
-
- pModel->pObjects = new t3DObject[pModel->MD3Header.nNumMeshes];
- MeshHeaderInfo meshHeaderInfo = {0};
- float dLongtitude = 0, dLattitude = 0;
- int32 nCurMeshOffset = pModel->MD3Header.nOffsetMeshes;
- for(int i = 0; i < pModel->MD3Header.nNumMeshes; ++i)
- {
- fseek(m_FilePointer, nCurMeshOffset, SEEK_SET);
- fread_s(&meshHeaderInfo, sizeof(MeshHeaderInfo), sizeof(unsigned char),
- sizeof(MeshHeaderInfo) / sizeof(unsigned char), m_FilePointer);
- int nTotalVerts = meshHeaderInfo.nNumVertices * pModel->MD3Header.nNumFrames
- FrameVertInfo *pFrameVertInfo = new FrameVertInfo[nTotalVerts];
-
- pModel->pObjects[i].pPosVerts = new Vector3[nTotalVerts];
- pModel->pObjects[i].pNormals = new Vector3[nTotalVerts];
- for(int j = 0; j < nTotalVerts; ++j)
- {
- pModel->pObjects[i].pPosVerts[j].set(pFrameVertInfo[j].vertSrc[0] / 64.0f,
- pFrameVertInfo[j].vertSrc[1] / 64.0f,
- pFrameVertInfo[j].vertSrc[2] / 64.0f);
- dLongtitude = pFrameVertInfo[j].normalInfo[0] * 2.0f * MD3_PI / 255;
- dLattitude = pFrameVertInfo[j].normalInfo[1] * 2.0f * MD3_PI / 255;
- pModel->pObjects[i].pNormals[j].set(cosf(dLattitude) * sinf(dLongtitude),
- cosf(dLongtitude),
- -sinf(dLattitude) * sinf(dLongtitude));
- }
- delete []pFrameVertInfo;
-
- }
- };
现在再来看看怎么连接模型吧(MD3的骨骼概念核心):
- void ZWModelMD3::LinkModels(t3DModel *pParentModel, t3DModel *pChildModel, char *szTagName)
- {
- for(int i = 0; i < pParentModel->MD3Header.nNumTags; ++i)
- {
- if(0 == strcmp(pParentModel->pLinkModels[i].szTagName, szTagName))
- {
- pParentModel->pLinkModels[i].LinkingModels.push_back(pChildModel);
- }
- }
- }
- LinkModels(&m_ModelMainMD3[MD3_MAINPART_LOWER], &m_ModelMainMD3[MD3_MAINPART_UPPER], "tag_torso");
- LinkModels(&m_ModelMainMD3[MD3_MAINPART_UPPER], &m_ModelMainMD3[MD3_MAINPART_HEAD], "tag_head");
后面是ImportModel里的执行代码。其实我们只是在t3DTagLink里保存了子模型的指针而已。下身模型的一个叫“tag_torso”的Tag连接上身模型,上身模型的一个叫“tag_head”的Tag连接头部模型。(顺带一提,上身模型的一个叫“tag_weapon”的Tag一般是用来连接武器模型的[如果有的话]。)我们渲染的时候,其实就是先渲染父模型,再渲染子模型,再渲染子模型的子模型这样下去(每个子模型渲染前都乘以对应连接tag的变换矩阵)……因为是一种树型的调用模式,所以递归出场了(实在兴奋不起来):
- void ZWModelMD3::DrawModelWithSubModel(t3DModel *pModel, bool bShaderMode)
- {
- DrawSubModel(pModel, bShaderMode);
- ZWQuaternion qQuat, qNextQuat, qInterpolatedQuat;
- Vector3 iterpPos;
- ZWMatrix16 mtFin;
- for(int i = 0; i < pModel->MD3Header.nNumTags; ++i)
- {
- if(pModel->pLinkModels[i].LinkingModels.size() > 0 && pModel->pFrameInfo)
- {
- iterpPos = pModel->pLinkModels[i].pTagFrameInfos[pModel->pFrameInfo->nCurFrame].fTranslation;
- iterpPos = iterpPos + (pModel->pLinkModels[i].pTagFrameInfos[pModel->pFrameInfo->nNextFrame].fTranslation
- - iterpPos) * pModel->pFrameInfo->fCurBlendValue;
-
- qQuat.SetFromMatrixRotationElements(&pModel->pLinkModels[i].pTagFrameInfos[pModel->pFrameInfo->nCurFrame].fRotation[0][0]);
- qNextQuat.SetFromMatrixRotationElements(&pModel->pLinkModels[i].pTagFrameInfos[pModel->pFrameInfo->nNextFrame].fRotation[0][0]);
- qInterpolatedQuat.SlerpFrom(qQuat, qNextQuat, pModel->pFrameInfo->fCurBlendValue);
- qInterpolatedQuat.GenerateMatrix3X3(&mtFin);
- mtFin.SetTranslationPart(ZWVector4D(iterpPos.x, iterpPos.y, iterpPos.z, 1.0f));
- glPushMatrix();
- glMultMatrixf(mtFin.mt);
- for(unsigned int j = 0; j < pModel->pLinkModels[i].LinkingModels.size(); ++j)
- {
- DrawModelWithSubModel((t3DModel*)pModel->pLinkModels[i].LinkingModels[j], bShaderMode);
- }
- glPopMatrix();
- }
- }
- }
- DrawModelWithSubModel(&m_ModelMainMD3[MD3_MAINPART_LOWER], true);
这部分参考的是GameTutorials, LLC的一个MD3导入教程(因为比较久远,那时候VBO还没出生)。这个教程更重要的是一些tips,譬如导入.animation文件时lower的索引要减去upper索引的首值。还有就是我们插值的方式,我们要进行动画帧间的插值,线性插值的前后应该是对应乘了变换矩阵的点坐标——这样麻烦了点,效果也不好。考虑到我们实际插值的是一个点坐标向量的旋转,所以真●主角出场了——
Quaternion四元数([GimbalLock万向节锁与四元数旋转])!!
这下倒有点兴奋了。我们在上面把Tag信息里相邻两帧的3X3roatation传给ZWQuaternion类生成两个四元数,再利用SLERP四元数球插值(网上参考很多)来弄出中间的代表旋转的四元数——变换回矩阵,加上translation部分,这样就把子模型的位置点坐标转到正确漂亮的位置了!
那你说最底的父模型——下身模型怎么办,没有其他外部的tag信息提供变换矩阵参数给它啊——是的,它的话,唯有线性插值了TAT(建议用shader Mode)。
渲染部分整个代码就跟其他格式模型差不多了,动画部分也可以根据MD2的那套延伸出[MD2格式模型的格式、导入与帧动画](毕竟本质上还是帧动画,只不过渲染上加了个骨骼的概念而已嘛)。导入skin文件其实就是查询匹配模型的网格对象的名字,然后把路径最后的文件名指涉的纹理文件导入为纹理而已;最后给出的是我的.animation文件的导入函数,其实就是上面提及的那个BUG不BUG的东西要注意点。注意,上下身的动画帧总数很多时候是8一样的,连续动画最后模型姿态会变囧的。(其实MD3模型就是上下身各指定一个动画吧,不过除了BOTH的动画外,其他的组合实在很容易让人,擦。)
- void ZWModelMD3::LoadAnimationInfo()
- {
- char strBuff[MAX_LINE] = {0};
- t3DAnim animation = {0};
- unsigned int numfr = 0, loopfr = 0;
- int nLegFrameFix = -1;
- while (fgets(strBuff, MAX_LINE, m_FilePointer))
- {
- if(!isdigit(strBuff[0]))
- {
- continue;
- }
- if(4 == sscanf_s(strBuff, "%d %d %d %d", &animation.nStartFrame, &numfr, &loopfr,
- &animation.nFramesPerSec))
- {
- animation.nEndFrame = animation.nStartFrame + numfr;
- if(strstr(strBuff, "BOTH"))
- {
- m_ModelMainMD3[MD3_MAINPART_UPPER].Animations.push_back(animation);
- m_ModelMainMD3[MD3_MAINPART_LOWER].Animations.push_back(animation);
- }
- else if(strstr(strBuff, "TORSO"))
- {
- m_ModelMainMD3[MD3_MAINPART_UPPER].Animations.push_back(animation);
- }
- else if(strstr(strBuff, "LEG"))
- {
- if(-1 == nLegFrameFix)
- {
- nLegFrameFix = animation.nStartFrame -
- m_ModelMainMD3[MD3_MAINPART_UPPER].Animations[0].nStartFrame;
- }
- animation.nStartFrame -= nLegFrameFix;
- animation.nEndFrame -= nLegFrameFix;
- m_ModelMainMD3[MD3_MAINPART_LOWER].Animations.push_back(animation);
- }
- }
- }
- }
就这样结束吧。老实说,MD3这么麻烦,要不是为了学习性连贯点,我就该直奔MD5去了。
model模型动画的处理
从bsp文件中的静态的md3 mesh模型,和这个游戏过程中加载的这个敌人的md3模型的处理等等
lod = (md4LOD_t *)( (byte *)header + header->ofsLODs );
这个家伙这个lod的这个处理,原来这个md4文件这个东西乃是骨骼的处理了
还有这个md3文件乃是这个网格的处理了
增加了这个动画的曲面surface
/ lerp all the needed bones
插值这个
header = (md4Header_t *)((byte *)surface + surface->ofsHeader);
frameSize = (int)( &((md4Frame_t *)0)->bones[ header->numBones ] );
frame = (md4Frame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.frame * frameSize );
oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames +
backEnd.currentEntity->e.oldframe * frameSize );
RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 );
triangles = (int *) ((byte *)surface + surface->ofsTriangles);
indexes = surface->numTriangles * 3;
baseIndex = tess.numIndexes;
baseVertex = tess.numVertexes;
for (j = 0 ; j < indexes ; j++) {
tess.indexes[baseIndex + j] = baseIndex + triangles[j];
}
tess.numIndexes += indexes;
//
// lerp all the needed bones
//
if ( !backlerp ) {
// no lerping needed
bonePtr = frame->bones;
} else {
bonePtr = bones;
for ( i = 0 ; i < header->numBones*12 ; i++ ) {
((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i]
+ backlerp * ((float *)oldFrame->bones)[i];
}
}
模型如果是水的波浪的话
对于水波浪的移动,根据法线来计算水波的颜色,从而来显示这个水面的颜色者也
波浪阶段wave的形式有几种
sin,三角形,四边形,还有其他的形,来处理者也,
根据shader文件中记录的这个wave的类型,来进行这个相应的水波的选择,来绘制
水波的移动,根据这个法线来计算水面的颜色
这个乃是使用的这个shader文件里面的东西者也
渲染器后端的处理
决定下了这个视口内部要渲染的多边形数目以后
光照的处理
渲染时,光源只有平移,缩放,旋转等状态,
从bsp这个地图的文件中找到了这个light,提取了出来了者也,
然后对于这个light的处理了这样
灯的闪烁,