@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
85 lines (74 loc) • 2.96 kB
text/typescript
/**
* Can create a texture from a snapshot of a COP/video or COP/webcam
*
*/
import {Constructor} from '../../../types/GlobalTypes';
import {TypedCopNode} from './_Base';
import {CanvasTexture, VideoTexture} from 'three';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {TextureParamsController} from './utils/TextureParamsController';
import {InputCloneMode} from '../../poly/InputCloneMode';
import {Texture} from 'three';
import {CopType} from '../../poly/registers/nodes/types/Cop';
import {BaseNodeType} from '../_Base';
import {isHTMLVideoElementLoaded} from '../../../core/DomUtils';
export function SnapshotCopParamConfig<TBase extends Constructor>(Base: TBase) {
return class Mixin extends Base {
/** @param capture */
capture = ParamConfig.BUTTON(null, {
callback: (node: BaseNodeType) => {
SnapshotCopNode.PARAM_CALLBACK_snapshot(node as SnapshotCopNode);
},
});
};
}
class SnapshotCopParamsConfig extends SnapshotCopParamConfig(NodeParamsConfig) {}
const ParamsConfig = new SnapshotCopParamsConfig();
export class SnapshotCopNode extends TypedCopNode<SnapshotCopParamsConfig> {
override paramsConfig = ParamsConfig;
static override type() {
return CopType.SNAPSHOT;
}
override initializeNode() {
this.io.inputs.setCount(1);
this.io.inputs.initInputsClonedState(InputCloneMode.NEVER);
}
override async cook(inputTextures: Texture[]) {
const inputTexture = inputTextures[0];
if (inputTexture && inputTexture instanceof VideoTexture) {
const texture = this._videoSnapshotCanvas(inputTexture);
if (texture) {
TextureParamsController.copyTextureAttributes(texture, inputTexture);
this.setTexture(texture);
return;
}
} else {
this.states.error.set('input texture is not a video');
}
this.cookController.endCook();
}
static PARAM_CALLBACK_snapshot(node: SnapshotCopNode) {
node.paramCallbackSnapshot();
}
private paramCallbackSnapshot() {
this.setDirty();
}
private _videoSnapshotCanvas(inputTexture: VideoTexture) {
const videoElement = inputTexture.image as HTMLVideoElement;
if (!isHTMLVideoElementLoaded(videoElement)) {
this.states.error.set('video not loaded');
return;
}
this._canvas = this._canvas || document.createElement('canvas');
this._canvasTexture = this._canvasTexture || new CanvasTexture(this._canvas);
// videoWidth and videoHeight are more robust than .width and .height (see https://discourse.threejs.org/t/how-to-get-camera-video-texture-size-or-resolution/2879)
this._canvas.width = inputTexture.image.videoWidth;
this._canvas.height = inputTexture.image.videoHeight;
const canvasCtx = this._canvas.getContext('2d')!;
canvasCtx.drawImage(inputTexture.image, 0, 0, this._canvas.width, this._canvas.height);
this._canvasTexture.needsUpdate = true;
return this._canvasTexture;
}
private _canvas: HTMLCanvasElement | undefined;
private _canvasTexture: CanvasTexture | undefined;
}