This commit is contained in:
Akko
2025-08-04 18:57:35 +02:00
parent 8cf6e78a79
commit 9495868c2e
5030 changed files with 518594 additions and 17609 deletions

View File

@@ -0,0 +1,14 @@
import { TextureSource } from './TextureSource';
import type { ExtensionMetadata } from '../../../../../extensions/Extensions';
import type { TypedArray } from '../../buffer/Buffer';
import type { TextureSourceOptions } from './TextureSource';
export interface BufferSourceOptions extends TextureSourceOptions<TypedArray | ArrayBuffer> {
width: number;
height: number;
}
export declare class BufferImageSource extends TextureSource<TypedArray | ArrayBuffer> {
static extension: ExtensionMetadata;
uploadMethodId: string;
constructor(options: BufferSourceOptions);
static test(resource: any): resource is TypedArray | ArrayBuffer;
}

View File

@@ -0,0 +1,42 @@
'use strict';
var Extensions = require('../../../../../extensions/Extensions.js');
var TextureSource = require('./TextureSource.js');
"use strict";
class BufferImageSource extends TextureSource.TextureSource {
constructor(options) {
const buffer = options.resource || new Float32Array(options.width * options.height * 4);
let format = options.format;
if (!format) {
if (buffer instanceof Float32Array) {
format = "rgba32float";
} else if (buffer instanceof Int32Array) {
format = "rgba32uint";
} else if (buffer instanceof Uint32Array) {
format = "rgba32uint";
} else if (buffer instanceof Int16Array) {
format = "rgba16uint";
} else if (buffer instanceof Uint16Array) {
format = "rgba16uint";
} else if (buffer instanceof Int8Array) {
format = "bgra8unorm";
} else {
format = "bgra8unorm";
}
}
super({
...options,
resource: buffer,
format
});
this.uploadMethodId = "buffer";
}
static test(resource) {
return resource instanceof Int8Array || resource instanceof Uint8Array || resource instanceof Uint8ClampedArray || resource instanceof Int16Array || resource instanceof Uint16Array || resource instanceof Int32Array || resource instanceof Uint32Array || resource instanceof Float32Array;
}
}
BufferImageSource.extension = Extensions.ExtensionType.TextureSource;
exports.BufferImageSource = BufferImageSource;
//# sourceMappingURL=BufferImageSource.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BufferImageSource.js","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/BufferImageSource.ts"],"sourcesContent":["import { ExtensionType } from '../../../../../extensions/Extensions';\nimport { TextureSource } from './TextureSource';\n\nimport type { ExtensionMetadata } from '../../../../../extensions/Extensions';\nimport type { TypedArray } from '../../buffer/Buffer';\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport interface BufferSourceOptions extends TextureSourceOptions<TypedArray | ArrayBuffer>\n{\n width: number;\n height: number;\n}\n\nexport class BufferImageSource extends TextureSource<TypedArray | ArrayBuffer>\n{\n public static extension: ExtensionMetadata = ExtensionType.TextureSource;\n\n public uploadMethodId = 'buffer';\n\n constructor(options: BufferSourceOptions)\n {\n const buffer = options.resource || new Float32Array(options.width * options.height * 4);\n let format = options.format;\n\n if (!format)\n {\n if (buffer instanceof Float32Array)\n {\n format = 'rgba32float';\n }\n else if (buffer instanceof Int32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Uint32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Int16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Uint16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Int8Array)\n {\n format = 'bgra8unorm';\n }\n else\n {\n format = 'bgra8unorm';\n }\n }\n\n super({\n ...options,\n resource: buffer,\n format,\n });\n }\n\n public static test(resource: any): resource is TypedArray | ArrayBuffer\n {\n return resource instanceof Int8Array\n || resource instanceof Uint8Array\n || resource instanceof Uint8ClampedArray\n || resource instanceof Int16Array\n || resource instanceof Uint16Array\n || resource instanceof Int32Array\n || resource instanceof Uint32Array\n || resource instanceof Float32Array;\n }\n}\n"],"names":["TextureSource","ExtensionType"],"mappings":";;;;;;AAaO,MAAM,0BAA0BA,2BACvC,CAAA;AAAA,EAKI,YAAY,OACZ,EAAA;AACI,IAAM,MAAA,MAAA,GAAS,QAAQ,QAAY,IAAA,IAAI,aAAa,OAAQ,CAAA,KAAA,GAAQ,OAAQ,CAAA,MAAA,GAAS,CAAC,CAAA,CAAA;AACtF,IAAA,IAAI,SAAS,OAAQ,CAAA,MAAA,CAAA;AAErB,IAAA,IAAI,CAAC,MACL,EAAA;AACI,MAAA,IAAI,kBAAkB,YACtB,EAAA;AACI,QAAS,MAAA,GAAA,aAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,UAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,WAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,UAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,WAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,SAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OAGb,MAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb;AAAA,KACJ;AAEA,IAAM,KAAA,CAAA;AAAA,MACF,GAAG,OAAA;AAAA,MACH,QAAU,EAAA,MAAA;AAAA,MACV,MAAA;AAAA,KACH,CAAA,CAAA;AA3CL,IAAA,IAAA,CAAO,cAAiB,GAAA,QAAA,CAAA;AAAA,GA4CxB;AAAA,EAEA,OAAc,KAAK,QACnB,EAAA;AACI,IAAA,OAAO,QAAoB,YAAA,SAAA,IACxB,QAAoB,YAAA,UAAA,IACpB,oBAAoB,iBACpB,IAAA,QAAA,YAAoB,UACpB,IAAA,QAAA,YAAoB,WACpB,IAAA,QAAA,YAAoB,UACpB,IAAA,QAAA,YAAoB,eACpB,QAAoB,YAAA,YAAA,CAAA;AAAA,GAC3B;AACJ,CAAA;AA7Da,iBAAA,CAEK,YAA+BC,wBAAc,CAAA,aAAA;;;;"}

View File

@@ -0,0 +1,40 @@
import { ExtensionType } from '../../../../../extensions/Extensions.mjs';
import { TextureSource } from './TextureSource.mjs';
"use strict";
class BufferImageSource extends TextureSource {
constructor(options) {
const buffer = options.resource || new Float32Array(options.width * options.height * 4);
let format = options.format;
if (!format) {
if (buffer instanceof Float32Array) {
format = "rgba32float";
} else if (buffer instanceof Int32Array) {
format = "rgba32uint";
} else if (buffer instanceof Uint32Array) {
format = "rgba32uint";
} else if (buffer instanceof Int16Array) {
format = "rgba16uint";
} else if (buffer instanceof Uint16Array) {
format = "rgba16uint";
} else if (buffer instanceof Int8Array) {
format = "bgra8unorm";
} else {
format = "bgra8unorm";
}
}
super({
...options,
resource: buffer,
format
});
this.uploadMethodId = "buffer";
}
static test(resource) {
return resource instanceof Int8Array || resource instanceof Uint8Array || resource instanceof Uint8ClampedArray || resource instanceof Int16Array || resource instanceof Uint16Array || resource instanceof Int32Array || resource instanceof Uint32Array || resource instanceof Float32Array;
}
}
BufferImageSource.extension = ExtensionType.TextureSource;
export { BufferImageSource };
//# sourceMappingURL=BufferImageSource.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BufferImageSource.mjs","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/BufferImageSource.ts"],"sourcesContent":["import { ExtensionType } from '../../../../../extensions/Extensions';\nimport { TextureSource } from './TextureSource';\n\nimport type { ExtensionMetadata } from '../../../../../extensions/Extensions';\nimport type { TypedArray } from '../../buffer/Buffer';\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport interface BufferSourceOptions extends TextureSourceOptions<TypedArray | ArrayBuffer>\n{\n width: number;\n height: number;\n}\n\nexport class BufferImageSource extends TextureSource<TypedArray | ArrayBuffer>\n{\n public static extension: ExtensionMetadata = ExtensionType.TextureSource;\n\n public uploadMethodId = 'buffer';\n\n constructor(options: BufferSourceOptions)\n {\n const buffer = options.resource || new Float32Array(options.width * options.height * 4);\n let format = options.format;\n\n if (!format)\n {\n if (buffer instanceof Float32Array)\n {\n format = 'rgba32float';\n }\n else if (buffer instanceof Int32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Uint32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Int16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Uint16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Int8Array)\n {\n format = 'bgra8unorm';\n }\n else\n {\n format = 'bgra8unorm';\n }\n }\n\n super({\n ...options,\n resource: buffer,\n format,\n });\n }\n\n public static test(resource: any): resource is TypedArray | ArrayBuffer\n {\n return resource instanceof Int8Array\n || resource instanceof Uint8Array\n || resource instanceof Uint8ClampedArray\n || resource instanceof Int16Array\n || resource instanceof Uint16Array\n || resource instanceof Int32Array\n || resource instanceof Uint32Array\n || resource instanceof Float32Array;\n }\n}\n"],"names":[],"mappings":";;;;AAaO,MAAM,0BAA0B,aACvC,CAAA;AAAA,EAKI,YAAY,OACZ,EAAA;AACI,IAAM,MAAA,MAAA,GAAS,QAAQ,QAAY,IAAA,IAAI,aAAa,OAAQ,CAAA,KAAA,GAAQ,OAAQ,CAAA,MAAA,GAAS,CAAC,CAAA,CAAA;AACtF,IAAA,IAAI,SAAS,OAAQ,CAAA,MAAA,CAAA;AAErB,IAAA,IAAI,CAAC,MACL,EAAA;AACI,MAAA,IAAI,kBAAkB,YACtB,EAAA;AACI,QAAS,MAAA,GAAA,aAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,UAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,WAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,UAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,WAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb,MAAA,IACS,kBAAkB,SAC3B,EAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OAGb,MAAA;AACI,QAAS,MAAA,GAAA,YAAA,CAAA;AAAA,OACb;AAAA,KACJ;AAEA,IAAM,KAAA,CAAA;AAAA,MACF,GAAG,OAAA;AAAA,MACH,QAAU,EAAA,MAAA;AAAA,MACV,MAAA;AAAA,KACH,CAAA,CAAA;AA3CL,IAAA,IAAA,CAAO,cAAiB,GAAA,QAAA,CAAA;AAAA,GA4CxB;AAAA,EAEA,OAAc,KAAK,QACnB,EAAA;AACI,IAAA,OAAO,QAAoB,YAAA,SAAA,IACxB,QAAoB,YAAA,UAAA,IACpB,oBAAoB,iBACpB,IAAA,QAAA,YAAoB,UACpB,IAAA,QAAA,YAAoB,WACpB,IAAA,QAAA,YAAoB,UACpB,IAAA,QAAA,YAAoB,eACpB,QAAoB,YAAA,YAAA,CAAA;AAAA,GAC3B;AACJ,CAAA;AA7Da,iBAAA,CAEK,YAA+B,aAAc,CAAA,aAAA;;;;"}

