From: Richard Huang Date: Thu, 26 Oct 2023 14:10:53 +0000 (+0100) Subject: Support glTF extention: KHR_mesh_quantization X-Git-Tag: accepted/tizen/unified/20231107.172907~3^2~3^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F22%2F300522%2F6;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git Support glTF extention: KHR_mesh_quantization Change-Id: I23fd7f3f481590f5716452813666cb31161bc8ae --- diff --git a/automated-tests/resources/AnimatedMorphCubeQuantized.bin b/automated-tests/resources/AnimatedMorphCubeQuantized.bin new file mode 100644 index 0000000000..7520d98ed9 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 index 0000000000..9d6d3644a1 --- /dev/null +++ b/automated-tests/resources/AnimatedMorphCubeQuantized.gltf @@ -0,0 +1,259 @@ +{ + "buffers":[ + { + "uri":"AnimatedMorphCubeQuantized.bin", + "byteLength":1700 + } + ], + "asset":{ + "version":"2.0", + "generator":"gltfpack 0.13" + }, + "extensionsUsed":[ + "KHR_mesh_quantization" + ], + "extensionsRequired":[ + "KHR_mesh_quantization" + ], + "bufferViews":[ + { + "buffer":0, + "byteOffset":0, + "byteLength":288, + "byteStride":4, + "target":34962 + }, + { + "buffer":0, + "byteOffset":288, + "byteLength":576, + "byteStride":8, + "target":34962 + }, + { + "buffer":0, + "byteOffset":864, + "byteLength":72, + "target":34963 + }, + { + "buffer":0, + "byteOffset":936, + "byteLength":508 + }, + { + "buffer":0, + "byteOffset":1444, + "byteLength":254 + } + ], + "accessors":[ + { + "bufferView":0, + "byteOffset":0, + "componentType":5120, + "count":24, + "type":"VEC3", + "normalized":true + }, + { + "bufferView":1, + "byteOffset":0, + "componentType":5123, + "count":24, + "type":"VEC3", + "min":[ + 0, + 5451, + 0 + ], + "max":[ + 5481, + 10932, + 5481 + ] + }, + { + "bufferView":0, + "byteOffset":96, + "componentType":5120, + "count":24, + "type":"VEC3", + "normalized":true + }, + { + "bufferView":1, + "byteOffset":192, + "componentType":5122, + "count":24, + "type":"VEC3", + "min":[ + 0, + 0, + 0 + ], + "max":[ + 0, + 5188, + 0 + ] + }, + { + "bufferView":0, + "byteOffset":192, + "componentType":5120, + "count":24, + "type":"VEC3", + "normalized":true + }, + { + "bufferView":1, + "byteOffset":384, + "componentType":5122, + "count":24, + "type":"VEC3", + "min":[ + 0, + 0, + 0 + ], + "max":[ + 0, + 5451, + 0 + ] + }, + { + "bufferView":2, + "byteOffset":0, + "componentType":5123, + "count":36, + "type":"SCALAR" + }, + { + "bufferView":3, + "byteOffset":0, + "componentType":5126, + "count":127, + "type":"SCALAR", + "min":[ + 0 + ], + "max":[ + 4.19999981 + ] + }, + { + "bufferView":4, + "byteOffset":0, + "componentType":5121, + "count":254, + "type":"SCALAR", + "normalized":true + } + ], + "materials":[ + { + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.603827417, + 0.603827417, + 0.603827417, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "NORMAL":0, + "POSITION":1 + }, + "mode":4, + "targets":[ + { + "NORMAL":2, + "POSITION":3 + }, + { + "NORMAL":4, + "POSITION":5 + } + ], + "indices":6, + "material":0 + } + ], + "weights":[ + 0, + 0 + ] + } + ], + "animations":[ + { + "name":"Square", + "samplers":[ + { + "input":7, + "output":8 + } + ], + "channels":[ + { + "sampler":0, + "target":{ + "node":0, + "path":"weights" + } + } + ] + } + ], + "nodes":[ + { + "mesh":0, + "translation":[ + -0.0100000044, + -0.0298908409, + -0.00999999978 + ], + "scale":[ + 3.64900689e-06, + 3.64900689e-06, + 3.64900689e-06 + ] + }, + { + "name":"AnimatedMorphCube", + "rotation":[ + 0, + 0.707106709, + -0.707106829, + 0 + ], + "scale":[ + 100, + 100, + 100 + ], + "children":[ + 0 + ] + } + ], + "scenes":[ + { + "nodes":[ + 1 + ] + } + ], + "scene":0 +} diff --git a/automated-tests/resources/AvocadoQuantized.bin b/automated-tests/resources/AvocadoQuantized.bin new file mode 100644 index 0000000000..7cdbdf057d 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 index 0000000000..2be992f34e --- /dev/null +++ b/automated-tests/resources/AvocadoQuantized.gltf @@ -0,0 +1,221 @@ +{ + "buffers":[ + { + "uri":"AvocadoQuantized.bin", + "byteLength":12212 + } + ], + "asset":{ + "version":"2.0", + "generator":"gltfpack 0.13" + }, + "extensionsUsed":[ + "KHR_mesh_quantization", + "KHR_texture_transform" + ], + "extensionsRequired":[ + "KHR_mesh_quantization" + ], + "bufferViews":[ + { + "buffer":0, + "byteOffset":0, + "byteLength":1624, + "byteStride":4, + "target":34962 + }, + { + "buffer":0, + "byteOffset":1624, + "byteLength":1624, + "byteStride":4, + "target":34962 + }, + { + "buffer":0, + "byteOffset":3248, + "byteLength":1624, + "byteStride":4, + "target":34962 + }, + { + "buffer":0, + "byteOffset":4872, + "byteLength":3248, + "byteStride":8, + "target":34962 + }, + { + "buffer":0, + "byteOffset":8120, + "byteLength":4092, + "target":34963 + } + ], + "accessors":[ + { + "bufferView":0, + "byteOffset":0, + "componentType":5123, + "count":406, + "type":"VEC2" + }, + { + "bufferView":1, + "byteOffset":0, + "componentType":5120, + "count":406, + "type":"VEC3", + "normalized":true + }, + { + "bufferView":2, + "byteOffset":0, + "componentType":5120, + "count":406, + "type":"VEC4", + "normalized":true + }, + { + "bufferView":3, + "byteOffset":0, + "componentType":5123, + "count":406, + "type":"VEC3", + "min":[ + 0, + 0, + 0 + ], + "max":[ + 11086, + 16383, + 7194 + ] + }, + { + "bufferView":4, + "byteOffset":0, + "componentType":5123, + "count":2046, + "type":"SCALAR" + } + ], + "images":[ + { + "uri":"Avocado_baseColor.png" + }, + { + "uri":"Avocado_roughnessMetallic.png" + }, + { + "uri":"Avocado_normal.png" + } + ], + "textures":[ + { + "source":0 + }, + { + "source":1 + }, + { + "source":2 + } + ], + "materials":[ + { + "name":"2256_Avocado_d", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0, + "texCoord":0, + "extensions":{ + "KHR_texture_transform":{ + "offset":[ + 0.00678020436, + 0.00298196077 + ], + "scale":[ + 0.000238270484, + 0.000242341906 + ] + } + } + }, + "metallicRoughnessTexture":{ + "index":1, + "texCoord":0, + "extensions":{ + "KHR_texture_transform":{ + "offset":[ + 0.00678020436, + 0.00298196077 + ], + "scale":[ + 0.000238270484, + 0.000242341906 + ] + } + } + } + }, + "normalTexture":{ + "index":2, + "texCoord":0, + "extensions":{ + "KHR_texture_transform":{ + "offset":[ + 0.00678020436, + 0.00298196077 + ], + "scale":[ + 0.000238270484, + 0.000242341906 + ] + } + } + } + } + ], + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "TEXCOORD_0":0, + "NORMAL":1, + "TANGENT":2, + "POSITION":3 + }, + "mode":4, + "indices":4, + "material":0 + } + ] + } + ], + "nodes":[ + { + "mesh":0, + "translation":[ + -0.0212809108, + -4.77385511e-05, + -0.0138090011 + ], + "scale":[ + 3.839089e-06, + 3.839089e-06, + 3.839089e-06 + ] + } + ], + "scenes":[ + { + "nodes":[ + 0 + ] + } + ], + "scene":0 +} diff --git a/automated-tests/resources/Avocado_baseColor.png b/automated-tests/resources/Avocado_baseColor.png new file mode 100644 index 0000000000..38630edf8d 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 index 0000000000..73f90d5979 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 index 0000000000..97c7f73539 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 index 0000000000..0bebaadc18 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 index 0000000000..2147577f65 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 index 0000000000..70d8410c12 --- /dev/null +++ b/automated-tests/resources/CesiumMilkTruckQuantized.gltf @@ -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 index 0000000000..fce9aac360 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 index 0000000000..4e3e2fe40c --- /dev/null +++ b/automated-tests/resources/MorphPrimitivesTestQuantized.gltf @@ -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 index 0000000000..291b43caea 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 index 0000000000..83668d6fe7 --- /dev/null +++ b/automated-tests/resources/UnsupportedExtension.gltf @@ -0,0 +1,89 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0, 1] + } + ], + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "UnsupportedExtension.bin", + "byteLength" : 80 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 72, + "byteStride" : 12, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 36, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 0.0, 0.0, 1.0 ], + "min" : [ 0.0, 0.0, 1.0 ] + } + ], + "asset" : { + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_invalid_extension" + ], + "extensionsRequired": [ + "KHR_invalid_extension" + ] +} diff --git a/automated-tests/resources/uv_texture.jpg b/automated-tests/resources/uv_texture.jpg new file mode 100644 index 0000000000..7f63873f9f Binary files /dev/null and b/automated-tests/resources/uv_texture.jpg differ diff --git a/automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp b/automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp index 723c3aeb2d..4b93f7f599 100644 --- a/automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp +++ b/automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp @@ -55,8 +55,7 @@ namespace { struct Context { - ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) - { + ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) { return TEST_RESOURCE_DIR "/"; }; @@ -98,7 +97,7 @@ struct ExceptionMessageStartsWith } // namespace -int UtcDaliGltfLoaderFailedToLoad(void) +int UtcDaliGltfLoaderFailedToLoad1(void) { Context ctx; @@ -121,6 +120,36 @@ int UtcDaliGltfLoaderFailedToLoad(void) END_TEST; } +int UtcDaliGltfLoaderFailedToLoad2(void) +{ + Context ctx; + + try + { + DALI_TEST_EQUAL(ctx.loader.LoadModel(TEST_RESOURCE_DIR "/UnsupportedExtension.gltf", ctx.loadResult), false); + } + catch(...) + { + printf("Unsupported glTF extension required.\n"); + } + + DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size()); + DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount()); + + DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size()); + DALI_TEST_EQUAL(0, ctx.resources.mShaders.size()); + DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size()); + + DALI_TEST_EQUAL(0, ctx.cameras.size()); + DALI_TEST_EQUAL(0, ctx.lights.size()); + DALI_TEST_EQUAL(0, ctx.animations.size()); + DALI_TEST_EQUAL(0, ctx.animationGroups.size()); + + END_TEST; +} + int UtcDaliGltfLoaderFailedToParse(void) { Context ctx; @@ -185,89 +214,87 @@ int UtcDaliGltfLoaderSuccess1(void) auto& materials = ctx.resources.mMaterials; DALI_TEST_EQUAL(2u, materials.size()); const MaterialDefinition materialGroundTruth[]{ - { - nullptr, - MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION | - MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR | - MaterialDefinition::GLTF_CHANNELS | (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT), - 0, - Color::WHITE, - 1.f, - 0.f, - Vector4(1.000, 0.766, 0.336, 1.0), - 1.f, - 1.f, - Vector3(0.2, 0.1, 0.0), - 1.0f, - 0.0f, - 0.5f, - Vector3(0, 0, 1), - true, - false, - true, - false, - Scene3D::Material::AlphaModeType::MASK, - true, - true, - true, - { - { - MaterialDefinition::ALBEDO, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - { - MaterialDefinition::NORMAL, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - { - MaterialDefinition::OCCLUSION, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - { - MaterialDefinition::EMISSIVE, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - { - MaterialDefinition::SPECULAR, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - { - MaterialDefinition::SPECULAR_COLOR, - { - "AnimatedCube_BaseColor.png", - SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), - ImageDimensions(256, 256), - SamplingMode::BOX_THEN_NEAREST, - }, - }, - }, - nullptr - }, + {nullptr, + MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION | + MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR | + MaterialDefinition::GLTF_CHANNELS | (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT), + 0, + Color::WHITE, + 1.f, + 0.f, + Vector4(1.000, 0.766, 0.336, 1.0), + 1.f, + 1.f, + Vector3(0.2, 0.1, 0.0), + 1.0f, + 0.0f, + 0.5f, + Vector3(0, 0, 1), + true, + false, + true, + false, + Scene3D::Material::AlphaModeType::MASK, + true, + true, + true, + { + { + MaterialDefinition::ALBEDO, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + { + MaterialDefinition::NORMAL, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + { + MaterialDefinition::OCCLUSION, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + { + MaterialDefinition::EMISSIVE, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + { + MaterialDefinition::SPECULAR, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + { + MaterialDefinition::SPECULAR_COLOR, + { + "AnimatedCube_BaseColor.png", + SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT), + ImageDimensions(256, 256), + SamplingMode::BOX_THEN_NEAREST, + }, + }, + }, + nullptr}, { nullptr, MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | @@ -458,7 +485,7 @@ int UtcDaliGltfLoaderSuccess1(void) int UtcDaliGltfLoaderSuccess2(void) { - Context ctx; + Context ctx; ctx.loader.LoadModel(TEST_RESOURCE_DIR "/AnimatedCubeStride.gltf", ctx.loadResult); @@ -487,8 +514,7 @@ int UtcDaliGltfLoaderSuccessShort(void) TestApplication app; const std::string resourcePath = TEST_RESOURCE_DIR "/"; - auto pathProvider = [resourcePath](ResourceType::Value) - { + auto pathProvider = [resourcePath](ResourceType::Value) { return resourcePath; }; @@ -508,6 +534,34 @@ int UtcDaliGltfLoaderSuccessShort(void) "MRendererTest", "SimpleSparseAccessor", "AnimatedCube", + /** + * For the Avocado glTF file and its Assets + * Donated by Microsoft for glTF testing + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Avocado/glTF-Quantized + */ + "AvocadoQuantized", + /** + * For the AnimatedMorphCube glTF file and its Assets + * Donated by Microsoft for glTF testing + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/AnimatedMorphCube/glTF-Quantized + */ + "AnimatedMorphCubeQuantized", + /** + * For the MorphPrimitivesTest glTF file and its Assets + * Created by @ft-lab + * Licensed under the terms of the CC BY 4.0 license: https://creativecommons.org/licenses/by/4.0/ + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/MorphPrimitivesTest/glTF + * Modified using gltfpack 0.18. + */ + "MorphPrimitivesTestQuantized", + /** + * For the CesiumMilkTruck glTF file and its Assets + * Donated by Cesium for glTF testing + * Licensed under the terms of the CC BY 4.0 license: http://creativecommons.org/licenses/by/4.0/ + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/CesiumMilkTruck/glTF + * Modified using gltfpack 0.18. + */ + "CesiumMilkTruckQuantized", }) { Context ctx; @@ -572,7 +626,7 @@ int UtcDaliGltfLoaderSuccessShort(void) int UtcDaliGltfLoaderMRendererTest(void) { Context ctx; - auto& resources = ctx.resources; + auto& resources = ctx.resources; ctx.loader.LoadModel(TEST_RESOURCE_DIR "/MRendererTest.gltf", ctx.loadResult); @@ -585,8 +639,8 @@ int UtcDaliGltfLoaderMRendererTest(void) DALI_TEST_EQUAL(scene.GetNodeCount(), 1u); Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager(); - ViewProjection viewProjection; - Transforms xforms{ + ViewProjection viewProjection; + Transforms xforms{ MatrixStack{}, viewProjection}; NodeDefinition::CreateParams nodeParams{ @@ -633,7 +687,7 @@ int UtcDaliGltfLoaderMRendererTest(void) int UtcDaliGltfLoaderAnimationLoadingTest(void) { TestApplication app; - Context ctx; + Context ctx; auto& resources = ctx.resources; @@ -644,8 +698,8 @@ int UtcDaliGltfLoaderAnimationLoadingTest(void) DALI_TEST_EQUAL(roots.size(), 1u); Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager(); - ViewProjection viewProjection; - Transforms xforms{ + ViewProjection viewProjection; + Transforms xforms{ MatrixStack{}, viewProjection}; NodeDefinition::CreateParams nodeParams{ @@ -695,8 +749,8 @@ int UtcDaliGltfLoaderImageFromBufferView(void) DALI_TEST_EQUAL(roots.size(), 1u); Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager(); - ViewProjection viewProjection; - Transforms xforms{ + ViewProjection viewProjection; + Transforms xforms{ MatrixStack{}, viewProjection}; NodeDefinition::CreateParams nodeParams{ @@ -746,8 +800,8 @@ int UtcDaliGltfLoaderUint8Indices(void) DALI_TEST_EQUAL(roots.size(), 1u); Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager(); - ViewProjection viewProjection; - Transforms xforms{ + ViewProjection viewProjection; + Transforms xforms{ MatrixStack{}, viewProjection}; NodeDefinition::CreateParams nodeParams{ @@ -783,3 +837,104 @@ int UtcDaliGltfLoaderUint8Indices(void) END_TEST; } + +int UtcDaliGltfLoaderQuantizedMesh(void) +{ + Context ctx; + + auto& resources = ctx.resources; + + /** + * For the Avocado glTF file and its Assets + * Donated by Microsoft for glTF testing + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Avocado/glTF-Quantized + */ + ctx.loader.LoadModel(TEST_RESOURCE_DIR "/AvocadoQuantized.gltf", ctx.loadResult); + + auto& scene = ctx.scene; + DALI_TEST_EQUAL(1u, scene.GetRoots().size()); + DALI_TEST_EQUAL(1u, scene.GetNodeCount()); + + auto& roots = scene.GetRoots(); + DALI_TEST_EQUAL(roots.size(), 1u); + + Scene3D::Loader::ShaderManagerPtr shaderManager = new Scene3D::Loader::ShaderManager(); + ViewProjection viewProjection; + Transforms xforms{ + MatrixStack{}, + viewProjection}; + NodeDefinition::CreateParams nodeParams{ + resources, + xforms, + shaderManager, + }; + + Customization::Choices choices; + + TestApplication app; + + Actor root = Actor::New(); + SetActorCentered(root); + for(auto iRoot : roots) + { + auto resourceRefs = resources.CreateRefCounter(); + scene.CountResourceRefs(iRoot, choices, resourceRefs); + resources.mReferenceCounts = std::move(resourceRefs); + resources.CountEnvironmentReferences(); + resources.LoadResources(ctx.pathProvider); + if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams)) + { + scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables)); + scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables)); + root.Add(actor); + } + } + + auto& meshes = ctx.resources.mMeshes; + DALI_TEST_EQUAL(1u, meshes.size()); + + auto& md = meshes[0u].first; + + DALI_TEST_EQUAL(MeshDefinition::Flags::U16_POSITION | MeshDefinition::Flags::S8_NORMAL | MeshDefinition::Flags::S8_TANGENT | MeshDefinition::Flags::U16_TEXCOORD, md.mFlags); + + DALI_TEST_EQUAL(true, md.mPositions.IsDefined()); + DALI_TEST_EQUAL(false, md.mPositions.mNormalized); + DALI_TEST_EQUAL(sizeof(uint16_t) * 3, md.mPositions.mBlob.mElementSizeHint); + DALI_TEST_EQUAL(true, md.mPositions.mBlob.IsDefined()); + DALI_TEST_EQUAL(2436, md.mPositions.mBlob.mLength); + DALI_TEST_EQUAL(3u, md.mPositions.mBlob.mMin.size()); + DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[0]); + DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[1]); + DALI_TEST_EQUAL(0.0f, md.mPositions.mBlob.mMin[2]); + DALI_TEST_EQUAL(3u, md.mPositions.mBlob.mMax.size()); + DALI_TEST_EQUAL(11086.0f, md.mPositions.mBlob.mMax[0]); + DALI_TEST_EQUAL(16383.0f, md.mPositions.mBlob.mMax[1]); + DALI_TEST_EQUAL(7194.0f, md.mPositions.mBlob.mMax[2]); + + DALI_TEST_EQUAL(true, md.mNormals.IsDefined()); + DALI_TEST_EQUAL(true, md.mNormals.mNormalized); + DALI_TEST_EQUAL(sizeof(int8_t) * 3, md.mNormals.mBlob.mElementSizeHint); + DALI_TEST_EQUAL(true, md.mNormals.mBlob.IsDefined()); + DALI_TEST_EQUAL(1218, md.mNormals.mBlob.mLength); + DALI_TEST_EQUAL(0u, md.mNormals.mBlob.mMin.size()); + DALI_TEST_EQUAL(0u, md.mNormals.mBlob.mMax.size()); + + DALI_TEST_EQUAL(true, md.mTangents.IsDefined()); + DALI_TEST_EQUAL(true, md.mTangents.mNormalized); + DALI_TEST_EQUAL(Property::VECTOR4, md.mTangentType); + DALI_TEST_EQUAL(sizeof(int8_t) * 4, md.mTangents.mBlob.mElementSizeHint); + DALI_TEST_EQUAL(true, md.mTangents.mBlob.IsDefined()); + DALI_TEST_EQUAL(1624, md.mTangents.mBlob.mLength); + DALI_TEST_EQUAL(0u, md.mTangents.mBlob.mMin.size()); + DALI_TEST_EQUAL(0u, md.mTangents.mBlob.mMax.size()); + + DALI_TEST_EQUAL(true, md.mTexCoords.IsDefined()); + DALI_TEST_EQUAL(false, md.mTexCoords.mNormalized); + DALI_TEST_EQUAL(sizeof(uint16_t) * 2, md.mTexCoords.mBlob.mElementSizeHint); + DALI_TEST_EQUAL(true, md.mTexCoords.mBlob.IsDefined()); + DALI_TEST_EQUAL(1624, md.mTexCoords.mBlob.mLength); + DALI_TEST_EQUAL(0u, md.mTexCoords.mBlob.mMin.size()); + DALI_TEST_EQUAL(0u, md.mTexCoords.mBlob.mMax.size()); + + END_TEST; +} diff --git a/dali-scene3d/internal/loader/gltf2-asset.h b/dali-scene3d/internal/loader/gltf2-asset.h index 7d2c332ada..8488c1eb29 100644 --- a/dali-scene3d/internal/loader/gltf2-asset.h +++ b/dali-scene3d/internal/loader/gltf2-asset.h @@ -28,6 +28,7 @@ // INTERNAL INCLUDES #include #include +#include #define ENUM_STRING_MAPPING(t, x) \ { \ @@ -563,6 +564,16 @@ struct Scene : Named std::vector> mNodes; }; +enum ExtensionFlags : uint32_t +{ + NONE = Dali::Scene3D::Loader::NthBit(0), + KHR_MESH_QUANTIZATION = Dali::Scene3D::Loader::NthBit(1), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform + KHR_TEXTURE_TRANSFORM = Dali::Scene3D::Loader::NthBit(2), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization + KHR_MATERIALS_IOR = Dali::Scene3D::Loader::NthBit(3), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior + KHR_MATERIALS_SPECULAR = Dali::Scene3D::Loader::NthBit(4), // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular + +}; + struct Document { Asset mAsset; @@ -584,6 +595,11 @@ struct Document std::vector mAnimations; + std::vector mExtensionsUsed; + std::vector mExtensionsRequired; + + uint32_t mExtensionFlags{0}; + std::vector mScenes; Ref mScene; diff --git a/dali-scene3d/internal/loader/gltf2-util.cpp b/dali-scene3d/internal/loader/gltf2-util.cpp index 1bda11a641..a6e34629cf 100644 --- a/dali-scene3d/internal/loader/gltf2-util.cpp +++ b/dali-scene3d/internal/loader/gltf2-util.cpp @@ -19,6 +19,8 @@ #include // EXTERNAL INCLUDES +#include +#include #include #include #include ///< for std::numeric_limits @@ -60,6 +62,18 @@ static struct AttributeMapping {gltf2::Attribute::WEIGHTS_0, &MeshDefinition::mWeights0, sizeof(Vector4)}, }; +static const Dali::Scripting::StringEnum EXTENSION_STRING_TABLE[] = + { + {"NONE", gltf2::ExtensionFlags::NONE}, + {"KHR_mesh_quantization", gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION}, + {"KHR_texture_transform", gltf2::ExtensionFlags::KHR_TEXTURE_TRANSFORM}, + {"KHR_materials_ior", gltf2::ExtensionFlags::KHR_MATERIALS_IOR}, + {"KHR_materials_specular", gltf2::ExtensionFlags::KHR_MATERIALS_SPECULAR}, + +}; + +static const unsigned int EXTENSION_STRING_TABLE_COUNT = sizeof(EXTENSION_STRING_TABLE) / sizeof(EXTENSION_STRING_TABLE[0]); + std::vector ReadAnimationArray(const json_value_s& j) { auto results = json::Read::Array::Read>(j); @@ -482,6 +496,12 @@ const json::Reader& GetDocumentReader() .Register(*json::MakeProperty("animations", ReadAnimationArray, &gltf2::Document::mAnimations)) + .Register(*json::MakeProperty("extensionsRequired", + json::Read::Array, + &gltf2::Document::mExtensionsRequired)) + .Register(*json::MakeProperty("extensionsUsed", + json::Read::Array, + &gltf2::Document::mExtensionsUsed)) .Register(*json::MakeProperty("scenes", json::Read::Array::Read>, &gltf2::Document::mScenes)) @@ -755,6 +775,8 @@ MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gltf2::Accessor& acc void ConvertMeshes(const gltf2::Document& document, ConversionContext& context) { + bool isQuantized = MaskMatch(document.mExtensionFlags, gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION); + uint32_t meshCount = 0; context.mMeshIds.reserve(document.mMeshes.size()); for(auto& mesh : document.mMeshes) @@ -782,8 +804,20 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context) continue; } - auto& accPositions = *positionIter->second; - meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions); + auto& accPositions = *positionIter->second; + meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions); + meshDefinition.mPositions.mNormalized = accPositions.mNormalized; + + if(isQuantized) + { + meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION; + meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_POSITION; + meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION; + meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_POSITION; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_POSITION))) || accPositions.mComponentType == gltf2::Component::FLOAT); + // glTF2 support vector4 tangent for mesh. // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview meshDefinition.mTangentType = Property::VECTOR4; @@ -797,6 +831,44 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context) auto& accessor = meshDefinition.*(attributeMapping.mAccessor); accessor = ConvertMeshPrimitiveAccessor(*iFind->second); + if(iFind->first == gltf2::Attribute::NORMAL) + { + meshDefinition.mNormals.mNormalized = iFind->second->mNormalized; + + if(isQuantized) + { + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL; + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_NORMAL))) || iFind->second->mComponentType == gltf2::Component::FLOAT); + } + if(iFind->first == gltf2::Attribute::TANGENT) + { + meshDefinition.mTangents.mNormalized = iFind->second->mNormalized; + + if(isQuantized) + { + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT; + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TANGENT))) || iFind->second->mComponentType == gltf2::Component::FLOAT); + } + if(iFind->first == gltf2::Attribute::TEXCOORD_0 || iFind->first == gltf2::Attribute::TEXCOORD_1) + { + meshDefinition.mTexCoords.mNormalized = iFind->second->mNormalized; + + if(isQuantized) + { + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TEXCOORD; + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_TEXCOORD; + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TEXCOORD; + meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_TEXCOORD; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_TEXCOORD))) || iFind->second->mComponentType == gltf2::Component::FLOAT); + } if(iFind->first == gltf2::Attribute::JOINTS_0) { meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS; @@ -849,17 +921,44 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context) auto it = target.find(gltf2::Attribute::POSITION); if(it != endIt) { - blendShape.deltas = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.deltas = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.deltas.mNormalized = it->second->mNormalized; + + if(isQuantized) + { + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION; + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(blendShape.mFlags, MeshDefinition::U16_POSITION))) || it->second->mComponentType == gltf2::Component::FLOAT); } it = target.find(gltf2::Attribute::NORMAL); if(it != endIt) { - blendShape.normals = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.normals = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.normals.mNormalized = it->second->mNormalized; + + if(isQuantized) + { + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL; + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))) || it->second->mComponentType == gltf2::Component::FLOAT); } it = target.find(gltf2::Attribute::TANGENT); if(it != endIt) { - blendShape.tangents = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.tangents = ConvertMeshPrimitiveAccessor(*it->second); + blendShape.tangents.mNormalized = it->second->mNormalized; + + if(isQuantized) + { + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT; + blendShape.mFlags |= (it->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT; + } + + DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))) || it->second->mComponentType == gltf2::Component::FLOAT); } if(!mesh.mWeights.empty()) @@ -1462,6 +1561,20 @@ bool GenerateDocument(json::unique_ptr& root, gt::Document& document, bool& isMR void ConvertGltfToContext(gt::Document& document, Gltf2Util::ConversionContext& context, bool isMRendererModel) { + for(auto& extension : document.mExtensionsRequired) + { + gltf2::ExtensionFlags flag; + if(Dali::Scripting::GetEnumeration(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); diff --git a/dali-scene3d/public-api/loader/mesh-definition.cpp b/dali-scene3d/public-api/loader/mesh-definition.cpp index 76a6fa19ee..264f2fa6b2 100644 --- a/dali-scene3d/public-api/loader/mesh-definition.cpp +++ b/dali-scene3d/public-api/loader/mesh-definition.cpp @@ -447,26 +447,144 @@ void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uin textureHeight = 1u << powHeight; } -void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers) +template +float GetNormalizedScale() +{ + return 1.0f / (std::numeric_limits::max()); +} + +template +void DequantizeData(std::vector& 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(buffer.data()); + + for(uint32_t i = 0; i < numValues; ++i) + { + *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale(), -1.0f) : *values; + + values++; + dequantizedValues++; + } +} + +void GetDequantizedData(std::vector& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized) +{ + bool dequantized = false; + + std::vector dequantizedBuffer(count * numComponents * sizeof(float)); + float* dequantizedValues = reinterpret_cast(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(buffer, dequantizedValues, numComponents * count, normalized); + dequantized = true; + } + else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD)) + { + DequantizeData(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(buffer, dequantizedValues, numComponents * count, normalized); + dequantized = true; + } + else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD)) + { + DequantizeData(buffer, dequantizedValues, numComponents * count, normalized); + dequantized = true; + } + + if(dequantized) + { + buffer = std::move(dequantizedBuffer); + } +} + +void GetDequantizedMinMax(std::vector& min, std::vector& 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(); + } + else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD)) + { + scale = GetNormalizedScale(); + } + 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(); + } + else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD)) + { + scale = GetNormalizedScale(); + } + + 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& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers) { uint32_t geometryBufferIndex = 0u; float maxDistanceSquared = 0.f; Vector3* geometryBufferV3 = reinterpret_cast(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(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(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(bufferSize / sizeof(Vector3)); + } - const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize(); std::vector buffer(bufferSize); std::vector sparseIndices{}; if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.deltas.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(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(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh. // Find the max distance to normalize the deltas. @@ -501,17 +619,44 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector= 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(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(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(bufferSize / sizeof(Vector3)); + } - const auto bufferSize = blendShape.normals.mBlob.GetBufferSize(); std::vector buffer(bufferSize); std::vector sparseIndices; if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.normals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(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(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); @@ -547,17 +692,45 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector= 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(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(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(bufferSize / sizeof(Vector3)); + } - const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize(); std::vector buffer(bufferSize); std::vector sparseIndices; if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.tangents.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(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(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); @@ -853,13 +1026,38 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& } } + uint32_t numberOfVertices = 0u; + std::vector 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(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(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(bufferSize / sizeof(Vector3)); + } + + numberOfVertices = numVector3; + std::vector buffer(bufferSize); std::string path; @@ -869,7 +1067,13 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'."; } - uint32_t numVector3 = static_cast(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(buffer.data())); @@ -892,10 +1096,31 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& auto hasNormals = mNormals.IsDefined(); if(hasNormals) { - DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) || - mNormals.mBlob.mStride >= sizeof(Vector3)) && - "Normal buffer length not a multiple of element size"); - const auto bufferSize = mNormals.mBlob.GetBufferSize(); + const auto bufferSize = mNormals.mBlob.GetBufferSize(); + uint32_t numVector3; + + if(MaskMatch(mFlags, S8_NORMAL)) + { + DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) || + mNormals.mBlob.mStride >= (sizeof(int8_t) * 3)) && + "Normal buffer length not a multiple of element size"); + numVector3 = static_cast(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(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(bufferSize / sizeof(Vector3)); + } + std::vector buffer(bufferSize); std::string path; @@ -905,9 +1130,16 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'."; } - mNormals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + GetDequantizedData(buffer, 3u, numVector3, mFlags & NORMALS_MASK, mNormals.mNormalized); - raw.mAttribs.push_back({"aNormal", Property::VECTOR3, static_cast(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(buffer.data())); + + raw.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)}); } else if(mNormals.mBlob.mLength != 0 && isTriangles) { @@ -931,10 +1163,32 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& const auto hasUvs = mTexCoords.IsDefined(); if(hasUvs) { - DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) || - mTexCoords.mBlob.mStride >= sizeof(Vector2)) && - "Normal buffer length not a multiple of element size"); - const auto bufferSize = mTexCoords.mBlob.GetBufferSize(); + const auto bufferSize = mTexCoords.mBlob.GetBufferSize(); + + uint32_t uvCount; + + if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD)) + { + DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) || + mTexCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) && + "TexCoords buffer length not a multiple of element size"); + uvCount = static_cast(bufferSize / (sizeof(uint8_t) * 2)); + } + else if(MaskMatch(mFlags, S16_TEXCOORD) || MaskMatch(mFlags, U16_TEXCOORD)) + { + DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) || + mTexCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) && + "TexCoords buffer length not a multiple of element size"); + uvCount = static_cast(bufferSize / (sizeof(uint16_t) * 2)); + } + else + { + DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) || + mTexCoords.mBlob.mStride >= sizeof(Vector2)) && + "TexCoords buffer length not a multiple of element size"); + uvCount = static_cast(bufferSize / sizeof(Vector2)); + } + std::vector buffer(bufferSize); std::string path; @@ -944,7 +1198,8 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'."; } - const auto uvCount = bufferSize / sizeof(Vector2); + GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, mTexCoords.mNormalized); + if(MaskMatch(mFlags, FLIP_UVS_VERTICAL)) { auto uv = reinterpret_cast(buffer.data()); @@ -956,6 +1211,11 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& } } + if(mTexCoords.mNormalized) + { + GetDequantizedMinMax(mTexCoords.mBlob.mMin, mTexCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK); + } + mTexCoords.mBlob.ApplyMinMax(static_cast(uvCount), reinterpret_cast(buffer.data())); raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast(uvCount), std::move(buffer)}); @@ -963,11 +1223,35 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& if(mTangents.IsDefined()) { - uint32_t propertySize = static_cast((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((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3)); + uint32_t componentCount = static_cast(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(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(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(bufferSize / propertySize); + } + std::vector buffer(bufferSize); std::string path; @@ -976,9 +1260,17 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& { ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'."; } - mTangents.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast(buffer.data())); - raw.mAttribs.push_back({"aTangent", mTangentType, static_cast(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(buffer.data())); + + raw.mAttribs.push_back({"aTangent", mTangentType, static_cast(numTangents), std::move(buffer)}); } else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles) { @@ -1086,22 +1378,31 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& blendShapesBlob.mOffset = std::numeric_limits::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(mPositions.mBlob.mLength / sizeof(Vector3)); - // Calculate the size of one buffer inside the texture. raw.mBlendShapeBufferOffset = numberOfVertices; @@ -1111,7 +1412,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& if(!mBlendShapeHeader.IsDefined()) { - CalculateTextureSize(static_cast(blendShapesBlob.mLength / sizeof(Vector3)), textureWidth, textureHeight); + CalculateTextureSize(totalTextureSize, textureWidth, textureHeight); calculateGltf2BlendShapes = true; } else diff --git a/dali-scene3d/public-api/loader/mesh-definition.h b/dali-scene3d/public-api/loader/mesh-definition.h index 7bf1f7c96b..d1016b5d47 100644 --- a/dali-scene3d/public-api/loader/mesh-definition.h +++ b/dali-scene3d/public-api/loader/mesh-definition.h @@ -45,7 +45,7 @@ struct DALI_SCENE3D_API MeshDefinition INVALID = std::numeric_limits::max() }; - enum Flags : uint16_t + enum Flags : uint32_t { FLIP_UVS_VERTICAL = NthBit(0), U32_INDICES = NthBit(1), // default is unsigned short @@ -54,6 +54,26 @@ struct DALI_SCENE3D_API MeshDefinition U8_JOINT_IDS = NthBit(4), U16_WEIGHT = NthBit(5), // default is floats U8_WEIGHT = NthBit(6), + S8_POSITION = NthBit(7), // default is floats + U8_POSITION = NthBit(8), // default is floats + S16_POSITION = NthBit(9), // default is floats + U16_POSITION = NthBit(10), // default is floats + S8_NORMAL = NthBit(11), // default is floats + S16_NORMAL = NthBit(12), // default is floats + S8_TANGENT = NthBit(13), // default is floats + S16_TANGENT = NthBit(14), // default is floats + S8_TEXCOORD = NthBit(15), // default is floats + U8_TEXCOORD = NthBit(16), // default is floats + S16_TEXCOORD = NthBit(17), // default is floats + U16_TEXCOORD = NthBit(18), // default is floats + }; + + enum FlagMasks : uint32_t + { + POSITIONS_MASK = 0x780, + NORMALS_MASK = 0x1800, + TANGENTS_MASK = 0x6000, + TEXCOORDS_MASK = 0x78000, }; enum Attributes @@ -166,6 +186,7 @@ struct DALI_SCENE3D_API MeshDefinition Blob mBlob; std::unique_ptr mSparse; Index mBufferIdx = INVALID_INDEX; + bool mNormalized{false}; Accessor() = default; @@ -198,6 +219,7 @@ struct DALI_SCENE3D_API MeshDefinition Accessor normals; Accessor tangents; float weight = 0.f; + uint32_t mFlags = 0x0; }; struct RawData