UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

86 lines (81 loc) 3.65 kB
import { DataTexture, DataUtils, FileLoader, FloatType, HalfFloatType, LinearFilter, LoadingManager, RGBAFormat, SRGBColorSpace, TextureDataType, } from 'three' import {imageUrlToImageData} from 'ts-browser-helpers' /** * 8bit HDR image in png format * not properly working with files from hdrpng.js but used in {@link GLTFViewerConfigExtension}, so a slightly modified version is used here */ export class RGBEPNGLoader extends FileLoader { type: TextureDataType = HalfFloatType constructor(manager?: LoadingManager) { super(manager) } async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<any> { const image = await this.parseAsync(url, onProgress, false) const texture = new DataTexture(image.data, image.width, image.height, RGBAFormat, this.type) texture.needsUpdate = true texture.flipY = true texture.colorSpace = SRGBColorSpace texture.minFilter = LinearFilter texture.magFilter = LinearFilter texture.source.data.complete = true return texture } async parseAsync(url: string, onProgress?: (event: ProgressEvent) => void, isFloat16Data = false): Promise<any> { let created = false if (!url.startsWith('data:') && !url.startsWith('blob:')) { this.responseType = 'blob' const blob = await super.loadAsync(url, onProgress) as any as Blob // url = await blobToDataURL(blob) // console.log(url) // url = url.replace('application/octet-stream', 'image/png') url = URL.createObjectURL(blob) created = true } const imageData = await imageUrlToImageData(url) if (created) URL.revokeObjectURL(url) let aType: any = Uint8Array if (this.type === HalfFloatType) aType = Uint16Array else if (this.type === FloatType) aType = Float32Array const buffer = rgbeToHalfFloat(imageData.data, 4, aType, isFloat16Data) return {data: buffer, width: imageData.width, height: imageData.height} } setDataType(value: TextureDataType) { this.type = value return this } } // adapted from https://github.com/enkimute/hdrpng.js/blob/3a62b3ae2940189777df9f669df5ece3e78d9c16/hdrpng.js#L253 // channels = 4 for RGBA data or 3 for RGB data. res to use with THREE.DataTexture function rgbeToHalfFloat(buffer: Uint8ClampedArray, channels = 3, type = Uint16Array, float16Data = false): Uint16Array { let s const l = buffer.byteLength >> 2 const res = new type(l * channels) for (let i = 0;i < l;i++) { s = Math.pow(2, buffer[i * 4 + 3] - (128 + 8)) if (float16Data) { res[ i * channels ] = Math.min(buffer[i * 4] * s, 65504) res[ i * channels + 1] = Math.min(buffer[i * 4 + 1] * s, 65504) res[ i * channels + 2] = Math.min(buffer[i * 4 + 2] * s, 65504) } else { res[i * channels] = DataUtils.toHalfFloat(Math.min(buffer[i * 4] * s, 65504)) res[i * channels + 1] = DataUtils.toHalfFloat(Math.min(buffer[i * 4 + 1] * s, 65504)) res[i * channels + 2] = DataUtils.toHalfFloat(Math.min(buffer[i * 4 + 2] * s, 65504)) } // res[i * channels] = Math.min(15360, buffer[i * 4] * s) // res[i * channels + 1] = Math.min(15360, buffer[i * 4 + 1] * s) // res[i * channels + 2] = Math.min(15360, buffer[i * 4 + 2] * s) if (channels === 4) res[i * channels + 3] = DataUtils.toHalfFloat(1) // alpha is always 1 // todo: handle for uint8 and float32 } return res }