diff --git a/Apps/SampleData/models/DracoCompressed/0.bin b/Apps/SampleData/models/DracoCompressed/0.bin new file mode 100644 index 000000000000..c4996c515465 Binary files /dev/null and b/Apps/SampleData/models/DracoCompressed/0.bin differ diff --git a/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.gltf b/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.gltf new file mode 100644 index 000000000000..2132e20303b6 --- /dev/null +++ b/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.gltf @@ -0,0 +1,574 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "mesh": 0, + "children": [ + 3, + 1 + ] + }, + { + "children": [ + 2 + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + -1.352329969406128, + 0.4277220070362091, + -2.98022992950564e-8, + 1 + ] + }, + { + "mesh": 1, + "rotation": [ + 0, + 0, + 0.08848590403795242, + -0.9960774183273317 + ] + }, + { + "children": [ + 4 + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1.432669997215271, + 0.4277220070362091, + -2.98022992950564e-8, + 1 + ] + }, + { + "mesh": 1, + "rotation": [ + 0, + 0, + 0.08848590403795242, + -0.9960774183273317 + ] + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 5, + "POSITION": 6, + "TEXCOORD_0": 7 + }, + "indices": 4, + "mode": 4, + "material": 0, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 2, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TEXCOORD_0": 2 + } + } + } + }, + { + "attributes": { + "NORMAL": 9, + "POSITION": 10 + }, + "indices": 8, + "mode": 4, + "material": 1, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 3, + "attributes": { + "NORMAL": 0, + "POSITION": 1 + } + } + } + }, + { + "attributes": { + "NORMAL": 12, + "POSITION": 13 + }, + "indices": 11, + "mode": 4, + "material": 2, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 4, + "attributes": { + "NORMAL": 0, + "POSITION": 1 + } + } + } + } + ], + "name": "Cesium_Milk_Truck" + }, + { + "primitives": [ + { + "attributes": { + "NORMAL": 15, + "POSITION": 16, + "TEXCOORD_0": 17 + }, + "indices": 14, + "mode": 4, + "material": 3, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 5, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TEXCOORD_0": 2 + } + } + } + } + ], + "name": "Wheels" + } + ], + "animations": [ + { + "channels": [ + { + "sampler": 0, + "target": { + "node": 4, + "path": "rotation" + } + }, + { + "sampler": 1, + "target": { + "node": 2, + "path": "rotation" + } + } + ], + "samplers": [ + { + "input": 0, + "interpolation": "LINEAR", + "output": 1 + }, + { + "input": 2, + "interpolation": "LINEAR", + "output": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 31, + "max": [ + 1.25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 31, + "max": [ + 0, + 0, + 0.9990190863609314, + 1 + ], + "min": [ + 0, + 0, + 0, + -0.9960774183273317 + ], + "type": "VEC4" + }, + { + "bufferView": 0, + "byteOffset": 124, + "componentType": 5126, + "count": 31, + "max": [ + 1.25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 496, + "componentType": 5126, + "count": 31, + "max": [ + 0, + 0, + 0.9990190863609314, + 1 + ], + "min": [ + 0, + 0, + 0, + -0.9960774183273317 + ], + "type": "VEC4" + }, + { + "componentType": 5123, + "count": 5232, + "max": [ + 1855 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1856, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1856, + "max": [ + 2.437999963760376, + 2.5843698978424072, + 1.3960000276565552 + ], + "min": [ + -2.430910110473633, + 0.2667999863624573, + -1.3960000276565552 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1856, + "max": [ + 0.8964580297470093, + 0.997245192527771 + ], + "min": [ + 0.002956389915198088, + 0.015672028064727783 + ], + "type": "VEC2" + }, + { + "componentType": 5123, + "count": 168, + "max": [ + 71 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 72, + "max": [ + 0.957480013370514, + 0.28850099444389343, + 1 + ], + "min": [ + -1, + 0, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 72, + "max": [ + 1.6011799573898315, + 2.3545401096343994, + 1.3960000276565552 + ], + "min": [ + 0.22885000705718997, + 1.631850004196167, + -1.3960000276565552 + ], + "type": "VEC3" + }, + { + "componentType": 5123, + "count": 864, + "max": [ + 463 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 464, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 464, + "max": [ + 1.62267005443573, + 2.3919999599456787, + 1.100000023841858 + ], + "min": [ + 0.1932000070810318, + 1.5961999893188477, + -1.1100000143051147 + ], + "type": "VEC3" + }, + { + "componentType": 5123, + "count": 2304, + "max": [ + 585 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 586, + "max": [ + 0.9990389943122864, + 0.9990379810333252, + 1 + ], + "min": [ + -0.9990379810333252, + -0.9990379810333252, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 586, + "max": [ + 0.4277999997138977, + 0.4277999997138977, + 1.0579999685287476 + ], + "min": [ + -0.4277999997138977, + -0.4277999997138977, + -1.0579999685287476 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 586, + "max": [ + 0.9936569929122924, + 0.9895756244659424 + ], + "min": [ + 0.6050930023193359, + 0.00905001163482666 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0 + }, + "name": "truck" + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0, + 0.04050629958510399, + 0.021240700036287308, + 1 + ], + "metallicFactor": 0 + }, + "name": "glass" + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.06400000303983688, + 0.06400000303983688, + 0.06400000303983688, + 1 + ], + "metallicFactor": 0 + }, + "name": "window_trim" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 1 + }, + "metallicFactor": 0 + }, + "name": "wheels" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "CesiumMilkTruck.png" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 248 + }, + { + "buffer": 0, + "byteOffset": 248, + "byteLength": 992 + }, + { + "buffer": 0, + "byteOffset": 1240, + "byteLength": 7871 + }, + { + "buffer": 0, + "byteOffset": 9111, + "byteLength": 474 + }, + { + "buffer": 0, + "byteOffset": 9585, + "byteLength": 1249 + }, + { + "buffer": 0, + "byteOffset": 10834, + "byteLength": 3137 + } + ], + "buffers": [ + { + "byteLength": 13971, + "uri": "0.bin" + } + ], + "extensionsRequired": [ + "KHR_draco_mesh_compression" + ], + "extensionsUsed": [ + "KHR_draco_mesh_compression" + ] +} diff --git a/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.png b/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.png new file mode 100644 index 000000000000..390ef28f1b53 Binary files /dev/null and b/Apps/SampleData/models/DracoCompressed/CesiumMilkTruck.png differ diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index 0384b95187ff..116af0bd84f1 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -82,6 +82,11 @@ onselect : function() { createModel('../../SampleData/models/CesiumMan/Cesium_Man.glb', 0); } +}, { + text : 'Draco Compressed Model', + onselect : function() { + createModel('../../SampleData/models/DracoCompressed/CesiumMilkTruck.gltf', 0); + } }]; Sandcastle.addToolbarMenu(options); diff --git a/Source/Scene/DracoLoader.js b/Source/Scene/DracoLoader.js index 4821e4dc511c..f0d27bc66eb3 100644 --- a/Source/Scene/DracoLoader.js +++ b/Source/Scene/DracoLoader.js @@ -43,44 +43,62 @@ define([ || defined(model.extensionsUsed.KHR_draco_mesh_compression)); } - function addBufferToModelResources(model, buffer) { - var resourceBuffers = model._rendererResources.buffers; - var bufferViewId = Object.keys(resourceBuffers).length; - resourceBuffers[bufferViewId] = buffer; - model._geometryByteLength += buffer.sizeInBytes; + function addBufferToLoadResources(loadResources, typedArray) { + // Create a new id to differentiate from original glTF bufferViews + var bufferViewId = 'runtime.' + Object.keys(loadResources.createdBufferViews).length; + + var loadResourceBuffers = loadResources.buffers; + var id = Object.keys(loadResourceBuffers).length; + loadResourceBuffers[id] = typedArray; + loadResources.createdBufferViews[bufferViewId] = { + buffer : id, + byteOffset : 0, + byteLength : typedArray.byteLength + }; return bufferViewId; } function addNewVertexBuffer(typedArray, model, context) { - var vertexBuffer = Buffer.createVertexBuffer({ - context : context, - typedArray : typedArray, - usage : BufferUsage.STATIC_DRAW - }); - vertexBuffer.vertexArrayDestroyable = false; - - return addBufferToModelResources(model, vertexBuffer); + var loadResources = model._loadResources; + var id = addBufferToLoadResources(loadResources, typedArray); + loadResources.vertexBuffersToCreate.enqueue(id); + return id; } - function addNewIndexBuffer(typedArray, model, context) { - var indexBuffer = Buffer.createIndexBuffer({ - context : context, - typedArray : typedArray, - usage : BufferUsage.STATIC_DRAW, - indexDatatype : ComponentDatatype.fromTypedArray(typedArray) + function addNewIndexBuffer(indexArray, model, context) { + var typedArray = indexArray.typedArray; + var loadResources = model._loadResources; + var id = addBufferToLoadResources(loadResources, typedArray); + loadResources.indexBuffersToCreate.enqueue({ + id : id, + componentType : ComponentDatatype.fromTypedArray(typedArray) }); - indexBuffer.vertexArrayDestroyable = false; - var bufferViewId = addBufferToModelResources(model, indexBuffer); return { - bufferViewId: bufferViewId, - numberOfIndices : indexBuffer.numberOfIndices + bufferViewId : id, + numberOfIndices : indexArray.numberOfIndices }; } - function addDecodededBuffers(primitive, model, context) { - return function (result) { + function scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context) { + var taskData = loadResources.primitivesToDecode.peek(); + if (!defined(taskData)) { + // All primitives are processing + return; + } + + var promise = decoderTaskProcessor.scheduleTask(taskData, [taskData.array.buffer]); + if (!defined(promise)) { + // Cannot schedule another task this frame + return; + } + + loadResources.activeDecodingTasks++; + loadResources.primitivesToDecode.dequeue(); + return promise.then(function (result) { + loadResources.activeDecodingTasks--; + var decodedIndexBuffer = addNewIndexBuffer(result.indexArray, model, context); var attributes = {}; @@ -98,29 +116,12 @@ define([ } } - model._decodedData[primitive.mesh + '.primitive.' + primitive.primitive] = { + model._decodedData[taskData.mesh + '.primitive.' + taskData.primitive] = { bufferView : decodedIndexBuffer.bufferViewId, numberOfIndices : decodedIndexBuffer.numberOfIndices, attributes : attributes }; - }; - } - - function scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context) { - var taskData = loadResources.primitivesToDecode.peek(); - if (!defined(taskData)) { - // All primitives are processing - return; - } - - var promise = decoderTaskProcessor.scheduleTask(taskData, [taskData.array.buffer]); - if (!defined(promise)) { - // Cannot schedule another task this frame - return; - } - - loadResources.primitivesToDecode.dequeue(); - return promise.then(addDecodededBuffers(taskData, model, context)); + }); } /** @@ -135,8 +136,6 @@ define([ } var loadResources = model._loadResources; - loadResources.decoding = true; - var gltf = model.gltf; ForEach.mesh(gltf, function(mesh, meshId) { ForEach.meshPrimitive(mesh, function(primitive, primitiveId) { @@ -151,7 +150,6 @@ define([ var bufferView = gltf.bufferViews[compressionData.bufferView]; var typedArray = arraySlice(gltf.buffers[bufferView.buffer].extras._pipeline.source, bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength); - loadResources.primitivesToDecode.enqueue({ mesh : meshId, primitive : primitiveId, @@ -187,10 +185,7 @@ define([ promise = scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context); } - return when.all(decodingPromises).then(function () { - // Done decoding when there are no more active tasks - loadResources.decoding = (decoderTaskProcessor._activeTasks !== 0); - }); + return when.all(decodingPromises); }; return DracoLoader; diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 30545a63b873..a7cb294318bb 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -241,7 +241,21 @@ define([ * resources are created. *

*

- * For high-precision rendering, Cesium supports the CESIUM_RTC extension, which introduces the + * Cesium supports glTF assets with the following extensions: + *

+ *

+ *

+ * For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. *

@@ -1072,7 +1086,21 @@ define([ * KHR_binary_glTF extension with a .glb extension. *

*

- * For high-precision rendering, Cesium supports the CESIUM_RTC extension, which introduces the + * Cesium supports glTF assets with the following extensions: + *

+ *

+ *

+ * For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. *

@@ -1715,6 +1743,11 @@ define([ var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; + // Use bufferView created at runtime + if (!defined(bufferView)) { + bufferView = loadResources.createdBufferViews[bufferViewId]; + } + var vertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : loadResources.getBuffer(bufferView), @@ -1752,6 +1785,11 @@ define([ var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; + // Use bufferView created at runtime + if (!defined(bufferView)) { + bufferView = loadResources.createdBufferViews[bufferViewId]; + } + var indexBuffer = Buffer.createIndexBuffer({ context : context, typedArray : loadResources.getBuffer(bufferView), @@ -4206,21 +4244,8 @@ define([ processModelMaterialsCommon(this.gltf, options); processPbrMetallicRoughness(this.gltf, options); - // Start draco decoding - DracoLoader.parse(this); - - loadResources.initialized = true; - } - - if (!loadResources.finishedDecoding()) { - DracoLoader.decode(this, context) - .otherwise(getFailedLoadFunction(this, 'model', this.basePath)); - } - - if (loadResources.finishedDecoding() && !loadResources.resourcesParsed) { // We do this after to make sure that the ids don't change addBuffersToLoadResources(this); - if (!this._loadRendererResourcesFromCache) { parseBufferViews(this); parseShaders(this); @@ -4231,6 +4256,18 @@ define([ parseMeshes(this); parseNodes(this); + // Start draco decoding + DracoLoader.parse(this); + + loadResources.initialized = true; + } + + if (!loadResources.finishedDecoding()) { + DracoLoader.decode(this, context) + .otherwise(getFailedLoadFunction(this, 'model', this.basePath)); + } + + if (loadResources.finishedDecoding() && !loadResources.resourcesParsed) { this._boundingSphere = computeBoundingSphere(this); this._initialRadius = this._boundingSphere.radius; diff --git a/Source/Scene/ModelLoadResources.js b/Source/Scene/ModelLoadResources.js index f2d1b5027365..19215e621d3c 100644 --- a/Source/Scene/ModelLoadResources.js +++ b/Source/Scene/ModelLoadResources.js @@ -34,8 +34,9 @@ define([ this.createUniformMaps = true; this.createRuntimeNodes = true; - this.decoding = false; + this.createdBufferViews = {}; this.primitivesToDecode = new Queue(); + this.activeDecodingTasks = 0; this.skinnedNodesIds = []; } @@ -89,7 +90,7 @@ define([ }; ModelLoadResources.prototype.finishedDecoding = function() { - return !this.decoding && this.primitivesToDecode.length === 0; + return this.primitivesToDecode.length === 0 && this.activeDecodingTasks === 0; }; ModelLoadResources.prototype.finished = function() { diff --git a/Source/Workers/decodeDraco.js b/Source/Workers/decodeDraco.js index 60ef04e873cf..577dfb947ff1 100644 --- a/Source/Workers/decodeDraco.js +++ b/Source/Workers/decodeDraco.js @@ -21,20 +21,25 @@ define([ var numFaces = dracoGeometry.num_faces(); var faceIndices = new draco.DracoInt32Array(); - var indexArray = IndexDatatype.createTypedArray(numPoints, numFaces * 3); + var numIndices = numFaces * 3; + var indexArray = IndexDatatype.createTypedArray(numPoints, numIndices); + var offset = 0; for (var i = 0; i < numFaces; ++i) { dracoDecoder.GetFaceFromMesh(dracoGeometry, i, faceIndices); - var offset = i * 3; indexArray[offset + 0] = faceIndices.GetValue(0); indexArray[offset + 1] = faceIndices.GetValue(1); indexArray[offset + 2] = faceIndices.GetValue(2); + offset += 3; } draco.destroy(faceIndices); - return indexArray; + return { + typedArray : indexArray, + numberOfIndices : numIndices + }; } function decodeAttributeData(dracoGeometry, compressedAttributes) {