View File

@@ -0,0 +1,27 @@
import { TextureSource } from './TextureSource';
import type { ICanvas } from '../../../../../environment/canvas/ICanvas';
import type { ExtensionMetadata } from '../../../../../extensions/Extensions';
import type { TextureSourceOptions } from './TextureSource';
export interface CanvasSourceOptions extends TextureSourceOptions<ICanvas> {
/** should the canvas be resized to preserve its screen width and height regardless of the resolution of the renderer */
autoDensity?: boolean;
/** if true, this canvas will be set up to be transparent where possible */
transparent?: boolean;
}
export declare class CanvasSource extends TextureSource<ICanvas> {
static extension: ExtensionMetadata;
uploadMethodId: string;
autoDensity: boolean;
transparent: boolean;
private _context2D;
constructor(options: CanvasSourceOptions);
resizeCanvas(): void;
resize(width?: number, height?: number, resolution?: number): boolean;
static test(resource: any): resource is ICanvas;
/**
* Returns the 2D rendering context for the canvas.
* Caches the context after creating it.
* @returns The 2D rendering context of the canvas.
*/
get context2D(): CanvasRenderingContext2D;
}

View File

@@ -0,0 +1,66 @@
'use strict';
var adapter = require('../../../../../environment/adapter.js');
var Extensions = require('../../../../../extensions/Extensions.js');
var TextureSource = require('./TextureSource.js');
"use strict";
class CanvasSource extends TextureSource.TextureSource {
constructor(options) {
if (!options.resource) {
options.resource = adapter.DOMAdapter.get().createCanvas();
}
if (!options.width) {
options.width = options.resource.width;
if (!options.autoDensity) {
options.width /= options.resolution;
}
}
if (!options.height) {
options.height = options.resource.height;
if (!options.autoDensity) {
options.height /= options.resolution;
}
}
super(options);
this.uploadMethodId = "image";
this.autoDensity = options.autoDensity;
const canvas = options.resource;
if (this.pixelWidth !== canvas.width || this.pixelWidth !== canvas.height) {
this.resizeCanvas();
}
this.transparent = !!options.transparent;
}
resizeCanvas() {
if (this.autoDensity) {
this.resource.style.width = `${this.width}px`;
this.resource.style.height = `${this.height}px`;
}
if (this.resource.width !== this.pixelWidth || this.resource.height !== this.pixelHeight) {
this.resource.width = this.pixelWidth;
this.resource.height = this.pixelHeight;
}
}
resize(width = this.width, height = this.height, resolution = this._resolution) {
const didResize = super.resize(width, height, resolution);
if (didResize) {
this.resizeCanvas();
}
return didResize;
}
static test(resource) {
return globalThis.HTMLCanvasElement && resource instanceof HTMLCanvasElement || globalThis.OffscreenCanvas && resource instanceof OffscreenCanvas;
}
/**
* Returns the 2D rendering context for the canvas.
* Caches the context after creating it.
* @returns The 2D rendering context of the canvas.
*/
get context2D() {
return this._context2D || (this._context2D = this.resource.getContext("2d"));
}
}
CanvasSource.extension = Extensions.ExtensionType.TextureSource;
exports.CanvasSource = CanvasSource;
//# sourceMappingURL=CanvasSource.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,64 @@
import { DOMAdapter } from '../../../../../environment/adapter.mjs';
import { ExtensionType } from '../../../../../extensions/Extensions.mjs';
import { TextureSource } from './TextureSource.mjs';
"use strict";
class CanvasSource extends TextureSource {
constructor(options) {
if (!options.resource) {
options.resource = DOMAdapter.get().createCanvas();
}
if (!options.width) {
options.width = options.resource.width;
if (!options.autoDensity) {
options.width /= options.resolution;
}
}
if (!options.height) {
options.height = options.resource.height;
if (!options.autoDensity) {
options.height /= options.resolution;
}
}
super(options);
this.uploadMethodId = "image";
this.autoDensity = options.autoDensity;
const canvas = options.resource;
if (this.pixelWidth !== canvas.width || this.pixelWidth !== canvas.height) {
this.resizeCanvas();
}
this.transparent = !!options.transparent;
}
resizeCanvas() {
if (this.autoDensity) {
this.resource.style.width = `${this.width}px`;
this.resource.style.height = `${this.height}px`;
}
if (this.resource.width !== this.pixelWidth || this.resource.height !== this.pixelHeight) {
this.resource.width = this.pixelWidth;
this.resource.height = this.pixelHeight;
}
}
resize(width = this.width, height = this.height, resolution = this._resolution) {
const didResize = super.resize(width, height, resolution);
if (didResize) {
this.resizeCanvas();
}
return didResize;
}
static test(resource) {
return globalThis.HTMLCanvasElement && resource instanceof HTMLCanvasElement || globalThis.OffscreenCanvas && resource instanceof OffscreenCanvas;
}
/**
* Returns the 2D rendering context for the canvas.
* Caches the context after creating it.
* @returns The 2D rendering context of the canvas.
*/
get context2D() {
return this._context2D || (this._context2D = this.resource.getContext("2d"));
}
}
CanvasSource.extension = ExtensionType.TextureSource;
export { CanvasSource };
//# sourceMappingURL=CanvasSource.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { TextureSource } from './TextureSource';
import type { TextureSourceOptions } from './TextureSource';
export declare class CompressedSource extends TextureSource<Uint8Array[]> {
readonly uploadMethodId = "compressed";
constructor(options: TextureSourceOptions);
}

View File

@@ -0,0 +1,16 @@
'use strict';
var TextureSource = require('./TextureSource.js');
"use strict";
class CompressedSource extends TextureSource.TextureSource {
constructor(options) {
super(options);
this.uploadMethodId = "compressed";
this.resource = options.resource;
this.mipLevelCount = this.resource.length;
}
}
exports.CompressedSource = CompressedSource;
//# sourceMappingURL=CompressedSource.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CompressedSource.js","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/CompressedSource.ts"],"sourcesContent":["import { TextureSource } from './TextureSource';\n\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport class CompressedSource extends TextureSource<Uint8Array[]>\n{\n public readonly uploadMethodId = 'compressed';\n\n constructor(options: TextureSourceOptions)\n {\n super(options);\n\n this.resource = options.resource;\n this.mipLevelCount = this.resource.length;\n }\n}\n"],"names":["TextureSource"],"mappings":";;;;;AAIO,MAAM,yBAAyBA,2BACtC,CAAA;AAAA,EAGI,YAAY,OACZ,EAAA;AACI,IAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAJjB,IAAA,IAAA,CAAgB,cAAiB,GAAA,YAAA,CAAA;AAM7B,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA,CAAA;AACxB,IAAK,IAAA,CAAA,aAAA,GAAgB,KAAK,QAAS,CAAA,MAAA,CAAA;AAAA,GACvC;AACJ;;;;"}

View File

@@ -0,0 +1,14 @@
import { TextureSource } from './TextureSource.mjs';
"use strict";
class CompressedSource extends TextureSource {
constructor(options) {
super(options);
this.uploadMethodId = "compressed";
this.resource = options.resource;
this.mipLevelCount = this.resource.length;
}
}
export { CompressedSource };
//# sourceMappingURL=CompressedSource.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CompressedSource.mjs","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/CompressedSource.ts"],"sourcesContent":["import { TextureSource } from './TextureSource';\n\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport class CompressedSource extends TextureSource<Uint8Array[]>\n{\n public readonly uploadMethodId = 'compressed';\n\n constructor(options: TextureSourceOptions)\n {\n super(options);\n\n this.resource = options.resource;\n this.mipLevelCount = this.resource.length;\n }\n}\n"],"names":[],"mappings":";;;AAIO,MAAM,yBAAyB,aACtC,CAAA;AAAA,EAGI,YAAY,OACZ,EAAA;AACI,IAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAJjB,IAAA,IAAA,CAAgB,cAAiB,GAAA,YAAA,CAAA;AAM7B,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA,CAAA;AACxB,IAAK,IAAA,CAAA,aAAA,GAAgB,KAAK,QAAS,CAAA,MAAA,CAAA;AAAA,GACvC;AACJ;;;;"}

View File

@@ -0,0 +1,11 @@
import { TextureSource } from './TextureSource';
import type { ICanvas } from '../../../../../environment/canvas/ICanvas';
import type { ExtensionMetadata } from '../../../../../extensions/Extensions';
import type { TextureSourceOptions } from './TextureSource';
export type ImageResource = ImageBitmap | HTMLCanvasElement | OffscreenCanvas | ICanvas | VideoFrame | HTMLImageElement | HTMLVideoElement;
export declare class ImageSource extends TextureSource<ImageResource> {
static extension: ExtensionMetadata;
uploadMethodId: string;
constructor(options: TextureSourceOptions<ImageResource>);
static test(resource: any): resource is ImageResource;
}

View File

