154 lines
3.1 KiB
JavaScript
154 lines
3.1 KiB
JavaScript
|
// https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
|
||
|
|
||
|
import {
|
||
|
ClampToEdgeWrapping,
|
||
|
Data3DTexture,
|
||
|
FileLoader,
|
||
|
FloatType,
|
||
|
LinearFilter,
|
||
|
Loader,
|
||
|
UnsignedByteType,
|
||
|
Vector3,
|
||
|
} from 'three';
|
||
|
|
||
|
export class LUTCubeLoader extends Loader {
|
||
|
|
||
|
constructor( manager ) {
|
||
|
|
||
|
super( manager );
|
||
|
|
||
|
this.type = UnsignedByteType;
|
||
|
|
||
|
}
|
||
|
|
||
|
setType( type ) {
|
||
|
|
||
|
if ( type !== UnsignedByteType && type !== FloatType ) {
|
||
|
|
||
|
throw new Error( 'LUTCubeLoader: Unsupported type' );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.type = type;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
load( url, onLoad, onProgress, onError ) {
|
||
|
|
||
|
const loader = new FileLoader( this.manager );
|
||
|
loader.setPath( this.path );
|
||
|
loader.setResponseType( 'text' );
|
||
|
loader.load( url, text => {
|
||
|
|
||
|
try {
|
||
|
|
||
|
onLoad( this.parse( text ) );
|
||
|
|
||
|
} catch ( e ) {
|
||
|
|
||
|
if ( onError ) {
|
||
|
|
||
|
onError( e );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
console.error( e );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.manager.itemError( url );
|
||
|
|
||
|
}
|
||
|
|
||
|
}, onProgress, onError );
|
||
|
|
||
|
}
|
||
|
|
||
|
parse( input ) {
|
||
|
|
||
|
const regExpTitle = /TITLE +"([^"]*)"/;
|
||
|
const regExpSize = /LUT_3D_SIZE +(\d+)/;
|
||
|
const regExpDomainMin = /DOMAIN_MIN +([\d.]+) +([\d.]+) +([\d.]+)/;
|
||
|
const regExpDomainMax = /DOMAIN_MAX +([\d.]+) +([\d.]+) +([\d.]+)/;
|
||
|
const regExpDataPoints = /^([\d.e+-]+) +([\d.e+-]+) +([\d.e+-]+) *$/gm;
|
||
|
|
||
|
let result = regExpTitle.exec( input );
|
||
|
const title = ( result !== null ) ? result[ 1 ] : null;
|
||
|
|
||
|
result = regExpSize.exec( input );
|
||
|
|
||
|
if ( result === null ) {
|
||
|
|
||
|
throw new Error( 'LUTCubeLoader: Missing LUT_3D_SIZE information' );
|
||
|
|
||
|
}
|
||
|
|
||
|
const size = Number( result[ 1 ] );
|
||
|
const length = size ** 3 * 4;
|
||
|
const data = this.type === UnsignedByteType ? new Uint8Array( length ) : new Float32Array( length );
|
||
|
|
||
|
const domainMin = new Vector3( 0, 0, 0 );
|
||
|
const domainMax = new Vector3( 1, 1, 1 );
|
||
|
|
||
|
result = regExpDomainMin.exec( input );
|
||
|
|
||
|
if ( result !== null ) {
|
||
|
|
||
|
domainMin.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
result = regExpDomainMax.exec( input );
|
||
|
|
||
|
if ( result !== null ) {
|
||
|
|
||
|
domainMax.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( domainMin.x > domainMax.x || domainMin.y > domainMax.y || domainMin.z > domainMax.z ) {
|
||
|
|
||
|
throw new Error( 'LUTCubeLoader: Invalid input domain' );
|
||
|
|
||
|
}
|
||
|
|
||
|
const scale = this.type === UnsignedByteType ? 255 : 1;
|
||
|
let i = 0;
|
||
|
|
||
|
while ( ( result = regExpDataPoints.exec( input ) ) !== null ) {
|
||
|
|
||
|
data[ i ++ ] = Number( result[ 1 ] ) * scale;
|
||
|
data[ i ++ ] = Number( result[ 2 ] ) * scale;
|
||
|
data[ i ++ ] = Number( result[ 3 ] ) * scale;
|
||
|
data[ i ++ ] = scale;
|
||
|
|
||
|
}
|
||
|
|
||
|
const texture3D = new Data3DTexture();
|
||
|
texture3D.image.data = data;
|
||
|
texture3D.image.width = size;
|
||
|
texture3D.image.height = size;
|
||
|
texture3D.image.depth = size;
|
||
|
texture3D.type = this.type;
|
||
|
texture3D.magFilter = LinearFilter;
|
||
|
texture3D.minFilter = LinearFilter;
|
||
|
texture3D.wrapS = ClampToEdgeWrapping;
|
||
|
texture3D.wrapT = ClampToEdgeWrapping;
|
||
|
texture3D.wrapR = ClampToEdgeWrapping;
|
||
|
texture3D.generateMipmaps = false;
|
||
|
texture3D.needsUpdate = true;
|
||
|
|
||
|
return {
|
||
|
title,
|
||
|
size,
|
||
|
domainMin,
|
||
|
domainMax,
|
||
|
texture3D,
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|