Support glTF extention: KHR_mesh_quantization 22/300522/6
authorRichard Huang <r.huang@samsung.com>
Thu, 26 Oct 2023 14:10:53 +0000 (15:10 +0100)
committerRichard Huang <r.huang@samsung.com>
Fri, 3 Nov 2023 15:05:36 +0000 (15:05 +0000)
Change-Id: I23fd7f3f481590f5716452813666cb31161bc8ae

20 files changed:
automated-tests/resources/AnimatedMorphCubeQuantized.bin [new file with mode: 0644]
automated-tests/resources/AnimatedMorphCubeQuantized.gltf [new file with mode: 0644]
automated-tests/resources/AvocadoQuantized.bin [new file with mode: 0644]
automated-tests/resources/AvocadoQuantized.gltf [new file with mode: 0644]
automated-tests/resources/Avocado_baseColor.png [new file with mode: 0644]
automated-tests/resources/Avocado_normal.png [new file with mode: 0644]
automated-tests/resources/Avocado_roughnessMetallic.png [new file with mode: 0644]
automated-tests/resources/CesiumMilkTruck.jpg [new file with mode: 0644]
automated-tests/resources/CesiumMilkTruckQuantized.bin [new file with mode: 0644]
automated-tests/resources/CesiumMilkTruckQuantized.gltf [new file with mode: 0644]
automated-tests/resources/MorphPrimitivesTestQuantized.bin [new file with mode: 0644]
automated-tests/resources/MorphPrimitivesTestQuantized.gltf [new file with mode: 0644]
automated-tests/resources/UnsupportedExtension.bin [new file with mode: 0644]
automated-tests/resources/UnsupportedExtension.gltf [new file with mode: 0644]
automated-tests/resources/uv_texture.jpg [new file with mode: 0644]
automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp
dali-scene3d/internal/loader/gltf2-asset.h
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/public-api/loader/mesh-definition.cpp
dali-scene3d/public-api/loader/mesh-definition.h

