ThreeJs入门25-vtk模型格式及模型解析

1,083 阅读4分钟

「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战

示例代码采用three.js-r73版本: cdnjs.cloudflare.com/ajax/libs/t…

yuque_diagram.jpg

我们已经对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
  1. # vtk DataFile Version 3.0表示这个vtk文件的版本是3.0。最新版本是4.0,不过改变不大。
  2. vtk output表示该文件是名字,一般写成vtk output就可以了,我们不需要修改它
  3. ASCII表示这份vtk使用的标准ASCII码字符集,如果写成binary,就表示这个文件是二进制格式的
  4. DATASET POLYDATA
    • DATASET是关键字,标识数据集的意思
    • POLYDATA表示数据类型,可以取STRUCTED_POINTS、STRUCTURED_GRID、UNSTRUCTURED_GRID、POLYDATA、FIELD等。这里取的是POLYDATA,表示三角形或者四边形数据。
  5. POINTS 35947 float表示这个模型由35947个点组成,数据类型是浮点型
  6. 每个点由三个数字组成,后面就是35947*3个float型数字
  7. POLYGONS 69451 277804
    • POLYGONS是关键字,表示多边形
    • 69451表示模型有69451个多边形组成
    • 277804表示整个POLYGONS占据的数组的长度,计算公式是69451*4 = 277804,乘数4是3 21216 21215 20399这组元素的长度,也就是每一行元素的个数,这个主要用来计算存储空间。
  8. 3 21216 21215 20399 后面每一行表示一个多边形
    • 3表示每个多边形由三个顶点组成,如果是4,那么每个多边形由4个顶点组成
    • 每个面由3个顶点组成,21216 21215 20399这三个数表示上面的POINTS 35947 float段的顶点索引
  9. CELL_DATA 69451表示面的个数,和POLYGONS 69451 277804定义的面数必须一致
  10. 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函数