@google/model-viewer-effects
Version:
Easily add and combine post-processing effects with <model-viewer>!
1,320 lines (1,316 loc) • 488 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) :
typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ModelViewerEffects = {}, global.three));
})(this, (function (exports, three) { 'use strict';
/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/ const t$1 = globalThis, e$2 = t$1.ShadowRoot && (void 0 === t$1.ShadyCSS || t$1.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, s$1 = Symbol(), o$3 = new WeakMap;
let n$3 = class n {
get styleSheet() {
let t = this.o;
const s = this.t;
if (e$2 && void 0 === t) {
const e = void 0 !== s && 1 === s.length;
e && (t = o$3.get(s)), void 0 === t && ((this.o = t = new CSSStyleSheet).replaceSync(this.cssText), e && o$3.set(s, t));
}
return t;
}
toString() {
return this.cssText;
}
constructor(t, e, o){
if (this._$cssResult$ = true, o !== s$1) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
this.cssText = t, this.t = e;
}
};
const r$4 = (t)=>new n$3("string" == typeof t ? t : t + "", void 0, s$1), S$1 = (s, o)=>{
if (e$2) s.adoptedStyleSheets = o.map((t)=>t instanceof CSSStyleSheet ? t : t.styleSheet);
else for (const e of o){
const o = document.createElement("style"), n = t$1.litNonce;
void 0 !== n && o.setAttribute("nonce", n), o.textContent = e.cssText, s.appendChild(o);
}
}, c$2 = e$2 ? (t)=>t : (t)=>t instanceof CSSStyleSheet ? ((t)=>{
let e = "";
for (const s of t.cssRules)e += s.cssText;
return r$4(e);
})(t) : t;
var _Symbol, _a$1, _a1;
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/ const { is: i$2, defineProperty: e$1, getOwnPropertyDescriptor: r$3, getOwnPropertyNames: h$1, getOwnPropertySymbols: o$2, getPrototypeOf: n$2 } = Object, a$1 = globalThis, c$1 = a$1.trustedTypes, l$1 = c$1 ? c$1.emptyScript : "", p$1 = a$1.reactiveElementPolyfillSupport, d$1 = (t, s)=>t, u$1 = {
toAttribute (t, s) {
switch(s){
case Boolean:
t = t ? l$1 : null;
break;
case Object:
case Array:
t = null == t ? t : JSON.stringify(t);
}
return t;
},
fromAttribute (t, s) {
let i = t;
switch(s){
case Boolean:
i = null !== t;
break;
case Number:
i = null === t ? null : Number(t);
break;
case Object:
case Array:
try {
i = JSON.parse(t);
} catch (t) {
i = null;
}
}
return i;
}
}, f$1 = (t, s)=>!i$2(t, s), y = {
attribute: true,
type: String,
converter: u$1,
reflect: false,
hasChanged: f$1
};
(_Symbol = Symbol).metadata ?? (_Symbol.metadata = Symbol("metadata")), (_a$1 = a$1).litPropertyMetadata ?? (_a$1.litPropertyMetadata = new WeakMap);
class b extends HTMLElement {
static addInitializer(t) {
this._$Ei(), (this.l ?? (this.l = [])).push(t);
}
static get observedAttributes() {
return this.finalize(), this._$Eh && [
...this._$Eh.keys()
];
}
static createProperty(t, s = y) {
if (s.state && (s.attribute = false), this._$Ei(), this.elementProperties.set(t, s), !s.noAccessor) {
const i = Symbol(), r = this.getPropertyDescriptor(t, i, s);
void 0 !== r && e$1(this.prototype, t, r);
}
}
static getPropertyDescriptor(t, s, i) {
const { get: e, set: h } = r$3(this.prototype, t) ?? {
get () {
return this[s];
},
set (t) {
this[s] = t;
}
};
return {
get () {
return e?.call(this);
},
set (s) {
const r = e?.call(this);
h.call(this, s), this.requestUpdate(t, r, i);
},
configurable: true,
enumerable: true
};
}
static getPropertyOptions(t) {
return this.elementProperties.get(t) ?? y;
}
static _$Ei() {
if (this.hasOwnProperty(d$1("elementProperties"))) return;
const t = n$2(this);
t.finalize(), void 0 !== t.l && (this.l = [
...t.l
]), this.elementProperties = new Map(t.elementProperties);
}
static finalize() {
if (this.hasOwnProperty(d$1("finalized"))) return;
if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d$1("properties"))) {
const t = this.properties, s = [
...h$1(t),
...o$2(t)
];
for (const i of s)this.createProperty(i, t[i]);
}
const t = this[Symbol.metadata];
if (null !== t) {
const s = litPropertyMetadata.get(t);
if (void 0 !== s) for (const [t, i] of s)this.elementProperties.set(t, i);
}
this._$Eh = new Map;
for (const [t, s] of this.elementProperties){
const i = this._$Eu(t, s);
void 0 !== i && this._$Eh.set(i, t);
}
this.elementStyles = this.finalizeStyles(this.styles);
}
static finalizeStyles(s) {
const i = [];
if (Array.isArray(s)) {
const e = new Set(s.flat(1 / 0).reverse());
for (const s of e)i.unshift(c$2(s));
} else void 0 !== s && i.push(c$2(s));
return i;
}
static _$Eu(t, s) {
const i = s.attribute;
return false === i ? void 0 : "string" == typeof i ? i : "string" == typeof t ? t.toLowerCase() : void 0;
}
_$Ev() {
this._$ES = new Promise((t)=>this.enableUpdating = t), this._$AL = new Map, this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t)=>t(this));
}
addController(t) {
(this._$EO ?? (this._$EO = new Set)).add(t), void 0 !== this.renderRoot && this.isConnected && t.hostConnected?.();
}
removeController(t) {
this._$EO?.delete(t);
}
_$E_() {
const t = new Map, s = this.constructor.elementProperties;
for (const i of s.keys())this.hasOwnProperty(i) && (t.set(i, this[i]), delete this[i]);
t.size > 0 && (this._$Ep = t);
}
createRenderRoot() {
const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
return S$1(t, this.constructor.elementStyles), t;
}
connectedCallback() {
this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this.enableUpdating(true), this._$EO?.forEach((t)=>t.hostConnected?.());
}
enableUpdating(t) {}
disconnectedCallback() {
this._$EO?.forEach((t)=>t.hostDisconnected?.());
}
attributeChangedCallback(t, s, i) {
this._$AK(t, i);
}
_$EC(t, s) {
const i = this.constructor.elementProperties.get(t), e = this.constructor._$Eu(t, i);
if (void 0 !== e && true === i.reflect) {
const r = (void 0 !== i.converter?.toAttribute ? i.converter : u$1).toAttribute(s, i.type);
this._$Em = t, null == r ? this.removeAttribute(e) : this.setAttribute(e, r), this._$Em = null;
}
}
_$AK(t, s) {
const i = this.constructor, e = i._$Eh.get(t);
if (void 0 !== e && this._$Em !== e) {
const t = i.getPropertyOptions(e), r = "function" == typeof t.converter ? {
fromAttribute: t.converter
} : void 0 !== t.converter?.fromAttribute ? t.converter : u$1;
this._$Em = e, this[e] = r.fromAttribute(s, t.type), this._$Em = null;
}
}
requestUpdate(t, s, i) {
if (void 0 !== t) {
if (i ?? (i = this.constructor.getPropertyOptions(t)), !(i.hasChanged ?? f$1)(this[t], s)) return;
this.P(t, s, i);
}
false === this.isUpdatePending && (this._$ES = this._$ET());
}
P(t, s, i) {
this._$AL.has(t) || this._$AL.set(t, s), true === i.reflect && this._$Em !== t && (this._$Ej ?? (this._$Ej = new Set)).add(t);
}
async _$ET() {
this.isUpdatePending = true;
try {
await this._$ES;
} catch (t) {
Promise.reject(t);
}
const t = this.scheduleUpdate();
return null != t && await t, !this.isUpdatePending;
}
scheduleUpdate() {
return this.performUpdate();
}
performUpdate() {
if (!this.isUpdatePending) return;
if (!this.hasUpdated) {
if (this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this._$Ep) {
for (const [t, s] of this._$Ep)this[t] = s;
this._$Ep = void 0;
}
const t = this.constructor.elementProperties;
if (t.size > 0) for (const [s, i] of t) true !== i.wrapped || this._$AL.has(s) || void 0 === this[s] || this.P(s, this[s], i);
}
let t = false;
const s = this._$AL;
try {
t = this.shouldUpdate(s), t ? (this.willUpdate(s), this._$EO?.forEach((t)=>t.hostUpdate?.()), this.update(s)) : this._$EU();
} catch (s) {
throw t = false, this._$EU(), s;
}
t && this._$AE(s);
}
willUpdate(t) {}
_$AE(t) {
this._$EO?.forEach((t)=>t.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t)), this.updated(t);
}
_$EU() {
this._$AL = new Map, this.isUpdatePending = false;
}
get updateComplete() {
return this.getUpdateComplete();
}
getUpdateComplete() {
return this._$ES;
}
shouldUpdate(t) {
return true;
}
update(t) {
this._$Ej && (this._$Ej = this._$Ej.forEach((t)=>this._$EC(t, this[t]))), this._$EU();
}
updated(t) {}
firstUpdated(t) {}
constructor(){
super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev();
}
}
b.elementStyles = [], b.shadowRootOptions = {
mode: "open"
}, b[d$1("elementProperties")] = new Map, b[d$1("finalized")] = new Map, p$1?.({
ReactiveElement: b
}), ((_a1 = a$1).reactiveElementVersions ?? (_a1.reactiveElementVersions = [])).push("2.0.4");
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/ const o$1 = {
attribute: true,
type: String,
converter: u$1,
reflect: false,
hasChanged: f$1
}, r$2 = (t = o$1, e, r)=>{
const { kind: n, metadata: i } = r;
let s = globalThis.litPropertyMetadata.get(i);
if (void 0 === s && globalThis.litPropertyMetadata.set(i, s = new Map), s.set(r.name, t), "accessor" === n) {
const { name: o } = r;
return {
set (r) {
const n = e.get.call(this);
e.set.call(this, r), this.requestUpdate(o, n, t);
},
init (e) {
return void 0 !== e && this.P(o, void 0, t), e;
}
};
}
if ("setter" === n) {
const { name: o } = r;
return function(r) {
const n = this[o];
e.call(this, r), this.requestUpdate(o, n, t);
};
}
throw Error("Unsupported decorator location: " + n);
};
function n$1(t) {
return (e, o)=>"object" == typeof o ? r$2(t, e, o) : ((t, e, o)=>{
const r = e.hasOwnProperty(o);
return e.constructor.createProperty(o, r ? {
...t,
wrapped: true
} : t), r ? Object.getOwnPropertyDescriptor(e, o) : void 0;
})(t, e, o);
}
/**
* postprocessing v6.37.2 build Fri Mar 28 2025
* https://github.com/pmndrs/postprocessing
* Copyright 2015-2025 Raoul van Rüschen
* @license Zlib
*/ // package.json
// src/core/Timer.js
var MILLISECONDS_TO_SECONDS = 1 / 1e3;
var SECONDS_TO_MILLISECONDS = 1e3;
var Timer = class {
/**
* Enables or disables auto reset based on page visibility.
*
* If enabled, the timer will be reset when the page becomes visible. This effectively pauses the timer when the page
* is hidden. Has no effect if the API is not supported.
*
* @type {Boolean}
* @see https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
*/ get autoReset() {
return this._autoReset;
}
set autoReset(value) {
if (typeof document !== "undefined" && document.hidden !== void 0) {
if (value) {
document.addEventListener("visibilitychange", this);
} else {
document.removeEventListener("visibilitychange", this);
}
this._autoReset = value;
}
}
get delta() {
return this._delta * MILLISECONDS_TO_SECONDS;
}
get fixedDelta() {
return this._fixedDelta * MILLISECONDS_TO_SECONDS;
}
set fixedDelta(value) {
this._fixedDelta = value * SECONDS_TO_MILLISECONDS;
}
get elapsed() {
return this._elapsed * MILLISECONDS_TO_SECONDS;
}
/**
* Updates this timer.
*
* @param {Boolean} [timestamp] - The current time in milliseconds.
*/ update(timestamp) {
if (this.useFixedDelta) {
this._delta = this.fixedDelta;
} else {
this.previousTime = this.currentTime;
this.currentTime = (timestamp !== void 0 ? timestamp : performance.now()) - this.startTime;
this._delta = this.currentTime - this.previousTime;
}
this._delta *= this.timescale;
this._elapsed += this._delta;
}
/**
* Resets this timer.
*/ reset() {
this._delta = 0;
this._elapsed = 0;
this.currentTime = performance.now() - this.startTime;
}
getDelta() {
return this.delta;
}
getElapsed() {
return this.elapsed;
}
handleEvent(e) {
if (!document.hidden) {
this.currentTime = performance.now() - this.startTime;
}
}
dispose() {
this.autoReset = false;
}
/**
* Constructs a new timer.
*/ constructor(){
this.startTime = performance.now();
this.previousTime = 0;
this.currentTime = 0;
this._delta = 0;
this._elapsed = 0;
this._fixedDelta = 1e3 / 60;
this.timescale = 1;
this.useFixedDelta = false;
this._autoReset = false;
}
};
var fullscreenGeometry = /* @__PURE__ */ (()=>{
const vertices = new Float32Array([
-1,
-1,
0,
3,
-1,
0,
-1,
3,
0
]);
const uvs = new Float32Array([
0,
0,
2,
0,
0,
2
]);
const geometry = new three.BufferGeometry();
geometry.setAttribute("position", new three.BufferAttribute(vertices, 3));
geometry.setAttribute("uv", new three.BufferAttribute(uvs, 2));
return geometry;
})();
var Pass = class _Pass {
/**
* A shared fullscreen triangle.
*
* The screen size is 2x2 units (NDC). A triangle needs to be 4x4 units to fill the screen.
* @see https://michaldrobot.com/2014/04/01/gcn-execution-patterns-in-full-screen-passes/
* @type {BufferGeometry}
* @internal
*/ static get fullscreenGeometry() {
return fullscreenGeometry;
}
/**
* Sets the render to screen flag.
*
* If this flag is changed, the fullscreen material will be updated as well.
*
* @type {Boolean}
*/ get renderToScreen() {
return !this.rtt;
}
set renderToScreen(value) {
if (this.rtt === value) {
const material = this.fullscreenMaterial;
if (material !== null) {
material.needsUpdate = true;
}
this.rtt = !value;
}
}
/**
* Sets the main scene.
*
* @type {Scene}
*/ set mainScene(value) {}
/**
* Sets the main camera.
*
* @type {Camera}
*/ set mainCamera(value) {}
/**
* Sets the renderer
*
* @deprecated
* @param {WebGLRenderer} renderer - The renderer.
*/ setRenderer(renderer) {
this.renderer = renderer;
}
/**
* Indicates whether this pass is enabled.
*
* @deprecated Use enabled instead.
* @return {Boolean} Whether this pass is enabled.
*/ isEnabled() {
return this.enabled;
}
/**
* Enables or disables this pass.
*
* @deprecated Use enabled instead.
* @param {Boolean} value - Whether the pass should be enabled.
*/ setEnabled(value) {
this.enabled = value;
}
/**
* The fullscreen material.
*
* @type {Material}
*/ get fullscreenMaterial() {
return this.screen !== null ? this.screen.material : null;
}
set fullscreenMaterial(value) {
let screen = this.screen;
if (screen !== null) {
screen.material = value;
} else {
screen = new three.Mesh(_Pass.fullscreenGeometry, value);
screen.frustumCulled = false;
if (this.scene === null) {
this.scene = new three.Scene();
}
this.scene.add(screen);
this.screen = screen;
}
}
/**
* Returns the current fullscreen material.
*
* @deprecated Use fullscreenMaterial instead.
* @return {Material} The current fullscreen material, or null if there is none.
*/ getFullscreenMaterial() {
return this.fullscreenMaterial;
}
/**
* Sets the fullscreen material.
*
* @deprecated Use fullscreenMaterial instead.
* @protected
* @param {Material} value - A fullscreen material.
*/ setFullscreenMaterial(value) {
this.fullscreenMaterial = value;
}
/**
* Returns the current depth texture.
*
* @return {Texture} The current depth texture, or null if there is none.
*/ getDepthTexture() {
return null;
}
/**
* Sets the depth texture.
*
* This method will be called automatically by the {@link EffectComposer}.
* You may override this method if your pass relies on the depth information of a preceding {@link RenderPass}.
*
* @param {Texture} depthTexture - A depth texture.
* @param {DepthPackingStrategy} [depthPacking=BasicDepthPacking] - The depth packing.
*/ setDepthTexture(depthTexture, depthPacking = three.BasicDepthPacking) {}
/**
* Renders this pass.
*
* This is an abstract method that must be overridden.
*
* @abstract
* @throws {Error} An error is thrown if the method is not overridden.
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
* @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
* @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
* @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
*/ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
throw new Error("Render method not implemented!");
}
/**
* Sets the size.
*
* You may override this method if you want to be informed about the size of the backbuffer/canvas.
* This method is called before {@link initialize} and every time the size of the {@link EffectComposer} changes.
*
* @param {Number} width - The width.
* @param {Number} height - The height.
*/ setSize(width, height) {}
/**
* Performs initialization tasks.
*
* This method is called when this pass is added to an {@link EffectComposer}.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {Boolean} alpha - Whether the renderer uses the alpha channel or not.
* @param {Number} frameBufferType - The type of the main frame buffers.
*/ initialize(renderer, alpha, frameBufferType) {}
/**
* Performs a shallow search for disposable properties and deletes them.
*
* The {@link EffectComposer} calls this method when it is being destroyed. You can use it independently to free
* memory when you're certain that you don't need this pass anymore.
*/ dispose() {
for (const key of Object.keys(this)){
const property = this[key];
const isDisposable = property instanceof three.WebGLRenderTarget || property instanceof three.Material || property instanceof three.Texture || property instanceof _Pass;
if (isDisposable) {
this[key].dispose();
}
}
if (this.fullscreenMaterial !== null) {
this.fullscreenMaterial.dispose();
}
}
/**
* Constructs a new pass.
*
* @param {String} [name] - The name of this pass. Does not have to be unique.
* @param {Scene} [scene] - The scene to render. The default scene contains a single mesh that fills the screen.
* @param {Camera} [camera] - A camera. Fullscreen effect passes don't require a camera.
*/ constructor(name = "Pass", scene = new three.Scene(), camera = new three.Camera()){
this.name = name;
this.renderer = null;
this.scene = scene;
this.camera = camera;
this.screen = null;
this.rtt = true;
this.needsSwap = true;
this.needsDepthTexture = false;
this.enabled = true;
}
};
// src/passes/ClearMaskPass.js
var ClearMaskPass = class extends Pass {
/**
* Disables the global stencil test.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
* @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
* @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
* @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
*/ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
const stencil = renderer.state.buffers.stencil;
stencil.setLocked(false);
stencil.setTest(false);
}
/**
* Constructs a new clear mask pass.
*/ constructor(){
super("ClearMaskPass", null, null);
this.needsSwap = false;
}
};
// src/materials/glsl/copy.frag
var copy_default = `#include <common>
#include <dithering_pars_fragment>
#ifdef FRAMEBUFFER_PRECISION_HIGH
uniform mediump sampler2D inputBuffer;
#else
uniform lowp sampler2D inputBuffer;
#endif
uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuffer,vUv);gl_FragColor=opacity*texel;
#include <colorspace_fragment>
#include <dithering_fragment>
}`;
// src/materials/glsl/common.vert
var common_default = `varying vec2 vUv;void main(){vUv=position.xy*0.5+0.5;gl_Position=vec4(position.xy,1.0,1.0);}`;
// src/materials/CopyMaterial.js
var CopyMaterial = class extends three.ShaderMaterial {
/**
* The input buffer.
*
* @type {Texture}
*/ set inputBuffer(value) {
this.uniforms.inputBuffer.value = value;
}
/**
* Sets the input buffer.
*
* @deprecated Use inputBuffer instead.
* @param {Number} value - The buffer.
*/ setInputBuffer(value) {
this.uniforms.inputBuffer.value = value;
}
/**
* Returns the opacity.
*
* @deprecated Use opacity instead.
* @return {Number} The opacity.
*/ getOpacity(value) {
return this.uniforms.opacity.value;
}
/**
* Sets the opacity.
*
* @deprecated Use opacity instead.
* @param {Number} value - The opacity.
*/ setOpacity(value) {
this.uniforms.opacity.value = value;
}
/**
* Constructs a new copy material.
*/ constructor(){
super({
name: "CopyMaterial",
uniforms: {
inputBuffer: new three.Uniform(null),
opacity: new three.Uniform(1)
},
blending: three.NoBlending,
toneMapped: false,
depthWrite: false,
depthTest: false,
fragmentShader: copy_default,
vertexShader: common_default
});
}
};
// src/passes/CopyPass.js
var CopyPass = class extends Pass {
/**
* Enables or disables auto resizing of the render target.
*
* @deprecated Use autoResize instead.
* @type {Boolean}
*/ get resize() {
return this.autoResize;
}
set resize(value) {
this.autoResize = value;
}
/**
* The output texture.
*
* @type {Texture}
*/ get texture() {
return this.renderTarget.texture;
}
/**
* Returns the output texture.
*
* @deprecated Use texture instead.
* @return {Texture} The texture.
*/ getTexture() {
return this.renderTarget.texture;
}
/**
* Enables or disables auto resizing of the render target.
*
* @deprecated Use autoResize instead.
* @param {Boolean} value - Whether the render target size should be updated automatically.
*/ setAutoResizeEnabled(value) {
this.autoResize = value;
}
/**
* Saves the input buffer.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
* @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
* @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
* @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
*/ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
this.fullscreenMaterial.inputBuffer = inputBuffer.texture;
renderer.setRenderTarget(this.renderToScreen ? null : this.renderTarget);
renderer.render(this.scene, this.camera);
}
/**
* Updates the size of this pass.
*
* @param {Number} width - The width.
* @param {Number} height - The height.
*/ setSize(width, height) {
if (this.autoResize) {
this.renderTarget.setSize(width, height);
}
}
/**
* Performs initialization tasks.
*
* @param {WebGLRenderer} renderer - A renderer.
* @param {Boolean} alpha - Whether the renderer uses the alpha channel.
* @param {Number} frameBufferType - The type of the main frame buffers.
*/ initialize(renderer, alpha, frameBufferType) {
if (frameBufferType !== void 0) {
this.renderTarget.texture.type = frameBufferType;
if (frameBufferType !== three.UnsignedByteType) {
this.fullscreenMaterial.defines.FRAMEBUFFER_PRECISION_HIGH = "1";
} else if (renderer !== null && renderer.outputColorSpace === three.SRGBColorSpace) {
this.renderTarget.texture.colorSpace = three.SRGBColorSpace;
}
}
}
/**
* Constructs a new save pass.
*
* @param {WebGLRenderTarget} [renderTarget] - A render target.
* @param {Boolean} [autoResize=true] - Whether the render target size should be updated automatically.
*/ constructor(renderTarget, autoResize = true){
super("CopyPass");
this.fullscreenMaterial = new CopyMaterial();
this.needsSwap = false;
this.renderTarget = renderTarget;
if (renderTarget === void 0) {
this.renderTarget = new three.WebGLRenderTarget(1, 1, {
minFilter: three.LinearFilter,
magFilter: three.LinearFilter,
stencilBuffer: false,
depthBuffer: false
});
this.renderTarget.texture.name = "CopyPass.Target";
}
this.autoResize = autoResize;
}
};
var color = /* @__PURE__ */ new three.Color();
var ClearPass = class extends Pass {
/**
* Sets the clear flags.
*
* @param {Boolean} color - Whether the color buffer should be cleared.
* @param {Boolean} depth - Whether the depth buffer should be cleared.
* @param {Boolean} stencil - Whether the stencil buffer should be cleared.
*/ setClearFlags(color2, depth, stencil) {
this.color = color2;
this.depth = depth;
this.stencil = stencil;
}
/**
* Returns the override clear color. Default is null.
*
* @deprecated Use overrideClearColor instead.
* @return {Color} The clear color.
*/ getOverrideClearColor() {
return this.overrideClearColor;
}
/**
* Sets the override clear color.
*
* @deprecated Use overrideClearColor instead.
* @param {Color} value - The clear color.
*/ setOverrideClearColor(value) {
this.overrideClearColor = value;
}
/**
* Returns the override clear alpha. Default is -1.
*
* @deprecated Use overrideClearAlpha instead.
* @return {Number} The clear alpha.
*/ getOverrideClearAlpha() {
return this.overrideClearAlpha;
}
/**
* Sets the override clear alpha.
*
* @deprecated Use overrideClearAlpha instead.
* @param {Number} value - The clear alpha.
*/ setOverrideClearAlpha(value) {
this.overrideClearAlpha = value;
}
/**
* Clears the input buffer or the screen.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
* @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
* @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
* @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
*/ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
const overrideClearColor = this.overrideClearColor;
const overrideClearAlpha = this.overrideClearAlpha;
const clearAlpha = renderer.getClearAlpha();
const hasOverrideClearColor = overrideClearColor !== null;
const hasOverrideClearAlpha = overrideClearAlpha >= 0;
if (hasOverrideClearColor) {
renderer.getClearColor(color);
renderer.setClearColor(overrideClearColor, hasOverrideClearAlpha ? overrideClearAlpha : clearAlpha);
} else if (hasOverrideClearAlpha) {
renderer.setClearAlpha(overrideClearAlpha);
}
renderer.setRenderTarget(this.renderToScreen ? null : inputBuffer);
renderer.clear(this.color, this.depth, this.stencil);
if (hasOverrideClearColor) {
renderer.setClearColor(color, clearAlpha);
} else if (hasOverrideClearAlpha) {
renderer.setClearAlpha(clearAlpha);
}
}
/**
* Constructs a new clear pass.
*
* @param {Boolean} [color=true] - Determines whether the color buffer should be cleared.
* @param {Boolean} [depth=true] - Determines whether the depth buffer should be cleared.
* @param {Boolean} [stencil=false] - Determines whether the stencil buffer should be cleared.
*/ constructor(color2 = true, depth = true, stencil = false){
super("ClearPass", null, null);
this.needsSwap = false;
this.color = color2;
this.depth = depth;
this.stencil = stencil;
this.overrideClearColor = null;
this.overrideClearAlpha = -1;
}
};
// src/passes/MaskPass.js
var MaskPass = class extends Pass {
set mainScene(value) {
this.scene = value;
}
set mainCamera(value) {
this.camera = value;
}
/**
* Indicates whether the mask should be inverted.
*
* @type {Boolean}
*/ get inverted() {
return this.inverse;
}
set inverted(value) {
this.inverse = value;
}
/**
* Indicates whether this pass should clear the stencil buffer.
*
* @type {Boolean}
* @deprecated Use clearPass.enabled instead.
*/ get clear() {
return this.clearPass.enabled;
}
set clear(value) {
this.clearPass.enabled = value;
}
/**
* Returns the internal clear pass.
*
* @deprecated Use clearPass.enabled instead.
* @return {ClearPass} The clear pass.
*/ getClearPass() {
return this.clearPass;
}
/**
* Indicates whether the mask is inverted.
*
* @deprecated Use inverted instead.
* @return {Boolean} Whether the mask is inverted.
*/ isInverted() {
return this.inverted;
}
/**
* Enables or disable mask inversion.
*
* @deprecated Use inverted instead.
* @param {Boolean} value - Whether the mask should be inverted.
*/ setInverted(value) {
this.inverted = value;
}
/**
* Renders the effect.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
* @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
* @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
* @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
*/ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
const context = renderer.getContext();
const buffers = renderer.state.buffers;
const scene = this.scene;
const camera = this.camera;
const clearPass = this.clearPass;
const writeValue = this.inverted ? 0 : 1;
const clearValue = 1 - writeValue;
buffers.color.setMask(false);
buffers.depth.setMask(false);
buffers.color.setLocked(true);
buffers.depth.setLocked(true);
buffers.stencil.setTest(true);
buffers.stencil.setOp(context.REPLACE, context.REPLACE, context.REPLACE);
buffers.stencil.setFunc(context.ALWAYS, writeValue, 4294967295);
buffers.stencil.setClear(clearValue);
buffers.stencil.setLocked(true);
if (this.clearPass.enabled) {
if (this.renderToScreen) {
clearPass.render(renderer, null);
} else {
clearPass.render(renderer, inputBuffer);
clearPass.render(renderer, outputBuffer);
}
}
if (this.renderToScreen) {
renderer.setRenderTarget(null);
renderer.render(scene, camera);
} else {
renderer.setRenderTarget(inputBuffer);
renderer.render(scene, camera);
renderer.setRenderTarget(outputBuffer);
renderer.render(scene, camera);
}
buffers.color.setLocked(false);
buffers.depth.setLocked(false);
buffers.stencil.setLocked(false);
buffers.stencil.setFunc(context.EQUAL, 1, 4294967295);
buffers.stencil.setOp(context.KEEP, context.KEEP, context.KEEP);
buffers.stencil.setLocked(true);
}
/**
* Constructs a new mask pass.
*
* @param {Scene} scene - The scene to render.
* @param {Camera} camera - The camera to use.
*/ constructor(scene, camera){
super("MaskPass", scene, camera);
this.needsSwap = false;
this.clearPass = new ClearPass(false, false, true);
this.inverse = false;
}
};
// src/core/EffectComposer.js
var EffectComposer$1 = class EffectComposer {
/**
* The current amount of samples used for multisample anti-aliasing.
*
* @type {Number}
*/ get multisampling() {
return this.inputBuffer.samples || 0;
}
/**
* Sets the amount of MSAA samples.
*
* Requires WebGL 2. Set to zero to disable multisampling.
*
* @type {Number}
*/ set multisampling(value) {
const buffer = this.inputBuffer;
const multisampling = this.multisampling;
if (multisampling > 0 && value > 0) {
this.inputBuffer.samples = value;
this.outputBuffer.samples = value;
this.inputBuffer.dispose();
this.outputBuffer.dispose();
} else if (multisampling !== value) {
this.inputBuffer.dispose();
this.outputBuffer.dispose();
this.inputBuffer = this.createBuffer(buffer.depthBuffer, buffer.stencilBuffer, buffer.texture.type, value);
this.inputBuffer.depthTexture = this.depthTexture;
this.outputBuffer = this.inputBuffer.clone();
}
}
/**
* Returns the internal timer.
*
* @return {Timer} The timer.
*/ getTimer() {
return this.timer;
}
/**
* Returns the renderer.
*
* @return {WebGLRenderer} The renderer.
*/ getRenderer() {
return this.renderer;
}
/**
* Sets the renderer.
*
* @param {WebGLRenderer} renderer - The renderer.
*/ setRenderer(renderer) {
this.renderer = renderer;
if (renderer !== null) {
const size = renderer.getSize(new three.Vector2());
const alpha = renderer.getContext().getContextAttributes().alpha;
const frameBufferType = this.inputBuffer.texture.type;
if (frameBufferType === three.UnsignedByteType && renderer.outputColorSpace === three.SRGBColorSpace) {
this.inputBuffer.texture.colorSpace = three.SRGBColorSpace;
this.outputBuffer.texture.colorSpace = three.SRGBColorSpace;
this.inputBuffer.dispose();
this.outputBuffer.dispose();
}
renderer.autoClear = false;
this.setSize(size.width, size.height);
for (const pass of this.passes){
pass.initialize(renderer, alpha, frameBufferType);
}
}
}
/**
* Replaces the current renderer with the given one.
*
* The auto clear mechanism of the provided renderer will be disabled. If the new render size differs from the
* previous one, all passes will be updated.
*
* By default, the DOM element of the current renderer will automatically be removed from its parent node and the DOM
* element of the new renderer will take its place.
*
* @deprecated Use setRenderer instead.
* @param {WebGLRenderer} renderer - The new renderer.
* @param {Boolean} updateDOM - Indicates whether the old canvas should be replaced by the new one in the DOM.
* @return {WebGLRenderer} The old renderer.
*/ replaceRenderer(renderer, updateDOM = true) {
const oldRenderer = this.renderer;
const parent = oldRenderer.domElement.parentNode;
this.setRenderer(renderer);
if (updateDOM && parent !== null) {
parent.removeChild(oldRenderer.domElement);
parent.appendChild(renderer.domElement);
}
return oldRenderer;
}
/**
* Creates a depth texture attachment that will be provided to all passes.
*
* Note: When a shader reads from a depth texture and writes to a render target that uses the same depth texture
* attachment, the depth information will be lost. This happens even if `depthWrite` is disabled.
*
* @private
* @return {DepthTexture} The depth texture.
*/ createDepthTexture() {
const depthTexture = this.depthTexture = new three.DepthTexture();
this.inputBuffer.depthTexture = depthTexture;
this.inputBuffer.dispose();
if (this.inputBuffer.stencilBuffer) {
depthTexture.format = three.DepthStencilFormat;
depthTexture.type = three.UnsignedInt248Type;
} else {
depthTexture.type = three.UnsignedIntType;
}
return depthTexture;
}
/**
* Deletes the current depth texture.
*
* @private
*/ deleteDepthTexture() {
if (this.depthTexture !== null) {
this.depthTexture.dispose();
this.depthTexture = null;
this.inputBuffer.depthTexture = null;
this.inputBuffer.dispose();
for (const pass of this.passes){
pass.setDepthTexture(null);
}
}
}
/**
* Creates a new render target.
*
* @deprecated Create buffers manually via WebGLRenderTarget instead.
* @param {Boolean} depthBuffer - Whether the render target should have a depth buffer.
* @param {Boolean} stencilBuffer - Whether the render target should have a stencil buffer.
* @param {Number} type - The frame buffer type.
* @param {Number} multisampling - The number of samples to use for antialiasing.
* @return {WebGLRenderTarget} A new render target that equals the renderer's canvas.
*/ createBuffer(depthBuffer, stencilBuffer, type, multisampling) {
const renderer = this.renderer;
const size = renderer === null ? new three.Vector2() : renderer.getDrawingBufferSize(new three.Vector2());
const options = {
minFilter: three.LinearFilter,
magFilter: three.LinearFilter,
stencilBuffer,
depthBuffer,
type
};
const renderTarget = new three.WebGLRenderTarget(size.width, size.height, options);
if (multisampling > 0) {
renderTarget.ignoreDepthForMultisampleCopy = false;
renderTarget.samples = multisampling;
}
if (type === three.UnsignedByteType && renderer !== null && renderer.outputColorSpace === three.SRGBColorSpace) {
renderTarget.texture.colorSpace = three.SRGBColorSpace;
}
renderTarget.texture.name = "EffectComposer.Buffer";
renderTarget.texture.generateMipmaps = false;
return renderTarget;
}
/**
* Can be used to change the main scene for all registered passes and effects.
*
* @param {Scene} scene - The scene.
*/ setMainScene(scene) {
for (const pass of this.passes){
pass.mainScene = scene;
}
}
/**
* Can be used to change the main camera for all registered passes and effects.
*
* @param {Camera} camera - The camera.
*/ setMainCamera(camera) {
for (const pass of this.passes){
pass.mainCamera = camera;
}
}
/**
* Adds a pass, optionally at a specific index.
*
* @param {Pass} pass - A new pass.
* @param {Number} [index] - An index at which the pass should be inserted.
*/ addPass(pass, index) {
const passes = this.passes;
const renderer = this.renderer;
const drawingBufferSize = renderer.getDrawingBufferSize(new three.Vector2());
const alpha = renderer.getContext().getContextAttributes().alpha;
const frameBufferType = this.inputBuffer.texture.type;
pass.setRenderer(renderer);
pass.setSize(drawingBufferSize.width, drawingBufferSize.height);
pass.initialize(renderer, alpha, frameBufferType);
if (this.autoRenderToScreen) {
if (passes.length > 0) {
passes[passes.length - 1].renderToScreen = false;
}
if (pass.renderToScreen) {
this.autoRenderToScreen = false;
}
}
if (index !== void 0) {
passes.splice(index, 0, pass);
} else {
passes.push(pass);
}
if (this.autoRenderToScreen) {
passes[passes.length - 1].renderToScreen = true;
}
if (pass.needsDepthTexture || this.depthTexture !== null) {
if (this.depthTexture === null) {
const depthTexture = this.createDepthTexture();
for (pass of passes){
pass.setDepthTexture(depthTexture);
}
} else {
pass.setDepthTexture(this.depthTexture);
}
}
}
/**
* Removes a pass.
*
* @param {Pass} pass - The pass.
*/ removePass(pass) {
const passes = this.passes;
const index = passes.indexOf(pass);
const exists = index !== -1;
const removed = exists && passes.splice(index, 1).length > 0;
if (removed) {
if (this.depthTexture !== null) {
const reducer = (a, b)=>a || b.needsDepthTexture;
const depthTextureRequired = passes.reduce(reducer, false);
if (!depthTextureRequired) {
if (pass.getDepthTexture() === this.depthTexture) {
pass.setDepthTexture(null);
}
this.deleteDepthTexture();
}
}
if (this.autoRenderToScreen) {
if (index === passes.length) {
pass.renderToScreen = false;
if (passes.length > 0) {
passes[passes.length - 1].renderToScreen = true;
}
}
}
}
}
/**
* Removes all passes.
*/ removeAllPasses() {
const passes = this.passes;
this.deleteDepthTexture();
if (passes.length > 0) {
if (this.autoRenderToScreen) {
passes[passes.length - 1].renderToScreen = false;
}
this.passes = [];
}
}
/**
* Renders all enabled passes in the order in which they were added.
*
* @param {Number} [deltaTime] - The time since the last frame in seconds.
*/ render(deltaTime) {
const renderer = this.renderer;
const copyPass = this.copyPass;
let inputBuffer = this.inputBuffer;
let outputBuffer = this.outputBuffer;
let stencilTest = false;
let context, stencil, buffer;
if (deltaTime === void 0) {
this.timer.update();
deltaTime = this.timer.getDelta();
}
for (const pass of this.passes){
if (pass.enabled) {
pass.render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
if (pass.needsSwap) {
if (stencilTest) {
copyPass.renderToScreen = pass.renderToScreen;
context = renderer.getContext();
stencil = renderer.state.buffers.stencil;
stencil.setFunc(context.NOTEQUAL, 1, 4294967295);
copyPass.render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
stencil.setFunc(context.EQUAL, 1, 4294967295);
}
buffer = inputBuffer;
inputBuffer = outputBuffer;
outputBuffer = buffer;
}
if (pass instanceof MaskPass) {
stencilTest = true;
} else if (pass instanceof ClearMaskPass) {
stencilTest = false;
}
}
}
}
/**
* Sets the size of the buffers, passes and the renderer.
*
* @param {Number} width - The width.
* @param {Number} height - The height.
* @param {Boolean} [updateStyle] - Determines whether the style of the canvas should be updated.
*/ setSize(width, height, updateStyle) {
con