diff --git a/automated-tests/resources/AnimatedMorphCubeQuantized.bin b/automated-tests/resources/AnimatedMorphCubeQuantized.bin
new file mode 100644 (file)
index 0000000..7520d98
Binary files /dev/null and b/automated-tests/resources/AnimatedMorphCubeQuantized.bin differ
diff --git a/automated-tests/resources/AnimatedMorphCubeQuantized.gltf b/automated-tests/resources/AnimatedMorphCubeQuantized.gltf
new file mode 100644 (file)
index 0000000..9d6d364
--- /dev/null
@@ -0,0 +1,259 @@
+{\r
+  "buffers":[\r
+    {\r
+      "uri":"AnimatedMorphCubeQuantized.bin",\r
+      "byteLength":1700\r
+    }\r
+  ],\r
+  "asset":{\r
+    "version":"2.0",\r
+    "generator":"gltfpack 0.13"\r
+  },\r
+  "extensionsUsed":[\r
+    "KHR_mesh_quantization"\r
+  ],\r
+  "extensionsRequired":[\r
+    "KHR_mesh_quantization"\r
+  ],\r
+  "bufferViews":[\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":0,\r
+      "byteLength":288,\r
+      "byteStride":4,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":288,\r
+      "byteLength":576,\r
+      "byteStride":8,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":864,\r
+      "byteLength":72,\r
+      "target":34963\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":936,\r
+      "byteLength":508\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":1444,\r
+      "byteLength":254\r
+    }\r
+  ],\r
+  "accessors":[\r
+    {\r
+      "bufferView":0,\r
+      "byteOffset":0,\r
+      "componentType":5120,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "normalized":true\r
+    },\r
+    {\r
+      "bufferView":1,\r
+      "byteOffset":0,\r
+      "componentType":5123,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "min":[\r
+        0,\r
+        5451,\r
+        0\r
+      ],\r
+      "max":[\r
+        5481,\r
+        10932,\r
+        5481\r
+      ]\r
+    },\r
+    {\r
+      "bufferView":0,\r
+      "byteOffset":96,\r
+      "componentType":5120,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "normalized":true\r
+    },\r
+    {\r
+      "bufferView":1,\r
+      "byteOffset":192,\r
+      "componentType":5122,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "min":[\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "max":[\r
+        0,\r
+        5188,\r
+        0\r
+      ]\r
+    },\r
+    {\r
+      "bufferView":0,\r
+      "byteOffset":192,\r
+      "componentType":5120,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "normalized":true\r
+    },\r
+    {\r
+      "bufferView":1,\r
+      "byteOffset":384,\r
+      "componentType":5122,\r
+      "count":24,\r
+      "type":"VEC3",\r
+      "min":[\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "max":[\r
+        0,\r
+        5451,\r
+        0\r
+      ]\r
+    },\r
+    {\r
+      "bufferView":2,\r
+      "byteOffset":0,\r
+      "componentType":5123,\r
+      "count":36,\r
+      "type":"SCALAR"\r
+    },\r
+    {\r
+      "bufferView":3,\r
+      "byteOffset":0,\r
+      "componentType":5126,\r
+      "count":127,\r
+      "type":"SCALAR",\r
+      "min":[\r
+        0\r
+      ],\r
+      "max":[\r
+        4.19999981\r
+      ]\r
+    },\r
+    {\r
+      "bufferView":4,\r
+      "byteOffset":0,\r
+      "componentType":5121,\r
+      "count":254,\r
+      "type":"SCALAR",\r
+      "normalized":true\r
+    }\r
+  ],\r
+  "materials":[\r
+    {\r
+      "name":"Material",\r
+      "pbrMetallicRoughness":{\r
+        "baseColorFactor":[\r
+          0.603827417,\r
+          0.603827417,\r
+          0.603827417,\r
+          1\r
+        ],\r
+        "metallicFactor":0,\r
+        "roughnessFactor":0.5\r
+      }\r
+    }\r
+  ],\r
+  "meshes":[\r
+    {\r
+      "primitives":[\r
+        {\r
+          "attributes":{\r
+            "NORMAL":0,\r
+            "POSITION":1\r
+          },\r
+          "mode":4,\r
+          "targets":[\r
+            {\r
+              "NORMAL":2,\r
+              "POSITION":3\r
+            },\r
+            {\r
+              "NORMAL":4,\r
+              "POSITION":5\r
+            }\r
+          ],\r
+          "indices":6,\r
+          "material":0\r
+        }\r
+      ],\r
+      "weights":[\r
+        0,\r
+        0\r
+      ]\r
+    }\r
+  ],\r
+  "animations":[\r
+    {\r
+      "name":"Square",\r
+      "samplers":[\r
+        {\r
+          "input":7,\r
+          "output":8\r
+        }\r
+      ],\r
+      "channels":[\r
+        {\r
+          "sampler":0,\r
+          "target":{\r
+            "node":0,\r
+            "path":"weights"\r
+          }\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "nodes":[\r
+    {\r
+      "mesh":0,\r
+      "translation":[\r
+        -0.0100000044,\r
+        -0.0298908409,\r
+        -0.00999999978\r
+      ],\r
+      "scale":[\r
+        3.64900689e-06,\r
+        3.64900689e-06,\r
+        3.64900689e-06\r
+      ]\r
+    },\r
+    {\r
+      "name":"AnimatedMorphCube",\r
+      "rotation":[\r
+        0,\r
+        0.707106709,\r
+        -0.707106829,\r
+        0\r
+      ],\r
+      "scale":[\r
+        100,\r
+        100,\r
+        100\r
+      ],\r
+      "children":[\r
+        0\r
+      ]\r
+    }\r
+  ],\r
+  "scenes":[\r
+    {\r
+      "nodes":[\r
+        1\r
+      ]\r
+    }\r
+  ],\r
+  "scene":0\r
+}\r
diff --git a/automated-tests/resources/AvocadoQuantized.bin b/automated-tests/resources/AvocadoQuantized.bin
new file mode 100644 (file)
index 0000000..7cdbdf0
Binary files /dev/null and b/automated-tests/resources/AvocadoQuantized.bin differ
diff --git a/automated-tests/resources/AvocadoQuantized.gltf b/automated-tests/resources/AvocadoQuantized.gltf
new file mode 100644 (file)
index 0000000..2be992f
--- /dev/null
@@ -0,0 +1,221 @@
+{\r
+  "buffers":[\r
+    {\r
+      "uri":"AvocadoQuantized.bin",\r
+      "byteLength":12212\r
+    }\r
+  ],\r
+  "asset":{\r
+    "version":"2.0",\r
+    "generator":"gltfpack 0.13"\r
+  },\r
+  "extensionsUsed":[\r
+    "KHR_mesh_quantization",\r
+    "KHR_texture_transform"\r
+  ],\r
+  "extensionsRequired":[\r
+    "KHR_mesh_quantization"\r
+  ],\r
+  "bufferViews":[\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":0,\r
+      "byteLength":1624,\r
+      "byteStride":4,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":1624,\r
+      "byteLength":1624,\r
+      "byteStride":4,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":3248,\r
+      "byteLength":1624,\r
+      "byteStride":4,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":4872,\r
+      "byteLength":3248,\r
+      "byteStride":8,\r
+      "target":34962\r
+    },\r
+    {\r
+      "buffer":0,\r
+      "byteOffset":8120,\r
+      "byteLength":4092,\r
+      "target":34963\r
+    }\r
+  ],\r
+  "accessors":[\r
+    {\r
+      "bufferView":0,\r
+      "byteOffset":0,\r
+      "componentType":5123,\r
+      "count":406,\r
+      "type":"VEC2"\r
+    },\r
+    {\r
+      "bufferView":1,\r
+      "byteOffset":0,\r
+      "componentType":5120,\r
+      "count":406,\r
+      "type":"VEC3",\r
+      "normalized":true\r
+    },\r
+    {\r
+      "bufferView":2,\r
+      "byteOffset":0,\r
+      "componentType":5120,\r
+      "count":406,\r
+      "type":"VEC4",\r
+      "normalized":true\r
+    },\r
+    {\r
+      "bufferView":3,\r
+      "byteOffset":0,\r
+      "componentType":5123,\r
+      "count":406,\r
+      "type":"VEC3",\r
+      "min":[\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "max":[\r
+        11086,\r
+        16383,\r
+        7194\r
+      ]\r
+    },\r
+    {\r
+      "bufferView":4,\r
+      "byteOffset":0,\r
+      "componentType":5123,\r
+      "count":2046,\r
+      "type":"SCALAR"\r
+    }\r
+  ],\r
+  "images":[\r
+    {\r
+      "uri":"Avocado_baseColor.png"\r
+    },\r
+    {\r
+      "uri":"Avocado_roughnessMetallic.png"\r
+    },\r
+    {\r
+      "uri":"Avocado_normal.png"\r
+    }\r
+  ],\r
+  "textures":[\r
+    {\r
+      "source":0\r
+    },\r
+    {\r
+      "source":1\r
+    },\r
+    {\r
+      "source":2\r
+    }\r
+  ],\r
+  "materials":[\r
+    {\r
+      "name":"2256_Avocado_d",\r
+      "pbrMetallicRoughness":{\r
+        "baseColorTexture":{\r
+          "index":0,\r
+          "texCoord":0,\r
+          "extensions":{\r
+            "KHR_texture_transform":{\r
+              "offset":[\r
+                0.00678020436,\r
+                0.00298196077\r
+              ],\r
+              "scale":[\r
+                0.000238270484,\r
+                0.000242341906\r
+              ]\r
+            }\r
+          }\r
+        },\r
+        "metallicRoughnessTexture":{\r
+          "index":1,\r
+          "texCoord":0,\r
+          "extensions":{\r
+            "KHR_texture_transform":{\r
+              "offset":[\r
+                0.00678020436,\r
+                0.00298196077\r
+              ],\r
+              "scale":[\r
+                0.000238270484,\r
+                0.000242341906\r
+              ]\r
+            }\r
+          }\r
+        }\r
+      },\r
+      "normalTexture":{\r
+        "index":2,\r
+        "texCoord":0,\r
+        "extensions":{\r
+          "KHR_texture_transform":{\r
+            "offset":[\r
+              0.00678020436,\r
+              0.00298196077\r
+            ],\r
+            "scale":[\r
+              0.000238270484,\r
+              0.000242341906\r
+            ]\r
+          }\r
+        }\r
+      }\r
+    }\r
+  ],\r
+  "meshes":[\r
+    {\r
+      "primitives":[\r
+        {\r
+          "attributes":{\r
+            "TEXCOORD_0":0,\r
+            "NORMAL":1,\r
+            "TANGENT":2,\r
+            "POSITION":3\r
+          },\r
+          "mode":4,\r
+          "indices":4,\r
+          "material":0\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "nodes":[\r
+    {\r
+      "mesh":0,\r
+      "translation":[\r
+        -0.0212809108,\r
+        -4.77385511e-05,\r
+        -0.0138090011\r
+      ],\r
+      "scale":[\r
+        3.839089e-06,\r
+        3.839089e-06,\r
+        3.839089e-06\r
+      ]\r
+    }\r
+  ],\r
+  "scenes":[\r
+    {\r
+      "nodes":[\r
+        0\r
+      ]\r
+    }\r
+  ],\r
+  "scene":0\r
+}\r
diff --git a/automated-tests/resources/Avocado_baseColor.png b/automated-tests/resources/Avocado_baseColor.png
new file mode 100644 (file)
index 0000000..38630ed
Binary files /dev/null and b/automated-tests/resources/Avocado_baseColor.png differ
diff --git a/automated-tests/resources/Avocado_normal.png b/automated-tests/resources/Avocado_normal.png
new file mode 100644 (file)
index 0000000..73f90d5
Binary files /dev/null and b/automated-tests/resources/Avocado_normal.png differ
diff --git a/automated-tests/resources/Avocado_roughnessMetallic.png b/automated-tests/resources/Avocado_roughnessMetallic.png
new file mode 100644 (file)
index 0000000..97c7f73
Binary files /dev/null and b/automated-tests/resources/Avocado_roughnessMetallic.png differ
diff --git a/automated-tests/resources/CesiumMilkTruck.jpg b/automated-tests/resources/CesiumMilkTruck.jpg
new file mode 100644 (file)
index 0000000..0bebaad
Binary files /dev/null and b/automated-tests/resources/CesiumMilkTruck.jpg differ
diff --git a/automated-tests/resources/CesiumMilkTruckQuantized.bin b/automated-tests/resources/CesiumMilkTruckQuantized.bin
new file mode 100644 (file)
index 0000000..2147577
Binary files /dev/null and b/automated-tests/resources/CesiumMilkTruckQuantized.bin differ
diff --git a/automated-tests/resources/CesiumMilkTruckQuantized.gltf b/automated-tests/resources/CesiumMilkTruckQuantized.gltf
new file mode 100644 (file)
index 0000000..70d8410
--- /dev/null
@@ -0,0 +1,509 @@
+{
+  "buffers": [
+    {
+      "uri": "CesiumMilkTruckQuantized.bin",
+      "byteLength": 93032
+    }
+  ],
+  "asset": {
+    "version": "2.0",
+    "generator": "gltfpack 0.18"
+  },
+  "extensionsUsed": [
+    "KHR_mesh_quantization",
+    "KHR_texture_transform"
+  ],
+  "extensionsRequired": [
+    "KHR_mesh_quantization"
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteOffset": 0,
+      "byteLength": 31328,
+      "byteStride": 8,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 31328,
+      "byteLength": 31328,
+      "byteStride": 8,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 62656,
+      "byteLength": 12460,
+      "byteStride": 4,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 75116,
+      "byteLength": 17136,
+      "target": 34963
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 92252,
+      "byteLength": 156
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 92408,
+      "byteLength": 624
+    }
+  ],
+  "accessors": [
+    {
+      "bufferView": 0,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 786,
+      "type": "VEC3",
+      "normalized": true,
+      "min": [
+        105,
+        18,
+        113
+      ],
+      "max": [
+        150,
+        129,
+        158
+      ]
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 0,
+      "componentType": 5122,
+      "count": 786,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 786,
+      "type": "VEC2",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 2304,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 6288,
+      "componentType": 5123,
+      "count": 2329,
+      "type": "VEC3",
+      "normalized": true,
+      "min": [
+        0,
+        0,
+        0
+      ],
+      "max": [
+        255,
+        146,
+        121
+      ]
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 6288,
+      "componentType": 5122,
+      "count": 2329,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3144,
+      "componentType": 5123,
+      "count": 2329,
+      "type": "VEC2",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 4608,
+      "componentType": 5123,
+      "count": 5232,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 24920,
+      "componentType": 5123,
+      "count": 151,
+      "type": "VEC3",
+      "normalized": true,
+      "min": [
+        139,
+        0,
+        12
+      ],
+      "max": [
+        211,
+        146,
+        50
+      ]
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 24920,
+      "componentType": 5122,
+      "count": 151,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 15072,
+      "componentType": 5123,
+      "count": 168,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 26128,
+      "componentType": 5123,
+      "count": 650,
+      "type": "VEC3",
+      "normalized": true,
+      "min": [
+        137,
+        15,
+        10
+      ],
+      "max": [
+        212,
+        131,
+        52
+      ]
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 26128,
+      "componentType": 5122,
+      "count": 650,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 15408,
+      "componentType": 5123,
+      "count": 864,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 4,
+      "byteOffset": 0,
+      "componentType": 5126,
+      "count": 39,
+      "type": "SCALAR",
+      "min": [
+        0
+      ],
+      "max": [
+        1.26666677
+      ]
+    },
+    {
+      "bufferView": 5,
+      "byteOffset": 0,
+      "componentType": 5122,
+      "count": 39,
+      "type": "VEC4",
+      "normalized": true
+    },
+    {
+      "bufferView": 5,
+      "byteOffset": 312,
+      "componentType": 5122,
+      "count": 39,
+      "type": "VEC4",
+      "normalized": true
+    }
+  ],
+  "images": [
+    {
+      "uri": "CesiumMilkTruck.jpg"
+    }
+  ],
+  "textures": [
+    {
+      "source": 0
+    },
+    {
+      "source": 0
+    }
+  ],
+  "materials": [
+    {
+      "name": "wheels",
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 0,
+          "extensions": {
+            "KHR_texture_transform": {
+              "offset": [
+                0.605093002,
+                0.00905001163
+              ],
+              "scale": [
+                99.8609467,
+                251.995087
+              ]
+            }
+          }
+        },
+        "metallicFactor": 0
+      }
+    },
+    {
+      "name": "truck",
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 1,
+          "extensions": {
+            "KHR_texture_transform": {
+              "offset": [
+                0.00295638992,
+                0.0156720281
+              ],
+              "scale": [
+                229.629913,
+                252.264313
+              ]
+            }
+          }
+        },
+        "metallicFactor": 0
+      }
+    },
+    {
+      "name": "glass",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          0,
+          0.0405062996,
+          0.0212407,
+          1
+        ],
+        "metallicFactor": 0
+      }
+    },
+    {
+      "name": "window_trim",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          0.064000003,
+          0.064000003,
+          0.064000003,
+          1
+        ],
+        "metallicFactor": 0
+      }
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0,
+            "NORMAL": 1,
+            "TEXCOORD_0": 2
+          },
+          "indices": 3,
+          "material": 0
+        }
+      ]
+    },
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 4,
+            "NORMAL": 5,
+            "TEXCOORD_0": 6
+          },
+          "indices": 7,
+          "material": 1
+        },
+        {
+          "attributes": {
+            "POSITION": 8,
+            "NORMAL": 9
+          },
+          "indices": 10,
+          "material": 2
+        },
+        {
+          "attributes": {
+            "POSITION": 11,
+            "NORMAL": 12
+          },
+          "indices": 13,
+          "material": 3
+        }
+      ]
+    }
+  ],
+  "animations": [
+    {
+      "name": "Wheels",
+      "samplers": [
+        {
+          "input": 14,
+          "output": 15
+        },
+        {
+          "input": 14,
+          "output": 16
+        }
+      ],
+      "channels": [
+        {
+          "sampler": 0,
+          "target": {
+            "node": 3,
+            "path": "rotation"
+          }
+        },
+        {
+          "sampler": 1,
+          "target": {
+            "node": 5,
+            "path": "rotation"
+          }
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0,
+      "translation": [
+        -2.43091011,
+        -1.39600003,
+        -2.5843699
+      ],
+      "scale": [
+        1251.30994,
+        1251.30994,
+        1251.30994
+      ]
+    },
+    {
+      "mesh": 0,
+      "translation": [
+        -2.43091011,
+        -1.39600003,
+        -2.5843699
+      ],
+      "scale": [
+        1251.30994,
+        1251.30994,
+        1251.30994
+      ]
+    },
+    {
+      "mesh": 1,
+      "translation": [
+        -2.43091011,
+        -1.39600003,
+        -2.5843699
+      ],
+      "scale": [
+        1251.30994,
+        1251.30994,
+        1251.30994
+      ]
+    },
+    {
+      "name": "Wheels",
+      "rotation": [
+        0,
+        0.0884858891,
+        0,
+        -0.996077418
+      ],
+      "children": [
+        0
+      ]
+    },
+    {
+      "name": "Node",
+      "translation": [
+        1.43267,
+        0,
+        -0.427722007
+      ],
+      "children": [
+        3
+      ]
+    },
+    {
+      "name": "Wheels.001",
+      "rotation": [
+        0,
+        0.0884858891,
+        0,
+        -0.996077418
+      ],
+      "children": [
+        1
+      ]
+    },
+    {
+      "name": "Node.001",
+      "translation": [
+        -1.35232997,
+        0,
+        -0.427722007
+      ],
+      "children": [
+        5
+      ]
+    },
+    {
+      "name": "Cesium_Milk_Truck",
+      "children": [
+        4,
+        6,
+        2
+      ]
+    },
+    {
+      "name": "Yup2Zup",
+      "rotation": [
+        0.49999997,
+        -0.5,
+        0.5,
+        0.49999997
+      ],
+      "children": [
+        7
+      ]
+    }
+  ],
+  "scenes": [
+    {
+      "name": "Scene",
+      "nodes": [
+        8
+      ]
+    }
+  ],
+  "scene": 0
+}
diff --git a/automated-tests/resources/MorphPrimitivesTestQuantized.bin b/automated-tests/resources/MorphPrimitivesTestQuantized.bin
new file mode 100644 (file)
index 0000000..fce9aac
Binary files /dev/null and b/automated-tests/resources/MorphPrimitivesTestQuantized.bin differ
diff --git a/automated-tests/resources/MorphPrimitivesTestQuantized.gltf b/automated-tests/resources/MorphPrimitivesTestQuantized.gltf
new file mode 100644 (file)
index 0000000..4e3e2fe
--- /dev/null
@@ -0,0 +1,321 @@
+{
+  "buffers": [
+    {
+      "uri": "MorphPrimitivesTestQuantized.bin",
+      "byteLength": 912
+    }
+  ],
+  "asset": {
+    "version": "2.0",
+    "generator": "gltfpack 0.18",
+    "extras": {
+      "title": "multiple_primitives",
+      "author": "ft-lab",
+      "license": "CC BY-4.0 (https://creativecommons.org/licenses/by/4.0/)"
+    }
+  },
+  "extensionsUsed": [
+    "KHR_mesh_quantization",
+    "KHR_texture_transform"
+  ],
+  "extensionsRequired": [
+    "KHR_mesh_quantization"
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteOffset": 0,
+      "byteLength": 240,
+      "byteStride": 8,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 240,
+      "byteLength": 240,
+      "byteStride": 8,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 480,
+      "byteLength": 120,
+      "byteStride": 4,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 600,
+      "byteLength": 120,
+      "byteStride": 4,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 720,
+      "byteLength": 192,
+      "target": 34963
+    }
+  ],
+  "accessors": [
+    {
+      "bufferView": 0,
+      "byteOffset": 0,
+      "componentType": 5122,
+      "count": 21,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 21,
+      "type": "VEC3",
+      "min": [
+        0,
+        0,
+        0
+      ],
+      "max": [
+        1,
+        0,
+        1
+      ]
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 21,
+      "type": "VEC2",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 0,
+      "componentType": 5120,
+      "count": 21,
+      "type": "VEC3",
+      "min": [
+        0,
+        0,
+        0
+      ],
+      "max": [
+        0,
+        0,
+        0
+      ]
+    },
+    {
+      "bufferView": 4,
+      "byteOffset": 0,
+      "componentType": 5123,
+      "count": 72,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 168,
+      "componentType": 5123,
+      "count": 9,
+      "type": "VEC3",
+      "min": [
+        1,
+        0,
+        0
+      ],
+      "max": [
+        1,
+        0,
+        1
+      ]
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 168,
+      "componentType": 5122,
+      "count": 9,
+      "type": "VEC3",
+      "normalized": true
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 84,
+      "componentType": 5123,
+      "count": 9,
+      "type": "VEC2",
+      "normalized": true
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 84,
+      "componentType": 5120,
+      "count": 9,
+      "type": "VEC3",
+      "min": [
+        0,
+        0,
+        0
+      ],
+      "max": [
+        0,
+        0,
+        0
+      ]
+    },
+    {
+      "bufferView": 4,
+      "byteOffset": 144,
+      "componentType": 5123,
+      "count": 24,
+      "type": "SCALAR"
+    }
+  ],
+  "samplers": [
+    {
+      "minFilter": 9729
+    }
+  ],
+  "images": [
+    {
+      "uri": "uv_texture.jpg"
+    }
+  ],
+  "textures": [
+    {
+      "sampler": 0,
+      "source": 0
+    }
+  ],
+  "materials": [
+    {
+      "name": "red",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          1,
+          0,
+          0,
+          1
+        ],
+        "baseColorTexture": {
+          "index": 0,
+          "extensions": {
+            "KHR_texture_transform": {
+              "offset": [
+                0,
+                0
+              ],
+              "scale": [
+                65535,
+                65535
+              ]
+            }
+          }
+        },
+        "metallicFactor": 0
+      }
+    },
+    {
+      "name": "green",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          0,
+          1,
+          0,
+          1
+        ],
+        "baseColorTexture": {
+          "index": 0,
+          "extensions": {
+            "KHR_texture_transform": {
+              "offset": [
+                0,
+                0
+              ],
+              "scale": [
+                32767.5,
+                32767.5
+              ]
+            }
+          }
+        },
+        "metallicFactor": 0
+      }
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 0,
+            "POSITION": 1,
+            "TEXCOORD_0": 2
+          },
+          "targets": [
+            {
+              "POSITION": 3
+            }
+          ],
+          "indices": 4,
+          "material": 0
+        },
+        {
+          "attributes": {
+            "POSITION": 5,
+            "NORMAL": 6,
+            "TEXCOORD_0": 7
+          },
+          "targets": [
+            {
+              "POSITION": 8
+            }
+          ],
+          "indices": 9,
+          "material": 1
+        }
+      ],
+      "weights": [
+        0.5
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0,
+      "translation": [
+        -0.5,
+        -0.400000006,
+        -0.5
+      ],
+      "scale": [
+        1,
+        1,
+        1
+      ]
+    },
+    {
+      "name": "ルートパート",
+      "children": [
+        2
+      ]
+    },
+    {
+      "name": "mesh",
+      "children": [
+        0
+      ]
+    }
+  ],
+  "scenes": [
+    {
+      "name": "Scene",
+      "nodes": [
+        1
+      ]
+    }
+  ],
+  "scene": 0
+}
diff --git a/automated-tests/resources/UnsupportedExtension.bin b/automated-tests/resources/UnsupportedExtension.bin
new file mode 100644 (file)
index 0000000..291b43c
Binary files /dev/null and b/automated-tests/resources/UnsupportedExtension.bin differ
diff --git a/automated-tests/resources/UnsupportedExtension.gltf b/automated-tests/resources/UnsupportedExtension.gltf
new file mode 100644 (file)
index 0000000..83668d6
--- /dev/null
@@ -0,0 +1,89 @@
+{\r
+  "scene" : 0,\r
+  "scenes" : [\r
+    {\r
+      "nodes" : [ 0, 1]\r
+    }\r
+  ],\r
+  "nodes" : [\r
+    {\r
+      "mesh" : 0\r
+    },\r
+    {\r
+      "mesh" : 0,\r
+      "translation" : [ 1.0, 0.0, 0.0 ]\r
+    }\r
+  ],\r
+\r
+  "meshes" : [\r
+    {\r
+      "primitives" : [ {\r
+        "attributes" : {\r
+          "POSITION" : 1,\r
+          "NORMAL" : 2\r
+        },\r
+        "indices" : 0\r
+      } ]\r
+    }\r
+  ],\r
+\r
+  "buffers" : [\r
+    {\r
+      "uri" : "UnsupportedExtension.bin",\r
+      "byteLength" : 80\r
+    }\r
+  ],\r
+  "bufferViews" : [\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 6,\r
+      "target" : 34963\r
+    },\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 8,\r
+      "byteLength" : 72,\r
+      "byteStride" : 12,\r
+      "target" : 34962\r
+    }\r
+  ],\r
+  "accessors" : [\r
+    {\r
+      "bufferView" : 0,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5123,\r
+      "count" : 3,\r
+      "type" : "SCALAR",\r
+      "max" : [ 2 ],\r
+      "min" : [ 0 ]\r
+    },\r
+    {\r
+      "bufferView" : 1,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 3,\r
+      "type" : "VEC3",\r
+      "max" : [ 1.0, 1.0, 0.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 1,\r
+      "byteOffset" : 36,\r
+      "componentType" : 5126,\r
+      "count" : 3,\r
+      "type" : "VEC3",\r
+      "max" : [ 0.0, 0.0, 1.0 ],\r
+      "min" : [ 0.0, 0.0, 1.0 ]\r
+    }\r
+  ],\r
+  "asset" : {\r
+    "version" : "2.0"\r
+  },\r
+  "extensionsUsed": [\r
+    "KHR_invalid_extension"\r
+  ],\r
+  "extensionsRequired": [\r
+    "KHR_invalid_extension"\r
+  ]\r
+}\r
diff --git a/automated-tests/resources/uv_texture.jpg b/automated-tests/resources/uv_texture.jpg
new file mode 100644 (file)
index 0000000..7f63873
Binary files /dev/null and b/automated-tests/resources/uv_texture.jpg differ
index 723c3ae..4b93f7f 100644 (file)
@@ -55,8 +55,7 @@ namespace
 {
 struct Context
 {
-  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type)
-  {
+  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
     return TEST_RESOURCE_DIR "/";
   };
 
@@ -98,7 +97,7 @@ struct ExceptionMessageStartsWith
 
 } // namespace
 
