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: Add EXT_texture_avif support. #25173

Merged
merged 6 commits into from
Jan 31, 2023
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
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"webgl_loader_gltf_sheen",
"webgl_loader_gltf_transmission",
"webgl_loader_gltf_variants",
"webgl_loader_gltf_avif",
"webgl_loader_ifc",
"webgl_loader_imagebitmap",
"webgl_loader_kmz",
Expand Down
90 changes: 90 additions & 0 deletions examples/jsm/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ class GLTFLoader extends Loader {

} );

this.register( function ( parser ) {

return new GLTFTextureAVIFExtension( parser );

} );

this.register( function ( parser ) {

return new GLTFMaterialsSheenExtension( parser );
Expand Down Expand Up @@ -471,6 +477,7 @@ const EXTENSIONS = {
KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength',
EXT_TEXTURE_WEBP: 'EXT_texture_webp',
EXT_TEXTURE_AVIF: 'EXT_texture_avif',
EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing'
};
Expand Down Expand Up @@ -1314,6 +1321,89 @@ class GLTFTextureWebPExtension {

}

/**
* AVIF Texture Extension
*
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_avif
*/
class GLTFTextureAVIFExtension {
leon marked this conversation as resolved.
Show resolved Hide resolved
Fixed Show fixed Hide fixed

constructor( parser ) {

this.parser = parser;
this.name = EXTENSIONS.EXT_TEXTURE_AVIF;
this.isSupported = null;

}

loadTexture( textureIndex ) {

const name = this.name;
const parser = this.parser;
const json = parser.json;

const textureDef = json.textures[ textureIndex ];

if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) {

return null;

}

const extension = textureDef.extensions[ name ];
const source = json.images[ extension.source ];

let loader = parser.textureLoader;
if ( source.uri ) {

const handler = parser.options.manager.getHandler( source.uri );
if ( handler !== null ) loader = handler;

}

return this.detectSupport().then( function ( isSupported ) {

if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader );

if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) {

throw new Error( 'THREE.GLTFLoader: AVIF required by asset but unsupported.' );

}

// Fall back to PNG or JPEG.
return parser.loadTexture( textureIndex );

} );

}

detectSupport() {

if ( ! this.isSupported ) {

this.isSupported = new Promise( function ( resolve ) {

const image = new Image();

// Lossy test image.
image.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI=';
image.onload = image.onerror = function () {

resolve( image.height === 1 );

};

} );

}

return this.isSupported;

}

}

/**
* meshopt BufferView Compression Extension
*
Expand Down
Binary file not shown.
Binary file added examples/models/gltf/AVIFTest/avif-test.avif
Binary file not shown.
Binary file added examples/models/gltf/AVIFTest/avif-test.bin
Binary file not shown.
134 changes: 134 additions & 0 deletions examples/models/gltf/AVIFTest/avif-test.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
{
"asset": {
"generator": "Khronos glTF Blender I/O v3.4.50",
"version": "2.0"
},
"extensionsUsed": ["EXT_texture_avif"],
"extensionsRequired": ["EXT_texture_avif"],
"scene": 0,
"scenes": [
{
"name": "Scene",
"nodes": [0]
}
],
"nodes": [
{
"mesh": 0,
"name": "Cube"
}
],
"materials": [
{
"doubleSided": true,
"name": "Material",
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0,
"roughnessFactor": 0
}
}
],
"meshes": [
{
"name": "Cube",
"primitives": [
{
"attributes": {
"POSITION": 0,
"TEXCOORD_0": 1,
"NORMAL": 2
},
"indices": 3,
"material": 0
}
]
}
],
"textures": [
{
"extensions": {
"EXT_texture_avif": {
"source": 0
}
},
"sampler": 0,
"source": 0
}
],
"images": [
{
"mimeType": "image/avif",
"name": "avif-test",
"uri": "avif-test.avif"
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 864,
"max": [1, 1, 1],
"min": [-1, -1, -1],
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 864,
"type": "VEC2"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 864,
"type": "VEC3"
},
{
"bufferView": 3,
"componentType": 5123,
"count": 1284,
"type": "SCALAR"
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 10368,
"byteOffset": 0,
"target": 34962
},
{
"buffer": 0,
"byteLength": 6912,
"byteOffset": 10368,
"target": 34962
},
{
"buffer": 0,
"byteLength": 10368,
"byteOffset": 17280,
"target": 34962
},
{
"buffer": 0,
"byteLength": 2568,
"byteOffset": 27648,
"target": 34963
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9987
}
],
"buffers": [
{
"byteLength": 30216,
"uri": "avif-test.bin"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
131 changes: 131 additions & 0 deletions examples/webgl_loader_gltf_avif.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - GLTFloader + EXT_texture_avif</title>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<link type="text/css" rel="stylesheet" href="main.css" />
</head>

<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a>
- GLTFLoader +
<a
href="https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_texture_avif"
target="_blank"
rel="noopener"
>EXT_texture_avif</a
><br />
</div>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script
async
src="https://unpkg.com/[email protected]/dist/es-module-shims.js"
></script>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">
import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

let camera, scene, renderer;

init();
render();

function init() {

const container = document.createElement( 'div' );
document.body.appendChild( container );

camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.25,
20
);
camera.position.set( 2.5, 0, 3.0 );

scene = new THREE.Scene();

new RGBELoader()
.setPath( 'textures/equirectangular/' )
.load( 'quarry_01_1k.hdr', function ( texture ) {

texture.mapping = THREE.EquirectangularReflectionMapping;

scene.background = texture;
scene.environment = texture;

render();

// model

const loader = new GLTFLoader().setPath( 'models/gltf/AVIFTest/' );
loader.load( 'DamagedHelmetAVIF.glb', function ( gltf ) {

scene.add( gltf.scene );

render();

} );

} );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild( renderer.domElement );

const controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 2;
controls.maxDistance = 10;
controls.target.set( 0, 0, 0);
controls.update();

window.addEventListener( 'resize', onWindowResize );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

render();

}

//

function render() {

renderer.render( scene, camera );

}
</script>
</body>
</html>