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

Support animation compression extension #4

Open
wants to merge 5 commits into
base: feat-gltf-draco-extension-v4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
105 changes: 101 additions & 4 deletions examples/js/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ THREE.GLTFLoader = ( function () {

}

if ( json.extensionsUsed.indexOf( EXTENSIONS.DRACO_ANIMATION_COMPRESSION ) >= 0 ) {
if (json.extensions.Draco_animation_compression == undefined) {
onError( new Error( 'THREE.GLTFLoader: Animation compression extension used but not defined.' ) );
return;
}

extensions[ EXTENSIONS.DRACO_ANIMATION_COMPRESSION ] = new GLTFDracoAnimationCompressionExtension( this.dracoLoader );

}

}

console.time( 'GLTFLoader' );
Expand Down Expand Up @@ -196,6 +206,7 @@ THREE.GLTFLoader = ( function () {
var EXTENSIONS = {
KHR_BINARY_GLTF: 'KHR_binary_glTF',
KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
DRACO_ANIMATION_COMPRESSION: 'Draco_animation_compression',
KHR_LIGHTS: 'KHR_lights',
KHR_MATERIALS_COMMON: 'KHR_materials_common',
KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness'
Expand Down Expand Up @@ -511,6 +522,34 @@ THREE.GLTFLoader = ( function () {

};

function GLTFDracoAnimationCompressionExtension ( dracoLoader ) {

if ( ! dracoLoader ) {

throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );

}

if (!dracoLoader.supportAnimationDecoding()) {

throw new Error( 'THREE.GLTFLoader: DRACOLoader does not support animation decoding.' );
}

this.name = EXTENSIONS.DRACO_ANIMATION_COMPRESSION;
this.dracoLoader = dracoLoader;
}

GLTFDracoAnimationCompressionExtension.prototype.decodeAnimation = function ( compressedAnimation, parser ) {

var dracoLoader = this.dracoLoader;
var bufferViewIndex = compressedAnimation.bufferView;
return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
return new Promise(function(resolve) {
resolve(dracoLoader.decodeDracoAnimation( bufferView ));
});
});
}

/**
* Specular-Glossiness Extension
*
Expand Down Expand Up @@ -1317,11 +1356,12 @@ THREE.GLTFLoader = ( function () {

GLTFParser.prototype.parse = function ( onLoad, onError ) {

var parser = this;
var json = this.json;
var extensions = this.extensions;

// Clear the loader cache
this.cache.removeAll();

// Fire the callback on complete
this._withDependencies( [

Expand Down Expand Up @@ -2072,19 +2112,74 @@ THREE.GLTFLoader = ( function () {

};

GLTFParser.prototype.loadAnimations = function () {
GLTFParser.prototype.loadCompressedAnimations = function () {

var json = this.json;
var parser = this;
var extensions = this.extensions;

return this._withDependencies( [

'accessors',
'nodes'
'bufferViews',
'accessors'

] ).then( function ( dependencies ) {
return _each( json.extensions.Draco_animation_compression, function (compressedAnimation) {
return extensions[ EXTENSIONS.DRACO_ANIMATION_COMPRESSION ].decodeAnimation(compressedAnimation, parser);
});
});
};

GLTFParser.prototype.loadAnimations = function () {

var json = this.json;
var parser = this;
var extensions = this.extensions;
var dependenciseList = [ 'bufferViews', 'accessors', 'nodes' ];
if (json.extensionsUsed !== undefined &&
json.extensionsUsed.indexOf( EXTENSIONS.DRACO_ANIMATION_COMPRESSION ) >= 0)
dependenciseList.push('compressedAnimations');

return this._withDependencies( dependenciseList ).then( function ( dependencies ) {

if (json.extensionsUsed !== undefined &&
json.extensionsUsed.indexOf( EXTENSIONS.DRACO_ANIMATION_COMPRESSION ) >= 0) {
// If using Draco animation compression, need to first
// put decoded data back to all accessors of samplers.
for (var i = 0; i < json.extensions.Draco_animation_compression.length;
++i) {
var compressedAnimation = json.extensions.Draco_animation_compression[i];
var decodedAnimation = dependencies.compressedAnimations[i];

// Put timestamps to input
var inputAccessorId = compressedAnimation.input;
var inputAccessor = json.accessors[inputAccessorId];
var itemSize = WEBGL_TYPE_SIZES[ inputAccessor.type ];
var TypedArray = WEBGL_COMPONENT_TYPES[ inputAccessor.componentType ];
var array = new TypedArray( decodedAnimation.timestamps, inputAccessor.byteOffset,
inputAccessor.count * itemSize );
dependencies.accessors[inputAccessorId] = new THREE.BufferAttribute( array, itemSize );


for (var j = 0; j < compressedAnimation.outputs.length; ++j) {
var outputAccessorId = compressedAnimation.outputs[j];
var outputAccessor = json.accessors[outputAccessorId];
var itemSize = WEBGL_TYPE_SIZES[ outputAccessor.type ];
var TypedArray = WEBGL_COMPONENT_TYPES[ outputAccessor.componentType ];

// If attribute id is x, then the id in keyframes
// will actually be x - 1.
var array = new TypedArray(decodedAnimation.keyframes[compressedAnimation.attributesId[j] - 1],
outputAccessor.byteOffset,
outputAccessor.count * itemSize );
dependencies.accessors[outputAccessorId] = new THREE.BufferAttribute( array, itemSize );
}
}
}

return _each( json.animations, function ( animation, animationId ) {

console.log("Handle animatin in gltf");
var tracks = [];

for ( var i = 0; i < animation.channels.length; i ++ ) {
Expand All @@ -2093,9 +2188,11 @@ THREE.GLTFLoader = ( function () {
var sampler = animation.samplers[ channel.sampler ];

if ( sampler ) {


var target = channel.target;
var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.

var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input;
var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output;

Expand Down
78 changes: 78 additions & 0 deletions examples/js/loaders/draco/DRACOLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,80 @@ THREE.DRACOLoader.prototype = {
if (typeof this.attributeOptions[attributeName] === 'undefined')
this.attributeOptions[attributeName] = {};
return this.attributeOptions[attributeName];
},

supportAnimationDecoding: function() {
if (typeof DracoAnimationDecoderModule === 'undefined') {
// No animation decoder is found.
return false;
}
return true;
},

decodeDracoAnimation: function(rawBuffer) {
console.log("Calling decodeDracoAnimation in DracoLoader");

if (typeof DracoAnimationDecoderModule === 'undefined') {
console.log('No animation decoder is found.');
return;
}

const decoderModule = DracoAnimationDecoderModule();
const buffer = new decoderModule.DecoderBuffer();
buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
const decoder = new decoderModule.AnimationDecoder();

console.log('Animation decoder is created.');

const dracoAnimation = new decoderModule.KeyframeAnimation();
decoder.DecodeBufferToKeyframeAnimation(buffer, dracoAnimation);

if (dracoAnimation.ptr == 0) {
console.log("Error: Decode animation failed.");
return;
}

const numKeyframes = dracoAnimation.num_frames();
console.log("Number of frames: " + numKeyframes);

// Get timestamps.
const timestampAttData = new decoderModule.DracoFloat32Array();
if (!decoder.GetTimestamps(dracoAnimation, timestampAttData)) {
console.log("Error: Get timestamps failed.");
return;
}
const timestamps = new Float32Array(numKeyframes);
for (let i = 0; i < numKeyframes; ++i) {
timestamps[i] = timestampAttData.GetValue(i);
}

const numAnimations = dracoAnimation.num_animations();
const keyframes = new Array(numAnimations);
for (let keyframeId = 0; keyframeId < numAnimations; ++keyframeId) {
const animationAttData = new decoderModule.DracoFloat32Array();
// The id of keyframe attribute starts at 1.
if (!decoder.GetKeyframes(dracoAnimation, keyframeId + 1,
animationAttData)) {
console.log("Error: Get keyframes failed.");
return;
}
keyframes[keyframeId] = new Float32Array(animationAttData.size());
for (let i = 0; i < animationAttData.size(); ++i) {
keyframes[keyframeId][i] = animationAttData.GetValue(i);
}
}

const decodedAnimation = {
timestamps : timestamps,
keyframes : keyframes
}
/*
if (resolve !== undefined)
resolve();
*/
return decodedAnimation;
}

};

// This function loads a JavaScript file and adds it to the page. "path"
Expand Down Expand Up @@ -403,6 +476,11 @@ THREE.DRACOLoader.loadDracoDecoder = function(dracoDecoder) {
}
}

THREE.DRACOLoader.loadDracoAnimationDecoder = function(dracoAnimationDecoder) {
THREE.DRACOLoader.loadJavaScriptFile(dracoAnimationDecoder.dracoSrcPath +
'draco_animation_decoder.js', null, dracoAnimationDecoder);
}

/**
* Creates and returns a singleton instance of the DracoDecoderModule.
* The module loading is done asynchronously for WebAssembly. Initialized module
Expand Down
27 changes: 27 additions & 0 deletions examples/js/loaders/draco/draco_animation_decoder.js

Large diffs are not rendered by default.

21 changes: 10 additions & 11 deletions examples/js/loaders/draco/draco_decoder.js

Large diffs are not rendered by default.

Loading