Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Draco support to point clouds #6559

Merged
merged 35 commits into from
Jul 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9cfba49
Added Draco loading to pnts
lilleyse Apr 25, 2018
c5870f8
Updated CHANGES.md
lilleyse May 3, 2018
ccc04ee
Merge branch 'master' into point-clouds-draco
lilleyse May 7, 2018
706a918
Combine point cloud and model into the same draco loader
lilleyse May 7, 2018
f618fb0
Formatting updates
lilleyse May 8, 2018
4ef9401
Tweaked CHANGES.md
lilleyse May 8, 2018
929ce88
Change names of test data, update link in Sandcastle demo
lilleyse May 8, 2018
b153553
Merge branch 'master' into point-clouds-draco
lilleyse May 8, 2018
845846c
Removed isPointCloud parameter
lilleyse May 8, 2018
a79b93f
Revert changes in #6558
lilleyse May 8, 2018
054fc5b
Remove decodeDracoPointCloud
lilleyse May 8, 2018
4156778
Merge branch 'master' into point-clouds-draco
lilleyse May 11, 2018
ebd98ca
Revert a79b93f
lilleyse May 11, 2018
8e551be
Update Draco format
lilleyse May 22, 2018
2f940d7
Support 8-bit Draco quantized points
lilleyse May 23, 2018
a552a9f
Fix when batch table doesn't exist
lilleyse May 23, 2018
99040bc
Fix for batched points
lilleyse May 23, 2018
4951e17
Adjustments to support a mix of draco and non-draco properties
lilleyse May 25, 2018
c54add9
Update tilesets and specs
lilleyse May 25, 2018
258ebf8
Remove IE checks
lilleyse May 25, 2018
0376b5d
Changed .include to .indexOf in some tests
lilleyse May 25, 2018
bea4f5b
Fix bad CHANGES.md merge
lilleyse May 25, 2018
0c303b9
Updates
lilleyse May 30, 2018
d264443
Merge branch 'master' into point-clouds-draco
lilleyse Jun 18, 2018
4dd4334
Merge branch 'master' into point-clouds-draco
lilleyse Jun 19, 2018
52eac03
Merge branch 'master' into point-clouds-draco
lilleyse Jun 22, 2018
dc74460
Fix bad merge
lilleyse Jun 22, 2018
41962c3
Merge branch 'master' into point-clouds-draco
lilleyse Jul 2, 2018
a67704e
Fix CHANGES.md bad merge again
lilleyse Jul 2, 2018
d9c3dee
Set isQuantized and isOctEncoded after Draco decode
lilleyse Jul 5, 2018
3e96229
Fix tilesets
lilleyse Jul 5, 2018
8efda1b
Destroy decoder
lilleyse Jul 5, 2018
c7948c4
Merge branch 'master' into point-clouds-draco
lilleyse Jul 5, 2018
9e502b7
Update draco files
lilleyse Jul 6, 2018
620d975
Move draco object creation
lilleyse Jul 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Apps/Sandcastle/gallery/3D Tiles Formats.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
}, {
name: 'PointCloudBatched',
resource: Cesium.IonResource.fromAssetId(3878)
}, {
name: 'PointCloudDraco',
resource: Cesium.IonResource.fromAssetId(4702)
}
],
selectedTileset: undefined,
Expand Down
7 changes: 5 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ Change Log

### 1.48 - 2018-08-01

