UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

193 lines (178 loc) 6.43 kB
import {Data3DTexture, RedFormat, LinearFilter, HalfFloatType, FloatType, ClampToEdgeWrapping, Vector3} from 'three'; import {CoreBaseLoader} from '../_Base'; import {BaseNodeType} from '../../../engine/nodes/_Base'; import {CoreUserAgent} from '../../UserAgent'; export interface SDFLoaderOptions {} export class SDFLoader extends CoreBaseLoader<string> { constructor(url: string, protected override _node?: BaseNodeType) { super(url, _node); } async load( successCallback: (geometry: Data3DTexture) => void, progressCallback: (() => void) | undefined, errorCallback: (error: ErrorEvent) => void | undefined ) { const url = this._urlToLoad(); // const loader = new FileLoader(this.loadingManager); try { const response = await fetch(url); const blob = await response.blob(); var fileReader = new FileReader(); fileReader.onload = function (event) { try { const arrayBuffer = event.target?.result; if (arrayBuffer && arrayBuffer instanceof ArrayBuffer) { const texture = loadSDFMetadata(arrayBuffer); successCallback(texture); } else { errorCallback(new ErrorEvent('content is not an arrayBuffer')); } } catch (err) { errorCallback(err instanceof ErrorEvent ? err : new ErrorEvent('fail to read SDF file')); } }; fileReader.onerror = () => { errorCallback(new ErrorEvent('failure to read response content')); }; fileReader.readAsArrayBuffer(blob); } catch (err) { errorCallback(err as ErrorEvent); } } } interface MetadataOptions { boundMin: Vector3; boundMax: Vector3; resolution: Vector3; } export interface SDFDataContainer { width: number; height: number; depth: number; boundMinx: number; boundMiny: number; boundMinz: number; boundMaxx: number; boundMaxy: number; boundMaxz: number; resolutionx: number; resolutiony: number; resolutionz: number; data: Float32Array; } export function addSDFMetadataToContainer(texture: Data3DTexture, options: MetadataOptions) { const dataContainer = texture?.image as unknown as SDFDataContainer; if (!dataContainer) { throw new Error('the input must be a 3D texture'); return; } dataContainer.boundMinx = options.boundMin.x; dataContainer.boundMiny = options.boundMin.y; dataContainer.boundMinz = options.boundMin.z; dataContainer.boundMaxx = options.boundMax.x; dataContainer.boundMaxy = options.boundMax.y; dataContainer.boundMaxz = options.boundMax.z; dataContainer.resolutionx = options.resolution.x; dataContainer.resolutiony = options.resolution.y; dataContainer.resolutionz = options.resolution.z; } export function readSDFMetadataFromContainer(texture: Data3DTexture) { const dataContainer = texture?.image as unknown as SDFDataContainer; if (!dataContainer) { throw new Error('the input must be a 3D texture'); return; } return dataContainer; } export function saveSDFMetadata(texture: Data3DTexture) { const dataContainer = readSDFMetadataFromContainer(texture); if (!dataContainer) { throw new Error('the input must be a 3D texture'); return; } const data: Float32Array = dataContainer.data; const metadataSizes = { metadataLength: 1, dimensions: 3, bounds: 6, resolution: 3, }; const metadataLength = metadataSizes.metadataLength + metadataSizes.dimensions + metadataSizes.bounds + metadataSizes.resolution; // add metadata const currentLength = data.length; const dataWithMetadata = new Float32Array(currentLength + metadataLength); // 1. numbers 0 is length of metadata dataWithMetadata[0] = metadataLength; // 2. numbers 1 to 3 are the resolution dataWithMetadata[1] = dataContainer.width; dataWithMetadata[2] = dataContainer.height; dataWithMetadata[3] = dataContainer.depth; // 3. numbers 4 to 9 are the bounds dataWithMetadata[4] = dataContainer.boundMinx; dataWithMetadata[5] = dataContainer.boundMiny; dataWithMetadata[6] = dataContainer.boundMinz; dataWithMetadata[7] = dataContainer.boundMaxx; dataWithMetadata[8] = dataContainer.boundMaxy; dataWithMetadata[9] = dataContainer.boundMaxz; // 4. numbers 10 to 12 are the resolution dataWithMetadata[10] = dataContainer.resolutionx; dataWithMetadata[11] = dataContainer.resolutiony; dataWithMetadata[12] = dataContainer.resolutionz; // 5. create new array for (let i = 0; i < currentLength; i++) { dataWithMetadata[i + metadataLength] = data[i]; } return dataWithMetadata; } function loadSDFMetadata(arrayBuffer: ArrayBuffer): Data3DTexture { const float32Array = new Float32Array(arrayBuffer); const currentLength = float32Array.length; // 1. numbers 0 is length of metadata const metadataLength = float32Array[0]; // 2. numbers 1 to 3 are the resolution const width = float32Array[1]; const height = float32Array[2]; const depth = float32Array[3]; // 3. numbers 4 to 9 are the bounds const boundMinx = float32Array[4]; const boundMiny = float32Array[5]; const boundMinz = float32Array[6]; const boundMaxx = float32Array[7]; const boundMaxy = float32Array[8]; const boundMaxz = float32Array[9]; // 4. numbers 10 to 12 are the resolution const resolutionx = float32Array[10]; const resolutiony = float32Array[11]; const resolutionz = float32Array[12]; // 3. create new array const texture = createSDFTexture(width, height, depth); const dataWithoutMetadata = texture.image.data; for (let i = 0; i < currentLength; i++) { dataWithoutMetadata[i] = float32Array[i + metadataLength]; } const dataContainer = texture.image as unknown as SDFDataContainer; dataContainer.boundMinx = boundMinx; dataContainer.boundMiny = boundMiny; dataContainer.boundMinz = boundMinz; dataContainer.boundMaxx = boundMaxx; dataContainer.boundMaxy = boundMaxy; dataContainer.boundMaxz = boundMaxz; dataContainer.resolutionx = resolutionx; dataContainer.resolutiony = resolutiony; dataContainer.resolutionz = resolutionz; return texture; } export function createSDFTexture(width: number, height: number, depth: number) { const texture = new Data3DTexture(new Float32Array(width * height * depth).fill(0), width, height, depth); texture.format = RedFormat; texture.minFilter = LinearFilter; texture.magFilter = LinearFilter; texture.wrapS = ClampToEdgeWrapping; texture.wrapT = ClampToEdgeWrapping; (texture as any).wrapR = ClampToEdgeWrapping; texture.unpackAlignment = 1; texture.needsUpdate = true; texture.type = CoreUserAgent.isiOS() ? HalfFloatType : FloatType; return texture; }