UNPKG

three

Version:

JavaScript 3D library

684 lines (538 loc) 16 kB
import { EventDispatcher } from '../core/EventDispatcher.js'; import { MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, UnsignedByteType, RGBAFormat, LinearMipmapLinearFilter, LinearFilter, UVMapping, NoColorSpace, } from '../constants.js'; import { generateUUID } from '../math/MathUtils.js'; import { Vector2 } from '../math/Vector2.js'; import { Matrix3 } from '../math/Matrix3.js'; import { Source } from './Source.js'; let _textureId = 0; /** * Base class for all textures. * * Note: After the initial use of a texture, its dimensions, format, and type * cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one. * * @augments EventDispatcher */ class Texture extends EventDispatcher { /** * Constructs a new texture. * * @param {?Object} [image=Texture.DEFAULT_IMAGE] - The image holding the texture data. * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. * @param {number} [magFilter=LinearFilter] - The mag filter value. * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. * @param {number} [format=RGBAFormat] - The texture format. * @param {number} [type=UnsignedByteType] - The texture type. * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. * @param {string} [colorSpace=NoColorSpace] - The color space. */ constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { super(); /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isTexture = true; /** * The ID of the texture. * * @name Texture#id * @type {number} * @readonly */ Object.defineProperty( this, 'id', { value: _textureId ++ } ); /** * The UUID of the material. * * @type {string} * @readonly */ this.uuid = generateUUID(); /** * The name of the material. * * @type {string} */ this.name = ''; /** * The data definition of a texture. A reference to the data source can be * shared across textures. This is often useful in context of spritesheets * where multiple textures render the same data but with different texture * transformations. * * @type {Source} */ this.source = new Source( image ); /** * An array holding user-defined mipmaps. * * @type {Array<Object>} */ this.mipmaps = []; /** * How the texture is applied to the object. The value `UVMapping` * is the default, where texture or uv coordinates are used to apply the map. * * @type {(UVMapping|CubeReflectionMapping|CubeRefractionMapping|EquirectangularReflectionMapping|EquirectangularRefractionMapping|CubeUVReflectionMapping)} * @default UVMapping */ this.mapping = mapping; /** * Lets you select the uv attribute to map the texture to. `0` for `uv`, * `1` for `uv1`, `2` for `uv2` and `3` for `uv3`. * * @type {number} * @default 0 */ this.channel = 0; /** * This defines how the texture is wrapped horizontally and corresponds to * *U* in UV mapping. * * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} * @default ClampToEdgeWrapping */ this.wrapS = wrapS; /** * This defines how the texture is wrapped horizontally and corresponds to * *V* in UV mapping. * * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} * @default ClampToEdgeWrapping */ this.wrapT = wrapT; /** * How the texture is sampled when a texel covers more than one pixel. * * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} * @default LinearFilter */ this.magFilter = magFilter; /** * How the texture is sampled when a texel covers less than one pixel. * * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} * @default LinearMipmapLinearFilter */ this.minFilter = minFilter; /** * The number of samples taken along the axis through the pixel that has the * highest density of texels. By default, this value is `1`. A higher value * gives a less blurry result than a basic mipmap, at the cost of more * texture samples being used. * * @type {number} * @default 0 */ this.anisotropy = anisotropy; /** * The format of the texture. * * @type {number} * @default RGBAFormat */ this.format = format; /** * The default internal format is derived from {@link Texture#format} and {@link Texture#type} and * defines how the texture data is going to be stored on the GPU. * * This property allows to overwrite the default format. * * @type {?string} * @default null */ this.internalFormat = null; /** * The data type of the texture. * * @type {number} * @default UnsignedByteType */ this.type = type; /** * How much a single repetition of the texture is offset from the beginning, * in each direction U and V. Typical range is `0.0` to `1.0`. * * @type {Vector2} * @default (0,0) */ this.offset = new Vector2( 0, 0 ); /** * How many times the texture is repeated across the surface, in each * direction U and V. If repeat is set greater than `1` in either direction, * the corresponding wrap parameter should also be set to `RepeatWrapping` * or `MirroredRepeatWrapping` to achieve the desired tiling effect. * * @type {Vector2} * @default (1,1) */ this.repeat = new Vector2( 1, 1 ); /** * The point around which rotation occurs. A value of `(0.5, 0.5)` corresponds * to the center of the texture. Default is `(0, 0)`, the lower left. * * @type {Vector2} * @default (0,0) */ this.center = new Vector2( 0, 0 ); /** * How much the texture is rotated around the center point, in radians. * Positive values are counter-clockwise. * * @type {number} * @default 0 */ this.rotation = 0; /** * Whether to update the texture's uv-transformation {@link Texture#matrix} * from the properties {@link Texture#offset}, {@link Texture#repeat}, * {@link Texture#rotation}, and {@link Texture#center}. * * Set this to `false` if you are specifying the uv-transform matrix directly. * * @type {boolean} * @default true */ this.matrixAutoUpdate = true; /** * The uv-transformation matrix of the texture. * * @type {Matrix3} */ this.matrix = new Matrix3(); /** * Whether to generate mipmaps (if possible) for a texture. * * Set this to `false` if you are creating mipmaps manually. * * @type {boolean} * @default true */ this.generateMipmaps = true; /** * If set to `true`, the alpha channel, if present, is multiplied into the * color channels when the texture is uploaded to the GPU. * * Note that this property has no effect when using `ImageBitmap`. You need to * configure premultiply alpha on bitmap creation instead. * * @type {boolean} * @default false */ this.premultiplyAlpha = false; /** * If set to `true`, the texture is flipped along the vertical axis when * uploaded to the GPU. * * Note that this property has no effect when using `ImageBitmap`. You need to * configure the flip on bitmap creation instead. * * @type {boolean} * @default true */ this.flipY = true; /** * Specifies the alignment requirements for the start of each pixel row in memory. * The allowable values are `1` (byte-alignment), `2` (rows aligned to even-numbered bytes), * `4` (word-alignment), and `8` (rows start on double-word boundaries). * * @type {number} * @default 4 */ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) /** * Textures containing color data should be annotated with `SRGBColorSpace` or `LinearSRGBColorSpace`. * * @type {string} * @default NoColorSpace */ this.colorSpace = colorSpace; /** * An object that can be used to store custom data about the texture. It * should not hold references to functions as these will not be cloned. * * @type {Object} */ this.userData = {}; /** * This starts at `0` and counts how many times {@link Texture#needsUpdate} is set to `true`. * * @type {number} * @readonly * @default 0 */ this.version = 0; /** * A callback function, called when the texture is updated (e.g., when * {@link Texture#needsUpdate} has been set to true and then the texture is used). * * @type {?Function} * @default null */ this.onUpdate = null; /** * An optional back reference to the textures render target. * * @type {?(RenderTarget|WebGLRenderTarget)} * @default null */ this.renderTarget = null; /** * Indicates whether a texture belongs to a render target or not. * * @type {boolean} * @readonly * @default false */ this.isRenderTargetTexture = false; /** * Indicates whether this texture should be processed by `PMREMGenerator` or not * (only relevant for render target textures). * * @type {number} * @readonly * @default 0 */ this.pmremVersion = 0; } /** * The image object holding the texture data. * * @type {?Object} */ get image() { return this.source.data; } set image( value = null ) { this.source.data = value; } /** * Updates the texture transformation matrix from the from the properties {@link Texture#offset}, * {@link Texture#repeat}, {@link Texture#rotation}, and {@link Texture#center}. */ updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } /** * Returns a new texture with copied values from this instance. * * @return {Texture} A clone of this instance. */ clone() { return new this.constructor().copy( this ); } /** * Copies the values of the given texture to this instance. * * @param {Texture} source - The texture to copy. * @return {Texture} A reference to this instance. */ copy( source ) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.center.copy( source.center ); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy( source.matrix ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.renderTarget = source.renderTarget; this.isRenderTargetTexture = source.isRenderTargetTexture; this.userData = JSON.parse( JSON.stringify( source.userData ) ); this.needsUpdate = true; return this; } /** * Serializes the texture into JSON. * * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. * @return {Object} A JSON object representing the serialized texture. * @see {@link ObjectLoader#parse} */ toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === 'string' ); if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } const output = { metadata: { version: 4.6, type: 'Texture', generator: 'Texture.toJSON' }, uuid: this.uuid, name: this.name, image: this.source.toJSON( meta ).uuid, mapping: this.mapping, channel: this.channel, repeat: [ this.repeat.x, this.repeat.y ], offset: [ this.offset.x, this.offset.y ], center: [ this.center.x, this.center.y ], rotation: this.rotation, wrap: [ this.wrapS, this.wrapT ], format: this.format, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment }; if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; } return output; } /** * Frees the GPU-related resources allocated by this instance. Call this * method whenever this instance is no longer used in your app. * * @fires Texture#dispose */ dispose() { /** * Fires when the texture has been disposed of. * * @event Texture#dispose * @type {Object} */ this.dispatchEvent( { type: 'dispose' } ); } /** * Transforms the given uv vector with the textures uv transformation matrix. * * @param {Vector2} uv - The uv vector. * @return {Vector2} The transformed uv vector. */ transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; uv.applyMatrix3( this.matrix ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } return uv; } /** * Setting this property to `true` indicates the engine the texture * must be updated in the next render. This triggers a texture upload * to the GPU and ensures correct texture parameter configuration. * * @type {boolean} * @default false * @param {boolean} value */ set needsUpdate( value ) { if ( value === true ) { this.version ++; this.source.needsUpdate = true; } } /** * Setting this property to `true` indicates the engine the PMREM * must be regenerated. * * @type {boolean} * @default false * @param {boolean} value */ set needsPMREMUpdate( value ) { if ( value === true ) { this.pmremVersion ++; } } } /** * The default image for all textures. * * @static * @type {?Image} * @default null */ Texture.DEFAULT_IMAGE = null; /** * The default mapping for all textures. * * @static * @type {number} * @default UVMapping */ Texture.DEFAULT_MAPPING = UVMapping; /** * The default anisotropy value for all textures. * * @static * @type {number} * @default 1 */ Texture.DEFAULT_ANISOTROPY = 1; export { Texture };