Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add EXRLoader example #12891

Merged
merged 2 commits into from
Dec 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 346 additions & 0 deletions examples/js/loaders/EXRLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
/**
* @author Richard M. / https://github.com/richardmonette
*/

// https://github.com/mrdoob/three.js/issues/10652
// https://en.wikipedia.org/wiki/OpenEXR

THREE.EXRLoader = function ( manager ) {

this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.EXRLoader.prototype = Object.create( THREE.DataTextureLoader.prototype );

THREE.EXRLoader.prototype._parser = function ( buffer ) {

var parseNullTerminatedString = function( buffer, offset ) {

var uintBuffer = new Uint8Array( buffer );
var endOffset = 0;

while ( uintBuffer[ offset.value + endOffset ] != 0 ) {

endOffset += 1;

}

var stringValue = new TextDecoder().decode(
new Uint8Array( buffer ).slice( offset.value, offset.value + endOffset )
);

offset.value = offset.value + endOffset + 1;

return stringValue;

}

var parseFixedLengthString = function( buffer, offset, size ) {

var stringValue = new TextDecoder().decode(
new Uint8Array( buffer ).slice( offset.value, offset.value + size )
);

offset.value = offset.value + size;

return stringValue;

}

var parseUlong = function( buffer, offset ) {

var uLong = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );

offset.value = offset.value + 8;

return uLong;

}

var parseUint32 = function( buffer, offset ) {

var Uint32 = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );

offset.value = offset.value + 4;

return Uint32;

}

var parseUint8 = function( buffer, offset ) {

var Uint8 = new DataView( buffer.slice( offset.value, offset.value + 1 ) ).getUint8( 0, true );

offset.value = offset.value + 1;

return Uint8;

}

var parseFloat32 = function( buffer, offset ) {

var float = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getFloat32( 0, true );

offset.value += 4;

return float;

}

// https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript
var decodeFloat16 = function( binary ) {

var exponent = ( binary & 0x7C00 ) >> 10,
fraction = binary & 0x03FF;

return ( binary >> 15 ? - 1 : 1 ) * (
exponent ?
(
exponent === 0x1F ?
fraction ? NaN : Infinity :
Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 )
) :
6.103515625e-5 * ( fraction / 0x400 )
);

}

var parseFloat16 = function( buffer, offset ) {

var float = new DataView( buffer.slice( offset.value, offset.value + 2 ) ).getUint16( 0, true );

offset.value += 2;

return decodeFloat16( float );

}

var parseChlist = function( buffer, offset, size ) {

var startOffset = offset.value;
var channels = [];

while ( offset.value < ( startOffset + size - 1 ) ) {

var name = parseNullTerminatedString( buffer, offset );
var pixelType = parseUint32( buffer, offset ); // TODO: Cast this to UINT, HALF or FLOAT
var pLinear = parseUint8( buffer, offset );
offset.value += 3; // reserved, three chars
var xSampling = parseUint32( buffer, offset );
var ySampling = parseUint32( buffer, offset );

channels.push( {
name: name,
pixelType: pixelType,
pLinear: pLinear,
xSampling: xSampling,
ySampling: ySampling
} );

}

offset.value += 1;

return channels;

}

var parseChromaticities = function( buffer, offset ) {

var redX = parseFloat32( buffer, offset );
var redY = parseFloat32( buffer, offset );
var greenX = parseFloat32( buffer, offset );
var greenY = parseFloat32( buffer, offset );
var blueX = parseFloat32( buffer, offset );
var blueY = parseFloat32( buffer, offset );
var whiteX = parseFloat32( buffer, offset );
var whiteY = parseFloat32( buffer, offset );

return { redX: redX, redY: redY, greenX, greenY, blueX, blueY, whiteX, whiteY };

}

var parseCompression = function( buffer, offset ) {

var compressionCodes = [
'NO_COMPRESSION',
'PIZ_COMPRESSION'
];

var compression = parseUint8( buffer, offset );

return compressionCodes[ compression ];

}

var parseBox2i = function( buffer, offset ) {

var xMin = parseUint32( buffer, offset );
var yMin = parseUint32( buffer, offset );
var xMax = parseUint32( buffer, offset );
var yMax = parseUint32( buffer, offset );

return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax };

}

var parseLineOrder = function( buffer, offset ) {

var lineOrders = [
'INCREASING_Y'
];

var lineOrder = parseUint8( buffer, offset );

return lineOrders[ lineOrder ];

}

var parseV2f = function( buffer, offset ) {

var x = parseFloat32( buffer, offset );
var y = parseFloat32( buffer, offset );

return [ x, y ];

}

var parseValue = function( buffer, offset, type, size ) {

if ( type == 'string' || type == 'iccProfile' ) {

return parseFixedLengthString( buffer, offset, size );

} else if ( type == 'chlist' ) {

return parseChlist( buffer, offset, size );

} else if ( type == 'chromaticities' ) {

return parseChromaticities( buffer, offset );

} else if ( type == 'compression' ) {

return parseCompression( buffer, offset );

} else if ( type == 'box2i' ) {

return parseBox2i( buffer, offset );

} else if ( type == 'lineOrder' ) {

return parseLineOrder( buffer, offset );

} else if ( type == 'float' ) {

return parseFloat32( buffer, offset );

} else if ( type == 'v2f' ) {

return parseV2f( buffer, offset );

} else {

throw 'Cannot parse value for unsupported type: ' + type;

}

}

var EXRHeader = {};

var magic = new DataView( buffer ).getUint32( 0, true );
var versionByteZero = new DataView( buffer ).getUint8( 4, true );
var fullMask = new DataView( buffer ).getUint8( 5, true );

// start of header

var offset = { value: 8 }; // start at 8, after magic stuff

var keepReading = true;

while ( keepReading ) {

var attributeName = parseNullTerminatedString( buffer, offset );

if ( attributeName == 0 ) {

keepReading = false;

} else {

var attributeType = parseNullTerminatedString( buffer, offset );
var attributeSize = parseUint32( buffer, offset );
var attributeValue = parseValue( buffer, offset, attributeType, attributeSize );

EXRHeader[ attributeName ] = attributeValue;

}

}

// offsets

var dataWindowHeight = EXRHeader.dataWindow.yMax + 1;
var scanlineBlockSize = 1; // 1 for no compression, 32 for PIZ
var numBlocks = dataWindowHeight / scanlineBlockSize;

for ( var i = 0; i < numBlocks; i ++ ) {

var scanlineOffset = parseUlong( buffer, offset );

}

// we should be passed the scanline offset table, start reading pixel data

var width = EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1;
var height = EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1;
var numChannels = EXRHeader.channels.length;

var byteArray = new Float32Array( width * height * numChannels );

var channelOffsets = {
R: 0,
G: 1,
B: 2,
A: 3
};

for ( var y = 0; y < height; y ++ ) {

var y_scanline = parseUint32( buffer, offset );
var dataSize = parseUint32( buffer, offset );

for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
if ( EXRHeader.channels[ channelID ].pixelType == 1 ) {
// HALF
for ( var x = 0; x < width; x ++ ) {

var val = parseFloat16( buffer, offset );
var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];

byteArray[ ( ( ( width - y_scanline ) * ( height * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;

}

} else {

throw 'Only supported pixel format is HALF';

}

}

}

return {
header: EXRHeader,
width: width,
height: height,
data: byteArray,
format: THREE.RGBAFormat,
type: THREE.FloatType
};

};
Binary file added examples/textures/uncompressed.exr
Binary file not shown.
Loading