@pirireis/webglobeplugins 1.1.12 → 1.1.14
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.
- package/package.json +1 -1
- package/programs/polygon-on-globe/texture-dem-triangles.js +14 -15
- package/semiplugins/shape-on-terrain/terrain-polygon/data/master-worker.js +1 -1
- package/semiplugins/shape-on-terrain/terrain-polygon/data/worker.js +1 -1
- package/semiplugins/shape-on-terrain/terrain-polygon/terrain-polygon.js +65 -87
- package/util/gl-util/buffer/attribute-loader.js +17 -27
- package/util/picking/picker-displayer-uint.js +172 -0
- package/util/picking/picker-displayer.js +76 -30
package/package.json
CHANGED
|
@@ -14,15 +14,16 @@ const uniformBindingPoints = {
|
|
|
14
14
|
dem: 2,
|
|
15
15
|
selection: 3,
|
|
16
16
|
};
|
|
17
|
-
|
|
17
|
+
// last value of uint16 is 65535
|
|
18
|
+
export const IndexAttributeEscapeValue = 65535;
|
|
18
19
|
const styleBlockManager = new UniformBlockManager('Style', [
|
|
19
20
|
{ name: "defaultColor", type: "vec4", value: new Float32Array([0.0, 0.0, 0.0, 0.0]) },
|
|
20
21
|
{ name: "pickedColor", type: "vec4", value: new Float32Array([0, 0, 0, 0]) },
|
|
21
22
|
{ name: "u_pointSize", type: "float", value: new Float32Array([6.0]) },
|
|
22
23
|
{ name: "opacity", type: "float", value: new Float32Array([1.0]) },
|
|
23
|
-
{ name: "private_isPickedOn", type: "
|
|
24
|
-
{ name: "private_pickedIndex", type: "
|
|
25
|
-
{ name: "useDefaultColor", type: "
|
|
24
|
+
{ name: "private_isPickedOn", type: "bool", value: new Float32Array([0]) },
|
|
25
|
+
{ name: "private_pickedIndex", type: "uint", value: new Uint32Array([0]) },
|
|
26
|
+
{ name: "useDefaultColor", type: "bool", value: new Float32Array([0]) },
|
|
26
27
|
], uniformBindingPoints.style);
|
|
27
28
|
const vertexShaderSource = `#version 300 es
|
|
28
29
|
#pragma vscode_glsllint_stage : vert
|
|
@@ -39,13 +40,13 @@ ${DEM_TEXTURE_BLOCK_STRING}
|
|
|
39
40
|
|
|
40
41
|
in vec3 a_position;
|
|
41
42
|
in vec2 a_xy;
|
|
42
|
-
in
|
|
43
|
+
in uint a_index;
|
|
43
44
|
in uvec4 a_color;
|
|
44
45
|
|
|
45
46
|
${styleBlockManager.glslCode()}
|
|
46
47
|
|
|
47
48
|
out vec4 v_color;
|
|
48
|
-
flat out
|
|
49
|
+
flat out uint v_index;
|
|
49
50
|
|
|
50
51
|
${relativeBBoxPositionRadian}
|
|
51
52
|
|
|
@@ -62,7 +63,7 @@ void main() {
|
|
|
62
63
|
);
|
|
63
64
|
|
|
64
65
|
// a_color.a == 255u is reserved as "no per-vertex color" sentinel.
|
|
65
|
-
v_color = (a_color.a == 255u || useDefaultColor ==
|
|
66
|
+
v_color = (a_color.a == 255u || useDefaultColor == true) ? defaultColor : decodedColor;
|
|
66
67
|
|
|
67
68
|
if (is3D == true) {
|
|
68
69
|
for (int i = 0; i < 6; i++) {
|
|
@@ -87,11 +88,11 @@ void main() {
|
|
|
87
88
|
gl_Position = mercatorXYToGLPosition(mercatorXY);
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
if (private_isPickedOn ==
|
|
91
|
+
if (private_isPickedOn == true){
|
|
91
92
|
if (a_index == private_pickedIndex) {
|
|
92
93
|
v_color = pickedColor;
|
|
93
94
|
}
|
|
94
|
-
v_index =
|
|
95
|
+
v_index = a_index;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
|
|
@@ -109,9 +110,9 @@ precision highp int;
|
|
|
109
110
|
|
|
110
111
|
|
|
111
112
|
in vec4 v_color; // color from vertex shader
|
|
112
|
-
flat in
|
|
113
|
+
flat in uint v_index; // index from vertex shader
|
|
113
114
|
layout(location = 0) out vec4 outColor;
|
|
114
|
-
layout(location = 1) out
|
|
115
|
+
layout(location = 1) out uint outIndex;
|
|
115
116
|
|
|
116
117
|
void main() {
|
|
117
118
|
outColor = v_color;
|
|
@@ -177,13 +178,11 @@ export class TextureDemTriangles {
|
|
|
177
178
|
attributeLoader(this.gl, pos3dBufferInfo, this.locations.attributes.a_position, 3);
|
|
178
179
|
attributeLoader(this.gl, longLatBufferInfo, this.locations.attributes.a_xy, 2);
|
|
179
180
|
attributeLoader(this.gl, indexBufferInfo, this.locations.attributes.a_index, 1, {
|
|
181
|
+
dataType: "uint16",
|
|
180
182
|
escapeValues: [IndexAttributeEscapeValue],
|
|
181
|
-
normalized: false,
|
|
182
183
|
});
|
|
183
184
|
attributeLoader(this.gl, variativeColorBuffer, this.locations.attributes.a_color, 4, {
|
|
184
|
-
|
|
185
|
-
integer: true,
|
|
186
|
-
integerUnsigned: true,
|
|
185
|
+
dataType: "uint8",
|
|
187
186
|
escapeValues: [0, 0, 0, 255]
|
|
188
187
|
});
|
|
189
188
|
this.gl.bindVertexArray(null);
|
|
@@ -95,7 +95,7 @@ function mergeAndSendResults() {
|
|
|
95
95
|
indices: new Uint32Array(totalIndices),
|
|
96
96
|
longLats: new Float32Array(totalLongLats),
|
|
97
97
|
realEdgeArcIndices: _arcState && totalRealEdgeArcIndices > 0 ? new Uint32Array(totalRealEdgeArcIndices) : null,
|
|
98
|
-
pickIndices: totalPickIndices > 0 ? new
|
|
98
|
+
pickIndices: totalPickIndices > 0 ? new Uint16Array(totalPickIndices) : null,
|
|
99
99
|
variativeColors: totalVariativeColors > 0 ? new Uint8Array(totalVariativeColors) : null,
|
|
100
100
|
};
|
|
101
101
|
// Sentinel/header (as in master-worker.js)
|
|
@@ -106,7 +106,7 @@ self.onmessage = (event) => {
|
|
|
106
106
|
longLats: new Float32Array((counter / 3) * 2),
|
|
107
107
|
indices: new Uint32Array(indexCounter),
|
|
108
108
|
realEdgeArcIndices: _arcState ? new Uint32Array(totalArcCount) : null,
|
|
109
|
-
pickIndices: _pickableState ? new
|
|
109
|
+
pickIndices: _pickableState ? new Uint16Array(counter / 3) : null,
|
|
110
110
|
variativeColors: _variativeColorsOnState ? new Uint8Array((counter / 3) * 4) : null,
|
|
111
111
|
};
|
|
112
112
|
let currentVertexOffset = 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkerContact } from "./data/worker-contact";
|
|
2
|
-
import { TextureDemTriangles } from "../../../programs/polygon-on-globe/texture-dem-triangles";
|
|
2
|
+
import { TextureDemTriangles, IndexAttributeEscapeValue } from "../../../programs/polygon-on-globe/texture-dem-triangles";
|
|
3
3
|
import { noRegisterGlobeProgramCache } from "../../../programs/programcache";
|
|
4
4
|
import { PickerDisplayer } from "../../../util/picking/picker-displayer";
|
|
5
5
|
import { IndexPolygonMap } from "./data/index-polygon-map";
|
|
@@ -11,16 +11,19 @@ export class TerrainPolygonSemiPlugin {
|
|
|
11
11
|
_options = {
|
|
12
12
|
drawEdges: true,
|
|
13
13
|
pickable: false,
|
|
14
|
+
pickablePause: false,
|
|
14
15
|
variativeColorsOn: false,
|
|
15
16
|
polygonStyle: {
|
|
16
17
|
defaultColor: [0.5, 0.5, 1, 1],
|
|
17
18
|
pickedColor: [1, 0, 0, 1],
|
|
18
19
|
opacity: 1.0,
|
|
20
|
+
useDefaultColor: false,
|
|
19
21
|
},
|
|
20
22
|
edgeStyle: {
|
|
21
23
|
defaultColor: [1, 1, 0, 1],
|
|
22
24
|
pickedColor: [1, 0, 0, 1],
|
|
23
25
|
opacity: 1.0,
|
|
26
|
+
useDefaultColor: true,
|
|
24
27
|
},
|
|
25
28
|
// Global opacity multiplier applied on top of style opacities
|
|
26
29
|
opacity: 1.0,
|
|
@@ -83,11 +86,16 @@ export class TerrainPolygonSemiPlugin {
|
|
|
83
86
|
console.log("TerrainPolygonSemiPlugin options:", this._options);
|
|
84
87
|
}
|
|
85
88
|
;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
_effectiveOpacity(target) {
|
|
90
|
+
if (target === "polygon") {
|
|
91
|
+
return new Float32Array([this._options.opacity * this._options.polygonStyle.opacity]);
|
|
92
|
+
}
|
|
93
|
+
else if (target === "edgeArc") {
|
|
94
|
+
return new Float32Array([this._options.opacity * this._options.edgeStyle.opacity]);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw new Error(`Unknown target ${target} for _effectiveOpacity, must be "polygon" or "edgeArc"`);
|
|
98
|
+
}
|
|
91
99
|
}
|
|
92
100
|
init(globe, gl) {
|
|
93
101
|
this.globe = globe;
|
|
@@ -98,11 +106,11 @@ export class TerrainPolygonSemiPlugin {
|
|
|
98
106
|
this._vec3Buffer = gl.createBuffer();
|
|
99
107
|
this._mercatorXYBuffer = gl.createBuffer();
|
|
100
108
|
this._uboHandler = this._program.createUBO();
|
|
101
|
-
this._uboHandler.updateSingle("opacity",
|
|
109
|
+
this._uboHandler.updateSingle("opacity", this._effectiveOpacity("polygon"));
|
|
102
110
|
this._uboHandler.updateSingle("defaultColor", new Float32Array(this._options.polygonStyle.defaultColor));
|
|
103
111
|
if (this._options.pickable) {
|
|
104
112
|
this._pickIndexBuffer = gl.createBuffer();
|
|
105
|
-
this._pickerDisplayer = new PickerDisplayer(this.globe, "
|
|
113
|
+
this._pickerDisplayer = new PickerDisplayer(this.globe, "R16UI");
|
|
106
114
|
this._uboHandler.updateSingle("private_isPickedOn", new Float32Array([1.0]));
|
|
107
115
|
this._uboHandler.updateSingle("pickedColor", new Float32Array(this._options.polygonStyle.pickedColor));
|
|
108
116
|
}
|
|
@@ -110,7 +118,7 @@ export class TerrainPolygonSemiPlugin {
|
|
|
110
118
|
this._uboForRealEdgeArcs = this._program.createUBO();
|
|
111
119
|
this._uboForRealEdgeArcs.updateSingle("defaultColor", new Float32Array(this._options.edgeStyle.defaultColor));
|
|
112
120
|
this._uboForRealEdgeArcs.updateSingle("useDefaultColor", new Float32Array([1]));
|
|
113
|
-
this._uboForRealEdgeArcs.updateSingle("opacity",
|
|
121
|
+
this._uboForRealEdgeArcs.updateSingle("opacity", this._effectiveOpacity("edgeArc"));
|
|
114
122
|
this._drawRealEdgeArcs.elementBuffer = gl.createBuffer();
|
|
115
123
|
}
|
|
116
124
|
if (this._options.variativeColorsOn) {
|
|
@@ -135,7 +143,6 @@ export class TerrainPolygonSemiPlugin {
|
|
|
135
143
|
} : undefined);
|
|
136
144
|
this._drawRangeIndexParams.elementBuffer = this._elementArrayBuffer;
|
|
137
145
|
this._workerContact = new WorkerContact(this.globe, this._options, (result) => {
|
|
138
|
-
// console.log('TerrainPolygonSemiPlugin received data from worker', result);
|
|
139
146
|
this.setBuffers(result);
|
|
140
147
|
});
|
|
141
148
|
this._drawRangeIndexParams.elementBuffer = this._elementArrayBuffer;
|
|
@@ -164,64 +171,45 @@ export class TerrainPolygonSemiPlugin {
|
|
|
164
171
|
if (name.startsWith("private_")) {
|
|
165
172
|
throw new Error(`Cannot set private uniform ${name}`);
|
|
166
173
|
}
|
|
167
|
-
const isStyleKey = name === "defaultColor" || name === "pickedColor" || name === "opacity";
|
|
174
|
+
const isStyleKey = name === "defaultColor" || name === "pickedColor" || name === "opacity" || name === "useDefaultColor";
|
|
175
|
+
let targetUBO;
|
|
168
176
|
if (target === "polygon") {
|
|
169
|
-
|
|
170
|
-
const prev = this._options.polygonStyle[name];
|
|
171
|
-
if (prev === value)
|
|
172
|
-
return;
|
|
173
|
-
this._options.polygonStyle[name] = value;
|
|
174
|
-
if (name === "opacity") {
|
|
175
|
-
this._uboHandler.updateSingle("opacity", new Float32Array([this._effectivePolygonOpacity()]));
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
this._uboHandler.updateSingle(name, value);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
this._uboHandler.updateSingle(name, value);
|
|
183
|
-
}
|
|
177
|
+
targetUBO = this._uboHandler;
|
|
184
178
|
}
|
|
185
179
|
else if (target === "edgeArc") {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
180
|
+
targetUBO = this._uboForRealEdgeArcs;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.warn(`Unknown target ${target} for setUniform, must be "polygon" or "edgeArc"`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (isStyleKey) {
|
|
187
|
+
const prev = this._options.polygonStyle[name];
|
|
188
|
+
if (prev === value)
|
|
189
|
+
return;
|
|
190
|
+
this._options.polygonStyle[name] = value;
|
|
191
|
+
if (name === "opacity") {
|
|
192
|
+
targetUBO.updateSingle("opacity", this._effectiveOpacity(target));
|
|
197
193
|
}
|
|
198
194
|
else {
|
|
199
|
-
|
|
195
|
+
targetUBO.updateSingle(name, value);
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
else {
|
|
203
|
-
|
|
199
|
+
targetUBO.updateSingle(name, value);
|
|
204
200
|
}
|
|
205
201
|
this.globe.DrawRender();
|
|
206
202
|
}
|
|
207
|
-
setDrawEdgesState(state
|
|
203
|
+
setDrawEdgesState(state) {
|
|
208
204
|
const oldState = this._options.drawEdges;
|
|
209
205
|
if (oldState === state)
|
|
210
206
|
return;
|
|
211
|
-
if (options?.defaultColor) {
|
|
212
|
-
this._options.edgeStyle.defaultColor = options.defaultColor;
|
|
213
|
-
}
|
|
214
|
-
if (options?.pickedColor) {
|
|
215
|
-
this._options.edgeStyle.pickedColor = options.pickedColor;
|
|
216
|
-
}
|
|
217
|
-
if (options?.opacity !== undefined) {
|
|
218
|
-
this._options.edgeStyle.opacity = options.opacity;
|
|
219
|
-
}
|
|
220
207
|
if (oldState === false && state === true) {
|
|
221
208
|
this._uboForRealEdgeArcs = this._program.createUBO();
|
|
222
209
|
this._uboForRealEdgeArcs.updateSingle("defaultColor", new Float32Array(this._options.edgeStyle.defaultColor));
|
|
223
|
-
this._uboForRealEdgeArcs.updateSingle("useDefaultColor", new Float32Array([1]));
|
|
224
|
-
this._uboForRealEdgeArcs.updateSingle("
|
|
210
|
+
this._uboForRealEdgeArcs.updateSingle("useDefaultColor", new Float32Array([this._options.edgeStyle.useDefaultColor ? 1 : 0]));
|
|
211
|
+
this._uboForRealEdgeArcs.updateSingle("pickedColor", new Float32Array(this._options.edgeStyle.pickedColor));
|
|
212
|
+
this._uboForRealEdgeArcs.updateSingle("opacity", this._effectiveOpacity("edgeArc"));
|
|
225
213
|
this._drawRealEdgeArcs.elementBuffer = this.globe.gl.createBuffer();
|
|
226
214
|
}
|
|
227
215
|
else if (oldState === true && state === false) {
|
|
@@ -237,28 +225,42 @@ export class TerrainPolygonSemiPlugin {
|
|
|
237
225
|
setOpacity(opacity) {
|
|
238
226
|
if (this._options.opacity !== opacity) {
|
|
239
227
|
this._options.opacity = opacity;
|
|
240
|
-
this._uboHandler.updateSingle("opacity",
|
|
241
|
-
this._uboForRealEdgeArcs?.updateSingle("opacity",
|
|
228
|
+
this._uboHandler.updateSingle("opacity", this._effectiveOpacity("polygon"));
|
|
229
|
+
this._uboForRealEdgeArcs?.updateSingle("opacity", this._effectiveOpacity("edgeArc"));
|
|
242
230
|
this.globe.DrawRender();
|
|
243
231
|
}
|
|
244
232
|
}
|
|
233
|
+
setPausePickableState(state) {
|
|
234
|
+
if (this._options.pickable === false) {
|
|
235
|
+
throw new Error("TerrainPolygonSemiPlugin is not pickable. Set pickable option to true on construction to enable picking.");
|
|
236
|
+
}
|
|
237
|
+
const oldState = this._options.pickablePause;
|
|
238
|
+
if (oldState === state)
|
|
239
|
+
return;
|
|
240
|
+
this._options.pickablePause = state;
|
|
241
|
+
// dont initialize anything. just set private_isPickedOn uniform
|
|
242
|
+
this._uboHandler.updateSingle("private_isPickedOn", new Float32Array([state ? 0.0 : 1.0]));
|
|
243
|
+
this.globe.DrawRender();
|
|
244
|
+
}
|
|
245
245
|
setPickableState(state) {
|
|
246
|
+
throw new Error("setPickableState is buggy. use setPausePickableState instead.");
|
|
246
247
|
const oldState = this._options.pickable;
|
|
247
248
|
if (oldState === state)
|
|
248
249
|
return;
|
|
249
250
|
if (oldState === false && state === true) {
|
|
250
|
-
|
|
251
|
+
this._pickIndexBuffer = this.globe.gl.createBuffer();
|
|
251
252
|
this._uboHandler.updateSingle("private_isPickedOn", new Float32Array([1.0]));
|
|
252
253
|
this._uboHandler.updateSingle("pickedColor", new Float32Array(this._options.polygonStyle.pickedColor));
|
|
253
|
-
|
|
254
|
+
this._pickerDisplayer = new PickerDisplayer(this.globe, "R32I");
|
|
254
255
|
}
|
|
255
256
|
else if (oldState === true && state === false) {
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
this.globe.gl.deleteBuffer(this._pickIndexBuffer);
|
|
258
|
+
this._pickIndexBuffer = null;
|
|
258
259
|
this._uboHandler.updateSingle("private_isPickedOn", new Float32Array([0.0]));
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
this._uboHandler.updateSingle("private_pickedIndex", new Float32Array([-1]));
|
|
261
|
+
this._pickerDisplayer?.free();
|
|
262
|
+
this._pickerDisplayer = null;
|
|
263
|
+
this._lastPickedPolygon = null;
|
|
262
264
|
}
|
|
263
265
|
this._options.pickable = state;
|
|
264
266
|
this._workerContact.setPickableState(state);
|
|
@@ -287,30 +289,6 @@ export class TerrainPolygonSemiPlugin {
|
|
|
287
289
|
if (this._options.variativeColorsOn && data.variativeColors) {
|
|
288
290
|
safeVertexCount = Math.min(safeVertexCount, Math.floor(data.variativeColors.length / 4));
|
|
289
291
|
}
|
|
290
|
-
// Guard against invalid indices referencing beyond uploaded attribute buffers.
|
|
291
|
-
// This prevents ANGLE "Vertex buffer is not big enough for the draw call" errors.
|
|
292
|
-
// let maxTriangleIndex = -1;
|
|
293
|
-
// // for (let i = 0; i < data.indices.length; i++) {
|
|
294
|
-
// // const v = data.indices[i];
|
|
295
|
-
// // if (v > maxTriangleIndex) maxTriangleIndex = v;
|
|
296
|
-
// // }
|
|
297
|
-
// const trianglesInvalid = safeVertexCount === 0 || maxTriangleIndex >= safeVertexCount;
|
|
298
|
-
// if (trianglesInvalid) {
|
|
299
|
-
// console.warn(
|
|
300
|
-
// "TerrainPolygonSemiPlugin: skipping draw due to invalid vertex/index sizing",
|
|
301
|
-
// {
|
|
302
|
-
// posVertexCount,
|
|
303
|
-
// xyVertexCount,
|
|
304
|
-
// safeVertexCount,
|
|
305
|
-
// indicesLength: data.indices.length,
|
|
306
|
-
// maxTriangleIndex,
|
|
307
|
-
// pickIndicesLength: data.pickIndices?.length ?? null,
|
|
308
|
-
// variativeColorsLength: data.variativeColors?.length ?? null,
|
|
309
|
-
// }
|
|
310
|
-
// );
|
|
311
|
-
// this._drawRangeIndexParams.drawRange.count = 0;
|
|
312
|
-
// this._drawPointsRangeIndexParams.drawRange.count = 0;
|
|
313
|
-
// }
|
|
314
292
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._elementArrayBuffer);
|
|
315
293
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.indices, gl.STREAM_DRAW);
|
|
316
294
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._vec3Buffer);
|
|
@@ -375,7 +353,7 @@ export class TerrainPolygonSemiPlugin {
|
|
|
375
353
|
if (this._lastPickedPolygon !== null) {
|
|
376
354
|
this._lastPickedPolygon = null;
|
|
377
355
|
globe.DrawRender();
|
|
378
|
-
this._uboHandler.updateSingle("private_pickedIndex", new Float32Array([
|
|
356
|
+
this._uboHandler.updateSingle("private_pickedIndex", new Float32Array([IndexAttributeEscapeValue]));
|
|
379
357
|
}
|
|
380
358
|
}
|
|
381
359
|
});
|
|
@@ -399,7 +377,7 @@ export class TerrainPolygonSemiPlugin {
|
|
|
399
377
|
if (this._options.showTesselationPoints) {
|
|
400
378
|
this._program.draw(this._vao, this._drawPointsRangeIndexParams, this._uboHandler);
|
|
401
379
|
}
|
|
402
|
-
if (this._pickerDisplayer && this._options.
|
|
380
|
+
if (this._pickerDisplayer && !this._options.pickablePause) {
|
|
403
381
|
this._pickerDisplayer.bindFBO();
|
|
404
382
|
this._pickerDisplayer.clearTextures();
|
|
405
383
|
// gl.enable(gl.DEPTH_TEST);
|
|
@@ -407,7 +385,7 @@ export class TerrainPolygonSemiPlugin {
|
|
|
407
385
|
gl.frontFace(gl.CW);
|
|
408
386
|
this._program.draw(this._vao, this._drawRangeIndexParams, this._uboHandler);
|
|
409
387
|
gl.frontFace(gl.CCW);
|
|
410
|
-
if (this._pickerDisplayer && this._options.
|
|
388
|
+
if (this._pickerDisplayer && !this._options.pickablePause) {
|
|
411
389
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
412
390
|
this._pickerDisplayer.drawColorTexture();
|
|
413
391
|
this._selfSelect();
|
|
@@ -1,27 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* @param {BufferAndReadInfo} bufferAndReadInfo
|
|
13
|
-
* @param {number} index
|
|
14
|
-
* @param {number} size
|
|
15
|
-
* @param {Object} options
|
|
16
|
-
* @param {*} options.type | default gl.FLOAT, gl.UNSIGNED_BYTE, gl.SHORT, gl.UNSIGNED_SHORT, gl.INT, gl.UNSIGNED_INT
|
|
17
|
-
* @param {number} options.divisor
|
|
18
|
-
* @param {Array<number>} options.escapeValues
|
|
19
|
-
* @returns
|
|
20
|
-
*/
|
|
21
|
-
const attributeLoader = (gl, bufferAndReadInfo, index, size, { divisor = null, type = null, escapeValues = null, normalized = false, integer = false, integerUnsigned = true, } = {}) => {
|
|
1
|
+
const DATA_TYPE_MAP = {
|
|
2
|
+
float: { getGLType: (gl) => gl.FLOAT, useIntegerPointer: false, isSigned: true },
|
|
3
|
+
int8: { getGLType: (gl) => gl.BYTE, useIntegerPointer: true, isSigned: true },
|
|
4
|
+
uint8: { getGLType: (gl) => gl.UNSIGNED_BYTE, useIntegerPointer: true, isSigned: false },
|
|
5
|
+
int16: { getGLType: (gl) => gl.SHORT, useIntegerPointer: true, isSigned: true },
|
|
6
|
+
uint16: { getGLType: (gl) => gl.UNSIGNED_SHORT, useIntegerPointer: true, isSigned: false },
|
|
7
|
+
int32: { getGLType: (gl) => gl.INT, useIntegerPointer: true, isSigned: true },
|
|
8
|
+
uint32: { getGLType: (gl) => gl.UNSIGNED_INT, useIntegerPointer: true, isSigned: false },
|
|
9
|
+
};
|
|
10
|
+
const attributeLoader = (gl, bufferAndReadInfo, index, size, { divisor = null, dataType = null, escapeValues = null, normalized = false, } = {}) => {
|
|
11
|
+
const typeInfo = dataType ? DATA_TYPE_MAP[dataType] : DATA_TYPE_MAP.float;
|
|
22
12
|
if (bufferAndReadInfo == null || bufferAndReadInfo.buffer == null) {
|
|
23
13
|
if (escapeValues)
|
|
24
|
-
constantFunction(gl, index, size, escapeValues,
|
|
14
|
+
constantFunction(gl, index, size, escapeValues, typeInfo);
|
|
25
15
|
return;
|
|
26
16
|
}
|
|
27
17
|
const { buffer, stride, offset } = bufferAndReadInfo;
|
|
@@ -34,10 +24,10 @@ const attributeLoader = (gl, bufferAndReadInfo, index, size, { divisor = null, t
|
|
|
34
24
|
if (stride < 0 || offset < 0) {
|
|
35
25
|
throw new Error("Stride and offset must be non-negative");
|
|
36
26
|
}
|
|
37
|
-
const attribType =
|
|
27
|
+
const attribType = typeInfo.getGLType(gl);
|
|
38
28
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
39
29
|
gl.enableVertexAttribArray(index);
|
|
40
|
-
if (
|
|
30
|
+
if (typeInfo.useIntegerPointer) {
|
|
41
31
|
gl.vertexAttribIPointer(index, size, attribType, stride, offset);
|
|
42
32
|
}
|
|
43
33
|
else {
|
|
@@ -59,15 +49,15 @@ const createBufferAndReadInfo = (buffer, stride = 0, offset = 0) => {
|
|
|
59
49
|
return null;
|
|
60
50
|
return { buffer, stride, offset };
|
|
61
51
|
};
|
|
62
|
-
const constantFunction = (gl, index, size, escapeValues,
|
|
63
|
-
if (!
|
|
52
|
+
const constantFunction = (gl, index, size, escapeValues, typeInfo) => {
|
|
53
|
+
if (!typeInfo.useIntegerPointer) {
|
|
64
54
|
const func = `vertexAttrib${size}f`;
|
|
65
55
|
// @ts-ignore
|
|
66
56
|
gl[func](index, ...escapeValues);
|
|
67
57
|
return;
|
|
68
58
|
}
|
|
69
59
|
const values = escapeValues.map((v) => Math.trunc(v));
|
|
70
|
-
const func =
|
|
60
|
+
const func = typeInfo.isSigned ? `vertexAttribI${size}i` : `vertexAttribI${size}ui`;
|
|
71
61
|
// @ts-ignore
|
|
72
62
|
gl[func](index, ...values);
|
|
73
63
|
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { textureOnCanvasProgramCache } from "../programs/draw-texture-on-canvas";
|
|
2
|
+
import { fence } from "./fence";
|
|
3
|
+
/** * Use 0xFFFFFFFF (4,294,967,295) as the "no-object" value.
|
|
4
|
+
* This allows 0 to be a valid Object ID.
|
|
5
|
+
*/
|
|
6
|
+
const ESCAPE_VALUE = 4294967295;
|
|
7
|
+
class PickerDisplayer {
|
|
8
|
+
globe;
|
|
9
|
+
gl;
|
|
10
|
+
colorTexture;
|
|
11
|
+
indexTexture;
|
|
12
|
+
fbo;
|
|
13
|
+
_pbo = null;
|
|
14
|
+
_pboSize = 0;
|
|
15
|
+
displayer;
|
|
16
|
+
_inProgress = false;
|
|
17
|
+
_lastWidthHeight = null;
|
|
18
|
+
constructor(globe) {
|
|
19
|
+
this.globe = globe;
|
|
20
|
+
this.gl = globe.gl;
|
|
21
|
+
const gl = this.gl;
|
|
22
|
+
this.colorTexture = gl.createTexture();
|
|
23
|
+
this.indexTexture = gl.createTexture();
|
|
24
|
+
this.fbo = gl.createFramebuffer();
|
|
25
|
+
this.resize();
|
|
26
|
+
this.displayer = textureOnCanvasProgramCache.get(this.gl);
|
|
27
|
+
}
|
|
28
|
+
_saveState() {
|
|
29
|
+
const gl = this.gl;
|
|
30
|
+
return {
|
|
31
|
+
framebuffer: gl.getParameter(gl.FRAMEBUFFER_BINDING),
|
|
32
|
+
pixelPackBuffer: gl.getParameter(gl.PIXEL_PACK_BUFFER_BINDING),
|
|
33
|
+
readBuffer: gl.getParameter(gl.READ_BUFFER),
|
|
34
|
+
depthTestEnabled: gl.isEnabled(gl.DEPTH_TEST),
|
|
35
|
+
activeTexture: gl.getParameter(gl.ACTIVE_TEXTURE),
|
|
36
|
+
tex2D_unit0: (gl.activeTexture(gl.TEXTURE0), gl.getParameter(gl.TEXTURE_BINDING_2D)),
|
|
37
|
+
drawBuffers: [gl.getParameter(gl.DRAW_BUFFER0), gl.getParameter(gl.DRAW_BUFFER1)]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
_restoreState(s) {
|
|
41
|
+
const gl = this.gl;
|
|
42
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, s.framebuffer);
|
|
43
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, s.pixelPackBuffer);
|
|
44
|
+
gl.readBuffer(s.readBuffer);
|
|
45
|
+
if (s.depthTestEnabled)
|
|
46
|
+
gl.enable(gl.DEPTH_TEST);
|
|
47
|
+
else
|
|
48
|
+
gl.disable(gl.DEPTH_TEST);
|
|
49
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
50
|
+
gl.bindTexture(gl.TEXTURE_2D, s.tex2D_unit0);
|
|
51
|
+
gl.activeTexture(s.activeTexture);
|
|
52
|
+
gl.drawBuffers(s.drawBuffers.filter(b => b !== gl.NONE));
|
|
53
|
+
}
|
|
54
|
+
resize() {
|
|
55
|
+
const width = this.globe.api_ScrW();
|
|
56
|
+
const height = this.globe.api_ScrH();
|
|
57
|
+
if (this._lastWidthHeight?.width === width && this._lastWidthHeight?.height === height)
|
|
58
|
+
return;
|
|
59
|
+
this._lastWidthHeight = { width, height };
|
|
60
|
+
const gl = this.gl;
|
|
61
|
+
// Attachment 0: Visual Color (RGBA8)
|
|
62
|
+
gl.bindTexture(gl.TEXTURE_2D, this.colorTexture);
|
|
63
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
|
|
64
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
65
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
66
|
+
// Attachment 1: Picking ID (R32UI - Unsigned Integer)
|
|
67
|
+
gl.bindTexture(gl.TEXTURE_2D, this.indexTexture);
|
|
68
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R32UI, width, height);
|
|
69
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
70
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
71
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
72
|
+
}
|
|
73
|
+
bindFBO() {
|
|
74
|
+
const gl = this.gl;
|
|
75
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
|
|
76
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture, 0);
|
|
77
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.indexTexture, 0);
|
|
78
|
+
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
|
|
79
|
+
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
|
80
|
+
throw new Error("Framebuffer incomplete");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
clearTextures() {
|
|
84
|
+
const gl = this.gl;
|
|
85
|
+
const saved = this._saveState();
|
|
86
|
+
this.bindFBO();
|
|
87
|
+
// Clear Color to transparent black
|
|
88
|
+
gl.clearBufferfv(gl.COLOR, 0, [0, 0, 0, 0]);
|
|
89
|
+
// Clear Index to Max Uint32 (ESCAPE_VALUE)
|
|
90
|
+
gl.clearBufferuiv(gl.COLOR, 1, [ESCAPE_VALUE, 0, 0, 0]);
|
|
91
|
+
this._restoreState(saved);
|
|
92
|
+
}
|
|
93
|
+
pickXY(x, y, radius = 0, callback) {
|
|
94
|
+
const side = 1 + radius * 2;
|
|
95
|
+
// Invert Y because WebGL screen space starts bottom-left
|
|
96
|
+
const webglY = this.globe.api_ScrH() - y - radius;
|
|
97
|
+
return this._pick(x - radius, webglY, side, side, callback);
|
|
98
|
+
}
|
|
99
|
+
getEscapeValue() {
|
|
100
|
+
return ESCAPE_VALUE;
|
|
101
|
+
}
|
|
102
|
+
_pick(x, y, w, h, callback) {
|
|
103
|
+
if (this._inProgress)
|
|
104
|
+
return false;
|
|
105
|
+
this._inProgress = true;
|
|
106
|
+
const gl = this.gl;
|
|
107
|
+
const saved = this._saveState();
|
|
108
|
+
const pixelCount = w * h;
|
|
109
|
+
const byteSize = pixelCount * 4;
|
|
110
|
+
try {
|
|
111
|
+
this._initPBO(byteSize);
|
|
112
|
+
this.bindFBO();
|
|
113
|
+
gl.readBuffer(gl.COLOR_ATTACHMENT1);
|
|
114
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
115
|
+
// CRITICAL: Format must be RED_INTEGER and type UNSIGNED_INT for UI
|
|
116
|
+
gl.readPixels(x, y, w, h, gl.RED_INTEGER, gl.UNSIGNED_INT, 0);
|
|
117
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
118
|
+
this._restoreState(saved);
|
|
119
|
+
fence(gl).then(() => {
|
|
120
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
121
|
+
const data = new Uint32Array(pixelCount);
|
|
122
|
+
gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
|
|
123
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
124
|
+
const resultSet = new Set();
|
|
125
|
+
for (let i = 0; i < data.length; i++) {
|
|
126
|
+
if (data[i] !== ESCAPE_VALUE)
|
|
127
|
+
resultSet.add(data[i]);
|
|
128
|
+
}
|
|
129
|
+
callback(resultSet);
|
|
130
|
+
}).finally(() => {
|
|
131
|
+
this._inProgress = false;
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
console.error("Picking Error:", e);
|
|
137
|
+
this._restoreState(saved);
|
|
138
|
+
this._inProgress = false;
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
_initPBO(size) {
|
|
143
|
+
const gl = this.gl;
|
|
144
|
+
if (this._pbo && this._pboSize >= size)
|
|
145
|
+
return;
|
|
146
|
+
if (this._pbo)
|
|
147
|
+
gl.deleteBuffer(this._pbo);
|
|
148
|
+
this._pbo = gl.createBuffer();
|
|
149
|
+
this._pboSize = size;
|
|
150
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
|
|
151
|
+
gl.bufferData(gl.PIXEL_PACK_BUFFER, size, gl.STREAM_READ);
|
|
152
|
+
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
153
|
+
}
|
|
154
|
+
drawColorTexture() {
|
|
155
|
+
const gl = this.gl;
|
|
156
|
+
const saved = this._saveState();
|
|
157
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
158
|
+
gl.disable(gl.DEPTH_TEST);
|
|
159
|
+
this.displayer.draw(this.colorTexture);
|
|
160
|
+
this._restoreState(saved);
|
|
161
|
+
}
|
|
162
|
+
free() {
|
|
163
|
+
const gl = this.gl;
|
|
164
|
+
gl.deleteTexture(this.colorTexture);
|
|
165
|
+
gl.deleteTexture(this.indexTexture);
|
|
166
|
+
gl.deleteFramebuffer(this.fbo);
|
|
167
|
+
if (this._pbo)
|
|
168
|
+
gl.deleteBuffer(this._pbo);
|
|
169
|
+
textureOnCanvasProgramCache.release(this.gl);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export { PickerDisplayer };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* add implicit texture display program for color
|
|
3
3
|
* add fence on query return and return id.
|
|
4
|
-
|
|
4
|
+
* support R32I, R32F, R16UI, R32UI
|
|
5
|
+
*/
|
|
5
6
|
import { textureOnCanvasProgramCache } from "../programs/draw-texture-on-canvas";
|
|
6
7
|
import { fence } from "./fence";
|
|
7
8
|
const ESCAPE_VALUE = -1;
|
|
@@ -15,8 +16,10 @@ class PickerDisplayer {
|
|
|
15
16
|
_pboSize;
|
|
16
17
|
displayer;
|
|
17
18
|
_inProgress;
|
|
18
|
-
_indexType;
|
|
19
|
+
_indexType;
|
|
19
20
|
_typedArrayConstructor;
|
|
21
|
+
// The value representing "nothing selected" in the current type format
|
|
22
|
+
_escapeValue;
|
|
20
23
|
_lastWidthHeight = null;
|
|
21
24
|
constructor(globe, indexType = "R32I") {
|
|
22
25
|
this.globe = globe;
|
|
@@ -29,15 +32,25 @@ class PickerDisplayer {
|
|
|
29
32
|
throw new Error("EXT_color_buffer_float extension is required for R32F index type.");
|
|
30
33
|
}
|
|
31
34
|
this._indexType = indexType;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// Assign TypedArray constructor and calculate the specific Escape Value
|
|
36
|
+
switch (indexType) {
|
|
37
|
+
case "R32I":
|
|
38
|
+
this._typedArrayConstructor = Int32Array;
|
|
39
|
+
break;
|
|
40
|
+
case "R32F":
|
|
41
|
+
this._typedArrayConstructor = Float32Array;
|
|
42
|
+
break;
|
|
43
|
+
case "R16UI":
|
|
44
|
+
this._typedArrayConstructor = Uint16Array;
|
|
45
|
+
break;
|
|
46
|
+
case "R32UI":
|
|
47
|
+
this._typedArrayConstructor = Uint32Array;
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error("Invalid index type. Must be 'R32I', 'R32F', 'R16UI', or 'R32UI'.");
|
|
40
51
|
}
|
|
52
|
+
// Calculate what -1 looks like in the target type (e.g., 0xFFFF for Uint16)
|
|
53
|
+
this._escapeValue = (new this._typedArrayConstructor([ESCAPE_VALUE]))[0];
|
|
41
54
|
this._pbo = undefined;
|
|
42
55
|
this._pboSize = 0;
|
|
43
56
|
this._inProgress = false;
|
|
@@ -85,8 +98,6 @@ class PickerDisplayer {
|
|
|
85
98
|
gl.activeTexture(gl.TEXTURE1);
|
|
86
99
|
gl.bindTexture(gl.TEXTURE_2D, s.tex2D_unit1);
|
|
87
100
|
gl.activeTexture(s.activeTexture);
|
|
88
|
-
// drawBuffers restore: default framebuffer expects BACK/NONE (implementation details vary),
|
|
89
|
-
// so only restore the number of buffers that makes sense for the bound FB.
|
|
90
101
|
if (s.framebuffer === null) {
|
|
91
102
|
gl.drawBuffers([s.drawBuffer0]);
|
|
92
103
|
}
|
|
@@ -109,14 +120,18 @@ class PickerDisplayer {
|
|
|
109
120
|
const { colorTexture, indexTexture } = this;
|
|
110
121
|
gl.deleteTexture(colorTexture);
|
|
111
122
|
gl.deleteTexture(indexTexture);
|
|
123
|
+
// 1. Color Texture (Standard RGBA8)
|
|
112
124
|
const colorTextureNew = gl.createTexture();
|
|
113
125
|
gl.bindTexture(gl.TEXTURE_2D, colorTextureNew);
|
|
114
126
|
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
|
|
115
127
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
116
128
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
129
|
+
// 2. Index Texture (Varies based on this._indexType)
|
|
117
130
|
const indexTextureNew = gl.createTexture();
|
|
118
131
|
gl.bindTexture(gl.TEXTURE_2D, indexTextureNew);
|
|
119
|
-
|
|
132
|
+
// Map string type to GL constant
|
|
133
|
+
const internalFormat = gl[this._indexType];
|
|
134
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, internalFormat, width, height);
|
|
120
135
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
121
136
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
122
137
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
@@ -127,14 +142,24 @@ class PickerDisplayer {
|
|
|
127
142
|
const gl = this.gl;
|
|
128
143
|
const saved = this._saveState();
|
|
129
144
|
try {
|
|
130
|
-
// Must clear the *FBO attachments*, not "textures".
|
|
131
145
|
this.bindFBO();
|
|
146
|
+
// Clear Index Attachment (Attachment 1)
|
|
132
147
|
if (this._indexType === "R32I") {
|
|
133
|
-
|
|
148
|
+
// Signed Integer
|
|
149
|
+
gl.clearBufferiv(gl.COLOR, 1, new Int32Array([ESCAPE_VALUE, ESCAPE_VALUE, ESCAPE_VALUE, ESCAPE_VALUE]));
|
|
150
|
+
}
|
|
151
|
+
else if (this._indexType === "R32F") {
|
|
152
|
+
// Float
|
|
153
|
+
gl.clearBufferfv(gl.COLOR, 1, new Float32Array([ESCAPE_VALUE, ESCAPE_VALUE, ESCAPE_VALUE, ESCAPE_VALUE]));
|
|
134
154
|
}
|
|
135
155
|
else {
|
|
136
|
-
|
|
156
|
+
// Unsigned Integer (R16UI, R32UI)
|
|
157
|
+
// We use ESCAPE_VALUE cast to uint. For -1 this is MaxUint (all 1s).
|
|
158
|
+
// clearBufferuiv expects Uint32Array even for 16-bit buffers.
|
|
159
|
+
const clearVal = this._escapeValue;
|
|
160
|
+
gl.clearBufferuiv(gl.COLOR, 1, new Uint32Array([clearVal, clearVal, clearVal, clearVal]));
|
|
137
161
|
}
|
|
162
|
+
// Clear Color Attachment (Attachment 0)
|
|
138
163
|
gl.clearBufferfv(gl.COLOR, 0, new Float32Array([0, 0, 0, 0]));
|
|
139
164
|
}
|
|
140
165
|
finally {
|
|
@@ -146,7 +171,6 @@ class PickerDisplayer {
|
|
|
146
171
|
const { gl, fbo } = this;
|
|
147
172
|
this.resize();
|
|
148
173
|
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
149
|
-
// No need to bind textures or change ACTIVE_TEXTURE for framebufferTexture2D.
|
|
150
174
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture, 0);
|
|
151
175
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.indexTexture, 0);
|
|
152
176
|
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
|
|
@@ -183,15 +207,39 @@ class PickerDisplayer {
|
|
|
183
207
|
const gl = this.gl;
|
|
184
208
|
const saved = this._saveState();
|
|
185
209
|
try {
|
|
186
|
-
|
|
210
|
+
// Allocate PBO with enough size for the specific type (2 bytes or 4 bytes per pixel)
|
|
211
|
+
const bytesPerPixel = this._typedArrayConstructor.BYTES_PER_ELEMENT;
|
|
212
|
+
this._initHoldBuffer(size * bytesPerPixel);
|
|
187
213
|
const { _pbo } = this;
|
|
188
214
|
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
|
|
189
215
|
this.bindFBO();
|
|
190
216
|
gl.readBuffer(gl.COLOR_ATTACHMENT1);
|
|
191
|
-
|
|
192
|
-
|
|
217
|
+
let format;
|
|
218
|
+
let type;
|
|
219
|
+
switch (this._indexType) {
|
|
220
|
+
case "R32I":
|
|
221
|
+
format = gl.RED_INTEGER;
|
|
222
|
+
type = gl.INT;
|
|
223
|
+
break;
|
|
224
|
+
case "R32F":
|
|
225
|
+
format = gl.RED;
|
|
226
|
+
type = gl.FLOAT;
|
|
227
|
+
break;
|
|
228
|
+
case "R16UI":
|
|
229
|
+
format = gl.RED_INTEGER;
|
|
230
|
+
type = gl.UNSIGNED_SHORT;
|
|
231
|
+
break;
|
|
232
|
+
case "R32UI":
|
|
233
|
+
format = gl.RED_INTEGER;
|
|
234
|
+
type = gl.UNSIGNED_INT;
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
// Fallback
|
|
238
|
+
format = gl.RED_INTEGER;
|
|
239
|
+
type = gl.INT;
|
|
240
|
+
}
|
|
193
241
|
gl.readPixels(startX, startY, lengthX, lengthY, format, type, 0);
|
|
194
|
-
// Restore state immediately after issuing readPixels
|
|
242
|
+
// Restore state immediately after issuing readPixels
|
|
195
243
|
this._restoreState(saved);
|
|
196
244
|
fence(this.gl)
|
|
197
245
|
.then(() => {
|
|
@@ -203,7 +251,7 @@ class PickerDisplayer {
|
|
|
203
251
|
callback(result);
|
|
204
252
|
})
|
|
205
253
|
.catch(() => {
|
|
206
|
-
// Swallow or rethrow based on your app needs
|
|
254
|
+
// Swallow or rethrow based on your app needs
|
|
207
255
|
})
|
|
208
256
|
.finally(() => {
|
|
209
257
|
this._inProgress = false;
|
|
@@ -215,30 +263,28 @@ class PickerDisplayer {
|
|
|
215
263
|
this._inProgress = false;
|
|
216
264
|
throw new Error("Picking failed.");
|
|
217
265
|
}
|
|
218
|
-
finally {
|
|
219
|
-
this._restoreState(saved);
|
|
220
|
-
}
|
|
221
266
|
}
|
|
222
267
|
_pickFromBuffer(array, size) {
|
|
223
268
|
const selectedObjects = new Set();
|
|
269
|
+
const esc = this._escapeValue;
|
|
224
270
|
for (let i = 0; i < size; i += 1) {
|
|
225
271
|
const id = array[i];
|
|
226
|
-
if (id !==
|
|
272
|
+
if (id !== esc) {
|
|
227
273
|
selectedObjects.add(id);
|
|
228
274
|
}
|
|
229
275
|
}
|
|
230
276
|
return selectedObjects;
|
|
231
277
|
}
|
|
232
|
-
_initHoldBuffer(
|
|
233
|
-
if (this._pbo && this._pboSize >=
|
|
278
|
+
_initHoldBuffer(sizeInBytes) {
|
|
279
|
+
if (this._pbo && this._pboSize >= sizeInBytes) {
|
|
234
280
|
return;
|
|
235
281
|
}
|
|
236
282
|
const { gl } = this;
|
|
237
283
|
const pbo = gl.createBuffer();
|
|
238
284
|
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
|
|
239
|
-
gl.bufferData(gl.PIXEL_PACK_BUFFER,
|
|
285
|
+
gl.bufferData(gl.PIXEL_PACK_BUFFER, sizeInBytes, gl.STREAM_READ);
|
|
240
286
|
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
|
|
241
|
-
this._pboSize =
|
|
287
|
+
this._pboSize = sizeInBytes;
|
|
242
288
|
if (this._pbo !== undefined) {
|
|
243
289
|
gl.deleteBuffer(this._pbo);
|
|
244
290
|
}
|