@@ -0,0 +1,29 @@
'use strict';
var adapter = require('../../../../../environment/adapter.js');
var Extensions = require('../../../../../extensions/Extensions.js');
var warn = require('../../../../../utils/logging/warn.js');
var TextureSource = require('./TextureSource.js');
"use strict";
class ImageSource extends TextureSource.TextureSource {
constructor(options) {
if (options.resource && (globalThis.HTMLImageElement && options.resource instanceof HTMLImageElement)) {
const canvas = adapter.DOMAdapter.get().createCanvas(options.resource.width, options.resource.height);
const context = canvas.getContext("2d");
context.drawImage(options.resource, 0, 0, options.resource.width, options.resource.height);
options.resource = canvas;
warn.warn("ImageSource: Image element passed, converting to canvas. Use CanvasSource instead.");
}
super(options);
this.uploadMethodId = "image";
this.autoGarbageCollect = true;
}
static test(resource) {
return globalThis.HTMLImageElement && resource instanceof HTMLImageElement || typeof ImageBitmap !== "undefined" && resource instanceof ImageBitmap || globalThis.VideoFrame && resource instanceof VideoFrame;
}
}
ImageSource.extension = Extensions.ExtensionType.TextureSource;
exports.ImageSource = ImageSource;
//# sourceMappingURL=ImageSource.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ImageSource.js","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/ImageSource.ts"],"sourcesContent":["import { DOMAdapter } from '../../../../../environment/adapter';\nimport { ExtensionType } from '../../../../../extensions/Extensions';\nimport { warn } from '../../../../../utils/logging/warn';\nimport { TextureSource } from './TextureSource';\n\nimport type { ICanvas } from '../../../../../environment/canvas/ICanvas';\nimport type { ExtensionMetadata } from '../../../../../extensions/Extensions';\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport type ImageResource =\nImageBitmap\n| HTMLCanvasElement\n| OffscreenCanvas\n| ICanvas\n| VideoFrame\n| HTMLImageElement\n| HTMLVideoElement;\n\nexport class ImageSource extends TextureSource<ImageResource>\n{\n public static extension: ExtensionMetadata = ExtensionType.TextureSource;\n public uploadMethodId = 'image';\n\n constructor(options: TextureSourceOptions<ImageResource>)\n {\n if (options.resource && (globalThis.HTMLImageElement && options.resource instanceof HTMLImageElement))\n {\n const canvas = DOMAdapter.get().createCanvas(options.resource.width, options.resource.height);\n const context = canvas.getContext('2d');\n\n context.drawImage(options.resource, 0, 0, options.resource.width, options.resource.height);\n options.resource = canvas;\n\n // #if _DEBUG\n warn('ImageSource: Image element passed, converting to canvas. Use CanvasSource instead.');\n // #endif\n }\n\n super(options);\n\n this.autoGarbageCollect = true;\n }\n\n public static test(resource: any): resource is ImageResource\n {\n return (globalThis.HTMLImageElement && resource instanceof HTMLImageElement)\n || (typeof ImageBitmap !== 'undefined' && resource instanceof ImageBitmap)\n || (globalThis.VideoFrame && resource instanceof VideoFrame);\n }\n}\n"],"names":["TextureSource","DOMAdapter","warn","ExtensionType"],"mappings":";;;;;;;;AAkBO,MAAM,oBAAoBA,2BACjC,CAAA;AAAA,EAII,YAAY,OACZ,EAAA;AACI,IAAA,IAAI,QAAQ,QAAa,KAAA,UAAA,CAAW,gBAAoB,IAAA,OAAA,CAAQ,oBAAoB,gBACpF,CAAA,EAAA;AACI,MAAM,MAAA,MAAA,GAASC,kBAAW,CAAA,GAAA,EAAM,CAAA,YAAA,CAAa,QAAQ,QAAS,CAAA,KAAA,EAAO,OAAQ,CAAA,QAAA,CAAS,MAAM,CAAA,CAAA;AAC5F,MAAM,MAAA,OAAA,GAAU,MAAO,CAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAEtC,MAAQ,OAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,QAAA,EAAU,CAAG,EAAA,CAAA,EAAG,QAAQ,QAAS,CAAA,KAAA,EAAO,OAAQ,CAAA,QAAA,CAAS,MAAM,CAAA,CAAA;AACzF,MAAA,OAAA,CAAQ,QAAW,GAAA,MAAA,CAAA;AAGnB,MAAAC,SAAA,CAAK,oFAAoF,CAAA,CAAA;AAAA,KAE7F;AAEA,IAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAjBjB,IAAA,IAAA,CAAO,cAAiB,GAAA,OAAA,CAAA;AAmBpB,IAAA,IAAA,CAAK,kBAAqB,GAAA,IAAA,CAAA;AAAA,GAC9B;AAAA,EAEA,OAAc,KAAK,QACnB,EAAA;AACI,IAAQ,OAAA,UAAA,CAAW,gBAAoB,IAAA,QAAA,YAAoB,gBACvD,IAAA,OAAO,WAAgB,KAAA,WAAA,IAAe,QAAoB,YAAA,WAAA,IAC1D,UAAW,CAAA,UAAA,IAAc,QAAoB,YAAA,UAAA,CAAA;AAAA,GACrD;AACJ,CAAA;AA/Ba,WAAA,CAEK,YAA+BC,wBAAc,CAAA,aAAA;;;;"}

View File

@@ -0,0 +1,27 @@
import { DOMAdapter } from '../../../../../environment/adapter.mjs';
import { ExtensionType } from '../../../../../extensions/Extensions.mjs';
import { warn } from '../../../../../utils/logging/warn.mjs';
import { TextureSource } from './TextureSource.mjs';
"use strict";
class ImageSource extends TextureSource {
constructor(options) {
if (options.resource && (globalThis.HTMLImageElement && options.resource instanceof HTMLImageElement)) {
const canvas = DOMAdapter.get().createCanvas(options.resource.width, options.resource.height);
const context = canvas.getContext("2d");
context.drawImage(options.resource, 0, 0, options.resource.width, options.resource.height);
options.resource = canvas;
warn("ImageSource: Image element passed, converting to canvas. Use CanvasSource instead.");
}
super(options);
this.uploadMethodId = "image";
this.autoGarbageCollect = true;
}
static test(resource) {
return globalThis.HTMLImageElement && resource instanceof HTMLImageElement || typeof ImageBitmap !== "undefined" && resource instanceof ImageBitmap || globalThis.VideoFrame && resource instanceof VideoFrame;
}
}
ImageSource.extension = ExtensionType.TextureSource;
export { ImageSource };
//# sourceMappingURL=ImageSource.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ImageSource.mjs","sources":["../../../../../../src/rendering/renderers/shared/texture/sources/ImageSource.ts"],"sourcesContent":["import { DOMAdapter } from '../../../../../environment/adapter';\nimport { ExtensionType } from '../../../../../extensions/Extensions';\nimport { warn } from '../../../../../utils/logging/warn';\nimport { TextureSource } from './TextureSource';\n\nimport type { ICanvas } from '../../../../../environment/canvas/ICanvas';\nimport type { ExtensionMetadata } from '../../../../../extensions/Extensions';\nimport type { TextureSourceOptions } from './TextureSource';\n\nexport type ImageResource =\nImageBitmap\n| HTMLCanvasElement\n| OffscreenCanvas\n| ICanvas\n| VideoFrame\n| HTMLImageElement\n| HTMLVideoElement;\n\nexport class ImageSource extends TextureSource<ImageResource>\n{\n public static extension: ExtensionMetadata = ExtensionType.TextureSource;\n public uploadMethodId = 'image';\n\n constructor(options: TextureSourceOptions<ImageResource>)\n {\n if (options.resource && (globalThis.HTMLImageElement && options.resource instanceof HTMLImageElement))\n {\n const canvas = DOMAdapter.get().createCanvas(options.resource.width, options.resource.height);\n const context = canvas.getContext('2d');\n\n context.drawImage(options.resource, 0, 0, options.resource.width, options.resource.height);\n options.resource = canvas;\n\n // #if _DEBUG\n warn('ImageSource: Image element passed, converting to canvas. Use CanvasSource instead.');\n // #endif\n }\n\n super(options);\n\n this.autoGarbageCollect = true;\n }\n\n public static test(resource: any): resource is ImageResource\n {\n return (globalThis.HTMLImageElement && resource instanceof HTMLImageElement)\n || (typeof ImageBitmap !== 'undefined' && resource instanceof ImageBitmap)\n || (globalThis.VideoFrame && resource instanceof VideoFrame);\n }\n}\n"],"names":[],"mappings":";;;;;;AAkBO,MAAM,oBAAoB,aACjC,CAAA;AAAA,EAII,YAAY,OACZ,EAAA;AACI,IAAA,IAAI,QAAQ,QAAa,KAAA,UAAA,CAAW,gBAAoB,IAAA,OAAA,CAAQ,oBAAoB,gBACpF,CAAA,EAAA;AACI,MAAM,MAAA,MAAA,GAAS,UAAW,CAAA,GAAA,EAAM,CAAA,YAAA,CAAa,QAAQ,QAAS,CAAA,KAAA,EAAO,OAAQ,CAAA,QAAA,CAAS,MAAM,CAAA,CAAA;AAC5F,MAAM,MAAA,OAAA,GAAU,MAAO,CAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAEtC,MAAQ,OAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,QAAA,EAAU,CAAG,EAAA,CAAA,EAAG,QAAQ,QAAS,CAAA,KAAA,EAAO,OAAQ,CAAA,QAAA,CAAS,MAAM,CAAA,CAAA;AACzF,MAAA,OAAA,CAAQ,QAAW,GAAA,MAAA,CAAA;AAGnB,MAAA,IAAA,CAAK,oFAAoF,CAAA,CAAA;AAAA,KAE7F;AAEA,IAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAjBjB,IAAA,IAAA,CAAO,cAAiB,GAAA,OAAA,CAAA;AAmBpB,IAAA,IAAA,CAAK,kBAAqB,GAAA,IAAA,CAAA;AAAA,GAC9B;AAAA,EAEA,OAAc,KAAK,QACnB,EAAA;AACI,IAAQ,OAAA,UAAA,CAAW,gBAAoB,IAAA,QAAA,YAAoB,gBACvD,IAAA,OAAO,WAAgB,KAAA,WAAA,IAAe,QAAoB,YAAA,WAAA,IAC1D,UAAW,CAAA,UAAA,IAAc,QAAoB,YAAA,UAAA,CAAA;AAAA,GACrD;AACJ,CAAA;AA/Ba,WAAA,CAEK,YAA+B,aAAc,CAAA,aAAA;;;;"}

View File

