-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Changes from all commits
9cfba49
c5870f8
ccc04ee
706a918
f618fb0
4ef9401
929ce88
b153553
845846c
a79b93f
054fc5b
4156778
ebd98ca
8e551be
2f940d7
a552a9f
99040bc
4951e17
c54add9
258ebf8
0376b5d
bea4f5b
0c303b9
d264443
4dd4334
52eac03
dc74460
41962c3
a67704e
d9c3dee
3e96229
8efda1b
c7948c4
9e502b7
620d975
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
} | ||
|
@@ -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) { | ||
var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); | ||
if (!DracoLoader._taskProcessorReady) { | ||
// The task processor is not ready to schedule tasks | ||
return; | ||
} | ||
return decoderTaskProcessor.scheduleTask(parameters, [parameters.buffer.buffer]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other decode function encapsulated the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While different than |
||
}; | ||
|
||
/** | ||
* Caches a models decoded data so it doesn't need to decode more than once. | ||
* @private | ||
|
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
@@ -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); | ||
|
@@ -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; | ||
} | ||
|
||
|
@@ -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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to include generic attributes here, for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't include them because the other attributes like |
||
} | ||
|
||
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) { | ||
|
@@ -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); | ||
} | ||
|
||
|
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" | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe update
decode
todecodeModel
to disambiguate.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.