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