@@ -0,0 +1,270 @@
import EventEmitter from 'eventemitter3';
import { TextureStyle } from '../TextureStyle';
import type { BindResource } from '../../../gpu/shader/BindResource';
import type { ALPHA_MODES, SCALE_MODE, TEXTURE_DIMENSIONS, TEXTURE_FORMATS, WRAP_MODE } from '../const';
import type { TextureStyleOptions } from '../TextureStyle';
import type { TextureResourceOrOptions } from '../utils/textureFrom';
/**
* options for creating a new TextureSource
* @memberof rendering
*/
export interface TextureSourceOptions<T extends Record<string, any> = any> extends TextureStyleOptions {
/**
* the resource that will be uploaded to the GPU. This is where we get our pixels from
* eg an ImageBimt / Canvas / Video etc
*/
resource?: T;
/** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */
width?: number;
/** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */
height?: number;
/** the resolution of the texture. */
resolution?: number;
/** the format that the texture data has */
format?: TEXTURE_FORMATS;
/**
* Used by internal textures
* @ignore
*/
sampleCount?: number;
/**
* Only really affects RenderTextures.
* Should we use antialiasing for this texture. It will look better, but may impact performance as a
* Blit operation will be required to resolve the texture.
*/
antialias?: boolean;
/** how many dimensions does this texture have? currently v8 only supports 2d */
dimensions?: TEXTURE_DIMENSIONS;
/** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */
mipLevelCount?: number;
/**
* Should we auto generate mipmaps for this texture? This will automatically generate mipmaps
* for this texture when uploading to the GPU. Mipmapped textures take up more memory, but
* can look better when scaled down.
*
* For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.
* If you do, make sure to call `updateMipmaps` after you update the texture.
*/
autoGenerateMipmaps?: boolean;
/** the alpha mode of the texture */
alphaMode?: ALPHA_MODES;
/** optional label, can be used for debugging */
label?: string;
/** If true, the Garbage Collector will unload this texture if it is not used after a period of time */
autoGarbageCollect?: boolean;
}
/**
* A TextureSource stores the information that represents an image.
* All textures have require TextureSource, which contains information about the source.
* Therefore you can have many textures all using a single TextureSource (eg a sprite sheet)
*
* This is an class is extended depending on the source of the texture.
* Eg if you are using an an image as your resource, then an ImageSource is used.
* @memberof rendering
* @typeParam T - The TextureSource's Resource type.
*/
export declare class TextureSource<T extends Record<string, any> = any> extends EventEmitter<{
change: BindResource;
update: TextureSource;
unload: TextureSource;
destroy: TextureSource;
resize: TextureSource;
styleChange: TextureSource;
updateMipmaps: TextureSource;
error: Error;
}> implements BindResource {
protected readonly options: TextureSourceOptions<T>;
/** The default options used when creating a new TextureSource. override these to add your own defaults */
static defaultOptions: TextureSourceOptions;
/** unique id for this Texture source */
readonly uid: number;
/** optional label, can be used for debugging */
label: string;
/**
* The resource type used by this TextureSource. This is used by the bind groups to determine
* how to handle this resource.
* @ignore
* @internal
*/
readonly _resourceType = "textureSource";
/**
* i unique resource id, used by the bind group systems.
* This can change if the texture is resized or its resource changes
*/
_resourceId: number;
/**
* this is how the backends know how to upload this texture to the GPU
* It changes depending on the resource type. Classes that extend TextureSource
* should override this property.
* @ignore
* @internal
*/
uploadMethodId: string;
_resolution: number;
/** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */
pixelWidth: number;
/** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */
pixelHeight: number;
/**
* the width of this texture source, accounting for resolution
* eg pixelWidth 200, resolution 2, then width will be 100
*/
width: number;
/**
* the height of this texture source, accounting for resolution
* eg pixelHeight 200, resolution 2, then height will be 100
*/
height: number;
/**
* the resource that will be uploaded to the GPU. This is where we get our pixels from
* eg an ImageBimt / Canvas / Video etc
*/
resource: T;
/**
* The number of samples of a multisample texture. This is always 1 for non-multisample textures.
* To enable multisample for a texture, set antialias to true
* @internal
* @ignore
*/
sampleCount: number;
/** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */
mipLevelCount: number;
/**
* Should we auto generate mipmaps for this texture? This will automatically generate mipmaps
* for this texture when uploading to the GPU. Mipmapped textures take up more memory, but
* can look better when scaled down.
*
* For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.
* If you do, make sure to call `updateMipmaps` after you update the texture.
*/
autoGenerateMipmaps: boolean;
/** the format that the texture data has */
format: TEXTURE_FORMATS;
/** how many dimensions does this texture have? currently v8 only supports 2d */
dimension: TEXTURE_DIMENSIONS;
/** the alpha mode of the texture */
alphaMode: ALPHA_MODES;
private _style;
/**
* Only really affects RenderTextures.
* Should we use antialiasing for this texture. It will look better, but may impact performance as a
* Blit operation will be required to resolve the texture.
*/
antialias: boolean;
/**
* Has the source been destroyed?
* @readonly
*/
destroyed: boolean;
/**
* Used by automatic texture Garbage Collection, stores last GC tick when it was bound
* @protected
*/
_touched: number;
/**
* Used by the batcher to build texture batches. faster to have the variable here!
* @protected
*/
_batchTick: number;
/**
* A temporary batch location for the texture batching. Here for performance reasons only!
* @protected
*/
_textureBindLocation: number;
isPowerOfTwo: boolean;
/** If true, the Garbage Collector will unload this texture if it is not used after a period of time */
autoGarbageCollect: boolean;
/**
* used internally to know where a texture came from. Usually assigned by the asset loader!
* @ignore
*/
_sourceOrigin: string;
/**
* @param options - options for creating a new TextureSource
*/
constructor(options?: TextureSourceOptions<T>);
/** returns itself */
get source(): TextureSource;
/** the style of the texture */
get style(): TextureStyle;
set style(value: TextureStyle);
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get addressMode(): WRAP_MODE;
set addressMode(value: WRAP_MODE);
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get repeatMode(): WRAP_MODE;
set repeatMode(value: WRAP_MODE);
/** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */
get magFilter(): SCALE_MODE;
set magFilter(value: SCALE_MODE);
/** Specifies the sampling behavior when the sample footprint is larger than one texel. */
get minFilter(): SCALE_MODE;
set minFilter(value: SCALE_MODE);
/** Specifies behavior for sampling between mipmap levels. */
get mipmapFilter(): SCALE_MODE;
set mipmapFilter(value: SCALE_MODE);
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMinClamp(): number;
set lodMinClamp(value: number);
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMaxClamp(): number;
set lodMaxClamp(value: number);
private _onStyleChange;
/** call this if you have modified the texture outside of the constructor */
update(): void;
/** Destroys this texture source */
destroy(): void;
/**
* This will unload the Texture source from the GPU. This will free up the GPU memory
* As soon as it is required fore rendering, it will be re-uploaded.
*/
unload(): void;
/** the width of the resource. This is the REAL pure number, not accounting resolution */
get resourceWidth(): number;
/** the height of the resource. This is the REAL pure number, not accounting resolution */
get resourceHeight(): number;
/**
* the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture
* but will the size of the texture when rendered.
*
* changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel
* density will have increased)
*/
get resolution(): number;
set resolution(resolution: number);
/**
* Resize the texture, this is handy if you want to use the texture as a render texture
* @param width - the new width of the texture
* @param height - the new height of the texture
* @param resolution - the new resolution of the texture
* @returns - if the texture was resized
*/
resize(width?: number, height?: number, resolution?: number): boolean;
/**
* Lets the renderer know that this texture has been updated and its mipmaps should be re-generated.
* This is only important for RenderTexture instances, as standard Texture instances will have their
* mipmaps generated on upload. You should call this method after you make any change to the texture
*
* The reason for this is is can be quite expensive to update mipmaps for a texture. So by default,
* We want you, the developer to specify when this action should happen.
*
* Generally you don't want to have mipmaps generated on Render targets that are changed every frame,
*/
updateMipmaps(): void;
set wrapMode(value: WRAP_MODE);
get wrapMode(): WRAP_MODE;
set scaleMode(value: SCALE_MODE);
/** setting this will set magFilter,minFilter and mipmapFilter all at once! */
get scaleMode(): SCALE_MODE;
/**
* Refresh check for isPowerOfTwo texture based on size
* @private
*/
protected _refreshPOT(): void;
static test(_resource: any): any;
/**
* A helper function that creates a new TextureSource based on the resource you provide.
* @param resource - The resource to create the texture source from.
*/
static from: (resource: TextureResourceOrOptions) => TextureSource;
}

View File

