UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

85 lines (74 loc) 2.96 kB
/** * 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; }