[dali_2.2.52] Merge branch 'devel/master' 09/301209/1
authorAdam Bialogonski <adam.b@samsung.com>
Fri, 10 Nov 2023 07:43:57 +0000 (07:43 +0000)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 10 Nov 2023 07:43:57 +0000 (07:43 +0000)
Change-Id: Id093999582dc886d79e42d55bd9de2418f868305

71 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-Gltf2Asset.cpp
automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp
automated-tests/src/dali-scene3d/utc-Dali-MeshDefinition.cpp
automated-tests/src/dali-scene3d/utc-Dali-ShaderManager.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-VisualUrl.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-encoded-image-buffer.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-encoded-image-buffer.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-program.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-program.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.vert
dali-scene3d/internal/graphics/shaders/shadow-map-shader.vert
dali-scene3d/internal/loader/dli-loader-impl.cpp
dali-scene3d/internal/loader/gltf2-asset.cpp
dali-scene3d/internal/loader/gltf2-asset.h
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/internal/model-components/model-primitive-impl.cpp
dali-scene3d/internal/model-components/model-primitive-impl.h
dali-scene3d/public-api/loader/mesh-definition.cpp
dali-scene3d/public-api/loader/mesh-definition.h
dali-scene3d/public-api/loader/node-definition.cpp
dali-scene3d/public-api/loader/shader-definition.cpp
dali-scene3d/public-api/loader/shader-definition.h
dali-scene3d/public-api/loader/shader-manager.cpp
dali-scene3d/public-api/loader/shader-manager.h
dali-scene3d/public-api/loader/shader-option.cpp
dali-scene3d/public-api/loader/shader-option.h
dali-scene3d/public-api/loader/skinning-details.cpp
dali-scene3d/public-api/model-components/model-node.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/image-loader/fast-track-loading-task.cpp
dali-toolkit/internal/image-loader/loading-task.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/visuals/animated-gradient/animated-gradient-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/svg/svg-task.cpp
dali-toolkit/internal/visuals/svg/svg-task.h
dali-toolkit/internal/visuals/svg/svg-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.h
dali-toolkit/internal/visuals/visual-url.cpp
dali-toolkit/internal/visuals/visual-url.h
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

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 9b01631..e60dfe4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@
 // Enable debug log for test coverage
 #define DEBUG_ENABLED 1
 
-#include "dali-scene3d/internal/loader/gltf2-asset.h"
 #include <dali-test-suite-utils.h>
 #include <string>
+#include "dali-scene3d/internal/loader/gltf2-asset.h"
 
 using namespace Dali;
 using namespace Dali::Scene3D::Loader;
@@ -52,8 +52,12 @@ int UtcDaliGltf2AssetComponentSize(void)
 }
 
 #define FROM_STRING_HELPER(x) FromString(#x, strlen(#x))
+#define TARGET_FROM_STRING_HELPER(x) TargetFromString(#x, strlen(#x))
+#define HASH_FROM_STRING_HELPER(x) HashFromString(#x, strlen(#x))
 
-#define STRING_CHECK(type, x) DALI_TEST_EQUAL(gltf2::type:: FROM_STRING_HELPER(x), gltf2::type::x)
+#define STRING_CHECK(type, x) DALI_TEST_EQUAL(gltf2::type::FROM_STRING_HELPER(x), gltf2::type::x)
+#define TARGET_STRING_CHECK(type, x) DALI_TEST_EQUAL(gltf2::type::TARGET_FROM_STRING_HELPER(x), gltf2::type::x)
+#define HASH_STRING_CHECK(type, x, y, z) DALI_TEST_EQUAL(gltf2::type::HASH_FROM_STRING_HELPER(x), gltf2::type::ToHash(gltf2::type::y, true, z))
 
 int UtcDaliGltf2AssetAccessorType(void)
 {
@@ -81,15 +85,20 @@ int UtcDaliGltf2AssetAlphaMode(void)
 
 int UtcDaliGltf2AssetAttribute(void)
 {
-  STRING_CHECK(Attribute, POSITION);
-  STRING_CHECK(Attribute, NORMAL);
-  STRING_CHECK(Attribute, TANGENT);
-  STRING_CHECK(Attribute, TEXCOORD_0);
-  STRING_CHECK(Attribute, TEXCOORD_1);
-  STRING_CHECK(Attribute, COLOR_0);
-  STRING_CHECK(Attribute, JOINTS_0);
-  STRING_CHECK(Attribute, WEIGHTS_0);
-  DALI_TEST_EQUAL(gltf2::Attribute::FROM_STRING_HELPER(VISCOSITY), gltf2::Attribute::INVALID);
+  TARGET_STRING_CHECK(Attribute, POSITION);
+  TARGET_STRING_CHECK(Attribute, NORMAL);
+  TARGET_STRING_CHECK(Attribute, TANGENT);
+  HASH_STRING_CHECK(Attribute, TEXCOORD_0, TEXCOORD_N, 0);
+  HASH_STRING_CHECK(Attribute, TEXCOORD_1, TEXCOORD_N, 1);
+  HASH_STRING_CHECK(Attribute, COLOR_0, COLOR_N, 0);
+  HASH_STRING_CHECK(Attribute, COLOR_1, COLOR_N, 1);
+  HASH_STRING_CHECK(Attribute, JOINTS_0, JOINTS_N, 0);
+  HASH_STRING_CHECK(Attribute, JOINTS_1, JOINTS_N, 1);
+  HASH_STRING_CHECK(Attribute, JOINTS_2, JOINTS_N, 2);
+  HASH_STRING_CHECK(Attribute, WEIGHTS_0, WEIGHTS_N, 0);
+  HASH_STRING_CHECK(Attribute, WEIGHTS_1, WEIGHTS_N, 1);
+  HASH_STRING_CHECK(Attribute, WEIGHTS_2, WEIGHTS_N, 2);
+  DALI_TEST_EQUAL(gltf2::Attribute::TARGET_FROM_STRING_HELPER(VISCOSITY), gltf2::Attribute::INVALID);
 
   END_TEST;
 }
@@ -122,11 +131,11 @@ int UtcDaliGltf2AssetAccessorSparse(void)
 
   std::vector<gltf2::BufferView> bufferViews;
 
-  gltf2::Accessor::Sparse sparse{ 256u };
-  sparse.mIndices.mBufferView = gltf2::Ref<gltf2::BufferView>(bufferViews, 5u);
+  gltf2::Accessor::Sparse sparse{256u};
+  sparse.mIndices.mBufferView    = gltf2::Ref<gltf2::BufferView>(bufferViews, 5u);
   sparse.mIndices.mComponentType = gltf2::Component::FLOAT;
-  sparse.mValues.mBufferView = gltf2::Ref<gltf2::BufferView>(bufferViews, 284u);
-  sparse.mValues.mByteOffset = 16532;
+  sparse.mValues.mBufferView     = gltf2::Ref<gltf2::BufferView>(bufferViews, 284u);
+  sparse.mValues.mByteOffset     = 16532;
   acc.SetSparse(sparse);
 
   DALI_TEST_EQUAL(acc.mSparse->mCount, sparse.mCount);
@@ -138,4 +147,3 @@ int UtcDaliGltf2AssetAccessorSparse(void)
 
   END_TEST;
 }
-
index 723c3ae..30ad93f 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 |
@@ -392,7 +419,7 @@ int UtcDaliGltfLoaderSuccess1(void)
 
   using Blob     = MeshDefinition::Blob;
   using Accessor = MeshDefinition::Accessor;
