@xeokit/xeokit-sdk 2.4.1 → 2.4.2-beta-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1830 @@
1
+ import {FrameContext} from './FrameContext.js';
2
+ import {math} from '../math/math.js';
3
+ import {stats} from '../stats.js';
4
+ import {WEBGL_INFO} from '../webglInfo.js';
5
+ import {Map} from "../utils/Map.js";
6
+ import {PickResult} from "./PickResult.js";
7
+ import {OcclusionTester} from "./occlusion/OcclusionTester.js";
8
+ import {SAOOcclusionRenderer} from "./sao/SAOOcclusionRenderer.js";
9
+ import {createRTCViewMat} from "../math/rtcCoords.js";
10
+ import {SAODepthLimitedBlurRenderer} from "./sao/SAODepthLimitedBlurRenderer.js";
11
+ import {RenderBufferManager} from "./RenderBufferManager.js";
12
+ import {getExtension} from "./getExtension.js";
13
+
14
+ /**
15
+ * @private
16
+ */
17
+ const Renderer = function (scene, options) {
18
+
19
+ options = options || {};
20
+
21
+ const frameCtx = new FrameContext(scene);
22
+ const canvas = scene.canvas.canvas;
23
+ /**
24
+ * @type {WebGL2RenderingContext}
25
+ */
26
+ const gl = scene.canvas.gl;
27
+ const canvasTransparent = (!!options.transparent);
28
+ const alphaDepthMask = options.alphaDepthMask;
29
+
30
+ const pickIDs = new Map({});
31
+
32
+ let drawableTypeInfo = {};
33
+ let drawables = {};
34
+
35
+ let drawableListDirty = true;
36
+ let stateSortDirty = true;
37
+ let imageDirty = true;
38
+ let shadowsDirty = true;
39
+
40
+ let transparentEnabled = true;
41
+ let edgesEnabled = true;
42
+ let saoEnabled = true;
43
+ let pbrEnabled = true;
44
+ let colorTextureEnabled = true;
45
+
46
+ const renderBufferManager = new RenderBufferManager(scene);
47
+
48
+ let snapshotBound = false;
49
+
50
+ const bindOutputFrameBuffer = null;
51
+ const unbindOutputFrameBuffer = null;
52
+
53
+ const saoOcclusionRenderer = new SAOOcclusionRenderer(scene);
54
+ const saoDepthLimitedBlurRenderer = new SAODepthLimitedBlurRenderer(scene);
55
+
56
+ this.scene = scene;
57
+
58
+ this._occlusionTester = null; // Lazy-created in #addMarker()
59
+
60
+ this.capabilities = {
61
+ astcSupported: !!getExtension(gl, 'WEBGL_compressed_texture_astc'),
62
+ etc1Supported: true, // WebGL2
63
+ etc2Supported: !!getExtension(gl, 'WEBGL_compressed_texture_etc'),
64
+ dxtSupported: !!getExtension(gl, 'WEBGL_compressed_texture_s3tc'),
65
+ bptcSupported: !!getExtension(gl, 'EXT_texture_compression_bptc'),
66
+ pvrtcSupported: !!(getExtension(gl, 'WEBGL_compressed_texture_pvrtc') || getExtension(gl, 'WEBKIT_WEBGL_compressed_texture_pvrtc'))
67
+ };
68
+
69
+ this.setTransparentEnabled = function (enabled) {
70
+ transparentEnabled = enabled;
71
+ imageDirty = true;
72
+ };
73
+
74
+ this.setEdgesEnabled = function (enabled) {
75
+ edgesEnabled = enabled;
76
+ imageDirty = true;
77
+ };
78
+
79
+ this.setSAOEnabled = function (enabled) {
80
+ saoEnabled = enabled;
81
+ imageDirty = true;
82
+ };
83
+
84
+ this.setPBREnabled = function (enabled) {
85
+ pbrEnabled = enabled;
86
+ imageDirty = true;
87
+ };
88
+
89
+ this.setColorTextureEnabled = function (enabled) {
90
+ colorTextureEnabled = enabled;
91
+ imageDirty = true;
92
+ };
93
+
94
+ this.needStateSort = function () {
95
+ stateSortDirty = true;
96
+ };
97
+
98
+ this.shadowsDirty = function () {
99
+ shadowsDirty = true;
100
+ };
101
+
102
+ this.imageDirty = function () {
103
+ imageDirty = true;
104
+ };
105
+
106
+ this.webglContextLost = function () {
107
+ };
108
+
109
+ this.webglContextRestored = function (gl) {
110
+
111
+ // renderBufferManager.webglContextRestored(gl);
112
+
113
+ saoOcclusionRenderer.init();
114
+ saoDepthLimitedBlurRenderer.init();
115
+
116
+ imageDirty = true;
117
+ };
118
+
119
+ /**
120
+ * Inserts a drawable into this renderer.
121
+ * @private
122
+ */
123
+ this.addDrawable = function (id, drawable) {
124
+ const type = drawable.type;
125
+ if (!type) {
126
+ console.error("Renderer#addDrawable() : drawable with ID " + id + " has no 'type' - ignoring");
127
+ return;
128
+ }
129
+ let drawableInfo = drawableTypeInfo[type];
130
+ if (!drawableInfo) {
131
+ drawableInfo = {
132
+ type: drawable.type,
133
+ count: 0,
134
+ isStateSortable: drawable.isStateSortable,
135
+ stateSortCompare: drawable.stateSortCompare,
136
+ drawableMap: {},
137
+ drawableListPreCull: [],
138
+ drawableList: []
139
+ };
140
+ drawableTypeInfo[type] = drawableInfo;
141
+ }
142
+ drawableInfo.count++;
143
+ drawableInfo.drawableMap[id] = drawable;
144
+ drawables[id] = drawable;
145
+ drawableListDirty = true;
146
+ };
147
+
148
+ /**
149
+ * Removes a drawable from this renderer.
150
+ * @private
151
+ */
152
+ this.removeDrawable = function (id) {
153
+ const drawable = drawables[id];
154
+ if (!drawable) {
155
+ console.error("Renderer#removeDrawable() : drawable not found with ID " + id + " - ignoring");
156
+ return;
157
+ }
158
+ const type = drawable.type;
159
+ const drawableInfo = drawableTypeInfo[type];
160
+ if (--drawableInfo.count <= 0) {
161
+ delete drawableTypeInfo[type];
162
+ } else {
163
+ delete drawableInfo.drawableMap[id];
164
+ }
165
+ delete drawables[id];
166
+ drawableListDirty = true;
167
+ };
168
+
169
+ /**
170
+ * Gets a unique pick ID for the given Pickable. A Pickable can be a {@link Mesh} or a {@link PerformanceMesh}.
171
+ * @returns {Number} New pick ID.
172
+ */
173
+ this.getPickID = function (entity) {
174
+ return pickIDs.addItem(entity);
175
+ };
176
+
177
+ /**
178
+ * Released a pick ID for reuse.
179
+ * @param {Number} pickID Pick ID to release.
180
+ */
181
+ this.putPickID = function (pickID) {
182
+ pickIDs.removeItem(pickID);
183
+ };
184
+
185
+ /**
186
+ * Clears the canvas.
187
+ * @private
188
+ */
189
+ this.clear = function (params) {
190
+ params = params || {};
191
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
192
+ if (canvasTransparent) {
193
+ gl.clearColor(1, 1, 1, 1);
194
+ } else {
195
+ const backgroundColor = scene.canvas.backgroundColorFromAmbientLight ? this.lights.getAmbientColorAndIntensity() : scene.canvas.backgroundColor;
196
+ gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0);
197
+ }
198
+ if (bindOutputFrameBuffer) {
199
+ bindOutputFrameBuffer(params.pass);
200
+ }
201
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
202
+ if (unbindOutputFrameBuffer) {
203
+ unbindOutputFrameBuffer(params.pass);
204
+ }
205
+ };
206
+
207
+ /**
208
+ * Returns true if the next call to render() will draw something
209
+ * @returns {Boolean}
210
+ */
211
+ this.needsRender = function () {
212
+ const needsRender = (imageDirty || drawableListDirty || stateSortDirty);
213
+ return needsRender;
214
+ }
215
+
216
+ /**
217
+ * Renders inserted drawables.
218
+ * @private
219
+ */
220
+ this.render = function (params) {
221
+ params = params || {};
222
+ if (params.force) {
223
+ imageDirty = true;
224
+ }
225
+ updateDrawlist();
226
+ if (imageDirty) {
227
+ draw(params);
228
+ stats.frame.frameCount++;
229
+ imageDirty = false;
230
+ }
231
+ };
232
+
233
+ function updateDrawlist() { // Prepares state-sorted array of drawables from maps of inserted drawables
234
+ if (drawableListDirty) {
235
+ buildDrawableList();
236
+ drawableListDirty = false;
237
+ stateSortDirty = true;
238
+ }
239
+ if (stateSortDirty) {
240
+ sortDrawableList();
241
+ stateSortDirty = false;
242
+ imageDirty = true;
243
+ }
244
+ if (imageDirty) { // Image is usually dirty because the camera moved
245
+ cullDrawableList();
246
+ }
247
+ }
248
+
249
+ function buildDrawableList() {
250
+ for (let type in drawableTypeInfo) {
251
+ if (drawableTypeInfo.hasOwnProperty(type)) {
252
+ const drawableInfo = drawableTypeInfo[type];
253
+ const drawableMap = drawableInfo.drawableMap;
254
+ const drawableListPreCull = drawableInfo.drawableListPreCull;
255
+ let lenDrawableList = 0;
256
+ for (let id in drawableMap) {
257
+ if (drawableMap.hasOwnProperty(id)) {
258
+ drawableListPreCull[lenDrawableList++] = drawableMap[id];
259
+ }
260
+ }
261
+ drawableListPreCull.length = lenDrawableList;
262
+ }
263
+ }
264
+ }
265
+
266
+ function sortDrawableList() {
267
+ for (let type in drawableTypeInfo) {
268
+ if (drawableTypeInfo.hasOwnProperty(type)) {
269
+ const drawableInfo = drawableTypeInfo[type];
270
+ if (drawableInfo.isStateSortable) {
271
+ drawableInfo.drawableListPreCull.sort(drawableInfo.stateSortCompare);
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ function cullDrawableList() {
278
+ for (let type in drawableTypeInfo) {
279
+ if (drawableTypeInfo.hasOwnProperty(type)) {
280
+ const drawableInfo = drawableTypeInfo[type];
281
+ const drawableListPreCull = drawableInfo.drawableListPreCull;
282
+ const drawableList = drawableInfo.drawableList;
283
+ let lenDrawableList = 0;
284
+ for (let i = 0, len = drawableListPreCull.length; i < len; i++) {
285
+ const drawable = drawableListPreCull[i];
286
+ drawable.rebuildRenderFlags();
287
+ if (!drawable.renderFlags.culled) {
288
+ drawableList[lenDrawableList++] = drawable;
289
+ }
290
+ }
291
+ drawableList.length = lenDrawableList;
292
+ }
293
+ }
294
+ }
295
+
296
+ function draw(params) {
297
+
298
+ const sao = scene.sao;
299
+
300
+ if (saoEnabled && sao.possible) {
301
+ drawSAOBuffers(params);
302
+ }
303
+
304
+ drawShadowMaps();
305
+
306
+ drawColor(params);
307
+ }
308
+
309
+ function drawSAOBuffers(params) {
310
+
311
+ const sao = scene.sao;
312
+
313
+ // Render depth buffer
314
+
315
+ const saoDepthRenderBuffer = renderBufferManager.getRenderBuffer("saoDepth", {
316
+ depthTexture: true
317
+ });
318
+
319
+ saoDepthRenderBuffer.bind();
320
+ saoDepthRenderBuffer.clear();
321
+ drawDepth(params);
322
+ saoDepthRenderBuffer.unbind();
323
+
324
+ // Render occlusion buffer
325
+
326
+ const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion");
327
+
328
+ occlusionRenderBuffer1.bind();
329
+ occlusionRenderBuffer1.clear();
330
+ saoOcclusionRenderer.render(saoDepthRenderBuffer);
331
+ occlusionRenderBuffer1.unbind();
332
+
333
+ if (sao.blur) {
334
+
335
+ // Horizontally blur occlusion buffer 1 into occlusion buffer 2
336
+
337
+ const occlusionRenderBuffer2 = renderBufferManager.getRenderBuffer("saoOcclusion2");
338
+
339
+ occlusionRenderBuffer2.bind();
340
+ occlusionRenderBuffer2.clear();
341
+ saoDepthLimitedBlurRenderer.render(saoDepthRenderBuffer, occlusionRenderBuffer1, 0);
342
+ occlusionRenderBuffer2.unbind();
343
+
344
+ // Vertically blur occlusion buffer 2 back into occlusion buffer 1
345
+
346
+ occlusionRenderBuffer1.bind();
347
+ occlusionRenderBuffer1.clear();
348
+ saoDepthLimitedBlurRenderer.render(saoDepthRenderBuffer, occlusionRenderBuffer2, 1);
349
+ occlusionRenderBuffer1.unbind();
350
+ }
351
+ }
352
+
353
+ function drawDepth(params) {
354
+
355
+ frameCtx.reset();
356
+ frameCtx.pass = params.pass;
357
+
358
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
359
+
360
+ gl.clearColor(0, 0, 0, 0);
361
+ gl.enable(gl.DEPTH_TEST);
362
+ gl.frontFace(gl.CCW);
363
+ gl.enable(gl.CULL_FACE);
364
+ gl.depthMask(true);
365
+
366
+ if (params.clear !== false) {
367
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
368
+ }
369
+
370
+ for (let type in drawableTypeInfo) {
371
+ if (drawableTypeInfo.hasOwnProperty(type)) {
372
+
373
+ const drawableInfo = drawableTypeInfo[type];
374
+ const drawableList = drawableInfo.drawableList;
375
+
376
+ for (let i = 0, len = drawableList.length; i < len; i++) {
377
+
378
+ const drawable = drawableList[i];
379
+
380
+ if (drawable.culled === true || drawable.visible === false || !drawable.drawDepth || !drawable.saoEnabled) {
381
+ continue;
382
+ }
383
+
384
+ if (drawable.renderFlags.colorOpaque) {
385
+ drawable.drawDepth(frameCtx);
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
392
+ // for (let ii = 0; ii < numVertexAttribs; ii++) {
393
+ // gl.disableVertexAttribArray(ii);
394
+ // }
395
+
396
+ }
397
+
398
+ function drawShadowMaps() {
399
+
400
+ let lights = scene._lightsState.lights;
401
+
402
+ for (let i = 0, len = lights.length; i < len; i++) {
403
+ const light = lights[i];
404
+ if (!light.castsShadow) {
405
+ continue;
406
+ }
407
+ drawShadowMap(light);
408
+ }
409
+
410
+ // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
411
+ // for (let ii = 0; ii < numVertexAttribs; ii++) {
412
+ // gl.disableVertexAttribArray(ii);
413
+ // }
414
+ //
415
+ shadowsDirty = false;
416
+ }
417
+
418
+ function drawShadowMap(light) {
419
+
420
+ const castsShadow = light.castsShadow;
421
+
422
+ if (!castsShadow) {
423
+ return;
424
+ }
425
+
426
+ const shadowRenderBuf = light.getShadowRenderBuf();
427
+
428
+ if (!shadowRenderBuf) {
429
+ return;
430
+ }
431
+
432
+ shadowRenderBuf.bind();
433
+
434
+ frameCtx.reset();
435
+
436
+ frameCtx.backfaces = true;
437
+ frameCtx.frontface = true;
438
+ frameCtx.shadowViewMatrix = light.getShadowViewMatrix();
439
+ frameCtx.shadowProjMatrix = light.getShadowProjMatrix();
440
+
441
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
442
+
443
+ gl.clearColor(0, 0, 0, 1);
444
+ gl.enable(gl.DEPTH_TEST);
445
+ gl.disable(gl.BLEND);
446
+
447
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
448
+
449
+ for (let type in drawableTypeInfo) {
450
+
451
+ if (drawableTypeInfo.hasOwnProperty(type)) {
452
+
453
+ const drawableInfo = drawableTypeInfo[type];
454
+ const drawableList = drawableInfo.drawableList;
455
+
456
+ for (let i = 0, len = drawableList.length; i < len; i++) {
457
+
458
+ const drawable = drawableList[i];
459
+
460
+ if (drawable.visible === false || !drawable.castsShadow || !drawable.drawShadow) {
461
+ continue;
462
+ }
463
+
464
+ if (drawable.renderFlags.colorOpaque) { // Transparent objects don't cast shadows (yet)
465
+ drawable.drawShadow(frameCtx);
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+ shadowRenderBuf.unbind();
472
+ }
473
+
474
+ function drawColor(params) {
475
+
476
+ const normalDrawSAOBin = [];
477
+ const normalEdgesOpaqueBin = [];
478
+ const normalFillTransparentBin = [];
479
+ const normalEdgesTransparentBin = [];
480
+
481
+ const xrayedFillOpaqueBin = [];
482
+ const xrayEdgesOpaqueBin = [];
483
+ const xrayedFillTransparentBin = [];
484
+ const xrayEdgesTransparentBin = [];
485
+
486
+ const highlightedFillOpaqueBin = [];
487
+ const highlightedEdgesOpaqueBin = [];
488
+ const highlightedFillTransparentBin = [];
489
+ const highlightedEdgesTransparentBin = [];
490
+
491
+ const selectedFillOpaqueBin = [];
492
+ const selectedEdgesOpaqueBin = [];
493
+ const selectedFillTransparentBin = [];
494
+ const selectedEdgesTransparentBin = [];
495
+
496
+
497
+ const ambientColorAndIntensity = scene._lightsState.getAmbientColorAndIntensity();
498
+
499
+ frameCtx.reset();
500
+ frameCtx.pass = params.pass;
501
+ frameCtx.withSAO = false;
502
+ frameCtx.pbrEnabled = pbrEnabled && !!scene.pbrEnabled;
503
+ frameCtx.colorTextureEnabled = colorTextureEnabled && !!scene.colorTextureEnabled;
504
+
505
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
506
+
507
+ if (canvasTransparent) {
508
+ gl.clearColor(0, 0, 0, 0);
509
+ } else {
510
+ const backgroundColor = scene.canvas.backgroundColorFromAmbientLight ? ambientColorAndIntensity : scene.canvas.backgroundColor;
511
+ gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0);
512
+ }
513
+
514
+ gl.enable(gl.DEPTH_TEST);
515
+ gl.frontFace(gl.CCW);
516
+ gl.enable(gl.CULL_FACE);
517
+ gl.depthMask(true);
518
+ gl.lineWidth(1);
519
+
520
+ frameCtx.lineWidth = 1;
521
+
522
+ const saoPossible = scene.sao.possible;
523
+
524
+ if (saoEnabled && saoPossible) {
525
+ const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion");
526
+ frameCtx.occlusionTexture = occlusionRenderBuffer1 ? occlusionRenderBuffer1.getTexture() : null;
527
+ } else {
528
+ frameCtx.occlusionTexture = null;
529
+
530
+ }
531
+
532
+ let i;
533
+ let len;
534
+ let drawable;
535
+
536
+ const startTime = Date.now();
537
+
538
+ if (bindOutputFrameBuffer) {
539
+ bindOutputFrameBuffer(params.pass);
540
+ }
541
+
542
+ if (params.clear !== false) {
543
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
544
+ }
545
+
546
+ let normalDrawSAOBinLen = 0;
547
+ let normalEdgesOpaqueBinLen = 0;
548
+ let normalFillTransparentBinLen = 0;
549
+ let normalEdgesTransparentBinLen = 0;
550
+
551
+ let xrayedFillOpaqueBinLen = 0;
552
+ let xrayEdgesOpaqueBinLen = 0;
553
+ let xrayedFillTransparentBinLen = 0;
554
+ let xrayEdgesTransparentBinLen = 0;
555
+
556
+ let highlightedFillOpaqueBinLen = 0;
557
+ let highlightedEdgesOpaqueBinLen = 0;
558
+ let highlightedFillTransparentBinLen = 0;
559
+ let highlightedEdgesTransparentBinLen = 0;
560
+
561
+ let selectedFillOpaqueBinLen = 0;
562
+ let selectedEdgesOpaqueBinLen = 0;
563
+ let selectedFillTransparentBinLen = 0;
564
+ let selectedEdgesTransparentBinLen = 0;
565
+
566
+ //------------------------------------------------------------------------------------------------------
567
+ // Render normal opaque solids, defer others to bins to render after
568
+ //------------------------------------------------------------------------------------------------------
569
+
570
+ for (let type in drawableTypeInfo) {
571
+ if (drawableTypeInfo.hasOwnProperty(type)) {
572
+
573
+ const drawableInfo = drawableTypeInfo[type];
574
+ const drawableList = drawableInfo.drawableList;
575
+
576
+ for (i = 0, len = drawableList.length; i < len; i++) {
577
+
578
+ drawable = drawableList[i];
579
+
580
+ if (drawable.culled === true || drawable.visible === false) {
581
+ continue;
582
+ }
583
+
584
+ const renderFlags = drawable.renderFlags;
585
+
586
+ if (renderFlags.colorOpaque) {
587
+ if (saoEnabled && saoPossible && drawable.saoEnabled) {
588
+ normalDrawSAOBin[normalDrawSAOBinLen++] = drawable;
589
+ } else {
590
+ drawable.drawColorOpaque(frameCtx);
591
+ }
592
+ }
593
+
594
+ if (transparentEnabled) {
595
+ if (renderFlags.colorTransparent) {
596
+ normalFillTransparentBin[normalFillTransparentBinLen++] = drawable;
597
+ }
598
+ }
599
+
600
+ if (renderFlags.xrayedSilhouetteTransparent) {
601
+ xrayedFillTransparentBin[xrayedFillTransparentBinLen++] = drawable;
602
+ }
603
+
604
+ if (renderFlags.xrayedSilhouetteOpaque) {
605
+ xrayedFillOpaqueBin[xrayedFillOpaqueBinLen++] = drawable;
606
+ }
607
+
608
+ if (renderFlags.highlightedSilhouetteTransparent) {
609
+ highlightedFillTransparentBin[highlightedFillTransparentBinLen++] = drawable;
610
+ }
611
+
612
+ if (renderFlags.highlightedSilhouetteOpaque) {
613
+ highlightedFillOpaqueBin[highlightedFillOpaqueBinLen++] = drawable;
614
+ }
615
+
616
+ if (renderFlags.selectedSilhouetteTransparent) {
617
+ selectedFillTransparentBin[selectedFillTransparentBinLen++] = drawable;
618
+ }
619
+
620
+ if (renderFlags.selectedSilhouetteOpaque) {
621
+ selectedFillOpaqueBin[selectedFillOpaqueBinLen++] = drawable;
622
+ }
623
+
624
+ if (edgesEnabled) {
625
+ if (renderFlags.edgesOpaque) {
626
+ normalEdgesOpaqueBin[normalEdgesOpaqueBinLen++] = drawable;
627
+ }
628
+
629
+ if (renderFlags.edgesTransparent) {
630
+ normalEdgesTransparentBin[normalEdgesTransparentBinLen++] = drawable;
631
+ }
632
+ }
633
+ if (renderFlags.selectedEdgesTransparent) {
634
+ selectedEdgesTransparentBin[selectedEdgesTransparentBinLen++] = drawable;
635
+ }
636
+
637
+ if (renderFlags.selectedEdgesOpaque) {
638
+ selectedEdgesOpaqueBin[selectedEdgesOpaqueBinLen++] = drawable;
639
+ }
640
+
641
+ if (renderFlags.xrayedEdgesTransparent) {
642
+ xrayEdgesTransparentBin[xrayEdgesTransparentBinLen++] = drawable;
643
+ }
644
+
645
+ if (renderFlags.xrayedEdgesOpaque) {
646
+ xrayEdgesOpaqueBin[xrayEdgesOpaqueBinLen++] = drawable;
647
+ }
648
+
649
+ if (renderFlags.highlightedEdgesTransparent) {
650
+ highlightedEdgesTransparentBin[highlightedEdgesTransparentBinLen++] = drawable;
651
+ }
652
+
653
+ if (renderFlags.highlightedEdgesOpaque) {
654
+ highlightedEdgesOpaqueBin[highlightedEdgesOpaqueBinLen++] = drawable;
655
+ }
656
+ }
657
+ }
658
+ }
659
+
660
+ //------------------------------------------------------------------------------------------------------
661
+ // Render deferred bins
662
+ //------------------------------------------------------------------------------------------------------
663
+
664
+ // Opaque color with SAO
665
+
666
+ if (normalDrawSAOBinLen > 0) {
667
+ frameCtx.withSAO = true;
668
+ for (i = 0; i < normalDrawSAOBinLen; i++) {
669
+ normalDrawSAOBin[i].drawColorOpaque(frameCtx);
670
+ }
671
+ }
672
+
673
+ // Opaque edges
674
+
675
+ if (normalEdgesOpaqueBinLen > 0) {
676
+ for (i = 0; i < normalEdgesOpaqueBinLen; i++) {
677
+ normalEdgesOpaqueBin[i].drawEdgesColorOpaque(frameCtx);
678
+ }
679
+ }
680
+
681
+ // Opaque X-ray fill
682
+
683
+ if (xrayedFillOpaqueBinLen > 0) {
684
+ for (i = 0; i < xrayedFillOpaqueBinLen; i++) {
685
+ xrayedFillOpaqueBin[i].drawSilhouetteXRayed(frameCtx);
686
+ }
687
+ }
688
+
689
+ // Opaque X-ray edges
690
+
691
+ if (xrayEdgesOpaqueBinLen > 0) {
692
+ for (i = 0; i < xrayEdgesOpaqueBinLen; i++) {
693
+ xrayEdgesOpaqueBin[i].drawEdgesXRayed(frameCtx);
694
+ }
695
+ }
696
+
697
+ // Transparent
698
+
699
+ if (xrayedFillTransparentBinLen > 0 || xrayEdgesTransparentBinLen > 0 || normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) {
700
+ gl.enable(gl.CULL_FACE);
701
+ gl.enable(gl.BLEND);
702
+ if (canvasTransparent) {
703
+ gl.blendEquation(gl.FUNC_ADD);
704
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
705
+ } else {
706
+ gl.blendEquation(gl.FUNC_ADD);
707
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
708
+ }
709
+ frameCtx.backfaces = false;
710
+ if (!alphaDepthMask) {
711
+ gl.depthMask(false);
712
+ }
713
+
714
+ // Transparent color edges
715
+
716
+ if (normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) {
717
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
718
+ }
719
+ if (normalEdgesTransparentBinLen > 0) {
720
+ for (i = 0; i < normalEdgesTransparentBinLen; i++) {
721
+ drawable = normalEdgesTransparentBin[i];
722
+ drawable.drawEdgesColorTransparent(frameCtx);
723
+ }
724
+ }
725
+
726
+ // Transparent color fill
727
+
728
+ if (normalFillTransparentBinLen > 0) {
729
+ for (i = 0; i < normalFillTransparentBinLen; i++) {
730
+ drawable = normalFillTransparentBin[i];
731
+ drawable.drawColorTransparent(frameCtx);
732
+ }
733
+ }
734
+
735
+ // Transparent X-ray edges
736
+
737
+ if (xrayEdgesTransparentBinLen > 0) {
738
+ for (i = 0; i < xrayEdgesTransparentBinLen; i++) {
739
+ xrayEdgesTransparentBin[i].drawEdgesXRayed(frameCtx);
740
+ }
741
+ }
742
+
743
+ // Transparent X-ray fill
744
+
745
+ if (xrayedFillTransparentBinLen > 0) {
746
+ for (i = 0; i < xrayedFillTransparentBinLen; i++) {
747
+ xrayedFillTransparentBin[i].drawSilhouetteXRayed(frameCtx);
748
+ }
749
+ }
750
+
751
+ gl.disable(gl.BLEND);
752
+ if (!alphaDepthMask) {
753
+ gl.depthMask(true);
754
+ }
755
+ }
756
+
757
+ // Opaque highlight
758
+
759
+ if (highlightedFillOpaqueBinLen > 0 || highlightedEdgesOpaqueBinLen > 0) {
760
+ frameCtx.lastProgramId = null;
761
+ if (scene.highlightMaterial.glowThrough) {
762
+ gl.clear(gl.DEPTH_BUFFER_BIT);
763
+ }
764
+
765
+ // Opaque highlighted edges
766
+
767
+ if (highlightedEdgesOpaqueBinLen > 0) {
768
+ for (i = 0; i < highlightedEdgesOpaqueBinLen; i++) {
769
+ highlightedEdgesOpaqueBin[i].drawEdgesHighlighted(frameCtx);
770
+ }
771
+ }
772
+
773
+ // Opaque highlighted fill
774
+
775
+ if (highlightedFillOpaqueBinLen > 0) {
776
+ for (i = 0; i < highlightedFillOpaqueBinLen; i++) {
777
+ highlightedFillOpaqueBin[i].drawSilhouetteHighlighted(frameCtx);
778
+ }
779
+ }
780
+ }
781
+
782
+ // Highlighted transparent
783
+
784
+ if (highlightedFillTransparentBinLen > 0 || highlightedEdgesTransparentBinLen > 0 || highlightedFillOpaqueBinLen > 0) {
785
+ frameCtx.lastProgramId = null;
786
+ if (scene.selectedMaterial.glowThrough) {
787
+ gl.clear(gl.DEPTH_BUFFER_BIT);
788
+ }
789
+ gl.enable(gl.BLEND);
790
+ if (canvasTransparent) {
791
+ gl.blendEquation(gl.FUNC_ADD);
792
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
793
+ } else {
794
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
795
+ }
796
+ gl.enable(gl.CULL_FACE);
797
+
798
+ // Highlighted transparent edges
799
+
800
+ if (highlightedEdgesTransparentBinLen > 0) {
801
+ for (i = 0; i < highlightedEdgesTransparentBinLen; i++) {
802
+ highlightedEdgesTransparentBin[i].drawEdgesHighlighted(frameCtx);
803
+ }
804
+ }
805
+
806
+ // Highlighted transparent fill
807
+
808
+ if (highlightedFillTransparentBinLen > 0) {
809
+ for (i = 0; i < highlightedFillTransparentBinLen; i++) {
810
+ highlightedFillTransparentBin[i].drawSilhouetteHighlighted(frameCtx);
811
+ }
812
+ }
813
+ gl.disable(gl.BLEND);
814
+ }
815
+
816
+ // Selected opaque
817
+
818
+ if (selectedFillOpaqueBinLen > 0 || selectedEdgesOpaqueBinLen > 0) {
819
+ frameCtx.lastProgramId = null;
820
+ if (scene.selectedMaterial.glowThrough) {
821
+ gl.clear(gl.DEPTH_BUFFER_BIT);
822
+ }
823
+
824
+ // Selected opaque fill
825
+
826
+ if (selectedEdgesOpaqueBinLen > 0) {
827
+ for (i = 0; i < selectedEdgesOpaqueBinLen; i++) {
828
+ selectedEdgesOpaqueBin[i].drawEdgesSelected(frameCtx);
829
+ }
830
+ }
831
+
832
+ // Selected opaque edges
833
+
834
+ if (selectedFillOpaqueBinLen > 0) {
835
+ for (i = 0; i < selectedFillOpaqueBinLen; i++) {
836
+ selectedFillOpaqueBin[i].drawSilhouetteSelected(frameCtx);
837
+ }
838
+ }
839
+ }
840
+
841
+ // Selected transparent
842
+
843
+ if (selectedFillTransparentBinLen > 0 || selectedEdgesTransparentBinLen > 0) {
844
+ frameCtx.lastProgramId = null;
845
+ if (scene.selectedMaterial.glowThrough) {
846
+ gl.clear(gl.DEPTH_BUFFER_BIT);
847
+ }
848
+ gl.enable(gl.CULL_FACE);
849
+ gl.enable(gl.BLEND);
850
+ if (canvasTransparent) {
851
+ gl.blendEquation(gl.FUNC_ADD);
852
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
853
+ } else {
854
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
855
+ }
856
+
857
+ // Selected transparent edges
858
+
859
+ if (selectedEdgesTransparentBinLen > 0) {
860
+ for (i = 0; i < selectedEdgesTransparentBinLen; i++) {
861
+ selectedEdgesTransparentBin[i].drawEdgesSelected(frameCtx);
862
+ }
863
+ }
864
+
865
+ // Selected transparent fill
866
+
867
+ if (selectedFillTransparentBinLen > 0) {
868
+ for (i = 0; i < selectedFillTransparentBinLen; i++) {
869
+ selectedFillTransparentBin[i].drawSilhouetteSelected(frameCtx);
870
+ }
871
+ }
872
+ gl.disable(gl.BLEND);
873
+ }
874
+
875
+ const endTime = Date.now();
876
+ const frameStats = stats.frame;
877
+
878
+ frameStats.renderTime = (endTime - startTime) / 1000.0;
879
+ frameStats.drawElements = frameCtx.drawElements;
880
+ frameStats.drawArrays = frameCtx.drawArrays;
881
+ frameStats.useProgram = frameCtx.useProgram;
882
+ frameStats.bindTexture = frameCtx.bindTexture;
883
+ frameStats.bindArray = frameCtx.bindArray;
884
+
885
+ const numTextureUnits = WEBGL_INFO.MAX_TEXTURE_IMAGE_UNITS;
886
+ for (let ii = 0; ii < numTextureUnits; ii++) {
887
+ gl.activeTexture(gl.TEXTURE0 + ii);
888
+ }
889
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
890
+ gl.bindTexture(gl.TEXTURE_2D, null);
891
+
892
+ const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
893
+ for (let ii = 0; ii < numVertexAttribs; ii++) {
894
+ gl.disableVertexAttribArray(ii);
895
+ }
896
+
897
+ if (unbindOutputFrameBuffer) {
898
+ unbindOutputFrameBuffer(params.pass);
899
+ }
900
+ }
901
+
902
+ /**
903
+ * Picks an Entity.
904
+ * @private
905
+ */
906
+ this.pick = (function () {
907
+
908
+ const tempVec3a = math.vec3();
909
+ const tempMat4a = math.mat4();
910
+ const tempMat4b = math.mat4();
911
+
912
+ const randomVec3 = math.vec3();
913
+ const up = math.vec3([0, 1, 0]);
914
+ const _pickResult = new PickResult();
915
+
916
+ const nearAndFar = math.vec2();
917
+
918
+ const canvasPos = math.vec3();
919
+
920
+ const worldRayOrigin = math.vec3();
921
+ const worldRayDir = math.vec3();
922
+ const worldSurfacePos = math.vec3();
923
+ const worldSurfaceNormal = math.vec3();
924
+
925
+ return function (params, pickResult = _pickResult) {
926
+
927
+ pickResult.reset();
928
+
929
+ updateDrawlist();
930
+
931
+ let look;
932
+ let pickViewMatrix = null;
933
+ let pickProjMatrix = null;
934
+
935
+ pickResult.pickSurface = params.pickSurface;
936
+
937
+ if (params.canvasPos) {
938
+
939
+ canvasPos[0] = params.canvasPos[0];
940
+ canvasPos[1] = params.canvasPos[1];
941
+
942
+ pickViewMatrix = scene.camera.viewMatrix;
943
+ pickProjMatrix = scene.camera.projMatrix;
944
+
945
+ pickResult.canvasPos = params.canvasPos;
946
+
947
+ } else {
948
+
949
+ // Picking with arbitrary World-space ray
950
+ // Align camera along ray and fire ray through center of canvas
951
+
952
+ const pickFrustumMatrix = math.frustumMat4(-1, 1, -1, 1, 0.01, scene.camera.project.far, tempMat4a);
953
+
954
+ if (params.matrix) {
955
+
956
+ pickViewMatrix = params.matrix;
957
+ pickProjMatrix = pickFrustumMatrix;
958
+
959
+ } else {
960
+
961
+ worldRayOrigin.set(params.origin || [0, 0, 0]);
962
+ worldRayDir.set(params.direction || [0, 0, 1]);
963
+
964
+ look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a);
965
+
966
+ randomVec3[0] = Math.random();
967
+ randomVec3[1] = Math.random();
968
+ randomVec3[2] = Math.random();
969
+
970
+ math.normalizeVec3(randomVec3);
971
+ math.cross3Vec3(worldRayDir, randomVec3, up);
972
+
973
+ pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b);
974
+ pickProjMatrix = pickFrustumMatrix;
975
+
976
+ pickResult.origin = worldRayOrigin;
977
+ pickResult.direction = worldRayDir;
978
+ }
979
+
980
+ canvasPos[0] = canvas.clientWidth * 0.5;
981
+ canvasPos[1] = canvas.clientHeight * 0.5;
982
+ }
983
+
984
+ for (let type in drawableTypeInfo) {
985
+ if (drawableTypeInfo.hasOwnProperty(type)) {
986
+ const drawableList = drawableTypeInfo[type].drawableList;
987
+ for (let i = 0, len = drawableList.length; i < len; i++) {
988
+ const drawable = drawableList[i];
989
+ if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
990
+ drawable.setPickMatrices(pickViewMatrix, pickProjMatrix);
991
+ }
992
+ }
993
+ }
994
+ }
995
+
996
+ const pickBuffer = renderBufferManager.getRenderBuffer("pick");
997
+
998
+ pickBuffer.bind();
999
+
1000
+ const pickable = gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult);
1001
+
1002
+ if (!pickable) {
1003
+ pickBuffer.unbind();
1004
+ return null;
1005
+ }
1006
+
1007
+ const pickedEntity = (pickable.delegatePickedEntity) ? pickable.delegatePickedEntity() : pickable;
1008
+
1009
+ if (!pickedEntity) {
1010
+ pickBuffer.unbind();
1011
+ return null;
1012
+ }
1013
+
1014
+ if (params.pickSurface) {
1015
+
1016
+ // GPU-based ray-picking
1017
+
1018
+ if (pickable.canPickTriangle && pickable.canPickTriangle()) {
1019
+
1020
+ gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1021
+
1022
+ pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, pickResult);
1023
+
1024
+ pickResult.pickSurfacePrecision = false;
1025
+
1026
+ } else {
1027
+
1028
+ if (pickable.canPickWorldPos && pickable.canPickWorldPos()) {
1029
+
1030
+ nearAndFar[0] = scene.camera.project.near;
1031
+ nearAndFar[1] = scene.camera.project.far;
1032
+
1033
+ gpuPickWorldPos(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult);
1034
+
1035
+ if (params.pickSurfaceNormal !== false) {
1036
+ gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1037
+ }
1038
+
1039
+ pickResult.pickSurfacePrecision = false;
1040
+ }
1041
+ }
1042
+ }
1043
+ pickBuffer.unbind();
1044
+ pickResult.entity = pickedEntity;
1045
+ return pickResult;
1046
+ };
1047
+ })();
1048
+
1049
+ function gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult) {
1050
+
1051
+ frameCtx.reset();
1052
+ frameCtx.backfaces = true;
1053
+ frameCtx.frontface = true; // "ccw"
1054
+ frameCtx.pickOrigin = pickResult.origin;
1055
+ frameCtx.pickViewMatrix = pickViewMatrix;
1056
+ frameCtx.pickProjMatrix = pickProjMatrix;
1057
+ frameCtx.pickInvisible = !!params.pickInvisible;
1058
+
1059
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1060
+
1061
+ gl.clearColor(0, 0, 0, 0);
1062
+ gl.depthMask(true);
1063
+ gl.enable(gl.DEPTH_TEST);
1064
+ gl.disable(gl.CULL_FACE);
1065
+ gl.disable(gl.BLEND);
1066
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1067
+
1068
+ const includeEntityIds = params.includeEntityIds;
1069
+ const excludeEntityIds = params.excludeEntityIds;
1070
+
1071
+ for (let type in drawableTypeInfo) {
1072
+ if (drawableTypeInfo.hasOwnProperty(type)) {
1073
+
1074
+ const drawableInfo = drawableTypeInfo[type];
1075
+ const drawableList = drawableInfo.drawableList;
1076
+
1077
+ for (let i = 0, len = drawableList.length; i < len; i++) {
1078
+
1079
+ const drawable = drawableList[i];
1080
+
1081
+ if (!drawable.drawPickMesh || (params.pickInvisible !== true && drawable.visible === false) || drawable.pickable === false) {
1082
+ continue;
1083
+ }
1084
+ if (includeEntityIds && !includeEntityIds[drawable.id]) { // TODO: push this logic into drawable
1085
+ continue;
1086
+ }
1087
+ if (excludeEntityIds && excludeEntityIds[drawable.id]) {
1088
+ continue;
1089
+ }
1090
+
1091
+ drawable.drawPickMesh(frameCtx);
1092
+ }
1093
+ }
1094
+ }
1095
+ const resolutionScale = scene.canvas.resolutionScale;
1096
+ const pix = pickBuffer.read(Math.round(canvasPos[0] * resolutionScale), Math.round(canvasPos[1] * resolutionScale));
1097
+ let pickID = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256);
1098
+
1099
+ if (pickID < 0) {
1100
+ return;
1101
+ }
1102
+
1103
+ const pickable = pickIDs.items[pickID];
1104
+
1105
+ return pickable;
1106
+ }
1107
+
1108
+ function gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1109
+
1110
+ if (!pickable.drawPickTriangles) {
1111
+ return;
1112
+ }
1113
+
1114
+ frameCtx.reset();
1115
+ frameCtx.backfaces = true;
1116
+ frameCtx.frontface = true; // "ccw"
1117
+ frameCtx.pickOrigin = pickResult.origin;
1118
+ frameCtx.pickViewMatrix = pickViewMatrix; // Can be null
1119
+ frameCtx.pickProjMatrix = pickProjMatrix; // Can be null
1120
+ // frameCtx.pickInvisible = !!params.pickInvisible;
1121
+
1122
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1123
+
1124
+ gl.clearColor(0, 0, 0, 0);
1125
+ gl.enable(gl.DEPTH_TEST);
1126
+ gl.disable(gl.CULL_FACE);
1127
+ gl.disable(gl.BLEND);
1128
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1129
+
1130
+ pickable.drawPickTriangles(frameCtx);
1131
+
1132
+ const resolutionScale = scene.canvas.resolutionScale;
1133
+ const pix = pickBuffer.read(Math.round(canvasPos[0] * resolutionScale), Math.round(canvasPos[1] * resolutionScale));
1134
+
1135
+ let primIndex = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256);
1136
+
1137
+ primIndex *= 3; // Convert from triangle number to first vertex in indices
1138
+
1139
+ pickResult.primIndex = primIndex;
1140
+ }
1141
+
1142
+ const gpuPickWorldPos = (function () {
1143
+
1144
+ const tempVec4a = math.vec4();
1145
+ const tempVec4b = math.vec4();
1146
+ const tempVec4c = math.vec4();
1147
+ const tempVec4d = math.vec4();
1148
+ const tempVec4e = math.vec4();
1149
+ const tempMat4a = math.mat4();
1150
+ const tempMat4b = math.mat4();
1151
+ const tempMat4c = math.mat4();
1152
+
1153
+ return function (pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult) {
1154
+
1155
+ frameCtx.reset();
1156
+ frameCtx.backfaces = true;
1157
+ frameCtx.frontface = true; // "ccw"
1158
+ frameCtx.pickOrigin = pickResult.origin;
1159
+ frameCtx.pickViewMatrix = pickViewMatrix;
1160
+ frameCtx.pickProjMatrix = pickProjMatrix;
1161
+ frameCtx.pickZNear = nearAndFar[0];
1162
+ frameCtx.pickZFar = nearAndFar[1];
1163
+ frameCtx.pickElementsCount = pickable.pickElementsCount;
1164
+ frameCtx.pickElementsOffset = pickable.pickElementsOffset;
1165
+
1166
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1167
+
1168
+ gl.clearColor(0, 0, 0, 0);
1169
+ gl.depthMask(true);
1170
+ gl.enable(gl.DEPTH_TEST);
1171
+ gl.disable(gl.CULL_FACE);
1172
+ gl.disable(gl.BLEND);
1173
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1174
+
1175
+ pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths
1176
+
1177
+ const resolutionScale = scene.canvas.resolutionScale;
1178
+ const pix = pickBuffer.read(Math.round(canvasPos[0] * resolutionScale), Math.round(canvasPos[1] * resolutionScale));
1179
+
1180
+ const screenZ = unpackDepth(pix); // Get screen-space Z at the given canvas coords
1181
+
1182
+ // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top
1183
+ const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2);
1184
+ const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2);
1185
+
1186
+ const origin = pickable.origin;
1187
+ let pvMat;
1188
+
1189
+ if (origin) {
1190
+ const rtcPickViewMat = createRTCViewMat(pickViewMatrix, origin, tempMat4a);
1191
+ pvMat = math.mulMat4(pickProjMatrix, rtcPickViewMat, tempMat4b);
1192
+
1193
+ } else {
1194
+ pvMat = math.mulMat4(pickProjMatrix, pickViewMatrix, tempMat4b);
1195
+ }
1196
+
1197
+ const pvMatInverse = math.inverseMat4(pvMat, tempMat4c);
1198
+
1199
+ tempVec4a[0] = x;
1200
+ tempVec4a[1] = y;
1201
+ tempVec4a[2] = -1;
1202
+ tempVec4a[3] = 1;
1203
+
1204
+ let world1 = math.transformVec4(pvMatInverse, tempVec4a);
1205
+ world1 = math.mulVec4Scalar(world1, 1 / world1[3]);
1206
+
1207
+ tempVec4b[0] = x;
1208
+ tempVec4b[1] = y;
1209
+ tempVec4b[2] = 1;
1210
+ tempVec4b[3] = 1;
1211
+
1212
+ let world2 = math.transformVec4(pvMatInverse, tempVec4b);
1213
+ world2 = math.mulVec4Scalar(world2, 1 / world2[3]);
1214
+
1215
+ const dir = math.subVec3(world2, world1, tempVec4c);
1216
+ const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e);
1217
+
1218
+ if (origin) {
1219
+ math.addVec3(worldPos, origin);
1220
+ }
1221
+
1222
+ pickResult.worldPos = worldPos;
1223
+ }
1224
+ })();
1225
+
1226
+ function snapInitDepthBuf(frameCtx) {
1227
+ frameCtx.snapPickLayerParams = [];
1228
+ frameCtx.snapPickLayerNumber = 0;
1229
+ for (let type in drawableTypeInfo) {
1230
+ const drawableInfo = drawableTypeInfo[type];
1231
+ const drawableList = drawableInfo.drawableList;
1232
+ for (let i = 0, len = drawableList.length; i < len; i++) {
1233
+ const drawable = drawableList[i];
1234
+ if (drawable.drawSnapInitDepthBuf) {
1235
+ if (!drawable.culled && drawable.visible && drawable.pickable) {
1236
+ drawable.drawSnapInitDepthBuf(frameCtx);
1237
+ }
1238
+ }
1239
+ }
1240
+ }
1241
+ return frameCtx.snapPickLayerParams;
1242
+ }
1243
+
1244
+ function snapPickDrawSnapDepths(frameCtx) {
1245
+ frameCtx.snapPickLayerParams = frameCtx.snapPickLayerParams || [];
1246
+ frameCtx.snapPickLayerNumber = frameCtx.snapPickLayerParams.length;
1247
+ for (let type in drawableTypeInfo) {
1248
+ const drawableInfo = drawableTypeInfo[type];
1249
+ const drawableList = drawableInfo.drawableList;
1250
+ for (let i = 0, len = drawableList.length; i < len; i++) {
1251
+ const drawable = drawableList[i];
1252
+ if (drawable.drawSnapDepths) {
1253
+ if (!drawable.culled && drawable.visible && drawable.pickable) {
1254
+ drawable.drawSnapDepths(frameCtx);
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ return frameCtx.snapPickLayerParams;
1260
+ }
1261
+
1262
+ function getClipPosX(pos, size) {
1263
+ return 2 * (pos / size) - 1;
1264
+ }
1265
+
1266
+ function getClipPosY(pos, size) {
1267
+ return 1 - 2 * (pos / size);
1268
+ }
1269
+
1270
+ /**
1271
+ * @param {[number, number]} canvasPos
1272
+ * @param {number} [snapRadiusInPixels=30]
1273
+ * @param {boolean} [snapVertex=true]
1274
+ * @param {boolean} [snapEdge=true]
1275
+ *
1276
+ * @returns {{worldPos:number[],snappedWorldPos:null|number[],snappedCanvasPos:null|number[], snapType:null|"vertex"|"edge"}}
1277
+ */
1278
+ this.snapPick = function (canvasPos, snapRadiusInPixels = 30, snapVertex = true, snapEdge = true) {
1279
+
1280
+ if (!snapVertex && !snapEdge) {
1281
+ return this.pick({canvasPos, pickSurface: true});
1282
+ }
1283
+
1284
+ frameCtx.reset();
1285
+ frameCtx.backfaces = true;
1286
+ frameCtx.frontface = true; // "ccw"
1287
+ frameCtx.pickZNear = scene.camera.project.near;
1288
+ frameCtx.pickZFar = scene.camera.project.far;
1289
+
1290
+ let vertexPickBuffer = renderBufferManager.getRenderBuffer("uniquePickColors-aabs", {
1291
+ depthTexture: true,
1292
+ size: [
1293
+ 2 * snapRadiusInPixels + 1,
1294
+ 2 * snapRadiusInPixels + 1,
1295
+ ]
1296
+ });
1297
+
1298
+ frameCtx.snapVectorA = [
1299
+ getClipPosX(canvasPos[0], gl.drawingBufferWidth),
1300
+ getClipPosY(canvasPos[1], gl.drawingBufferHeight),
1301
+ ];
1302
+
1303
+ frameCtx.snapInvVectorAB = [
1304
+ gl.drawingBufferWidth / (2 * snapRadiusInPixels),
1305
+ gl.drawingBufferHeight / (2 * snapRadiusInPixels),
1306
+ ];
1307
+
1308
+ // Bind and clear the snap render target
1309
+
1310
+ vertexPickBuffer.bind(gl.RGBA32I);
1311
+ gl.viewport(0, 0, vertexPickBuffer.size[0], vertexPickBuffer.size[1]);
1312
+ gl.enable(gl.DEPTH_TEST);
1313
+ gl.frontFace(gl.CCW);
1314
+ gl.disable(gl.CULL_FACE);
1315
+ gl.depthMask(true);
1316
+ gl.disable(gl.BLEND);
1317
+ gl.depthFunc(gl.LESS);
1318
+ gl.clear(gl.DEPTH_BUFFER_BIT);
1319
+ gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0]));
1320
+
1321
+ //////////////////////////////////
1322
+ // Set view and proj mats for VBO renderers
1323
+ ///////////////////////////////////////
1324
+
1325
+ const pickViewMatrix = scene.camera.viewMatrix;
1326
+ const pickProjMatrix = scene.camera.projMatrix;
1327
+
1328
+ for (let type in drawableTypeInfo) {
1329
+ if (drawableTypeInfo.hasOwnProperty(type)) {
1330
+ const drawableList = drawableTypeInfo[type].drawableList;
1331
+ for (let i = 0, len = drawableList.length; i < len; i++) {
1332
+ const drawable = drawableList[i];
1333
+ if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
1334
+ drawable.setPickMatrices(pickViewMatrix, pickProjMatrix);
1335
+ }
1336
+ }
1337
+ }
1338
+ }
1339
+
1340
+ // a) init z-buffer
1341
+ const layerParamsSurface = snapInitDepthBuf(frameCtx);
1342
+
1343
+ // b) snap-pick
1344
+ const layerParamsSnap = []
1345
+ frameCtx.snapPickLayerParams = layerParamsSnap;
1346
+
1347
+ gl.depthMask(false);
1348
+
1349
+ if (snapVertex && snapEdge) {
1350
+ frameCtx.snapMode = "edge";
1351
+ snapPickDrawSnapDepths(frameCtx);
1352
+
1353
+ frameCtx.snapMode = "vertex";
1354
+ frameCtx.snapPickLayerNumber++;
1355
+
1356
+ snapPickDrawSnapDepths(frameCtx);
1357
+ } else {
1358
+ frameCtx.snapMode = snapVertex ? "vertex" : "edge";
1359
+
1360
+ snapPickDrawSnapDepths(frameCtx);
1361
+ }
1362
+
1363
+ gl.depthMask(true);
1364
+
1365
+ // Read and decode the snapped coordinates
1366
+
1367
+ const snapPickResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1368
+
1369
+ vertexPickBuffer.unbind();
1370
+
1371
+ drawSnapDebbug(vertexPickBuffer.buffer.texture, layerParamsSurface.length);
1372
+
1373
+ // result 1) regular hi-precision world position
1374
+
1375
+ let worldPos = null;
1376
+
1377
+ const middleX = snapRadiusInPixels;
1378
+ const middleY = snapRadiusInPixels;
1379
+ const middleIndex = (middleX * 4) + (middleY * vertexPickBuffer.size[0] * 4);
1380
+ const pickResultMiddleXY = snapPickResultArray.slice(middleIndex, middleIndex + 4);
1381
+
1382
+ if (pickResultMiddleXY[3] !== 0) {
1383
+ const pickedLayerParmasSurface = layerParamsSurface[Math.abs(pickResultMiddleXY[3]) % layerParamsSurface.length];
1384
+ const origin = pickedLayerParmasSurface.origin;
1385
+ const scale = pickedLayerParmasSurface.coordinateScale;
1386
+ worldPos = [
1387
+ pickResultMiddleXY[0] * scale[0] + origin[0],
1388
+ pickResultMiddleXY[1] * scale[1] + origin[1],
1389
+ pickResultMiddleXY[2] * scale[2] + origin[2],
1390
+ ];
1391
+ }
1392
+
1393
+ // result 2) hi-precision snapped (to vertex/edge) world position
1394
+
1395
+ let snapPickResult = [];
1396
+
1397
+ for (let i = 0; i < snapPickResultArray.length; i += 4) {
1398
+ if (snapPickResultArray[i + 3] > 0) {
1399
+ const pixelNumber = Math.floor(i / 4);
1400
+ const w = vertexPickBuffer.size[0];
1401
+ const x = pixelNumber % w - Math.floor(w / 2);
1402
+ const y = Math.floor(pixelNumber / w) - Math.floor(w / 2);
1403
+ const dist = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
1404
+ snapPickResult.push({
1405
+ x,
1406
+ y,
1407
+ dist,
1408
+ isVertex: snapVertex && snapEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapVertex,
1409
+ result: [
1410
+ snapPickResultArray[i + 0],
1411
+ snapPickResultArray[i + 1],
1412
+ snapPickResultArray[i + 2],
1413
+ snapPickResultArray[i + 3],
1414
+ ]
1415
+ });
1416
+ }
1417
+ }
1418
+
1419
+ let snappedWorldPos = null;
1420
+ let snapType = null;
1421
+
1422
+ if (snapPickResult.length > 0) {
1423
+ // vertex snap first, then edge snap
1424
+ snapPickResult.sort((a, b) => {
1425
+ if (a.isVertex !== b.isVertex) {
1426
+ return a.isVertex ? -1 : 1;
1427
+ } else {
1428
+ return a.dist - b.dist;
1429
+ }
1430
+ });
1431
+
1432
+ snapType = snapPickResult[0].isVertex ? "vertex" : "edge";
1433
+ snapPickResult = snapPickResult[0].result;
1434
+
1435
+ const pickedLayerParmas = layerParamsSnap[snapPickResult[3]];
1436
+
1437
+ const origin = pickedLayerParmas.origin;
1438
+ const scale = pickedLayerParmas.coordinateScale;
1439
+
1440
+ snappedWorldPos = [
1441
+ snapPickResult[0] * scale[0] + origin[0],
1442
+ snapPickResult[1] * scale[1] + origin[1],
1443
+ snapPickResult[2] * scale[2] + origin[2],
1444
+ ];
1445
+ }
1446
+
1447
+ if (null === worldPos && null == snappedWorldPos) { // If neither regular pick or snap pick, return null
1448
+ return null;
1449
+ }
1450
+
1451
+ let snappedCanvasPos = null;
1452
+
1453
+ if (null !== snappedWorldPos) {
1454
+ snappedCanvasPos = scene.camera.projectWorldPos(snappedWorldPos);
1455
+ }
1456
+
1457
+ return {
1458
+ snapType,
1459
+ snappedToVertex: snapType === "vertex",
1460
+ snappedToEdge: snapType === "edge",
1461
+ worldPos,
1462
+ snappedWorldPos,
1463
+ snappedCanvasPos
1464
+ };
1465
+ };
1466
+
1467
+ function unpackDepth(depthZ) {
1468
+ const vec = [depthZ[0] / 256.0, depthZ[1] / 256.0, depthZ[2] / 256.0, depthZ[3] / 256.0];
1469
+ const bitShift = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0];
1470
+ return math.dotVec4(vec, bitShift);
1471
+ }
1472
+
1473
+ function gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1474
+
1475
+ frameCtx.reset();
1476
+ frameCtx.backfaces = true;
1477
+ frameCtx.frontface = true; // "ccw"
1478
+ frameCtx.pickOrigin = pickResult.origin;
1479
+ frameCtx.pickViewMatrix = pickViewMatrix;
1480
+ frameCtx.pickProjMatrix = pickProjMatrix;
1481
+
1482
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1483
+
1484
+ gl.clearColor(0, 0, 0, 0);
1485
+ gl.enable(gl.DEPTH_TEST);
1486
+ gl.disable(gl.CULL_FACE);
1487
+ gl.disable(gl.BLEND);
1488
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1489
+
1490
+ pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals
1491
+
1492
+ const resolutionScale = scene.canvas.resolutionScale;
1493
+ const pix = pickBuffer.read(Math.round(canvasPos[0] * resolutionScale), Math.round(canvasPos[1] * resolutionScale));
1494
+
1495
+ const worldNormal = [(pix[0] / 256.0) - 0.5, (pix[1] / 256.0) - 0.5, (pix[2] / 256.0) - 0.5];
1496
+
1497
+ math.normalizeVec3(worldNormal);
1498
+
1499
+ pickResult.worldNormal = worldNormal;
1500
+ }
1501
+
1502
+ /**
1503
+ * Adds a {@link Marker} for occlusion testing.
1504
+ * @param marker
1505
+ */
1506
+ this.addMarker = function (marker) {
1507
+ this._occlusionTester = this._occlusionTester || new OcclusionTester(scene, renderBufferManager);
1508
+ this._occlusionTester.addMarker(marker);
1509
+ scene.occlusionTestCountdown = 0;
1510
+ };
1511
+
1512
+ /**
1513
+ * Notifies that a {@link Marker#worldPos} has updated.
1514
+ * @param marker
1515
+ */
1516
+ this.markerWorldPosUpdated = function (marker) {
1517
+ this._occlusionTester.markerWorldPosUpdated(marker);
1518
+ };
1519
+
1520
+ /**
1521
+ * Removes a {@link Marker} from occlusion testing.
1522
+ * @param marker
1523
+ */
1524
+ this.removeMarker = function (marker) {
1525
+ this._occlusionTester.removeMarker(marker);
1526
+ };
1527
+
1528
+ /**
1529
+ * Performs an occlusion test for all added {@link Marker}s, updating
1530
+ * their {@link Marker#visible} properties accordingly.
1531
+ */
1532
+ this.doOcclusionTest = function () {
1533
+
1534
+ if (this._occlusionTester && this._occlusionTester.needOcclusionTest) {
1535
+
1536
+ updateDrawlist();
1537
+
1538
+ this._occlusionTester.bindRenderBuf();
1539
+
1540
+ frameCtx.reset();
1541
+ frameCtx.backfaces = true;
1542
+ frameCtx.frontface = true; // "ccw"
1543
+
1544
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1545
+ gl.clearColor(0, 0, 0, 0);
1546
+ gl.enable(gl.DEPTH_TEST);
1547
+ gl.disable(gl.CULL_FACE);
1548
+ gl.disable(gl.BLEND);
1549
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1550
+
1551
+ for (let type in drawableTypeInfo) {
1552
+ if (drawableTypeInfo.hasOwnProperty(type)) {
1553
+ const drawableInfo = drawableTypeInfo[type];
1554
+ const drawableList = drawableInfo.drawableList;
1555
+ for (let i = 0, len = drawableList.length; i < len; i++) {
1556
+ const drawable = drawableList[i];
1557
+ if (!drawable.drawOcclusion || drawable.culled === true || drawable.visible === false || drawable.pickable === false) { // TODO: Option to exclude transparent?
1558
+ continue;
1559
+ }
1560
+
1561
+ drawable.drawOcclusion(frameCtx);
1562
+ }
1563
+ }
1564
+ }
1565
+
1566
+ this._occlusionTester.drawMarkers(frameCtx);
1567
+ this._occlusionTester.doOcclusionTest(); // Updates Marker "visible" properties
1568
+ this._occlusionTester.unbindRenderBuf();
1569
+ }
1570
+ };
1571
+
1572
+ /**
1573
+ * Read pixels from the renderer's current output. Performs a force-render first.
1574
+ * @param pixels
1575
+ * @param colors
1576
+ * @param len
1577
+ * @param opaqueOnly
1578
+ * @private
1579
+ */
1580
+ this.readPixels = function (pixels, colors, len, opaqueOnly) {
1581
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1582
+ snapshotBuffer.bind();
1583
+ snapshotBuffer.clear();
1584
+ this.render({force: true, opaqueOnly: opaqueOnly});
1585
+ let color;
1586
+ let i;
1587
+ let j;
1588
+ let k;
1589
+ for (i = 0; i < len; i++) {
1590
+ j = i * 2;
1591
+ k = i * 4;
1592
+ color = snapshotBuffer.read(pixels[j], pixels[j + 1]);
1593
+ colors[k] = color[0];
1594
+ colors[k + 1] = color[1];
1595
+ colors[k + 2] = color[2];
1596
+ colors[k + 3] = color[3];
1597
+ }
1598
+ snapshotBuffer.unbind();
1599
+ imageDirty = true;
1600
+ };
1601
+
1602
+ /**
1603
+ * Enter snapshot mode.
1604
+ *
1605
+ * Switches rendering to a hidden snapshot canvas.
1606
+ *
1607
+ * Exit snapshot mode using endSnapshot().
1608
+ */
1609
+ this.beginSnapshot = function (params = {}) {
1610
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1611
+ if (params.width && params.height) {
1612
+ snapshotBuffer.setSize([params.width, params.height]);
1613
+ }
1614
+ snapshotBuffer.bind();
1615
+ snapshotBuffer.clear();
1616
+ snapshotBound = true;
1617
+ };
1618
+
1619
+ /**
1620
+ * When in snapshot mode, renders a frame of the current Scene state to the snapshot canvas.
1621
+ */
1622
+ this.renderSnapshot = function () {
1623
+ if (!snapshotBound) {
1624
+ return;
1625
+ }
1626
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1627
+ snapshotBuffer.clear();
1628
+ this.render({force: true, opaqueOnly: false});
1629
+ imageDirty = true;
1630
+ };
1631
+
1632
+ /**
1633
+ * When in snapshot mode, gets an image of the snapshot canvas.
1634
+ *
1635
+ * @private
1636
+ * @returns {String} The image data URI.
1637
+ */
1638
+ this.readSnapshot = function (params) {
1639
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1640
+ return snapshotBuffer.readImage(params);
1641
+ };
1642
+
1643
+ /**
1644
+ * Returns an HTMLCanvas containing an image of the snapshot canvas.
1645
+ *
1646
+ * - The HTMLCanvas has a CanvasRenderingContext2D.
1647
+ * - Expects the caller to draw more things on the HTMLCanvas (annotations etc).
1648
+ *
1649
+ * @returns {HTMLCanvasElement}
1650
+ */
1651
+ this.readSnapshotAsCanvas = function () {
1652
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1653
+ return snapshotBuffer.readImageAsCanvas();
1654
+ };
1655
+
1656
+ /**
1657
+ * Exists snapshot mode.
1658
+ *
1659
+ * Switches rendering back to the main canvas.
1660
+ */
1661
+ this.endSnapshot = function () {
1662
+ if (!snapshotBound) {
1663
+ return;
1664
+ }
1665
+ const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1666
+ snapshotBuffer.unbind();
1667
+ snapshotBound = false;
1668
+ };
1669
+
1670
+ /**
1671
+ * Destroys this renderer.
1672
+ * @private
1673
+ */
1674
+ this.destroy = function () {
1675
+
1676
+ drawableTypeInfo = {};
1677
+ drawables = {};
1678
+
1679
+ renderBufferManager.destroy();
1680
+
1681
+ saoOcclusionRenderer.destroy();
1682
+ saoDepthLimitedBlurRenderer.destroy();
1683
+
1684
+ if (this._occlusionTester) {
1685
+ this._occlusionTester.destroy();
1686
+ }
1687
+ };
1688
+
1689
+ let snapDebbugRenderer = null;
1690
+
1691
+ function drawSnapDebbug(texture, len) {
1692
+ if (!snapDebbugRenderer) {
1693
+ snapDebbugRenderer = makeSnapDebbugRenderer();
1694
+ }
1695
+
1696
+ snapDebbugRenderer.draw(texture, len);
1697
+ }
1698
+
1699
+ function makeSnapDebbugRenderer() {
1700
+ const vertexShaderSource = `\
1701
+ #version 300 es
1702
+ in vec2 position;
1703
+ in vec2 texCoord;
1704
+
1705
+ out vec2 v_texCoord;
1706
+
1707
+ void main() {
1708
+ v_texCoord = texCoord;
1709
+
1710
+ gl_Position = vec4(position, 0.0, 1.0);
1711
+ }
1712
+ `;
1713
+
1714
+ const fragmentShaderSource = `\
1715
+ #version 300 es
1716
+ precision highp float;
1717
+ precision highp isampler2D;
1718
+
1719
+ uniform isampler2D u_texture;
1720
+ uniform float u_len;
1721
+
1722
+ in vec2 v_texCoord;
1723
+
1724
+ out vec4 outColor;
1725
+
1726
+ void main() {
1727
+ vec4 color = vec4(texture(u_texture, v_texCoord));
1728
+ if (color.w > 0.0) {
1729
+ if (color.w < u_len) {
1730
+ color = vec4(1.0, 0.0, 0.0, 1.0);
1731
+ } else {
1732
+ color = vec4(0.0, 1.0, 0.0, 1.0);
1733
+ }
1734
+ }
1735
+ outColor = abs(color);
1736
+ }
1737
+ `;
1738
+
1739
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
1740
+ gl.shaderSource(vertexShader, vertexShaderSource);
1741
+ gl.compileShader(vertexShader);
1742
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
1743
+ throw new Error(gl.getShaderInfoLog(vertexShader));
1744
+ }
1745
+
1746
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
1747
+ gl.shaderSource(fragmentShader, fragmentShaderSource);
1748
+ gl.compileShader(fragmentShader);
1749
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
1750
+ throw new Error(gl.getShaderInfoLog(fragmentShader));
1751
+ }
1752
+
1753
+ const program = gl.createProgram();
1754
+
1755
+ gl.attachShader(program, vertexShader);
1756
+ gl.attachShader(program, fragmentShader);
1757
+ gl.linkProgram(program);
1758
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
1759
+ throw new Error(gl.getProgramInfoLog(program));
1760
+ }
1761
+
1762
+ const vao = gl.createVertexArray();
1763
+ gl.bindVertexArray(vao);
1764
+
1765
+ const positionLocation = gl.getAttribLocation(program, "position");
1766
+ const positions = new Float32Array([-1, -1, 1, -1, 1, 1, -1, 1]);
1767
+
1768
+ const positionBuffer = gl.createBuffer();
1769
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
1770
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
1771
+
1772
+ gl.enableVertexAttribArray(positionLocation);
1773
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
1774
+
1775
+ const texCoordLocation = gl.getAttribLocation(program, "texCoord");
1776
+ const textureCoordinates = new Float32Array([
1777
+ 0, 0, 1, 0, 1, 1, 0, 1
1778
+ ]);
1779
+
1780
+ const textureCoordinateBuffer = gl.createBuffer();
1781
+ gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordinateBuffer);
1782
+ gl.bufferData(gl.ARRAY_BUFFER, textureCoordinates, gl.STATIC_DRAW);
1783
+
1784
+ gl.enableVertexAttribArray(texCoordLocation);
1785
+ gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
1786
+
1787
+ const index = new Uint16Array([0, 1, 2, 0, 2, 3]);
1788
+
1789
+ const indexBuffer = gl.createBuffer();
1790
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
1791
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index, gl.STATIC_DRAW);
1792
+
1793
+ const lenLocation = gl.getUniformLocation(program, "u_len");
1794
+
1795
+ gl.useProgram(null);
1796
+ gl.bindVertexArray(null);
1797
+
1798
+ return {
1799
+ async draw(texture, len) {
1800
+ gl.activeTexture(gl.TEXTURE0);
1801
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1802
+
1803
+ gl.useProgram(program);
1804
+ gl.bindVertexArray(vao);
1805
+
1806
+ gl.uniform1f(lenLocation, len);
1807
+
1808
+ const width = gl.drawingBufferWidth / 2;
1809
+ const height = gl.drawingBufferHeight;
1810
+
1811
+ gl.enable(gl.SCISSOR_TEST);
1812
+ gl.scissor(0, 0, width, height);
1813
+
1814
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1815
+
1816
+ gl.viewport(0, 0, width, height);
1817
+
1818
+ gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
1819
+
1820
+ gl.bindVertexArray(null);
1821
+ gl.useProgram(null);
1822
+ gl.bindTexture(gl.TEXTURE_2D, null);
1823
+ gl.disable(gl.SCISSOR_TEST);
1824
+ gl.scissor(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1825
+ }
1826
+ }
1827
+ }
1828
+ };
1829
+
1830
+ export {Renderer};