@@ -0,0 +1,334 @@
'use strict';
var EventEmitter = require('eventemitter3');
var pow2 = require('../../../../../maths/misc/pow2.js');
var definedProps = require('../../../../../scene/container/utils/definedProps.js');
var uid = require('../../../../../utils/data/uid.js');
var TextureStyle = require('../TextureStyle.js');
"use strict";
const _TextureSource = class _TextureSource extends EventEmitter {
/**
* @param options - options for creating a new TextureSource
*/
constructor(options = {}) {
super();
this.options = options;
/** unique id for this Texture source */
this.uid = uid.uid("textureSource");
/**
* The resource type used by this TextureSource. This is used by the bind groups to determine
* how to handle this resource.
* @ignore
* @internal
*/
this._resourceType = "textureSource";
/**
* i unique resource id, used by the bind group systems.
* This can change if the texture is resized or its resource changes
*/
this._resourceId = uid.uid("resource");
/**
* this is how the backends know how to upload this texture to the GPU
* It changes depending on the resource type. Classes that extend TextureSource
* should override this property.
* @ignore
* @internal
*/
this.uploadMethodId = "unknown";
// dimensions
this._resolution = 1;
/** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */
this.pixelWidth = 1;
/** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */
this.pixelHeight = 1;
/**
* the width of this texture source, accounting for resolution
* eg pixelWidth 200, resolution 2, then width will be 100
*/
this.width = 1;
/**
* the height of this texture source, accounting for resolution
* eg pixelHeight 200, resolution 2, then height will be 100
*/
this.height = 1;
/**
* The number of samples of a multisample texture. This is always 1 for non-multisample textures.
* To enable multisample for a texture, set antialias to true
* @internal
* @ignore
*/
this.sampleCount = 1;
/** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */
this.mipLevelCount = 1;
/**
* Should we auto generate mipmaps for this texture? This will automatically generate mipmaps
* for this texture when uploading to the GPU. Mipmapped textures take up more memory, but
* can look better when scaled down.
*
* For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.
* If you do, make sure to call `updateMipmaps` after you update the texture.
*/
this.autoGenerateMipmaps = false;
/** the format that the texture data has */
this.format = "rgba8unorm";
/** how many dimensions does this texture have? currently v8 only supports 2d */
this.dimension = "2d";
/**
* Only really affects RenderTextures.
* Should we use antialiasing for this texture. It will look better, but may impact performance as a
* Blit operation will be required to resolve the texture.
*/
this.antialias = false;
/**
* Used by automatic texture Garbage Collection, stores last GC tick when it was bound
* @protected
*/
this._touched = 0;
/**
* Used by the batcher to build texture batches. faster to have the variable here!
* @protected
*/
this._batchTick = -1;
/**
* A temporary batch location for the texture batching. Here for performance reasons only!
* @protected
*/
this._textureBindLocation = -1;
options = { ..._TextureSource.defaultOptions, ...options };
this.label = options.label ?? "";
this.resource = options.resource;
this.autoGarbageCollect = options.autoGarbageCollect;
this._resolution = options.resolution;
if (options.width) {
this.pixelWidth = options.width * this._resolution;
} else {
this.pixelWidth = this.resource ? this.resourceWidth ?? 1 : 1;
}
if (options.height) {
this.pixelHeight = options.height * this._resolution;
} else {
this.pixelHeight = this.resource ? this.resourceHeight ?? 1 : 1;
}
this.width = this.pixelWidth / this._resolution;
this.height = this.pixelHeight / this._resolution;
this.format = options.format;
this.dimension = options.dimensions;
this.mipLevelCount = options.mipLevelCount;
this.autoGenerateMipmaps = options.autoGenerateMipmaps;
this.sampleCount = options.sampleCount;
this.antialias = options.antialias;
this.alphaMode = options.alphaMode;
this.style = new TextureStyle.TextureStyle(definedProps.definedProps(options));
this.destroyed = false;
this._refreshPOT();
}
/** returns itself */
get source() {
return this;
}
/** the style of the texture */
get style() {
return this._style;
}
set style(value) {
if (this.style === value)
return;
this._style?.off("change", this._onStyleChange, this);
this._style = value;
this._style?.on("change", this._onStyleChange, this);
this._onStyleChange();
}
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get addressMode() {
return this._style.addressMode;
}
set addressMode(value) {
this._style.addressMode = value;
}
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get repeatMode() {
return this._style.addressMode;
}
set repeatMode(value) {
this._style.addressMode = value;
}
/** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */
get magFilter() {
return this._style.magFilter;
}
set magFilter(value) {
this._style.magFilter = value;
}
/** Specifies the sampling behavior when the sample footprint is larger than one texel. */
get minFilter() {
return this._style.minFilter;
}
set minFilter(value) {
this._style.minFilter = value;
}
/** Specifies behavior for sampling between mipmap levels. */
get mipmapFilter() {
return this._style.mipmapFilter;
}
set mipmapFilter(value) {
this._style.mipmapFilter = value;
}
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMinClamp() {
return this._style.lodMinClamp;
}
set lodMinClamp(value) {
this._style.lodMinClamp = value;
}
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMaxClamp() {
return this._style.lodMaxClamp;
}
set lodMaxClamp(value) {
this._style.lodMaxClamp = value;
}
_onStyleChange() {
this.emit("styleChange", this);
}
/** call this if you have modified the texture outside of the constructor */
update() {
if (this.resource) {
const resolution = this._resolution;
const didResize = this.resize(this.resourceWidth / resolution, this.resourceHeight / resolution);
if (didResize)
return;
}
this.emit("update", this);
}
/** Destroys this texture source */
destroy() {
this.destroyed = true;
this.emit("destroy", this);
this.emit("change", this);
if (this._style) {
this._style.destroy();
this._style = null;
}
this.uploadMethodId = null;
this.resource = null;
this.removeAllListeners();
}
/**
* This will unload the Texture source from the GPU. This will free up the GPU memory
* As soon as it is required fore rendering, it will be re-uploaded.
*/
unload() {
this._resourceId = uid.uid("resource");
this.emit("change", this);
this.emit("unload", this);
}
/** the width of the resource. This is the REAL pure number, not accounting resolution */
get resourceWidth() {
const { resource } = this;
return resource.naturalWidth || resource.videoWidth || resource.displayWidth || resource.width;
}
/** the height of the resource. This is the REAL pure number, not accounting resolution */
get resourceHeight() {
const { resource } = this;
return resource.naturalHeight || resource.videoHeight || resource.displayHeight || resource.height;
}
/**
* the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture
* but will the size of the texture when rendered.
*
* changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel
* density will have increased)
*/
get resolution() {
return this._resolution;
}
set resolution(resolution) {
if (this._resolution === resolution)
return;
this._resolution = resolution;
this.width = this.pixelWidth / resolution;
this.height = this.pixelHeight / resolution;
}
/**
* Resize the texture, this is handy if you want to use the texture as a render texture
* @param width - the new width of the texture
* @param height - the new height of the texture
* @param resolution - the new resolution of the texture
* @returns - if the texture was resized
*/
resize(width, height, resolution) {
resolution = resolution || this._resolution;
width = width || this.width;
height = height || this.height;
const newPixelWidth = Math.round(width * resolution);
const newPixelHeight = Math.round(height * resolution);
this.width = newPixelWidth / resolution;
this.height = newPixelHeight / resolution;
this._resolution = resolution;
if (this.pixelWidth === newPixelWidth && this.pixelHeight === newPixelHeight) {
return false;
}
this._refreshPOT();
this.pixelWidth = newPixelWidth;
this.pixelHeight = newPixelHeight;
this.emit("resize", this);
this._resourceId = uid.uid("resource");
this.emit("change", this);
return true;
}
/**
* Lets the renderer know that this texture has been updated and its mipmaps should be re-generated.
* This is only important for RenderTexture instances, as standard Texture instances will have their
* mipmaps generated on upload. You should call this method after you make any change to the texture
*
* The reason for this is is can be quite expensive to update mipmaps for a texture. So by default,
* We want you, the developer to specify when this action should happen.
*
* Generally you don't want to have mipmaps generated on Render targets that are changed every frame,
*/
updateMipmaps() {
if (this.autoGenerateMipmaps && this.mipLevelCount > 1) {
this.emit("updateMipmaps", this);
}
}
set wrapMode(value) {
this._style.wrapMode = value;
}
get wrapMode() {
return this._style.wrapMode;
}
set scaleMode(value) {
this._style.scaleMode = value;
}
/** setting this will set magFilter,minFilter and mipmapFilter all at once! */
get scaleMode() {
return this._style.scaleMode;
}
/**
* Refresh check for isPowerOfTwo texture based on size
* @private
*/
_refreshPOT() {
this.isPowerOfTwo = pow2.isPow2(this.pixelWidth) && pow2.isPow2(this.pixelHeight);
}
static test(_resource) {
throw new Error("Unimplemented");
}
};
/** The default options used when creating a new TextureSource. override these to add your own defaults */
_TextureSource.defaultOptions = {
resolution: 1,
format: "bgra8unorm",
alphaMode: "premultiply-alpha-on-upload",
dimensions: "2d",
mipLevelCount: 1,
autoGenerateMipmaps: false,
sampleCount: 1,
antialias: false,
autoGarbageCollect: false
};
let TextureSource = _TextureSource;
exports.TextureSource = TextureSource;
//# sourceMappingURL=TextureSource.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,332 @@
import EventEmitter from 'eventemitter3';
import { isPow2 } from '../../../../../maths/misc/pow2.mjs';
import { definedProps } from '../../../../../scene/container/utils/definedProps.mjs';
import { uid } from '../../../../../utils/data/uid.mjs';
import { TextureStyle } from '../TextureStyle.mjs';
"use strict";
const _TextureSource = class _TextureSource extends EventEmitter {
/**
* @param options - options for creating a new TextureSource
*/
constructor(options = {}) {
super();
this.options = options;
/** unique id for this Texture source */
this.uid = uid("textureSource");
/**
* The resource type used by this TextureSource. This is used by the bind groups to determine
* how to handle this resource.
* @ignore
* @internal
*/
this._resourceType = "textureSource";
/**
* i unique resource id, used by the bind group systems.
* This can change if the texture is resized or its resource changes
*/
this._resourceId = uid("resource");
/**
* this is how the backends know how to upload this texture to the GPU
* It changes depending on the resource type. Classes that extend TextureSource
* should override this property.
* @ignore
* @internal
*/
this.uploadMethodId = "unknown";
// dimensions
this._resolution = 1;
/** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */
this.pixelWidth = 1;
/** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */
this.pixelHeight = 1;
/**
* the width of this texture source, accounting for resolution
* eg pixelWidth 200, resolution 2, then width will be 100
*/
this.width = 1;
/**
* the height of this texture source, accounting for resolution
* eg pixelHeight 200, resolution 2, then height will be 100
*/
this.height = 1;
/**
* The number of samples of a multisample texture. This is always 1 for non-multisample textures.
* To enable multisample for a texture, set antialias to true
* @internal
* @ignore
*/
this.sampleCount = 1;
/** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */
this.mipLevelCount = 1;
/**
* Should we auto generate mipmaps for this texture? This will automatically generate mipmaps
* for this texture when uploading to the GPU. Mipmapped textures take up more memory, but
* can look better when scaled down.
*
* For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.
* If you do, make sure to call `updateMipmaps` after you update the texture.
*/
this.autoGenerateMipmaps = false;
/** the format that the texture data has */
this.format = "rgba8unorm";
/** how many dimensions does this texture have? currently v8 only supports 2d */
this.dimension = "2d";
/**
* Only really affects RenderTextures.
* Should we use antialiasing for this texture. It will look better, but may impact performance as a
* Blit operation will be required to resolve the texture.
*/
this.antialias = false;
/**
* Used by automatic texture Garbage Collection, stores last GC tick when it was bound
* @protected
*/
this._touched = 0;
/**
* Used by the batcher to build texture batches. faster to have the variable here!
* @protected
*/
this._batchTick = -1;
/**
* A temporary batch location for the texture batching. Here for performance reasons only!
* @protected
*/
this._textureBindLocation = -1;
options = { ..._TextureSource.defaultOptions, ...options };
this.label = options.label ?? "";
this.resource = options.resource;
this.autoGarbageCollect = options.autoGarbageCollect;
this._resolution = options.resolution;
if (options.width) {
this.pixelWidth = options.width * this._resolution;
} else {
this.pixelWidth = this.resource ? this.resourceWidth ?? 1 : 1;
}
if (options.height) {
this.pixelHeight = options.height * this._resolution;
} else {
this.pixelHeight = this.resource ? this.resourceHeight ?? 1 : 1;
}
this.width = this.pixelWidth / this._resolution;
this.height = this.pixelHeight / this._resolution;
this.format = options.format;
this.dimension = options.dimensions;
this.mipLevelCount = options.mipLevelCount;
this.autoGenerateMipmaps = options.autoGenerateMipmaps;
this.sampleCount = options.sampleCount;
this.antialias = options.antialias;
this.alphaMode = options.alphaMode;
this.style = new TextureStyle(definedProps(options));
this.destroyed = false;
this._refreshPOT();
}
/** returns itself */
get source() {
return this;
}
/** the style of the texture */
get style() {
return this._style;
}
set style(value) {
if (this.style === value)
return;
this._style?.off("change", this._onStyleChange, this);
this._style = value;
this._style?.on("change", this._onStyleChange, this);
this._onStyleChange();
}
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get addressMode() {
return this._style.addressMode;
}
set addressMode(value) {
this._style.addressMode = value;
}
/** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */
get repeatMode() {
return this._style.addressMode;
}
set repeatMode(value) {
this._style.addressMode = value;
}
/** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */
get magFilter() {
return this._style.magFilter;
}
set magFilter(value) {
this._style.magFilter = value;
}
/** Specifies the sampling behavior when the sample footprint is larger than one texel. */
get minFilter() {
return this._style.minFilter;
}
set minFilter(value) {
this._style.minFilter = value;
}
/** Specifies behavior for sampling between mipmap levels. */
get mipmapFilter() {
return this._style.mipmapFilter;
}
set mipmapFilter(value) {
this._style.mipmapFilter = value;
}
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMinClamp() {
return this._style.lodMinClamp;
}
set lodMinClamp(value) {
this._style.lodMinClamp = value;
}
/** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */
get lodMaxClamp() {
return this._style.lodMaxClamp;
}
set lodMaxClamp(value) {
this._style.lodMaxClamp = value;
}
_onStyleChange() {
this.emit("styleChange", this);
}
/** call this if you have modified the texture outside of the constructor */
update() {
if (this.resource) {
const resolution = this._resolution;
const didResize = this.resize(this.resourceWidth / resolution, this.resourceHeight / resolution);
if (didResize)
return;
}
this.emit("update", this);
}
/** Destroys this texture source */
destroy() {
this.destroyed = true;
this.emit("destroy", this);
this.emit("change", this);
if (this._style) {
this._style.destroy();
this._style = null;
}
this.uploadMethodId = null;
this.resource = null;
this.removeAllListeners();
}
/**
* This will unload the Texture source from the GPU. This will free up the GPU memory
* As soon as it is required fore rendering, it will be re-uploaded.
*/
unload() {
this._resourceId = uid("resource");
this.emit("change", this);
this.emit("unload", this);
}
/** the width of the resource. This is the REAL pure number, not accounting resolution */
get resourceWidth() {
const { resource } = this;
return resource.naturalWidth || resource.videoWidth || resource.displayWidth || resource.width;
}
/** the height of the resource. This is the REAL pure number, not accounting resolution */
get resourceHeight() {
const { resource } = this;
return resource.naturalHeight || resource.videoHeight || resource.displayHeight || resource.height;
}
/**
* the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture
* but will the size of the texture when rendered.
*
* changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel
* density will have increased)
*/
get resolution() {
return this._resolution;
}
set resolution(resolution) {
if (this._resolution === resolution)
return;
this._resolution = resolution;
this.width = this.pixelWidth / resolution;
this.height = this.pixelHeight / resolution;
}
/**
* Resize the texture, this is handy if you want to use the texture as a render texture
* @param width - the new width of the texture
* @param height - the new height of the texture
* @param resolution - the new resolution of the texture
* @returns - if the texture was resized
*/
resize(width, height, resolution) {
resolution = resolution || this._resolution;
width = width || this.width;
height = height || this.height;
const newPixelWidth = Math.round(width * resolution);
const newPixelHeight = Math.round(height * resolution);
this.width = newPixelWidth / resolution;
this.height = newPixelHeight / resolution;
this._resolution = resolution;
if (this.pixelWidth === newPixelWidth && this.pixelHeight === newPixelHeight) {
return false;
}
this._refreshPOT();
this.pixelWidth = newPixelWidth;
this.pixelHeight = newPixelHeight;
this.emit("resize", this);
this._resourceId = uid("resource");
this.emit("change", this);
return true;
}
/**
* Lets the renderer know that this texture has been updated and its mipmaps should be re-generated.
* This is only important for RenderTexture instances, as standard Texture instances will have their
* mipmaps generated on upload. You should call this method after you make any change to the texture
*
* The reason for this is is can be quite expensive to update mipmaps for a texture. So by default,
* We want you, the developer to specify when this action should happen.
*
* Generally you don't want to have mipmaps generated on Render targets that are changed every frame,
*/
updateMipmaps() {
if (this.autoGenerateMipmaps && this.mipLevelCount > 1) {
this.emit("updateMipmaps", this);
}
}
set wrapMode(value) {
this._style.wrapMode = value;
}
get wrapMode() {
return this._style.wrapMode;
}
set scaleMode(value) {
this._style.scaleMode = value;
}
/** setting this will set magFilter,minFilter and mipmapFilter all at once! */
get scaleMode() {
return this._style.scaleMode;
}
/**
* Refresh check for isPowerOfTwo texture based on size
* @private
*/
_refreshPOT() {
this.isPowerOfTwo = isPow2(this.pixelWidth) && isPow2(this.pixelHeight);
}
static test(_resource) {
throw new Error("Unimplemented");
}
};
/** The default options used when creating a new TextureSource. override these to add your own defaults */
_TextureSource.defaultOptions = {
resolution: 1,
format: "bgra8unorm",
alphaMode: "premultiply-alpha-on-upload",
dimensions: "2d",
mipLevelCount: 1,
autoGenerateMipmaps: false,
sampleCount: 1,
antialias: false,
autoGarbageCollect: false
};
let TextureSource = _TextureSource;
export { TextureSource };
//# sourceMappingURL=TextureSource.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,149 @@
import { TextureSource } from './TextureSource';
import type { ExtensionMetadata } from '../../../../../extensions/Extensions';
import type { Dict } from '../../../../../utils/types';
import type { ALPHA_MODES } from '../const';
import type { TextureSourceOptions } from './TextureSource';
type VideoResource = HTMLVideoElement;
/**
* Options for video sources.
* @memberof rendering
*/
export interface VideoSourceOptions extends TextureSourceOptions<VideoResource> {
/** If true, the video will start loading immediately. */
autoLoad?: boolean;
/** If true, the video will start playing as soon as it is loaded. */
autoPlay?: boolean;
/** The number of times a second to update the texture from the video. Leave at 0 to update at every render. */
updateFPS?: number;
/** If true, the video will be loaded with the `crossorigin` attribute. */
crossorigin?: boolean | string;
/** If true, the video will loop when it ends. */
loop?: boolean;
/** If true, the video will be muted. */
muted?: boolean;
/** If true, the video will play inline. */
playsinline?: boolean;
/** If true, the video will be preloaded. */
preload?: boolean;
/** The time in milliseconds to wait for the video to preload before timing out. */
preloadTimeoutMs?: number;
/** The alpha mode of the video. */
alphaMode?: ALPHA_MODES;
}
export interface VideoResourceOptionsElement {
src: string;
mime: string;
}
/**
* A source for video-based textures.
* @memberof rendering
*/
export declare class VideoSource extends TextureSource<VideoResource> {
static extension: ExtensionMetadata;
/** The default options for video sources. */
static defaultOptions: VideoSourceOptions;
/** Whether or not the video is ready to play. */
isReady: boolean;
/** The upload method for this texture. */
uploadMethodId: string;
/**
* When set to true will automatically play videos used by this texture once
* they are loaded. If false, it will not modify the playing state.
* @default true
*/
protected autoPlay: boolean;
/**
* `true` to use Ticker.shared to auto update the base texture.
* @default true
*/
private _autoUpdate;
/**
* `true` if the instance is currently connected to Ticker.shared to auto update the base texture.
* @default false
*/
private _isConnectedToTicker;
/**
* Promise when loading.
* @default null
*/
private _load;
private _msToNextUpdate;
private _preloadTimeout;
/** Callback when completed with load. */
private _resolve;
private _reject;
private _updateFPS;
private _videoFrameRequestCallbackHandle;
constructor(options: VideoSourceOptions);
/** Update the video frame if the source is not destroyed and meets certain conditions. */
protected updateFrame(): void;
/** Callback to update the video frame and potentially request the next frame update. */
private _videoFrameRequestCallback;
/**
* Checks if the resource has valid dimensions.
* @returns {boolean} True if width and height are set, otherwise false.
*/
get isValid(): boolean;
/**
* Start preloading the video resource.
* @returns {Promise<this>} Handle the validate event
*/
load(): Promise<this>;
/**
* Handle video error events.
* @param event - The error event
*/
private _onError;
/**
* Checks if the underlying source is playing.
* @returns True if playing.
*/
private _isSourcePlaying;
/**
* Checks if the underlying source is ready for playing.
* @returns True if ready.
*/
private _isSourceReady;
/** Runs the update loop when the video is ready to play. */
private _onPlayStart;
/** Stops the update loop when a pause event is triggered. */
private _onPlayStop;
/** Handles behavior when the video completes seeking to the current playback position. */
private _onSeeked;
private _onCanPlay;
private _onCanPlayThrough;
/** Fired when the video is loaded and ready to play. */
private _mediaReady;
/** Cleans up resources and event listeners associated with this texture. */
destroy(): void;
/** Should the base texture automatically update itself, set to true by default. */
get autoUpdate(): boolean;
set autoUpdate(value: boolean);
/**
* How many times a second to update the texture from the video.
* Leave at 0 to update at every render.
* A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient.
*/
get updateFPS(): number;
set updateFPS(value: number);
/**
* Configures the updating mechanism based on the current state and settings.
*
* This method decides between using the browser's native video frame callback or a custom ticker
* for updating the video frame. It ensures optimal performance and responsiveness
* based on the video's state, playback status, and the desired frames-per-second setting.
*
* - If `_autoUpdate` is enabled and the video source is playing:
* - It will prefer the native video frame callback if available and no specific FPS is set.
* - Otherwise, it will use a custom ticker for manual updates.
* - If `_autoUpdate` is disabled or the video isn't playing, any active update mechanisms are halted.
*/
private _configureAutoUpdate;
/**
* Map of video MIME types that can't be directly derived from file extensions.
* @readonly
*/
static MIME_TYPES: Dict<string>;
static test(resource: any): resource is VideoResource;
}
export {};