-int UtcDaliGltfLoaderFailedToLoad(void)
+int UtcDaliGltfLoaderFailedToLoad1(void)
 {
   Context ctx;
 
@@ -121,6 +120,36 @@ int UtcDaliGltfLoaderFailedToLoad(void)
   END_TEST;
 }
 
+int UtcDaliGltfLoaderFailedToLoad2(void)
+{
+  Context ctx;
+
+  try
+  {
+    DALI_TEST_EQUAL(ctx.loader.LoadModel(TEST_RESOURCE_DIR "/UnsupportedExtension.gltf", ctx.loadResult), false);
+  }
+  catch(...)
+  {
+    printf("Unsupported glTF extension required.\n");
+  }
+
+  DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
+  DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
+
+  DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
+
+  DALI_TEST_EQUAL(0, ctx.cameras.size());
+  DALI_TEST_EQUAL(0, ctx.lights.size());
+  DALI_TEST_EQUAL(0, ctx.animations.size());
+  DALI_TEST_EQUAL(0, ctx.animationGroups.size());
+
+  END_TEST;
+}
+
 int UtcDaliGltfLoaderFailedToParse(void)
 {
   Context ctx;
@@ -185,89 +214,87 @@ int UtcDaliGltfLoaderSuccess1(void)
   auto& materials = ctx.resources.mMaterials;
   DALI_TEST_EQUAL(2u, materials.size());
   const MaterialDefinition materialGroundTruth[]{
-    {
-      nullptr,
-      MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
-        MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
-        MaterialDefinition::GLTF_CHANNELS | (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
-      0,
-      Color::WHITE,
-      1.f,
-      0.f,
-      Vector4(1.000, 0.766, 0.336, 1.0),
-      1.f,
-      1.f,
-      Vector3(0.2, 0.1, 0.0),
-      1.0f,
-      0.0f,
-      0.5f,
-      Vector3(0, 0, 1),
-      true,
-      false,
-      true,
-      false,
-      Scene3D::Material::AlphaModeType::MASK,
-      true,
-      true,
-      true,
-      {
-        {
-          MaterialDefinition::ALBEDO,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-        {
-          MaterialDefinition::NORMAL,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-        {
-          MaterialDefinition::OCCLUSION,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-        {
-          MaterialDefinition::EMISSIVE,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-        {
-          MaterialDefinition::SPECULAR,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-        {
-          MaterialDefinition::SPECULAR_COLOR,
-          {
-            "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-            ImageDimensions(256, 256),
-            SamplingMode::BOX_THEN_NEAREST,
-          },
-        },
-      },
-      nullptr
-    },
+    {nullptr,
+     MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
+       MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
+       MaterialDefinition::GLTF_CHANNELS | (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
+     0,
+     Color::WHITE,
+     1.f,
+     0.f,
+     Vector4(1.000, 0.766, 0.336, 1.0),
+     1.f,
+     1.f,
+     Vector3(0.2, 0.1, 0.0),
+     1.0f,
+     0.0f,
+     0.5f,
+     Vector3(0, 0, 1),
+     true,
+     false,
+     true,
+     false,
+     Scene3D::Material::AlphaModeType::MASK,
+     true,
+     true,
+     true,
+     {
+       {
+         MaterialDefinition::ALBEDO,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+       {
+         MaterialDefinition::NORMAL,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+       {
+         MaterialDefinition::OCCLUSION,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+       {
+         MaterialDefinition::EMISSIVE,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+       {
+         MaterialDefinition::SPECULAR,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+       {
+         MaterialDefinition::SPECULAR_COLOR,
+         {
+           "AnimatedCube_BaseColor.png",
+           SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+           ImageDimensions(256, 256),
+           SamplingMode::BOX_THEN_NEAREST,
+         },
+       },
+     },
+     nullptr},
     {
       nullptr,
       MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
@@ -458,7 +485,7 @@ int UtcDaliGltfLoaderSuccess1(void)
 
 int UtcDaliGltfLoaderSuccess2(void)
 {
-  Context                 ctx;
+  Context ctx;
 
   ctx.loader.LoadModel(TEST_RESOURCE_DIR "/AnimatedCubeStride.gltf", ctx.loadResult);
 
@@ -487,8 +514,7 @@ int UtcDaliGltfLoaderSuccessShort(void)
   TestApplication app;
 
   const std::string resourcePath = TEST_RESOURCE_DIR "/";
-  auto              pathProvider = [resourcePath](ResourceType::Value)
-  {
+  auto              pathProvider = [resourcePath](ResourceType::Value) {
     return resourcePath;
   };
 
@@ -508,6 +534,34 @@ int UtcDaliGltfLoaderSuccessShort(void)
         "MRendererTest",
         "SimpleSparseAccessor",
         "AnimatedCube",
+        /**
+         * For the Avocado glTF file and its Assets
+         * Donated by Microsoft for glTF testing
+         * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Avocado/glTF-Quantized
+         */
+        "AvocadoQuantized",
+        /**
+         * For the AnimatedMorphCube glTF file and its Assets
+         * Donated by Microsoft for glTF testing
+         * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/AnimatedMorphCube/glTF-Quantized
+         */
+        "AnimatedMorphCubeQuantized",
+        /**
+         * For the MorphPrimitivesTest glTF file and its Assets
+         * Created by @ft-lab
+         * Licensed under the terms of the CC BY 4.0 license: https://creativecommons.org/licenses/by/4.0/
+         * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/MorphPrimitivesTest/glTF
+         * Modified using gltfpack 0.18.
+         */
+        "MorphPrimitivesTestQuantized",
+        /**
+         * For the CesiumMilkTruck glTF file and its Assets
+         * Donated by Cesium for glTF testing
+         * Licensed under the terms of the CC BY 4.0 license: http://creativecommons.org/licenses/by/4.0/
+         * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/CesiumMilkTruck/glTF
+         * Modified using gltfpack 0.18.
+         */
+        "CesiumMilkTruckQuantized",
       })
   {
     Context ctx;
@@ -572,7 +626,7 @@ int UtcDaliGltfLoaderSuccessShort(void)
 int UtcDaliGltfLoaderMRendererTest(void)
 {
   Context ctx;
-  auto& resources = ctx.resources;
+  auto&   resources = ctx.resources;
 
   ctx.loader.LoadModel(TEST_RESOURCE_DIR "/MRendererTest.gltf", ctx.loadResult);
 
@@ -585,8 +639,8 @@ int UtcDaliGltfLoaderMRendererTest(void)
   DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
 
   Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager();
-  ViewProjection viewProjection;
-  Transforms     xforms{
+  ViewProjection                    viewProjection;
+  Transforms                        xforms{
     MatrixStack{},
     viewProjection};
   NodeDefinition::CreateParams nodeParams{
@@ -633,7 +687,7 @@ int UtcDaliGltfLoaderMRendererTest(void)
 int UtcDaliGltfLoaderAnimationLoadingTest(void)
 {
   TestApplication app;
-  Context ctx;
+  Context         ctx;
 
   auto& resources = ctx.resources;
 
@@ -644,8 +698,8 @@ int UtcDaliGltfLoaderAnimationLoadingTest(void)
   DALI_TEST_EQUAL(roots.size(), 1u);
 
   Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager();
-  ViewProjection viewProjection;
-  Transforms     xforms{
+  ViewProjection                    viewProjection;
+  Transforms                        xforms{
     MatrixStack{},
     viewProjection};
   NodeDefinition::CreateParams nodeParams{
@@ -695,8 +749,8 @@ int UtcDaliGltfLoaderImageFromBufferView(void)
   DALI_TEST_EQUAL(roots.size(), 1u);
 
   Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager();
-  ViewProjection viewProjection;
-  Transforms     xforms{
+  ViewProjection                    viewProjection;
+  Transforms                        xforms{
     MatrixStack{},
     viewProjection};
   NodeDefinition::CreateParams nodeParams{
@@ -746,8 +800,8 @@ int UtcDaliGltfLoaderUint8Indices(void)
   DALI_TEST_EQUAL(roots.size(), 1u);
 
   Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager();
-  ViewProjection viewProjection;
-  Transforms     xforms{
+  ViewProjection                    viewProjection;
+  Transforms                        xforms{
     MatrixStack{},
     viewProjection};
   NodeDefinition::CreateParams nodeParams{
@@ -783,3 +837,104 @@ int UtcDaliGltfLoaderUint8Indices(void)
 
   END_TEST;
 }
+
+int UtcDaliGltfLoaderQuantizedMesh(void)
+{
+  Context ctx;
+
+  auto& resources = ctx.resources;
+
+  /**
+   * For the Avocado glTF file and its Assets
+   * Donated by Microsoft for glTF testing
+   * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Avocado/glTF-Quantized
+   */
+  ctx.loader.LoadModel(TEST_RESOURCE_DIR "/AvocadoQuantized.gltf", ctx.loadResult);
+
+  auto& scene = ctx.scene;
+  DALI_TEST_EQUAL(1u, scene.GetRoots().size());
+  DALI_TEST_EQUAL(1u, scene.GetNodeCount());
+
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+
+  Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager();
+  ViewProjection                    viewProjection;
+  Transforms                        xforms{
+    MatrixStack{},
+    viewProjection};
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+    shaderManager,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = Actor::New();
+  SetActorCentered(root);
+  for(auto iRoot : roots)
+  {
+    auto resourceRefs = resources.CreateRefCounter();
+    scene.CountResourceRefs(iRoot, choices, resourceRefs);
+    resources.mReferenceCounts = std::move(resourceRefs);
+    resources.CountEnvironmentReferences();
+    resources.LoadResources(ctx.pathProvider);
+    if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
+    {
+      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
+      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      root.Add(actor);
+    }
+  }
+
+  auto& meshes = ctx.resources.mMeshes;
+  DALI_TEST_EQUAL(1u, meshes.size());
+
+  auto& md = meshes[0u].first;
+
+  DALI_TEST_EQUAL(MeshDefinition::Flags::U16_POSITION | MeshDefinition::Flags::S8_NORMAL | MeshDefinition::Flags::S8_TANGENT | MeshDefinition::Flags::U16_TEXCOORD, md.mFlags);
+
+  DALI_TEST_EQUAL(true, md.mPositions.IsDefined());
+  DALI_TEST_EQUAL(false, md.mPositions.mNormalized);
+  DALI_TEST_EQUAL(sizeof(uint16_t) * 3, md.mPositions.mBlob.mElementSizeHint);
+  DALI_TEST_EQUAL(true, md.mPositions.mBlob.IsDefined());
+  DALI_TEST_EQUAL(2436, md.mPositions.mBlob.mLength);
+  DALI_TEST_EQUAL(3u, md.mPositions.mBlob.mMin.size());
+  DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[0]);
+  DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[1]);
+  DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[2]);
+  DALI_TEST_EQUAL(3u, md.mPositions.mBlob.mMax.size());
+  DALI_TEST_EQUAL(11086.0f, md.mPositions.mBlob.mMax[0]);
+  DALI_TEST_EQUAL(16383.0f, md.mPositions.mBlob.mMax[1]);
+  DALI_TEST_EQUAL(7194.0f, md.mPositions.mBlob.mMax[2]);
+
+  DALI_TEST_EQUAL(true, md.mNormals.IsDefined());
+  DALI_TEST_EQUAL(true, md.mNormals.mNormalized);
+  DALI_TEST_EQUAL(sizeof(int8_t) * 3, md.mNormals.mBlob.mElementSizeHint);
+  DALI_TEST_EQUAL(true, md.mNormals.mBlob.IsDefined());
+  DALI_TEST_EQUAL(1218, md.mNormals.mBlob.mLength);
+  DALI_TEST_EQUAL(0u, md.mNormals.mBlob.mMin.size());
+  DALI_TEST_EQUAL(0u, md.mNormals.mBlob.mMax.size());
+
+  DALI_TEST_EQUAL(true, md.mTangents.IsDefined());
+  DALI_TEST_EQUAL(true, md.mTangents.mNormalized);
+  DALI_TEST_EQUAL(Property::VECTOR4, md.mTangentType);
+  DALI_TEST_EQUAL(sizeof(int8_t) * 4, md.mTangents.mBlob.mElementSizeHint);
+  DALI_TEST_EQUAL(true, md.mTangents.mBlob.IsDefined());
+  DALI_TEST_EQUAL(1624, md.mTangents.mBlob.mLength);
+  DALI_TEST_EQUAL(0u, md.mTangents.mBlob.mMin.size());
+  DALI_TEST_EQUAL(0u, md.mTangents.mBlob.mMax.size());
+
+  DALI_TEST_EQUAL(true, md.mTexCoords.IsDefined());
+  DALI_TEST_EQUAL(false, md.mTexCoords.mNormalized);
+  DALI_TEST_EQUAL(sizeof(uint16_t) * 2, md.mTexCoords.mBlob.mElementSizeHint);
+  DALI_TEST_EQUAL(true, md.mTexCoords.mBlob.IsDefined());
+  DALI_TEST_EQUAL(1624, md.mTexCoords.mBlob.mLength);
+  DALI_TEST_EQUAL(0u, md.mTexCoords.mBlob.mMin.size());
+  DALI_TEST_EQUAL(0u, md.mTexCoords.mBlob.mMax.size());
+
+  END_TEST;
+}
index 7d2c332..8488c1e 100644 (file)
@@ -28,6 +28,7 @@
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/loader/json-reader.h>
 #include <dali-scene3d/public-api/loader/index.h>
+#include <dali-scene3d/public-api/loader/utils.h>
 
 #define ENUM_STRING_MAPPING(t, x) \
   {                               \
@@ -563,6 +564,16 @@ struct Scene : Named
   std::vector<Ref<Node>> mNodes;
 };
 
+enum ExtensionFlags : uint32_t
+{
+  NONE                   = Dali::Scene3D::Loader::NthBit(0),
+  KHR_MESH_QUANTIZATION  = Dali::Scene3D::Loader::NthBit(1), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform
+  KHR_TEXTURE_TRANSFORM  = Dali::Scene3D::Loader::NthBit(2), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization
+  KHR_MATERIALS_IOR      = Dali::Scene3D::Loader::NthBit(3), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior
+  KHR_MATERIALS_SPECULAR = Dali::Scene3D::Loader::NthBit(4), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular
+
+};
+
 struct Document
 {
   Asset mAsset;
@@ -584,6 +595,11 @@ struct Document
 
   std::vector<Animation> mAnimations;
 
+  std::vector<std::string_view> mExtensionsUsed;
+  std::vector<std::string_view> mExtensionsRequired;
+
+  uint32_t mExtensionFlags{0};
+
   std::vector<Scene> mScenes;
   Ref<Scene>         mScene;
 
index 1bda11a..a6e3462 100644 (file)
@@ -19,6 +19,8 @@
 #include <dali-scene3d/internal/loader/gltf2-util.h>
 
 // EXTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/utils.h>
+#include <dali/devel-api/scripting/scripting.h>
 #include <dali/devel-api/threading/mutex.h>
 #include <dali/integration-api/debug.h>
 #include <limits> ///< for std::numeric_limits
@@ -60,6 +62,18 @@ static struct AttributeMapping
   {gltf2::Attribute::WEIGHTS_0, &MeshDefinition::mWeights0, sizeof(Vector4)},
 };
 
+static const Dali::Scripting::StringEnum EXTENSION_STRING_TABLE[] =
+  {
+    {"NONE", gltf2::ExtensionFlags::NONE},
+    {"KHR_mesh_quantization", gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION},
+    {"KHR_texture_transform", gltf2::ExtensionFlags::KHR_TEXTURE_TRANSFORM},
+    {"KHR_materials_ior", gltf2::ExtensionFlags::KHR_MATERIALS_IOR},
+    {"KHR_materials_specular", gltf2::ExtensionFlags::KHR_MATERIALS_SPECULAR},
+
+};
+
+static const unsigned int EXTENSION_STRING_TABLE_COUNT = sizeof(EXTENSION_STRING_TABLE) / sizeof(EXTENSION_STRING_TABLE[0]);
+
 std::vector<gltf2::Animation> ReadAnimationArray(const json_value_s& j)
 {
   auto results = json::Read::Array<gltf2::Animation, json::ObjectReader<gltf2::Animation>::Read>(j);
@@ -482,6 +496,12 @@ const json::Reader<gltf2::Document>& GetDocumentReader()
                                                   .Register(*json::MakeProperty("animations",
                                                                                 ReadAnimationArray,
                                                                                 &gltf2::Document::mAnimations))
+                                                  .Register(*json::MakeProperty("extensionsRequired",
+                                                                                json::Read::Array<std::string_view, json::Read::StringView>,
+                                                                                &gltf2::Document::mExtensionsRequired))
+                                                  .Register(*json::MakeProperty("extensionsUsed",
+                                                                                json::Read::Array<std::string_view, json::Read::StringView>,
+                                                                                &gltf2::Document::mExtensionsUsed))
                                                   .Register(*json::MakeProperty("scenes",
                                                                                 json::Read::Array<gltf2::Scene, json::ObjectReader<gltf2::Scene>::Read>,
                                                                                 &gltf2::Document::mScenes))
@@ -755,6 +775,8 @@ MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gltf2::Accessor& acc
 
 void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
 {
+  bool isQuantized = MaskMatch(document.mExtensionFlags, gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION);
+
   uint32_t meshCount = 0;
   context.mMeshIds.reserve(document.mMeshes.size());
   for(auto& mesh : document.mMeshes)
@@ -782,8 +804,20 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
         continue;
       }
 
-      auto& accPositions        = *positionIter->second;
-      meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
+      auto& accPositions                    = *positionIter->second;
+      meshDefinition.mPositions             = ConvertMeshPrimitiveAccessor(accPositions);
+      meshDefinition.mPositions.mNormalized = accPositions.mNormalized;
+
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION;
+        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_POSITION;
+        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION;
+        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_POSITION;
+      }
+
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_POSITION))) || accPositions.mComponentType == gltf2::Component::FLOAT);
+
       // glTF2 support vector4 tangent for mesh.
       // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview
       meshDefinition.mTangentType = Property::VECTOR4;
@@ -797,6 +831,44 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
           auto& accessor = meshDefinition.*(attributeMapping.mAccessor);
           accessor       = ConvertMeshPrimitiveAccessor(*iFind->second);
 
+          if(iFind->first == gltf2::Attribute::NORMAL)
+          {
+            meshDefinition.mNormals.mNormalized = iFind->second->mNormalized;
+
+            if(isQuantized)
+            {
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL;
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_NORMAL))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
+          }
+          if(iFind->first == gltf2::Attribute::TANGENT)
+          {
+            meshDefinition.mTangents.mNormalized = iFind->second->mNormalized;
+
+            if(isQuantized)
+            {
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT;
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TANGENT))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
+          }
+          if(iFind->first == gltf2::Attribute::TEXCOORD_0 || iFind->first == gltf2::Attribute::TEXCOORD_1)
+          {
+            meshDefinition.mTexCoords.mNormalized = iFind->second->mNormalized;
+
+            if(isQuantized)
+            {
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TEXCOORD;
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_TEXCOORD;
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TEXCOORD;
+              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_TEXCOORD;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_TEXCOORD))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
+          }
           if(iFind->first == gltf2::Attribute::JOINTS_0)
           {
             meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
@@ -849,17 +921,44 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
           auto it    = target.find(gltf2::Attribute::POSITION);
           if(it != endIt)
           {
-            blendShape.deltas = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.deltas             = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.deltas.mNormalized = it->second->mNormalized;
+
+            if(isQuantized)
+            {
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION;
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::U16_POSITION))) || it->second->mComponentType == gltf2::Component::FLOAT);
           }
           it = target.find(gltf2::Attribute::NORMAL);
           if(it != endIt)
           {
-            blendShape.normals = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.normals             = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.normals.mNormalized = it->second->mNormalized;
+
+            if(isQuantized)
+            {
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL;
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))) || it->second->mComponentType == gltf2::Component::FLOAT);
           }
           it = target.find(gltf2::Attribute::TANGENT);
           if(it != endIt)
           {
-            blendShape.tangents = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.tangents             = ConvertMeshPrimitiveAccessor(*it->second);
+            blendShape.tangents.mNormalized = it->second->mNormalized;
+
+            if(isQuantized)
+            {
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT;
+              blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT;
+            }
+
+            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))) || it->second->mComponentType == gltf2::Component::FLOAT);
           }
 
           if(!mesh.mWeights.empty())
@@ -1462,6 +1561,20 @@ bool GenerateDocument(json::unique_ptr& root, gt::Document& document, bool& isMR
 
 void ConvertGltfToContext(gt::Document& document, Gltf2Util::ConversionContext& context, bool isMRendererModel)
 {
+  for(auto& extension : document.mExtensionsRequired)
+  {
+    gltf2::ExtensionFlags flag;
+    if(Dali::Scripting::GetEnumeration<gltf2::ExtensionFlags>(extension.data(), EXTENSION_STRING_TABLE, EXTENSION_STRING_TABLE_COUNT, flag))
+    {
+      document.mExtensionFlags |= flag;
+    }
+    else
+    {
+      DALI_LOG_ERROR("Unsupported glTF extension required: %s\n", extension.data());
+      DALI_ASSERT_DEBUG(false && "Unsupported glTF extension required");
+    }
+  }
+
   Gltf2Util::ConvertBuffers(document, context);
   Gltf2Util::ConvertMaterials(document, context);
   Gltf2Util::ConvertMeshes(document, context);
index 76a6fa1..264f2fa 100644 (file)
@@ -447,26 +447,144 @@ void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uin
   textureHeight = 1u << powHeight;
 }
 
-void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
+template<typename T>
+float GetNormalizedScale()
+{
+  return 1.0f / (std::numeric_limits<T>::max());
+}
+
+template<typename T>
+void DequantizeData(std::vector<uint8_t>& buffer, float* dequantizedValues, uint32_t numValues, bool normalized)
+{
+  // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
+
+  T* values = reinterpret_cast<T*>(buffer.data());
+
+  for(uint32_t i = 0; i < numValues; ++i)
+  {
+    *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale<T>(), -1.0f) : *values;
+
+    values++;
+    dequantizedValues++;
+  }
+}
+
+void GetDequantizedData(std::vector<uint8_t>& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized)
+{
+  bool dequantized = false;
+
+  std::vector<uint8_t> dequantizedBuffer(count * numComponents * sizeof(float));
+  float*               dequantizedValues = reinterpret_cast<float*>(dequantizedBuffer.data());
+
+  if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
+  {
+    DequantizeData<int8_t>(buffer, dequantizedValues, numComponents * count, normalized);
+    dequantized = true;
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
+  {
+    DequantizeData<uint8_t>(buffer, dequantizedValues, numComponents * count, normalized);
+    dequantized = true;
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
+  {
+    DequantizeData<int16_t>(buffer, dequantizedValues, numComponents * count, normalized);
+    dequantized = true;
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
+  {
+    DequantizeData<uint16_t>(buffer, dequantizedValues, numComponents * count, normalized);
+    dequantized = true;
+  }
+
+  if(dequantized)
+  {
+    buffer = std::move(dequantizedBuffer);
+  }
+}
+
+void GetDequantizedMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t flags)
+{
+  float scale = 1.0f;
+
+  if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
+  {
+    scale = GetNormalizedScale<int8_t>();
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
+  {
+    scale = GetNormalizedScale<uint8_t>();
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
+  {
+    scale = GetNormalizedScale<int16_t>();
+  }
+  else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
+  {
+    scale = GetNormalizedScale<uint16_t>();
+  }
+
+  if(scale != 1.0f)
+  {
+    for(float& value : min)
+    {
+      value = std::max(value * scale, -1.0f);
+    }
+
+    for(float& value : max)
+    {
+      value = std::min(value * scale, 1.0f);
+    }
+  }
+}
+
+void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
 {
   uint32_t geometryBufferIndex = 0u;
   float    maxDistanceSquared  = 0.f;
   Vector3* geometryBufferV3    = reinterpret_cast<Vector3*>(geometryBuffer);
-  for(const auto& blendShape : blendShapes)
+  for(auto& blendShape : blendShapes)
   {
     if(blendShape.deltas.IsDefined())
     {
-      DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0u) ||
-                          blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
-                         "Blend Shape position buffer length not a multiple of element size");
+      const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
+      uint32_t   numVector3;
+
+      if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
+                            blendShape.deltas.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
+                           "Blend Shape position buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
+      }
+      else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
+                            blendShape.deltas.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
+                           "Blend Shape position buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
+      }
+      else
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0) ||
+                            blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
+                           "Blend Shape position buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+      }
 
-      const auto            bufferSize = blendShape.deltas.mBlob.GetBufferSize();
       std::vector<uint8_t>  buffer(bufferSize);
       std::vector<uint32_t> sparseIndices{};
 
       if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
       {
-        blendShape.deltas.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
+        GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::POSITIONS_MASK, blendShape.deltas.mNormalized);
+
+        if(blendShape.deltas.mNormalized)
+        {
+          GetDequantizedMinMax(blendShape.deltas.mBlob.mMin, blendShape.deltas.mBlob.mMax, blendShape.mFlags & MeshDefinition::POSITIONS_MASK);
+        }
+
+        blendShape.deltas.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
 
         // Calculate the difference with the original mesh.
         // Find the max distance to normalize the deltas.
@@ -501,17 +619,44 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector<MeshDe
 
     if(blendShape.normals.IsDefined())
     {
-      DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0u) ||
-                          blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
-                         "Blend Shape normals buffer length not a multiple of element size");
+      const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
+      uint32_t   numVector3;
+
+      if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
+                            blendShape.normals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
+                           "Blend Shape normals buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
+      }
+      else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
+                            blendShape.normals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
+                           "Blend Shape normals buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
+      }
+      else
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0) ||
+                            blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
+                           "Blend Shape normals buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+      }
 
-      const auto            bufferSize = blendShape.normals.mBlob.GetBufferSize();
       std::vector<uint8_t>  buffer(bufferSize);
       std::vector<uint32_t> sparseIndices;
 
       if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
       {
-        blendShape.normals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
+        GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::NORMALS_MASK, blendShape.normals.mNormalized);
+
+        if(blendShape.normals.mNormalized)
+        {
+          GetDequantizedMinMax(blendShape.normals.mBlob.mMin, blendShape.normals.mBlob.mMax, blendShape.mFlags & MeshDefinition::NORMALS_MASK);
+        }
+
+        blendShape.normals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
 
         // Calculate the difference with the original mesh, and translate to make all values positive.
         const Vector3* const deltasBuffer  = reinterpret_cast<const Vector3* const>(buffer.data());
@@ -547,17 +692,45 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector<MeshDe
 
     if(blendShape.tangents.IsDefined())
     {
-      DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0u) ||
-                          blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
-                         "Blend Shape tangents buffer length not a multiple of element size");
+      const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
+
+      uint32_t numVector3;
+
+      if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
+                            blendShape.tangents.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
+                           "Blend Shape tangents buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
+      }
+      else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
+                            blendShape.tangents.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
+                           "Blend Shape tangents buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
+      }
+      else
+      {
+        DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0) ||
+                            blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
+                           "Blend Shape tangents buffer length not a multiple of element size");
+        numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+      }
 