-  const MeshDefinition meshGroundTruth[]{
+  MeshDefinition meshGroundTruth[]{
     {
       nullptr,
       0,
@@ -402,8 +429,6 @@ int UtcDaliGltfLoaderSuccess1(void)
       Accessor{Blob{0, 0}, {}},
       Accessor{Blob{0, 0}, {}},
       Accessor{Blob{0, 0}, {}},
-      Accessor{Blob{0, 0}, {}},
-      Accessor{Blob{0, 0}, {}},
     },
     {
       nullptr,
@@ -414,10 +439,12 @@ int UtcDaliGltfLoaderSuccess1(void)
       Accessor{Blob{0, 0}, {}},
       Accessor{Blob{0, 0}, {}},
       Accessor{Blob{0, 0}, {}},
-      Accessor{Blob{0, 0}, {}},
-      Accessor{Blob{0, 0}, {}},
     },
   };
+  meshGroundTruth[0].mColors.push_back(Accessor{Blob{0, 0}, {}});
+  meshGroundTruth[0].mTexCoords.push_back(Accessor{Blob{0, 0}, {}});
+  meshGroundTruth[1].mColors.push_back(Accessor{Blob{0, 0}, {}});
+  meshGroundTruth[1].mTexCoords.push_back(Accessor{Blob{0, 0}, {}});
 
   auto iMesh = meshes.begin();
   for(auto& m : meshGroundTruth)
@@ -427,19 +454,24 @@ int UtcDaliGltfLoaderSuccess1(void)
     auto& md = iMesh->first;
     DALI_TEST_EQUAL(md.mFlags, m.mFlags);
     DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
-    for(auto mp : {
-          &MeshDefinition::mIndices,
-          &MeshDefinition::mPositions,
-          &MeshDefinition::mNormals,
-          &MeshDefinition::mTexCoords,
-          &MeshDefinition::mColors,
-          &MeshDefinition::mTangents,
-          &MeshDefinition::mJoints0,
-          &MeshDefinition::mWeights0})
-    {
-      DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
-      DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
-    }
+
+    DALI_TEST_EQUAL((md.mIndices).IsDefined(), (m.mIndices).IsDefined());
+    DALI_TEST_EQUAL((md.mIndices).mBlob.IsDefined(), (m.mIndices).mBlob.IsDefined());
+
+    DALI_TEST_EQUAL((md.mPositions).IsDefined(), (m.mPositions).IsDefined());
+    DALI_TEST_EQUAL((md.mPositions).mBlob.IsDefined(), (m.mPositions).mBlob.IsDefined());
+
+    DALI_TEST_EQUAL((md.mNormals).IsDefined(), (m.mNormals).IsDefined());
+    DALI_TEST_EQUAL((md.mNormals).mBlob.IsDefined(), (m.mNormals).mBlob.IsDefined());
+
+    DALI_TEST_EQUAL((md.mTangents).IsDefined(), (m.mTangents).IsDefined());
+    DALI_TEST_EQUAL((md.mTangents).mBlob.IsDefined(), (m.mTangents).mBlob.IsDefined());
+
+    DALI_TEST_EQUAL(md.mTexCoords.empty(), m.mTexCoords.empty());
+    DALI_TEST_EQUAL(md.mColors.empty(), m.mColors.empty());
+
+    DALI_TEST_EQUAL(md.mJoints.empty(), (m.mJoints.empty()));
+    DALI_TEST_EQUAL(md.mWeights.empty(), (m.mWeights.empty()));
 
     DALI_TEST_EQUAL(md.mBlendShapeHeader.IsDefined(), m.mBlendShapeHeader.IsDefined());
 
@@ -458,7 +490,7 @@ int UtcDaliGltfLoaderSuccess1(void)
 
 int UtcDaliGltfLoaderSuccess2(void)
 {
-  Context                 ctx;
+  Context ctx;
 
   ctx.loader.LoadModel(TEST_RESOURCE_DIR "/AnimatedCubeStride.gltf", ctx.loadResult);
 
@@ -487,8 +519,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 +539,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 +631,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 +644,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 +692,7 @@ int UtcDaliGltfLoaderMRendererTest(void)
 int UtcDaliGltfLoaderAnimationLoadingTest(void)
 {
   TestApplication app;
-  Context ctx;
+  Context         ctx;
 
   auto& resources = ctx.resources;
 
@@ -644,8 +703,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 +754,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 +805,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 +842,105 @@ 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(false, md.mTexCoords.empty());
+  DALI_TEST_EQUAL(true, md.mTexCoords[0].IsDefined());
+  DALI_TEST_EQUAL(false, md.mTexCoords[0].mNormalized);
+  DALI_TEST_EQUAL(sizeof(uint16_t) * 2, md.mTexCoords[0].mBlob.mElementSizeHint);
+  DALI_TEST_EQUAL(true, md.mTexCoords[0].mBlob.IsDefined());
+  DALI_TEST_EQUAL(1624, md.mTexCoords[0].mBlob.mLength);
+  DALI_TEST_EQUAL(0u, md.mTexCoords[0].mBlob.mMin.size());
+  DALI_TEST_EQUAL(0u, md.mTexCoords[0].mBlob.mMax.size());
+
+  END_TEST;
+}
index 5ee5617..ba67d42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
 
 #include <vector>
 
-#include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-scene3d/public-api/loader/buffer-definition.h>
+#include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-test-suite-utils.h>
 
 using namespace Dali;
@@ -28,14 +28,14 @@ int UtcDaliMeshDefinitionBlobApplyMinMaxBothMinMaxApplied(void)
 {
   using Container = std::vector<float>;
 
-  Container buffer = { 4.0f, 6.0f, 8.0f, 10.0f, 12.0f };
-  Container minValues = { 5.0f };
-  Container maxValues = { 10.0f };
-  Container result = {5.0f, 6.0f, 8.0f, 10.0f, 10.0f};
+  Container buffer    = {4.0f, 6.0f, 8.0f, 10.0f, 12.0f};
+  Container minValues = {5.0f};
+  Container maxValues = {10.0f};
+  Container result    = {5.0f, 6.0f, 8.0f, 10.0f, 10.0f};
 
   MeshDefinition::Blob::ApplyMinMax(minValues, maxValues, 5, buffer.data());
 
-  for( auto i = 0u; i < result.size(); ++i)
+  for(auto i = 0u; i < result.size(); ++i)
   {
     DALI_TEST_EQUALS(buffer[i], result[i], TEST_LOCATION);
   }
@@ -47,14 +47,14 @@ int UtcDaliMeshDefinitionBlobApplyMinMaxOnlyMin(void)
 {
   using Container = std::vector<float>;
 
-  Container buffer = { 4.0f, 6.0f, 8.0f, 10.0f, 12.0f };
-  Container minValues = { 5.0f };
+  Container buffer    = {4.0f, 6.0f, 8.0f, 10.0f, 12.0f};
+  Container minValues = {5.0f};
   Container maxValues = {};
-  Container result = {5.0f, 6.0f, 8.0f, 10.0f, 12.0f};
+  Container result    = {5.0f, 6.0f, 8.0f, 10.0f, 12.0f};
 
   MeshDefinition::Blob::ApplyMinMax(minValues, maxValues, 5, buffer.data());
 
-  for( auto i = 0u; i < result.size(); ++i)
+  for(auto i = 0u; i < result.size(); ++i)
   {
     DALI_TEST_EQUALS(buffer[i], result[i], TEST_LOCATION);
   }
@@ -66,14 +66,14 @@ int UtcDaliMeshDefinitionBlobApplyMinMaxOnlyMax(void)
 {
   using Container = std::vector<float>;
 
-  Container buffer = { 4.0f, 6.0f, 8.0f, 10.0f, 12.0f };
-  Container minValues = { };
-  Container maxValues = { 10.0f };
-  Container result = {4.0f, 6.0f, 8.0f, 10.0f, 10.0f};
+  Container buffer    = {4.0f, 6.0f, 8.0f, 10.0f, 12.0f};
+  Container minValues = {};
+  Container maxValues = {10.0f};
+  Container result    = {4.0f, 6.0f, 8.0f, 10.0f, 10.0f};
 
   MeshDefinition::Blob::ApplyMinMax(minValues, maxValues, 5, buffer.data());
 
-  for( auto i = 0u; i < result.size(); ++i)
+  for(auto i = 0u; i < result.size(); ++i)
   {
     DALI_TEST_EQUALS(buffer[i], result[i], TEST_LOCATION);
   }
@@ -85,14 +85,14 @@ int UtcDaliMeshDefinitionBlobApplyMinMaxBothEmpty(void)
 {
   using Container = std::vector<float>;
 
-  Container buffer = { 4.0f, 6.0f, 8.0f, 10.0f, 12.0f };
+  Container buffer = {4.0f, 6.0f, 8.0f, 10.0f, 12.0f};
   Container minValues;
   Container maxValues;
   Container result = {4.0f, 6.0f, 8.0f, 10.0f, 12.0f};
 
   MeshDefinition::Blob::ApplyMinMax(minValues, maxValues, 5, buffer.data());
 
-  for( auto i = 0u; i < result.size(); ++i)
+  for(auto i = 0u; i < result.size(); ++i)
   {
     DALI_TEST_EQUALS(buffer[i], result[i], TEST_LOCATION);
   }
@@ -116,17 +116,17 @@ int UtcDaliMeshDefinitionByteSkinWeight(void)
   meshDefinition.mPositions =
     MeshDefinition::Accessor{
       std::move(MeshDefinition::Blob{0, 12, 0, (uint16_t)12, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
-  meshDefinition.mJoints0 =
+  meshDefinition.mJoints.push_back(
     MeshDefinition::Accessor{
-      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
-  meshDefinition.mWeights0 =
+      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0});
+  meshDefinition.mWeights.push_back(
     MeshDefinition::Accessor{
-      std::move(MeshDefinition::Blob{0, 8, 0, (uint16_t)8, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
+      std::move(MeshDefinition::Blob{0, 8, 0, (uint16_t)8, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0});
 
   MeshDefinition::RawData rawData = meshDefinition.LoadRaw("", buffers);
 
   DALI_TEST_EQUALS(rawData.mAttribs.size(), 4, TEST_LOCATION);
-  DALI_TEST_EQUALS(rawData.mAttribs[3].mName, "aWeights", TEST_LOCATION);
+  DALI_TEST_EQUALS(rawData.mAttribs[3].mName, "aWeights0", TEST_LOCATION);
   DALI_TEST_EQUALS(rawData.mAttribs[3].mNumElements, 2, TEST_LOCATION);
   float* value = reinterpret_cast<float*>(rawData.mAttribs[3].mData.data());
   for(uint32_t i = 0; i < rawData.mAttribs[3].mNumElements * 4; ++i)
@@ -139,7 +139,7 @@ int UtcDaliMeshDefinitionByteSkinWeight(void)
 
 int UtcDaliMeshDefinitionShortSkinWeight(void)
 {
-    float data8[8] = {0.062516, 0.098634, 0.749752, 0.936492, 0.741207, 0.379873, 0.392386, 0.380468};
+  float data8[8] = {0.062516, 0.098634, 0.749752, 0.936492, 0.741207, 0.379873, 0.392386, 0.380468};
 
   BufferDefinition bufferDefinition;
   bufferDefinition.mUri        = "data:application/base64,ARBAGe+/ve+/vT9hc2RmYXNkZmFzZGZhc2RmYXNkZmE=";
@@ -153,17 +153,17 @@ int UtcDaliMeshDefinitionShortSkinWeight(void)
   meshDefinition.mPositions =
     MeshDefinition::Accessor{
       std::move(MeshDefinition::Blob{0, 12, 0, (uint16_t)12, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
-  meshDefinition.mJoints0 =
+  meshDefinition.mJoints.push_back(
     MeshDefinition::Accessor{
-      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
-  meshDefinition.mWeights0 =
+      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0});
+  meshDefinition.mWeights.push_back(
     MeshDefinition::Accessor{
-      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0};
+      std::move(MeshDefinition::Blob{0, 16, 0, (uint16_t)16, std::vector<float>(), std::vector<float>()}), std::move(sparseBlob), 0});
 
   MeshDefinition::RawData rawData = meshDefinition.LoadRaw("", buffers);
 
   DALI_TEST_EQUALS(rawData.mAttribs.size(), 4, TEST_LOCATION);
-  DALI_TEST_EQUALS(rawData.mAttribs[3].mName, "aWeights", TEST_LOCATION);
+  DALI_TEST_EQUALS(rawData.mAttribs[3].mName, "aWeights0", TEST_LOCATION);
   DALI_TEST_EQUALS(rawData.mAttribs[3].mNumElements, 2, TEST_LOCATION);
   float* value = reinterpret_cast<float*>(rawData.mAttribs[3].mData.data());
   for(uint32_t i = 0; i < rawData.mAttribs[3].mNumElements * 4; ++i)
index f272ba9..191a631 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -68,89 +68,90 @@ int UtcDaliShaderManagerProduceShader(void)
 
   Permutation permutations[]{
     {
+      //0
       [](ShaderParameters& p) {},
       {},
       RendererState::DEPTH_TEST | RendererState::CULL_BACK,
     },
     {
-      [](ShaderParameters& p)
-      {
+      //1
+      [](ShaderParameters& p) {
         p.materialDefinition.mFlags |= MaterialDefinition::TRANSPARENCY;
       },
       {ShaderOption::Type::THREE_TEXTURE},
       RendererState::ALPHA_BLEND,
     },
-    {[](ShaderParameters& p)
-     {
+    {//2
+     [](ShaderParameters& p) {
        p.materialDefinition.mFlags |= MaterialDefinition::ALBEDO;
        p.materialDefinition.mTextureStages.push_back({MaterialDefinition::ALBEDO, {}});
      },
      {ShaderOption::Type::THREE_TEXTURE, ShaderOption::Type::BASE_COLOR_TEXTURE}},
-    {[](ShaderParameters& p)
-     {
+    {//3
+     [](ShaderParameters& p) {
        p.materialDefinition.mTextureStages.push_back({MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS, {}});
      },
      {ShaderOption::Type::THREE_TEXTURE, ShaderOption::Type::METALLIC_ROUGHNESS_TEXTURE}},
-    {[](ShaderParameters& p)
-     {
+    {//4
+     [](ShaderParameters& p) {
        p.materialDefinition.mFlags |= MaterialDefinition::NORMAL;
        p.materialDefinition.mTextureStages.push_back({MaterialDefinition::NORMAL, {}});
      },
      {ShaderOption::Type::THREE_TEXTURE, ShaderOption::Type::NORMAL_TEXTURE}},
-    {[](ShaderParameters& p)
-     {
+    {//5
+     [](ShaderParameters& p) {
        p.materialDefinition.mFlags |= MaterialDefinition::SUBSURFACE;
      },
      {ShaderOption::Type::SUBSURFACE}},
-    {[](ShaderParameters& p)
-     {
+    {//6
+     [](ShaderParameters& p) {
        p.materialDefinition.SetAlphaCutoff(.5f);
      },
      {ShaderOption::Type::ALPHA_TEST}},
-    {[](ShaderParameters& p)
-     {
+    {//7
+     [](ShaderParameters& p) {
        p.materialDefinition.SetAlphaCutoff(1.f);
      },
      {ShaderOption::Type::ALPHA_TEST}},
-    {[](ShaderParameters& p)
-     {
+    {//8
+     [](ShaderParameters& p) {
        p.materialDefinition.mFlags |= MaterialDefinition::GLTF_CHANNELS;
      },
      {ShaderOption::Type::GLTF_CHANNELS}},
-    {[](ShaderParameters& p)
-     {
-       p.meshDefinition.mJoints0.mBlob.mOffset  = 0;
-       p.meshDefinition.mWeights0.mBlob.mOffset = 0;
+    {//9
+     [](ShaderParameters& p) {
+       p.meshDefinition.mJoints[0].mBlob.mOffset  = 0;
+       p.meshDefinition.mWeights[0].mBlob.mOffset = 0;
      },
      {ShaderOption::Type::SKINNING}},
-    {[](ShaderParameters& p)
-     {
+    {//10
+     [](ShaderParameters& p) {
        p.meshDefinition.mFlags |= MeshDefinition::FLIP_UVS_VERTICAL;
      },
      {ShaderOption::Type::FLIP_UVS_VERTICAL}},
     {
-      [](ShaderParameters& p)
-      {
+      //11
+      [](ShaderParameters& p) {
         p.meshDefinition.mBlendShapes.push_back({});
       },
     },
-    {[](ShaderParameters& p)
-     {
+    {//12
+     [](ShaderParameters& p) {
        p.meshDefinition.mBlendShapes.back().deltas.mBlob.mOffset = 0;
      },
      {ShaderOption::Type::MORPH_POSITION}},
-    {[](ShaderParameters& p)
-     {
+    {//13
+     [](ShaderParameters& p) {
        p.meshDefinition.mBlendShapes.back().normals.mBlob.mOffset = 0;
      },
      {ShaderOption::Type::MORPH_NORMAL}},
-    {[](ShaderParameters& p)
-     {
+    {//14
+     [](ShaderParameters& p) {
        p.meshDefinition.mBlendShapes.back().tangents.mBlob.mOffset = 0;
      },
      {ShaderOption::Type::MORPH_TANGENT}},
-    {[](ShaderParameters& p)
-     {
+    {//15
+     [](ShaderParameters& p) {
        auto& blendShapes = p.meshDefinition.mBlendShapes;
        DALI_ASSERT_ALWAYS(!blendShapes.empty() &&
                           (blendShapes.back().deltas.mBlob.mOffset != MeshDefinition::INVALID ||
@@ -160,19 +161,19 @@ int UtcDaliShaderManagerProduceShader(void)
      },
      {ShaderOption::Type::MORPH_VERSION_2_0}},
 
-    {[](ShaderParameters& p)
-     {
+    {//16
+     [](ShaderParameters& p) {
        p.materialDefinition.mFlags |= MaterialDefinition::OCCLUSION;
      },
      {ShaderOption::Type::OCCLUSION}},
 
-    {[](ShaderParameters& p)
-     {
-       p.meshDefinition.mColors.mBlob.mOffset = 0;
+    {//17
+     [](ShaderParameters& p) {
+       p.meshDefinition.mColors[0].mBlob.mOffset = 0;
      },
      {ShaderOption::Type::COLOR_ATTRIBUTE}},
-    {[](ShaderParameters& p)
-     {
+    {//18
+     [](ShaderParameters& p) {
        p.meshDefinition.mTangentType = Property::VECTOR4;
      },
      {ShaderOption::Type::VEC4_TANGENT}},
@@ -254,28 +255,41 @@ int UtcDaliShaderManagerProduceShader(void)
     MeshDefinition     meshDefinition;
     MaterialDefinition materialDefinition;
     ShaderParameters   shaderParameter{meshDefinition, materialDefinition, nodeDefinition};
-
+    // Only define skinning accessors for skinning test...
+    if(permutationSet.permutations.size() > 1)
+    {
+      auto& checkP = permutationSet.permutations[1];
+      if(auto search = checkP->options.find(ShaderOption::Type::SKINNING);
+         search != checkP->options.end())
+      {
+        meshDefinition.mJoints.push_back(MeshDefinition::Accessor{MeshDefinition::Blob{0, 0}, {}});
+        meshDefinition.mWeights.push_back(MeshDefinition::Accessor{MeshDefinition::Blob{0, 0}, {}});
+      }
+    }
     std::set<std::string> defines;
-    ShaderOption          option;
+    ShaderOption          option1;
     RendererState::Type   rendererState = 0;
     for(auto permutation : permutationSet.permutations)
     {
       permutation->configureFn(shaderParameter);
       if(materialDefinition.mFlags & MaterialDefinition::TRANSPARENCY)
       {
-        option.SetTransparency();
+        option1.SetTransparency();
       }
       for(auto&& optionType : permutation->options)
       {
-        option.AddOption(optionType);
+        option1.AddOption(optionType);
       }
       rendererState = (rendererState | permutation->rendererStateSet) & ~permutation->rendererStateClear;
     }
-    option.AddOption(ShaderOption::Type::THREE_TEXTURE);
+    option1.AddOption(ShaderOption::Type::THREE_TEXTURE);
 
-    Shader shaderFromMeshAndMaterial = shaderManager.ProduceShader(materialDefinition, meshDefinition);
-    Shader shaderFromOption          = shaderManager.ProduceShader(option);
-    DALI_TEST_EQUAL(shaderFromMeshAndMaterial, shaderFromOption);
+    ShaderOption option2 = shaderManager.ProduceShaderOption(materialDefinition, meshDefinition);
+
+    Shader shaderFromOption1 = shaderManager.ProduceShader(option1);
+    Shader shaderFromOption2 = shaderManager.ProduceShader(option2);
+    DALI_TEST_EQUAL(option1.GetOptionHash(), option2.GetOptionHash());
+    DALI_TEST_EQUAL(shaderFromOption1, shaderFromOption2);
 
     RendererState::Type rendererStateFromMaterialDefinition = shaderManager.GetRendererState(materialDefinition);
     DALI_TEST_EQUAL(rendererStateFromMaterialDefinition, rendererState);
@@ -306,7 +320,7 @@ int UtcDaliShaderManagerAddAndRemoveLights(void)
 
   DALI_TEST_EQUALS(shader1.GetProperty<int>(shader1.GetPropertyIndex("uLightCount")), 1, TEST_LOCATION);
   DALI_TEST_EQUALS(shader2.GetProperty<int>(shader2.GetPropertyIndex("uLightCount")), 1, TEST_LOCATION);
-  
+
   ShaderOption option3;
   option3.AddOption(ShaderOption::Type::METALLIC_ROUGHNESS_TEXTURE);
   Dali::Shader shader3 = shaderManager.ProduceShader(option3);
index 3e9a117..f9df7d8 100644 (file)
@@ -62,6 +62,9 @@ const char* TEST_IMAGE_3_FILE_NAME = TEST_RESOURCE_DIR "/icon-edit.png";
 const char* TEST_IMAGE_4_FILE_NAME = TEST_RESOURCE_DIR "/application-icon-20.png";
 const char* TEST_MASK_FILE_NAME    = TEST_RESOURCE_DIR "/mask.png";
 
+const char* TEST_SVG_FILE_NAME                   = TEST_RESOURCE_DIR "/svg1.svg";
+const char* TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/insta_camera.json";
+
 class TestObserver : public Dali::Toolkit::TextureUploadObserver
 {
 public:
@@ -258,7 +261,7 @@ int UtcTextureManagerEncodedImageBuffer(void)
 
   std::string url1 = textureManager.AddEncodedImageBuffer(buffer1);
   std::string url2 = textureManager.AddEncodedImageBuffer(buffer1);
-  std::string url3 = VisualUrl::CreateBufferUrl(""); ///< Impossible Buffer URL. for coverage
+  std::string url3 = VisualUrl::CreateBufferUrl("", ""); ///< Impossible Buffer URL. for coverage
 
   // Check if same EncodedImageBuffer get same url
   DALI_TEST_CHECK(url1 == url2);
@@ -437,6 +440,72 @@ int UtcTextureManagerEncodedImageBuffer(void)
   END_TEST;
 }
 
+int UtcTextureManagerEncodedImageBufferWithImageType(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcTextureManagerEncodedImageBufferWithImageType");
+
+  auto  visualFactory  = Toolkit::VisualFactory::Get();
+  auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); // Use VisualFactory's texture manager
+
+  // Get encoded raw-buffer image and generate url
+  EncodedImageBuffer buffer1 = ConvertFileToEncodedImageBuffer(TEST_SVG_FILE_NAME);
+  EncodedImageBuffer buffer2 = ConvertFileToEncodedImageBuffer(TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME);
+
+  std::string url1 = textureManager.AddEncodedImageBuffer(buffer1);
+  std::string url2 = textureManager.AddEncodedImageBuffer(buffer1);
+
+  // Check if same EncodedImageBuffer get same url
+  DALI_TEST_CHECK(url1 == url2);
+  // Reduce reference count
+  textureManager.RemoveEncodedImageBuffer(url1);
+  // Check whethere url1 still valid
+  DALI_TEST_CHECK(textureManager.GetEncodedImageBuffer(url1));
+
+  url2 = textureManager.AddEncodedImageBuffer(buffer2);
+  // Check if difference EncodedImageBuffer get difference url
+  DALI_TEST_CHECK(url1 != url2);
+
+  buffer1.SetImageType(EncodedImageBuffer::ImageType::VECTOR_IMAGE);
+  buffer2.SetImageType(EncodedImageBuffer::ImageType::ANIMATED_VECTOR_IMAGE);
+
+  std::string url1AfterType = textureManager.AddEncodedImageBuffer(buffer1);
+  std::string url2AfterType = textureManager.AddEncodedImageBuffer(buffer2);
+
+  // Check if EncodedImageBuffer with imagetype get difference url.
+  DALI_TEST_CHECK(url1 != url1AfterType);
+  DALI_TEST_CHECK(url2 != url2AfterType);
+  DALI_TEST_CHECK(url1AfterType != url2AfterType);
+
+  int  bufferId      = std::atoi(VisualUrl::GetLocationWithoutExtension(url1AfterType).c_str());
+  auto urlFromBuffer = textureManager.GetVisualUrl(bufferId);
+
+  // Check url from buffer id is equal with what we know.
+  DALI_TEST_CHECK(url1AfterType == urlFromBuffer.GetUrl());
+
+  // Reduce reference count
+  textureManager.RemoveEncodedImageBuffer(url1AfterType);
+  // Check whethere url1 still valid
+  DALI_TEST_CHECK(textureManager.GetEncodedImageBuffer(url1AfterType));
+
+  // Reduce reference count
+  textureManager.RemoveEncodedImageBuffer(url1AfterType);
+  // Check whethere url1 is invalid
+  DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url1AfterType));
+
+  // Reduce reference count
+  textureManager.RemoveEncodedImageBuffer(url2);
+  // Check whethere url2 is still valid
+  DALI_TEST_CHECK(textureManager.GetEncodedImageBuffer(url2));
+
+  // Reduce reference count
+  textureManager.RemoveEncodedImageBuffer(url2);
+  // Check whethere url2 is invalid
+  DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url2));
+
+  END_TEST;
+}
+
 int UtcTextureManagerExternalTexture(void)
 {
   ToolkitTestApplication application;
index 3003ae4..3e5e8eb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -418,6 +418,19 @@ int UtcDaliVisualUrlGetLocationP(void)
   END_TEST;
 }
 
+int UtcDaliVisualUrlGetLocationWithoutExtensionP(void)
+{
+  tet_infoline("UtcDaliVisualUrl GetLocationWithoutExtension Positive");
+
+  DALI_TEST_EQUAL("a", VisualUrl("http://a.png").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("1", VisualUrl("dali://1.jpg").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("4", VisualUrl("enbuf://4.svg").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("", VisualUrl("ftp://.png").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("http://a.jpg", VisualUrl("http://http://a.jpg.jpg").GetLocationWithoutExtension());
+
+  END_TEST;
+}
+
 int UtcDaliVisualUrlGetLocationN(void)
 {
   tet_infoline("UtcDaliVisualUrl GetLocation Negative");
@@ -432,6 +445,20 @@ int UtcDaliVisualUrlGetLocationN(void)
   END_TEST;
 }
 
+int UtcDaliVisualUrlGetLocationWithoutExtensionN(void)
+{
+  tet_infoline("UtcDaliVisualUrl GetLocationWithoutExtension Negative");
+
+  DALI_TEST_EQUAL("", VisualUrl("").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("a", VisualUrl("a").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("dali:/1.jpg", VisualUrl("dali:/1.jpg").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("dali//1.jpg", VisualUrl("dali//1.jpg").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("enbuf:/2.png", VisualUrl("enbuf:/2.png").GetLocationWithoutExtension());
+  DALI_TEST_EQUAL("a.jpg", VisualUrl("http:/http://a.jpg.jpngif").GetLocationWithoutExtension());
+
+  END_TEST;
+}
+
 int UtcDaliVisualUrlCreateTextureUrl(void)
 {
   tet_infoline("UtcDaliVisualUrl CreateTextureUrl");
@@ -447,9 +474,20 @@ int UtcDaliVisualUrlCreateBufferUrl(void)
 {
   tet_infoline("UtcDaliVisualUrl CreateBufferUrl");
 
-  DALI_TEST_EQUAL("enbuf://a", VisualUrl::CreateBufferUrl("a"));
-  DALI_TEST_EQUAL("enbuf://1234", VisualUrl::CreateBufferUrl("1234"));
-  DALI_TEST_EQUAL("enbuf://", VisualUrl::CreateBufferUrl(""));
+  DALI_TEST_EQUAL("enbuf://a", VisualUrl::CreateBufferUrl("a", ""));
+  DALI_TEST_EQUAL("enbuf://1234", VisualUrl::CreateBufferUrl("1234", ""));
+  DALI_TEST_EQUAL("enbuf://", VisualUrl::CreateBufferUrl("", ""));
+
+  END_TEST;
+}
+
+int UtcDaliVisualUrlCreateBufferUrlWithExtension(void)
+{
+  tet_infoline("UtcDaliVisualUrl CreateBufferUrl with extension");
+
+  DALI_TEST_EQUAL("enbuf://a.jpg", VisualUrl::CreateBufferUrl("a", ".jpg"));
+  DALI_TEST_EQUAL("enbuf://1234567", VisualUrl::CreateBufferUrl("1234", "567"));
+  DALI_TEST_EQUAL("enbuf://b", VisualUrl::CreateBufferUrl("", "b"));
 
   END_TEST;
 }
index ef389ed..600a866 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 namespace Dali
 {
-EncodedImageBuffer ConvertFileToEncodedImageBuffer(const char* url)
+EncodedImageBuffer ConvertFileToEncodedImageBuffer(const char* url, EncodedImageBuffer::ImageType imageType)
 {
   EncodedImageBuffer buffer;
-  FILE *fp;
+  FILE*              fp;
   fp = fopen(url, "rb");
   if(fp != NULL)
   {
     fseek(fp, 0, SEEK_END);
-    size_t size = ftell(fp);
+    size_t                size = ftell(fp);
     Dali::Vector<uint8_t> data;
     data.Resize(size);
     fseek(fp, 0, SEEK_SET);
     size_t realSize = fread(data.Begin(), sizeof(uint8_t), size, fp);
     fclose(fp);
     data.Resize(realSize);
-    buffer = EncodedImageBuffer::New(data);
+    buffer = EncodedImageBuffer::New(data, imageType);
   }
   return buffer;
 }
index 96fbe46..3b6e28b 100644 (file)
@@ -2,7 +2,7 @@
 #define TEST_ENCODED_IMAGE_BUFFER_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,9 +23,8 @@
 
 namespace Dali
 {
-
 // util function to convert local file to EncodedImageBuffer
-EncodedImageBuffer ConvertFileToEncodedImageBuffer(const char* url);
+EncodedImageBuffer ConvertFileToEncodedImageBuffer(const char* url, EncodedImageBuffer::ImageType imageType = EncodedImageBuffer::ImageType::DEFAULT);
 
 } // namespace Dali
 
index 527b9f4..24bb51e 100644 (file)
@@ -1289,7 +1289,7 @@ Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(con
   }
 
   mProgramCache.emplace_back();
-  mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms, mCustomUniformBlocks);
+  mProgramCache.back().programImpl = new TestGraphicsProgramImpl(*this, mGl, programCreateInfo, mVertexFormats, mCustomUniforms, mCustomUniformBlocks);
 
   for(auto& shader : *(programCreateInfo.shaderState))
   {
index 9540c3e..e14499b 100644 (file)
@@ -424,6 +424,15 @@ public: // ResourceId relative API.
   Graphics::UniquePtr<Graphics::Texture> ReleaseTextureFromResourceId(uint32_t resourceId) override;
 
 public: // Test Functions
+  void SetAutoAttrCreation(bool v)
+  {
+    mAutoAttrCreation = v;
+  }
+  bool AutoAttrCreation()
+  {
+    return mAutoAttrCreation;
+  }
+
   void SetVertexFormats(Property::Array& vfs)
   {
     mVertexFormats = vfs;
@@ -458,9 +467,9 @@ public:
   TestGraphicsSyncImplementation mGraphicsSyncImpl;
   TestGlContextHelperAbstraction mGlContextHelperAbstraction;
 
-  bool isDiscardQueueEmptyResult{true};
-  bool isDrawOnResumeRequiredResult{true};
-
+  bool            isDiscardQueueEmptyResult{true};
+  bool            isDrawOnResumeRequiredResult{true};
+  bool            mAutoAttrCreation{true};
   Property::Array mVertexFormats;
 
   struct ProgramCache
index 2706305..3c8418e 100644 (file)
 
 namespace Dali
 {
-TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
-: mGl(gl),
+TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGraphicsController& controller, TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
+: mController(controller),
+  mGl(gl),
   mId(gl.CreateProgram()),
   mCreateInfo(createInfo),
-  mReflection(gl, mId, vertexFormats, createInfo, customUniforms, customUniformBlocks)
+  mReflection(controller, gl, mId, vertexFormats, createInfo, customUniforms, customUniformBlocks)
 {
   // Ensure active sampler uniforms are set
   mGl.SetCustomUniforms(customUniforms);
index 8641800..b81185a 100644 (file)
 
 namespace Dali
 {
+class TestGraphicsController;
+
 class TestGraphicsProgramImpl
 {
 public:
-  TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks);
+  TestGraphicsProgramImpl(TestGraphicsController& controller, TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks);
 
   // For API
   const TestGraphicsReflection& GetReflection() const
@@ -45,6 +47,7 @@ public:
   bool GetParameter(uint32_t parameterId, void* outData);
 
 public:
+  TestGraphicsController&     mController;
   TestGlAbstraction&          mGl;
   uint32_t                    mId;
   Graphics::ProgramCreateInfo mCreateInfo;
index 2822c0a..7b3e6c8 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "test-graphics-reflection.h"
+#include "test-graphics-controller.h"
 #include "test-graphics-shader.h"
 
 #include <dali/public-api/object/property-map.h>
@@ -110,8 +111,9 @@ constexpr int GetSizeForType(Property::Type type)
 
 } // namespace
 
-TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
-: mGl(gl),
+TestGraphicsReflection::TestGraphicsReflection(TestGraphicsController& controller, TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
+: mController(controller),
+  mGl(gl),
   mCustomUniforms(customUniforms)
 {
   for(Property::Array::SizeType i = 0; i < vfs.Count(); ++i)
@@ -241,19 +243,19 @@ TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t p
 
 uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& name) const
 {
-  // Automatically assign locations to named attributes when requested
   auto iter = std::find(mAttributes.begin(), mAttributes.end(), name);
   if(iter != mAttributes.end())
   {
     return iter - mAttributes.begin();
   }
-  else
+  else if(mController.AutoAttrCreation())
   {
     uint32_t location = mAttributes.size();
     mAttributes.push_back(name);
     return location;
   }
-  return 0u;
+
+  return -1;
 }
 
 Dali::Graphics::VertexInputAttributeFormat TestGraphicsReflection::GetVertexAttributeFormat(uint32_t location) const
index e701e17..b598435 100644 (file)
 
 namespace Dali
 {
+class TestGraphicsController;
+
 class TestGraphicsReflection : public Graphics::Reflection
 {
 public:
   class TestUniformBlockInfo;
 
-  TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestUniformBlockInfo>& customUniformBlocks);
+  TestGraphicsReflection(TestGraphicsController& controller, TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestUniformBlockInfo>& customUniformBlocks);
 
   uint32_t                                        GetVertexAttributeLocation(const std::string& name) const override;
   Dali::Graphics::VertexInputAttributeFormat      GetVertexAttributeFormat(uint32_t location) const override;
@@ -84,6 +86,7 @@ public: // Test methods
     return mUniformBlocks[index];
   }
 
+  TestGraphicsController&          mController;
   TestGlAbstraction&               mGl;
   mutable std::vector<std::string> mAttributes;
   std::vector<UniformData>         mCustomUniforms;
index bb45bda..4aec20d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 #include <dali/devel-api/adaptor-framework/vector-animation-renderer.h>
 #include <dali/devel-api/threading/mutex.h>
 #include <dali/public-api/object/base-object.h>
+#include <dali/public-api/object/property-array.h>
 #include <toolkit-application.h>
 #include <toolkit-event-thread-callback.h>
 #include <toolkit-vector-animation-renderer.h>
@@ -374,6 +375,12 @@ bool VectorAnimationRenderer::GetMarkerInfo(const std::string& marker, uint32_t&
   return Internal::Adaptor::GetImplementation(*this).GetMarkerInfo(marker, startFrame, endFrame);
 }
 
+void VectorAnimationRenderer::GetMarkerInfo(Property::Map& map) const
+{
+  map.Add(VECTOR_ANIMATION_MARKER_NAME_1, Property::Array({VECTOR_ANIMATION_MARKER_START_FRAME_1, VECTOR_ANIMATION_MARKER_END_FRAME_1}));
+  map.Add(VECTOR_ANIMATION_MARKER_NAME_2, Property::Array({VECTOR_ANIMATION_MARKER_START_FRAME_2, VECTOR_ANIMATION_MARKER_END_FRAME_2}));
+}
+
 void VectorAnimationRenderer::InvalidateBuffer()
 {
   return Internal::Adaptor::GetImplementation(*this).InvalidateBuffer();
index 732d6b1..907b8af 100644 (file)
@@ -386,6 +386,9 @@ int UtcDaliAnimatedVectorImageVisualGetPropertyMap01(void)
   value = resultMap.Find(DevelImageVisual::Property::CONTENT_INFO, Property::MAP);
   DALI_TEST_CHECK(value);
 
+  value = resultMap.Find(DevelImageVisual::Property::MARKER_INFO, Property::MAP);
+  DALI_TEST_CHECK(value);
+
   value = resultMap.Find(DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, Property::BOOLEAN);
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<bool>() == true); // Check default value
@@ -967,6 +970,85 @@ int UtcDaliAnimatedVectorImageVisualPlayRangeMarker(void)
   END_TEST;
 }
 
+int UtcDaliAnimatedVectorImageVisualMarkerInfo(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliAnimatedVectorImageVisualMarkerInfo");
+
+  Property::Map propertyMap;
+  propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
+    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
+    .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
+
+  Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New(true);
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+  Vector2 controlSize(20.f, 30.f);
+  actor.SetProperty(Actor::Property::SIZE, controlSize);
+
+  application.GetScene().Add(actor);
+
+  Property::Map attributes;
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  // renderer is added to actor
+  DALI_TEST_CHECK(actor.GetRendererCount() == 1u);
+  Renderer renderer = actor.GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  Property::Value* value = map.Find(DevelImageVisual::Property::MARKER_INFO);
+
+  DALI_TEST_CHECK(value);
+
+  Property::Map* result = value->GetMap();
+  DALI_TEST_CHECK(result);
+
+  std::string resultMarkerName;
+  int         resultStartFrame, resultEndFrame;
+  DALI_TEST_EQUALS(2u, result->Count(), TEST_LOCATION);
+
+  for(uint32_t i = 0u; i < result->Count(); ++i)
+  {
+    if(result->GetKeyAt(i).stringKey == VECTOR_ANIMATION_MARKER_NAME_1)
+    {
+      Property::Array* frameArray = result->GetValue(i).GetArray();
+      DALI_TEST_CHECK(frameArray);
+      frameArray->GetElementAt(0).Get(resultStartFrame);
+      frameArray->GetElementAt(1).Get(resultEndFrame);
+
+      DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_START_FRAME_1, resultStartFrame, TEST_LOCATION);
+      DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_END_FRAME_1, resultEndFrame, TEST_LOCATION);
+    }
+    else if(result->GetKeyAt(i).stringKey == VECTOR_ANIMATION_MARKER_NAME_2)
+    {
+      Property::Array* frameArray = result->GetValue(i).GetArray();
+      DALI_TEST_CHECK(frameArray);
+      frameArray->GetElementAt(0).Get(resultStartFrame);
+      frameArray->GetElementAt(1).Get(resultEndFrame);
+
+      DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_START_FRAME_2, resultStartFrame, TEST_LOCATION);
+      DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_END_FRAME_2, resultEndFrame, TEST_LOCATION);
+    }
+    else
+    {
+      DALI_TEST_CHECK(false);
+    }
+  }
+
+  END_TEST;
+}
+
 int UtcDaliAnimatedVectorImageVisualAnimationFinishedSignal(void)
 {
   ToolkitTestApplication application;
index 2f2901f..5ca6052 100644 (file)
@@ -879,6 +879,86 @@ int UtcDaliImageViewSyncLoadingEncodedBuffer(void)
   END_TEST;
 }
 
+int UtcDaliImageViewEncodedBufferWithSvg(void)
+{
+  ToolkitTestApplication     application;
+  TestGlAbstraction&         gl          = application.GetGlAbstraction();
+  const std::vector<GLuint>& textures    = gl.GetBoundTextures();
+  size_t                     numTextures = textures.size();
+
+  // Get encoded raw-buffer svg image and generate url
+  EncodedImageBuffer buffer = ConvertFileToEncodedImageBuffer(TEST_SVG_FILE_NAME, EncodedImageBuffer::ImageType::VECTOR_IMAGE);
+  ImageUrl           url    = Toolkit::Image::GenerateUrl(buffer);
+
+  // Async loading, no atlasing for big size image
+  ImageView imageView = ImageView::New(url.GetUrl());
+
+  // By default, Aysnc loading is used
+  application.GetScene().Add(imageView);
+  imageView.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+  imageView.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Load svg image + rasterize.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+  application.SendNotification();
+
+  const std::vector<GLuint>& textures2 = gl.GetBoundTextures();
+  DALI_TEST_GREATER(textures2.size(), numTextures, TEST_LOCATION);
+
+  // Remove visual, for line coverage.
+  imageView.Unparent();
+  application.SendNotification();
+  application.Render(16);
+
+  END_TEST;
+}
+
+int UtcDaliImageViewEncodedBufferWithAnimatedVectorImage(void)
+{
+  ToolkitTestApplication     application;
+  TestGlAbstraction&         gl          = application.GetGlAbstraction();
+  const std::vector<GLuint>& textures    = gl.GetBoundTextures();
+  size_t                     numTextures = textures.size();
+
+  // Get encoded raw-buffer lottie image and generate url
+  EncodedImageBuffer buffer = ConvertFileToEncodedImageBuffer(TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME, EncodedImageBuffer::ImageType::ANIMATED_VECTOR_IMAGE);
+  ImageUrl           url    = Toolkit::Image::GenerateUrl(buffer);
+
+  // Async loading, no atlasing for big size image
+  ImageView imageView = ImageView::New(url.GetUrl());
+
+  // By default, Aysnc loading is used
+  application.GetScene().Add(imageView);
+  imageView.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+  imageView.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Load lottie image is sync. Only wait rasterize.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+  application.SendNotification();
+
+  const std::vector<GLuint>& textures2 = gl.GetBoundTextures();
+  DALI_TEST_GREATER(textures2.size(), numTextures, TEST_LOCATION);
+
+  // Remove visual, for line coverage.
+  imageView.Unparent();
+  application.SendNotification();
+  application.Render(16);
+
+  END_TEST;
+}
+
 int UtcDaliImageViewAddedTexture(void)
 {
   ToolkitTestApplication application;
@@ -2789,6 +2869,34 @@ int UtcDaliImageViewLoadRemoteSVG(void)
   END_TEST;
 }
 
+int UtcDaliImageViewLoadRemoteLottie(void)
+{
+  tet_infoline("Test load from a remote server. (Note we don't support real download now. Just for line coverage)");
+
+  ToolkitTestApplication application;
+
+  {
+    Toolkit::ImageView imageView;
+    imageView = Toolkit::ImageView::New();
+    imageView.SetImage("https://lottie.json");
+    imageView.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+    imageView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+    imageView.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
+    imageView.SetProperty(Actor::Property::POSITION, Vector3(150.0f, 150.0f, 0.0f));
+
+    application.GetScene().Add(imageView);
+
+    DALI_TEST_CHECK(imageView);
+
+    application.SendNotification();
+    application.Render();
+
+    // Do not check anything for here.
+  }
+
+  END_TEST;
+}
+
 int UtcDaliImageViewSyncSVGLoading(void)
 {
   ToolkitTestApplication application;
@@ -3773,6 +3881,34 @@ void OnResourceReadySignal10(Control control)
   }
 }
 
+void OnResourceReadySignal11(Control control)
+{
+  gResourceReadySignalCounter++;
+
+  if(!gImageView2)
+  {
+    auto scene = gImageView1.GetParent();
+
+    // Try to load animated image visual here which is already cached, and then ignore forcely.
+
+    Property::Map map1;
+    map1[Toolkit::ImageVisual::Property::URL] = TEST_GIF_FILE_NAME;
+
+    gImageView2 = ImageView::New();
+    gImageView2.SetProperty(Toolkit::ImageView::Property::IMAGE, map1);
+
+    gImageView3 = ImageView::New();
+    gImageView3.SetProperty(Toolkit::ImageView::Property::IMAGE, map1);
+
+    scene.Add(gImageView2);
+    gImageView2.Unparent();
+
+    scene.Add(gImageView3);
+    gImageView3.Unparent();
+    gImageView3.Reset(); // Destroy visual
+  }
+}
+
 } // namespace
 
 int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
@@ -4617,6 +4753,82 @@ int UtcDaliImageViewSetImageOnResourceReadySignal10(void)
   END_TEST;
 }
 
+int UtcDaliImageViewSetImageOnResourceReadySignal11(void)
+{
+  tet_infoline("Test ResourceReady Add AnimatedImageVisual and then Remove immediately.");
+
+  ToolkitTestApplication application;
+
+  gResourceReadySignalCounter = 0;
+
+  // Clear image view for clear test
+
+  if(gImageView1)
+  {
+    gImageView1.Reset();
+  }
+  if(gImageView2)
+  {
+    gImageView2.Reset();
+  }
+  if(gImageView3)
+  {
+    gImageView3.Reset();
+  }
+
+  try
+  {
+    gImageView1 = ImageView::New();
+    gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, TEST_GIF_FILE_NAME);
+    gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal11);
+    application.GetScene().Add(gImageView1); // It will call resourceReady signal 1 time.
+
+    tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 0, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    // Load gImageView1
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+    tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION);
+
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    // Exception should not happened
+    DALI_TEST_CHECK(false);
+  }
+
+  // Clear cache.
+  application.SendNotification();
+  application.Render();
+
+  gResourceReadySignalCounter = 0;
+
+  // Clear image view for clear test
+
+  if(gImageView1)
+  {
+    gImageView1.Reset();
+  }
+  if(gImageView2)
+  {
+    gImageView2.Reset();
+  }
+  if(gImageView3)
+  {
+    gImageView3.Reset();
+  }
+
+  END_TEST;
+}
+
 int UtcDaliImageViewUseSameUrlWithAnimatedImageVisual(void)
 {
   tet_infoline("Test multiple views with same image in animated image visual");
index a63addb..7ea0654 100644 (file)
@@ -6,6 +6,10 @@
 
 #define MORPH defined(MORPH_POSITION) || defined(MORPH_NORMAL) || defined(MORPH_TANGENT)
 
+// If needed, define these strings in code, insert after each.
+#define ADD_EXTRA_SKINNING_ATTRIBUTES
+#define ADD_EXTRA_WEIGHTS
+
 #ifdef HIGHP
   precision highp float;
 #else
@@ -24,6 +28,12 @@ in vec3 aTangent;
 
 in vec4 aVertexColor;
 
+#ifdef SKINNING
+in vec4 aJoints0;
+in vec4 aWeights0;
+ADD_EXTRA_SKINNING_ATTRIBUTES;
+#endif
+
 #ifdef MORPH
   uniform highp sampler2D sBlendShapeGeometry;
 #endif
@@ -39,11 +49,14 @@ uniform mat4 uModelMatrix;
 uniform mat4 uProjection;
 
 #ifdef SKINNING
-  in vec4 aJoints;
-  in vec4 aWeights;
-  #define MAX_BONES 80
-  uniform mat4 uBone[MAX_BONES];
-  uniform mediump vec3 uYDirection;
+
+#define MAX_BONES 256
+layout(std140) uniform Bones
+{
+  mat4 uBone[MAX_BONES];
+};
+
+uniform mediump vec3 uYDirection;
 #endif
 
 #ifdef MORPH
@@ -142,11 +155,18 @@ void main()
 
 #endif
 
+
+
+
 #ifdef SKINNING
-  highp mat4 bone = uBone[int(aJoints.x)] * aWeights.x +
-    uBone[int(aJoints.y)] * aWeights.y +
-    uBone[int(aJoints.z)] * aWeights.z +
-    uBone[int(aJoints.w)] * aWeights.w;
+  highp mat4 bone =
+    uBone[int(aJoints0.x)] * aWeights0.x +
+    uBone[int(aJoints0.y)] * aWeights0.y +
+    uBone[int(aJoints0.z)] * aWeights0.z +
+    uBone[int(aJoints0.w)] * aWeights0.w;
+
+  ADD_EXTRA_WEIGHTS;
+
   position = bone * position;
   normal = uYDirection * (bone * vec4(normal, 0.0)).xyz;
   tangent = uYDirection * (bone * vec4(tangent, 0.0)).xyz;
@@ -155,6 +175,7 @@ void main()
 #else
   highp vec4 positionW = uModelMatrix * position;
 #endif
+
   highp vec4 positionV = uViewMatrix * positionW;
 
   vPositionToCamera = transpose(mat3(uViewMatrix)) * -vec3(positionV.xyz / positionV.w);
index 73bb37c..945198b 100644 (file)
@@ -1,11 +1,20 @@
 #version 300 es
 
+#define ADD_EXTRA_SKINNING_ATTRIBUTES
+#define ADD_EXTRA_WEIGHTS
+
 precision highp float;
 
 in vec3 aPosition;
 in vec2 aTexCoord;
 in vec4 aVertexColor;
 
+#ifdef SKINNING
+in vec4 aJoints0;
+in vec4 aWeights0;
+ADD_EXTRA_SKINNING_ATTRIBUTES;
+#endif
+
 out mediump vec2 vUV;
 out lowp vec4 vColor;
 
@@ -14,11 +23,12 @@ uniform highp mat4 uModelMatrix;
 uniform highp mat4 uProjection;
 
 #ifdef SKINNING
-  in vec4 aJoints;
-  in vec4 aWeights;
-  #define MAX_BONES 64
-  uniform mat4 uBone[MAX_BONES];
-  uniform mediump vec3 uYDirection;
+#define MAX_BONES 256
+layout(std140) uniform Bones
+{
+  mat4 uBone[MAX_BONES];
+};
+uniform mediump vec3 uYDirection;
 #endif
 
 #ifdef MORPH
@@ -86,12 +96,14 @@ void main()
 #endif
 
 #ifdef SKINNING
-  highp mat4 bone = uBone[int(aJoints.x)] * aWeights.x +
-    uBone[int(aJoints.y)] * aWeights.y +
-    uBone[int(aJoints.z)] * aWeights.z +
-    uBone[int(aJoints.w)] * aWeights.w;
-  position = bone * position;
+  highp mat4 bone = uBone[int(aJoints0.x)] * aWeights0.x +
+    uBone[int(aJoints0.y)] * aWeights0.y +
+    uBone[int(aJoints0.z)] * aWeights0.z +
+    uBone[int(aJoints0.w)] * aWeights0.w;
 
+  ADD_EXTRA_WEIGHTS;
+
+  position = bone * position;
   highp vec4 positionW = position;
 #else
   highp vec4 positionW = uModelMatrix * position;
index 668a10c..1ed195d 100644 (file)
@@ -667,8 +667,7 @@ void DliLoaderImpl::Impl::ParseShaders(const TreeNode* shaders, Dali::Scene3D::L
     auto&            node = (*i0).second;
     ShaderDefinition shaderDef;
     ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
-    auto sssIter = std::find_if(shaderDef.mDefines.begin(), shaderDef.mDefines.end(), [](std::string& item)
-                                { return (item == "SSS"); });
+    auto sssIter = std::find_if(shaderDef.mDefines.begin(), shaderDef.mDefines.end(), [](std::string& item) { return (item == "SSS"); });
     if(sssIter != shaderDef.mDefines.end())
     {
       shaderDef.mDefines.erase(sssIter);
@@ -843,10 +842,13 @@ void DliLoaderImpl::Impl::ParseMeshes(const TreeNode* meshes, Dali::Scene3D::Loa
         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "normals"));
       }
 
-      if(MaskMatch(attributes, MeshDefinition::TEX_COORDS) &&
-         !ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords))
+      if(MaskMatch(attributes, MeshDefinition::TEX_COORDS))
       {
-        mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
+        meshDef.mTexCoords.emplace_back(MeshDefinition::Accessor{});
+        if(!ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords[0]))
+        {
+          mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
+        }
       }
 
       if(MaskMatch(attributes, MeshDefinition::TANGENTS) &&
@@ -864,11 +866,16 @@ void DliLoaderImpl::Impl::ParseMeshes(const TreeNode* meshes, Dali::Scene3D::Loa
           mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
                                 resources.mMeshes.size()));
         }
-        else if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints0) ||
-                !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights0))
+        else
         {
-          mOnError(FormatString("mesh %d: Failed to read skinning information.",
-                                resources.mMeshes.size()));
+          meshDef.mJoints.emplace_back(MeshDefinition::Accessor{});
+          meshDef.mWeights.emplace_back(MeshDefinition::Accessor{});
+          if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints[0]) ||
+             !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights[0]))
+          {
+            mOnError(FormatString("mesh %d: Failed to read skinning information.",
+                                  resources.mMeshes.size()));
+          }
         }
       }
 
@@ -1033,17 +1040,17 @@ void DliLoaderImpl::Impl::ParseMaterials(const TreeNode* materials, DliInputPara
       materialDef.mFlags |= semantic;
     }
 
-/// @TODO : Some dli shader don't implement this subsurfaceMp usage.
-///         To make visual test pass, Skip subsurfaceMap texture using
-///         until dli shaders are support it.
-//    if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
-//    {
-//      ToUnixFileSeparators(texturePath);
-//
-//      const auto semantic = MaterialDefinition::SUBSURFACE;
-//      materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
-//      materialDef.mFlags |= semantic;
-//    }
+    /// @TODO : Some dli shader don't implement this subsurfaceMp usage.
+    ///         To make visual test pass, Skip subsurfaceMap texture using
+    ///         until dli shaders are support it.
+    //    if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
+    //    {
+    //      ToUnixFileSeparators(texturePath);
+    //
+    //      const auto semantic = MaterialDefinition::SUBSURFACE;
+    //      materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
+    //      materialDef.mFlags |= semantic;
+    //    }
 
     if(ReadString(node.GetChild("occlusionMap"), texturePath))
     {
@@ -1447,10 +1454,10 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
       iAnim != iAnimEnd;
       ++iAnim)
   {
-    const TreeNode&     tnAnim = (*iAnim).second;
-    uint32_t animationPropertyIndex = 0;
+    const TreeNode&     tnAnim                 = (*iAnim).second;
+    uint32_t            animationPropertyIndex = 0;
     AnimationDefinition animDef;
-    std::string animationName;
+    std::string         animationName;
     ReadString(tnAnim.GetChild(NAME), animationName);
     animDef.SetName(animationName);
 
index a6ac398..bbc092e 100644 (file)
@@ -63,21 +63,6 @@ const std::map<std::string_view, AlphaMode::Type>& GetAlphaModeTypes()
   return ALPHA_MODE_TYPES;
 }
 
-const std::map<std::string_view, Attribute::Type>& GetAttributeTypes()
-{
-  static const std::map<std::string_view, Attribute::Type> ATTRIBUTE_TYPES{
-    ENUM_STRING_MAPPING(Attribute::Type, POSITION),
-    ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
-    ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
-    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_0),
-    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_1),
-    ENUM_STRING_MAPPING(Attribute::Type, COLOR_0),
-    ENUM_STRING_MAPPING(Attribute::Type, JOINTS_0),
-    ENUM_STRING_MAPPING(Attribute::Type, WEIGHTS_0),
-  };
-  return ATTRIBUTE_TYPES;
-}
-
 const std::map<std::string_view, Animation::Sampler::Interpolation::Type>& GetAnimationSamplerInterpolation()
 {
   static const std::map<std::string_view, Animation::Sampler::Interpolation::Type> ANIMATION_SAMPLER_INTERPOLATION{
@@ -103,10 +88,70 @@ const std::map<std::string_view, Animation::Channel::Target::Type>& GetAnimation
 
 ENUM_TYPE_FROM_STRING(AccessorType, GetAccessorTypes())
 ENUM_TYPE_FROM_STRING(AlphaMode, GetAlphaModeTypes())
-ENUM_TYPE_FROM_STRING(Attribute, GetAttributeTypes())
 ENUM_TYPE_FROM_STRING(Animation::Sampler::Interpolation, GetAnimationSamplerInterpolation())
 ENUM_TYPE_FROM_STRING(Animation::Channel::Target, GetAnimationChannelTargetPathTypes())
 
+const std::map<std::string_view, Attribute::Type>& GetTargetTypes()
+{
+  static const std::map<std::string_view, Attribute::Type> TARGET_TYPES{
+    ENUM_STRING_MAPPING(Attribute::Type, POSITION),
+    ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
+    ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
+  };
+  return TARGET_TYPES;
+}
+
+const std::map<Attribute::Type, const char*>& GetAttributeSetTypes()
+{
+  static const std::map<Attribute::Type, const char*> ATTRIBUTE_SET_TYPES{
+    {Attribute::Type::TEXCOORD_N, "TEXCOORD_%u"},
+    {Attribute::Type::COLOR_N, "COLOR_%u"},
+    {Attribute::Type::JOINTS_N, "JOINTS_%u"},
+    {Attribute::Type::WEIGHTS_N, "WEIGHTS_%u"},
+  };
+  return ATTRIBUTE_SET_TYPES;
+}
+
+uint32_t Attribute::HashFromString(const char* token, size_t length)
+{
+  auto& table1 = GetTargetTypes();
+  auto& table2 = GetAttributeSetTypes();
+
+  std::string target(token, length);
+  std::transform(target.begin(), target.end(), target.begin(), ::toupper);
+
+  auto iFind = table1.find(std::string_view(target.c_str(), length));
+  if(iFind != table1.end())
+  {
+    return Attribute::ToHash(iFind->second, false, 0);
+  }
+
+  uint32_t hash = Attribute::ToHash(Attribute::INVALID, false, 0);
+  for(const auto& [key, match] : table2)
+  {
+    int setIndex;
+    if(sscanf(target.c_str(), match, &setIndex) > 0)
+    {
+      hash = Attribute::ToHash(key, true, setIndex);
+      break;
+    }
+  }
+  return hash;
+}
+
+Attribute::Type Attribute::TargetFromString(const char* token, size_t length)
+{
+  std::string target(token, length);
+  std::transform(target.begin(), target.end(), target.begin(), ::toupper);
+
+  auto iFind = GetTargetTypes().find(std::string_view(target.c_str(), length));
+  if(iFind != GetTargetTypes().end())
+  {
+    return iFind->second;
+  }
+  return Attribute::INVALID;
+}
+
 bool Component::IsUnsigned(Type t)
 {
   return t == UNSIGNED_BYTE || t == UNSIGNED_SHORT || t == UNSIGNED_INT;
index 7d2c332..e90cf38 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) \
   {                               \
@@ -179,15 +180,57 @@ struct Attribute
     POSITION,
     NORMAL,
     TANGENT,
-    TEXCOORD_0,
-    TEXCOORD_1,
-    COLOR_0,
-    JOINTS_0,
-    WEIGHTS_0,
+    TEXCOORD_N,
+    COLOR_N,
+    JOINTS_N,
+    WEIGHTS_N,
     INVALID
   };
 
-  static Type FromString(const char* s, size_t len);
+  using HashType = uint32_t;
+
+  // Hash bit layout
+  // +--+--+--+--+--+--+--+
+  // |31|30|29|28|27|..| 0| bit index
+  // +--+--+--+--+--+--+--+
+  //  \_/ - Set is used
+  //     \______/ - Type enum
+  //              \_______/ - Set ID
+  static const HashType SET_SHIFT{31};
+  static const HashType TYPE_SHIFT{28};
+  static const HashType SET_MASK{0x01u << SET_SHIFT};
+  static const HashType TYPE_MASK{0x07 << TYPE_SHIFT};
+  static const HashType SET_ID_MASK{0x0fffffff};
+
+  static HashType ToHash(Type type, bool set, HashType setIndex)
+  {
+    return ((set << SET_SHIFT) & SET_MASK) | ((static_cast<HashType>(type) << TYPE_SHIFT) & TYPE_MASK) | (setIndex & SET_ID_MASK);
+  }
+
+  static Attribute::Type TypeFromHash(HashType hash)
+  {
+    return static_cast<Type>((hash & TYPE_MASK) >> TYPE_SHIFT);
+  }
+
+  static bool SetFromHash(HashType hash)
+  {
+    return (hash & SET_SHIFT) != 0;
+  }
+
+  static HashType SetIdFromHash(HashType hash)
+  {
+    return (hash & SET_ID_MASK);
+  }
+
+  /**
+   * Convert to Type + setIndex, where setIndex is N for that attr, e.g. "JOINTS_1" => {JOINTS_N, 1}
+   */
+  static HashType HashFromString(const char* s, size_t len);
+
+  /**
+   * Convert to type only, there is no set for POSITION, NORMALS or TANGENT.
+   */
+  static Attribute::Type TargetFromString(const char* s, size_t len);
 
   Attribute() = delete;
 };
@@ -415,7 +458,7 @@ struct Mesh : Named
       INVALID
     };
 
-    std::map<Attribute::Type, Ref<Accessor>>              mAttributes;
+    std::map<Attribute::HashType, Ref<Accessor>>          mAttributes;
     std::vector<std::map<Attribute::Type, Ref<Accessor>>> mTargets;
     Ref<Accessor>                                         mIndices;
     Ref<Material>                                         mMaterial;
@@ -563,6 +606,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 +637,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..c4e492f 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
@@ -46,19 +48,15 @@ static const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
   Geometry::TRIANGLE_STRIP,
   Geometry::TRIANGLE_FAN}; //...because Dali swaps the last two.
 
-static struct AttributeMapping
-{
-  gltf2::Attribute::Type   mType;
-  MeshDefinition::Accessor MeshDefinition::*mAccessor;
-  uint16_t                                  mElementSizeRequired;
-} ATTRIBUTE_MAPPINGS[]{
-  {gltf2::Attribute::NORMAL, &MeshDefinition::mNormals, sizeof(Vector3)},
-  {gltf2::Attribute::TANGENT, &MeshDefinition::mTangents, sizeof(Vector3)},
-  {gltf2::Attribute::TEXCOORD_0, &MeshDefinition::mTexCoords, sizeof(Vector2)},
-  {gltf2::Attribute::COLOR_0, &MeshDefinition::mColors, sizeof(Vector4)},
-  {gltf2::Attribute::JOINTS_0, &MeshDefinition::mJoints0, sizeof(Vector4)},
-  {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)
 {
@@ -133,9 +131,7 @@ const json::Reader<gltf2::Accessor>& GetAccessorReader()
                                                   .Register(*new json::Property<gltf2::Accessor, gltf2::Ref<gltf2::BufferView>>("bufferView",
                                                                                                                                 gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>,
                                                                                                                                 &gltf2::Accessor::mBufferView))
-                                                  .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset",
-                                                                                                           json::Read::Number<uint32_t>,
-                                                                                                           &gltf2::Accessor::mByteOffset))
+                                                  .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset", json::Read::Number<uint32_t>, &gltf2::Accessor::mByteOffset))
                                                   .Register(*new json::Property<gltf2::Accessor, gltf2::Component::Type>("componentType",
                                                                                                                          json::Read::Enum<gltf2::Component::Type>,
                                                                                                                          &gltf2::Accessor::mComponentType))
@@ -239,7 +235,26 @@ const json::Reader<gltf2::Material>& GetMaterialReader()
   return MATERIAL_READER;
 }
 
-std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
+std::map<gltf2::Attribute::HashType, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
+{
+  auto&                                                             jsonObject = json::Cast<json_object_s>(j);
+  std::map<gltf2::Attribute::HashType, gltf2::Ref<gltf2::Accessor>> result;
+
+  auto element = jsonObject.start;
+  while(element)
+  {
+    auto jsonString = *element->name;
+
+    gltf2::Attribute::HashType hash = gltf2::Attribute::HashFromString(jsonString.string, jsonString.string_size);
+
+    result[hash] =
+      gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
+    element = element->next;
+  }
+  return result;
+}
+
+std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes2(const json_value_s& j)
 {
   auto&                                                         jsonObject = json::Cast<json_object_s>(j);
   std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> result;
@@ -247,9 +262,11 @@ std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveA
   auto element = jsonObject.start;
   while(element)
   {
-    auto jsonString                                                                 = *element->name;
-    result[gltf2::Attribute::FromString(jsonString.string, jsonString.string_size)] = gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
-    element                                                                         = element->next;
+    auto jsonString = *element->name;
+
+    result[gltf2::Attribute::TargetFromString(jsonString.string, jsonString.string_size)] = gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
+
+    element = element->next;
   }
   return result;
 }
@@ -264,7 +281,7 @@ std::vector<std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>>> ReadM
   auto element = jsonObject.start;
   while(element)
   {
-    result.push_back(std::move(ReadMeshPrimitiveAttributes(*element->value)));
+    result.push_back(std::move(ReadMeshPrimitiveAttributes2(*element->value)));
     element = element->next;
   }
 
@@ -482,6 +499,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))
@@ -744,17 +767,167 @@ MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gltf2::Accessor& acc
 
   return MeshDefinition::Accessor{
     std::move(MeshDefinition::Blob{bufferViewOffset + accessor.mByteOffset,
-                                   accessor.GetBytesLength(),
-                                   static_cast<uint16_t>(bufferViewStride),
-                                   static_cast<uint16_t>(accessor.GetElementSizeBytes()),
-                                   accessor.mMin,
-                                   accessor.mMax}),
-    std::move(sparseBlob),
-    accessor.mBufferView ? accessor.mBufferView->mBuffer.GetIndex() : 0};
+          accessor.GetBytesLength(),
+          static_cast<uint16_t>(bufferViewStride),
+          static_cast<uint16_t>(accessor.GetElementSizeBytes()),
+          accessor.mMin,
+          accessor.mMax}),
+      std::move(sparseBlob),
+      accessor.mBufferView ? accessor.mBufferView->mBuffer.GetIndex() : 0,
+      accessor.mNormalized};
+}
+
+MeshDefinition::Accessor* GetAccessorFromAttribute(gltf2::Attribute::HashType attributeHash,
+                                                   MeshDefinition& meshDefinition,
+                                                   bool& needNormals, bool& needTangents)
+{
+  MeshDefinition::Accessor* accessorDest{nullptr};
+
+  switch(gltf2::Attribute::TypeFromHash(attributeHash))
+  {
+    case gltf2::Attribute::POSITION:
+    {
+      accessorDest = &meshDefinition.mPositions;
+      break;
+    }
+    case gltf2::Attribute::NORMAL:
+    {
+      accessorDest = &meshDefinition.mNormals;
+      needNormals  = false;
+      break;
+    }
+    case gltf2::Attribute::TANGENT:
+    {
+      accessorDest = &meshDefinition.mTangents;
+      needTangents = false;
+      break;
+    }
+    case gltf2::Attribute::TEXCOORD_N:
+    {
+      meshDefinition.mTexCoords.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mTexCoords.back();
+      break;
+    }
+    case gltf2::Attribute::COLOR_N:
+    {
+      meshDefinition.mColors.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mColors.back();
+      break;
+    }
+    case gltf2::Attribute::JOINTS_N:
+    {
+      if(meshDefinition.mJoints.size() < MeshDefinition::MAX_NUMBER_OF_JOINT_SETS)
+      {
+        meshDefinition.mJoints.emplace_back(MeshDefinition::Accessor{});
+        accessorDest = &meshDefinition.mJoints.back();
+      }
+      break;
+    }
+    case gltf2::Attribute::WEIGHTS_N:
+    {
+      if(meshDefinition.mWeights.size() < MeshDefinition::MAX_NUMBER_OF_JOINT_SETS)
+      {
+        meshDefinition.mWeights.emplace_back(MeshDefinition::Accessor{});
+        accessorDest = &meshDefinition.mWeights.back();
+      }
+      break;
+    }
+    case gltf2::Attribute::INVALID:
+    {
+      accessorDest = nullptr;
+      break;
+    }
+  }
+  return accessorDest;
+}
+
+void SetFlagsFromComponentType(const gltf2::Accessor& accessor,
+                               gltf2::Attribute::HashType attributeHash,
+                               MeshDefinition& meshDefinition,
+                               bool isQuantized)
+{
+  switch(gltf2::Attribute::TypeFromHash(attributeHash))
+  {
+    case gltf2::Attribute::POSITION:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_POSITION;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION;
+        meshDefinition.mFlags |= (accessor.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))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::NORMAL:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL;
+      }
+
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_NORMAL))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::TANGENT:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT;
+      }
+
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TANGENT))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::TEXCOORD_N:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.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))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::COLOR_N:
+    {
+      break;
+    }
+    case gltf2::Attribute::JOINTS_N:
+    {
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_JOINT_IDS;
+      DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_JOINT_IDS) ||
+                        MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_JOINT_IDS) ||
+                        accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::WEIGHTS_N:
+    {
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_WEIGHT;
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_WEIGHT;
+      DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_WEIGHT) ||
+                        MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_WEIGHT) ||
+                        accessor.mComponentType == gltf2::Component::FLOAT);
+
+      break;
+    }
+    case gltf2::Attribute::INVALID:
+    {
+      break;
+    }
+  }
 }
 
 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)
@@ -774,58 +947,40 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
       auto& attribs                 = primitive.mAttributes;
       meshDefinition.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[primitive.mMode];
 
-      auto positionIter = attribs.find(gltf2::Attribute::POSITION);
-
+      auto positionIter = attribs.find(gltf2::Attribute::ToHash(gltf2::Attribute::POSITION, false, 0));
       if(positionIter == attribs.end())
       {
         DALI_LOG_ERROR("Primitive mesh dosn't have POSITION atrributes!");
         continue;
       }
 
-      auto& accPositions        = *positionIter->second;
-      meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
+      auto& positionsAccessor = *positionIter->second;
+
       // 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;
 
-      const bool needNormalsTangents = accPositions.mType == gltf2::AccessorType::VEC3;
-      for(auto& attributeMapping : ATTRIBUTE_MAPPINGS)
+      bool needNormals  = (positionsAccessor.mType == gltf2::AccessorType::VEC3);
+      bool needTangents = (positionsAccessor.mType == gltf2::AccessorType::VEC3);
+
+      for(const auto& [attributeHash, accessor] : attribs)
       {
-        auto iFind = attribs.find(attributeMapping.mType);
-        if(iFind != attribs.end())
+        MeshDefinition::Accessor* accessorDest = GetAccessorFromAttribute(attributeHash, meshDefinition, needNormals, needTangents);
+        if(accessorDest == nullptr)
         {
-          auto& accessor = meshDefinition.*(attributeMapping.mAccessor);
-          accessor       = ConvertMeshPrimitiveAccessor(*iFind->second);
-
-          if(iFind->first == gltf2::Attribute::JOINTS_0)
-          {
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_JOINT_IDS;
-            DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_JOINT_IDS) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_JOINT_IDS) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
-          if(iFind->first == gltf2::Attribute::WEIGHTS_0)
-          {
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_WEIGHT;
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_WEIGHT;
-            DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_WEIGHT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_WEIGHT) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
+          continue;
         }
-        else if(needNormalsTangents)
-        {
-          switch(attributeMapping.mType)
-          {
-            case gltf2::Attribute::NORMAL:
-              meshDefinition.RequestNormals();
-              break;
-
-            case gltf2::Attribute::TANGENT:
-              meshDefinition.RequestTangents();
-              break;
+        *accessorDest = ConvertMeshPrimitiveAccessor(*accessor);
+        SetFlagsFromComponentType(*accessor, attributeHash, meshDefinition, isQuantized);
+      }
 
-            default:
-              break;
-          }
-        }
+      if(needNormals)
+      {
+        meshDefinition.RequestNormals();
+      }
+      if(needTangents)
+      {
+        meshDefinition.RequestTangents();
       }
 
       if(primitive.mIndices)
@@ -849,17 +1004,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 +1644,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 8370795..6cbc92b 100644 (file)
@@ -173,7 +173,7 @@ uint32_t ModelNode::GetModelPrimitiveCount() const
   return static_cast<uint32_t>(mModelPrimitiveContainer.size());
 }
 
-void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
+void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive, Loader::ShaderOption::HashType hash)
 {
   for(auto&& primitive : mModelPrimitiveContainer)
   {
@@ -197,7 +197,7 @@ void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
     GetImplementation(modelPrimitive).SetImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
   }
 
-  GetImplementation(modelPrimitive).UpdateShader(mShaderManager);
+  GetImplementation(modelPrimitive).UpdateShader(mShaderManager, hash);
 
   Dali::Renderer renderer = GetImplementation(modelPrimitive).GetRenderer();
   if(renderer)
@@ -241,7 +241,7 @@ void ModelNode::RemoveModelPrimitive(uint32_t index)
     return;
   }
 
-  GetImplementation(mModelPrimitiveContainer[index]).UpdateShader(nullptr);
+  GetImplementation(mModelPrimitiveContainer[index]).UpdateShader(nullptr, 0u);
 
   Actor self = Self();
   GetImplementation(mModelPrimitiveContainer[index]).RemovePrimitiveObserver(this);
@@ -326,7 +326,7 @@ void ModelNode::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager)
     mShaderManager = shaderManager;
     for(auto&& primitive : mModelPrimitiveContainer)
     {
-      GetImplementation(primitive).UpdateShader(mShaderManager);
+      GetImplementation(primitive).UpdateShader(mShaderManager, 0u);
     }
   }
 }
index ed17a45..7399406 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-scene3d/public-api/light/light.h>
 #include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-scene3d/public-api/loader/shader-manager.h>
+#include <dali-scene3d/public-api/loader/shader-option.h>
 #include <dali-scene3d/public-api/loader/skinning-details.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
 #include <dali-scene3d/public-api/model-components/model-primitive.h>
@@ -198,7 +199,7 @@ public: // Public Method
   /**
    * @copydoc Dali::Scene3D::ModelNode::AddModelPrimitive()
    */
-  void AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive);
+  void AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive, Loader::ShaderOption::HashType hash);
 
   /**
    * @copydoc Dali::Scene3D::ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
@@ -306,7 +307,7 @@ private:
   Scene3D::Loader::ShaderManagerPtr mShaderManager;
   ModelPrimitiveContainer           mModelPrimitiveContainer; ///< List of model primitives
   BoneDataContainer                 mBoneDataContainer;
-  BlendShapeIndexMap                mBlendShapeIndexMap;      ///< Index of blend shape by name
+  BlendShapeIndexMap                mBlendShapeIndexMap; ///< Index of blend shape by name
   Dali::Texture                     mShadowMapTexture;
   Dali::Texture                     mSpecularTexture;
   Dali::Texture                     mDiffuseTexture;
index e411694..e58327c 100644 (file)
 #include <dali-scene3d/public-api/loader/environment-definition.h>
 
 #include <dali/integration-api/debug.h>
+#include <dali/public-api/object/property-array.h>
+#include <dali/public-api/object/property-map.h>
+
+#if defined(DEBUG_ENABLED)
+#include <sys/types.h>
+#include <unistd.h>
+#include <filesystem>
+namespace fs = std::filesystem;
+#endif
 
 namespace Dali
 {
@@ -40,6 +49,55 @@ namespace Internal
 {
 namespace
 {
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_SCENE3D_MODEL_PRIMITIVE");
+
+std::string tmpFilename(std::string prefix, std::string suffix)
+{
+  static int id = 0;
+  id++;
+  std::ostringstream oss;
+  oss << prefix << getpid() << "_" << std::setfill('0') << std::setw(4) << id << suffix;
+  return oss.str();
+}
+
+#define DALI_LOG_WRITE_FILE(filename, stream) \
+  {                                           \
+    fs::path tmp = fs::temp_directory_path(); \
+    tmp /= filename;                          \
+    std::ofstream ostrm(tmp, std::ios::out);  \
+    ostrm << stream;                          \
+    ostrm.flush();                            \
+  }
+
+inline Property::Map GetMap(Shader shader)
+{
+  Property::Value program = shader[Shader::Property::PROGRAM];
+  Property::Map*  map{nullptr};
+  if(program.GetType() == Property::ARRAY)
+  {
+    Property::Array* array = program.GetArray();
+    if(array)
+    {
+      Property::Value& value = array->GetElementAt(0);
+      if(value.GetType() == Property::MAP)
+      {
+        map = value.GetMap();
+      }
+    }
+  }
+  else if(program.GetType() == Property::MAP)
+  {
+    map = program.GetMap();
+  }
+  if(map)
+  {
+    return *map;
+  }
+  return Property::Map();
+}
+
+#endif
 /**
  * Creates control through type registry
  */
@@ -185,14 +243,14 @@ void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
   }
 }
 
-void ModelPrimitive::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager)
+void ModelPrimitive::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager, Loader::ShaderOption::HashType hash)
 {
   if(mShaderManager != shaderManager)
   {
     mShaderManager = (shaderManager) ? shaderManager : new Scene3D::Loader::ShaderManager();
     if(mMaterial && GetImplementation(mMaterial).IsResourceReady())
     {
-      ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag::SHADER);
+      ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag::SHADER, hash);
     }
   }
 }
@@ -216,9 +274,15 @@ void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bo
   mBlendShapeVersion = version;
 }
 
-void ModelPrimitive::SetSkinned(bool isSkinned)
+void ModelPrimitive::SetSkinned(bool isSkinned, uint32_t numberOfJointSets)
 {
-  mHasSkinning = isSkinned;
+  mHasSkinning       = isSkinned;
+  mNumberOfJointSets = numberOfJointSets;
+}
+
+void ModelPrimitive::SetVertexColor(bool hasVertexColor)
+{
+  mHasVertexColor = hasVertexColor;
 }
 
 // From MaterialModifyObserver
@@ -228,7 +292,7 @@ void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, Materi
   ApplyMaterialToRenderer(flag);
 }
 
-void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag)
+void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag, Loader::ShaderOption::HashType oldHash)
 {
   if(!mMaterial)
   {
@@ -244,6 +308,11 @@ void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag
     if(mHasSkinning)
     {
       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SKINNING);
+      shaderOption.AddJointMacros(mNumberOfJointSets);
+    }
+    if(mHasVertexColor)
+    {
+      shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::COLOR_ATTRIBUTE);
     }
     if(mHasPositions || mHasNormals || mHasTangents)
     {
@@ -265,8 +334,27 @@ void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag
       }
     }
 
