Skip to content

Commit

Permalink
Support skin attributes (fanzhanggoogle#4)
Browse files Browse the repository at this point in the history
* Updated DRACOLoader
* Changed to request attribute only by unique id
* Added support for skinning
* Added more sample models
  • Loading branch information
fanzhanggoogle authored and donmccurdy committed Oct 6, 2017
1 parent 6f7950c commit 6b8ea5e
Show file tree
Hide file tree
Showing 14 changed files with 8,052 additions and 127 deletions.
21 changes: 19 additions & 2 deletions examples/js/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,19 +474,36 @@ THREE.GLTFLoader = ( function () {

this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
this.dracoLoader = dracoLoader;

this.glTFNameToThreeJSName = {
'POSITION' : 'position',
'NORMAL' : 'normal',
'TEXCOORD_0' : 'uv',
'TEXCOORD0' : 'uv',
'TEXCOORD' : 'uv',
'TEXCOORD_1' : 'uv2',
'COLOR_0' : 'color',
'COLOR0' : 'color',
'COLOR' : 'color',
'WEIGHTS_0' : 'skinWeight',
'JOINTS_0' : 'skinIndex'
};
}

GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {

var dracoLoader = this.dracoLoader;
var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
var attributesIdMap = primitive.extensions[ this.name ].attributes;
var attributeMap = {};
for (var attributeName in attributesIdMap) {
attributeMap[this.glTFNameToThreeJSName[attributeName]] = attributesIdMap[attributeName];
}

return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {

return new Promise( function ( resolve ) {

dracoLoader.decodeDracoFile( bufferView, resolve );
dracoLoader.decodeDracoFile( bufferView, resolve, attributeMap );

} );

Expand Down
208 changes: 87 additions & 121 deletions examples/js/loaders/draco/DRACOLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@ THREE.DRACOLoader = function(dracoPath, dracoDecoderType, manager) {
(dracoDecoderType !== undefined) ? dracoDecoderType : {};
this.drawMode = THREE.TrianglesDrawMode;
this.dracoSrcPath = (dracoPath !== undefined) ? dracoPath : './';
// If draco_decoder.js or wasm code is already loaded/included, then do
// not dynamically load the decoder.
if (typeof DracoDecoderModule === 'undefined') {
THREE.DRACOLoader.loadDracoDecoder(this);
}
// User defined unique id for attributes.
this.attributeUniqueIdMap = {};
// Native Draco attribute type to Three.JS attribute type.
this.nativeAttributeMap = {
'position' : 'POSITION',
'normal' : 'NORMAL',
'color' : 'COLOR',
'uv' : 'TEX_COORD'
};
};

THREE.DRACOLoader.prototype = {
Expand Down Expand Up @@ -86,15 +97,31 @@ THREE.DRACOLoader.prototype = {
skipDequantization;
},

decodeDracoFile: function(rawBuffer, callback) {
/**
* |attributeUniqueIdMap| specifies attribute unique id for an attribute in
* the geometry to be decoded. The name of the attribute must be one of the
* supported attribute type in Three.JS, including:
* 'position',
* 'color',
* 'normal',
* 'uv',
* 'uv2',
* 'skinIndex',
* 'skinWeight'.
* The format is:
* attributeUniqueIdMap[attributeName] = attributeId
*/
decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap) {
var scope = this;
this.attributeUniqueIdMap = (attributeUniqueIdMap !== undefined) ?
attributeUniqueIdMap : {};
THREE.DRACOLoader.getDecoder(this,
function(dracoDecoder) {
scope.decodeDracoFileInternal(rawBuffer, dracoDecoder, callback);
});
},

decodeDracoFileInternal : function(rawBuffer, dracoDecoder, callback) {
decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback) {
/*
* Here is how to use Draco Javascript decoder and get the geometry.
*/
Expand Down Expand Up @@ -123,6 +150,33 @@ THREE.DRACOLoader.prototype = {
geometryType, buffer));
},

addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry,
attributeName, attribute, geometry,
geometryBuffer) {
if (attribute.ptr === 0) {
var errorMsg = 'THREE.DRACOLoader: No attribute ' + attributeName;
console.error(errorMsg);
throw new Error(errorMsg);
}
var numComponents = attribute.num_components();
var attributeData = new dracoDecoder.DracoFloat32Array();
decoder.GetAttributeFloatForAllPoints(
dracoGeometry, attribute, attributeData);
var numPoints = dracoGeometry.num_points();
var numValues = numPoints * numComponents;
// Allocate space for attribute.
geometryBuffer[attributeName] = new Float32Array(numValues);
// Copy data from decoder.
for (var i = 0; i < numValues; i++) {
geometryBuffer[attributeName][i] = attributeData.GetValue(i);
}
// Add attribute to THREEJS geometry for rendering.
geometry.addAttribute(attributeName,
new THREE.Float32BufferAttribute(geometryBuffer[attributeName],
numComponents));
dracoDecoder.destroy(attributeData);
},

convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
buffer) {
if (this.getAttributeOptions('position').skipDequantization === true) {
Expand Down Expand Up @@ -153,12 +207,7 @@ THREE.DRACOLoader.prototype = {
/*
* Example on how to retrieve mesh and attributes.
*/
var numFaces, numPoints;
var numVertexCoordinates, numTextureCoordinates, numColorCoordinates;
var numAttributes;
var numColorCoordinateComponents = 3;
// For output basic geometry information.
var geometryInfoStr;
var numFaces;
if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
numFaces = dracoGeometry.num_faces();
if (this.verbosity > 0) {
Expand All @@ -167,20 +216,18 @@ THREE.DRACOLoader.prototype = {
} else {
numFaces = 0;
}
numPoints = dracoGeometry.num_points();
numVertexCoordinates = numPoints * 3;
numTextureCoordinates = numPoints * 2;
numColorCoordinates = numPoints * 3;
numAttributes = dracoGeometry.num_attributes();

var numPoints = dracoGeometry.num_points();
var numAttributes = dracoGeometry.num_attributes();
if (this.verbosity > 0) {
console.log('Number of points loaded: ' + numPoints.toString());
console.log('Number of attributes loaded: ' +
numAttributes.toString());
}

// Get position attribute. Must exists.
// Verify if there is position attribute.
var posAttId = decoder.GetAttributeId(dracoGeometry,
dracoDecoder.POSITION);
dracoDecoder.POSITION);
if (posAttId == -1) {
var errorMsg = 'THREE.DRACOLoader: No position attribute found.';
console.error(errorMsg);
Expand All @@ -189,104 +236,39 @@ THREE.DRACOLoader.prototype = {
throw new Error(errorMsg);
}
var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId);
var posAttributeData = new dracoDecoder.DracoFloat32Array();
decoder.GetAttributeFloatForAllPoints(
dracoGeometry, posAttribute, posAttributeData);
// Get color attributes if exists.
var colorAttId = decoder.GetAttributeId(dracoGeometry,
dracoDecoder.COLOR);
var colAttributeData;
if (colorAttId != -1) {
if (this.verbosity > 0) {
console.log('Loaded color attribute.');
}
var colAttribute = decoder.GetAttribute(dracoGeometry, colorAttId);
if (colAttribute.num_components() === 4) {
numColorCoordinates = numPoints * 4;
numColorCoordinateComponents = 4;
}
colAttributeData = new dracoDecoder.DracoFloat32Array();
decoder.GetAttributeFloatForAllPoints(dracoGeometry, colAttribute,
colAttributeData);
}

// Get normal attributes if exists.
var normalAttId =
decoder.GetAttributeId(dracoGeometry, dracoDecoder.NORMAL);
var norAttributeData;
if (normalAttId != -1) {
if (this.verbosity > 0) {
console.log('Loaded normal attribute.');
}
var norAttribute = decoder.GetAttribute(dracoGeometry, normalAttId);
norAttributeData = new dracoDecoder.DracoFloat32Array();
decoder.GetAttributeFloatForAllPoints(dracoGeometry, norAttribute,
norAttributeData);
}

// Get texture coord attributes if exists.
var texCoordAttId =
decoder.GetAttributeId(dracoGeometry, dracoDecoder.TEX_COORD);
var textCoordAttributeData;
if (texCoordAttId != -1) {
if (this.verbosity > 0) {
console.log('Loaded texture coordinate attribute.');
}
var texCoordAttribute = decoder.GetAttribute(dracoGeometry,
texCoordAttId);
textCoordAttributeData = new dracoDecoder.DracoFloat32Array();
decoder.GetAttributeFloatForAllPoints(dracoGeometry,
texCoordAttribute,
textCoordAttributeData);
}

// Structure for converting to THREEJS geometry later.
var geometryBuffer = {
vertices: new Float32Array(numVertexCoordinates),
normals: new Float32Array(numVertexCoordinates),
uvs: new Float32Array(numTextureCoordinates),
colors: new Float32Array(numColorCoordinates)
};

for (var i = 0; i < numVertexCoordinates; i += 3) {
geometryBuffer.vertices[i] = posAttributeData.GetValue(i);
geometryBuffer.vertices[i + 1] = posAttributeData.GetValue(i + 1);
geometryBuffer.vertices[i + 2] = posAttributeData.GetValue(i + 2);
// Add normal.
if (normalAttId != -1) {
geometryBuffer.normals[i] = norAttributeData.GetValue(i);
geometryBuffer.normals[i + 1] = norAttributeData.GetValue(i + 1);
geometryBuffer.normals[i + 2] = norAttributeData.GetValue(i + 2);
}
}
var geometryBuffer = {};
// Import data to Three JS geometry.
var geometry = new THREE.BufferGeometry();

// Add color.
for (var i = 0; i < numColorCoordinates; i += 1) {
if (colorAttId != -1) {
// Draco colors are already normalized.
geometryBuffer.colors[i] = colAttributeData.GetValue(i);
} else {
// Default is white. This is faster than TypedArray.fill().
geometryBuffer.colors[i] = 1.0;
// Add native Draco attribute type to geometry.
for (var attributeName in this.nativeAttributeMap) {
// The native attribute type is only used when no unique Id is
// provided. For example, loading .drc files.
if (this.attributeUniqueIdMap[attributeName] === undefined) {
var attId = decoder.GetAttributeId(dracoGeometry,
dracoDecoder[this.nativeAttributeMap[attributeName]]);
if (attId !== -1) {
if (this.verbosity > 0) {
console.log('Loaded ' + attributeName + ' attribute.');
}
var attribute = decoder.GetAttribute(dracoGeometry, attId);
this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
attributeName, attribute, geometry, geometryBuffer);
}
}
}

// Add texture coordinates.
if (texCoordAttId != -1) {
for (var i = 0; i < numTextureCoordinates; i += 2) {
geometryBuffer.uvs[i] = textCoordAttributeData.GetValue(i);
geometryBuffer.uvs[i + 1] = textCoordAttributeData.GetValue(i + 1);
}
// Add attributes of user specified unique id. E.g. GLTF models.
for (var attributeName in this.attributeUniqueIdMap) {
var attributeId = this.attributeUniqueIdMap[attributeName];
var attribute = decoder.GetAttributeByUniqueId(dracoGeometry,
attributeId);
this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
attributeName, attribute, geometry, geometryBuffer);
}

dracoDecoder.destroy(posAttributeData);
if (colorAttId != -1)
dracoDecoder.destroy(colAttributeData);
if (normalAttId != -1)
dracoDecoder.destroy(norAttributeData);
if (texCoordAttId != -1)
dracoDecoder.destroy(textCoordAttributeData);

// For mesh, we need to generate the faces.
if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
if (this.drawMode === THREE.TriangleStripDrawMode) {
Expand All @@ -313,16 +295,12 @@ THREE.DRACOLoader.prototype = {
}
}

// Import data to Three JS geometry.
var geometry = new THREE.BufferGeometry();
geometry.drawMode = this.drawMode;
if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute)
(geometryBuffer.indices, 1));
}
geometry.addAttribute('position',
new THREE.Float32BufferAttribute(geometryBuffer.vertices, 3));
var posTransform = new dracoDecoder.AttributeQuantizationTransform();
if (posTransform.InitFromAttribute(posAttribute)) {
// Quantized attribute. Store the quantization parameters into the
Expand All @@ -338,18 +316,6 @@ THREE.DRACOLoader.prototype = {
}
}
dracoDecoder.destroy(posTransform);
geometry.addAttribute('color',
new THREE.Float32BufferAttribute(geometryBuffer.colors,
numColorCoordinateComponents));
if (normalAttId != -1) {
geometry.addAttribute('normal',
new THREE.Float32BufferAttribute(geometryBuffer.normals, 3));
}
if (texCoordAttId != -1) {
geometry.addAttribute('uv',
new THREE.Float32BufferAttribute(geometryBuffer.uvs, 2));
}

dracoDecoder.destroy(decoder);
dracoDecoder.destroy(dracoGeometry);

Expand Down
Loading

0 comments on commit 6b8ea5e

Please sign in to comment.