UNPKG

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