1 | "use strict";
|
2 | var constants = require("@pixi/constants"), extensions = require("@pixi/extensions"), math = require("@pixi/math"), RenderTexturePool = require("../renderTexture/RenderTexturePool.js"), UniformGroup = require("../shader/UniformGroup.js"), Quad = require("../utils/Quad.js"), QuadUv = require("../utils/QuadUv.js"), FilterState = require("./FilterState.js");
|
3 | const tempPoints = [new math.Point(), new math.Point(), new math.Point(), new math.Point()], tempMatrix = new math.Matrix();
|
4 | class FilterSystem {
|
5 | |
6 |
|
7 |
|
8 | constructor(renderer) {
|
9 | this.renderer = renderer, this.defaultFilterStack = [{}], this.texturePool = new RenderTexturePool.RenderTexturePool(), this.statePool = [], this.quad = new Quad.Quad(), this.quadUv = new QuadUv.QuadUv(), this.tempRect = new math.Rectangle(), this.activeState = {}, this.globalUniforms = new UniformGroup.UniformGroup({
|
10 | outputFrame: new math.Rectangle(),
|
11 | inputSize: new Float32Array(4),
|
12 | inputPixel: new Float32Array(4),
|
13 | inputClamp: new Float32Array(4),
|
14 | resolution: 1,
|
15 |
|
16 | filterArea: new Float32Array(4),
|
17 | filterClamp: new Float32Array(4)
|
18 | }, !0), this.forceClear = !1, this.useMaxPadding = !1;
|
19 | }
|
20 | init() {
|
21 | this.texturePool.setScreenSize(this.renderer.view);
|
22 | }
|
23 | |
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | push(target, filters) {
|
30 | const renderer = this.renderer, filterStack = this.defaultFilterStack, state = this.statePool.pop() || new FilterState.FilterState(), renderTextureSystem = renderer.renderTexture;
|
31 | let currentResolution, currentMultisample;
|
32 | if (renderTextureSystem.current) {
|
33 | const renderTexture = renderTextureSystem.current;
|
34 | currentResolution = renderTexture.resolution, currentMultisample = renderTexture.multisample;
|
35 | } else
|
36 | currentResolution = renderer.resolution, currentMultisample = renderer.multisample;
|
37 | let resolution = filters[0].resolution || currentResolution, multisample = filters[0].multisample ?? currentMultisample, padding = filters[0].padding, autoFit = filters[0].autoFit, legacy = filters[0].legacy ?? !0;
|
38 | for (let i = 1; i < filters.length; i++) {
|
39 | const filter = filters[i];
|
40 | resolution = Math.min(resolution, filter.resolution || currentResolution), multisample = Math.min(multisample, filter.multisample ?? currentMultisample), padding = this.useMaxPadding ? Math.max(padding, filter.padding) : padding + filter.padding, autoFit = autoFit && filter.autoFit, legacy = legacy || (filter.legacy ?? !0);
|
41 | }
|
42 | filterStack.length === 1 && (this.defaultFilterStack[0].renderTexture = renderTextureSystem.current), filterStack.push(state), state.resolution = resolution, state.multisample = multisample, state.legacy = legacy, state.target = target, state.sourceFrame.copyFrom(target.filterArea || target.getBounds(!0)), state.sourceFrame.pad(padding);
|
43 | const sourceFrameProjected = this.tempRect.copyFrom(renderTextureSystem.sourceFrame);
|
44 | renderer.projection.transform && this.transformAABB(
|
45 | tempMatrix.copyFrom(renderer.projection.transform).invert(),
|
46 | sourceFrameProjected
|
47 | ), autoFit ? (state.sourceFrame.fit(sourceFrameProjected), (state.sourceFrame.width <= 0 || state.sourceFrame.height <= 0) && (state.sourceFrame.width = 0, state.sourceFrame.height = 0)) : state.sourceFrame.intersects(sourceFrameProjected) || (state.sourceFrame.width = 0, state.sourceFrame.height = 0), this.roundFrame(
|
48 | state.sourceFrame,
|
49 | renderTextureSystem.current ? renderTextureSystem.current.resolution : renderer.resolution,
|
50 | renderTextureSystem.sourceFrame,
|
51 | renderTextureSystem.destinationFrame,
|
52 | renderer.projection.transform
|
53 | ), state.renderTexture = this.getOptimalFilterTexture(
|
54 | state.sourceFrame.width,
|
55 | state.sourceFrame.height,
|
56 | resolution,
|
57 | multisample
|
58 | ), state.filters = filters, state.destinationFrame.width = state.renderTexture.width, state.destinationFrame.height = state.renderTexture.height;
|
59 | const destinationFrame = this.tempRect;
|
60 | destinationFrame.x = 0, destinationFrame.y = 0, destinationFrame.width = state.sourceFrame.width, destinationFrame.height = state.sourceFrame.height, state.renderTexture.filterFrame = state.sourceFrame, state.bindingSourceFrame.copyFrom(renderTextureSystem.sourceFrame), state.bindingDestinationFrame.copyFrom(renderTextureSystem.destinationFrame), state.transform = renderer.projection.transform, renderer.projection.transform = null, renderTextureSystem.bind(state.renderTexture, state.sourceFrame, destinationFrame), renderer.framebuffer.clear(0, 0, 0, 0);
|
61 | }
|
62 |
|
63 | pop() {
|
64 | const filterStack = this.defaultFilterStack, state = filterStack.pop(), filters = state.filters;
|
65 | this.activeState = state;
|
66 | const globalUniforms = this.globalUniforms.uniforms;
|
67 | globalUniforms.outputFrame = state.sourceFrame, globalUniforms.resolution = state.resolution;
|
68 | const inputSize = globalUniforms.inputSize, inputPixel = globalUniforms.inputPixel, inputClamp = globalUniforms.inputClamp;
|
69 | if (inputSize[0] = state.destinationFrame.width, inputSize[1] = state.destinationFrame.height, inputSize[2] = 1 / inputSize[0], inputSize[3] = 1 / inputSize[1], inputPixel[0] = Math.round(inputSize[0] * state.resolution), inputPixel[1] = Math.round(inputSize[1] * state.resolution), inputPixel[2] = 1 / inputPixel[0], inputPixel[3] = 1 / inputPixel[1], inputClamp[0] = 0.5 * inputPixel[2], inputClamp[1] = 0.5 * inputPixel[3], inputClamp[2] = state.sourceFrame.width * inputSize[2] - 0.5 * inputPixel[2], inputClamp[3] = state.sourceFrame.height * inputSize[3] - 0.5 * inputPixel[3], state.legacy) {
|
70 | const filterArea = globalUniforms.filterArea;
|
71 | filterArea[0] = state.destinationFrame.width, filterArea[1] = state.destinationFrame.height, filterArea[2] = state.sourceFrame.x, filterArea[3] = state.sourceFrame.y, globalUniforms.filterClamp = globalUniforms.inputClamp;
|
72 | }
|
73 | this.globalUniforms.update();
|
74 | const lastState = filterStack[filterStack.length - 1];
|
75 | if (this.renderer.framebuffer.blit(), filters.length === 1)
|
76 | filters[0].apply(this, state.renderTexture, lastState.renderTexture, constants.CLEAR_MODES.BLEND, state), this.returnFilterTexture(state.renderTexture);
|
77 | else {
|
78 | let flip = state.renderTexture, flop = this.getOptimalFilterTexture(
|
79 | flip.width,
|
80 | flip.height,
|
81 | state.resolution
|
82 | );
|
83 | flop.filterFrame = flip.filterFrame;
|
84 | let i = 0;
|
85 | for (i = 0; i < filters.length - 1; ++i) {
|
86 | i === 1 && state.multisample > 1 && (flop = this.getOptimalFilterTexture(
|
87 | flip.width,
|
88 | flip.height,
|
89 | state.resolution
|
90 | ), flop.filterFrame = flip.filterFrame), filters[i].apply(this, flip, flop, constants.CLEAR_MODES.CLEAR, state);
|
91 | const t = flip;
|
92 | flip = flop, flop = t;
|
93 | }
|
94 | filters[i].apply(this, flip, lastState.renderTexture, constants.CLEAR_MODES.BLEND, state), i > 1 && state.multisample > 1 && this.returnFilterTexture(state.renderTexture), this.returnFilterTexture(flip), this.returnFilterTexture(flop);
|
95 | }
|
96 | state.clear(), this.statePool.push(state);
|
97 | }
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 | bindAndClear(filterTexture, clearMode = constants.CLEAR_MODES.CLEAR) {
|
104 | const {
|
105 | renderTexture: renderTextureSystem,
|
106 | state: stateSystem
|
107 | } = this.renderer;
|
108 | if (filterTexture === this.defaultFilterStack[this.defaultFilterStack.length - 1].renderTexture ? this.renderer.projection.transform = this.activeState.transform : this.renderer.projection.transform = null, filterTexture?.filterFrame) {
|
109 | const destinationFrame = this.tempRect;
|
110 | destinationFrame.x = 0, destinationFrame.y = 0, destinationFrame.width = filterTexture.filterFrame.width, destinationFrame.height = filterTexture.filterFrame.height, renderTextureSystem.bind(filterTexture, filterTexture.filterFrame, destinationFrame);
|
111 | } else
|
112 | filterTexture !== this.defaultFilterStack[this.defaultFilterStack.length - 1].renderTexture ? renderTextureSystem.bind(filterTexture) : this.renderer.renderTexture.bind(
|
113 | filterTexture,
|
114 | this.activeState.bindingSourceFrame,
|
115 | this.activeState.bindingDestinationFrame
|
116 | );
|
117 | const autoClear = stateSystem.stateId & 1 || this.forceClear;
|
118 | (clearMode === constants.CLEAR_MODES.CLEAR || clearMode === constants.CLEAR_MODES.BLIT && autoClear) && this.renderer.framebuffer.clear(0, 0, 0, 0);
|
119 | }
|
120 | |
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | applyFilter(filter, input, output, clearMode) {
|
130 | const renderer = this.renderer;
|
131 | renderer.state.set(filter.state), this.bindAndClear(output, clearMode), filter.uniforms.uSampler = input, filter.uniforms.filterGlobals = this.globalUniforms, renderer.shader.bind(filter), filter.legacy = !!filter.program.attributeData.aTextureCoord, filter.legacy ? (this.quadUv.map(input._frame, input.filterFrame), renderer.geometry.bind(this.quadUv), renderer.geometry.draw(constants.DRAW_MODES.TRIANGLES)) : (renderer.geometry.bind(this.quad), renderer.geometry.draw(constants.DRAW_MODES.TRIANGLE_STRIP));
|
132 | }
|
133 | |
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | calculateSpriteMatrix(outputMatrix, sprite) {
|
142 | const { sourceFrame, destinationFrame } = this.activeState, { orig } = sprite._texture, mappedMatrix = outputMatrix.set(
|
143 | destinationFrame.width,
|
144 | 0,
|
145 | 0,
|
146 | destinationFrame.height,
|
147 | sourceFrame.x,
|
148 | sourceFrame.y
|
149 | ), worldTransform = sprite.worldTransform.copyTo(math.Matrix.TEMP_MATRIX);
|
150 | return worldTransform.invert(), mappedMatrix.prepend(worldTransform), mappedMatrix.scale(1 / orig.width, 1 / orig.height), mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y), mappedMatrix;
|
151 | }
|
152 |
|
153 | destroy() {
|
154 | this.renderer = null, this.texturePool.clear(!1);
|
155 | }
|
156 | |
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | getOptimalFilterTexture(minWidth, minHeight, resolution = 1, multisample = constants.MSAA_QUALITY.NONE) {
|
165 | return this.texturePool.getOptimalTexture(minWidth, minHeight, resolution, multisample);
|
166 | }
|
167 | |
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | getFilterTexture(input, resolution, multisample) {
|
175 | if (typeof input == "number") {
|
176 | const swap = input;
|
177 | input = resolution, resolution = swap;
|
178 | }
|
179 | input = input || this.activeState.renderTexture;
|
180 | const filterTexture = this.texturePool.getOptimalTexture(
|
181 | input.width,
|
182 | input.height,
|
183 | resolution || input.resolution,
|
184 | multisample || constants.MSAA_QUALITY.NONE
|
185 | );
|
186 | return filterTexture.filterFrame = input.filterFrame, filterTexture;
|
187 | }
|
188 | |
189 |
|
190 |
|
191 |
|
192 | returnFilterTexture(renderTexture) {
|
193 | this.texturePool.returnTexture(renderTexture);
|
194 | }
|
195 |
|
196 | emptyPool() {
|
197 | this.texturePool.clear(!0);
|
198 | }
|
199 |
|
200 | resize() {
|
201 | this.texturePool.setScreenSize(this.renderer.view);
|
202 | }
|
203 | |
204 |
|
205 |
|
206 |
|
207 | transformAABB(matrix, rect) {
|
208 | const lt = tempPoints[0], lb = tempPoints[1], rt = tempPoints[2], rb = tempPoints[3];
|
209 | lt.set(rect.left, rect.top), lb.set(rect.left, rect.bottom), rt.set(rect.right, rect.top), rb.set(rect.right, rect.bottom), matrix.apply(lt, lt), matrix.apply(lb, lb), matrix.apply(rt, rt), matrix.apply(rb, rb);
|
210 | const x0 = Math.min(lt.x, lb.x, rt.x, rb.x), y0 = Math.min(lt.y, lb.y, rt.y, rb.y), x1 = Math.max(lt.x, lb.x, rt.x, rb.x), y1 = Math.max(lt.y, lb.y, rt.y, rb.y);
|
211 | rect.x = x0, rect.y = y0, rect.width = x1 - x0, rect.height = y1 - y0;
|
212 | }
|
213 | roundFrame(frame, resolution, bindingSourceFrame, bindingDestinationFrame, transform) {
|
214 | if (!(frame.width <= 0 || frame.height <= 0 || bindingSourceFrame.width <= 0 || bindingSourceFrame.height <= 0)) {
|
215 | if (transform) {
|
216 | const { a, b, c, d } = transform;
|
217 | if ((Math.abs(b) > 1e-4 || Math.abs(c) > 1e-4) && (Math.abs(a) > 1e-4 || Math.abs(d) > 1e-4))
|
218 | return;
|
219 | }
|
220 | transform = transform ? tempMatrix.copyFrom(transform) : tempMatrix.identity(), transform.translate(-bindingSourceFrame.x, -bindingSourceFrame.y).scale(
|
221 | bindingDestinationFrame.width / bindingSourceFrame.width,
|
222 | bindingDestinationFrame.height / bindingSourceFrame.height
|
223 | ).translate(bindingDestinationFrame.x, bindingDestinationFrame.y), this.transformAABB(transform, frame), frame.ceil(resolution), this.transformAABB(transform.invert(), frame);
|
224 | }
|
225 | }
|
226 | }
|
227 | FilterSystem.extension = {
|
228 | type: extensions.ExtensionType.RendererSystem,
|
229 | name: "filter"
|
230 | };
|
231 | extensions.extensions.add(FilterSystem);
|
232 | exports.FilterSystem = FilterSystem;
|
233 |
|