Goldsrc 地图 BSP 文件格式规范
目录
- 介绍
- 一些常量
- BSP 文件头
- 实体块(LUMP_ENTITIES)
- 平面块(LUMP_PLANES)
- 纹理块(LUMP_TEXTURES)
- 顶点块(LUMP_VERTICES)
- 可视块(LUMP_VISIBILITY)
- 节点块 (LUMP_NODES)
- 纹理信息块 (LUMP_TEXINFO)
- 多边形面块(LUMP_FACES)
- 光照贴图数据块(LUMP_LIGHTING)
- Clip节点数据块(LUMP_CLIPNODES)
- 叶子数据块(LUMP_LEAVES)
- 标记面数据块 (LUMP_MARKSURFACES)
- 边数据块(LUMP_EDGES)
- 面的边数据块(LUMP_SURFEDGES)
- 模型块(LUMP_MODELS)
- 附录
介绍
许多游戏的地图文件后缀都是 BSP,是因为使用了一种场景管理技术,叫“二叉空间分割”。但不同的游戏不同的版本的BSP文件规范都是不一样的,下面介绍 Goldsrc 的 BSP 文件格式规范。Goldsrc 使用的 BSP 版本号为 30.
NOTE: 以下信息是非官方的资料,规范是从 HL SDK 和其他一些开放源代码整理出来的,且经过实验能够正确地解析地图。
这篇文档使用 C# 的结构体来描述 BSP 文件中不同的数据结构。因为 C# 基元类型的长度是平台无关的,而 C 语言的基元类型如 int , long 的位宽取决于编译器和平台。命名保持原样,所以看起来是 C 风格的命名。
以下术语解释
长度:以字节为单位
位宽:以位为单位
偏移:以字节为单位
位偏移:以位为单位
一些常量
const int LUMP_ENTITIES = 0;
const int LUMP_PLANES = 1;
const int LUMP_TEXTURES = 2;
const int LUMP_VERTICES = 3;
const int LUMP_VISIBILITY = 4;
const int LUMP_NODES = 5;
const int LUMP_TEXINFO = 6;
const int LUMP_FACES = 7;
const int LUMP_LIGHTING = 8;
const int LUMP_CLIPNODES = 9;
const int LUMP_LEAVES = 10;
const int LUMP_MARKSURFACES = 11;
const int LUMP_EDGES = 12;
const int LUMP_SURFEDGES = 13;
const int LUMP_MODELS = 14;
const int HEADER_LUMPS = 15;#define MAX_MAP_HULLS 4#define MAX_MAP_MODELS 400
#define MAX_MAP_BRUSHES 4096
#define MAX_MAP_ENTITIES 1024
#define MAX_MAP_ENTSTRING (128*1024)#define MAX_MAP_PLANES 32767
#define MAX_MAP_NODES 32767
#define MAX_MAP_CLIPNODES 32767
#define MAX_MAP_LEAFS 8192
#define MAX_MAP_VERTS 65535
#define MAX_MAP_FACES 65535
#define MAX_MAP_MARKSURFACES 65535
#define MAX_MAP_TEXINFO 8192
#define MAX_MAP_EDGES 256000
#define MAX_MAP_SURFEDGES 512000
#define MAX_MAP_TEXTURES 512
#define MAX_MAP_MIPTEX 0x200000
#define MAX_MAP_LIGHTING 0x200000
#define MAX_MAP_VISIBILITY 0x200000#define MAX_MAP_PORTALS 65536
接下来介绍每个数据块。
BSP 文件头
与大多数文件一样,BSP 文件也以特定的文件头开始,其结构如下:
struct BSPHEADER
{int nVersion; // 一个有效的 Goldsrc 地图应该为 30fixed BSPLUMP lump[HEADER_LUMPS]; // 储存数据块的目录
}
文件头包含 BSP 的版本(一个幻数,magic number)。Goldsrc 能够使用的地图版本为 30 。接下来是数据块结构数组(BSPLUMP array),作为数据块目录指出了这些块的地址和长度。一个数据块包含特定类型的数据。
数据块的结构定义为:
struct BSPLUMP
{int nOffset;// 相对文件开头(文件流 Position = 0 的位置)的偏移int nLength;//数据长度
}
下面就是数据块的定义了。
实体块(LUMP_ENTITIES)
实体块是纯ASCII文本的,由所有的字符串表示实体。地图编译器直接将实体信息从地图源文件复制到编译后的 BSP 文件。实体信息是一组键值对,看起来像这样:
{"origin" "0 0 -64"
"angles" "0 0 0"
"classname" "info_player_start"
}
每个实体都由花括号括着,里面是实体的属性,每一行一对键值字符串。第一个字符串是属性名,第二个字符串是属性的值。每个实体都必须有“classname”属性,它指定了实体的类型,引擎依据这个属性判断是哪种实体从而用合适的方法解析。
键值字符串也有长度限制:
#define MAX_KEY 32
#define MAX_VALUE 1024
平面块(LUMP_PLANES)
平面块是平面结构体数组的二进制序列化对象
平面结构体:
// 指出平面垂直于某个轴
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
// 非轴向平面法线被吸附到最近的轴
#define PLANE_ANYX 3
#define PLANE_ANYY 4
#define PLANE_ANYZ 5
struct BSPPLANE
{VECTOR3D vNormal; // 平面法向量float fDist; // 平面方程为 vNormal * X = fDistint nType; // 平面类型 详见 #defines
}
这个结构使用 Hesse 范式在三维空间中定义了一个平面:
normal * point - distance = 0
其中 vNormal 是平面的归一化法向量, fDist 是平面到坐标系统原点的距离。此外,该结构还保存了描述平面在空间中的方向的整数。如果 nType == PLANE_X 那么平面的法线将平行于 X 轴;如果 nType 等于 PLANE_ANYX ,那么平面的法线更接近 X 轴。渲染器使用这些信息加快某些计算。
纹理块(LUMP_TEXTURES)
纹理块在某种程度上比其他块更复杂,因为可以直接将纹理保存在 BSP 文件中。(一般保存在外部的 WAD 文件)这个块有一个小标头:
struct BSPTEXTUREHEADER
{uint nMipTextures;// BSP Mip 纹理数量
}
标头仅由一个无符号 32 位整数组成,该整数指示纹理块中存储或引用的纹理数。在头后面紧接着是一个 32 位偏移量的数组,数组长度和纹理头的值相等。偏移指向每个纹理的起始位置。
fixed int BSPMIPTEXOFFSET[nMipTextures]
每个偏移量以字节为单位给出从纹理块开始到 BSPMIPTEX 结构起始的距离。
TIP: 不懂Mip 纹理的同学可以去网上看一下资料。
const MAXTEXTURENAME = 16
const MIPLEVELS = 4
struct BSPMIPTEX
{fixed sbyte szName[MAXTEXTURENAME]; // 纹理名uint nWidth, nHeight; // 纹理宽高fixed uint nOffsets[MIPLEVELS]; // mipmaps 的偏移,从 BSPMIPTEX 结构开始;
}
每个结构都描述一个纹理。 纹理的名称是一个字符串,可能有16个字符长(包括末尾的空字符,char等于8位有符号整数)。 如果必须从外部WAD文件中找到和加载纹理,则纹理的名称是必须的。 此外,结构包含纹理的宽度和高度。 如果纹理存储在外部WAD文件中,则末尾的 4 个偏移量可以为零,或者指向纹理块中相对于 BSPMIPTEX 结构开始的二进制纹理数据的开头。
顶点块(LUMP_VERTICES)
这个块比较简单,仅仅由 BSP 树的所有顶点组成。是一个顶点数组。
Vector3[]
每个 Vector3 表示一个三维坐标 float x,y,z;
。
可视块(LUMP_VISIBILITY)
可视块包含与 BSP 树无关的数据,只是提供了一种显著提高渲染器速度的方法。特别复杂的地图可使用其获利。和 潜在可见集(PVS) 有关。
VIS 数据生成是一个非常耗时的过程,编过CS 地图的同学们都应该知道(几个小时甚至几天),在编译时可以选择跳过 VIS 步骤,不过生成的 BSP 就不带 VIS 数据了,VIS 数据块长度为零。
节点块 (LUMP_NODES)
这个块由一个结构体数组组成,它是 BSP 树的主要部分。结构体称为 BSP 节点。
typedef struct _BSPNODE
{uint32_t iPlane; // 平面索引int16_t iChildren[2]; // 如果大于0,则为节点索引,否则按位取反变成叶子索引int16_t nMins[3], nMaxs[3]; // AABB 包围盒uint16_t firstFace, nFaces; // 多边形面索引 和 多边形面个数
} BSPNODE;
每个 BSPNODE 代表一个 BSP 节点,每个节点都是 BSP 算法分割的产物。因此,每个节点都有一个平面索引指向分割了这个节点的平面。分割后产生了两个孩子,两个孩子都用有符号数存储了一个索引。如果索引大于 0 ,那么为另一个节点的索引;如果小于 0 ,那么按位取反后的值是叶子节点索引。另外,AABB 为节点的包围盒。最后的两个成员描述了 节点中的多边形面。
纹理信息块 (LUMP_TEXINFO)
纹理信息块包含了如何将纹理应用到面的信息,是一个结构体数组。
多边形面块(LUMP_FACES)
多边形面块包含场景中的面信息,是下面结构的结构体数组。
struct BSPFACE
{uint16_t iPlane; // 与之平行的平面uint16_t nPlaneSide; // 法线方向,bool 值?uint32_t iFirstEdge; // 面的第一条边索引uint16_t nEdges; // 连续的边的数量uint16_t iTextureInfo; // 纹理信息索引uint8_t nStyles[4]; // 指定的照明样式uint32_t nLightmapOffset; // 光照贴图数据偏移
}
此数据结构 的第一个成员是平面块的索引,给出一个平行于该多边形面的平面,意味着它们共享法线。
第二个成员指出法线方向,同向或异向。接下来两个成员给出了该多边形面的连续的边,FirstEdge 为第一条边的索引,nEdges 为从 面的边数据块中取出的边的数量。然后是纹理信息块的索引,纹理信息指出该面如何贴图。接下来的字节数组给出了一些光照信息(部分用于渲染器隐藏天空表面)。最后的一个成员是该面光照贴图数据在光照贴图数据块的偏移。
光照贴图数据块(LUMP_LIGHTING)
这是BSP 文件中最大的数据块之一。如果编译地图没有进行光照处理(RAD),这个数据块长度为零,也就是没有光照数据。这个块连续存储 RGB 数据,三字节一组 RGB。
Clip节点数据块(LUMP_CLIPNODES)
此块就是所谓的Clip节点,地图构建了仅用于碰撞检测的第二个BSP树。noclip on。
typedef struct _BSPCLIPNODE
{int32_t iPlane; // 平面索引int16_t iChildren[2]; // 负数为空地?
} BSPCLIPNODE;
叶子数据块(LUMP_LEAVES)
#define CONTENTS_EMPTY -1
#define CONTENTS_SOLID -2
#define CONTENTS_WATER -3
#define CONTENTS_SLIME -4
#define CONTENTS_LAVA -5
#define CONTENTS_SKY -6
#define CONTENTS_ORIGIN -7
#define CONTENTS_CLIP -8
#define CONTENTS_CURRENT_0 -9
#define CONTENTS_CURRENT_90 -10
#define CONTENTS_CURRENT_180 -11
#define CONTENTS_CURRENT_270 -12
#define CONTENTS_CURRENT_UP -13
#define CONTENTS_CURRENT_DOWN -14
#define CONTENTS_TRANSLUCENT -15typedef struct _BSPLEAF
{int32_t nContents; // 叶子内容的枚举值int32_t nVisOffset; // 可见性数据索引int16_t nMins[3], nMaxs[3]; // AABBuint16_t iFirstMarkSurface, nMarkSurfaces; // merk surface 的索引和数量uint8_t nAmbientLevels[4]; // 环境音等级
} BSPLEAF;
第一个成员指出了叶子的类型。第二个成员为该叶子的潜在可见集,如果为-1则说明此叶没有可用的VIS数据,通常是编译地图时没有经过VIS编译。标记面在渲染过程中被遍历访问,被标记面表指向实际的多边形面。环境音等级控制一块区域的后期音效,跟区域大小等有关。
标记面数据块 (LUMP_MARKSURFACES)
一个简单的无符号短整型数组。
这是一个表,用于将叶中的标记面重定向到实际的多边形面。
边数据块(LUMP_EDGES)
struct BSPEDGE
{uint16_t iVertex[2]; // 边的两个顶点的索引,起始点和终点。
}
表示多边形面的边。
面的边数据块(LUMP_SURFEDGES)
是一个有符号整型数组。
这个块有着与标记面几乎相同的机制。重定向到具体的边。如果符号为正,则使用边的第一个顶点,也就是起始顶点作为渲染面的顶点;如果符号为负,则该值乘以-1索引到边,使用第二个顶点,也就是终点作为渲染面的顶点。
模型块(LUMP_MODELS)
结构数组。
#define MAX_MAP_HULLS 4typedef struct _BSPMODEL
{float nMins[3], nMaxs[3]; // AABBVECTOR3D vOrigin; // 模型在世界坐标系的原点int32_t iHeadnodes[MAX_MAP_HULLS]; // 节点们的索引int32_t nVisLeafs; // ???int32_t iFirstFace, nFaces; // 构成模型的多边形面索引和数量
} BSPMODEL;
一个模型是一种迷你的BSP树。它的 AABB 由前两个成员给出。模型和场景的BSP树之间的主要区别在于,模型对其顶点使用局部坐标系,使用模型Origin表示模型在世界坐标系中的原点。在渲染过程中,顶点通过Origin 变换到世界坐标系,在BSP树遍历完之后再移回来。(译注:应该是通过这个方法跟踪移动的物体。但是发现BSP文件直接通过模型面解析出的Mesh已经是世界坐标系了的 实际上,模型的顶点确实是局部坐标系的。未使用origin贴图指定origin的实体模型默认使用世界坐标(0,0,0)作为origin。)此外,有4个节点索引。第一个已经证明了是用于渲染的迷你BSP树的根节点的索引。其他三个索引可能会用于碰撞检测,这意味着它们指向Clip节点,但我不确定。下一个VisLeaf的值的意义对我来说也有些不清楚。最后是直接索引到面块中的索引,而不是由标记面重定向。
附录
Plane 是一个无限大的平面,用多条边SurfEdge围了一个多边形面Face出来,Face 包含三角面。
BSP 树的遍历是一种中序遍历(左根右),一般的二叉树对左右子树的遍历顺序没有要求,但是BSP 对顺序是有要求的。BSP 构建的时候把分割面前面的物体放到其中一个孩子节点,后面的物体放到另一个孩子节点。遍历的时候需要根据相机和物体的位置关系确定遍历顺序,总是先访问和相机不在同一边的节点。如果相机在该面的正面,就先访问该面反面的节点。
BSP 其实只是确定三角面的绘制顺序算法而已,场景管理和PVS 有关。如果编译时不产生Vis 数据,那么整个地图就总是绘制的。
Source BSP Format
Goldsrc 地图 BSP 文件格式规范相关推荐
- Apollo进阶课程 ⑧ | 高精地图的格式规范
目录 高精地图规范格式分类 NDS格式规范 Open DRIVE格式规范 原文链接:Apollo进阶课程 ⑧ | 高精地图的格式规范 上周阿波君为大家详细介绍了「Apollo进阶课程⑦高精地图的采集与 ...
- zip文件的由来以及zip文件格式规范
ZIP文件格式是一种流行的数据压缩和文档储存的文件格式,原名Deflate,发明者为菲尔·卡茨(Phil Katz). 他于1989年1月公布了该格式的资料.ZIP通常使用后缀名".zip& ...
- INI 文件 - 文件格式规范
什么是一 .ini 文件? INI 文件是计算机程序的消息配置文档,其中包含用于在框架和语法中组织属性的特征和部分的公钥.这些系统文件格式配置文件的名称来自 MS-DOS 操作系统的目录扩展名 INI ...
- FBX二进制文件格式规范
FBX binary file format specification FBX是一种流行的3D文件格式,最初由Kaydara为MotionBuilder开发,于2006年被Autodesk公司收购. ...
- 标准MIDI文件格式规范。 1.1(机翻)
0-简介 本文档概述了MIDI文件的规范. MIDI文件的目的是提供一种在相同或不同计算机上的不同程序之间交换带时间戳的MIDI数据的方法.主要设计目标之一是紧凑表示,这使其非常适合基于磁盘的文件格式 ...
- Android进阶-百度地图申请key规范和230,200,no config chosen分析解决办法。
一.申请规范: 来到申请key官网:点击进入申请key 可以看到申请密钥要填写四个值, 规范一.应用名称: 必须和你准备使用百度地图SDK的项目名称是一模一样,允许复制粘贴进去 规范二.:发布版SHA ...
- True Type 文件格式规范
TrueType 字体文件是由一系列连接表组成的二进制文件.每个表都是a sequence of words,并有一个称为 Tag的名称.每个Tag都是 uint32 数据类型.文件中的第一个表是 f ...
- QuickTime文件格式规范
QuickTime文件格式概述 QuickTime影片是存储在磁盘上,使用两个基本结构存储信息:atoms(也被称为simple atoms或classic atoms)和QT atoms.为了了解Q ...
- 兼容性运行程序永远_永远不会有太多的应用程序
兼容性运行程序永远 It's late. You have to pay your electricity bill before tomorrow. You're in bed and can't ...
最新文章
- 洛谷P1032 字串变换
- httpWebRequest 错误
- 动态规划之背包模型及其扩展应用
- BOOST_TEST_GT和BOOST_TEST_GE的用法
- python基础教程笔记—即时标记(详解)
- linux 终端与shell输出大小不匹配
- centos7.4下安装配置PHP服务(源码安装)并配置nginx支持php
- Java程序员最常用的20%技术总结
- tar+openssl加密压缩解压缩
- python自学教程-3D图示Python标准自学教程入门篇
- 问题四十七:怎么用ray tracing画superellipsoid (2)
- 【干货摘录】和秋叶一起学PPT书本中干货集合、PPT设计必备网站、找字体网站
- 计算机管理事件id10016,【已解决】如何解决事件ID:10016错误
- linux下反汇编命令,Linux下反汇编指定的函数
- Unity实战篇:实现LOL英雄的移动方式。
- coc机器人苹果_coc机器人
- r1笔记第9天 逻辑英语随堂笔记 (01)
- 输入三角形三边长,求三角形面积
- 女人不得不学的七个人生规律
- 基于 SoC 的卷积神经网络车牌识别系统设计(4-4)基于 Verilog 的 Dilate Erode IP 设计
热门文章
- 2021年建筑架子工(建筑特殊工种)试题及答案及建筑架子工(建筑特殊工种)
- 2015自然基金一审结果:项目申请的共性问题。
- 川土微电子 | CA-IF1051 CAN-FD收发器
- 如何删除电脑EFI分区|删除电脑中的EFI分区的方法
- 什么设备升级android9,三星升级Android 9 Pie设备公布:S9明年1月上线
- 学习正则表达式 - 匹配 Unicode 和其他字符
- Android View简易生成Pdf
- 使用Win10 Hyper-V 创建虚拟机
- SSH远程ubuntu【无公网IP、内网穿透】 3-3
- matlab x和y不对应,用matlAB求x和y的对应每行的相关系数!比如x中第一行和y中第一行的相关系数!依次的到没行的一个相关系数...