Texture.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import { EventDispatcher } from '../core/EventDispatcher.js';
  2. import {
  3. MirroredRepeatWrapping,
  4. ClampToEdgeWrapping,
  5. RepeatWrapping,
  6. LinearEncoding,
  7. UnsignedByteType,
  8. RGBAFormat,
  9. LinearMipmapLinearFilter,
  10. LinearFilter,
  11. UVMapping
  12. } from '../constants.js';
  13. import * as MathUtils from '../math/MathUtils.js';
  14. import { Vector2 } from '../math/Vector2.js';
  15. import { Matrix3 } from '../math/Matrix3.js';
  16. import { ImageUtils } from '../extras/ImageUtils.js';
  17. let textureId = 0;
  18. class Texture extends EventDispatcher {
  19. constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) {
  20. super();
  21. Object.defineProperty( this, 'id', { value: textureId ++ } );
  22. this.uuid = MathUtils.generateUUID();
  23. this.name = '';
  24. this.image = image;
  25. this.mipmaps = [];
  26. this.mapping = mapping;
  27. this.wrapS = wrapS;
  28. this.wrapT = wrapT;
  29. this.magFilter = magFilter;
  30. this.minFilter = minFilter;
  31. this.anisotropy = anisotropy;
  32. this.format = format;
  33. this.internalFormat = null;
  34. this.type = type;
  35. this.offset = new Vector2( 0, 0 );
  36. this.repeat = new Vector2( 1, 1 );
  37. this.center = new Vector2( 0, 0 );
  38. this.rotation = 0;
  39. this.matrixAutoUpdate = true;
  40. this.matrix = new Matrix3();
  41. this.generateMipmaps = true;
  42. this.premultiplyAlpha = false;
  43. this.flipY = true;
  44. this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
  45. // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
  46. //
  47. // Also changing the encoding after already used by a Material will not automatically make the Material
  48. // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.
  49. this.encoding = encoding;
  50. this.userData = {};
  51. this.version = 0;
  52. this.onUpdate = null;
  53. this.isRenderTargetTexture = false;
  54. }
  55. updateMatrix() {
  56. this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
  57. }
  58. clone() {
  59. return new this.constructor().copy( this );
  60. }
  61. copy( source ) {
  62. this.name = source.name;
  63. this.image = source.image;
  64. this.mipmaps = source.mipmaps.slice( 0 );
  65. this.mapping = source.mapping;
  66. this.wrapS = source.wrapS;
  67. this.wrapT = source.wrapT;
  68. this.magFilter = source.magFilter;
  69. this.minFilter = source.minFilter;
  70. this.anisotropy = source.anisotropy;
  71. this.format = source.format;
  72. this.internalFormat = source.internalFormat;
  73. this.type = source.type;
  74. this.offset.copy( source.offset );
  75. this.repeat.copy( source.repeat );
  76. this.center.copy( source.center );
  77. this.rotation = source.rotation;
  78. this.matrixAutoUpdate = source.matrixAutoUpdate;
  79. this.matrix.copy( source.matrix );
  80. this.generateMipmaps = source.generateMipmaps;
  81. this.premultiplyAlpha = source.premultiplyAlpha;
  82. this.flipY = source.flipY;
  83. this.unpackAlignment = source.unpackAlignment;
  84. this.encoding = source.encoding;
  85. this.userData = JSON.parse( JSON.stringify( source.userData ) );
  86. return this;
  87. }
  88. toJSON( meta ) {
  89. const isRootObject = ( meta === undefined || typeof meta === 'string' );
  90. if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
  91. return meta.textures[ this.uuid ];
  92. }
  93. const output = {
  94. metadata: {
  95. version: 4.5,
  96. type: 'Texture',
  97. generator: 'Texture.toJSON'
  98. },
  99. uuid: this.uuid,
  100. name: this.name,
  101. mapping: this.mapping,
  102. repeat: [ this.repeat.x, this.repeat.y ],
  103. offset: [ this.offset.x, this.offset.y ],
  104. center: [ this.center.x, this.center.y ],
  105. rotation: this.rotation,
  106. wrap: [ this.wrapS, this.wrapT ],
  107. format: this.format,
  108. type: this.type,
  109. encoding: this.encoding,
  110. minFilter: this.minFilter,
  111. magFilter: this.magFilter,
  112. anisotropy: this.anisotropy,
  113. flipY: this.flipY,
  114. premultiplyAlpha: this.premultiplyAlpha,
  115. unpackAlignment: this.unpackAlignment
  116. };
  117. if ( this.image !== undefined ) {
  118. // TODO: Move to THREE.Image
  119. const image = this.image;
  120. if ( image.uuid === undefined ) {
  121. image.uuid = MathUtils.generateUUID(); // UGH
  122. }
  123. if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {
  124. let url;
  125. if ( Array.isArray( image ) ) {
  126. // process array of images e.g. CubeTexture
  127. url = [];
  128. for ( let i = 0, l = image.length; i < l; i ++ ) {
  129. // check cube texture with data textures
  130. if ( image[ i ].isDataTexture ) {
  131. url.push( serializeImage( image[ i ].image ) );
  132. } else {
  133. url.push( serializeImage( image[ i ] ) );
  134. }
  135. }
  136. } else {
  137. // process single image
  138. url = serializeImage( image );
  139. }
  140. meta.images[ image.uuid ] = {
  141. uuid: image.uuid,
  142. url: url
  143. };
  144. }
  145. output.image = image.uuid;
  146. }
  147. if ( JSON.stringify( this.userData ) !== '{}' ) output.userData = this.userData;
  148. if ( ! isRootObject ) {
  149. meta.textures[ this.uuid ] = output;
  150. }
  151. return output;
  152. }
  153. dispose() {
  154. this.dispatchEvent( { type: 'dispose' } );
  155. }
  156. transformUv( uv ) {
  157. if ( this.mapping !== UVMapping ) return uv;
  158. uv.applyMatrix3( this.matrix );
  159. if ( uv.x < 0 || uv.x > 1 ) {
  160. switch ( this.wrapS ) {
  161. case RepeatWrapping:
  162. uv.x = uv.x - Math.floor( uv.x );
  163. break;
  164. case ClampToEdgeWrapping:
  165. uv.x = uv.x < 0 ? 0 : 1;
  166. break;
  167. case MirroredRepeatWrapping:
  168. if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
  169. uv.x = Math.ceil( uv.x ) - uv.x;
  170. } else {
  171. uv.x = uv.x - Math.floor( uv.x );
  172. }
  173. break;
  174. }
  175. }
  176. if ( uv.y < 0 || uv.y > 1 ) {
  177. switch ( this.wrapT ) {
  178. case RepeatWrapping:
  179. uv.y = uv.y - Math.floor( uv.y );
  180. break;
  181. case ClampToEdgeWrapping:
  182. uv.y = uv.y < 0 ? 0 : 1;
  183. break;
  184. case MirroredRepeatWrapping:
  185. if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
  186. uv.y = Math.ceil( uv.y ) - uv.y;
  187. } else {
  188. uv.y = uv.y - Math.floor( uv.y );
  189. }
  190. break;
  191. }
  192. }
  193. if ( this.flipY ) {
  194. uv.y = 1 - uv.y;
  195. }
  196. return uv;
  197. }
  198. set needsUpdate( value ) {
  199. if ( value === true ) this.version ++;
  200. }
  201. }
  202. Texture.DEFAULT_IMAGE = undefined;
  203. Texture.DEFAULT_MAPPING = UVMapping;
  204. Texture.prototype.isTexture = true;
  205. function serializeImage( image ) {
  206. if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
  207. ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
  208. ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
  209. // default images
  210. return ImageUtils.getDataURL( image );
  211. } else {
  212. if ( image.data ) {
  213. // images of DataTexture
  214. return {
  215. data: Array.prototype.slice.call( image.data ),
  216. width: image.width,
  217. height: image.height,
  218. type: image.data.constructor.name
  219. };
  220. } else {
  221. console.warn( 'THREE.Texture: Unable to serialize Texture.' );
  222. return {};
  223. }
  224. }
  225. }
  226. export { Texture };