Skip to content

Commit

Permalink
Merge pull request #13831 from donmccurdy/feat-gltfloader-texturetran…
Browse files Browse the repository at this point in the history
…sform

GLTFLoader: Implement KHR_texture_transform extension.
  • Loading branch information
mrdoob authored Oct 23, 2018
2 parents 80cd24c + 5e6cbc5 commit dab285d
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 26 deletions.
24 changes: 18 additions & 6 deletions docs/examples/loaders/GLTFLoader.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,20 @@ <h2>Extensions</h2>
<li>KHR_materials_pbrSpecularGlossiness</li>
<li>KHR_materials_unlit</li>
<li>KHR_lights_punctual (experimental)</li>
<li>KHR_texture_transform<sup>*</sup></li>
<li>MSFT_texture_dds</li>
</ul>

<p><i>
<sup>*</sup>UV transforms are supported, with several key limitations. Transforms applied to
a texture using the first UV slot (all textures except aoMap and lightMap) must share the same
transform, or no transfor at all. The aoMap and lightMap textures cannot be transformed. No
more than one transform may be used per material. Each use of a texture with a unique
transform will result in an additional GPU texture upload. See
#[link:https://github.com/mrdoob/three.js/pull/13831 13831] and
#[link:https://github.com/mrdoob/three.js/issues/12788 12788].
</i></p>

<h2>Example</h2>

<code>
Expand Down Expand Up @@ -85,30 +97,30 @@ <h2>Browser compatibility</h2>
providing a Promise replacement.</p>

<h2>Textures</h2>

<p>Textures containing color information (.map, .emissiveMap, and .specularMap) always use sRGB colorspace in
glTF, while vertex colors and material properties (.color, .emissive, .specular) use linear colorspace. In a
typical rendering workflow, textures are converted to linear colorspace by the renderer, lighting calculations
are made, then final output is converted back to sRGB and displayed on screen. Unless you need post-processing
in linear colorspace, always configure [page:WebGLRenderer] as follows when using glTF:</p>

<code>
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
</code>

<p>GLTFLoader will automatically configure textures referenced from a .gltf or .glb file correctly, with the
assumption that the renderer is set up as shown above. When loading textures externally (e.g., using
[page:TextureLoader]) and applying them to a glTF model, colorspace and orientation must be given:</p>

<code>
// If texture is used for color information, set colorspace.
texture.encoding = THREE.sRGBEncoding;

// UVs use the convention that (0, 0) corresponds to the upper left corner of a texture.
texture.flipY = false;
</code>
</code>

<h2>Custom extensions</h2>

<p>
Expand Down
11 changes: 11 additions & 0 deletions docs/page.css
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,14 @@ span.param {
a.param:hover {
color: #777;
}

sup, sub {
/* prevent superscript and subscript elements from affecting line-height */
vertical-align: baseline;
position: relative;
top: -0.4em;
}

sub {
top: 0.4em;
}
103 changes: 83 additions & 20 deletions examples/js/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,19 @@ THREE.GLTFLoader = ( function () {
break;

case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension( json );
break;

case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
break;

case EXTENSIONS.MSFT_TEXTURE_DDS:
extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension();
extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension( json );
break;

case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension( json );
break;

default:
Expand Down Expand Up @@ -282,6 +286,7 @@ THREE.GLTFLoader = ( function () {
KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
};

Expand Down Expand Up @@ -409,7 +414,7 @@ THREE.GLTFLoader = ( function () {

if ( metallicRoughness.baseColorTexture !== undefined ) {

pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture.index ) );
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );

}

Expand Down Expand Up @@ -563,6 +568,51 @@ THREE.GLTFLoader = ( function () {

};

/**
* Texture Transform Extension
*
* Specification:
*/
function GLTFTextureTransformExtension( json ) {

this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;

}

GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) {

texture = texture.clone();

if ( transform.offset !== undefined ) {

texture.offset.fromArray( transform.offset );

}

if ( transform.rotation !== undefined ) {

texture.rotation = transform.rotation;

}

if ( transform.scale !== undefined ) {

texture.repeat.fromArray( transform.scale );

}

if ( transform.texCoord !== undefined ) {

console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );

}

texture.needsUpdate = true;

return texture;

};

/**
* Specular-Glossiness Extension
*
Expand Down Expand Up @@ -692,7 +742,7 @@ THREE.GLTFLoader = ( function () {

if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {

pending.push( parser.assignTexture( params, 'map', pbrSpecularGlossiness.diffuseTexture.index ) );
pending.push( parser.assignTexture( params, 'map', pbrSpecularGlossiness.diffuseTexture ) );

}

Expand All @@ -708,9 +758,9 @@ THREE.GLTFLoader = ( function () {

if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {

var specGlossIndex = pbrSpecularGlossiness.specularGlossinessTexture.index;
pending.push( parser.assignTexture( params, 'glossinessMap', specGlossIndex ) );
pending.push( parser.assignTexture( params, 'specularMap', specGlossIndex ) );
var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
pending.push( parser.assignTexture( params, 'glossinessMap', specGlossMapDef ) );
pending.push( parser.assignTexture( params, 'specularMap', specGlossMapDef ) );

}

Expand Down Expand Up @@ -2146,15 +2196,29 @@ THREE.GLTFLoader = ( function () {
/**
* Asynchronously assigns a texture to the given material parameters.
* @param {Object} materialParams
* @param {string} textureName
* @param {number} textureIndex
* @return {Promise<THREE.Texture>}
* @param {string} mapName
* @param {Object} mapDef
* @return {Promise}
*/
GLTFParser.prototype.assignTexture = function ( materialParams, textureName, textureIndex ) {
GLTFParser.prototype.assignTexture = function ( materialParams, mapName, mapDef ) {

var parser = this;

return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) {

if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) {

var transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined;

return this.getDependency( 'texture', textureIndex ).then( function ( texture ) {
if ( transform ) {

texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform );

}

}

materialParams[ textureName ] = texture;
materialParams[ mapName ] = texture;

} );

Expand Down Expand Up @@ -2213,7 +2277,7 @@ THREE.GLTFLoader = ( function () {

if ( metallicRoughness.baseColorTexture !== undefined ) {

pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture.index ) );
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );

}

Expand All @@ -2222,9 +2286,8 @@ THREE.GLTFLoader = ( function () {

if ( metallicRoughness.metallicRoughnessTexture !== undefined ) {

var textureIndex = metallicRoughness.metallicRoughnessTexture.index;
pending.push( parser.assignTexture( materialParams, 'metalnessMap', textureIndex ) );
pending.push( parser.assignTexture( materialParams, 'roughnessMap', textureIndex ) );
pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) );
pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) );

}

Expand Down Expand Up @@ -2256,7 +2319,7 @@ THREE.GLTFLoader = ( function () {

if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {

pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture.index ) );
pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );

materialParams.normalScale = new THREE.Vector2( 1, 1 );

Expand All @@ -2270,7 +2333,7 @@ THREE.GLTFLoader = ( function () {

if ( materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {

pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture.index ) );
pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) );

if ( materialDef.occlusionTexture.strength !== undefined ) {

Expand All @@ -2288,7 +2351,7 @@ THREE.GLTFLoader = ( function () {

if ( materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {

pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture.index ) );
pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) );

}

Expand Down

0 comments on commit dab285d

Please sign in to comment.