@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
190 lines (186 loc) • 7.58 kB
JavaScript
import { bY as Lerp, C as Constants } from './index-FzOfPXLV.esm.js';
function LineToArray(line) {
return line
.split(" ")
.filter((x) => x !== "")
.map((x) => parseFloat(x));
}
function ReadArray(dataPointer, count, targetArray) {
while (targetArray.length !== count) {
const line = LineToArray(dataPointer.lines[dataPointer.index++]);
targetArray.push(...line);
}
}
function InterpolateCandelaValues(data, phi, theta) {
let phiIndex = 0;
let thetaIndex = 0;
let startTheta = 0;
let endTheta = 0;
let startPhi = 0;
let endPhi = 0;
// Check if the angle is outside the range
for (let index = 0; index < data.numberOfHorizontalAngles - 1; index++) {
if (theta < data.horizontalAngles[index + 1] || index === data.numberOfHorizontalAngles - 2) {
thetaIndex = index;
startTheta = data.horizontalAngles[index];
endTheta = data.horizontalAngles[index + 1];
break;
}
}
for (let index = 0; index < data.numberOfVerticalAngles - 1; index++) {
if (phi < data.verticalAngles[index + 1] || index === data.numberOfVerticalAngles - 2) {
phiIndex = index;
startPhi = data.verticalAngles[index];
endPhi = data.verticalAngles[index + 1];
break;
}
}
const deltaTheta = endTheta - startTheta;
const deltaPhi = endPhi - startPhi;
if (deltaPhi === 0) {
return 0;
}
// Interpolate
const t1 = deltaTheta === 0 ? 0 : (theta - startTheta) / deltaTheta;
const t2 = (phi - startPhi) / deltaPhi;
const nextThetaIndex = deltaTheta === 0 ? thetaIndex : thetaIndex + 1;
const v1 = Lerp(data.candelaValues[thetaIndex][phiIndex], data.candelaValues[nextThetaIndex][phiIndex], t1);
const v2 = Lerp(data.candelaValues[thetaIndex][phiIndex + 1], data.candelaValues[nextThetaIndex][phiIndex + 1], t1);
const v = Lerp(v1, v2, t2);
return v;
}
/**
* Generates IES data buffer from a string representing the IES data.
* @param uint8Array defines the IES data
* @returns the IES data buffer
* @see https://ieslibrary.com/browse
* @see https://playground.babylonjs.com/#UQGPDT#1
*/
function LoadIESData(uint8Array) {
const decoder = new TextDecoder("utf-8");
const source = decoder.decode(uint8Array);
// Read data
const dataPointer = {
lines: source.split("\n"),
index: 0,
};
const data = { version: dataPointer.lines[0], candelaValues: [], horizontalAngles: [], verticalAngles: [], numberOfHorizontalAngles: 0, numberOfVerticalAngles: 0 };
// Skip metadata
dataPointer.index = 1;
while (dataPointer.lines.length > 0 && !dataPointer.lines[dataPointer.index].includes("TILT=")) {
dataPointer.index++;
}
// Process tilt data?
if (dataPointer.lines[dataPointer.index].includes("INCLUDE")) ;
dataPointer.index++;
// Header
const header = LineToArray(dataPointer.lines[dataPointer.index++]);
data.numberOfLights = header[0];
data.lumensPerLamp = header[1];
data.candelaMultiplier = header[2];
data.numberOfVerticalAngles = header[3];
data.numberOfHorizontalAngles = header[4];
data.photometricType = header[5]; // We ignore cylindrical type for now. Will add support later if needed
data.unitsType = header[6];
data.width = header[7];
data.length = header[8];
data.height = header[9];
// Additional data
const additionalData = LineToArray(dataPointer.lines[dataPointer.index++]);
data.ballastFactor = additionalData[0];
data.fileGenerationType = additionalData[1];
data.inputWatts = additionalData[2];
// Prepare arrays
for (let index = 0; index < data.numberOfHorizontalAngles; index++) {
data.candelaValues[index] = [];
}
// Vertical angles
ReadArray(dataPointer, data.numberOfVerticalAngles, data.verticalAngles);
// Horizontal angles
ReadArray(dataPointer, data.numberOfHorizontalAngles, data.horizontalAngles);
// Candela values
for (let index = 0; index < data.numberOfHorizontalAngles; index++) {
ReadArray(dataPointer, data.numberOfVerticalAngles, data.candelaValues[index]);
}
// Evaluate candela values
let maxCandela = -1;
for (let index = 0; index < data.numberOfHorizontalAngles; index++) {
for (let subIndex = 0; subIndex < data.numberOfVerticalAngles; subIndex++) {
data.candelaValues[index][subIndex] *= data.candelaValues[index][subIndex] * data.candelaMultiplier * data.ballastFactor * data.fileGenerationType;
maxCandela = Math.max(maxCandela, data.candelaValues[index][subIndex]);
}
}
// Normalize candela values
if (maxCandela > 0) {
for (let index = 0; index < data.numberOfHorizontalAngles; index++) {
for (let subIndex = 0; subIndex < data.numberOfVerticalAngles; subIndex++) {
data.candelaValues[index][subIndex] /= maxCandela;
}
}
}
// Create the cylindrical texture
const height = 180;
const width = height * 2;
const size = width * height;
const arrayBuffer = new Float32Array(width * height);
// Fill the texture
const startTheta = data.horizontalAngles[0];
const endTheta = data.horizontalAngles[data.numberOfHorizontalAngles - 1];
for (let index = 0; index < size; index++) {
let theta = index % width;
const phi = Math.floor(index / width);
// Symmetry
if (endTheta - startTheta !== 0 && (theta < startTheta || theta >= endTheta)) {
theta %= endTheta * 2;
if (theta > endTheta) {
theta = endTheta * 2 - theta;
}
}
arrayBuffer[phi + theta * height] = InterpolateCandelaValues(data, phi, theta);
}
// So far we only need the first half of the first row of the texture as we only support IES for spot light. We can add support for other types later.
return {
width: width / 2,
height: 1,
data: arrayBuffer,
};
}
/**
* Implementation of the IES Texture Loader.
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
class _IESTextureLoader {
constructor() {
/**
* Defines whether the loader supports cascade loading the different faces.
*/
this.supportCascades = false;
}
/**
* Uploads the cube texture data to the WebGL texture. It has already been bound.
*/
loadCubeData() {
// eslint-disable-next-line no-throw-literal
throw ".ies not supported in Cube.";
}
/**
* Uploads the 2D texture data to the WebGL texture. It has already been bound once in the callback.
* @param data contains the texture data
* @param texture defines the BabylonJS internal texture
* @param callback defines the method to call once ready to upload
*/
loadData(data, texture, callback) {
const uint8array = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
const textureData = LoadIESData(uint8array);
callback(textureData.width, textureData.height, false, false, () => {
const engine = texture.getEngine();
texture.type = Constants.TEXTURETYPE_FLOAT;
texture.format = Constants.TEXTUREFORMAT_R;
texture._gammaSpace = false;
engine._uploadDataToTextureDirectly(texture, textureData.data);
});
}
}
export { _IESTextureLoader };
//# sourceMappingURL=iesTextureLoader-iqMVpnQR.esm.js.map