View File

@@ -0,0 +1,327 @@
'use strict';
var Extensions = require('../../../../../extensions/Extensions.js');
var Ticker = require('../../../../../ticker/Ticker.js');
var detectVideoAlphaMode = require('../../../../../utils/browser/detectVideoAlphaMode.js');
var TextureSource = require('./TextureSource.js');
"use strict";
const _VideoSource = class _VideoSource extends TextureSource.TextureSource {
constructor(options) {
super(options);
// Public
/** Whether or not the video is ready to play. */
this.isReady = false;
/** The upload method for this texture. */
this.uploadMethodId = "video";
options = {
..._VideoSource.defaultOptions,
...options
};
this._autoUpdate = true;
this._isConnectedToTicker = false;
this._updateFPS = options.updateFPS || 0;
this._msToNextUpdate = 0;
this.autoPlay = options.autoPlay !== false;
this.alphaMode = options.alphaMode ?? "premultiply-alpha-on-upload";
this._videoFrameRequestCallback = this._videoFrameRequestCallback.bind(this);
this._videoFrameRequestCallbackHandle = null;
this._load = null;
this._resolve = null;
this._reject = null;
this._onCanPlay = this._onCanPlay.bind(this);
this._onCanPlayThrough = this._onCanPlayThrough.bind(this);
this._onError = this._onError.bind(this);
this._onPlayStart = this._onPlayStart.bind(this);
this._onPlayStop = this._onPlayStop.bind(this);
this._onSeeked = this._onSeeked.bind(this);
if (options.autoLoad !== false) {
void this.load();
}
}
/** Update the video frame if the source is not destroyed and meets certain conditions. */
updateFrame() {
if (this.destroyed) {
return;
}
if (this._updateFPS) {
const elapsedMS = Ticker.Ticker.shared.elapsedMS * this.resource.playbackRate;
this._msToNextUpdate = Math.floor(this._msToNextUpdate - elapsedMS);
}
if (!this._updateFPS || this._msToNextUpdate <= 0) {
this._msToNextUpdate = this._updateFPS ? Math.floor(1e3 / this._updateFPS) : 0;
}
if (this.isValid) {
this.update();
}
}
/** Callback to update the video frame and potentially request the next frame update. */
_videoFrameRequestCallback() {
this.updateFrame();
if (this.destroyed) {
this._videoFrameRequestCallbackHandle = null;
} else {
this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
this._videoFrameRequestCallback
);
}
}
/**
* Checks if the resource has valid dimensions.
* @returns {boolean} True if width and height are set, otherwise false.
*/
get isValid() {
return !!this.resource.videoWidth && !!this.resource.videoHeight;
}
/**
* Start preloading the video resource.
* @returns {Promise<this>} Handle the validate event
*/
async load() {
if (this._load) {
return this._load;
}
const source = this.resource;
const options = this.options;
if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) {
source.complete = true;
}
source.addEventListener("play", this._onPlayStart);
source.addEventListener("pause", this._onPlayStop);
source.addEventListener("seeked", this._onSeeked);
if (!this._isSourceReady()) {
if (!options.preload) {
source.addEventListener("canplay", this._onCanPlay);
}
source.addEventListener("canplaythrough", this._onCanPlayThrough);
source.addEventListener("error", this._onError, true);
} else {
this._mediaReady();
}
this.alphaMode = await detectVideoAlphaMode.detectVideoAlphaMode();
this._load = new Promise((resolve, reject) => {
if (this.isValid) {
resolve(this);
} else {
this._resolve = resolve;
this._reject = reject;
if (options.preloadTimeoutMs !== void 0) {
this._preloadTimeout = setTimeout(() => {
this._onError(new ErrorEvent(`Preload exceeded timeout of ${options.preloadTimeoutMs}ms`));
});
}
source.load();
}
});
return this._load;
}
/**
* Handle video error events.
* @param event - The error event
*/
_onError(event) {
this.resource.removeEventListener("error", this._onError, true);
this.emit("error", event);
if (this._reject) {
this._reject(event);
this._reject = null;
this._resolve = null;
}
}
/**
* Checks if the underlying source is playing.
* @returns True if playing.
*/
_isSourcePlaying() {
const source = this.resource;
return !source.paused && !source.ended;
}
/**
* Checks if the underlying source is ready for playing.
* @returns True if ready.
*/
_isSourceReady() {
const source = this.resource;
return source.readyState > 2;
}
/** Runs the update loop when the video is ready to play. */
_onPlayStart() {
if (!this.isValid) {
this._mediaReady();
}
this._configureAutoUpdate();
}
/** Stops the update loop when a pause event is triggered. */
_onPlayStop() {
this._configureAutoUpdate();
}
/** Handles behavior when the video completes seeking to the current playback position. */
_onSeeked() {
if (this._autoUpdate && !this._isSourcePlaying()) {
this._msToNextUpdate = 0;
this.updateFrame();
this._msToNextUpdate = 0;
}
}
_onCanPlay() {
const source = this.resource;
source.removeEventListener("canplay", this._onCanPlay);
this._mediaReady();
}
_onCanPlayThrough() {
const source = this.resource;
source.removeEventListener("canplaythrough", this._onCanPlay);
if (this._preloadTimeout) {
clearTimeout(this._preloadTimeout);
this._preloadTimeout = void 0;
}
this._mediaReady();
}
/** Fired when the video is loaded and ready to play. */
_mediaReady() {
const source = this.resource;
if (this.isValid) {
this.isReady = true;
this.resize(source.videoWidth, source.videoHeight);
}
this._msToNextUpdate = 0;
this.updateFrame();
this._msToNextUpdate = 0;
if (this._resolve) {
this._resolve(this);
this._resolve = null;
this._reject = null;
}
if (this._isSourcePlaying()) {
this._onPlayStart();
} else if (this.autoPlay) {
void this.resource.play();
}
}
/** Cleans up resources and event listeners associated with this texture. */
destroy() {
this._configureAutoUpdate();
const source = this.resource;
if (source) {
source.removeEventListener("play", this._onPlayStart);
source.removeEventListener("pause", this._onPlayStop);
source.removeEventListener("seeked", this._onSeeked);
source.removeEventListener("canplay", this._onCanPlay);
source.removeEventListener("canplaythrough", this._onCanPlayThrough);
source.removeEventListener("error", this._onError, true);
source.pause();
source.src = "";
source.load();
}
super.destroy();
}
/** Should the base texture automatically update itself, set to true by default. */
get autoUpdate() {
return this._autoUpdate;
}
set autoUpdate(value) {
if (value !== this._autoUpdate) {
this._autoUpdate = value;
this._configureAutoUpdate();
}
}
/**
* How many times a second to update the texture from the video.
* Leave at 0 to update at every render.
* A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient.
*/
get updateFPS() {
return this._updateFPS;
}
set updateFPS(value) {
if (value !== this._updateFPS) {
this._updateFPS = value;
this._configureAutoUpdate();
}
}
/**
* Configures the updating mechanism based on the current state and settings.
*
* This method decides between using the browser's native video frame callback or a custom ticker
* for updating the video frame. It ensures optimal performance and responsiveness
* based on the video's state, playback status, and the desired frames-per-second setting.
*
* - If `_autoUpdate` is enabled and the video source is playing:
* - It will prefer the native video frame callback if available and no specific FPS is set.
* - Otherwise, it will use a custom ticker for manual updates.
* - If `_autoUpdate` is disabled or the video isn't playing, any active update mechanisms are halted.
*/
_configureAutoUpdate() {
if (this._autoUpdate && this._isSourcePlaying()) {
if (!this._updateFPS && this.resource.requestVideoFrameCallback) {
if (this._isConnectedToTicker) {
Ticker.Ticker.shared.remove(this.updateFrame, this);
this._isConnectedToTicker = false;
this._msToNextUpdate = 0;
}
if (this._videoFrameRequestCallbackHandle === null) {
this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
this._videoFrameRequestCallback
);
}
} else {
if (this._videoFrameRequestCallbackHandle !== null) {
this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
this._videoFrameRequestCallbackHandle = null;
}
if (!this._isConnectedToTicker) {
Ticker.Ticker.shared.add(this.updateFrame, this);
this._isConnectedToTicker = true;
this._msToNextUpdate = 0;
}
}
} else {
if (this._videoFrameRequestCallbackHandle !== null) {
this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
this._videoFrameRequestCallbackHandle = null;
}
if (this._isConnectedToTicker) {
Ticker.Ticker.shared.remove(this.updateFrame, this);
this._isConnectedToTicker = false;
this._msToNextUpdate = 0;
}
}
}
static test(resource) {
return globalThis.HTMLVideoElement && resource instanceof HTMLVideoElement;
}
};
_VideoSource.extension = Extensions.ExtensionType.TextureSource;
/** The default options for video sources. */
_VideoSource.defaultOptions = {
...TextureSource.TextureSource.defaultOptions,
/** If true, the video will start loading immediately. */
autoLoad: true,
/** If true, the video will start playing as soon as it is loaded. */
autoPlay: true,
/** The number of times a second to update the texture from the video. Leave at 0 to update at every render. */
updateFPS: 0,
/** If true, the video will be loaded with the `crossorigin` attribute. */
crossorigin: true,
/** If true, the video will loop when it ends. */
loop: false,
/** If true, the video will be muted. */
muted: true,
/** If true, the video will play inline. */
playsinline: true,
/** If true, the video will be preloaded. */
preload: false
};
/**
* Map of video MIME types that can't be directly derived from file extensions.
* @readonly
*/
_VideoSource.MIME_TYPES = {
ogv: "video/ogg",
mov: "video/quicktime",
m4v: "video/mp4"
};
let VideoSource = _VideoSource;
exports.VideoSource = VideoSource;
//# sourceMappingURL=VideoSource.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,325 @@
import { ExtensionType } from '../../../../../extensions/Extensions.mjs';
import { Ticker } from '../../../../../ticker/Ticker.mjs';
import { detectVideoAlphaMode } from '../../../../../utils/browser/detectVideoAlphaMode.mjs';
import { TextureSource } from './TextureSource.mjs';
"use strict";
const _VideoSource = class _VideoSource extends TextureSource {
constructor(options) {
super(options);
// Public
/** Whether or not the video is ready to play. */
this.isReady = false;
/** The upload method for this texture. */
this.uploadMethodId = "video";
options = {
..._VideoSource.defaultOptions,
...options
};
this._autoUpdate = true;
this._isConnectedToTicker = false;
this._updateFPS = options.updateFPS || 0;
this._msToNextUpdate = 0;
this.autoPlay = options.autoPlay !== false;
this.alphaMode = options.alphaMode ?? "premultiply-alpha-on-upload";
this._videoFrameRequestCallback = this._videoFrameRequestCallback.bind(this);
this._videoFrameRequestCallbackHandle = null;
this._load = null;
this._resolve = null;
this._reject = null;
this._onCanPlay = this._onCanPlay.bind(this);
this._onCanPlayThrough = this._onCanPlayThrough.bind(this);
this._onError = this._onError.bind(this);
this._onPlayStart = this._onPlayStart.bind(this);
this._onPlayStop = this._onPlayStop.bind(this);
this._onSeeked = this._onSeeked.bind(this);
if (options.autoLoad !== false) {
void this.load();
}
}
/** Update the video frame if the source is not destroyed and meets certain conditions. */
updateFrame() {
if (this.destroyed) {
return;
}
if (this._updateFPS) {
const elapsedMS = Ticker.shared.elapsedMS * this.resource.playbackRate;
this._msToNextUpdate = Math.floor(this._msToNextUpdate - elapsedMS);
}
if (!this._updateFPS || this._msToNextUpdate <= 0) {
this._msToNextUpdate = this._updateFPS ? Math.floor(1e3 / this._updateFPS) : 0;
}
if (this.isValid) {
this.update();
}
}
/** Callback to update the video frame and potentially request the next frame update. */
_videoFrameRequestCallback() {
this.updateFrame();
if (this.destroyed) {
this._videoFrameRequestCallbackHandle = null;
} else {
this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
this._videoFrameRequestCallback
);
}
}
/**
* Checks if the resource has valid dimensions.
* @returns {boolean} True if width and height are set, otherwise false.
*/
get isValid() {
return !!this.resource.videoWidth && !!this.resource.videoHeight;
}
/**
* Start preloading the video resource.
* @returns {Promise<this>} Handle the validate event
*/
async load() {
if (this._load) {
return this._load;
}
const source = this.resource;
const options = this.options;
if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) {
source.complete = true;
}
source.addEventListener("play", this._onPlayStart);
source.addEventListener("pause", this._onPlayStop);
source.addEventListener("seeked", this._onSeeked);
if (!this._isSourceReady()) {
if (!options.preload) {
source.addEventListener("canplay", this._onCanPlay);
}
source.addEventListener("canplaythrough", this._onCanPlayThrough);
source.addEventListener("error", this._onError, true);
} else {
this._mediaReady();
}
this.alphaMode = await detectVideoAlphaMode();
this._load = new Promise((resolve, reject) => {
if (this.isValid) {
resolve(this);
} else {
this._resolve = resolve;
this._reject = reject;
if (options.preloadTimeoutMs !== void 0) {
this._preloadTimeout = setTimeout(() => {
this._onError(new ErrorEvent(`Preload exceeded timeout of ${options.preloadTimeoutMs}ms`));
});
}
source.load();
}
});
return this._load;
}
/**
* Handle video error events.
* @param event - The error event
*/
_onError(event) {
this.resource.removeEventListener("error", this._onError, true);
this.emit("error", event);
if (this._reject) {
this._reject(event);
this._reject = null;
this._resolve = null;
}
}
/**
* Checks if the underlying source is playing.
* @returns True if playing.
*/
_isSourcePlaying() {
const source = this.resource;
return !source.paused && !source.ended;
}
/**
* Checks if the underlying source is ready for playing.
* @returns True if ready.
*/
_isSourceReady() {
const source = this.resource;
return source.readyState > 2;
}
/** Runs the update loop when the video is ready to play. */
_onPlayStart() {
if (!this.isValid) {
this._mediaReady();
}
this._configureAutoUpdate();
}
/** Stops the update loop when a pause event is triggered. */
_onPlayStop() {
this._configureAutoUpdate();
}
/** Handles behavior when the video completes seeking to the current playback position. */
_onSeeked() {
if (this._autoUpdate && !this._isSourcePlaying()) {
this._msToNextUpdate = 0;
this.updateFrame();
this._msToNextUpdate = 0;
}
}
_onCanPlay() {
const source = this.resource;
source.removeEventListener("canplay", this._onCanPlay);
this._mediaReady();
}
_onCanPlayThrough() {
const source = this.resource;
source.removeEventListener("canplaythrough", this._onCanPlay);
if (this._preloadTimeout) {
clearTimeout(this._preloadTimeout);
this._preloadTimeout = void 0;
}
this._mediaReady();
}
/** Fired when the video is loaded and ready to play. */
_mediaReady() {
const source = this.resource;
if (this.isValid) {
this.isReady = true;
this.resize(source.videoWidth, source.videoHeight);
}
this._msToNextUpdate = 0;
this.updateFrame();
this._msToNextUpdate = 0;
if (this._resolve) {
this._resolve(this);
this._resolve = null;
this._reject = null;
}
if (this._isSourcePlaying()) {
this._onPlayStart();
} else if (this.autoPlay) {
void this.resource.play();
}
}
/** Cleans up resources and event listeners associated with this texture. */
destroy() {
this._configureAutoUpdate();
const source = this.resource;
if (source) {
source.removeEventListener("play", this._onPlayStart);
source.removeEventListener("pause", this._onPlayStop);
source.removeEventListener("seeked", this._onSeeked);
source.removeEventListener("canplay", this._onCanPlay);
source.removeEventListener("canplaythrough", this._onCanPlayThrough);
source.removeEventListener("error", this._onError, true);
source.pause();
source.src = "";
source.load();
}
super.destroy();
}
/** Should the base texture automatically update itself, set to true by default. */
get autoUpdate() {
return this._autoUpdate;
}
set autoUpdate(value) {
if (value !== this._autoUpdate) {
this._autoUpdate = value;
this._configureAutoUpdate();
}
}
/**
* How many times a second to update the texture from the video.
* Leave at 0 to update at every render.
* A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient.
*/
get updateFPS() {
return this._updateFPS;
}
set updateFPS(value) {
if (value !== this._updateFPS) {
this._updateFPS = value;
this._configureAutoUpdate();
}
}
/**
* Configures the updating mechanism based on the current state and settings.
*
* This method decides between using the browser's native video frame callback or a custom ticker
* for updating the video frame. It ensures optimal performance and responsiveness
* based on the video's state, playback status, and the desired frames-per-second setting.
*
* - If `_autoUpdate` is enabled and the video source is playing:
* - It will prefer the native video frame callback if available and no specific FPS is set.
* - Otherwise, it will use a custom ticker for manual updates.
* - If `_autoUpdate` is disabled or the video isn't playing, any active update mechanisms are halted.
*/
_configureAutoUpdate() {
if (this._autoUpdate && this._isSourcePlaying()) {
if (!this._updateFPS && this.resource.requestVideoFrameCallback) {
if (this._isConnectedToTicker) {
Ticker.shared.remove(this.updateFrame, this);
this._isConnectedToTicker = false;
this._msToNextUpdate = 0;
}
if (this._videoFrameRequestCallbackHandle === null) {
this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback(
this._videoFrameRequestCallback
);
}
} else {
if (this._videoFrameRequestCallbackHandle !== null) {
this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
this._videoFrameRequestCallbackHandle = null;
}
if (!this._isConnectedToTicker) {
Ticker.shared.add(this.updateFrame, this);
this._isConnectedToTicker = true;
this._msToNextUpdate = 0;
}
}
} else {
if (this._videoFrameRequestCallbackHandle !== null) {
this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle);
this._videoFrameRequestCallbackHandle = null;
}
if (this._isConnectedToTicker) {
Ticker.shared.remove(this.updateFrame, this);
this._isConnectedToTicker = false;
this._msToNextUpdate = 0;
}
}
}
static test(resource) {
return globalThis.HTMLVideoElement && resource instanceof HTMLVideoElement;
}
};
_VideoSource.extension = ExtensionType.TextureSource;
/** The default options for video sources. */
_VideoSource.defaultOptions = {
...TextureSource.defaultOptions,
/** If true, the video will start loading immediately. */
autoLoad: true,
/** If true, the video will start playing as soon as it is loaded. */
autoPlay: true,
/** The number of times a second to update the texture from the video. Leave at 0 to update at every render. */
updateFPS: 0,
/** If true, the video will be loaded with the `crossorigin` attribute. */
crossorigin: true,
/** If true, the video will loop when it ends. */
loop: false,
/** If true, the video will be muted. */
muted: true,
/** If true, the video will play inline. */
playsinline: true,
/** If true, the video will be preloaded. */
preload: false
};
/**
* Map of video MIME types that can't be directly derived from file extensions.
* @readonly
*/
_VideoSource.MIME_TYPES = {
ogv: "video/ogg",
mov: "video/quicktime",
m4v: "video/mp4"
};
let VideoSource = _VideoSource;
export { VideoSource };
//# sourceMappingURL=VideoSource.mjs.map

File diff suppressed because one or more lines are too long