-    mShader.Reset();
-    mShader = mShaderManager->ProduceShader(shaderOption);
+    Shader newShader = mShaderManager->ProduceShader(shaderOption);
+    if(mShader != newShader)
+    {
+      DALI_LOG_INFO(gLogFilter, Debug::General, "Warning!  Model primitive shader changed: OldHash:%x NewHash:%x\n", oldHash, shaderOption.GetOptionHash());
+
+#if defined(DEBUG_ENABLED)
+      if(mShader)
+      {
+        Property::Map oldMap = GetMap(mShader);
+        DALI_LOG_WRITE_FILE(tmpFilename("oldShader", ".txt"), "Vertex Shader:\n"
+                            << oldMap["vertex"] << "\n\nFragmentShader: " << oldMap["fragment"] << "\n");
+      }
+      if(newShader)
+      {
+        Property::Map newMap = GetMap(newShader);
+        DALI_LOG_WRITE_FILE(tmpFilename("newShader", ".txt"), "Vertex Shader:\n"
+                            << newMap["vertex"] << "\n\nFragmentShader: " << newMap["fragment"] << "\n");
+      }
+#endif
+    }
+    mShader = newShader;
 
     if(!mRenderer)
     {
index c6dbf37..5913424 100644 (file)
@@ -151,8 +151,9 @@ public:
    * @brief Updates shaders by using current material
    *
    * @param[in] shaderManager Shader manager to create shader.
+   * @param[in] hash of old shader option
    */
-  void UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager);
+  void UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager, Loader::ShaderOption::HashType hash);
 
   /**
    * @brief Sets the blend shape data for this model primitive.
@@ -182,8 +183,16 @@ public:
    * @brief Sets whether or not this model primitive is skinned.
    *
    * @param[in] isSkinned Whether or not this model primitive is skinned.
+   * @param[in] numberOfJointSets How many joint sets the mesh expects in the shader
    */