-      const auto            bufferSize = blendShape.tangents.mBlob.GetBufferSize();
       std::vector<uint8_t>  buffer(bufferSize);
       std::vector<uint32_t> sparseIndices;
 
       if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
       {
-        blendShape.tangents.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
+        GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::TANGENTS_MASK, blendShape.tangents.mNormalized);
+
+        if(blendShape.tangents.mNormalized)
+        {
+          GetDequantizedMinMax(blendShape.tangents.mBlob.mMin, blendShape.tangents.mBlob.mMax, blendShape.mFlags & MeshDefinition::TANGENTS_MASK);
+        }
+
+        blendShape.tangents.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
 
         // Calculate the difference with the original mesh, and translate to make all values positive.
         const Vector3* const deltasBuffer  = reinterpret_cast<const Vector3* const>(buffer.data());
@@ -853,13 +1026,38 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     }
   }
 
+  uint32_t numberOfVertices = 0u;
+
   std::vector<Vector3> positions;
   if(mPositions.IsDefined())
   {
-    DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
-                        mPositions.mBlob.mStride >= sizeof(Vector3)) &&
-                       "Position buffer length not a multiple of element size");
-    const auto           bufferSize = mPositions.mBlob.GetBufferSize();
+    const auto bufferSize = mPositions.mBlob.GetBufferSize();
+    uint32_t   numVector3;
+
+    if(MaskMatch(mFlags, S8_POSITION) || MaskMatch(mFlags, U8_POSITION))
+    {
+      DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
+                          mPositions.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
+                         "Position buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
+    }
+    else if(MaskMatch(mFlags, S16_POSITION) || MaskMatch(mFlags, U16_POSITION))
+    {
+      DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
+                          mPositions.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
+                         "Position buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
+                          mPositions.mBlob.mStride >= sizeof(Vector3)) &&
+                         "Position buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+    }
+
+    numberOfVertices = numVector3;
+
     std::vector<uint8_t> buffer(bufferSize);
 
     std::string path;
