数据格式

b3dm包含一个28B的头部和两个数据table和外部的GLTF文件。

b3dm:
|----header(28 B)--------|--feature table---|--batch table---|---------GLB-----------|

header:
magic | version | byteLength | featureTableJsonByteLength | featureTableBinaryLength | batchTableJsonByteLength | batchTableBinaryLength

feature table:
json | binary

batch table:
json | binary

其中 byteLength = 28 + featureTableJSONByteLength + featureTableBinaryByteLength + batchTableJSONByteLength + batchTableBinaryByteLength + glb的字节长度

magic 是固定的b3dm

featureTableBinaryLength 和 batchTableBinaryLength 在没数据的话,可以为0

feature table 的json 一般直接一个{"BATCH_LENGTH":1}就行了。

然后是batch table的json,

{"batchId":[0],"maxPoint":[[{},{},{}]],"minPoint":[[{},{},{}]],"name":["default"]}

每个字段都是一个数组,上边的batch length 有多少个,每个字段就有多少个数据。

batchId 指的是 glb 文件里 meshes[].primitives[] 的第几个。链接:STL文件解析及转换到GLTF

maxPoint和minPoint是glb的最大最小顶点。

把文件头、featTableJson、batchTableJson写入文件后,需要使用空格来padding到8的倍数.

featureTableJsonByteLength = featureTableJsonByteLength + paddingLength
(28 + featureTableJsonByteLength) % 8 == 0

batchTableJsonByteLength = batchTableJsonByteLength + paddingLength
(28 + batchTableJsonByteLength) % 8 == 0


while (buf.size() < 28 + featureJsonLength) {
    // add b'20'
    for (byte b : " ".getBytes()) {
        buf.add(b);
    }
}
...

GLB的处理

写入头部和批量表等数据后,需要处理GLB的内容来配合头部。

batchTableJson描述了怎么找到GLB文件里primitive的位置。

下一步需要改造glb,在primitive加上_BATCHID属性,指明accessor,用来读取glb 文件buffer的内容。

"meshes" : [{"primitives" : [ {"attributes" : {"POSITION" : 1,"_BATCHID":2},"indices" : 0} ]}]

这里就会去找到第三个accessor

{"bufferView" : 2,"byteOffset" : 0,"componentType" : 5125,"count" : %d,"type" : "SCALAR","max" : [ 0 ],"min" : [ 0 ], "name":"_batchId"}

然后是他对应的bufferview

{"buffer" : 0,"byteOffset" : %d,"byteLength" : %d,"target" : 34963}

那么写入buffer的是什么数据呢?

int[] _batches = new int[顶点数];
for (int x = 0; x < _batches.length; x++) {
    _batches[x] = 0;
}

这些写就好了, 把_batches写道buffer里

最后把这个glb文件写入到b3dm里即可。