-  void SetSkinned(bool isSkinned);
+  void SetSkinned(bool isSkinned, uint32_t numberOfJointSets);
+
+  /**
+   * @brief Set whether this model primitve has vertex color attributes
+   *
+   * @param[in] hasVertexColor Whether or not this model primitive has vertex color attributes
+   */
+  void SetVertexColor(bool hasVertexColor);
 
 private: // From MaterialModifyObserver
   /**
@@ -195,7 +204,8 @@ private:
   /**
    * @brief Apply materials data into renderer.
    */
-  void ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag = MaterialModifyObserver::ModifyFlag::NONE);
+  void ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag    = MaterialModifyObserver::ModifyFlag::NONE,
+                               Loader::ShaderOption::HashType     oldHash = 0u);
 
   /**
    * @brief Updates the uniform of renderer.
@@ -216,9 +226,9 @@ private:
 
 private:
   // Delete copy & move operator
-  ModelPrimitive(const ModelPrimitive&)                    = delete;
-  ModelPrimitive(ModelPrimitive&&)                         = delete;
-  ModelPrimitive& operator=(const ModelPrimitive& rhs)     = delete;
+  ModelPrimitive(const ModelPrimitive&) = delete;
+  ModelPrimitive(ModelPrimitive&&)      = delete;
+  ModelPrimitive& operator=(const ModelPrimitive& rhs) = delete;
   ModelPrimitive& operator=(ModelPrimitive&& rhs) noexcept = delete;
 
 private:
@@ -242,10 +252,14 @@ private:
   float         mIblScaleFactor{1.0f};
   uint32_t      mSpecularMipmapLevels{1u};
 
+  // For skinning
+  uint32_t mNumberOfJointSets{0};
+
   // For blend shape
   Scene3D::Loader::BlendShapes::BlendShapeData mBlendShapeData;
   Dali::Texture                                mBlendShapeGeometry;
   bool                                         mHasSkinning       = false;
+  bool                                         mHasVertexColor    = false;
   bool                                         mHasPositions      = false;
   bool                                         mHasNormals        = false;
   bool                                         mHasTangents       = false;
index 76a6fa1..6b2feb6 100644 (file)
@@ -211,7 +211,7 @@ bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source
 }
 
 template<typename T>
-void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
+void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
 {
   constexpr auto sizeofBlobUnit = sizeof(T) * 4;
 
@@ -241,11 +241,27 @@ void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acces
       ++floats;
     }
   }
-  raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+  raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+}
+
+void ReadTypedJointAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, const std::string& name)
+{
+  if(MaskMatch(flags, MeshDefinition::U16_JOINT_IDS))
+  {
+    ReadJointAccessor<uint16_t>(raw, accessor, stream, path, name);
+  }
+  else if(MaskMatch(flags, MeshDefinition::U8_JOINT_IDS))
+  {
+    ReadJointAccessor<uint8_t>(raw, accessor, stream, path, name);
+  }
+  else
+  {
+    ReadJointAccessor<float>(raw, accessor, stream, path, name);
+  }
 }
 
 template<typename T>
-void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
+void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
 {
   constexpr auto sizeofBlobUnit = sizeof(T) * 4;
 
@@ -276,7 +292,23 @@ void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acce
       ++floats;
     }
   }
-  raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+  raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+}
+
+void ReadTypedWeightAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, std::string name)
+{
+  if(MaskMatch(flags, MeshDefinition::U16_WEIGHT))
+  {
+    ReadWeightAccessor<uint16_t>(raw, accessor, stream, path, name);
+  }
+  else if(MaskMatch(flags, MeshDefinition::U8_WEIGHT))
+  {
+    ReadWeightAccessor<uint8_t>(raw, accessor, stream, path, name);
+  }
+  else
+  {
+    ReadWeightAccessor<float>(raw, accessor, stream, path, name);
+  }
 }
 
 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
@@ -447,26 +479,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 +651,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 +724,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());
@@ -654,19 +859,23 @@ MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t c
 
 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob&       blob,
                                    const MeshDefinition::SparseBlob& sparse,
-                                   Index                             bufferIndex)
+                                   Index                             bufferIndex,
+                                   bool                              normalized)
 : mBlob{blob},
   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
-  mBufferIdx(bufferIndex)
+  mBufferIdx(bufferIndex),
+  mNormalized(normalized)
 {
 }
 
 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&&       blob,
                                    MeshDefinition::SparseBlob&& sparse,
-                                   Index                        bufferIndex)
+                                   Index                        bufferIndex,
+                                   bool                         normalized)
 : mBlob{std::move(blob)},
   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
-  mBufferIdx(bufferIndex)
+  mBufferIdx(bufferIndex),
+  mNormalized(normalized)
 {
 }
 
@@ -756,7 +965,22 @@ bool MeshDefinition::IsQuad() const
 
 bool MeshDefinition::IsSkinned() const
 {
-  return mJoints0.IsDefined() && mWeights0.IsDefined();
+  return !mJoints.empty() && !mWeights.empty();
+}
+
+bool MeshDefinition::HasVertexColor() const
+{
+  return !mColors.empty();
+}
+
+uint32_t MeshDefinition::GetNumberOfJointSets() const
+{
+  uint32_t number = static_cast<uint32_t>(mJoints.size());
+  if(number > MeshDefinition::MAX_NUMBER_OF_JOINT_SETS)
+  {
+    number = MeshDefinition::MAX_NUMBER_OF_JOINT_SETS;
+  }
+  return number;
 }
 
 bool MeshDefinition::HasBlendShapes() const
@@ -853,13 +1077,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 +1118,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 +1147,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 +1181,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)
   {
@@ -928,23 +1211,45 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     }
   }
 
-  const auto hasUvs = mTexCoords.IsDefined();
-  if(hasUvs)
+  if(!mTexCoords.empty() && mTexCoords[0].IsDefined())
   {
-    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();
+    auto& texCoords = mTexCoords[0];
+    const auto bufferSize = texCoords.mBlob.GetBufferSize();
+    uint32_t uvCount;
+
+    if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD))
+    {
+      DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
+                          texCoords.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(((texCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
+                          texCoords.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(((texCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
+                          texCoords.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;
-    auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mTexCoords.mBufferIdx], path);
-    if(!ReadAccessor(mTexCoords, stream, buffer.data()))
+    auto&       stream = GetAvailableData(fileStream, meshPath, buffers[texCoords.mBufferIdx], path);
+    if(!ReadAccessor(texCoords, stream, buffer.data()))
     {
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
     }
 
-    const auto uvCount = bufferSize / sizeof(Vector2);
+    GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, texCoords.mNormalized);
+
     if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
     {
       auto uv    = reinterpret_cast<Vector2*>(buffer.data());
@@ -956,18 +1261,46 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       }
     }
 
-    mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
+    if(texCoords.mNormalized)
+    {
+      GetDequantizedMinMax(texCoords.mBlob.mMin, texCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
+    }
 
+    texCoords.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)});
   }
 
   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 +1309,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)
   {
@@ -1005,6 +1346,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
             GenerateTangents<true, true, true>,
           },
         }};
+    const bool hasUvs            = !mTexCoords.empty() && mTexCoords[0].IsDefined();
     const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
     if(!generateSuccessed)
     {
@@ -1012,25 +1354,26 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     }
   }
 
-  if(mColors.IsDefined())
+  // Only support 1 vertex color
+  if(!mColors.empty() && mColors[0].IsDefined())
   {
-    uint32_t       propertySize = mColors.mBlob.mElementSizeHint;
+    uint32_t       propertySize = mColors[0].mBlob.mElementSizeHint;
     Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
     if(propertyType != Property::NONE)
     {
-      DALI_ASSERT_ALWAYS(((mColors.mBlob.mLength % propertySize == 0) ||
-                          mColors.mBlob.mStride >= propertySize) &&
+      DALI_ASSERT_ALWAYS(((mColors[0].mBlob.mLength % propertySize == 0) ||
+                          mColors[0].mBlob.mStride >= propertySize) &&
                          "Colors buffer length not a multiple of element size");
-      const auto           bufferSize = mColors.mBlob.GetBufferSize();
+      const auto           bufferSize = mColors[0].mBlob.GetBufferSize();
       std::vector<uint8_t> buffer(bufferSize);
 
       std::string path;
-      auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mColors.mBufferIdx], path);
-      if(!ReadAccessor(mColors, stream, buffer.data()))
+      auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mColors[0].mBufferIdx], path);
+      if(!ReadAccessor(mColors[0], stream, buffer.data()))
       {
         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
       }
-      mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
+      mColors[0].mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
 
       raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
     }
@@ -1050,34 +1393,25 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
 
   if(IsSkinned())
   {
-    std::string pathJoint;
-    auto&       streamJoint = GetAvailableData(fileStream, meshPath, buffers[mJoints0.mBufferIdx], pathJoint);
-    if(MaskMatch(mFlags, U16_JOINT_IDS))
-    {
-      ReadJointAccessor<uint16_t>(raw, mJoints0, streamJoint, pathJoint);
-    }
-    else if(MaskMatch(mFlags, U8_JOINT_IDS))
-    {
-      ReadJointAccessor<uint8_t>(raw, mJoints0, streamJoint, pathJoint);
-    }
-    else
-    {
-      ReadJointAccessor<float>(raw, mJoints0, streamJoint, pathJoint);
-    }
-
-    std::string pathWeight;
-    auto&       streamWeight = GetAvailableData(fileStream, meshPath, buffers[mWeights0.mBufferIdx], pathWeight);
-    if(MaskMatch(mFlags, U16_WEIGHT))
-    {
-      ReadWeightAccessor<uint16_t>(raw, mWeights0, streamWeight, pathWeight);
-    }
-    else if(MaskMatch(mFlags, U8_WEIGHT))
+    int setIndex = 0;
+    for(auto& accessor : mJoints)
     {
-      ReadWeightAccessor<uint8_t>(raw, mWeights0, streamWeight, pathWeight);
+      std::string        pathJoint;
+      auto&              streamJoint = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathJoint);
+      std::ostringstream jointName;
+      jointName << "aJoints" << setIndex;
+      ++setIndex;
+      ReadTypedJointAccessor(raw, mFlags, accessor, streamJoint, pathJoint, jointName.str());
     }
-    else
+    setIndex = 0;
+    for(auto& accessor : mWeights)
     {
-      ReadWeightAccessor<float>(raw, mWeights0, streamWeight, pathWeight);
+      std::string        pathWeight;
+      auto&              streamWeight = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathWeight);
+      std::ostringstream weightName;
+      weightName << "aWeights" << setIndex;
+      ++setIndex;
+      ReadTypedWeightAccessor(raw, mFlags, accessor, streamWeight, pathWeight, weightName.str());
     }
   }
 
@@ -1086,22 +1420,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 +1454,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..ec3e160 100644 (file)
@@ -45,7 +45,12 @@ struct DALI_SCENE3D_API MeshDefinition
     INVALID = std::numeric_limits<uint32_t>::max()
   };
 
-  enum Flags : uint16_t
+  enum : uint32_t
+  {
+    MAX_NUMBER_OF_JOINT_SETS = 4
+  };
+
+  enum Flags : uint32_t
   {
     FLIP_UVS_VERTICAL = NthBit(0),
     U32_INDICES       = NthBit(1), // default is unsigned short
@@ -54,6 +59,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 +191,7 @@ struct DALI_SCENE3D_API MeshDefinition
     Blob                        mBlob;
     std::unique_ptr<SparseBlob> mSparse;
     Index                       mBufferIdx = INVALID_INDEX;
+    bool                        mNormalized{false};
 
     Accessor() = default;
 
@@ -177,10 +203,13 @@ struct DALI_SCENE3D_API MeshDefinition
 
     Accessor(const MeshDefinition::Blob&       blob,
              const MeshDefinition::SparseBlob& sparse,
-             Index                             bufferIndex = INVALID_INDEX);
+             Index                             bufferIndex = INVALID_INDEX,
+             bool                              normalized = false);
+
     Accessor(MeshDefinition::Blob&&       blob,
              MeshDefinition::SparseBlob&& sparse,
-             Index                        bufferIndex = INVALID_INDEX);
+             Index                        bufferIndex = INVALID_INDEX,
+             bool                         normalized = false);
 
     bool IsDefined() const
     {
@@ -198,6 +227,7 @@ struct DALI_SCENE3D_API MeshDefinition
     Accessor    normals;
     Accessor    tangents;
     float       weight = 0.f;
+    uint32_t    mFlags = 0x0;
   };
 
   struct RawData
@@ -239,6 +269,18 @@ struct DALI_SCENE3D_API MeshDefinition
   bool IsSkinned() const;
 
   /**
+   * @brief Determines if the mesh has any vertex colors
+   */
+  bool HasVertexColor() const;
+
+  /**
+   * @brief Returns the number of joint sets defined by the mesh
+   *
+   * @note Clamped to 4 to minimise GPU attrs.
+   */
+  uint32_t GetNumberOfJointSets() const;
+
+  /**
    * @brief Whether the mesh has blend shapes.
    */
   bool HasBlendShapes() const;