@@ -869,7 +1067,13 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
     }
 
-    uint32_t numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+    GetDequantizedData(buffer, 3u, numVector3, mFlags & POSITIONS_MASK, mPositions.mNormalized);
+
+    if(mPositions.mNormalized)
+    {
+      GetDequantizedMinMax(mPositions.mBlob.mMin, mPositions.mBlob.mMax, mFlags & POSITIONS_MASK);
+    }
+
     if(mPositions.mBlob.mMin.size() != 3u || mPositions.mBlob.mMax.size() != 3u)
     {
       mPositions.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
@@ -892,10 +1096,31 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
   auto       hasNormals  = mNormals.IsDefined();
   if(hasNormals)
   {
-    DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
-                        mNormals.mBlob.mStride >= sizeof(Vector3)) &&
-                       "Normal buffer length not a multiple of element size");
-    const auto           bufferSize = mNormals.mBlob.GetBufferSize();
+    const auto bufferSize = mNormals.mBlob.GetBufferSize();
+    uint32_t   numVector3;
+
+    if(MaskMatch(mFlags, S8_NORMAL))
+    {
+      DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
+                          mNormals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
+                         "Normal buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
+    }
+    else if(MaskMatch(mFlags, S16_NORMAL))
+    {
+      DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
+                          mNormals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
+                         "Normal buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
+                          mNormals.mBlob.mStride >= sizeof(Vector3)) &&
+                         "Normal buffer length not a multiple of element size");
+      numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
+    }
+
     std::vector<uint8_t> buffer(bufferSize);
 
     std::string path;
