Skip to content
Merged
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
142 changes: 124 additions & 18 deletions examples/jsm/loaders/EXRLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LinearFilter,
LinearSRGBColorSpace,
RedFormat,
RGFormat,
RGBAFormat
} from 'three';
import * as fflate from '../libs/fflate.module.js';
Expand Down Expand Up @@ -113,6 +114,14 @@ class EXRLoader extends DataTextureLoader {
*/
this.type = HalfFloatType;

/**
* Texture output format.
*
* @type {(RGBAFormat|RGFormat|RedFormat)}
* @default RGBAFormat
*/
this.outputFormat = RGBAFormat;

}

/**
Expand Down Expand Up @@ -2374,7 +2383,7 @@ class EXRLoader extends DataTextureLoader {

}

function setupDecoder( EXRHeader, dataView, uInt8Array, offset, outputType ) {
function setupDecoder( EXRHeader, dataView, uInt8Array, offset, outputType, outputFormat ) {

const EXRDecoder = {
size: 0,
Expand All @@ -2385,6 +2394,7 @@ class EXRLoader extends DataTextureLoader {
height: EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1,
inputChannels: EXRHeader.channels,
channelByteOffsets: {},
shouldExpand: false,
scanOrder: null,
totalBytes: null,
columns: null,
Expand Down Expand Up @@ -2462,24 +2472,100 @@ class EXRLoader extends DataTextureLoader {

// RGB images will be converted to RGBA format, preventing software emulation in select devices.
let fillAlpha = false;
let invalidOutput = false;

// Validate if input texture contain supported channels
if ( channels.R && channels.G && channels.B ) {

fillAlpha = ! channels.A;
EXRDecoder.outputChannels = 4;
EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };

} else if ( channels.Y ) {

EXRDecoder.outputChannels = 1;
EXRDecoder.decodeChannels = { Y: 0 };

} else {

throw new Error( 'EXRLoader.parse: file contains unsupported data channels.' );

}

// Setup output texture configuration
switch ( EXRDecoder.outputChannels ) {

case 4:

if ( outputFormat == RGBAFormat ) {

fillAlpha = ! channels.A;
EXRDecoder.format = RGBAFormat;
EXRDecoder.colorSpace = LinearSRGBColorSpace;
EXRDecoder.outputChannels = 4;
EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };

} else if ( outputFormat == RGFormat ) {

EXRDecoder.format = RGFormat;
EXRDecoder.colorSpace = NoColorSpace;
EXRDecoder.outputChannels = 2;
EXRDecoder.decodeChannels = { R: 0, G: 1 };

} else if ( outputFormat == RedFormat ) {

EXRDecoder.format = RedFormat;
EXRDecoder.colorSpace = NoColorSpace;
EXRDecoder.outputChannels = 1;
EXRDecoder.decodeChannels = { R: 0 };

} else {

invalidOutput = true;

}

break;

case 1:

if ( outputFormat == RGBAFormat ) {

fillAlpha = true;
EXRDecoder.format = RGBAFormat;
EXRDecoder.colorSpace = LinearSRGBColorSpace;
EXRDecoder.outputChannels = 4;
EXRDecoder.shouldExpand = true;
EXRDecoder.decodeChannels = { Y: 0 };

} else if ( outputFormat == RGFormat ) {

EXRDecoder.format = RGFormat;
EXRDecoder.colorSpace = NoColorSpace;
EXRDecoder.outputChannels = 2;
EXRDecoder.shouldExpand = true;
EXRDecoder.decodeChannels = { Y: 0 };

} else if ( outputFormat == RedFormat ) {

EXRDecoder.format = RedFormat;
EXRDecoder.colorSpace = NoColorSpace;
EXRDecoder.outputChannels = 1;
EXRDecoder.decodeChannels = { Y: 0 };

} else {

invalidOutput = true;

}

break;

default:

invalidOutput = true;

}

if (invalidOutput) throw new Error( 'EXRLoader.parse: invalid output format for specified file.' );

if ( EXRDecoder.type == 1 ) {

// half
Expand Down Expand Up @@ -2569,18 +2655,6 @@ class EXRLoader extends DataTextureLoader {

}

if ( EXRDecoder.outputChannels == 4 ) {

EXRDecoder.format = RGBAFormat;
EXRDecoder.colorSpace = LinearSRGBColorSpace;

} else {

EXRDecoder.format = RedFormat;
EXRDecoder.colorSpace = NoColorSpace;

}

if ( EXRHeader.spec.singleTile ) {

EXRDecoder.blockHeight = EXRHeader.tiles.ySize;
Expand Down Expand Up @@ -2626,11 +2700,30 @@ class EXRLoader extends DataTextureLoader {
const EXRHeader = parseHeader( bufferDataView, buffer, offset );

// get input compression information and prepare decoding.
const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type );
const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type, this.outputFormat );

// parse input data
EXRDecoder.decode();

// output texture post-processing
if ( EXRDecoder.shouldExpand ) {

const byteArray = EXRDecoder.byteArray;

if ( this.outputFormat == RGBAFormat ) {

for ( let i = 0; i < byteArray.length; i += 4 )
byteArray [i + 2 ] = ( byteArray [ i + 1 ] = byteArray[ i ] );

} else if ( this.outputFormat == RGFormat ) {

for ( let i = 0; i < byteArray.length; i += 2 )
byteArray [ i + 1 ] = byteArray[ i ] ;

}

}

return {
header: EXRHeader,
width: EXRDecoder.width,
Expand All @@ -2647,7 +2740,7 @@ class EXRLoader extends DataTextureLoader {
* Sets the texture type.
*
* @param {(HalfFloatType|FloatType)} value - The texture type to set.
* @return {RGBMLoader} A reference to this loader.
* @return {EXRLoader} A reference to this loader.
*/
setDataType( value ) {

Expand All @@ -2656,6 +2749,19 @@ class EXRLoader extends DataTextureLoader {

}

/**
* Sets texture output format. Defaults to `RGBAFormat`.
*
* @param {(RGBAFormat|RGFormat|RedFormat)} value - Texture output format.
* @return {EXRLoader} A reference to this loader.
*/
setOutputFormat( value ) {

this.outputFormat = value;
return this;

}

load( url, onLoad, onProgress, onError ) {

function onLoadCallback( texture, texData ) {
Expand Down