import {
NodeMaterial,
BoxGeometry,
BufferAttribute,
Mesh,
PlaneGeometry,
DoubleSide,
Vector3,
} from 'three';
import { texture as textureNode, cubeTexture, texture3D, float, vec4, attribute } from 'three/tsl';
import { mergeGeometries } from '../utils/BufferGeometryUtils.js';
class TextureHelper extends Mesh {
constructor( texture, width = 1, height = 1, depth = 1 ) {
const material = new NodeMaterial();
material.side = DoubleSide;
material.transparent = true;
material.name = 'TextureHelper';
let colorNode;
const uvw = attribute( 'uvw' );
if ( texture.isCubeTexture ) {
colorNode = cubeTexture( texture ).sample( uvw );
} else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
colorNode = texture3D( texture ).sample( uvw );
} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
colorNode = textureNode( texture ).sample( uvw.xy ).depth( uvw.z );
} else {
colorNode = textureNode( texture );
}
const alphaNode = float( getAlpha( texture ) );
material.colorNode = vec4( colorNode.rgb, alphaNode );
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 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 };