@@ -289,12 +331,12 @@ public: // DATA
   std::string              mUri; // When the mesh data is loaded from embedded resources, this URI is used as a data stream.
   Accessor                 mIndices;
   Accessor                 mPositions;
-  Accessor                 mNormals; // data can be generated based on positions
-  Accessor                 mTexCoords;
-  Accessor                 mColors;
+  Accessor                 mNormals;  // data can be generated based on positions
   Accessor                 mTangents; // data can be generated based on normals and texCoords (the latter isn't mandatory; the results will be better if available)
-  Accessor                 mJoints0;
-  Accessor                 mWeights0;
+  std::vector<Accessor>    mTexCoords;
+  std::vector<Accessor>    mColors;
+  std::vector<Accessor>    mJoints;
+  std::vector<Accessor>    mWeights;
   Property::Type           mTangentType{Property::VECTOR3};
 
   Blob                    mBlendShapeHeader;
index f0a0eba..56e63af 100644 (file)
@@ -253,12 +253,18 @@ void ModelRenderable::ReflectResources(IResourceReflector& reflector)
   reflector.Reflect(ResourceType::Material, mMaterialIdx);
 }
 
+// How many shader managers are there?!
 void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const
 {
   DALI_ASSERT_DEBUG(mMeshIdx != INVALID_INDEX);
+  ShaderOption::HashType shaderOptionHash{0u};
   if(mShaderIdx == INVALID_INDEX)
   {
-    Shader          shader          = params.mShaderManager->ProduceShader(params.mResources.mMaterials[mMaterialIdx].first, params.mResources.mMeshes[mMeshIdx].first);
+    ShaderOption option = params.mShaderManager->ProduceShaderOption(params.mResources.mMaterials[mMaterialIdx].first,
+                                                                     params.mResources.mMeshes[mMeshIdx].first);
+    shaderOptionHash    = option.GetOptionHash();
+    Shader shader       = params.mShaderManager->ProduceShader(option);
+
     static Geometry defaultGeometry = Geometry::New();
     Renderer        renderer        = Renderer::New(defaultGeometry, shader);
 
@@ -299,23 +305,24 @@ void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinit
 
   {
     mesh.first.mModelPrimitive = ModelPrimitive::New();
-    auto primitive             = mesh.first.mModelPrimitive;
-    GetImplementation(primitive).SetRenderer(renderer);
+    auto& primitive            = GetImplementation(mesh.first.mModelPrimitive);
+    primitive.SetRenderer(renderer);
 
     Index    envIndex       = resources.mMaterials[mMaterialIdx].first.mEnvironmentIdx;
     uint32_t specularMipmap = resources.mEnvironmentMaps[envIndex].second.mSpecularMipmapLevels;
-    GetImplementation(primitive).SetImageBasedLightTexture(resources.mEnvironmentMaps[envIndex].second.mDiffuse,
-                                                           resources.mEnvironmentMaps[envIndex].second.mSpecular,
-                                                           resources.mEnvironmentMaps[envIndex].first.mIblIntensity,
-                                                           specularMipmap);
+    primitive.SetImageBasedLightTexture(resources.mEnvironmentMaps[envIndex].second.mDiffuse,
+                                        resources.mEnvironmentMaps[envIndex].second.mSpecular,
+                                        resources.mEnvironmentMaps[envIndex].first.mIblIntensity,
+                                        specularMipmap);
 
     bool hasPositions = false;
     bool hasNormals   = false;
     bool hasTangents  = false;
     mesh.first.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
-    GetImplementation(primitive).SetBlendShapeOptions(hasPositions, hasNormals, hasTangents, mesh.first.mBlendShapeVersion);
-    GetImplementation(primitive).SetBlendShapeGeometry(mesh.second.blendShapeGeometry);
-    GetImplementation(primitive).SetSkinned(mesh.first.IsSkinned());
+    primitive.SetBlendShapeOptions(hasPositions, hasNormals, hasTangents, mesh.first.mBlendShapeVersion);
+    primitive.SetBlendShapeGeometry(mesh.second.blendShapeGeometry);
+    primitive.SetSkinned(mesh.first.IsSkinned(), mesh.first.GetNumberOfJointSets());
+    primitive.SetVertexColor(mesh.first.HasVertexColor());
   }
 
   auto shader = renderer.GetShader();
@@ -383,11 +390,13 @@ void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinit
     material.SetProperty(Scene3D::Material::Property::ALPHA_CUTOFF, matDef.GetAlphaCutoff());
     material.SetProperty(Scene3D::Material::Property::DOUBLE_SIDED, matDef.mDoubleSided);
     material.SetProperty(Scene3D::Material::Property::IOR, matDef.mIor);
+
+    // This _should_ keep the same shader as generated at the top of the method.
     GetImplementation(mesh.first.mModelPrimitive).SetMaterial(material, false);
     GetImplementation(material).ResetFlag();
   }
 
-  node.AddModelPrimitive(mesh.first.mModelPrimitive);
+  Internal::GetImplementation(node).AddModelPrimitive(mesh.first.mModelPrimitive, shaderOptionHash);
 }
 
 void ArcRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const
