From 7b39d56092d600627bd5838297a336494656c198 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Wed, 8 Nov 2023 11:25:49 -0500 Subject: [PATCH] Add TextureHelper --- examples/jsm/Addons.js | 1 + examples/jsm/helpers/TextureHelper.js | 237 ++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 examples/jsm/helpers/TextureHelper.js diff --git a/examples/jsm/Addons.js b/examples/jsm/Addons.js index 7e273d41a03ea5..43d9df71c9ff41 100644 --- a/examples/jsm/Addons.js +++ b/examples/jsm/Addons.js @@ -60,6 +60,7 @@ export * from './helpers/LightProbeHelper.js'; export * from './helpers/OctreeHelper.js'; export * from './helpers/PositionalAudioHelper.js'; export * from './helpers/RectAreaLightHelper.js'; +export * from './helpers/TextureHelper.js'; export * from './helpers/VertexNormalsHelper.js'; export * from './helpers/VertexTangentsHelper.js'; export * from './helpers/ViewHelper.js'; diff --git a/examples/jsm/helpers/TextureHelper.js b/examples/jsm/helpers/TextureHelper.js new file mode 100644 index 00000000000000..00f8e539a4cada --- /dev/null +++ b/examples/jsm/helpers/TextureHelper.js @@ -0,0 +1,237 @@ +import { + BoxGeometry, + BufferAttribute, + DoubleSide, + Mesh, + PlaneGeometry, + ShaderMaterial, + Vector3, +} from 'three'; +import { mergeGeometries } from '../utils/BufferGeometryUtils.js'; + +class TextureHelper extends Mesh { + + constructor( texture, width = 1, height = 1, depth = 1 ) { + + const material = new ShaderMaterial( { + + type: 'TextureHelperMaterial', + + side: DoubleSide, + transparent: true, + + uniforms: { + + map: { value: texture }, + alpha: { value: getAlpha( texture ) }, + + }, + + vertexShader: [ + + 'attribute vec3 uvw;', + + 'varying vec3 vUvw;', + + 'void main() {', + + ' vUvw = uvw;', + + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + + '}', + + ].join( '\n' ), + + fragmentShader: [ + + 'precision highp float;', + + 'precision highp sampler2DArray;', + + 'precision highp sampler3D;', + + 'uniform {samplerType} map;', + + 'uniform float alpha;', + + 'varying vec3 vUvw;', + + 'vec4 textureHelper( in sampler2D map ) { return texture( map, vUvw.xy ); }', + + 'vec4 textureHelper( in sampler2DArray map ) { return texture( map, vUvw ); }', + + 'vec4 textureHelper( in sampler3D map ) { return texture( map, vUvw ); }', + + 'vec4 textureHelper( in samplerCube map ) { return texture( map, vUvw ); }', + + 'void main() {', + + ' gl_FragColor = linearToOutputTexel( vec4( textureHelper( map ).xyz, alpha ) );', + + '}' + + ].join( '\n' ).replace( '{samplerType}', getSamplerType( texture ) ) + + } ); + + const geometry = texture.isCubeTexture + ? createCubeGeometry( width, height, depth ) + : createSliceGeometry( texture, width, height, depth ); + + super( geometry, material ); + + this.texture = texture; + this.type = 'TextureHelper'; + + } + + dispose() { + + this.geometry.dispose(); + this.material.dispose(); + + } + +} + +function getSamplerType( texture ) { + + if ( texture.isCubeTexture ) { + + return 'samplerCube'; + + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + + return 'sampler2DArray'; + + } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) { + + return 'sampler3D'; + + } else { + + return 'sampler2D'; + + } + +} + +function getImageCount( texture ) { + + if ( texture.isCubeTexture ) { + + return 6; + + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + + return texture.image.depth; + + } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) { + + return texture.image.depth; + + } else { + + return 1; + + } + +} + +function getAlpha( texture ) { + + if ( texture.isCubeTexture ) { + + return 1; + + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + + return Math.max( 1 / texture.image.depth, 0.25 ); + + } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) { + + return Math.max( 1 / texture.image.depth, 0.25 ); + + } else { + + return 1; + + } + +} + +function createCubeGeometry( width, height, depth ) { + + const geometry = new BoxGeometry( width, height, depth ); + + const position = geometry.attributes.position; + const uv = geometry.attributes.uv; + const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 ); + + const _direction = new Vector3(); + + for ( let j = 0, jl = uv.count; j < jl; ++ j ) { + + _direction.fromBufferAttribute( position, j ).normalize(); + + const u = _direction.x; + const v = _direction.y; + const w = _direction.z; + + uvw.setXYZ( j, u, v, w ); + + } + + geometry.deleteAttribute( 'uv' ); + geometry.setAttribute( 'uvw', uvw ); + + return geometry; + +} + +function createSliceGeometry( texture, width, height, depth ) { + + const sliceCount = getImageCount( texture ); + + const geometries = []; + + for ( let i = 0; i < sliceCount; ++ i ) { + + const geometry = new PlaneGeometry( width, height ); + + if ( sliceCount > 1 ) { + + geometry.translate( 0, 0, depth * ( i / ( sliceCount - 1 ) - 0.5 ) ); + + } + + const uv = geometry.attributes.uv; + const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 ); + + for ( let j = 0, jl = uv.count; j < jl; ++ j ) { + + const u = uv.getX( j ); + const v = texture.flipY ? uv.getY( j ) : 1 - uv.getY( j ); + const w = sliceCount === 1 + ? 1 + : texture.isDataArrayTexture || texture.isCompressedArrayTexture + ? i + : i / ( sliceCount - 1 ); + + uvw.setXYZ( j, u, v, w ); + + } + + geometry.deleteAttribute( 'uv' ); + geometry.setAttribute( 'uvw', uvw ); + + geometries.push( geometry ); + + } + + return mergeGeometries( geometries ); + +} + +export { TextureHelper }; \ No newline at end of file