3dTiles 數據規範詳解[4.2] i3dm瓦片二進制數據文件結構

i3dm,即 Instanced 3D Model,實例三維模型的意思。html

諸如樹木、路燈、路邊的垃圾桶、長椅等具備明顯 重複 特徵的數據。這類數據用得較少(笑,如今都喜歡搞BIM、傾斜攝影、精模、白模等)git

個人git地址:github.com/onsummer
轉載請規範化轉載。出處:@秋意正寒 http://www.javashuo.com/article/p-qxyyfyhq-mc.htmlgithub

瓦片文件二進制佈局(文件結構)

與 b3dm 一致,文件頭多了個屬性。json

1. 文件頭:32byte

i3dm的文件頭有8個屬性,前7個與b3dm是同樣的。佈局

屬性的官方名稱 字節長 類型 含義
magic 4 string(或char[4]) 該瓦片文件的類型,在i3dm中是 "i3dm"
version 4 uint32 該瓦片的版本,目前限定是 1.
byteLength 4 uint32 該瓦片文件的文件大小,單位:byte
featureTableJSONByteLength 4 uint32 要素表的JSON文本(二進制形式)長度
featureTableBinaryByteLength 4 uint32 要素表的二進制數據長度
batchTableJSONByteLength 4 uint32 批量表的JSON文本(二進制形式)長度
batchTableBinaryByteLength 4 uint32 批量表的二進制數據長度
gltfFormat 4 uint32 gltf在i3dm瓦片中存在的形式

其中,前7個和b3dm意義同樣,不作解釋。ui

第8個,gltfFormat 只有兩個值:0和1.編碼

0,則位於 i3dm 瓦片文件最後的 gltf 內容是一個 uri,指向gltf的數據內容(多是Base64 DataURL,也多是其餘地方的地址,筆者沒見過...)spa

1,則位於 i3dm 瓦片文件最後的 gltf 內容是 二進制的 glb,大多數狀況見的是這個。3d

默認狀況,gltf 是 y 軸朝上,3dTiles 是z軸朝上,須要座標轉換。code

2. 要素表

在上篇,有介紹到要素表存在 全局屬性要素屬性。在 i3dm 中,這對概念就能獲得很好的解釋。

① 要素表的全局屬性

屬性名 數據類型 描述 是否必須
INSTANCES_LENGTH uint32 instance的個數
RTC_CENTER float32[3] 若是座標是相對座標,那麼相對中心由此屬性給出
QUANTIZED_VOLUME_OFFSET float32[3] 量化空間範圍體的偏移量 否,與要素屬性中的POSITION_QUANTIZED 共存亡
QUANTIZED_VOLUME_SCALE float32[3] 量化空間範圍體的縮放比例 否,與要素屬性中的POSITION_QUANTIZED 共存亡
EAST_NORTH_UP boolean 若是這個屬性值是true,並且每一個實例的方向沒有定義,那麼每一個實例將默認指向WGS84橢球的正東、正北方向。

第一第二個能與 b3dm 中的 BATCH_LENGTHRTC_CENTER 類比來理解,就不解釋了。

最後一個屬性指示當前 i3dm 瓦片的座標軸朝向。

下列要着重介紹這個所謂的 QUANTIZED_VOLUME,即 量化空間範圍體

量化空間範圍體

這個詞「量化空間範圍體」是我本身意譯的。

每一個瓦片,都有它本身的空間範圍,爲了節約數據佔用,可使用相對座標來記錄每一個 instance 的位置,也即記錄全局屬性中的 RTC_CENTER 屬性。

可是,即使用了相對座標,instance 的座標值仍然是 FLOAT 類型,佔 4字節。

假設,存在一個矩形空間,它的左下角點的座標是 (x, y, z),將矩形空間按 \(2^{16}\) 等分其 x、y、z 三個方向,定義矩形空間的三條邊長對應瓦片自己的座標空間的縮放比例爲 (ScaleX, ScaleY, ScaleZ),以下圖所示:

這樣,被細分出來的每個 「小矩形」,都有它本身在這個矩形空間的量化座標,由於 x、y、z 三個方向被分割成了 \(2^{16}\) 塊,咱們可使用 uint16 類型的數值來記錄座標,這樣每一個數字只佔了 16bit,也即 2byte,比 FLOAT 的4byte 小了一倍,對於頂點數據的壓縮十分具備價值。

那麼,如何將 (16464, 2172, 63312) 這個量化的座標映射回瓦片本來的座標呢?參考公式:

\(\vec{Position} = Scale * \displaystyle\frac{\vec{PositionQuantized}}{65535} + \vec{Offset}\)

即量化座標 PositionQuantized 各個座標份量乘上縮放因子( Scale / 65535 ),而後加偏移座標便可。

三個方向的縮放因子 QUANTIZED_VOLUME_SCALE:float[3] 和 偏移量 QUANTIZED_VOLUME_OFFSET:float[3] 做爲全局屬性寫在要素表JSON中。

若是這兩個全局屬性未定義,則 逐要素屬性中的 POSITION_QUANTIZED 這個量化座標也不會存在,即便用原有的 float 類型座標記法。

須要注意的是,量化座標和普通座標只能二選一,若是都不存在,那麼這個 i3dm 瓦片就不會被渲染。

看到這,是否能理解「要素表的全局屬性是對於整個瓦片文件而言」這句話了呢?

② 要素表的(逐)要素屬性

