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

GLTFLoader: Implement KHR_draco_mesh_compression #13194

Merged
merged 15 commits into from
Feb 16, 2018
Merged
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
14 changes: 14 additions & 0 deletions docs/examples/loaders/GLTFLoader.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ <h2>Extensions</h2>
KHR_materials_pbrSpecularGlossiness
</a>
</li>
<li>
<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression">
KHR_draco_mesh_compression
</a>
</li>
<li>
KHR_lights (experimental)
</li>
Expand All @@ -43,6 +48,10 @@ <h2>Example</h2>
// Instantiate a loader
var loader = new THREE.GLTFLoader();

// Optional: Provide a DRACOLoader instance to decode compressed mesh data
THREE.DRACOLoader.setDecoderPath( '/examples/js/loaders/draco' );
loader.setDRACOLoader( new THREE.DRACOLoader() );

// Load a glTF resource
loader.load(
// resource URL
Expand Down Expand Up @@ -124,6 +133,11 @@ <h3>[method:null setCrossOrigin]( [page:String value] )</h3>
[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
</div>

<h3>[method:null setDRACOLoader]( [page:DRACOLoader dracoLoader] )</h3>
<div>
[page:DRACOLoader dracoLoader] — Instance of THREE.DRACOLoader, to be used for decoding assets compressed with the KHR_draco_mesh_compression extension.
</div>

<h3>[method:null parse]( [page:ArrayBuffer data], [page:String path], [page:Function onLoad], [page:Function onError] )</h3>
<div>
[page:ArrayBuffer data] — glTF asset to parse, as an ArrayBuffer or <em>JSON</em> string.<br />
Expand Down
203 changes: 144 additions & 59 deletions examples/js/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ THREE.GLTFLoader = ( function () {
function GLTFLoader( manager ) {

this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
this.dracoLoader = null;

}

Expand Down Expand Up @@ -68,6 +69,12 @@ THREE.GLTFLoader = ( function () {

},

setDRACOLoader: function ( dracoLoader ) {

this.dracoLoader = dracoLoader;

},

parse: function ( data, path, onLoad, onError ) {

var content;
Expand Down Expand Up @@ -127,6 +134,12 @@ THREE.GLTFLoader = ( function () {

}

if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ) >= 0 ) {

extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] = new GLTFDracoMeshCompressionExtension( this.dracoLoader );

}

}

console.time( 'GLTFLoader' );
Expand Down Expand Up @@ -201,6 +214,7 @@ THREE.GLTFLoader = ( function () {

var EXTENSIONS = {
KHR_BINARY_GLTF: 'KHR_binary_glTF',
KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
KHR_LIGHTS: 'KHR_lights',
KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness'
};
Expand Down Expand Up @@ -357,6 +371,51 @@ THREE.GLTFLoader = ( function () {

}

/**
* DRACO Mesh Compression Extension
*
* Specification: https://github.com/KhronosGroup/glTF/pull/874
*/
function GLTFDracoMeshCompressionExtension ( dracoLoader ) {

if ( ! dracoLoader ) {

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

}

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

}

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

var dracoLoader = this.dracoLoader;
var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
var gltfAttributeMap = primitive.extensions[ this.name ].attributes;
var threeAttributeMap = {};

for ( var attributeName in gltfAttributeMap ) {

if ( !( attributeName in ATTRIBUTES ) ) continue;

threeAttributeMap[ ATTRIBUTES[ attributeName ] ] = gltfAttributeMap[ attributeName ];

}

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

return new Promise( function ( resolve ) {

dracoLoader.decodeDracoFile( bufferView, resolve, threeAttributeMap );

} );

} );

};

/**
* Specular-Glossiness Extension
*
Expand Down Expand Up @@ -941,6 +1000,22 @@ THREE.GLTFLoader = ( function () {
'MAT4': 16
};

var ATTRIBUTES = {
POSITION: 'position',
NORMAL: 'normal',
TEXCOORD_0: 'uv',
TEXCOORD0: 'uv', // deprecated
TEXCOORD: 'uv', // deprecated
TEXCOORD_1: 'uv2',
COLOR_0: 'color',
COLOR0: 'color', // deprecated
COLOR: 'color', // deprecated
WEIGHTS_0: 'skinWeight',
WEIGHT: 'skinWeight', // deprecated
JOINTS_0: 'skinIndex',
JOINT: 'skinIndex' // deprecated
}

var PATH_PROPERTIES = {
scale: 'scale',
translation: 'position',
Expand Down Expand Up @@ -1013,8 +1088,6 @@ THREE.GLTFLoader = ( function () {
/**
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
*
* TODO: Implement support for morph targets on TANGENT attribute.
*
* @param {THREE.Mesh} mesh
* @param {GLTF.Mesh} meshDef
* @param {GLTF.Primitive} primitiveDef
Expand Down Expand Up @@ -1177,7 +1250,7 @@ THREE.GLTFLoader = ( function () {

if ( isPrimitiveEqual( cached.primitive, newPrimitive ) ) {

return cached.geometry;
return cached.promise;

}

Expand Down Expand Up @@ -1480,6 +1553,15 @@ THREE.GLTFLoader = ( function () {

var accessorDef = this.json.accessors[ accessorIndex ];

if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) {

// Ignore empty accessors, which may be used to declare runtime
// information about attributes coming from another source (e.g. Draco
// compression extension).
return null;

}

var pendingBufferViews = [];

if ( accessorDef.bufferView !== undefined ) {
Expand Down Expand Up @@ -1873,18 +1955,51 @@ THREE.GLTFLoader = ( function () {

};

/**
* @param {THREE.BufferGeometry} geometry
* @param {GLTF.Primitive} primitiveDef
* @param {Array<THREE.BufferAttribute>} accessors
*/
function addPrimitiveAttributes ( geometry, primitiveDef, accessors ) {

var attributes = primitiveDef.attributes;

for ( var gltfAttributeName in attributes ) {

var threeAttributeName = ATTRIBUTES[ gltfAttributeName ];
var bufferAttribute = accessors[ attributes[ gltfAttributeName ] ];

// Skip attributes already provided by e.g. Draco extension.
if ( !threeAttributeName ) continue;
if ( threeAttributeName in geometry.attributes ) continue;

geometry.addAttribute( threeAttributeName, bufferAttribute );

}

if ( primitiveDef.indices !== undefined && !geometry.index ) {

geometry.setIndex( accessors[ primitiveDef.indices ] );

}

}

/**
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
* @param {Array<Object>} primitives
* @return {Promise<Array<THREE.BufferGeometry>>}
*/
GLTFParser.prototype.loadGeometries = function ( primitives ) {

var parser = this;
var extensions = this.extensions;
var cache = this.primitiveCache;

return this.getDependencies( 'accessor' ).then( function ( accessors ) {

var geometries = [];
var pending = [];

for ( var i = 0, il = primitives.length; i < il; i ++ ) {

Expand All @@ -1893,82 +2008,48 @@ THREE.GLTFLoader = ( function () {
// See if we've already created this geometry
var cached = getCachedGeometry( cache, primitive );

var geometry;

if ( cached ) {

// Use the cached geometry if it exists
geometries.push( cached );
pending.push( cached.then( function ( geometry ) {

} else {
geometries.push( geometry );

// Otherwise create a new geometry
var geometry = new THREE.BufferGeometry();
} ) );

var attributes = primitive.attributes;
} else if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) {

for ( var attributeId in attributes ) {
// Use DRACO geometry if available
var geometryPromise = extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]
.decodePrimitive( primitive, parser )
.then( function ( geometry ) {

var attributeEntry = attributes[ attributeId ];
addPrimitiveAttributes( geometry, primitive, accessors );

var bufferAttribute = accessors[ attributeEntry ];
geometries.push( geometry );

switch ( attributeId ) {
return geometry;

case 'POSITION':
} );

geometry.addAttribute( 'position', bufferAttribute );
break;

case 'NORMAL':
cache.push( { primitive: primitive, promise: geometryPromise } );

geometry.addAttribute( 'normal', bufferAttribute );
break;
pending.push( geometryPromise );

case 'TEXCOORD_0':
case 'TEXCOORD0':
case 'TEXCOORD':

geometry.addAttribute( 'uv', bufferAttribute );
break;
} else {

case 'TEXCOORD_1':

geometry.addAttribute( 'uv2', bufferAttribute );
break;

case 'COLOR_0':
case 'COLOR0':
case 'COLOR':

geometry.addAttribute( 'color', bufferAttribute );
break;

case 'WEIGHTS_0':
case 'WEIGHT': // WEIGHT semantic deprecated.

geometry.addAttribute( 'skinWeight', bufferAttribute );
break;

case 'JOINTS_0':
case 'JOINT': // JOINT semantic deprecated.

geometry.addAttribute( 'skinIndex', bufferAttribute );
break;

}

}

if ( primitive.indices !== undefined ) {

geometry.setIndex( accessors[ primitive.indices ] );
// Otherwise create a new geometry
geometry = new THREE.BufferGeometry();

}
addPrimitiveAttributes( geometry, primitive, accessors );

// Cache this geometry
cache.push( {

primitive: primitive,
geometry: geometry
promise: Promise.resolve( geometry )

} );

Expand All @@ -1978,7 +2059,11 @@ THREE.GLTFLoader = ( function () {

}

return geometries;
return Promise.all( pending ).then( function () {

return geometries;

} );

} );

Expand Down
Loading