@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
460 lines • 38.2 kB
JavaScript
import { TgdEvent } from "./../event/index.js";
import { tgdLoadImage } from "./../loader/image.js";
import { TgdPainterFilter, TgdPainterFramebuffer, TgdPainterLogic } from "./../painter/index.js";
import { isWebglImage } from "./../types/index.js";
import { isString } from "./../types/guards.js";
import { ensureArrayNumber4, resolveErrorMessage, tgdCanvasCreate, webglLookup } from "./../utils/index.js";
import { webglTextureParametersSet } from "./../webgl/index.js";
import { isLoadBmpOptions } from "./types.js";
export class TgdTexture2D {
constructor(context, options = {}) {
this.context = context;
this.options = options;
this.eventChange = new TgdEvent();
this._texture = null;
this._width = -1;
this._height = -1;
this.params = {
magFilter: "LINEAR",
minFilter: "LINEAR",
wrapS: "REPEAT",
wrapT: "REPEAT",
wrapR: "REPEAT",
};
this.videoUpdater = null;
const { storage, params, load } = options;
const { gl } = context;
this.gl = gl;
this.name = options.name ?? `Texture2D/${TgdTexture2D.counter++}`;
this.storage = {
width: 0,
height: 0,
format: storage?.format ?? "RGBA8 / RGBA / UNSIGNED_BYTE",
levels: 1,
flipY: false,
premultipliedAlpha: false,
...storage,
};
const width = storage?.width;
const height = storage?.height;
this.createTexture(options.color);
if (typeof width === "number" && typeof height === "number") {
this.resize(width, height);
}
if (params)
this.setParams(params);
if (isWebglImage(load) || isString(load)) {
this.loadBitmap(load);
}
else if (isLoadBmpOptions(load)) {
this.loadBitmap(load.bmp, load);
}
}
clone(name) {
return new TgdTexture2D(this.context, structuredClone({
...this.options,
name: name ?? this.options.name,
})).loadBitmap(tgdCanvasCreate(this.width, this.height));
}
get format() {
return this.storage.format;
}
set format(format) {
this.storage.format = format;
}
get internalFormat() {
const [internalFormat] = this.splitFormat(this.context.gl);
return internalFormat;
}
get sourceFormat() {
const [, sourceFormat] = this.splitFormat(this.context.gl);
return sourceFormat;
}
get sourceType() {
const [, , sourceType] = this.splitFormat(this.context.gl);
return sourceType;
}
delete() {
const { videoUpdater } = this;
if (videoUpdater)
this.context.remove(videoUpdater);
if (this._texture)
this.gl.deleteTexture(this._texture);
this._texture = null;
}
get width() {
return this._width;
}
get height() {
return this._height;
}
createTexture(color) {
this.delete();
const { gl } = this;
const texture = gl.createTexture();
if (!texture)
throw new Error("Unable to create a WebGLTexture!");
this._texture = texture;
this.bind();
this.setParams(this.params);
const color4 = ensureArrayNumber4(color, [1, 0, 0.667, 1]);
const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl);
gl.texImage2D(gl.TEXTURE_2D, // target
0, // level of detail
internalFormat, // internal format
1, // width
1, // height
0, // border
sourceFormat, // source format
sourceType, // source type
sourceType === gl.FLOAT ? new Float32Array(color4) : new Uint8Array(color4));
this._width = 1;
this._height = 1;
}
splitFormat(gl) {
const [internalFormat, sourceFormat, sourceType] = this.storage.format.split(" / ");
return [gl[internalFormat], gl[sourceFormat], gl[sourceType]];
}
resize(w, h) {
const width = Math.ceil(w);
const height = Math.ceil(h);
if (width === this.width && height === this.height)
return;
this.loadBitmap(tgdCanvasCreate(width, height));
}
checkError(caption, action) {
const { gl } = this;
const error = gl.getError();
if (error !== gl.NO_ERROR) {
this.context.console.error(`[TgdTexture2D::${this.name}] Error in ${caption}:`, webglLookup(error), this);
action?.();
}
}
get glTexture() {
if (this._texture)
return this._texture;
const error = `[TgdTexture2D] Texture "${this.name}" has been deleted!`;
this.context.console.error(error);
throw new Error(error);
}
bind() {
this.gl.bindTexture(this.gl.TEXTURE_2D, this.glTexture);
}
unbind() {
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
}
loadVideo(url, options = {}) {
let playing = false;
let timeupdate = false;
const video = document.createElement("video");
let initialized = false;
const checkReady = () => {
if (!playing || !timeupdate || initialized)
return;
initialized = true;
this.loadBitmap(video, options);
const videoUpdater = new TgdPainterLogic(() => {
if (!video.paused && !video.ended) {
this.loadBitmap(video);
}
});
if (this.videoUpdater)
this.context.remove(this.videoUpdater);
this.context.add(videoUpdater);
this.videoUpdater = videoUpdater;
};
video.playsInline = true;
video.muted = true;
video.loop = true;
video.addEventListener("playing", () => {
playing = true;
checkReady();
});
video.addEventListener("timeupdate", () => {
timeupdate = true;
checkReady();
});
video.src = url;
video.play().catch(() => {
// We want to prevent this error:
// The fetching process for the media resource was aborted by the user agent at the user's request.
});
return this;
}
loadBitmap(bmp, options = {}) {
if (!bmp)
return this;
if (typeof bmp === "string") {
if (isImageURL(bmp))
return this.loadBitmap(tgdLoadImage(bmp), options);
return this.loadVideo(bmp, options);
}
if (!isWebglImage(bmp)) {
// This is a Promise
bmp.then((data) => {
this.loadBitmap(data);
}).catch((error) => this.context.console.error("Unable to load texture BMP:", error));
return this;
}
if (bmp instanceof HTMLImageElement) {
if (!bmp.complete) {
bmp.addEventListener("load", () => this.loadBitmap(bmp, options));
return this;
}
}
else if (bmp instanceof HTMLCanvasElement) {
if (bmp.width <= 0 || bmp.height <= 0) {
return this;
}
}
const { storage, gl } = this;
const { level = 0 } = options;
this._width = bmp.width;
this._height = bmp.height;
this.bind();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.storage.flipY);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.storage.premultipliedAlpha);
const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, sourceFormat, sourceType, bmp);
this.checkError(`loadBitmap(${JSON.stringify({
width: bmp.width,
height: bmp.height,
})})\ngl.texImage2D(gl.TEXTURE_2D, ${level}, gl.${"RGBA" // storage.internalFormat
}, gl.${"RGBA"}, gl.UNSIGNED_BYTE, bmp)`, () => {
this.context.console.log("bmp =", bmp); // @FIXME: Remove this line written on 2026-03-24 at 09:58
this.context.console.log("storage.flipY =", this.storage.flipY);
this.context.console.log("storage.premultipliedAlpha =", this.storage.premultipliedAlpha);
});
if (options.generateMipmap) {
this.generateMipmap();
const { context } = this;
this.checkError(`loadBitmap(${JSON.stringify({
width: bmp.width,
height: bmp.height,
})})\ngl.texImage2D(gl.TEXTURE_2D, ${level}, gl.${context.lookupWebglConstant(internalFormat)}, gl.${context.lookupWebglConstant(sourceFormat)}, gl.${context.lookupWebglConstant(sourceType)}, bmp)\ngenerateMipmap()`);
}
const { filter } = this.options;
if (filter)
this.applyFilter(filter);
this.unbind();
options.onLoad?.();
this.eventChange.dispatch(this);
return this;
}
applyFilter(filter) {
if (this.width < 1 || this.height < 1) {
throw new Error(`[TgdTexture2D.applyFilter] Current size is invalie (${this.width}, ${this.height})! Maybe the texture is not loaded yet.`);
}
const output = new TgdTexture2D(this.context, {
params: { ...this.params },
storage: {
...this.storage,
width: this.width,
height: this.height,
},
});
const painter = new TgdPainterFilter(this.context, {
filters: [filter],
flipY: true,
texture: this,
});
const framebuffer = new TgdPainterFramebuffer(this.context, {
textureColor0: output,
depthBuffer: false,
children: [painter],
fixedSize: true,
});
framebuffer.paint(0, 0);
this.gl.deleteTexture(this._texture);
this._texture = output.glTexture;
this.bind();
this.setParams(this.params);
return this;
}
loadData(data, options) {
try {
const { level = 0, width, height, format,
// offset = 0,
} = options;
const { gl, context } = this;
if (format) {
this.format = format;
}
checkIfDataHasMinimalRequestedSize(data, this.format, width, height);
this._width = width;
this._height = height;
this.bind();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.storage.flipY);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.storage.premultipliedAlpha);
const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl);
if (sourceType === gl.FLOAT && !(data instanceof Float32Array)) {
throw new Error(`You must pass a Float32Array with this format: ${this.format}!`);
}
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, 0, sourceFormat, sourceType, data);
this.checkError(`loadData(${printArray(data)}, ${JSON.stringify(options)})
internalFormat: ${context.lookupWebglConstant(internalFormat)}
sourceFormat: ${context.lookupWebglConstant(sourceFormat)}
sourceType: ${context.lookupWebglConstant(sourceType)}
`);
this.unbind();
this.eventChange.dispatch(this);
return this;
}
catch (error) {
throw new Error(`[${this.name}::loadData()] ${resolveErrorMessage(error)}`);
}
}
/**
*
* @param unit Unit to link the texture to
* @param program The program or uniform block object that owns the uniform to update
* @param uniformName The uniform that holds the texture
*/
activate(unit, program, uniformName) {
const { gl } = this;
gl.activeTexture(gl.TEXTURE0 + unit);
this.bind();
if (program && uniformName) {
program.uniform1i(uniformName, unit);
}
return this;
}
generateMipmap() {
const { gl } = this;
this.bind();
gl.generateMipmap(gl.TEXTURE_2D);
return this;
}
setParams(parameters) {
this.bind();
webglTextureParametersSet(this.gl, parameters);
this.params = {
...this.params,
...parameters,
};
return this;
}
set textureBaseLevel(value) {
const { gl } = this;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, value);
}
get textureBaseLevel() {
const { gl } = this;
this.bind();
const value = gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL);
return value;
}
set textureMaxLevel(value) {
const { gl } = this;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, value);
}
get textureMaxLevel() {
const { gl } = this;
this.bind();
const value = gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL);
return value;
}
getParameter(parameter) {
const { gl } = this;
this.bind();
const value = gl.getTexParameter(gl.TEXTURE_2D, gl[parameter]);
return value;
}
debug(title) {
console.debug(title ?? this.name ?? "TgdTexture2D", " ", this.width, "×", this.height);
const parameters = [
"TEXTURE_MAG_FILTER",
"TEXTURE_MIN_FILTER",
"TEXTURE_WRAP_R",
"TEXTURE_WRAP_S",
"TEXTURE_WRAP_T",
"TEXTURE_MAX_LEVEL",
"TEXTURE_MAX_LOD",
"TEXTURE_MIN_LOD",
"TEXTURE_BASE_LEVEL",
"TEXTURE_COMPARE_FUNC",
"TEXTURE_COMPARE_MODE",
"TEXTURE_IMMUTABLE_FORMAT",
"TEXTURE_IMMUTABLE_LEVELS",
];
for (const name of parameters) {
const value = this.getParameter(name);
console.debug(">", name, "=", value, typeof value === "number" ? `(${webglLookup(value)})` : " ");
}
}
}
TgdTexture2D.counter = 0;
function printArray(data) {
if (data.length > 10) {
return `[${data.slice(0, 10).join(", ")}, ...] (${data.length} elements)`;
}
return JSON.stringify(data);
}
function isImageURL(url) {
const extensions = [".webp", ".jpg", ".jpeg", ".png", ".avif", ".gif"];
const urlLowercase = url.toLowerCase().trim();
for (const ext of extensions) {
if (urlLowercase.endsWith(ext))
return true;
}
return false;
}
function computeDataSizePerRow(format, width) {
const [, sourceFormat, sourceType] = format.split(" / ");
const componentCount = {
RED: 1,
RED_INTEGER: 1,
RG: 2,
RG_INTEGER: 2,
RGB: 3,
RGB_INTEGER: 3,
RGBA: 4,
RGBA_INTEGER: 4,
DEPTH_COMPONENT: 1,
DEPTH_STENCIL: 1,
};
const typeBytes = {
UNSIGNED_BYTE: 1,
BYTE: 1,
UNSIGNED_SHORT: 2,
SHORT: 2,
HALF_FLOAT: 2,
UNSIGNED_INT: 4,
INT: 4,
FLOAT: 4,
UNSIGNED_SHORT_5_6_5: 2,
UNSIGNED_SHORT_5_5_5_1: 2,
UNSIGNED_SHORT_4_4_4_4: 2,
UNSIGNED_INT_2_10_10_10_REV: 4,
UNSIGNED_INT_10F_11F_11F_REV: 4,
UNSIGNED_INT_5_9_9_9_REV: 4,
UNSIGNED_INT_24_8: 4,
FLOAT_32_UNSIGNED_INT_24_8_REV: 8,
};
const packed = typeBytes[sourceType] !== undefined &&
sourceType !== "UNSIGNED_BYTE" &&
sourceType !== "BYTE" &&
sourceType !== "UNSIGNED_SHORT" &&
sourceType !== "SHORT" &&
sourceType !== "UNSIGNED_INT" &&
sourceType !== "INT" &&
sourceType !== "HALF_FLOAT" &&
sourceType !== "FLOAT";
const bytesPerPixel = packed
? typeBytes[sourceType]
: (componentCount[sourceFormat] ?? 4) * (typeBytes[sourceType] ?? 1);
const rowBytes = width * bytesPerPixel;
return Math.ceil(rowBytes / 4) * 4;
}
function checkIfDataHasMinimalRequestedSize(data, format, width, height) {
const expectedSizePerRow = computeDataSizePerRow(format, width);
const expectedSize = expectedSizePerRow * height;
if (data.byteLength < expectedSize) {
throw new Error(`For a texture of ${width}×${height} with format "${format}", you need a storage of ${expectedSizePerRow}×${height} = ${expectedSize}.
But your data has only ${data.byteLength} bytes.
Did you specify the right format?`);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dHVyZTJkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RleHR1cmUvdGV4dHVyZTJkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFckMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBRWhELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxxQkFBcUIsRUFBRSxlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFFdkYsT0FBTyxFQUFnQixZQUFZLEVBQTJDLE1BQU0sWUFBWSxDQUFBO0FBQ2hHLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUM1QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUFFLFdBQVcsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUNsRyxPQUFPLEVBQStCLHlCQUF5QixFQUFFLE1BQU0sWUFBWSxDQUFBO0FBQ25GLE9BQU8sRUFBRSxnQkFBZ0IsRUFBdUIsTUFBTSxTQUFTLENBQUE7QUE4Ri9ELE1BQU0sT0FBTyxZQUFZO0lBbUJyQixZQUNvQixPQUFtQixFQUNsQixVQUErQixFQUFFO1FBRGxDLFlBQU8sR0FBUCxPQUFPLENBQVk7UUFDbEIsWUFBTyxHQUFQLE9BQU8sQ0FBMEI7UUFsQnRDLGdCQUFXLEdBQUcsSUFBSSxRQUFRLEVBQWdCLENBQUE7UUFFbEQsYUFBUSxHQUF3QixJQUFJLENBQUE7UUFDcEMsV0FBTSxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQ1gsWUFBTyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBRVosV0FBTSxHQUEyQjtZQUNyQyxTQUFTLEVBQUUsUUFBUTtZQUNuQixTQUFTLEVBQUUsUUFBUTtZQUNuQixLQUFLLEVBQUUsUUFBUTtZQUNmLEtBQUssRUFBRSxRQUFRO1lBQ2YsS0FBSyxFQUFFLFFBQVE7U0FDbEIsQ0FBQTtRQUNPLGlCQUFZLEdBQTJC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