index 49ff885..a7727d6 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/object/property-array.h>
+#include <regex>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-scene3d/public-api/loader/utils.h>
 
+#include <dali/integration-api/debug.h>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_MODEL_SHADER_DEFINITION");
+#endif
+} // namespace
+
 namespace Dali::Scene3D::Loader
 {
 namespace
@@ -44,7 +54,7 @@ ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
 {
 }
 
-void ShaderDefinition::ApplyDefine(std::string& shaderCode, const std::string& definevar)
+void ApplyDefine(std::string& shaderCode, const std::string& definevar)
 {
   const std::string IF_1 = "#if 1";
 
@@ -79,6 +89,26 @@ void ShaderDefinition::ApplyDefine(std::string& shaderCode, const std::string& d
   }
 }
 
+void RedefineMacro(std::string& shaderCode, const std::string& macro, const std::string& value)
+{
+  std::string definition = "#define " + macro;
+  std::size_t found      = shaderCode.find(definition);
+  if(found != std::string::npos)
+  {
+    std::size_t insertionPoint = found + definition.length();
+
+    // Automatically insert line-continuation character into value
+    std::regex                 re("\n");
+    std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
+    for(auto i = first; i != last; ++i)
+    {
+      std::string line = std::string(" \\\n") + (*i).str();
+      shaderCode.insert(insertionPoint, line);
+      insertionPoint += line.length();
+    }
+  }
+}
+
 ShaderDefinition::RawData
 ShaderDefinition::LoadRaw(const std::string& shadersPath) const
 {
@@ -117,6 +147,12 @@ ShaderDefinition::LoadRaw(const std::string& shadersPath) const
       ApplyDefine(raw.mFragmentShaderSource, definevar);
       ApplyDefine(raw.mShadowVertexShaderSource, definevar);
     }
+    for(const auto& macroDef : mMacros)
+    {
+      RedefineMacro(raw.mVertexShaderSource, macroDef.macro, macroDef.definition);
+      RedefineMacro(raw.mFragmentShaderSource, macroDef.macro, macroDef.definition);
+      RedefineMacro(raw.mShadowVertexShaderSource, macroDef.macro, macroDef.definition);
+    }
   }
 
   return raw;
@@ -151,6 +187,7 @@ Shader ShaderDefinition::Load(RawData&& raw) const
   array.PushBack(map[0]);
   array.PushBack(map[1]);
 
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Vert Shader src: \n%s\n", raw.mVertexShaderSource.c_str());
   Shader shader = Shader::New(array);
   for(Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
   {
index 0fe83d3..f39ac5f 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/loader/renderer-state.h>
+#include <dali-scene3d/public-api/loader/shader-option.h>
 
 namespace Dali::Scene3D::Loader
 {
@@ -44,17 +45,12 @@ struct DALI_SCENE3D_API ShaderDefinition
     std::string mShadowFragmentShaderSource;
   };
 
-  /*
-   * @brief Apply the defines values to shader.
-   */
-  static void ApplyDefine(std::string& shaderCode, const std::string& definevar);
-
   ShaderDefinition() = default;
 
   ShaderDefinition(const ShaderDefinition& other);
   ShaderDefinition& operator=(const ShaderDefinition& other);
 
-  ShaderDefinition(ShaderDefinition&&)            = default;
+  ShaderDefinition(ShaderDefinition&&) = default;
   ShaderDefinition& operator=(ShaderDefinition&&) = default;
 
   /*
@@ -76,12 +72,13 @@ public: // DATA
   std::shared_ptr<RawData> mRawData;
   RendererState::Type      mRendererState = RendererState::NONE;
 
-  std::string              mVertexShaderPath;
-  std::string              mFragmentShaderPath;
-  std::vector<std::string> mDefines;
-  std::vector<std::string> mHints;
-  Property::Map            mUniforms;
-  bool                     mUseBuiltInShader{false};
+  std::string                                mVertexShaderPath;
+  std::string                                mFragmentShaderPath;
+  std::vector<std::string>                   mDefines;
+  std::vector<ShaderOption::MacroDefinition> mMacros;
+  std::vector<std::string>                   mHints;
+  Property::Map                              mUniforms;
+  bool                                       mUseBuiltInShader{false};
 };
 
 } // namespace Dali::Scene3D::Loader
index c3ef1ce..6adb452 100644 (file)
 
 #include <dali/integration-api/debug.h>
 
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_MODEL_SHADER_MANAGER");
+#endif
+} // namespace
+
 namespace Dali::Scene3D::Loader
 {
 namespace
@@ -109,6 +116,7 @@ ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinit
   if(meshDef.IsSkinned())
   {
     option.AddOption(ShaderOption::Type::SKINNING);
+    option.AddJointMacros(meshDef.mJoints.size());
   }
 
   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
@@ -116,7 +124,7 @@ ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinit
     option.AddOption(ShaderOption::Type::FLIP_UVS_VERTICAL);
   }
 
-  if(meshDef.mColors.IsDefined())
+  if(!meshDef.mColors.empty() && meshDef.mColors[0].IsDefined())
   {
     option.AddOption(ShaderOption::Type::COLOR_ATTRIBUTE);
   }
@@ -176,10 +184,10 @@ ShaderManager::ShaderManager()
 
 ShaderManager::~ShaderManager() = default;
 
-Dali::Shader ShaderManager::ProduceShader(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition)
+ShaderOption ShaderManager::ProduceShaderOption(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition)
 {
-  ShaderOption option = MakeOption(materialDefinition, meshDefinition);
-  return ProduceShader(option);
+  DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining shader from mat/mesh definitions\n");
+  return MakeOption(materialDefinition, meshDefinition);
 }
 
 Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
@@ -188,17 +196,39 @@ Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
 
   auto&    shaderMap = mImpl->mShaderMap;
   uint64_t hash      = shaderOption.GetOptionHash();
-  auto     iFind     = shaderMap.find(hash);
+
+#if defined(DEBUG_ENABLED)
+  std::ostringstream oss;
+  oss << "  ShaderOption defines:";
+  std::vector<std::string> defines;
+  shaderOption.GetDefines(defines);
+  for(auto& def : defines)
+  {
+    oss << def << ", ";
+  }
+  oss << std::endl
+      << "  ShaderOption macro definitions:" << std::endl;
+  for(auto& macro : shaderOption.GetMacroDefinitions())
+  {
+    oss << macro.macro << " : " << macro.definition << std::endl;
+  }
+  DALI_LOG_INFO(gLogFilter, Debug::Concise, "ShaderOption:\n%s\n", oss.str().c_str());
+#endif
+
+  auto iFind = shaderMap.find(hash);
   if(iFind != shaderMap.end())
   {
+    DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining Shader found: hash: %lx", hash);
     result = mImpl->mShaders[iFind->second];
   }
   else
   {
+    DALI_LOG_INFO(gLogFilter, Debug::Concise, "Creating new shader: hash: %lx\n", hash);
     ShaderDefinition shaderDef;
     shaderDef.mUseBuiltInShader = true;
 
     shaderOption.GetDefines(shaderDef.mDefines);
+    shaderDef.mMacros                  = shaderOption.GetMacroDefinitions();
     shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
 
     shaderMap[hash] = mImpl->mShaders.size();
@@ -396,14 +426,12 @@ void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
   std::string       shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
   auto              shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
   Dali::CameraActor shadowLightCamera                 = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
-  auto tempViewProjectionMatrixIndex = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
+  auto              tempViewProjectionMatrixIndex     = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
   if(tempViewProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
   {
     tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
   }
-  Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs)
-                                                                                  {
-                                                                                    output = inputs[0]->GetMatrix(); });
+  Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetMatrix(); });
   shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewProjectionMatrixIndex});
   shadowViewProjectionConstraint.ApplyPost();
   shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
index 4ca1384..af28cd8 100644 (file)
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/light/light.h>
 #include <dali-scene3d/public-api/loader/index.h>
 #include <dali-scene3d/public-api/loader/material-definition.h>
 #include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-scene3d/public-api/loader/renderer-state.h>
 #include <dali-scene3d/public-api/loader/shader-option.h>
-#include <dali-scene3d/public-api/light/light.h>
 
 namespace Dali::Scene3D::Loader
 {
@@ -55,9 +55,9 @@ public:
    * (Although the input materialDefinition and meshDefinition are not identical to those used to create the cached Dali::Shader, they share the cached one.)
    * @param[in] materialDefinition MaterialDefinition that includes information of material to create Shader.
    * @param[in] meshDefinition meshDefinition that includes information of mesh to create Shader.
-   * @return Dali::Shader for the materialDefinition and meshDefinition.
+   * @return ShaderOption for the materialDefinition and meshDefinition.
    */
-  Dali::Shader ProduceShader(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition);
+  ShaderOption ProduceShaderOption(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition);
 
   /**
    * @brief Produces a Dali::Shader for the input ShaderOption
@@ -147,6 +147,7 @@ private:
    * @param[in] shader Shader that the constraint will be applied.
    */
   DALI_INTERNAL void SetShadowConstraintToShader(Dali::Shader shader);
+
 private:
   struct Impl;
   const std::unique_ptr<Impl> mImpl;
index 85489ab..eec5c6b 100644 (file)
@@ -19,6 +19,9 @@
 #include <dali-scene3d/public-api/loader/shader-option.h>
 
 // EXTERNAL INCLUDES
+#include <algorithm>
+#include <ostream>
+#include <sstream>
 #include <string>
 
 namespace Dali::Scene3D::Loader
@@ -48,8 +51,41 @@ static constexpr std::string_view OPTION_KEYWORD[] =
     "MORPH_VERSION_2_0",
 };
 static constexpr uint32_t NUMBER_OF_OPTIONS = sizeof(OPTION_KEYWORD) / sizeof(OPTION_KEYWORD[0]);
+static const char*        ADD_EXTRA_SKINNING_ATTRIBUTES{"ADD_EXTRA_SKINNING_ATTRIBUTES"};
+static const char*        ADD_EXTRA_WEIGHTS{"ADD_EXTRA_WEIGHTS"};
+
+inline void HashString(std::uint64_t& hash, const char* string)
+{
+  char c;
+  while((c = *string++))
+  {
+    hash = hash * 33 + c;
+  }
+}
 } // namespace
 