@@ -905,9 +1130,16 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
     }
 
-    mNormals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
+    GetDequantizedData(buffer, 3u, numVector3, mFlags & NORMALS_MASK, mNormals.mNormalized);
 
-    raw.mAttribs.push_back({"aNormal", Property::VECTOR3, static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer)});
+    if(mNormals.mNormalized)
+    {
+      GetDequantizedMinMax(mNormals.mBlob.mMin, mNormals.mBlob.mMax, mFlags & NORMALS_MASK);
+    }
+
+    mNormals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
+
+    raw.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)});
   }
   else if(mNormals.mBlob.mLength != 0 && isTriangles)
   {
@@ -931,10 +1163,32 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
   const auto hasUvs = mTexCoords.IsDefined();
   if(hasUvs)
   {
-    DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
-                        mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
-                       "Normal buffer length not a multiple of element size");
-    const auto           bufferSize = mTexCoords.mBlob.GetBufferSize();
+    const auto bufferSize = mTexCoords.mBlob.GetBufferSize();
+
+    uint32_t uvCount;
+
+    if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD))
+    {
+      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
+                          mTexCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
+                         "TexCoords buffer length not a multiple of element size");
+      uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
+    }
+    else if(MaskMatch(mFlags, S16_TEXCOORD) || MaskMatch(mFlags, U16_TEXCOORD))
+    {
+      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
+                          mTexCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
+                         "TexCoords buffer length not a multiple of element size");
+      uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
+                          mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
+                         "TexCoords buffer length not a multiple of element size");
+      uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
+    }
+
     std::vector<uint8_t> buffer(bufferSize);
 
     std::string path;