#### Deprecated :hourglass_flowing_sand:
##### Additions :tada:
* Added support for loading Draco compressed Point Cloud tiles for 2-3x better compression. [#6559](https://github.com/AnalyticalGraphicsInc/cesium/pull/6559)

##### Deprecated :hourglass_flowing_sand:
* Support for 3D Tiles `content.url` is deprecated to reflect updates to the [3D Tiles spec](https://github.com/AnalyticalGraphicsInc/3d-tiles/pull/301). Use `content.uri instead`. Support for `content.url` will remain for backwards compatibility. [#6744](https://github.com/AnalyticalGraphicsInc/cesium/pull/6744)

#### Fixes :wrench:
##### Fixes :wrench:
* Fixed bug causing billboards and labels to appear the wrong size when switching scene modes [#6745](https://github.com/AnalyticalGraphicsInc/cesium/issues/6745)

### 1.47 - 2018-07-02
Expand Down
15 changes: 14 additions & 1 deletion Source/Scene/DracoLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ define([
* Schedules decoding tasks available this frame.
* @private
*/
DracoLoader.decode = function(model, context) {
DracoLoader.decodeModel = function(model, context) {
if (!DracoLoader.hasExtension(model)) {
return when.resolve();
}
Expand Down Expand Up @@ -245,6 +245,19 @@ define([
return when.all(decodingPromises);
};

/**
* Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled.
* @private
*/
DracoLoader.decodePointCloud = function(parameters) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe update decode to decodeModel to disambiguate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
if (!DracoLoader._taskProcessorReady) {
// The task processor is not ready to schedule tasks
return;
}
return decoderTaskProcessor.scheduleTask(parameters, [parameters.buffer.buffer]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other decode function encapsulated the DracoLoader._taskProcessorReady logic and never return undefined. Can we make these two function consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While different than decodeModel, I think the simplicity suits the point cloud decoder better. Model has more of an infrastructure around decoding and watches for loadResources.finishedDecoding rather than the result of the promise, which would sometimes be undefined.

};

/**
* Caches a models decoded data so it doesn't need to decode more than once.
* @private
Expand Down
2 changes: 1 addition & 1 deletion Source/Scene/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -4281,7 +4281,7 @@ define([
}

if (!loadResources.finishedDecoding()) {
DracoLoader.decode(this, context)
DracoLoader.decodeModel(this, context)
.otherwise(getFailedLoadFunction(this, 'model', this.basePath));
}

Expand Down
429 changes: 295 additions & 134 deletions Source/Scene/PointCloud3DTileContent.js

Large diffs are not rendered by default.

27 changes: 14 additions & 13 deletions Source/ThirdParty/Workers/draco_decoder.js

Large diffs are not rendered by default.

248 changes: 129 additions & 119 deletions Source/ThirdParty/Workers/draco_wasm_wrapper.js

Large diffs are not rendered by default.

Binary file modified Source/ThirdParty/draco_decoder.wasm
Binary file not shown.
203 changes: 127 additions & 76 deletions Source/Workers/decodeDraco.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ define([
'use strict';

var draco;
var dracoDecoder;

function decodeIndexArray(dracoGeometry) {
function decodeIndexArray(dracoGeometry, dracoDecoder) {
var numPoints = dracoGeometry.num_points();
var numFaces = dracoGeometry.num_faces();
var faceIndices = new draco.DracoInt32Array();
Expand All @@ -40,15 +39,18 @@ define([
};
}

function decodeQuantizedDracoTypedArray(dracoGeometry, attribute, quantization, vertexArrayLength) {
function decodeQuantizedDracoTypedArray(dracoGeometry, dracoDecoder, dracoAttribute, quantization, vertexArrayLength) {
var vertexArray;
var attributeData = new draco.DracoInt32Array();
if (quantization.octEncoded) {
vertexArray = new Int16Array(vertexArrayLength);
var attributeData;
if (quantization.quantizationBits <= 8) {
attributeData = new draco.DracoUInt8Array();
vertexArray = new Uint8Array(vertexArrayLength);
dracoDecoder.GetAttributeUInt8ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
} else {
attributeData = new draco.DracoUInt16Array();
vertexArray = new Uint16Array(vertexArrayLength);
dracoDecoder.GetAttributeUInt16ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
}
dracoDecoder.GetAttributeInt32ForAllPoints(dracoGeometry, attribute, attributeData);

for (var i = 0; i < vertexArrayLength; ++i) {
vertexArray[i] = attributeData.GetValue(i);
Expand All @@ -58,46 +60,46 @@ define([
return vertexArray;
}

function decodeDracoTypedArray(dracoGeometry, attribute, vertexArrayLength) {
function decodeDracoTypedArray(dracoGeometry, dracoDecoder, dracoAttribute, vertexArrayLength) {
var vertexArray;
var attributeData;

// Some attribute types are casted down to 32 bit since Draco only returns 32 bit values
switch (attribute.data_type()) {
switch (dracoAttribute.data_type()) {
case 1: case 11: // DT_INT8 or DT_BOOL
attributeData = new draco.DracoInt8Array();
vertexArray = new Int8Array(vertexArrayLength);
dracoDecoder.GetAttributeInt8ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeInt8ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 2: // DT_UINT8
attributeData = new draco.DracoUInt8Array();
vertexArray = new Uint8Array(vertexArrayLength);
dracoDecoder.GetAttributeUInt8ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeUInt8ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 3: // DT_INT16
attributeData = new draco.DracoInt16Array();
vertexArray = new Int16Array(vertexArrayLength);
dracoDecoder.GetAttributeInt16ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeInt16ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 4: // DT_UINT16
attributeData = new draco.DracoUInt16Array();
vertexArray = new Uint16Array(vertexArrayLength);
dracoDecoder.GetAttributeUInt16ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeUInt16ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 5: case 7: // DT_INT32 or DT_INT64
attributeData = new draco.DracoInt32Array();
vertexArray = new Int32Array(vertexArrayLength);
dracoDecoder.GetAttributeInt32ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeInt32ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 6: case 8: // DT_UINT32 or DT_UINT64
attributeData = new draco.DracoUint32Array();
attributeData = new draco.DracoUInt32Array();
vertexArray = new Uint32Array(vertexArrayLength);
dracoDecoder.GetAttributeUInt32ForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeUInt32ForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
case 9: case 10: // DT_FLOAT32 or DT_FLOAT64
attributeData = new draco.DracoFloat32Array();
vertexArray = new Float32Array(vertexArrayLength);
dracoDecoder.GetAttributeFloatForAllPoints(dracoGeometry, attribute, attributeData);
dracoDecoder.GetAttributeFloatForAllPoints(dracoGeometry, dracoAttribute, attributeData);
break;
}

Expand All @@ -109,72 +111,103 @@ define([
return vertexArray;
}

function decodeAttributeData(dracoGeometry, compressedAttributes) {
function decodeAttribute(dracoGeometry, dracoDecoder, dracoAttribute) {
var numPoints = dracoGeometry.num_points();
var decodedAttributeData = {};
var vertexArray;
var numComponents = dracoAttribute.num_components();

var quantization;
for (var attributeName in compressedAttributes) {
if (compressedAttributes.hasOwnProperty(attributeName)) {
var compressedAttribute = compressedAttributes[attributeName];
var attribute = dracoDecoder.GetAttributeByUniqueId(dracoGeometry, compressedAttribute);
var numComponents = attribute.num_components();

var i;
var transform = new draco.AttributeQuantizationTransform();
if (transform.InitFromAttribute(attribute)) {
var minValues = new Array(numComponents);
for (i = 0; i < numComponents; ++i) {
minValues[i] = transform.min_value(i);
}
var transform = new draco.AttributeQuantizationTransform();
if (transform.InitFromAttribute(dracoAttribute)) {
var minValues = new Array(numComponents);
for (var i = 0; i < numComponents; ++i) {
minValues[i] = transform.min_value(i);
}
quantization = {
quantizationBits : transform.quantization_bits(),
minValues : minValues,
range : transform.range(),
octEncoded : false
};
}
draco.destroy(transform);

transform = new draco.AttributeOctahedronTransform();
if (transform.InitFromAttribute(dracoAttribute)) {
quantization = {
quantizationBits : transform.quantization_bits(),
octEncoded : true
};
}
draco.destroy(transform);

quantization = {
quantizationBits : transform.quantization_bits(),
minValues : minValues,
range : transform.range(),
octEncoded : false
};
}
draco.destroy(transform);

transform = new draco.AttributeOctahedronTransform();
if (transform.InitFromAttribute(attribute)) {
quantization = {
quantizationBits : transform.quantization_bits(),
octEncoded : true
};
}
draco.destroy(transform);
var vertexArrayLength = numPoints * numComponents;
var vertexArray;
if (defined(quantization)) {
vertexArray = decodeQuantizedDracoTypedArray(dracoGeometry, dracoDecoder, dracoAttribute, quantization, vertexArrayLength);
} else {
vertexArray = decodeDracoTypedArray(dracoGeometry, dracoDecoder, dracoAttribute, vertexArrayLength);
}

var vertexArrayLength = numPoints * numComponents;
if (defined(quantization)) {
vertexArray = decodeQuantizedDracoTypedArray(dracoGeometry, attribute, quantization, vertexArrayLength);
} else {
vertexArray = decodeDracoTypedArray(dracoGeometry, attribute, vertexArrayLength);
}
var componentDatatype = ComponentDatatype.fromTypedArray(vertexArray);

var componentDatatype = ComponentDatatype.fromTypedArray(vertexArray);
decodedAttributeData[attributeName] = {
array : vertexArray,
data : {
componentsPerAttribute : numComponents,
componentDatatype : componentDatatype,
byteOffset : attribute.byte_offset(),
byteStride : ComponentDatatype.getSizeInBytes(componentDatatype) * numComponents,
normalized : attribute.normalized(),
quantization : quantization
}
};
return {
array : vertexArray,
data : {
componentsPerAttribute : numComponents,
componentDatatype : componentDatatype,
byteOffset : dracoAttribute.byte_offset(),
byteStride : ComponentDatatype.getSizeInBytes(componentDatatype) * numComponents,
normalized : dracoAttribute.normalized(),
quantization : quantization
}
};
}

quantization = undefined;
function decodePointCloud(parameters) {
var dracoDecoder = new draco.Decoder();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we are destroying the decoder anywhere once we're done.


if (parameters.dequantizeInShader) {
dracoDecoder.SkipAttributeTransform(draco.POSITION);
dracoDecoder.SkipAttributeTransform(draco.NORMAL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to include generic attributes here, for RGB, RGBA, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't include them because the other attributes like RGB, RGBA, intensity, and classification usually decode to 8-bit values and decoding to quantized form won't provide any GPU memory savings.

}

var buffer = new draco.DecoderBuffer();
buffer.Init(parameters.buffer, parameters.buffer.length);

var geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
if (geometryType !== draco.POINT_CLOUD) {
throw new RuntimeError('Draco geometry type must be POINT_CLOUD.');
}

var dracoPointCloud = new draco.PointCloud();
var decodingStatus = dracoDecoder.DecodeBufferToPointCloud(buffer, dracoPointCloud);
if (!decodingStatus.ok() || dracoPointCloud.ptr === 0) {
throw new RuntimeError('Error decoding draco point cloud: ' + decodingStatus.error_msg());
}

draco.destroy(buffer);

var result = {};

var properties = parameters.properties;
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
var attributeId = properties[propertyName];
var dracoAttribute = dracoDecoder.GetAttributeByUniqueId(dracoPointCloud, attributeId);
result[propertyName] = decodeAttribute(dracoPointCloud, dracoDecoder, dracoAttribute);
}
}

return decodedAttributeData;
draco.destroy(dracoPointCloud);
draco.destroy(dracoDecoder);

return result;
}

function decodeDracoPrimitive(parameters) {
// Skip all paramter types except generic
function decodePrimitive(parameters) {
var dracoDecoder = new draco.Decoder();

// Skip all parameter types except generic
var attributesToSkip = ['POSITION', 'NORMAL', 'COLOR', 'TEX_COORD'];
if (parameters.dequantizeInShader) {
for (var i = 0; i < attributesToSkip.length; ++i) {
Expand All @@ -199,20 +232,38 @@ define([

draco.destroy(buffer);

var attributeData = {};

var compressedAttributes = parameters.compressedAttributes;
for (var attributeName in compressedAttributes) {
if (compressedAttributes.hasOwnProperty(attributeName)) {
var compressedAttribute = compressedAttributes[attributeName];
var dracoAttribute = dracoDecoder.GetAttributeByUniqueId(dracoGeometry, compressedAttribute);
attributeData[attributeName] = decodeAttribute(dracoGeometry, dracoDecoder, dracoAttribute);
}
}

var result = {
indexArray : decodeIndexArray(dracoGeometry),
attributeData : decodeAttributeData(dracoGeometry, parameters.compressedAttributes)
indexArray : decodeIndexArray(dracoGeometry, dracoDecoder),
attributeData : attributeData
};

draco.destroy(dracoGeometry);
draco.destroy(dracoDecoder);

return result;
}

function decode(parameters) {
if (defined(parameters.primitive)) {
return decodePrimitive(parameters);
}
return decodePointCloud(parameters);
}

function initWorker(dracoModule) {
draco = dracoModule;
dracoDecoder = new draco.Decoder();
self.onmessage = createTaskProcessorWorker(decodeDracoPrimitive);
self.onmessage = createTaskProcessorWorker(decode);
self.postMessage(true);
}

Expand Down
Binary file not shown.
27 changes: 27 additions & 0 deletions Specs/Data/Cesium3DTiles/PointCloud/PointCloudDraco/tileset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"asset": {
"version": "1.0"
},
"extensionsUsed": [
"3DTILES_draco_point_compression"
],
"extensionsRequired": [
"3DTILES_draco_point_compression"
],
"geometricError": 17.32,
"root": {
"refine": "ADD",
"boundingVolume": {
"sphere": [
1215012.8828876738,
-4736313.051199594,
4081605.22126042,
5
]
},
"geometricError": 0,
"content": {
"uri": "pointCloudDraco.pnts"
}
}
}
Binary file not shown.
Loading