@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
255 lines (211 loc) • 6.12 kB
JavaScript
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;
}
}