@@ -944,7 +1198,8 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
     }
 
-    const auto uvCount = bufferSize / sizeof(Vector2);
+    GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, mTexCoords.mNormalized);
+
     if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
     {
       auto uv    = reinterpret_cast<Vector2*>(buffer.data());
@@ -956,6 +1211,11 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       }
     }
 
+    if(mTexCoords.mNormalized)
+    {
+      GetDequantizedMinMax(mTexCoords.mBlob.mMin, mTexCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
+    }
+
     mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
 
     raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
@@ -963,11 +1223,35 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
 
   if(mTangents.IsDefined())
   {
-    uint32_t propertySize = static_cast<uint32_t>((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
-    DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
-                        mTangents.mBlob.mStride >= propertySize) &&
-                       "Tangents buffer length not a multiple of element size");
-    const auto           bufferSize = mTangents.mBlob.GetBufferSize();
+    const auto bufferSize = mTangents.mBlob.GetBufferSize();
+
+    uint32_t propertySize   = static_cast<uint32_t>((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
+    uint32_t componentCount = static_cast<uint32_t>(propertySize / sizeof(float));
+
+    uint32_t numTangents;
+
+    if(MaskMatch(mFlags, S8_TANGENT))
+    {
+      DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int8_t) * componentCount) == 0) ||
+                          mTangents.mBlob.mStride >= (sizeof(int8_t) * componentCount)) &&
+                         "Tangents buffer length not a multiple of element size");
+      numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * componentCount));
+    }
+    else if(MaskMatch(mFlags, S16_TANGENT))
+    {
+      DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int16_t) * componentCount) == 0) ||
+                          mTangents.mBlob.mStride >= (sizeof(int16_t) * componentCount)) &&
+                         "Tangents buffer length not a multiple of element size");
+      numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * componentCount));
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
+                          mTangents.mBlob.mStride >= propertySize) &&
+                         "Tangents buffer length not a multiple of element size");
+      numTangents = static_cast<uint32_t>(bufferSize / propertySize);
+    }
+
     std::vector<uint8_t> buffer(bufferSize);
 
     std::string path;