屬性名稱 數據類型 描述 是否必須
POSITION float32[3] 模型實例的座標 是,與POSITION_QUANTIZED二選一
POSITION_QUANTIZED uint16[3] 量化空間範圍體內的模型實例座標 是,與POSITION二選一
NORMAL_UP float32[3] 模型上方向向量 否,與NORMAL_RIGHT共存亡
NORMAL_RIGHT float32[3] 模型右方向向量,必須與up向量正交 否,與NORMAL_UP共存亡
NORMAL_UP_OCT32P uint16[2] 模型上方向向量,32位精度八進制編碼 否,與NORMAL_RIGHT_OCT32P共存亡
NORMAL_RIGHT_OCT32P uint16[2] 模型右方向向量,必須與up向量正交,32位精度8進制編碼 否,與NORMAL_UP_OCT32P共存亡
SCALE float32 該 instance 對於 gltf 的縮放比例
SCALE_NON_UNIFORM float32[3] 該 instance 在三個方向上的縮放比例
BATCH_ID uin8/uint16(默認)/uint32 用於在批量表裏檢索數據用的batchId

當 i3dm 瓦片中逐個 instance 的POSITION 被定義時,量化座標 POSITION_QUANTIZED 就不該存在,反之亦然。

接下來四個方向向量屬性(法線)NORMAL_UPNORMAL_RIGHTNORMAL_UP_OCT32PNORMAL_RIGHT_OCT32P 也是一對反依賴的逐要素屬性。

SCALE 屬性定義了當前要素(instance或實例)對使用的 gltf 模型的縮放比例。

SCALE_NON_UNIFORM 屬性與 SCALE 屬性差很少,只不過是在三個方向上分別不一樣的縮放比例。

BATCH_ID,是當前要素(instance或實例)的 id 號,將 要素 與 批量表中的屬性 兩者聯繫起來。

我的以爲,應該叫 INSTANCE_ID 更合適一些?

默認方向

若是不給定要素屬性中與方向有關的向量時,每一個實例的朝向有一個默認值:在WGS84橢球上,上方向指向正北,右方向指向正東。

③ 要素表的JSON

上述全部屬性所有會記錄在要素表的 JSON 中,對於 全局屬性,其值記錄在 JSON 中,對於其要素屬性,由於要素(即instance)不少的時候寫在JSON中體積會變大,因此使用 JSON引用要素表二進制數據體 的形式。

下列是一個要素表的JSON:

{
    INSTANCES_LENGTH : 4, // 有4個實例
    POSITION : {
        byteOffset : 0 // POSITION的值從ftBinary的第0字節起開始計算
    }
}

讀者不妨回顧上一篇,b3dm的要素表JSON,並未出現有對要素表體引用的屬性,在這裏出現了:POSITION,它從要素表體的第 0 個字節開始記錄數據。

POSITION 這個逐要素(實例、instance)屬性的定義,早已在上文說起,即三個 FLOAT 類型數字爲一組,一共 INSTANCES_LENGTH 組的數據,記錄在要素表體。這是 instance 座標數據,寫在 JSON 中雖然沒問題,可是會形成空間浪費,以二進制形式記錄會比較划算。

④ 要素表體

要素表JSON中引用的二進制數據均順次記錄在此。

3. 批量表

批量表與 b3dm 的一致,均爲 JSON 記錄屬性元數據,批量表體記錄屬性具體數據。此處再也不舉例。

4. 要素舉例說明

此部分參考官方文檔。

① 僅有 POSITION 的 i3dm 瓦片

var featureTableJSON = {
    INSTANCES_LENGTH : 4, // 有4個實例
    POSITION : {
        byteOffset : 0 // POSITION的值從ftBinary的第0字節起開始計算
    }
};

var featureTableBinary = new Buffer(new Float32Array([
    0.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    0.0, 0.0, 1.0,
    1.0, 0.0, 1.0
]).buffer);

使用 JavaScript 語言記錄了 要素表JSON,以及要素表二進制數據(以ES6 TypedArray 形式)。

② 使用量化位置與八進制方向向量

var featureTableJSON = {
    INSTANCES_LENGTH : 4, // 有4個實例
    QUANTIZED_VOLUME_OFFSET : [-250.0, 0.0, -250.0],
    QUANTIZED_VOLUME_SCALE : [500.0, 0.0, 500.0],
    POSITION_QUANTIZED : {
        byteOffset : 0
    },
    NORMAL_UP_OCT32P : {
        byteOffset : 24
    },
    NORMAL_RIGHT_OCT32P : {
        byteOffset : 40
    }
};

var positionQuantizedBinary = new Buffer(new Uint16Array([
    0, 0, 0,
    65535, 0, 0,
    0, 0, 65535,
    65535, 0, 65535
]).buffer);

var normalUpOct32PBinary = new Buffer(new Uint16Array([
    32768, 65535,
    32768, 65535,
    32768, 65535,
    32768, 65535
]).buffer);

var normalRightOct32PBinary = new Buffer(new Uint16Array([
    65535, 32768,
    65535, 32768,
    65535, 32768,
    65535, 32768
]).buffer);

var featureTableBinary = Buffer.concat([positionQuantizedBinary, normalUpOct32PBinary, normalRightOct32PBinary]);

規定了全局屬性 QUANTIZED_VOLUME_OFFSETQUANTIZED_VOLUME_SCALE,規定了量化座標 POSITION_QUANTIZED、八進制上方向和右方向向量NORMAL_UP_OCT32PNORMAL_RIGHT_OCT32P 在要素表體中的起始偏移值。

因而,使用三個 TypedArray 構造的 Buffer 對象,再拼接在一塊兒,即要素表體 featureTableBinary

5. 字節對齊與編碼端序

與b3dm裏寫的一致,能夠回看:http://www.javashuo.com/article/p-hcrvzhto-nh.html

6. 擴展(extensions)和額外信息(extras)

一樣,這部份內容與b3dm篇章內介紹的一致,會在後續文章內介紹。

相關文章
相關標籤/搜索