1 | import Color from '../style-spec/util/color';
|
2 |
|
3 | import type Context from './context';
|
4 | import type {
|
5 | BlendFuncType,
|
6 | BlendEquationType,
|
7 | ColorMaskType,
|
8 | DepthRangeType,
|
9 | DepthMaskType,
|
10 | StencilFuncType,
|
11 | StencilOpType,
|
12 | DepthFuncType,
|
13 | TextureUnitType,
|
14 | ViewportType,
|
15 | CullFaceModeType,
|
16 | FrontFaceType,
|
17 | } from './types';
|
18 |
|
19 | export interface IValue<T> {
|
20 | current: T;
|
21 | default: T;
|
22 | dirty: boolean;
|
23 | get(): T;
|
24 | setDefault(): void;
|
25 | set(value: T): void;
|
26 | }
|
27 |
|
28 | class BaseValue<T> implements IValue<T> {
|
29 | gl: WebGLRenderingContext;
|
30 | current: T;
|
31 | default: T;
|
32 | dirty: boolean;
|
33 |
|
34 | constructor(context: Context) {
|
35 | this.gl = context.gl;
|
36 | this.default = this.getDefault();
|
37 | this.current = this.default;
|
38 | this.dirty = false;
|
39 | }
|
40 |
|
41 | get(): T {
|
42 | return this.current;
|
43 | }
|
44 | set(value: T) {
|
45 |
|
46 | }
|
47 |
|
48 | getDefault(): T {
|
49 | return this.default;
|
50 | }
|
51 | setDefault() {
|
52 | this.set(this.default);
|
53 | }
|
54 | }
|
55 |
|
56 | export class ClearColor extends BaseValue<Color> {
|
57 | getDefault(): Color {
|
58 | return Color.transparent;
|
59 | }
|
60 | set(v: Color) {
|
61 | const c = this.current;
|
62 | if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
|
63 | this.gl.clearColor(v.r, v.g, v.b, v.a);
|
64 | this.current = v;
|
65 | this.dirty = false;
|
66 | }
|
67 | }
|
68 |
|
69 | export class ClearDepth extends BaseValue<number> {
|
70 | getDefault(): number {
|
71 | return 1;
|
72 | }
|
73 | set(v: number) {
|
74 | if (v === this.current && !this.dirty) return;
|
75 | this.gl.clearDepth(v);
|
76 | this.current = v;
|
77 | this.dirty = false;
|
78 | }
|
79 | }
|
80 |
|
81 | export class ClearStencil extends BaseValue<number> {
|
82 | getDefault(): number {
|
83 | return 0;
|
84 | }
|
85 | set(v: number) {
|
86 | if (v === this.current && !this.dirty) return;
|
87 | this.gl.clearStencil(v);
|
88 | this.current = v;
|
89 | this.dirty = false;
|
90 | }
|
91 | }
|
92 |
|
93 | export class ColorMask extends BaseValue<ColorMaskType> {
|
94 | getDefault(): ColorMaskType {
|
95 | return [true, true, true, true];
|
96 | }
|
97 | set(v: ColorMaskType) {
|
98 | const c = this.current;
|
99 | if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
|
100 | this.gl.colorMask(v[0], v[1], v[2], v[3]);
|
101 | this.current = v;
|
102 | this.dirty = false;
|
103 | }
|
104 | }
|
105 |
|
106 | export class DepthMask extends BaseValue<DepthMaskType> {
|
107 | getDefault(): DepthMaskType {
|
108 | return true;
|
109 | }
|
110 | set(v: DepthMaskType): void {
|
111 | if (v === this.current && !this.dirty) return;
|
112 | this.gl.depthMask(v);
|
113 | this.current = v;
|
114 | this.dirty = false;
|
115 | }
|
116 | }
|
117 |
|
118 | export class StencilMask extends BaseValue<number> {
|
119 | getDefault(): number {
|
120 | return 0xFF;
|
121 | }
|
122 | set(v: number): void {
|
123 | if (v === this.current && !this.dirty) return;
|
124 | this.gl.stencilMask(v);
|
125 | this.current = v;
|
126 | this.dirty = false;
|
127 | }
|
128 | }
|
129 |
|
130 | export class StencilFunc extends BaseValue<StencilFuncType> {
|
131 | getDefault(): StencilFuncType {
|
132 | return {
|
133 | func: this.gl.ALWAYS,
|
134 | ref: 0,
|
135 | mask: 0xFF
|
136 | };
|
137 | }
|
138 | set(v: StencilFuncType): void {
|
139 | const c = this.current;
|
140 | if (v.func === c.func && v.ref === c.ref && v.mask === c.mask && !this.dirty) return;
|
141 | this.gl.stencilFunc(v.func, v.ref, v.mask);
|
142 | this.current = v;
|
143 | this.dirty = false;
|
144 | }
|
145 | }
|
146 |
|
147 | export class StencilOp extends BaseValue<StencilOpType> {
|
148 | getDefault(): StencilOpType {
|
149 | const gl = this.gl;
|
150 | return [gl.KEEP, gl.KEEP, gl.KEEP];
|
151 | }
|
152 | set(v: StencilOpType) {
|
153 | const c = this.current;
|
154 | if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && !this.dirty) return;
|
155 | this.gl.stencilOp(v[0], v[1], v[2]);
|
156 | this.current = v;
|
157 | this.dirty = false;
|
158 | }
|
159 | }
|
160 |
|
161 | export class StencilTest extends BaseValue<boolean> {
|
162 | getDefault(): boolean {
|
163 | return false;
|
164 | }
|
165 | set(v: boolean) {
|
166 | if (v === this.current && !this.dirty) return;
|
167 | const gl = this.gl;
|
168 | if (v) {
|
169 | gl.enable(gl.STENCIL_TEST);
|
170 | } else {
|
171 | gl.disable(gl.STENCIL_TEST);
|
172 | }
|
173 | this.current = v;
|
174 | this.dirty = false;
|
175 | }
|
176 | }
|
177 |
|
178 | export class DepthRange extends BaseValue<DepthRangeType> {
|
179 | getDefault(): DepthRangeType {
|
180 | return [0, 1];
|
181 | }
|
182 | set(v: DepthRangeType) {
|
183 | const c = this.current;
|
184 | if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
|
185 | this.gl.depthRange(v[0], v[1]);
|
186 | this.current = v;
|
187 | this.dirty = false;
|
188 | }
|
189 | }
|
190 |
|
191 | export class DepthTest extends BaseValue<boolean> {
|
192 | getDefault(): boolean {
|
193 | return false;
|
194 | }
|
195 | set(v: boolean) {
|
196 | if (v === this.current && !this.dirty) return;
|
197 | const gl = this.gl;
|
198 | if (v) {
|
199 | gl.enable(gl.DEPTH_TEST);
|
200 | } else {
|
201 | gl.disable(gl.DEPTH_TEST);
|
202 | }
|
203 | this.current = v;
|
204 | this.dirty = false;
|
205 | }
|
206 | }
|
207 |
|
208 | export class DepthFunc extends BaseValue<DepthFuncType> {
|
209 | getDefault(): DepthFuncType {
|
210 | return this.gl.LESS;
|
211 | }
|
212 | set(v: DepthFuncType) {
|
213 | if (v === this.current && !this.dirty) return;
|
214 | this.gl.depthFunc(v);
|
215 | this.current = v;
|
216 | this.dirty = false;
|
217 | }
|
218 | }
|
219 |
|
220 | export class Blend extends BaseValue<boolean> {
|
221 | getDefault(): boolean {
|
222 | return false;
|
223 | }
|
224 | set(v: boolean) {
|
225 | if (v === this.current && !this.dirty) return;
|
226 | const gl = this.gl;
|
227 | if (v) {
|
228 | gl.enable(gl.BLEND);
|
229 | } else {
|
230 | gl.disable(gl.BLEND);
|
231 | }
|
232 | this.current = v;
|
233 | this.dirty = false;
|
234 | }
|
235 | }
|
236 |
|
237 | export class BlendFunc extends BaseValue<BlendFuncType> {
|
238 | getDefault(): BlendFuncType {
|
239 | const gl = this.gl;
|
240 | return [gl.ONE, gl.ZERO];
|
241 | }
|
242 | set(v: BlendFuncType) {
|
243 | const c = this.current;
|
244 | if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
|
245 | this.gl.blendFunc(v[0], v[1]);
|
246 | this.current = v;
|
247 | this.dirty = false;
|
248 | }
|
249 | }
|
250 |
|
251 | export class BlendColor extends BaseValue<Color> {
|
252 | getDefault(): Color {
|
253 | return Color.transparent;
|
254 | }
|
255 | set(v: Color) {
|
256 | const c = this.current;
|
257 | if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
|
258 | this.gl.blendColor(v.r, v.g, v.b, v.a);
|
259 | this.current = v;
|
260 | this.dirty = false;
|
261 | }
|
262 | }
|
263 |
|
264 | export class BlendEquation extends BaseValue<BlendEquationType> {
|
265 | getDefault(): BlendEquationType {
|
266 | return this.gl.FUNC_ADD;
|
267 | }
|
268 | set(v: BlendEquationType) {
|
269 | if (v === this.current && !this.dirty) return;
|
270 | this.gl.blendEquation(v);
|
271 | this.current = v;
|
272 | this.dirty = false;
|
273 | }
|
274 | }
|
275 |
|
276 | export class CullFace extends BaseValue<boolean> {
|
277 | getDefault(): boolean {
|
278 | return false;
|
279 | }
|
280 | set(v: boolean) {
|
281 | if (v === this.current && !this.dirty) return;
|
282 | const gl = this.gl;
|
283 | if (v) {
|
284 | gl.enable(gl.CULL_FACE);
|
285 | } else {
|
286 | gl.disable(gl.CULL_FACE);
|
287 | }
|
288 | this.current = v;
|
289 | this.dirty = false;
|
290 | }
|
291 | }
|
292 |
|
293 | export class CullFaceSide extends BaseValue<CullFaceModeType> {
|
294 | getDefault(): CullFaceModeType {
|
295 | return this.gl.BACK;
|
296 | }
|
297 | set(v: CullFaceModeType) {
|
298 | if (v === this.current && !this.dirty) return;
|
299 | this.gl.cullFace(v);
|
300 | this.current = v;
|
301 | this.dirty = false;
|
302 | }
|
303 | }
|
304 |
|
305 | export class FrontFace extends BaseValue<FrontFaceType> {
|
306 | getDefault(): FrontFaceType {
|
307 | return this.gl.CCW;
|
308 | }
|
309 | set(v: FrontFaceType) {
|
310 | if (v === this.current && !this.dirty) return;
|
311 | this.gl.frontFace(v);
|
312 | this.current = v;
|
313 | this.dirty = false;
|
314 | }
|
315 | }
|
316 |
|
317 | export class ProgramValue extends BaseValue<WebGLProgram> {
|
318 | getDefault(): WebGLProgram {
|
319 | return null;
|
320 | }
|
321 | set(v?: WebGLProgram | null) {
|
322 | if (v === this.current && !this.dirty) return;
|
323 | this.gl.useProgram(v);
|
324 | this.current = v;
|
325 | this.dirty = false;
|
326 | }
|
327 | }
|
328 |
|
329 | export class ActiveTextureUnit extends BaseValue<TextureUnitType> {
|
330 | getDefault(): TextureUnitType {
|
331 | return this.gl.TEXTURE0;
|
332 | }
|
333 | set(v: TextureUnitType) {
|
334 | if (v === this.current && !this.dirty) return;
|
335 | this.gl.activeTexture(v);
|
336 | this.current = v;
|
337 | this.dirty = false;
|
338 | }
|
339 | }
|
340 |
|
341 | export class Viewport extends BaseValue<ViewportType> {
|
342 | getDefault(): ViewportType {
|
343 | const gl = this.gl;
|
344 | return [0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight];
|
345 | }
|
346 | set(v: ViewportType) {
|
347 | const c = this.current;
|
348 | if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
|
349 | this.gl.viewport(v[0], v[1], v[2], v[3]);
|
350 | this.current = v;
|
351 | this.dirty = false;
|
352 | }
|
353 | }
|
354 |
|
355 | export class BindFramebuffer extends BaseValue<WebGLFramebuffer> {
|
356 | getDefault(): WebGLFramebuffer {
|
357 | return null;
|
358 | }
|
359 | set(v?: WebGLFramebuffer | null) {
|
360 | if (v === this.current && !this.dirty) return;
|
361 | const gl = this.gl;
|
362 | gl.bindFramebuffer(gl.FRAMEBUFFER, v);
|
363 | this.current = v;
|
364 | this.dirty = false;
|
365 | }
|
366 | }
|
367 |
|
368 | export class BindRenderbuffer extends BaseValue<WebGLRenderbuffer> {
|
369 | getDefault(): WebGLRenderbuffer {
|
370 | return null;
|
371 | }
|
372 | set(v?: WebGLRenderbuffer | null) {
|
373 | if (v === this.current && !this.dirty) return;
|
374 | const gl = this.gl;
|
375 | gl.bindRenderbuffer(gl.RENDERBUFFER, v);
|
376 | this.current = v;
|
377 | this.dirty = false;
|
378 | }
|
379 | }
|
380 |
|
381 | export class BindTexture extends BaseValue<WebGLTexture> {
|
382 | getDefault(): WebGLTexture {
|
383 | return null;
|
384 | }
|
385 | set(v?: WebGLTexture | null) {
|
386 | if (v === this.current && !this.dirty) return;
|
387 | const gl = this.gl;
|
388 | gl.bindTexture(gl.TEXTURE_2D, v);
|
389 | this.current = v;
|
390 | this.dirty = false;
|
391 | }
|
392 | }
|
393 |
|
394 | export class BindVertexBuffer extends BaseValue<WebGLBuffer> {
|
395 | getDefault(): WebGLBuffer {
|
396 | return null;
|
397 | }
|
398 | set(v?: WebGLBuffer | null) {
|
399 | if (v === this.current && !this.dirty) return;
|
400 | const gl = this.gl;
|
401 | gl.bindBuffer(gl.ARRAY_BUFFER, v);
|
402 | this.current = v;
|
403 | this.dirty = false;
|
404 | }
|
405 | }
|
406 |
|
407 | export class BindElementBuffer extends BaseValue<WebGLBuffer> {
|
408 | getDefault(): WebGLBuffer {
|
409 | return null;
|
410 | }
|
411 | set(v?: WebGLBuffer | null) {
|
412 |
|
413 | const gl = this.gl;
|
414 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, v);
|
415 | this.current = v;
|
416 | this.dirty = false;
|
417 | }
|
418 | }
|
419 |
|
420 | export class BindVertexArrayOES extends BaseValue<any> {
|
421 | vao: any;
|
422 |
|
423 | constructor(context: Context) {
|
424 | super(context);
|
425 | this.vao = context.extVertexArrayObject;
|
426 | }
|
427 | getDefault(): any {
|
428 | return null;
|
429 | }
|
430 | set(v: any) {
|
431 | if (!this.vao || v === this.current && !this.dirty) return;
|
432 | this.vao.bindVertexArrayOES(v);
|
433 | this.current = v;
|
434 | this.dirty = false;
|
435 | }
|
436 | }
|
437 |
|
438 | export class PixelStoreUnpack extends BaseValue<number> {
|
439 | getDefault(): number {
|
440 | return 4;
|
441 | }
|
442 | set(v: number) {
|
443 | if (v === this.current && !this.dirty) return;
|
444 | const gl = this.gl;
|
445 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, v);
|
446 | this.current = v;
|
447 | this.dirty = false;
|
448 | }
|
449 | }
|
450 |
|
451 | export class PixelStoreUnpackPremultiplyAlpha extends BaseValue<boolean> {
|
452 | getDefault(): boolean {
|
453 | return false;
|
454 | }
|
455 | set(v: boolean): void {
|
456 | if (v === this.current && !this.dirty) return;
|
457 | const gl = this.gl;
|
458 | gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, ((v as any)));
|
459 | this.current = v;
|
460 | this.dirty = false;
|
461 | }
|
462 | }
|
463 |
|
464 | export class PixelStoreUnpackFlipY extends BaseValue<boolean> {
|
465 | getDefault(): boolean {
|
466 | return false;
|
467 | }
|
468 | set(v: boolean): void {
|
469 | if (v === this.current && !this.dirty) return;
|
470 | const gl = this.gl;
|
471 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, ((v as any)));
|
472 | this.current = v;
|
473 | this.dirty = false;
|
474 | }
|
475 | }
|
476 |
|
477 | class FramebufferAttachment<T> extends BaseValue<T> {
|
478 | parent: WebGLFramebuffer;
|
479 | context: Context;
|
480 |
|
481 | constructor(context: Context, parent: WebGLFramebuffer) {
|
482 | super(context);
|
483 | this.context = context;
|
484 | this.parent = parent;
|
485 | }
|
486 | getDefault() {
|
487 | return null;
|
488 | }
|
489 | }
|
490 |
|
491 | export class ColorAttachment extends FramebufferAttachment<WebGLTexture> {
|
492 | setDirty() {
|
493 | this.dirty = true;
|
494 | }
|
495 | set(v?: WebGLTexture | null): void {
|
496 | if (v === this.current && !this.dirty) return;
|
497 | this.context.bindFramebuffer.set(this.parent);
|
498 |
|
499 |
|
500 | const gl = this.gl;
|
501 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, v, 0);
|
502 | this.current = v;
|
503 | this.dirty = false;
|
504 | }
|
505 | }
|
506 |
|
507 | export class DepthAttachment extends FramebufferAttachment<WebGLRenderbuffer> {
|
508 | set(v?: WebGLRenderbuffer | null): void {
|
509 | if (v === this.current && !this.dirty) return;
|
510 | this.context.bindFramebuffer.set(this.parent);
|
511 |
|
512 |
|
513 | const gl = this.gl;
|
514 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, v);
|
515 | this.current = v;
|
516 | this.dirty = false;
|
517 | }
|
518 | }
|