UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

255 lines (211 loc) • 6.12 kB
import { AddEquation, CustomBlending, Mesh, OneFactor, OneMinusSrcAlphaFactor, RedFormat, RGBAFormat, ShaderMaterial } from "three"; import Vector2 from "../../../core/geom/Vector2.js"; import { assert } from "../../../core/assert.js"; import { blendingType2three } from "../texture/sampler/blendingType2Three.js"; import { ScreenSpaceQuadShader } from "../shaders/ScreenSpaceQuadShader.js"; import { BlendingType } from "../texture/sampler/BlendingType.js"; import { FULL_SCREEN_TRIANGLE_GEOMETRY } from "../geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js"; import Signal from "../../../core/events/signal/Signal.js"; /** * * @param {Material} material * @param {BlendingType} blending */ function assignMaterialBlending(material, blending) { if (blending === BlendingType.MultiplyAdd) { material.blending = CustomBlending; material.blendEquation = AddEquation; material.blendSrc = OneFactor; material.blendDst = OneMinusSrcAlphaFactor; } else { const threeBlending = blendingType2three(blending); material.blending = threeBlending; } } export class CompositLayer { /** * * @param {THREE.WebGLRenderTarget} renderTarget * @param {BlendingType} blending * @param {number} renderTargetScale */ constructor({ renderTarget, blending, renderTargetScale = 1 }) { /** * Human-readable name, useful for debugging * @type {string} */ this.name = ""; /** * * @type {BlendingType} */ this.__blending = blending; /** * * @type {WebGLRenderTarget} */ this.renderTarget = renderTarget; /** * * @type {number} */ this.renderTargetScale = renderTargetScale; this.object = new Mesh(FULL_SCREEN_TRIANGLE_GEOMETRY, this.buildMaterial()); /** * Which compositing stage this layer belongs to, stages are rendered independently * @type {number} */ this.stage = 0; this.size = new Vector2(); /** * * @type {boolean} */ this.__enabled = true; /** * Layers are composited in order from lowest to highest * @type {number} */ this.__order = 0; this.on = { preRender: new Signal() }; } get blending() { return this.__blending; } set blending(v) { if (v === this.__blending) { return; } this.__blending = v; assignMaterialBlending(this.object.material, v); } /** * * @param {number} v */ set order(v) { this.__order = v; this.object.renderOrder = v; } /** * * @return {number} */ get order() { return this.__order; } /** * * @param {number} v */ set enabled(v) { if (!this.__enabled && v) { this.enable(); } else if (this.__enabled && !v) { this.disable(); } } /** * * @returns {boolean} */ get enabled() { return this.__enabled; } enable() { if (!this.__enabled) { this.__enabled = true; this.object.visible = true; } } disable() { if (this.__enabled) { this.__enabled = false; this.object.visible = false; } } /** * * @param {number} x * @param {number} y */ setSize(x, y) { this.size.set(x, y); this.updateRenderTargetSize(); } updateRenderTargetSize() { if (this.renderTarget !== null) { const scale = this.renderTargetScale; const size = this.size; const width = size.x * scale; const height = size.y * scale; this.renderTarget.setSize(width, height); } } /** * * @param {number} scale */ setRenderTargetScale(scale) { assert.isNumber(scale, 'v'); assert.isFiniteNumber(scale, 'v'); this.renderTargetScale = scale; this.updateRenderTargetSize(); } /** * Override screen-space material of the layer. This allows you to render directly into the output buffer, bypassing the need to create an intermediate buffer * NOTE: Please make sure you know what you're doing if you use this method * @param {Material} material */ setCustomMaterial(material) { assignMaterialBlending(material, this.blending); // override material this.object.material = material; } buildMaterial() { const uniforms = { tTexture: { type: 't', value: this.renderTarget.texture } }; // determine swizzle for the material let swizzle; const texture_format = this.renderTarget.texture.format; if (texture_format === RedFormat) { swizzle = ['r', 'r', 'r', 1]; } else if (texture_format === RGBAFormat) { swizzle = ['r', 'g', 'b', 'a']; } else { throw new Error('Unsupported format'); } const material = new ShaderMaterial({ uniforms, vertexShader: ScreenSpaceQuadShader.vertexShader(), fragmentShader: ScreenSpaceQuadShader.fragmentShader(swizzle), lights: false, fog: false, depthTest: false, depthWrite: false, transparent: true, vertexColors: false }); assignMaterialBlending(material, this.blending); return material; } }