来解析一下qumesh数据格式,首先我们来看源代码,其中是如何根据摄影机高度,确定一张切片,然后生成地形数据的。下面是创建地形代码。
function createQuantizedMeshTerrainData(provider, buffer, level, x, y, tmsY, layer) {
var littleEndianExtensionSize = layer.littleEndianExtensionSize;
var pos = 0;
var cartesian3Elements = 3;
var boundingSphereElements = cartesian3Elements + 1;
var cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements;
var boundingSphereLength = Float64Array.BYTES_PER_ELEMENT * boundingSphereElements;
var encodedVertexElements = 3;
var encodedVertexLength = Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements;
var triangleElements = 3;
var bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;
var triangleLength = bytesPerIndex * triangleElements;
var view = new DataView(buffer);
var center = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
pos += cartesian3Length;
var minimumHeight = view.getFloat32(pos, true);
pos += Float32Array.BYTES_PER_ELEMENT;
var maximumHeight = view.getFloat32(pos, true);
pos += Float32Array.BYTES_PER_ELEMENT;
var boundingSphere = new BoundingSphere(
new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true)),
view.getFloat64(pos + cartesian3Length, true));
pos += boundingSphereLength;
var horizonOcclusionPoint = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
pos += cartesian3Length;
var vertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3);
pos += vertexCount * encodedVertexLength;
if (vertexCount > 64 * 1024) {
// More than 64k vertices, so indices are 32-bit.
bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT;
triangleLength = bytesPerIndex * triangleElements;
}
// Decode the vertex buffer.
var uBuffer = encodedVertexBuffer.subarray(0, vertexCount);
var vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount);
var heightBuffer = encodedVertexBuffer.subarray(vertexCount * 2, 3 * vertexCount);
AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
// skip over any additional padding that was added for 2/4 byte alignment
if (pos % bytesPerIndex !== 0) {
pos += (bytesPerIndex - (pos % bytesPerIndex));
}
var triangleCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var indices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, triangleCount * triangleElements);
pos += triangleCount * triangleLength;
// High water mark decoding based on decompressIndices_ in webgl-loader's loader.js.
// https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55
// Copyright 2012 Google Inc., Apache 2.0 license.
var highest = 0;
var length = indices.length;
for (var i = 0; i < length; ++i) {
var code = indices[i];
indices[i] = highest - code;
if (code === 0) {
++highest;
}
}
var westVertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var westIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, westVertexCount);
pos += westVertexCount * bytesPerIndex;
var southVertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var southIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, southVertexCount);
pos += southVertexCount * bytesPerIndex;
var eastVertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var eastIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, eastVertexCount);
pos += eastVertexCount * bytesPerIndex;
var northVertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var northIndices = IndexDatatype.createTypedArrayFromArrayBuffer(vertexCount, buffer, pos, northVertexCount);
pos += northVertexCount * bytesPerIndex;
var encodedNormalBuffer;
var waterMaskBuffer;
while (pos < view.byteLength) {
var extensionId = view.getUint8(pos, true);
pos += Uint8Array.BYTES_PER_ELEMENT;
var extensionLength = view.getUint32(pos, littleEndianExtensionSize);
pos += Uint32Array.BYTES_PER_ELEMENT;
if (extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS && provider._requestVertexNormals) {
encodedNormalBuffer = new Uint8Array(buffer, pos, vertexCount * 2);
} else if (extensionId === QuantizedMeshExtensionIds.WATER_MASK && provider._requestWaterMask) {
waterMaskBuffer = new Uint8Array(buffer, pos, extensionLength);
} else if (extensionId === QuantizedMeshExtensionIds.METADATA && provider._requestMetadata) {
var stringLength = view.getUint32(pos, true);
if (stringLength > 0) {
var jsonString =
getStringFromTypedArray(new Uint8Array(buffer), pos + Uint32Array.BYTES_PER_ELEMENT, stringLength);
var metadata = JSON.parse(jsonString);
var availableTiles = metadata.available;
if (defined(availableTiles)) {
for (var offset = 0; offset < availableTiles.length; ++offset) {
var availableLevel = level + offset + 1;
var rangesAtLevel = availableTiles[offset];
var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(availableLevel);
for (var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex) {
var range = rangesAtLevel[rangeIndex];
var yStart = yTiles - range.endY - 1;
var yEnd = yTiles - range.startY - 1;
provider.availability.addAvailableTileRange(availableLevel, range.startX, yStart, range.endX, yEnd);
layer.availability.addAvailableTileRange(availableLevel, range.startX, yStart, range.endX, yEnd);
}
}
}
}
layer.availabilityTilesLoaded.addAvailableTileRange(level, x, y, x, y);
}
pos += extensionLength;
}
var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;
var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);
var orientedBoundingBox;
if (rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
// Here, rectangle.width < pi/2, and rectangle.height < pi
// (though it would still work with rectangle.width up to pi)
// The skirt is not included in the OBB computation. If this ever
// causes any rendering artifacts (cracks), they are expected to be
// minor and in the corners of the screen. It's possible that this
// might need to be changed - just change to `minimumHeight - skirtHeight`
// A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid);
}
return new QuantizedMeshTerrainData({
center : center,
minimumHeight : minimumHeight,
maximumHeight : maximumHeight,
boundingSphere : boundingSphere,
orientedBoundingBox : orientedBoundingBox,
horizonOcclusionPoint : horizonOcclusionPoint,
quantizedVertices : encodedVertexBuffer,
encodedNormals : encodedNormalBuffer,
indices : indices,
westIndices : westIndices,
southIndices : southIndices,
eastIndices : eastIndices,
northIndices : northIndices,
westSkirtHeight : skirtHeight,
southSkirtHeight : skirtHeight,
eastSkirtHeight : skirtHeight,
northSkirtHeight : skirtHeight,
childTileMask: provider.availability.computeChildMaskForTile(level, x, y),
waterMask: waterMaskBuffer,
credits: provider._tileCredits
});
}
首先我们来看一下代码中开始工作都是计算数据试图中对应数据的位置。可能我们对于Float64Array这种强类型的字节长度不是特别熟悉。可以看一下下面的资料。
Int8Array.BYTES_PER_ELEMENT; // 1
Uint8Array.BYTES_PER_ELEMENT; // 1
Uint8ClampedArray.BYTES_PER_ELEMENT; // 1
Int16Array.BYTES_PER_ELEMENT; // 2
Uint16Array.BYTES_PER_ELEMENT; // 2
Int32Array.BYTES_PER_ELEMENT; // 4
Uint32Array.BYTES_PER_ELEMENT; // 4
Float32Array.BYTES_PER_ELEMENT; // 4
Float64Array.BYTES_PER_ELEMENT; // 8
var pos = 0;
var cartesian3Elements = 3;
var boundingSphereElements = cartesian3Elements + 1;
pos为起始的位置标记,cartesian3Elements为坐标数目,这里为3.booundingSphereElements为包围球单元数目,这里为4。
var cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements;
var boundingSphereLength = Float64Array.BYTES_PER_ELEMENT * boundingSphereElements;
var encodedVertexElements = 3;
var encodedVertexLength = Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements;
var triangleElements = 3;
var bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT;
var triangleLength = bytesPerIndex * triangleElements;
从代码中,我们可以看得出,cartesian3Length长度为8*3=24个字节长度,encodedVertexLength为解码顶点长度,其值为:2*3=6个字节,三角网中的三角形长度为triangleLength,数值为2*3=6个字节。
var view = new DataView(buffer);
var center = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
pos += cartesian3Length;
接下来就是数据解析了,开始获取中心点坐标,获取双精度64浮点型一个小端字节,以次偏移8、16个字节获取另外坐标数值。累加位置标记,现在的pos变为了24.
var minimumHeight = view.getFloat32(pos, true);
pos += Float32Array.BYTES_PER_ELEMENT;
var maximumHeight = view.getFloat32(pos, true);
pos += Float32Array.BYTES_PER_ELEMENT;
获取高度最小值,其值为一个单精度值。pos变为了24+4=28,同理获取一个高度最大值,其pos变为了28+4=32
var boundingSphere = new BoundingSphere(
new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true)),
view.getFloat64(pos + cartesian3Length, true));
pos += boundingSphereLength;
var horizonOcclusionPoint = new Cartesian3(view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true));
pos += cartesian3Length;
现在获取包围球坐标,pos变为了32+8*4=64,获取水平线点(也不知是不是这么翻译、也不知道拿来干嘛的,后面研究)pos为64+8*4=96。
var vertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3);
pos += vertexCount * encodedVertexLength;
if (vertexCount > 64 * 1024) {
// More than 64k vertices, so indices are 32-bit.
bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT;
triangleLength = bytesPerIndex * triangleElements;
}
获取顶点数目,pos=96+4=100,获取顶点缓冲数据,pos=100+vertexCount*6;接下根据顶点数量字节索引和三角长度都相应做调整。解码的顶点缓冲数据由总的缓冲数据中取出,起始位置为现在的pos,长度为顶点数量*3,为无符号的int数据。
var uBuffer = encodedVertexBuffer.subarray(0, vertexCount);
var vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount);
var heightBuffer = encodedVertexBuffer.subarray(vertexCount * 2, 3 * vertexCount);
AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer);
解码的uBuffer、vBuffer、heightBuffer,其中前者为位置值,后者为高度值。下面来看一下解码函数。
AttributeCompression.zigZagDeltaDecode = function(uBuffer, vBuffer, heightBuffer) {
//>>includeStart('debug', pragmas.debug);
Check.defined('uBuffer', uBuffer);
Check.defined('vBuffer', vBuffer);
Check.typeOf.number.equals('uBuffer.length', 'vBuffer.length', uBuffer.length, vBuffer.length);
if (defined(heightBuffer)) {
Check.typeOf.number.equals('uBuffer.length', 'heightBuffer.length', uBuffer.length, heightBuffer.length);
}
//>>includeEnd('debug');
var count = uBuffer.length;
var u = 0;
var v = 0;
var height = 0;
for (var i = 0; i < count; ++i) {
u += zigZagDecode(uBuffer[i]);
v += zigZagDecode(vBuffer[i]);
uBuffer[i] = u;
vBuffer[i] = v;
if (defined(heightBuffer)) {
height += zigZagDecode(heightBuffer[i]);
heightBuffer[i] = height;
}
}
};
function zigZagDecode(value) {
return (value >> 1) ^ (-(value & 1));
}