@@ -976,9 +1260,17 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     {
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
     }
-    mTangents.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
 
-    raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
+    GetDequantizedData(buffer, componentCount, numTangents, mFlags & TANGENTS_MASK, mTangents.mNormalized);
+
+    if(mTangents.mNormalized)
+    {
+      GetDequantizedMinMax(mTangents.mBlob.mMin, mTangents.mBlob.mMax, mFlags & TANGENTS_MASK);
+    }
+
+    mTangents.mBlob.ApplyMinMax(numTangents, reinterpret_cast<float*>(buffer.data()));
+
+    raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(numTangents), std::move(buffer)});
   }
   else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
   {
@@ -1086,22 +1378,31 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
   blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
   blendShapesBlob.mLength = 0u;
 
-  for(const auto& blendShape : mBlendShapes)
-  {
-    for(auto i : {&blendShape.deltas, &blendShape.normals, &blendShape.tangents})
+  uint32_t totalTextureSize(0u);
+
+  auto processAccessor = [&](const Accessor& accessor, uint32_t vector3Size) {
+    if(accessor.IsDefined())
     {
-      if(i->IsDefined())
-      {
-        blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, i->mBlob.mOffset);
-        blendShapesBlob.mLength += i->mBlob.mLength;
-      }
+      blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, accessor.mBlob.mOffset);
+      blendShapesBlob.mLength += accessor.mBlob.mLength;
+
+      totalTextureSize += accessor.mBlob.mLength / vector3Size;
     }
+  };
+
+  for(const auto& blendShape : mBlendShapes)
+  {
+    const auto positionMask = blendShape.mFlags & POSITIONS_MASK;
+    const auto normalMask   = blendShape.mFlags & NORMALS_MASK;
+    const auto tangentMask  = blendShape.mFlags & TANGENTS_MASK;
+
+    processAccessor(blendShape.deltas, MaskMatch(positionMask, S8_POSITION) ? sizeof(uint8_t) * 3 : (MaskMatch(positionMask, S16_POSITION) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
+    processAccessor(blendShape.normals, MaskMatch(normalMask, S8_NORMAL) ? sizeof(uint8_t) * 3 : (MaskMatch(normalMask, S16_NORMAL) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
+    processAccessor(blendShape.tangents, MaskMatch(tangentMask, S8_TANGENT) ? sizeof(uint8_t) * 3 : (MaskMatch(tangentMask, S16_TANGENT) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
   }
 
   if(HasBlendShapes())
   {
-    const uint32_t numberOfVertices = static_cast<uint32_t>(mPositions.mBlob.mLength / sizeof(Vector3));
-
     // Calculate the size of one buffer inside the texture.
     raw.mBlendShapeBufferOffset = numberOfVertices;
 
@@ -1111,7 +1412,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
 
     if(!mBlendShapeHeader.IsDefined())
     {
-      CalculateTextureSize(static_cast<uint32_t>(blendShapesBlob.mLength / sizeof(Vector3)), textureWidth, textureHeight);
+      CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
       calculateGltf2BlendShapes = true;
     }
     else
index 7bf1f7c..d1016b5 100644 (file)
@@ -45,7 +45,7 @@ struct DALI_SCENE3D_API MeshDefinition
     INVALID = std::numeric_limits<uint32_t>::max()
   };
 
-  enum Flags : uint16_t
+  enum Flags : uint32_t
   {
     FLIP_UVS_VERTICAL = NthBit(0),
     U32_INDICES       = NthBit(1), // default is unsigned short
@@ -54,6 +54,26 @@ struct DALI_SCENE3D_API MeshDefinition
     U8_JOINT_IDS      = NthBit(4),
     U16_WEIGHT        = NthBit(5), // default is floats
     U8_WEIGHT         = NthBit(6),
+    S8_POSITION       = NthBit(7),  // default is floats
+    U8_POSITION       = NthBit(8),  // default is floats
+    S16_POSITION      = NthBit(9),  // default is floats
+    U16_POSITION      = NthBit(10), // default is floats
+    S8_NORMAL         = NthBit(11), // default is floats
+    S16_NORMAL        = NthBit(12), // default is floats
+    S8_TANGENT        = NthBit(13), // default is floats
+    S16_TANGENT       = NthBit(14), // default is floats
+    S8_TEXCOORD       = NthBit(15), // default is floats
+    U8_TEXCOORD       = NthBit(16), // default is floats
+    S16_TEXCOORD      = NthBit(17), // default is floats
+    U16_TEXCOORD      = NthBit(18), // default is floats
+  };
+
+  enum FlagMasks : uint32_t
+  {
+    POSITIONS_MASK = 0x780,
+    NORMALS_MASK   = 0x1800,
+    TANGENTS_MASK  = 0x6000,
+    TEXCOORDS_MASK = 0x78000,
   };
 
   enum Attributes
@@ -166,6 +186,7 @@ struct DALI_SCENE3D_API MeshDefinition
     Blob                        mBlob;
     std::unique_ptr<SparseBlob> mSparse;
     Index                       mBufferIdx = INVALID_INDEX;
+    bool                        mNormalized{false};
 
     Accessor() = default;
 
@@ -198,6 +219,7 @@ struct DALI_SCENE3D_API MeshDefinition
     Accessor    normals;
     Accessor    tangents;
     float       weight = 0.f;
+    uint32_t    mFlags = 0x0;
   };
 
   struct RawData