497 lines
19 KiB
JavaScript
497 lines
19 KiB
JavaScript
"use strict";
|
|
var __defProp = Object.defineProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const THREE = require("three");
|
|
const _taskCache = /* @__PURE__ */ new WeakMap();
|
|
const BasisTextureLoader = /* @__PURE__ */ (() => {
|
|
const _BasisTextureLoader = class extends THREE.Loader {
|
|
constructor(manager) {
|
|
super(manager);
|
|
this.transcoderPath = "";
|
|
this.transcoderBinary = null;
|
|
this.transcoderPending = null;
|
|
this.workerLimit = 4;
|
|
this.workerPool = [];
|
|
this.workerNextTaskID = 1;
|
|
this.workerSourceURL = "";
|
|
this.workerConfig = null;
|
|
}
|
|
setTranscoderPath(path) {
|
|
this.transcoderPath = path;
|
|
return this;
|
|
}
|
|
setWorkerLimit(workerLimit) {
|
|
this.workerLimit = workerLimit;
|
|
return this;
|
|
}
|
|
detectSupport(renderer) {
|
|
this.workerConfig = {
|
|
astcSupported: renderer.extensions.has("WEBGL_compressed_texture_astc"),
|
|
etc1Supported: renderer.extensions.has("WEBGL_compressed_texture_etc1"),
|
|
etc2Supported: renderer.extensions.has("WEBGL_compressed_texture_etc"),
|
|
dxtSupported: renderer.extensions.has("WEBGL_compressed_texture_s3tc"),
|
|
bptcSupported: renderer.extensions.has("EXT_texture_compression_bptc"),
|
|
pvrtcSupported: renderer.extensions.has("WEBGL_compressed_texture_pvrtc") || renderer.extensions.has("WEBKIT_WEBGL_compressed_texture_pvrtc")
|
|
};
|
|
return this;
|
|
}
|
|
load(url, onLoad, onProgress, onError) {
|
|
const loader = new THREE.FileLoader(this.manager);
|
|
loader.setResponseType("arraybuffer");
|
|
loader.setWithCredentials(this.withCredentials);
|
|
const texture = new THREE.CompressedTexture();
|
|
loader.load(
|
|
url,
|
|
(buffer) => {
|
|
if (_taskCache.has(buffer)) {
|
|
const cachedTask = _taskCache.get(buffer);
|
|
return cachedTask.promise.then(onLoad).catch(onError);
|
|
}
|
|
this._createTexture([buffer]).then(function(_texture) {
|
|
texture.copy(_texture);
|
|
texture.needsUpdate = true;
|
|
if (onLoad)
|
|
onLoad(texture);
|
|
}).catch(onError);
|
|
},
|
|
onProgress,
|
|
onError
|
|
);
|
|
return texture;
|
|
}
|
|
/** Low-level transcoding API, exposed for use by KTX2Loader. */
|
|
parseInternalAsync(options) {
|
|
const { levels } = options;
|
|
const buffers = /* @__PURE__ */ new Set();
|
|
for (let i = 0; i < levels.length; i++) {
|
|
buffers.add(levels[i].data.buffer);
|
|
}
|
|
return this._createTexture(Array.from(buffers), { ...options, lowLevel: true });
|
|
}
|
|
/**
|
|
* @param {ArrayBuffer[]} buffers
|
|
* @param {object?} config
|
|
* @return {Promise<CompressedTexture>}
|
|
*/
|
|
_createTexture(buffers, config = {}) {
|
|
let worker;
|
|
let taskID;
|
|
const taskConfig = config;
|
|
let taskCost = 0;
|
|
for (let i = 0; i < buffers.length; i++) {
|
|
taskCost += buffers[i].byteLength;
|
|
}
|
|
const texturePending = this._allocateWorker(taskCost).then((_worker) => {
|
|
worker = _worker;
|
|
taskID = this.workerNextTaskID++;
|
|
return new Promise((resolve, reject) => {
|
|
worker._callbacks[taskID] = { resolve, reject };
|
|
worker.postMessage({ type: "transcode", id: taskID, buffers, taskConfig }, buffers);
|
|
});
|
|
}).then((message) => {
|
|
const { mipmaps, width, height, format } = message;
|
|
const texture = new THREE.CompressedTexture(mipmaps, width, height, format, THREE.UnsignedByteType);
|
|
texture.minFilter = mipmaps.length === 1 ? THREE.LinearFilter : THREE.LinearMipmapLinearFilter;
|
|
texture.magFilter = THREE.LinearFilter;
|
|
texture.generateMipmaps = false;
|
|
texture.needsUpdate = true;
|
|
return texture;
|
|
});
|
|
texturePending.catch(() => true).then(() => {
|
|
if (worker && taskID) {
|
|
worker._taskLoad -= taskCost;
|
|
delete worker._callbacks[taskID];
|
|
}
|
|
});
|
|
_taskCache.set(buffers[0], { promise: texturePending });
|
|
return texturePending;
|
|
}
|
|
_initTranscoder() {
|
|
if (!this.transcoderPending) {
|
|
const jsLoader = new THREE.FileLoader(this.manager);
|
|
jsLoader.setPath(this.transcoderPath);
|
|
jsLoader.setWithCredentials(this.withCredentials);
|
|
const jsContent = new Promise((resolve, reject) => {
|
|
jsLoader.load("basis_transcoder.js", resolve, void 0, reject);
|
|
});
|
|
const binaryLoader = new THREE.FileLoader(this.manager);
|
|
binaryLoader.setPath(this.transcoderPath);
|
|
binaryLoader.setResponseType("arraybuffer");
|
|
binaryLoader.setWithCredentials(this.withCredentials);
|
|
const binaryContent = new Promise((resolve, reject) => {
|
|
binaryLoader.load("basis_transcoder.wasm", resolve, void 0, reject);
|
|
});
|
|
this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent2, binaryContent2]) => {
|
|
const fn = _BasisTextureLoader.BasisWorker.toString();
|
|
const body = [
|
|
"/* constants */",
|
|
"let _EngineFormat = " + JSON.stringify(_BasisTextureLoader.EngineFormat),
|
|
"let _TranscoderFormat = " + JSON.stringify(_BasisTextureLoader.TranscoderFormat),
|
|
"let _BasisFormat = " + JSON.stringify(_BasisTextureLoader.BasisFormat),
|
|
"/* basis_transcoder.js */",
|
|
jsContent2,
|
|
"/* worker */",
|
|
fn.substring(fn.indexOf("{") + 1, fn.lastIndexOf("}"))
|
|
].join("\n");
|
|
this.workerSourceURL = URL.createObjectURL(new Blob([body]));
|
|
this.transcoderBinary = binaryContent2;
|
|
});
|
|
}
|
|
return this.transcoderPending;
|
|
}
|
|
_allocateWorker(taskCost) {
|
|
return this._initTranscoder().then(() => {
|
|
if (this.workerPool.length < this.workerLimit) {
|
|
const worker2 = new Worker(this.workerSourceURL);
|
|
worker2._callbacks = {};
|
|
worker2._taskLoad = 0;
|
|
worker2.postMessage({
|
|
type: "init",
|
|
config: this.workerConfig,
|
|
transcoderBinary: this.transcoderBinary
|
|
});
|
|
worker2.onmessage = function(e) {
|
|
const message = e.data;
|
|
switch (message.type) {
|
|
case "transcode":
|
|
worker2._callbacks[message.id].resolve(message);
|
|
break;
|
|
case "error":
|
|
worker2._callbacks[message.id].reject(message);
|
|
break;
|
|
default:
|
|
console.error('THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"');
|
|
}
|
|
};
|
|
this.workerPool.push(worker2);
|
|
} else {
|
|
this.workerPool.sort(function(a, b) {
|
|
return a._taskLoad > b._taskLoad ? -1 : 1;
|
|
});
|
|
}
|
|
const worker = this.workerPool[this.workerPool.length - 1];
|
|
worker._taskLoad += taskCost;
|
|
return worker;
|
|
});
|
|
}
|
|
dispose() {
|
|
for (let i = 0; i < this.workerPool.length; i++) {
|
|
this.workerPool[i].terminate();
|
|
}
|
|
this.workerPool.length = 0;
|
|
return this;
|
|
}
|
|
};
|
|
let BasisTextureLoader2 = _BasisTextureLoader;
|
|
/* CONSTANTS */
|
|
__publicField(BasisTextureLoader2, "BasisFormat", {
|
|
ETC1S: 0,
|
|
UASTC_4x4: 1
|
|
});
|
|
__publicField(BasisTextureLoader2, "TranscoderFormat", {
|
|
ETC1: 0,
|
|
ETC2: 1,
|
|
BC1: 2,
|
|
BC3: 3,
|
|
BC4: 4,
|
|
BC5: 5,
|
|
BC7_M6_OPAQUE_ONLY: 6,
|
|
BC7_M5: 7,
|
|
PVRTC1_4_RGB: 8,
|
|
PVRTC1_4_RGBA: 9,
|
|
ASTC_4x4: 10,
|
|
ATC_RGB: 11,
|
|
ATC_RGBA_INTERPOLATED_ALPHA: 12,
|
|
RGBA32: 13,
|
|
RGB565: 14,
|
|
BGR565: 15,
|
|
RGBA4444: 16
|
|
});
|
|
__publicField(BasisTextureLoader2, "EngineFormat", {
|
|
RGBAFormat: THREE.RGBAFormat,
|
|
RGBA_ASTC_4x4_Format: THREE.RGBA_ASTC_4x4_Format,
|
|
RGBA_BPTC_Format: THREE.RGBA_BPTC_Format,
|
|
RGBA_ETC2_EAC_Format: THREE.RGBA_ETC2_EAC_Format,
|
|
RGBA_PVRTC_4BPPV1_Format: THREE.RGBA_PVRTC_4BPPV1_Format,
|
|
RGBA_S3TC_DXT5_Format: THREE.RGBA_S3TC_DXT5_Format,
|
|
RGB_ETC1_Format: THREE.RGB_ETC1_Format,
|
|
RGB_ETC2_Format: THREE.RGB_ETC2_Format,
|
|
RGB_PVRTC_4BPPV1_Format: THREE.RGB_PVRTC_4BPPV1_Format,
|
|
RGB_S3TC_DXT1_Format: THREE.RGB_S3TC_DXT1_Format
|
|
});
|
|
/* WEB WORKER */
|
|
__publicField(BasisTextureLoader2, "BasisWorker", function() {
|
|
let config;
|
|
let transcoderPending;
|
|
let BasisModule;
|
|
const EngineFormat = _EngineFormat;
|
|
const TranscoderFormat = _TranscoderFormat;
|
|
const BasisFormat = _BasisFormat;
|
|
onmessage = function(e) {
|
|
const message = e.data;
|
|
switch (message.type) {
|
|
case "init":
|
|
config = message.config;
|
|
init(message.transcoderBinary);
|
|
break;
|
|
case "transcode":
|
|
transcoderPending.then(() => {
|
|
try {
|
|
const { width, height, hasAlpha, mipmaps, format } = message.taskConfig.lowLevel ? transcodeLowLevel(message.taskConfig) : transcode(message.buffers[0]);
|
|
const buffers = [];
|
|
for (let i = 0; i < mipmaps.length; ++i) {
|
|
buffers.push(mipmaps[i].data.buffer);
|
|
}
|
|
self.postMessage(
|
|
{ type: "transcode", id: message.id, width, height, hasAlpha, mipmaps, format },
|
|
buffers
|
|
);
|
|
} catch (error) {
|
|
console.error(error);
|
|
self.postMessage({ type: "error", id: message.id, error: error.message });
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
};
|
|
function init(wasmBinary) {
|
|
transcoderPending = new Promise((resolve) => {
|
|
BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
|
|
BASIS(BasisModule);
|
|
}).then(() => {
|
|
BasisModule.initializeBasis();
|
|
});
|
|
}
|
|
function transcodeLowLevel(taskConfig) {
|
|
const { basisFormat, width, height, hasAlpha } = taskConfig;
|
|
const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha);
|
|
const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat);
|
|
assert(BasisModule.isFormatSupported(transcoderFormat), "THREE.BasisTextureLoader: Unsupported format.");
|
|
const mipmaps = [];
|
|
if (basisFormat === BasisFormat.ETC1S) {
|
|
const transcoder = new BasisModule.LowLevelETC1SImageTranscoder();
|
|
const { endpointCount, endpointsData, selectorCount, selectorsData, tablesData } = taskConfig.globalData;
|
|
try {
|
|
let ok;
|
|
ok = transcoder.decodePalettes(endpointCount, endpointsData, selectorCount, selectorsData);
|
|
assert(ok, "THREE.BasisTextureLoader: decodePalettes() failed.");
|
|
ok = transcoder.decodeTables(tablesData);
|
|
assert(ok, "THREE.BasisTextureLoader: decodeTables() failed.");
|
|
for (let i = 0; i < taskConfig.levels.length; i++) {
|
|
const level = taskConfig.levels[i];
|
|
const imageDesc = taskConfig.globalData.imageDescs[i];
|
|
const dstByteLength = getTranscodedImageByteLength(transcoderFormat, level.width, level.height);
|
|
const dst = new Uint8Array(dstByteLength);
|
|
ok = transcoder.transcodeImage(
|
|
transcoderFormat,
|
|
dst,
|
|
dstByteLength / blockByteLength,
|
|
level.data,
|
|
getWidthInBlocks(transcoderFormat, level.width),
|
|
getHeightInBlocks(transcoderFormat, level.height),
|
|
level.width,
|
|
level.height,
|
|
level.index,
|
|
imageDesc.rgbSliceByteOffset,
|
|
imageDesc.rgbSliceByteLength,
|
|
imageDesc.alphaSliceByteOffset,
|
|
imageDesc.alphaSliceByteLength,
|
|
imageDesc.imageFlags,
|
|
hasAlpha,
|
|
false,
|
|
0,
|
|
0
|
|
);
|
|
assert(ok, "THREE.BasisTextureLoader: transcodeImage() failed for level " + level.index + ".");
|
|
mipmaps.push({ data: dst, width: level.width, height: level.height });
|
|
}
|
|
} finally {
|
|
transcoder.delete();
|
|
}
|
|
} else {
|
|
for (let i = 0; i < taskConfig.levels.length; i++) {
|
|
const level = taskConfig.levels[i];
|
|
const dstByteLength = getTranscodedImageByteLength(transcoderFormat, level.width, level.height);
|
|
const dst = new Uint8Array(dstByteLength);
|
|
const ok = BasisModule.transcodeUASTCImage(
|
|
transcoderFormat,
|
|
dst,
|
|
dstByteLength / blockByteLength,
|
|
level.data,
|
|
getWidthInBlocks(transcoderFormat, level.width),
|
|
getHeightInBlocks(transcoderFormat, level.height),
|
|
level.width,
|
|
level.height,
|
|
level.index,
|
|
0,
|
|
level.data.byteLength,
|
|
0,
|
|
hasAlpha,
|
|
false,
|
|
0,
|
|
0,
|
|
-1,
|
|
-1
|
|
);
|
|
assert(ok, "THREE.BasisTextureLoader: transcodeUASTCImage() failed for level " + level.index + ".");
|
|
mipmaps.push({ data: dst, width: level.width, height: level.height });
|
|
}
|
|
}
|
|
return { width, height, hasAlpha, mipmaps, format: engineFormat };
|
|
}
|
|
function transcode(buffer) {
|
|
const basisFile = new BasisModule.BasisFile(new Uint8Array(buffer));
|
|
const basisFormat = basisFile.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
|
|
const width = basisFile.getImageWidth(0, 0);
|
|
const height = basisFile.getImageHeight(0, 0);
|
|
const levels = basisFile.getNumLevels(0);
|
|
const hasAlpha = basisFile.getHasAlpha();
|
|
function cleanup() {
|
|
basisFile.close();
|
|
basisFile.delete();
|
|
}
|
|
const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha);
|
|
if (!width || !height || !levels) {
|
|
cleanup();
|
|
throw new Error("THREE.BasisTextureLoader: Invalid texture");
|
|
}
|
|
if (!basisFile.startTranscoding()) {
|
|
cleanup();
|
|
throw new Error("THREE.BasisTextureLoader: .startTranscoding failed");
|
|
}
|
|
const mipmaps = [];
|
|
for (let mip = 0; mip < levels; mip++) {
|
|
const mipWidth = basisFile.getImageWidth(0, mip);
|
|
const mipHeight = basisFile.getImageHeight(0, mip);
|
|
const dst = new Uint8Array(basisFile.getImageTranscodedSizeInBytes(0, mip, transcoderFormat));
|
|
const status = basisFile.transcodeImage(dst, 0, mip, transcoderFormat, 0, hasAlpha);
|
|
if (!status) {
|
|
cleanup();
|
|
throw new Error("THREE.BasisTextureLoader: .transcodeImage failed.");
|
|
}
|
|
mipmaps.push({ data: dst, width: mipWidth, height: mipHeight });
|
|
}
|
|
cleanup();
|
|
return { width, height, hasAlpha, mipmaps, format: engineFormat };
|
|
}
|
|
const FORMAT_OPTIONS = [
|
|
{
|
|
if: "astcSupported",
|
|
basisFormat: [BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4],
|
|
engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format],
|
|
priorityETC1S: Infinity,
|
|
priorityUASTC: 1,
|
|
needsPowerOfTwo: false
|
|
},
|
|
{
|
|
if: "bptcSupported",
|
|
basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5],
|
|
engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format],
|
|
priorityETC1S: 3,
|
|
priorityUASTC: 2,
|
|
needsPowerOfTwo: false
|
|
},
|
|
{
|
|
if: "dxtSupported",
|
|
basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3],
|
|
engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format],
|
|
priorityETC1S: 4,
|
|
priorityUASTC: 5,
|
|
needsPowerOfTwo: false
|
|
},
|
|
{
|
|
if: "etc2Supported",
|
|
basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2],
|
|
engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format],
|
|
priorityETC1S: 1,
|
|
priorityUASTC: 3,
|
|
needsPowerOfTwo: false
|
|
},
|
|
{
|
|
if: "etc1Supported",
|
|
basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC1],
|
|
engineFormat: [EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format],
|
|
priorityETC1S: 2,
|
|
priorityUASTC: 4,
|
|
needsPowerOfTwo: false
|
|
},
|
|
{
|
|
if: "pvrtcSupported",
|
|
basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4],
|
|
transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA],
|
|
engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format],
|
|
priorityETC1S: 5,
|
|
priorityUASTC: 6,
|
|
needsPowerOfTwo: true
|
|
}
|
|
];
|
|
const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function(a, b) {
|
|
return a.priorityETC1S - b.priorityETC1S;
|
|
});
|
|
const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function(a, b) {
|
|
return a.priorityUASTC - b.priorityUASTC;
|
|
});
|
|
function getTranscoderFormat(basisFormat, width, height, hasAlpha) {
|
|
let transcoderFormat;
|
|
let engineFormat;
|
|
const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
|
|
for (let i = 0; i < options.length; i++) {
|
|
const opt = options[i];
|
|
if (!config[opt.if])
|
|
continue;
|
|
if (!opt.basisFormat.includes(basisFormat))
|
|
continue;
|
|
if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height)))
|
|
continue;
|
|
transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0];
|
|
engineFormat = opt.engineFormat[hasAlpha ? 1 : 0];
|
|
return { transcoderFormat, engineFormat };
|
|
}
|
|
console.warn("THREE.BasisTextureLoader: No suitable compressed texture format found. Decoding to RGBA32.");
|
|
transcoderFormat = TranscoderFormat.RGBA32;
|
|
engineFormat = EngineFormat.RGBAFormat;
|
|
return { transcoderFormat, engineFormat };
|
|
}
|
|
function assert(ok, message) {
|
|
if (!ok)
|
|
throw new Error(message);
|
|
}
|
|
function getWidthInBlocks(transcoderFormat, width) {
|
|
return Math.ceil(width / BasisModule.getFormatBlockWidth(transcoderFormat));
|
|
}
|
|
function getHeightInBlocks(transcoderFormat, height) {
|
|
return Math.ceil(height / BasisModule.getFormatBlockHeight(transcoderFormat));
|
|
}
|
|
function getTranscodedImageByteLength(transcoderFormat, width, height) {
|
|
const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat);
|
|
if (BasisModule.formatIsUncompressed(transcoderFormat)) {
|
|
return width * height * blockByteLength;
|
|
}
|
|
if (transcoderFormat === TranscoderFormat.PVRTC1_4_RGB || transcoderFormat === TranscoderFormat.PVRTC1_4_RGBA) {
|
|
const paddedWidth = width + 3 & ~3;
|
|
const paddedHeight = height + 3 & ~3;
|
|
return (Math.max(8, paddedWidth) * Math.max(8, paddedHeight) * 4 + 7) / 8;
|
|
}
|
|
return getWidthInBlocks(transcoderFormat, width) * getHeightInBlocks(transcoderFormat, height) * blockByteLength;
|
|
}
|
|
function isPowerOfTwo(value) {
|
|
if (value <= 2)
|
|
return true;
|
|
return (value & value - 1) === 0 && value !== 0;
|
|
}
|
|
});
|
|
return BasisTextureLoader2;
|
|
})();
|
|
exports.BasisTextureLoader = BasisTextureLoader;
|
|
//# sourceMappingURL=BasisTextureLoader.cjs.map
|