Skip to content

Commit 4ad27a4

Browse files
authored
Merge pull request #6191 from AnalyticalGraphicsInc/draco
Add support for Draco glTF models
2 parents cf8772b + 4bc5336 commit 4ad27a4

16 files changed

+3826
-10
lines changed

CHANGES.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Change Log
33

44
### 1.44 - 2018-04-02
55

6+
##### Additions :tada:
7+
* Added support for glTF models with [Draco geometry compression](https://github.com/fanzhanggoogle/glTF/blob/KHR_mesh_compression/extensions/Khronos/KHR_draco_mesh_compression/README.md).
8+
69
##### Fixes :wrench:
710
* Fixed support of glTF-supplied tangent vectors. [#6302](https://github.com/AnalyticalGraphicsInc/cesium/pull/6302)
811
* Fixed improper zoom during model load failure. [#6305](https://github.com/AnalyticalGraphicsInc/cesium/pull/6305)

LICENSE.md

+16
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,22 @@ https://github.com/KhronosGroup/glTF-WebGL-PBR
670670
>CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
671671
>OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
672672
673+
### Draco
674+
675+
https://github.com/google/draco
676+
677+
>Licensed under the Apache License, Version 2.0 (the "License"); you may not
678+
>use this file except in compliance with the License. You may obtain a copy of
679+
>the License at
680+
>
681+
><http://www.apache.org/licenses/LICENSE-2.0>
682+
>
683+
>Unless required by applicable law or agreed to in writing, software
684+
>distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
685+
>WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
686+
>License for the specific language governing permissions and limitations under
687+
>the License.
688+
673689
Tests
674690
=====
675691

Source/Scene/DracoLoader.js

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
define([
2+
'../Core/arraySlice',
3+
'../Core/ComponentDatatype',
4+
'../Core/defined',
5+
'../Core/FeatureDetection',
6+
'../Core/TaskProcessor',
7+
'../Renderer/Buffer',
8+
'../Renderer/BufferUsage',
9+
'../ThirdParty/GltfPipeline/ForEach',
10+
'../ThirdParty/when'
11+
], function(
12+
arraySlice,
13+
ComponentDatatype,
14+
defined,
15+
FeatureDetection,
16+
TaskProcessor,
17+
Buffer,
18+
BufferUsage,
19+
ForEach,
20+
when) {
21+
'use strict';
22+
23+
/**
24+
* @private
25+
*/
26+
function DracoLoader() {}
27+
28+
// Maximum concurrency to use when deocding draco models
29+
DracoLoader._maxDecodingConcurrency = Math.max(FeatureDetection.hardwareConcurrency - 1, 1);
30+
31+
// Exposed for testing purposes
32+
DracoLoader._decoderTaskProcessor = undefined;
33+
DracoLoader._getDecoderTaskProcessor = function () {
34+
if (!defined(DracoLoader._decoderTaskProcessor)) {
35+
DracoLoader._decoderTaskProcessor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency);
36+
}
37+
38+
return DracoLoader._decoderTaskProcessor;
39+
};
40+
41+
function hasExtension(model) {
42+
return (defined(model.extensionsRequired.KHR_draco_mesh_compression)
43+
|| defined(model.extensionsUsed.KHR_draco_mesh_compression));
44+
}
45+
46+
function addBufferToModelResources(model, buffer) {
47+
var resourceBuffers = model._rendererResources.buffers;
48+
var bufferViewId = Object.keys(resourceBuffers).length;
49+
resourceBuffers[bufferViewId] = buffer;
50+
model._geometryByteLength += buffer.sizeInBytes;
51+
52+
return bufferViewId;
53+
}
54+
55+
function addNewVertexBuffer(typedArray, model, context) {
56+
var vertexBuffer = Buffer.createVertexBuffer({
57+
context : context,
58+
typedArray : typedArray,
59+
usage : BufferUsage.STATIC_DRAW
60+
});
61+
vertexBuffer.vertexArrayDestroyable = false;
62+
63+
return addBufferToModelResources(model, vertexBuffer);
64+
}
65+
66+
function addNewIndexBuffer(typedArray, model, context) {
67+
var indexBuffer = Buffer.createIndexBuffer({
68+
context : context,
69+
typedArray : typedArray,
70+
usage : BufferUsage.STATIC_DRAW,
71+
indexDatatype : ComponentDatatype.fromTypedArray(typedArray)
72+
});
73+
indexBuffer.vertexArrayDestroyable = false;
74+
75+
var bufferViewId = addBufferToModelResources(model, indexBuffer);
76+
return {
77+
bufferViewId: bufferViewId,
78+
numberOfIndices : indexBuffer.numberOfIndices
79+
};
80+
}
81+
82+
function addDecodededBuffers(primitive, model, context) {
83+
return function (result) {
84+
var decodedIndexBuffer = addNewIndexBuffer(result.indexArray, model, context);
85+
86+
var attributes = {};
87+
var decodedAttributeData = result.attributeData;
88+
for (var attributeName in decodedAttributeData) {
89+
if (decodedAttributeData.hasOwnProperty(attributeName)) {
90+
var attribute = decodedAttributeData[attributeName];
91+
var vertexArray = attribute.array;
92+
var vertexBufferView = addNewVertexBuffer(vertexArray, model, context);
93+
94+
var data = attribute.data;
95+
data.bufferView = vertexBufferView;
96+
97+
attributes[attributeName] = data;
98+
}
99+
}
100+
101+
model._decodedData[primitive.mesh + '.primitive.' + primitive.primitive] = {
102+
bufferView : decodedIndexBuffer.bufferViewId,
103+
numberOfIndices : decodedIndexBuffer.numberOfIndices,
104+
attributes : attributes
105+
};
106+
};
107+
}
108+
109+
function scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context) {
110+
var taskData = loadResources.primitivesToDecode.peek();
111+
if (!defined(taskData)) {
112+
// All primitives are processing
113+
return;
114+
}
115+
116+
var promise = decoderTaskProcessor.scheduleTask(taskData, [taskData.array.buffer]);
117+
if (!defined(promise)) {
118+
// Cannot schedule another task this frame
119+
return;
120+
}
121+
122+
loadResources.primitivesToDecode.dequeue();
123+
return promise.then(addDecodededBuffers(taskData, model, context));
124+
}
125+
126+
/**
127+
* Parses draco extension on model primitives and
128+
* adds the decoding data to the model's load resources.
129+
*
130+
* @private
131+
*/
132+
DracoLoader.parse = function(model) {
133+
if (!hasExtension(model)) {
134+
return;
135+
}
136+
137+
var loadResources = model._loadResources;
138+
loadResources.decoding = true;
139+
140+
var gltf = model.gltf;
141+
ForEach.mesh(gltf, function(mesh, meshId) {
142+
ForEach.meshPrimitive(mesh, function(primitive, primitiveId) {
143+
if (!defined(primitive.extensions)) {
144+
return;
145+
}
146+
147+
var compressionData = primitive.extensions.KHR_draco_mesh_compression;
148+
if (!defined(compressionData)) {
149+
return;
150+
}
151+
152+
var bufferView = gltf.bufferViews[compressionData.bufferView];
153+
var typedArray = arraySlice(gltf.buffers[bufferView.buffer].extras._pipeline.source, bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength);
154+
155+
loadResources.primitivesToDecode.enqueue({
156+
mesh : meshId,
157+
primitive : primitiveId,
158+
array : typedArray,
159+
bufferView : bufferView,
160+
compressedAttributes : compressionData.attributes
161+
});
162+
});
163+
});
164+
};
165+
166+
/**
167+
* Schedules decoding tasks available this frame.
168+
* @private
169+
*/
170+
DracoLoader.decode = function(model, context) {
171+
if (!hasExtension(model)) {
172+
return when.resolve();
173+
}
174+
175+
var loadResources = model._loadResources;
176+
if (loadResources.primitivesToDecode.length === 0) {
177+
// No more tasks to schedule
178+
return when.resolve();
179+
}
180+
181+
var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
182+
var decodingPromises = [];
183+
184+
var promise = scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context);
185+
while (defined(promise)) {
186+
decodingPromises.push(promise);
187+
promise = scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context);
188+
}
189+
190+
return when.all(decodingPromises).then(function () {
191+
// Done decoding when there are no more active tasks
192+
loadResources.decoding = (decoderTaskProcessor._activeTasks !== 0);
193+
});
194+
};
195+
196+
return DracoLoader;
197+
});

0 commit comments

Comments
 (0)