1
0

LUTCubeLoader.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
  2. import {
  3. ClampToEdgeWrapping,
  4. Data3DTexture,
  5. FileLoader,
  6. FloatType,
  7. LinearFilter,
  8. Loader,
  9. UnsignedByteType,
  10. Vector3,
  11. } from 'three';
  12. export class LUTCubeLoader extends Loader {
  13. constructor( manager ) {
  14. super( manager );
  15. this.type = UnsignedByteType;
  16. }
  17. setType( type ) {
  18. if ( type !== UnsignedByteType && type !== FloatType ) {
  19. throw new Error( 'LUTCubeLoader: Unsupported type' );
  20. }
  21. this.type = type;
  22. return this;
  23. }
  24. load( url, onLoad, onProgress, onError ) {
  25. const loader = new FileLoader( this.manager );
  26. loader.setPath( this.path );
  27. loader.setResponseType( 'text' );
  28. loader.load( url, text => {
  29. try {
  30. onLoad( this.parse( text ) );
  31. } catch ( e ) {
  32. if ( onError ) {
  33. onError( e );
  34. } else {
  35. console.error( e );
  36. }
  37. this.manager.itemError( url );
  38. }
  39. }, onProgress, onError );
  40. }
  41. parse( input ) {
  42. const regExpTitle = /TITLE +"([^"]*)"/;
  43. const regExpSize = /LUT_3D_SIZE +(\d+)/;
  44. const regExpDomainMin = /DOMAIN_MIN +([\d.]+) +([\d.]+) +([\d.]+)/;
  45. const regExpDomainMax = /DOMAIN_MAX +([\d.]+) +([\d.]+) +([\d.]+)/;
  46. const regExpDataPoints = /^([\d.e+-]+) +([\d.e+-]+) +([\d.e+-]+) *$/gm;
  47. let result = regExpTitle.exec( input );
  48. const title = ( result !== null ) ? result[ 1 ] : null;
  49. result = regExpSize.exec( input );
  50. if ( result === null ) {
  51. throw new Error( 'LUTCubeLoader: Missing LUT_3D_SIZE information' );
  52. }
  53. const size = Number( result[ 1 ] );
  54. const length = size ** 3 * 4;
  55. const data = this.type === UnsignedByteType ? new Uint8Array( length ) : new Float32Array( length );
  56. const domainMin = new Vector3( 0, 0, 0 );
  57. const domainMax = new Vector3( 1, 1, 1 );
  58. result = regExpDomainMin.exec( input );
  59. if ( result !== null ) {
  60. domainMin.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
  61. }
  62. result = regExpDomainMax.exec( input );
  63. if ( result !== null ) {
  64. domainMax.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
  65. }
  66. if ( domainMin.x > domainMax.x || domainMin.y > domainMax.y || domainMin.z > domainMax.z ) {
  67. throw new Error( 'LUTCubeLoader: Invalid input domain' );
  68. }
  69. const scale = this.type === UnsignedByteType ? 255 : 1;
  70. let i = 0;
  71. while ( ( result = regExpDataPoints.exec( input ) ) !== null ) {
  72. data[ i ++ ] = Number( result[ 1 ] ) * scale;
  73. data[ i ++ ] = Number( result[ 2 ] ) * scale;
  74. data[ i ++ ] = Number( result[ 3 ] ) * scale;
  75. data[ i ++ ] = scale;
  76. }
  77. const texture3D = new Data3DTexture();
  78. texture3D.image.data = data;
  79. texture3D.image.width = size;
  80. texture3D.image.height = size;
  81. texture3D.image.depth = size;
  82. texture3D.type = this.type;
  83. texture3D.magFilter = LinearFilter;
  84. texture3D.minFilter = LinearFilter;
  85. texture3D.wrapS = ClampToEdgeWrapping;
  86. texture3D.wrapT = ClampToEdgeWrapping;
  87. texture3D.wrapR = ClampToEdgeWrapping;
  88. texture3D.generateMipmaps = false;
  89. texture3D.needsUpdate = true;
  90. return {
  91. title,
  92. size,
  93. domainMin,
  94. domainMax,
  95. texture3D,
  96. };
  97. }
  98. }