+ShaderOption::ShaderOption(const ShaderOption& rhs)
+{
+  mOptionHash = rhs.mOptionHash;
+  for(auto& macroDef : rhs.mMacros)
+  {
+    mMacros.emplace_back(macroDef);
+  }
+}
+
+ShaderOption& ShaderOption::operator=(const ShaderOption& rhs)
+{
+  if(this != &rhs)
+  {
+    mOptionHash = rhs.mOptionHash;
+    for(auto& macroDef : rhs.mMacros)
+    {
+      mMacros.emplace_back(macroDef);
+    }
+  }
+  return *this;
+}
+
 void ShaderOption::SetTransparency()
 {
   mOptionHash |= (1 << NUMBER_OF_OPTIONS);
@@ -60,9 +96,61 @@ void ShaderOption::AddOption(Type shaderOptionType)
   mOptionHash |= (1 << static_cast<uint32_t>(shaderOptionType));
 }
 
+void ShaderOption::AddJointMacros(size_t numberOfJointSets)
+{
+  // Add options for ADD_EXTRA_SKINNING_ATTRIBUTES and ADD_EXTRA_WEIGHTS:
+  if(numberOfJointSets > 1)
+  {
+    std::ostringstream attributes;
+    std::ostringstream weights;
+    for(size_t i = 1; i < numberOfJointSets; ++i)
+    {
+      attributes << "in vec4 aJoints" << i << ";\n";
+      attributes << "in vec4 aWeights" << i << ";\n";
+
+      weights << "bone +=\n"
+              << "uBone[int(aJoints" << i << ".x)] * aWeights" << i << ".x +\n"
+              << "uBone[int(aJoints" << i << ".y)] * aWeights" << i << ".y +\n"
+              << "uBone[int(aJoints" << i << ".z)] * aWeights" << i << ".z +\n"
+              << "uBone[int(aJoints" << i << ".w)] * aWeights" << i << ".w;\n";
+    }
+    AddMacroDefinition(ADD_EXTRA_SKINNING_ATTRIBUTES, attributes.str());
+    AddMacroDefinition(ADD_EXTRA_WEIGHTS, weights.str());
+  }
+}
+
+void ShaderOption::AddMacroDefinition(std::string macro, std::string definition)
+{
+  auto iter = std::find_if(mMacros.begin(), mMacros.end(), [macro](ShaderOption::MacroDefinition& md) { return md.macro == macro; });
+  if(iter != mMacros.end())
+  {
+    iter->definition = definition;
+  }
+  else
+  {
+    mMacros.emplace_back(MacroDefinition{macro, definition});
+  }
+}
+
+const std::vector<ShaderOption::MacroDefinition>& ShaderOption::GetMacroDefinitions() const
+{
+  return mMacros;
+}
+
 uint64_t ShaderOption::GetOptionHash() const
 {
-  return mOptionHash;
+  uint64_t optionHash = mOptionHash;
+  if(!mMacros.empty())
+  {
+    uint64_t hash = 5381;
+    for(auto& macroDef : mMacros)
+    {
+      HashString(hash, macroDef.macro.c_str());
+      HashString(hash, macroDef.definition.c_str());
+    }
+    optionHash |= (hash << 32 & 0xFFFFFFFF00000000);
+  }
+  return optionHash;
 }
 
 void ShaderOption::GetDefines(std::vector<std::string>& defines) const
index e9d95c2..378d85b 100644 (file)
@@ -20,6 +20,7 @@
 // EXTERNAL INCLUDER
 #include <dali/public-api/common/vector-wrapper.h>
 #include <memory>
+#include <string>
 #include <string_view>
 
 // INTERNAL INCLUDES
 
 namespace Dali::Scene3D::Loader
 {
-
 class DALI_SCENE3D_API ShaderOption
 {
 public:
   enum class Type
   {
-    GLTF_CHANNELS = 0,
-    THREE_TEXTURE,
-    BASE_COLOR_TEXTURE,
-    METALLIC_ROUGHNESS_TEXTURE,
-    NORMAL_TEXTURE,
-    OCCLUSION,
-    EMISSIVE,
-    ALPHA_TEST,
-    SUBSURFACE,
-    SPECULAR,
-    SPECULAR_COLOR,
-    SKINNING,
-    FLIP_UVS_VERTICAL,
-    COLOR_ATTRIBUTE,
-    VEC4_TANGENT,
-    MORPH_POSITION,
-    MORPH_NORMAL,
-    MORPH_TANGENT,
-    MORPH_VERSION_2_0
+    GLTF_CHANNELS = 0,          // 00001
+    THREE_TEXTURE,              // 00002
+    BASE_COLOR_TEXTURE,         // 00004
+    METALLIC_ROUGHNESS_TEXTURE, // 00008
+    NORMAL_TEXTURE,             // 00010
+    OCCLUSION,                  // 00020
+    EMISSIVE,                   // 00040
+    ALPHA_TEST,                 // 00080
+    SUBSURFACE,                 // 00100
+    SPECULAR,                   // 00200
+    SPECULAR_COLOR,             // 00400
+    SKINNING,                   // 00800
+    FLIP_UVS_VERTICAL,          // 01000
+    COLOR_ATTRIBUTE,            // 02000
+    VEC4_TANGENT,               // 04000
+    MORPH_POSITION,             // 08000
+    MORPH_NORMAL,               // 10000
+    MORPH_TANGENT,              // 20000
+    MORPH_VERSION_2_0           // 40000
+  };
+
+  struct MacroDefinition
+  {
+    std::string macro;
+    std::string definition;
   };
 
+  ShaderOption() = default;
+  ShaderOption(const ShaderOption& rhs);
+  ShaderOption& operator=(const ShaderOption& rhs);
+
+  using HashType = uint64_t;
+
 public:
   /**
    * @brief Sets transparency option.
@@ -69,11 +81,21 @@ public:
   void AddOption(Type shaderOptionType);
 
   /**
+   * Adds macro definitions for joints based on the number of joint sets.
+   */
+  void AddJointMacros(size_t numberOfJointSets);
+
+  /**
+   * Enables empty preprocessor definitions to be defined to a value
+   */
+  void AddMacroDefinition(std::string macro, std::string definition);
+
+  /**
    * @brief Retrieves current shader option hash
    *
    * @return Hash value of currently added options.
    */
-  uint64_t GetOptionHash() const;
+  HashType GetOptionHash() const;
 
   /**
    * @brief Retrieves a list of define keywords.
@@ -90,8 +112,15 @@ public:
    */
   static std::string_view GetDefineKeyword(Type shaderOptionType);
 
+  /**
+   * Get any macro definitions
+   */
+  const std::vector<MacroDefinition>& GetMacroDefinitions() const;
+
 private:
-  uint64_t mOptionHash{0u};
+  HashType mOptionHash{0u};
+
+  std::vector<MacroDefinition> mMacros;
 };
 
 } // namespace Dali::Scene3D::Loader
index 2d49c1c..d857bb1 100644 (file)
@@ -25,7 +25,7 @@
 
 namespace Dali::Scene3D::Loader
 {
-const unsigned int Skinning::MAX_JOINTS = 80;
+const unsigned int Skinning::MAX_JOINTS = 256;
 
 const char* Skinning::BONE_UNIFORM_NAME = "uBone";
 } // namespace Dali::Scene3D::Loader
index a664eeb..f4bd758 100644 (file)
@@ -95,7 +95,7 @@ uint32_t ModelNode::GetModelPrimitiveCount() const
 
 void ModelNode::AddModelPrimitive(ModelPrimitive modelPrimitive)
 {
-  Internal::GetImplementation(*this).AddModelPrimitive(modelPrimitive);
+  Internal::GetImplementation(*this).AddModelPrimitive(modelPrimitive, 0u);
 }
 
 void ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
index 35dc49d..1677767 100644 (file)
@@ -133,7 +133,7 @@ enum Type
 
   /**
    * @brief The content information the AnimatedVectorImageVisual will use.
-   * @details Name "contentInfo", Type Property::MAP.
+   * @details Type Property::MAP.
    * The map contains the layer name as a key and Property::Array as a value.
    * And the array contains 2 integer values which are the frame numbers, the start frame number and the end frame number of the layer.
    * @note This property is read-only.
@@ -180,7 +180,16 @@ enum Type
    * Disable broken image for these visuals.
    * default is true.
    */
-  ENABLE_BROKEN_IMAGE = ORIENTATION_CORRECTION + 14
+  ENABLE_BROKEN_IMAGE = ORIENTATION_CORRECTION + 14,
+
+  /**
+   * @brief The marker information the AnimatedVectorImageVisual will use.
+   * @details Type Property::MAP.
+   * The map contains the marker name as a key and Property::Array as a value.
+   * And the array contains 2 integer values which are the frame numbers, the start frame number and the end frame number of the marker.
+   * @note This property is read-only.
+   */
+  MARKER_INFO = ORIENTATION_CORRECTION + 15,
 };
 
 } //namespace Property
index 9b4c92e..ebefb94 100644 (file)
@@ -150,7 +150,8 @@ void FastTrackLoadingTask::Load()
   {
     std::ostringstream oss;
     oss << "[url:" << mUrl.GetUrl() << "]";
-    DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("BEGIN: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
   }
 #endif
 
@@ -227,7 +228,8 @@ void FastTrackLoadingTask::Load()
       oss << "premult:" << mPremultiplied << " ";
     }
     oss << "url:" << mUrl.GetUrl() << "]";
-    DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_END(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("END: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
   }
 #endif
 }
index bab5888..e26a927 100644 (file)
@@ -163,7 +163,8 @@ void LoadingTask::Process()
   {
     std::ostringstream oss;
     oss << "[url:" << (!!(animatedImageLoading) ? animatedImageLoading.GetUrl() : url.GetUrl()) << "]";
-    DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_IMAGE_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_IMAGE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("BEGIN: DALI_IMAGE_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 
@@ -193,7 +194,8 @@ void LoadingTask::Process()
       oss << "premult:" << pixelBuffers[0].IsAlphaPreMultiplied() << " ";
     }
     oss << "url:" << (!!(animatedImageLoading) ? animatedImageLoading.GetUrl() : url.GetUrl()) << "]";
-    DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_IMAGE_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_END(gTraceFilter, "DALI_IMAGE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("END: DALI_IMAGE_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 }
@@ -213,6 +215,9 @@ void LoadingTask::Load()
   else if(encodedImageBuffer)
   {
     pixelBuffer = Dali::LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), dimensions, fittingMode, samplingMode, orientationCorrection);
+
+    // We don't need to hold image buffer anymore.
+    encodedImageBuffer.Reset();
   }
   else if(url.IsValid() && url.IsLocalResource())
   {
index fc77c6f..4343a84 100644 (file)
@@ -21,6 +21,8 @@
 // EXTERNAL HEADERS
 #include <dali/devel-api/common/hash.h>
 #include <dali/integration-api/debug.h>
+#include <string_view>
+#include <unordered_map>
 
 // INTERNAL HEADERS
 
@@ -30,6 +32,24 @@ namespace Toolkit
 {
 namespace Internal
 {
+namespace
+{
+const std::string_view& GetEncodedImageBufferExtensions(Dali::EncodedImageBuffer::ImageType imageType)
+{
+  static const std::unordered_map<Dali::EncodedImageBuffer::ImageType, const std::string_view> gEncodedImageBufferExtensionMap =
+    {
+      {Dali::EncodedImageBuffer::ImageType::REGULAR_IMAGE, ""},
+      {Dali::EncodedImageBuffer::ImageType::VECTOR_IMAGE, ".svg"},
+      {Dali::EncodedImageBuffer::ImageType::ANIMATED_VECTOR_IMAGE, ".json"},
+    };
+
+  const auto iter = gEncodedImageBufferExtensionMap.find(imageType);
+
+  DALI_ASSERT_DEBUG(iter != gEncodedImageBufferExtensionMap.end());
+
+  return iter->second;
+}
+} // namespace
 #ifdef DEBUG_ENABLED
 extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp
 
@@ -116,7 +136,10 @@ VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId
     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER:
     {
       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId);
-      visualUrl = VisualUrl::CreateBufferUrl(std::to_string(textureId));
+
+      EncodedImageBufferInfo& cachedEncodedImageBufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
+      const auto&             encodedImageBuffer = cachedEncodedImageBufferInfo.encodedImageBuffer;
+      visualUrl                                  = VisualUrl::CreateBufferUrl(std::to_string(textureId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
       break;
     }
     default:
@@ -217,7 +240,7 @@ EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& u
   EncodedImageBuffer encodedImageBuffer; // empty handle
   if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
   {
-    std::string location = url.GetLocation();
+    std::string location = url.GetLocationWithoutExtension();
     if(location.size() > 0u)
     {
       TextureId bufferId = std::stoi(location);
@@ -252,7 +275,7 @@ std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer&
 
     // If same buffer added, increase reference count and return.
     bufferInfo.referenceCount++;
-    return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId));
+    return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
   }
 
   TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
@@ -268,7 +291,7 @@ std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer&
 
   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
 
-  return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId));
+  return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
 }
 
 TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
