-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
Copy pathPickFramebuffer.js
125 lines (105 loc) · 3.46 KB
/
PickFramebuffer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import BoundingRectangle from "../Core/BoundingRectangle.js";
import Color from "../Core/Color.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import FramebufferManager from "../Renderer/FramebufferManager.js";
import PassState from "../Renderer/PassState.js";
/**
* @private
*/
function PickFramebuffer(context) {
// Override per-command states
const passState = new PassState(context);
passState.blendingEnabled = false;
passState.scissorTest = {
enabled: true,
rectangle: new BoundingRectangle(),
};
passState.viewport = new BoundingRectangle();
this._context = context;
this._fb = new FramebufferManager({
depthStencil: true,
});
this._passState = passState;
this._width = 0;
this._height = 0;
}
PickFramebuffer.prototype.begin = function (screenSpaceRectangle, viewport) {
const context = this._context;
const width = viewport.width;
const height = viewport.height;
BoundingRectangle.clone(
screenSpaceRectangle,
this._passState.scissorTest.rectangle
);
// Create or recreate renderbuffers and framebuffer used for picking
this._width = width;
this._height = height;
this._fb.update(context, width, height);
this._passState.framebuffer = this._fb.framebuffer;
this._passState.viewport.width = width;
this._passState.viewport.height = height;
return this._passState;
};
const colorScratch = new Color();
PickFramebuffer.prototype.end = function (screenSpaceRectangle) {
const width = defaultValue(screenSpaceRectangle.width, 1.0);
const height = defaultValue(screenSpaceRectangle.height, 1.0);
const context = this._context;
const pixels = context.readPixels({
x: screenSpaceRectangle.x,
y: screenSpaceRectangle.y,
width: width,
height: height,
framebuffer: this._fb.framebuffer,
});
const max = Math.max(width, height);
const length = max * max;
const halfWidth = Math.floor(width * 0.5);
const halfHeight = Math.floor(height * 0.5);
let x = 0;
let y = 0;
let dx = 0;
let dy = -1;
// Spiral around the center pixel, this is a workaround until
// we can access the depth buffer on all browsers.
// The region does not have to square and the dimensions do not have to be odd, but
// loop iterations would be wasted. Prefer square regions where the size is odd.
for (let i = 0; i < length; ++i) {
if (
-halfWidth <= x &&
x <= halfWidth &&
-halfHeight <= y &&
y <= halfHeight
) {
const index = 4 * ((halfHeight - y) * width + x + halfWidth);
colorScratch.red = Color.byteToFloat(pixels[index]);
colorScratch.green = Color.byteToFloat(pixels[index + 1]);
colorScratch.blue = Color.byteToFloat(pixels[index + 2]);
colorScratch.alpha = Color.byteToFloat(pixels[index + 3]);
const object = context.getObjectByPickColor(colorScratch);
if (defined(object)) {
return object;
}
}
// if (top right || bottom left corners) || (top left corner) || (bottom right corner + (1, 0))
// change spiral direction
if (x === y || (x < 0 && -x === y) || (x > 0 && x === 1 - y)) {
const temp = dx;
dx = -dy;
dy = temp;
}
x += dx;
y += dy;
}
return undefined;
};
PickFramebuffer.prototype.isDestroyed = function () {
return false;
};
PickFramebuffer.prototype.destroy = function () {
this._fb.destroy();
return destroyObject(this);
};
export default PickFramebuffer;