「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战」
示例代码采用three.js-r73版本: cdnjs.cloudflare.com/ajax/libs/t…
我们已经对VTKLoader内部加载过程做了解析,这一节我们看下VTK格式文件的格式具体是怎样的。
vtk文件格式
- 把我们的vtk文件简化,来解释我们的格式
# vtk DataFile Version 3.0
vtk output
ASCII
DATASET POLYDATA
POINTS 35947 float
# 下面是35947个点的数据
-0.0378297 0.12794 0.00447467 -0.0447794 0.128887 0.00190497 -0.0680095 0.151244 0.0371953
-0.00228741 0.13015 0.0232201 -0.0226054 0.126675 0.00715587 -0.0251078 0.125921 0.00624226
-0.0371209 0.127449 0.0017956 0.033213 0.112692 0.0276861 0.0380425 0.109755 0.0161689
..........................................
..........................................
POLYGONS 69451 277804
# 下面是69451行数据
3 21216 21215 20399
3 9186 9280 14838
3 16020 13433 5187
..........................................
..........................................
CELL_DATA 69451
POINT_DATA 35947
- 你可以在 wenku.baidu.com/view/a67cda… 这个网站找到vtk 4.0版本的解释。下面对几个重要的段落进行一下解释。
# vtk DataFile Version 3.0表示这个vtk文件的版本是3.0。最新版本是4.0,不过改变不大。vtk output表示该文件是名字,一般写成vtk output就可以了,我们不需要修改它ASCII表示这份vtk使用的标准ASCII码字符集,如果写成binary,就表示这个文件是二进制格式的DATASET POLYDATADATASET是关键字,标识数据集的意思POLYDATA表示数据类型,可以取STRUCTED_POINTS、STRUCTURED_GRID、UNSTRUCTURED_GRID、POLYDATA、FIELD等。这里取的是POLYDATA,表示三角形或者四边形数据。
POINTS 35947 float表示这个模型由35947个点组成,数据类型是浮点型- 每个点由三个数字组成,后面就是35947*3个float型数字
POLYGONS 69451 277804POLYGONS是关键字,表示多边形69451表示模型有69451个多边形组成277804表示整个POLYGONS占据的数组的长度,计算公式是69451*4 = 277804,乘数4是3 21216 21215 20399这组元素的长度,也就是每一行元素的个数,这个主要用来计算存储空间。
3 21216 21215 20399后面每一行表示一个多边形3表示每个多边形由三个顶点组成,如果是4,那么每个多边形由4个顶点组成- 每个面由3个顶点组成,
21216 21215 20399这三个数表示上面的POINTS 35947 float段的顶点索引
CELL_DATA 69451表示面的个数,和POLYGONS 69451 277804定义的面数必须一致POINT_DATA 35947表示点的个数,和POINTS 35947 float定义的点数必须相同
我们的vtk模型文件主要部分就是这些了。接下来,我们看下VTKLoader的解析器parse
VTKLoader模型解析parse函数
- parse函数主要做了数据解析,返回geometry
- 首先data数据就是我们通过url获取的响应数据
load: function (url, onLoad, onProgress, onError) {
...loader.load(url, function (text) {
// 通过parse解析文本数据,返回geometry
onLoad(scope.parse(text));
}, onProgress, onError);
}
// data 加载的文件内容
parse: function (data) {
....
// 返回 geometry
return geometry;
}
- 然后定义了三个参数用来存储数据
parse: function (data) {
var indices = []; // 存储顶点索引
var positions = []; // 存储顶点坐标
var result;
....
}
- 然后定义了正则表达式用来匹配对应的数据
// 匹配三个float变量 -0.0378297 0.12794 0.00447467
var pat3Floats = /([\-]?[\d]+[\.]?[\d|\-|e]*)[ ]+([\-]?[\d]+[\.]?[\d|\-|e]*)[ ]+([\-]?[\d]+[\.]?[\d|\-|e]*)/g;
// 匹配 3 开头的 3个顶点,组成一个三角形 3 21216 21215 20399
var patTriangle = /^3[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/;
// 匹配 4 开头的 4 个顶点,组成一个四边形
var patQuad = /^4[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/;
// 匹配 POINTS 开始的那一行 POINTS 35947 float
var patPOINTS = /^POINTS /;
// 匹配 POLYGONS 开始的那一行 POLYGONS 69451 277804
var patPOLYGONS = /^POLYGONS /;
var inPointsSection = false; // 是否是点数据所在的行,用于判断是否处理点数据
var inPolygonsSection = false; // 是否是面数据所在的行,用于判断是否处理顶点索引数据
- 然后对我们的原始数据,根据换行符号分割成数组,进行遍历
var lines = data.split('\n'); // 根据每一行进行分割,组成数组
for (var i = 0; i < lines.length; ++i) {
line = lines[i];
...
}
- 然后判断我们是处理点数据还是面数据
// 如果进入 POLYGONS ,表示我们该处理面数据的索引值了
if (patPOLYGONS.exec(line) !== null) {
inPointsSection = false;
inPolygonsSection = true;
}
// 如果进入 POINTS,表示我们该处理点数据了
if (patPOINTS.exec(line) !== null) {
inPolygonsSection = false;
inPointsSection = true;
}
- 如果是处理点的数据
- 通过正则表达式,对每一行点的数据进行分割,然后存入positions中
// 如果进入到 POINTS 之后的那一行
if (inPointsSection) {
// get the vertices
// 匹配到三个浮点数变量,存入 result
while ((result = pat3Floats.exec(line)) !== null) {
// 三个浮点数组成一个顶点,存入 positions
positions.push(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]));
}
}
- 如果是处理面的数据(分为3个顶点组成三角形和4个顶点组成多边形)
- 先匹配3个顶点数据的索引,如果不是,那就是4个顶点数据的索引
- 把索引值存放到indices中
// 如果进入到 POLYGONS 之后的那一行
else if (inPolygonsSection) {
// 匹配三个顶点
result = patTriangle.exec(line);
if (result !== null) {
// 3 int int int
// triangle
// 把顶点放到索引里
indices.push(parseInt(result[1]), parseInt(result[2]), parseInt(result[3]));
}
// 匹配四个顶点
else {
result = patQuad.exec(line);
if (result !== null) {
// 4 int int int int
// break quad into two triangles
indices.push(parseInt(result[1]), parseInt(result[2]), parseInt(result[4]));
indices.push(parseInt(result[2]), parseInt(result[3]), parseInt(result[4]));
}
}
}
- 最后,解析完的数据通过
THREE.BufferGeometry生成geometry
// 解析完成之后通过BufferGeometry生成geometry
var geometry = new THREE.BufferGeometry();
geometry.setIndex(new THREE.BufferAttribute(new (indices.length > 65535 ? Uint32Array : Uint16Array)(indices), 1));
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
我们的解析过程结束了,下一节重点讲一下THREE.BufferGeometry这个函数
总结
这一节我们主要讲了以下内容:
- vtk文件格式介绍
- VTKLoader模型解析parse函数