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 };