@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
249 lines • 10.5 kB
JavaScript
import { Tools } from "./tools.js";
/**
* The worker function that gets converted to a blob url to pass into a worker.
* To be used if a developer wants to create their own worker instance and inject it instead of using the default worker.
*/
export function workerFunction() {
const _BASIS_FORMAT = {
cTFETC1: 0,
cTFETC2: 1,
cTFBC1: 2,
cTFBC3: 3,
cTFBC4: 4,
cTFBC5: 5,
cTFBC7: 6,
cTFPVRTC1_4_RGB: 8,
cTFPVRTC1_4_RGBA: 9,
cTFASTC_4x4: 10,
cTFATC_RGB: 11,
cTFATC_RGBA_INTERPOLATED_ALPHA: 12,
cTFRGBA32: 13,
cTFRGB565: 14,
cTFBGR565: 15,
cTFRGBA4444: 16,
cTFFXT1_RGB: 17,
cTFPVRTC2_4_RGB: 18,
cTFPVRTC2_4_RGBA: 19,
cTFETC2_EAC_R11: 20,
cTFETC2_EAC_RG11: 21,
};
let transcoderModulePromise = null;
onmessage = (event) => {
if (event.data.action === "init") {
// Load the transcoder if it hasn't been yet
if (event.data.url) {
// make sure we loaded the script correctly
try {
importScripts(event.data.url);
}
catch (e) {
postMessage({ action: "error", error: e });
}
}
if (!transcoderModulePromise) {
transcoderModulePromise = BASIS({
// Override wasm binary
wasmBinary: event.data.wasmBinary,
});
}
if (transcoderModulePromise !== null) {
transcoderModulePromise.then((m) => {
BASIS = m;
m.initializeBasis();
postMessage({ action: "init" });
});
}
}
else if (event.data.action === "transcode") {
// Transcode the basis image and return the resulting pixels
const config = event.data.config;
const imgData = event.data.imageData;
const loadedFile = new BASIS.BasisFile(imgData);
const fileInfo = GetFileInfo(loadedFile);
let format = event.data.ignoreSupportedFormats ? null : GetSupportedTranscodeFormat(event.data.config, fileInfo);
let needsConversion = false;
if (format === null) {
needsConversion = true;
format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFBC3 : _BASIS_FORMAT.cTFBC1;
}
// Begin transcode
let success = true;
if (!loadedFile.startTranscoding()) {
success = false;
}
const buffers = [];
for (let imageIndex = 0; imageIndex < fileInfo.images.length; imageIndex++) {
if (!success) {
break;
}
const image = fileInfo.images[imageIndex];
if (config.loadSingleImage === undefined || config.loadSingleImage === imageIndex) {
let mipCount = image.levels.length;
if (config.loadMipmapLevels === false) {
mipCount = 1;
}
for (let levelIndex = 0; levelIndex < mipCount; levelIndex++) {
const levelInfo = image.levels[levelIndex];
const pixels = TranscodeLevel(loadedFile, imageIndex, levelIndex, format, needsConversion);
if (!pixels) {
success = false;
break;
}
levelInfo.transcodedPixels = pixels;
buffers.push(levelInfo.transcodedPixels.buffer);
}
}
}
// Close file
loadedFile.close();
loadedFile.delete();
if (needsConversion) {
format = -1;
}
if (!success) {
postMessage({ action: "transcode", success: success, id: event.data.id });
}
else {
postMessage({ action: "transcode", success: success, id: event.data.id, fileInfo: fileInfo, format: format }, buffers);
}
}
};
/**
* Detects the supported transcode format for the file
* @param config transcode config
* @param fileInfo info about the file
* @returns the chosed format or null if none are supported
*/
function GetSupportedTranscodeFormat(config, fileInfo) {
let format = null;
if (config.supportedCompressionFormats) {
if (config.supportedCompressionFormats.astc) {
format = _BASIS_FORMAT.cTFASTC_4x4;
}
else if (config.supportedCompressionFormats.bc7) {
format = _BASIS_FORMAT.cTFBC7;
}
else if (config.supportedCompressionFormats.s3tc) {
format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFBC3 : _BASIS_FORMAT.cTFBC1;
}
else if (config.supportedCompressionFormats.pvrtc) {
format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFPVRTC1_4_RGBA : _BASIS_FORMAT.cTFPVRTC1_4_RGB;
}
else if (config.supportedCompressionFormats.etc2) {
format = _BASIS_FORMAT.cTFETC2;
}
else if (config.supportedCompressionFormats.etc1) {
format = _BASIS_FORMAT.cTFETC1;
}
else {
format = _BASIS_FORMAT.cTFRGB565;
}
}
return format;
}
/**
* Retrieves information about the basis file eg. dimensions
* @param basisFile the basis file to get the info from
* @returns information about the basis file
*/
function GetFileInfo(basisFile) {
const hasAlpha = basisFile.getHasAlpha();
const imageCount = basisFile.getNumImages();
const images = [];
for (let i = 0; i < imageCount; i++) {
const imageInfo = {
levels: [],
};
const levelCount = basisFile.getNumLevels(i);
for (let level = 0; level < levelCount; level++) {
const levelInfo = {
width: basisFile.getImageWidth(i, level),
height: basisFile.getImageHeight(i, level),
};
imageInfo.levels.push(levelInfo);
}
images.push(imageInfo);
}
const info = { hasAlpha, images };
return info;
}
function TranscodeLevel(loadedFile, imageIndex, levelIndex, format, convertToRgb565) {
const dstSize = loadedFile.getImageTranscodedSizeInBytes(imageIndex, levelIndex, format);
let dst = new Uint8Array(dstSize);
if (!loadedFile.transcodeImage(dst, imageIndex, levelIndex, format, 1, 0)) {
return null;
}
// If no supported format is found, load as dxt and convert to rgb565
if (convertToRgb565) {
const alignedWidth = (loadedFile.getImageWidth(imageIndex, levelIndex) + 3) & ~3;
const alignedHeight = (loadedFile.getImageHeight(imageIndex, levelIndex) + 3) & ~3;
dst = ConvertDxtToRgb565(dst, 0, alignedWidth, alignedHeight);
}
return dst;
}
/**
* From https://github.com/BinomialLLC/basis_universal/blob/master/webgl/texture/dxt-to-rgb565.js
* An unoptimized version of dxtToRgb565. Also, the floating
* point math used to compute the colors actually results in
* slightly different colors compared to hardware DXT decoders.
* @param src dxt src pixels
* @param srcByteOffset offset for the start of src
* @param width aligned width of the image
* @param height aligned height of the image
* @returns the converted pixels
*/
function ConvertDxtToRgb565(src, srcByteOffset, width, height) {
const c = new Uint16Array(4);
const dst = new Uint16Array(width * height);
const blockWidth = width / 4;
const blockHeight = height / 4;
for (let blockY = 0; blockY < blockHeight; blockY++) {
for (let blockX = 0; blockX < blockWidth; blockX++) {
const i = srcByteOffset + 8 * (blockY * blockWidth + blockX);
c[0] = src[i] | (src[i + 1] << 8);
c[1] = src[i + 2] | (src[i + 3] << 8);
c[2] =
((2 * (c[0] & 0x1f) + 1 * (c[1] & 0x1f)) / 3) |
(((2 * (c[0] & 0x7e0) + 1 * (c[1] & 0x7e0)) / 3) & 0x7e0) |
(((2 * (c[0] & 0xf800) + 1 * (c[1] & 0xf800)) / 3) & 0xf800);
c[3] =
((2 * (c[1] & 0x1f) + 1 * (c[0] & 0x1f)) / 3) |
(((2 * (c[1] & 0x7e0) + 1 * (c[0] & 0x7e0)) / 3) & 0x7e0) |
(((2 * (c[1] & 0xf800) + 1 * (c[0] & 0xf800)) / 3) & 0xf800);
for (let row = 0; row < 4; row++) {
const m = src[i + 4 + row];
let dstI = (blockY * 4 + row) * width + blockX * 4;
dst[dstI++] = c[m & 0x3];
dst[dstI++] = c[(m >> 2) & 0x3];
dst[dstI++] = c[(m >> 4) & 0x3];
dst[dstI++] = c[(m >> 6) & 0x3];
}
}
}
return dst;
}
}
/**
* Initialize a web worker with the basis transcoder
* @param worker the worker to initialize
* @param wasmBinary the wasm binary to load into the worker
* @param moduleUrl the url to the basis transcoder module
* @returns a promise that resolves when the worker is initialized
*/
export function initializeWebWorker(worker, wasmBinary, moduleUrl) {
return new Promise((res, reject) => {
const initHandler = (msg) => {
if (msg.data.action === "init") {
worker.removeEventListener("message", initHandler);
res(worker);
}
else if (msg.data.action === "error") {
reject(msg.data.error || "error initializing worker");
}
};
worker.addEventListener("message", initHandler);
// we can use transferable objects here because the worker will own the ArrayBuffer
worker.postMessage({ action: "init", url: moduleUrl ? Tools.GetBabylonScriptURL(moduleUrl) : undefined, wasmBinary }, [wasmBinary]);
});
}
//# sourceMappingURL=basisWorker.js.map