@@ -321,7 +344,7 @@ EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl
     if(VisualUrl::BUFFER == url.GetProtocolType())
     {
       // get the location from the Url
-      std::string location = url.GetLocation();
+      std::string location = url.GetLocationWithoutExtension();
       if(location.size() > 0u)
       {
         TextureId bufferId = std::stoi(location);
@@ -376,7 +399,7 @@ void TextureCacheManager::UseExternalResource(const VisualUrl& url)
   }
   else if(VisualUrl::BUFFER == url.GetProtocolType())
   {
-    std::string location = url.GetLocation();
+    std::string location = url.GetLocationWithoutExtension();
     if(location.size() > 0u)
     {
       TextureId         id         = std::stoi(location);
index 0dceae2..0c3e3dd 100644 (file)
@@ -585,7 +585,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     if(textureInfo.loadState != LoadState::UPLOADED)
     {
       textureInfo.preMultiplied = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
-      textureInfo.loadState = TextureManager::LoadState::WAITING_FOR_MASK;
+      textureInfo.loadState     = TextureManager::LoadState::WAITING_FOR_MASK;
     }
   }
 
@@ -939,6 +939,8 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo
 
 void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
 {
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Add observer to observer queue (textureId:%d, observer:%p)\n", textureInfo.textureId, observer);
+
   const auto& textureId = textureInfo.textureId;
   mLoadQueue.PushBack(QueueElement(textureId, observer));
 
@@ -1413,6 +1415,7 @@ void TextureManager::ObserverDestroyed(TextureUploadObserver* observer)
   {
     if(element.mObserver == observer)
     {
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Remove observer from observer queue (textureId:%d, observer:%p)\n", element.mTextureId, element.mObserver);
       element.mTextureId = INVALID_TEXTURE_ID;
       element.mObserver  = nullptr;
     }
@@ -1515,6 +1518,22 @@ void TextureManager::RemoveTextureObserver(TextureManager::TextureInfo& textureI
       observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
       textureInfo.observerList.Erase(iter);
     }
+    else
+    {
+      // Given textureId might exist at load queue.
+      // Remove observer from the LoadQueue
+      for(auto&& element : mLoadQueue)
+      {
+        if(element.mTextureId == textureInfo.textureId && element.mObserver == observer)
+        {
+          DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Remove observer from observer queue (textureId:%d, observer:%p)\n", element.mTextureId, element.mObserver);
+          DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
+          observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
+          element.mObserver = nullptr;
+          break;
+        }
+      }
+    }
   }
 }
 
index d85d6ed..61f1b4d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(12); // 5 transform properties + Start point/color, end point/color, rotate center/angle, offset
+const int CUSTOM_PROPERTY_COUNT(7); // Start point/color, end point/color, rotate center/angle, offset
 
 DALI_ENUM_TO_STRING_TABLE_BEGIN(GRADIENT_TYPE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::DevelAnimatedGradientVisual::GradientType, LINEAR)
index 982d6dd..699c281 100644 (file)
@@ -113,6 +113,12 @@ AnimatedVectorImageVisual::~AnimatedVectorImageVisual()
 {
   if(!mCoreShutdown)
   {
+    if(mImageUrl.IsBufferResource())
+    {
+      TextureManager& textureManager = mFactoryCache.GetTextureManager();
+      textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
+    }
+
     auto& vectorAnimationManager = mFactoryCache.GetVectorAnimationManager();
     vectorAnimationManager.RemoveObserver(*this);
 
@@ -204,6 +210,10 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap(Property::Map& map) const
   mVectorAnimationTask->GetLayerInfo(layerInfo);
   map.Insert(Toolkit::DevelImageVisual::Property::CONTENT_INFO, layerInfo);
 
+  Property::Map markerInfo;
+  mVectorAnimationTask->GetMarkerInfo(markerInfo);
+  map.Insert(Toolkit::DevelImageVisual::Property::MARKER_INFO, markerInfo);
+
   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, IsSynchronousLoadingRequired());
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
@@ -380,7 +390,20 @@ void AnimatedVectorImageVisual::OnInitialize(void)
   mVectorAnimationTask->ResourceReadySignal().Connect(this, &AnimatedVectorImageVisual::OnResourceReady);
   mVectorAnimationTask->SetAnimationFinishedCallback(MakeCallback(this, &AnimatedVectorImageVisual::OnAnimationFinished));
 
-  mVectorAnimationTask->RequestLoad(mImageUrl, IsSynchronousLoadingRequired());
+  EncodedImageBuffer encodedImageBuffer;
+
+  if(mImageUrl.IsBufferResource())
+  {
+    // Increase reference count of External Resources :
+    // EncodedImageBuffer.
+    // Reference count will be decreased at destructor of the visual.
+    TextureManager& textureManager = mFactoryCache.GetTextureManager();
+    textureManager.UseExternalResource(mImageUrl.GetUrl());
+
+    encodedImageBuffer = textureManager.GetEncodedImageBuffer(mImageUrl.GetUrl());
+  }
+
+  mVectorAnimationTask->RequestLoad(mImageUrl, encodedImageBuffer, IsSynchronousLoadingRequired());
 
   auto& vectorAnimationManager = mFactoryCache.GetVectorAnimationManager();
   vectorAnimationManager.AddObserver(*this);
index df38f9c..c0d3802 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 #include <dali/public-api/math/math-utils.h>
@@ -55,6 +56,7 @@ DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false)
 VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
 : AsyncTask(MakeCallback(this, &VectorAnimationTask::TaskCompleted), AsyncTask::PriorityType::HIGH, AsyncTask::ThreadType::WORKER_THREAD),
   mImageUrl(),
+  mEncodedImageBuffer(),
   mVectorRenderer(VectorAnimationRenderer::New()),
   mAnimationData(),
   mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
@@ -148,18 +150,41 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
   {
     std::ostringstream oss;
     oss << "[url:" << mImageUrl.GetUrl() << "]";
-    DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("BEGIN: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 
-  if(!mVectorRenderer.Load(mImageUrl.GetUrl()))
+  if(mEncodedImageBuffer)
   {
-    DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mImageUrl.GetUrl().c_str());
-    mLoadFailed = true;
+    if(!mVectorRenderer.Load(mEncodedImageBuffer.GetRawBuffer()))
+    {
+      mLoadFailed = true;
+    }
+
+    // We don't need to hold image buffer anymore.
+    mEncodedImageBuffer.Reset();
+  }
+  else if(mImageUrl.IsLocalResource())
+  {
+    if(!mVectorRenderer.Load(mImageUrl.GetUrl()))
+    {
+      mLoadFailed = true;
+    }
+  }
+  else
+  {
+    Dali::Vector<uint8_t> remoteData;
+    if(!Dali::FileLoader::DownloadFileSynchronously(mImageUrl.GetUrl(), remoteData) || // Failed if we fail to download json file,
+       !mVectorRenderer.Load(remoteData))                                              // or download data is not valid vector animation file.
+    {
+      mLoadFailed = true;
+    }
   }
 
   if(mLoadFailed)
   {
+    DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mImageUrl.GetUrl().c_str());
     mLoadRequest = false;
     if(!synchronousLoading && mLoadCompletedCallback)
     {
@@ -170,7 +195,8 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
     {
       std::ostringstream oss;
       oss << "[url:" << mImageUrl.GetUrl() << "]";
-      DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", oss.str().c_str());
+      // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+      DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
     }
 #endif
     return false;
@@ -196,7 +222,8 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
   {
     std::ostringstream oss;
     oss << "[url:" << mImageUrl.GetUrl() << "]";
-    DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 
@@ -212,9 +239,10 @@ void VectorAnimationTask::SetRenderer(Renderer renderer)
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetRenderer [%p]\n", this);
 }
 
-void VectorAnimationTask::RequestLoad(const VisualUrl& url, bool synchronousLoading)
+void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, bool synchronousLoading)
 {
-  mImageUrl = url;
+  mImageUrl           = url;
+  mEncodedImageBuffer = encodedImageBuffer;
 
   if(!synchronousLoading)
   {
@@ -459,6 +487,11 @@ void VectorAnimationTask::GetLayerInfo(Property::Map& map) const
   mVectorRenderer.GetLayerInfo(map);
 }
 
+void VectorAnimationTask::GetMarkerInfo(Property::Map& map) const
+{
+  mVectorRenderer.GetMarkerInfo(map);
+}
+
 VectorAnimationTask::ResourceReadySignalType& VectorAnimationTask::ResourceReadySignal()
 {
   return mResourceReadySignal;
index c3ac749..367fc8a 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/devel-api/adaptor-framework/vector-animation-renderer.h>
 #include <dali/devel-api/threading/conditional-wait.h>
 #include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/property-array.h>
 #include <chrono>
@@ -162,9 +163,10 @@ public:
    * @brief Requests to load the animation file.
    *
    * @param[in] url The url of the vector animation file
+   * @param[in] encodedImageBuffer The resource buffer if required.
    * @param[in] synchronousLoading True if the url should be loaded synchronously
    */
-  void RequestLoad(const VisualUrl& url, bool synchronousLoading);
+  void RequestLoad(const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, bool synchronousLoading);
 
   /**
    * @brief Queries whether loading is requested.
@@ -216,6 +218,12 @@ public:
   void GetLayerInfo(Property::Map& map) const;
 
   /**
+   * @brief Gets the all marker information.
+   * @param[out] map The marker information
+   */
+  void GetMarkerInfo(Property::Map& map) const;
+
+  /**
    * @brief Connect to this signal to be notified when the resource is ready.
    * @return The signal to connect to.
    */
@@ -357,6 +365,7 @@ private:
   };
 
   VisualUrl                            mImageUrl;
+  EncodedImageBuffer                   mEncodedImageBuffer;
   VectorAnimationRenderer              mVectorRenderer;
   std::vector<AnimationData>           mAnimationData[2];
   VectorAnimationThread&               mVectorAnimationThread;
index 5bf92a4..9939dc7 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "svg-task.h"
+#include <dali-toolkit/internal/visuals/svg/svg-task.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/file-loader.h>
@@ -64,9 +64,10 @@ VectorImageRenderer SvgTask::GetRenderer()
   return mVectorRenderer;
 }
 
-SvgLoadingTask::SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi, CallbackBase* callback)
+SvgLoadingTask::SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback)
 : SvgTask(vectorRenderer, callback, url.GetProtocolType() == VisualUrl::ProtocolType::REMOTE ? AsyncTask::PriorityType::LOW : AsyncTask::PriorityType::HIGH),
   mImageUrl(url),
+  mEncodedImageBuffer(encodedImageBuffer),
   mDpi(dpi)
 {
 }
@@ -89,7 +90,8 @@ void SvgLoadingTask::Process()
   {
     std::ostringstream oss;
     oss << "[url:" << mImageUrl.GetUrl() << "]";
-    DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_SVG_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_SVG_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("BEGIN: DALI_SVG_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 
@@ -97,25 +99,35 @@ void SvgLoadingTask::Process()
 
   Dali::Vector<uint8_t> buffer;
 
-  if(!mImageUrl.IsLocalResource())
+  if(mEncodedImageBuffer)
   {
-    if(!Dali::FileLoader::DownloadFileSynchronously(mImageUrl.GetUrl(), buffer))
+    // Copy raw buffer.
+    // TODO : Can't we load svg without copy buffer in future?
+    buffer = mEncodedImageBuffer.GetRawBuffer();
+
+    // We don't need to hold image buffer anymore.
+    mEncodedImageBuffer.Reset();
+  }
+  else if(mImageUrl.IsLocalResource())
+  {
+    if(!Dali::FileLoader::ReadFile(mImageUrl.GetUrl(), buffer))
     {
-      DALI_LOG_ERROR("Failed to download file! [%s]\n", mImageUrl.GetUrl().c_str());
+      DALI_LOG_ERROR("Failed to read file! [%s]\n", mImageUrl.GetUrl().c_str());
       loadFailed = true;
     }
   }
   else
   {
-    if(!Dali::FileLoader::ReadFile(mImageUrl.GetUrl(), buffer))
+    if(!Dali::FileLoader::DownloadFileSynchronously(mImageUrl.GetUrl(), buffer))
     {
-      DALI_LOG_ERROR("Failed to read file! [%s]\n", mImageUrl.GetUrl().c_str());
+      DALI_LOG_ERROR("Failed to download file! [%s]\n", mImageUrl.GetUrl().c_str());
       loadFailed = true;
     }
   }
 
   if(!loadFailed)
   {
+    buffer.Reserve(buffer.Count() + 1u);
     buffer.PushBack('\0');
 
     if(!mVectorRenderer.Load(buffer, mDpi))
@@ -132,7 +144,8 @@ void SvgLoadingTask::Process()
     std::ostringstream oss;
     oss << "[success:" << mHasSucceeded << " ";
     oss << "url:" << mImageUrl.GetUrl() << "]";
-    DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_SVG_LOADING_TASK", oss.str().c_str());
+    // DALI_TRACE_END(gTraceFilter, "DALI_SVG_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
+    DALI_LOG_RELEASE_INFO("END: DALI_SVG_LOADING_TASK %s", oss.str().c_str());
   }
 #endif
 }
index beeaee4..8b421d4 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
 #include <memory>
 
 // INTERNAL INCLUDES
@@ -112,10 +113,11 @@ public:
    * Constructor
    * @param[in] vectorRenderer The vector rasterizer.
    * @param[in] url The URL to svg resource to use.
+   * @param[in] encodedImageBuffer The resource buffer if required.
    * @param[in] dpi The DPI of the screen.
    * @param[in] callback The callback that is called when the operation is completed.
    */
-  SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi, CallbackBase* callback);
+  SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback);
 
   /**
    * Destructor.
@@ -141,8 +143,9 @@ private:
   SvgLoadingTask& operator=(const SvgLoadingTask& task) = delete;
 
 private:
-  VisualUrl mImageUrl;
-  float     mDpi;
+  VisualUrl          mImageUrl;
+  EncodedImageBuffer mEncodedImageBuffer;
+  float              mDpi;
 };
 
 class SvgRasterizingTask : public SvgTask
index 33b7781..8d64c8f 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "svg-visual.h"
+#include <dali-toolkit/internal/visuals/svg/svg-visual.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
@@ -92,6 +92,12 @@ SvgVisual::~SvgVisual()
     {
       Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
     }
+
+    if(mImageUrl.IsBufferResource())
+    {
+      TextureManager& textureManager = mFactoryCache.GetTextureManager();
+      textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
+    }
   }
 }
 
@@ -105,9 +111,22 @@ void SvgVisual::OnInitialize()
   Vector2 dpi     = Stage::GetCurrent().GetDpi();
   float   meanDpi = (dpi.height + dpi.width) * 0.5f;
 
-  mLoadingTask = new SvgLoadingTask(mVectorRenderer, mImageUrl, meanDpi, MakeCallback(this, &SvgVisual::ApplyRasterizedImage));
+  EncodedImageBuffer encodedImageBuffer;
+
+  if(mImageUrl.IsBufferResource())
+  {
+    // Increase reference count of External Resources :
+    // EncodedImageBuffer.
+    // Reference count will be decreased at destructor of the visual.
+    TextureManager& textureManager = mFactoryCache.GetTextureManager();
+    textureManager.UseExternalResource(mImageUrl.GetUrl());
+
+    encodedImageBuffer = textureManager.GetEncodedImageBuffer(mImageUrl.GetUrl());
+  }
+
+  mLoadingTask = new SvgLoadingTask(mVectorRenderer, mImageUrl, encodedImageBuffer, meanDpi, MakeCallback(this, &SvgVisual::ApplyRasterizedImage));
 
-  if(IsSynchronousLoadingRequired() && mImageUrl.IsLocalResource())
+  if(IsSynchronousLoadingRequired() && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()))
   {
     mLoadingTask->Process();
     if(!mLoadingTask->HasSucceeded())
index b7bdbfb..b3801f6 100644 (file)
@@ -53,7 +53,7 @@ namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const int CUSTOM_PROPERTY_COUNT(5); // anim,premul,size,offset,multicol
+const int CUSTOM_PROPERTY_COUNT(2); // uTextColorAnimatable, uHasMultipleTextColors
 
 /**
  * Return Property index for the given string key
@@ -256,6 +256,7 @@ TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
+  mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
   mAnimatableTextColorPropertyIndex(Property::INVALID_INDEX),
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
   mRendererUpdateNeeded(false)
@@ -275,6 +276,7 @@ void TextVisual::OnInitialize()
 
   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
+  mHasMultipleTextColorsIndex = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", false);
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -643,7 +645,7 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
 
   // Enable the pre-multiplied alpha to improve the text quality
   renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
-  renderer.RegisterProperty(PREMULTIPLIED_ALPHA, 1.0f);
+  renderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, true);
 
   // Set size and offset for the tiling.
   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
@@ -672,7 +674,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     mImpl->mRenderer.SetTextures(textureSet);
     //Register transform properties
     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
-    mImpl->mRenderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(hasMultipleTextColors));
+    mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(hasMultipleTextColors));
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
 
     mRendererList.push_back(mImpl->mRenderer);
index 1b1930d..7be059c 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_TEXT_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -315,6 +315,7 @@ private:
   WeakHandle<Actor> mControl;                          ///< The control where the renderer is added.
   Constraint        mColorConstraint{};                ///< Color constraint
   Constraint        mOpacityConstraint{};              ///< Opacity constraint
+  Property::Index   mHasMultipleTextColorsIndex;       ///< The index of uHasMultipleTextColors proeprty.
   Property::Index   mAnimatableTextColorPropertyIndex; ///< The index of animatable text color property registered by the control.
   Property::Index   mTextColorAnimatableIndex;         ///< The index of uTextColorAnimatable property.
   bool              mRendererUpdateNeeded : 1;         ///< The flag to indicate whether the renderer needs to be updated.
index fc19714..93c1e78 100644 (file)
@@ -233,9 +233,9 @@ VisualUrl::VisualUrl(const std::string& url)
   if(!url.empty())
   {
     mLocation = ResolveLocation(url);
-    if(VisualUrl::TEXTURE != mLocation && VisualUrl::BUFFER != mLocation)
+    if(VisualUrl::TEXTURE != mLocation)
     {
-      // TEXTURE and BUFFER location url doesn't need type resolving, REGULAR_IMAGE is fine
+      // TEXTURE location url doesn't need type resolving, REGULAR_IMAGE is fine
       mType = ResolveType(url);
     }
   }
@@ -328,14 +328,19 @@ std::string VisualUrl::GetLocation() const
   return GetLocation(mUrl);
 }
 
+std::string VisualUrl::GetLocationWithoutExtension() const
+{
+  return GetLocationWithoutExtension(mUrl);
+}
+
 std::string VisualUrl::CreateTextureUrl(const std::string& location)
 {
   return "dali://" + location;
 }
 
-std::string VisualUrl::CreateBufferUrl(const std::string& location)
+std::string VisualUrl::CreateBufferUrl(const std::string& location, const std::string_view& extension)
 {
-  return "enbuf://" + location;
+  return "enbuf://" + location + std::string(extension);
 }
 
 VisualUrl::ProtocolType VisualUrl::GetProtocolType(const std::string& url)
@@ -353,6 +358,18 @@ std::string VisualUrl::GetLocation(const std::string& url)
   return url;
 }
 
+std::string VisualUrl::GetLocationWithoutExtension(const std::string& url)
+{
+  const auto location = url.find("://");
+  if(std::string::npos != location)
+  {
+    const auto extension = url.find_last_of("."); // Find last position of '.' keyword.
+    const auto locationLength = extension != std::string::npos ? extension - (location + 3u) : std::string::npos;
+    return url.substr(location + 3u, locationLength); // 3 characters forwards from the start of ://, and end of last '.' keyword.
+  }
+  return url;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 9a85ee3..246e4cf 100644 (file)
@@ -20,6 +20,7 @@
 // EXTERNAL INCLUDES
 #include <cstdint>
 #include <string>
+#include <string_view>
 
 namespace Dali
 {
@@ -143,6 +144,11 @@ public:
   std::string GetLocation() const;
 
   /**
+   * @return the location part of the url without extension
+   */
+  std::string GetLocationWithoutExtension() const;
+
+  /**
    * Helper to create a URL of type TEXTURE
    * @param location the location of the texture
    * @return the Url
@@ -151,10 +157,11 @@ public:
 
   /**
    * Helper to create a URL of type BUFFER
-   * @param location the location of the texture
+   * @param[in] location the location of the texture
+   * @param[in] extension the extension of url
    * @return the Url
    */
-  static std::string CreateBufferUrl(const std::string& location);
+  static std::string CreateBufferUrl(const std::string& location, const std::string_view& extension);
 
   /**
    * Helper to get a ProtocolType from url
@@ -170,6 +177,13 @@ public:
    */
   static std::string GetLocation(const std::string& url);
 
+  /**
+   * Helper to get a location from url without extension
+   * @param[in] url the location of the texture
+   * @return the location without extension
+   */
+  static std::string GetLocationWithoutExtension(const std::string& url);
+
 private:
   std::string  mUrl;
   Type         mType;
index 30096cb..f8a452e 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 2;
-const unsigned int TOOLKIT_MICRO_VERSION = 51;
+const unsigned int TOOLKIT_MICRO_VERSION = 52;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index ea0f852..591be97 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.2.51
+Version:    2.2.52
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT