@xeokit/xeokit-sdk 2.6.92 → 2.6.93

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.
Files changed (182) hide show
  1. package/dist/xeokit-sdk.cjs.js +13074 -44661
  2. package/dist/xeokit-sdk.es.js +13075 -44660
  3. package/dist/xeokit-sdk.es5.js +1403 -3501
  4. package/dist/xeokit-sdk.min.cjs.js +8 -8
  5. package/dist/xeokit-sdk.min.es.js +8 -8
  6. package/dist/xeokit-sdk.min.es5.js +7 -7
  7. package/package.json +1 -1
  8. package/src/viewer/Viewer.js +55 -103
  9. package/src/viewer/scene/canvas/Canvas.js +3 -43
  10. package/src/viewer/scene/geometry/ReadableGeometry.js +0 -25
  11. package/src/viewer/scene/lights/CubeTexture.js +0 -13
  12. package/src/viewer/scene/lights/DirLight.js +2 -1
  13. package/src/viewer/scene/lights/PointLight.js +2 -1
  14. package/src/viewer/scene/materials/Texture.js +0 -9
  15. package/src/viewer/scene/math/rtcCoords.js +1 -38
  16. package/src/viewer/scene/mesh/Mesh.js +454 -212
  17. package/src/viewer/scene/mesh/draw/DrawShaderSource.js +305 -1504
  18. package/src/viewer/scene/mesh/draw/LambertShaderSource.js +35 -0
  19. package/src/viewer/scene/mesh/emphasis/EmphasisShaderSource.js +34 -0
  20. package/src/viewer/scene/mesh/occlusion/OcclusionShaderSource.js +9 -158
  21. package/src/viewer/scene/mesh/pick/PickMeshShaderSource.js +21 -151
  22. package/src/viewer/scene/mesh/pick/PickTriangleShaderSource.js +12 -124
  23. package/src/viewer/scene/mesh/shadow/ShadowShaderSource.js +22 -97
  24. package/src/viewer/scene/model/SceneModel.js +248 -852
  25. package/src/viewer/scene/model/SceneModelEntity.js +4 -4
  26. package/src/viewer/scene/model/SceneModelMesh.js +3 -3
  27. package/src/viewer/scene/model/layer/DTXLayer.js +940 -0
  28. package/src/viewer/scene/model/layer/Layer.js +788 -0
  29. package/src/viewer/scene/model/layer/VBOLayer.js +1019 -0
  30. package/src/viewer/scene/model/layer/programs/ColorProgram.js +35 -0
  31. package/src/viewer/scene/model/layer/programs/ColorTextureProgram.js +46 -0
  32. package/src/viewer/scene/model/layer/programs/DepthProgram.js +11 -0
  33. package/src/viewer/scene/model/layer/programs/EdgesProgram.js +12 -0
  34. package/src/viewer/scene/model/layer/programs/FlatColorProgram.js +24 -0
  35. package/src/viewer/scene/model/layer/programs/OcclusionProgram.js +12 -0
  36. package/src/viewer/scene/model/layer/programs/PBRProgram.js +180 -0
  37. package/src/viewer/scene/model/layer/programs/PickDepthProgram.js +30 -0
  38. package/src/viewer/scene/model/layer/programs/PickMeshProgram.js +12 -0
  39. package/src/viewer/scene/model/layer/programs/PickNormalsProgram.js +18 -0
  40. package/src/viewer/scene/model/layer/programs/ShadowProgram.js +23 -0
  41. package/src/viewer/scene/model/layer/programs/SilhouetteProgram.js +15 -0
  42. package/src/viewer/scene/model/layer/programs/SnapProgram.js +29 -0
  43. package/src/viewer/scene/scene/Scene.js +21 -38
  44. package/src/viewer/scene/webgl/ArrayBuf.js +5 -20
  45. package/src/viewer/scene/webgl/FrameContext.js +14 -86
  46. package/src/viewer/scene/webgl/RenderBuffer.js +62 -262
  47. package/src/viewer/scene/webgl/Renderer.js +880 -843
  48. package/src/viewer/scene/webgl/WebGLRenderer.js +612 -0
  49. package/src/viewer/scene/webgl/occlusion/OcclusionLayer.js +60 -132
  50. package/src/viewer/scene/webgl/occlusion/OcclusionTester.js +167 -328
  51. package/src/viewer/scene/mesh/draw/DrawRenderer.js +0 -998
  52. package/src/viewer/scene/mesh/emphasis/EmphasisEdgesRenderer.js +0 -259
  53. package/src/viewer/scene/mesh/emphasis/EmphasisEdgesShaderSource.js +0 -160
  54. package/src/viewer/scene/mesh/emphasis/EmphasisFillRenderer.js +0 -286
  55. package/src/viewer/scene/mesh/emphasis/EmphasisFillShaderSource.js +0 -263
  56. package/src/viewer/scene/mesh/occlusion/OcclusionRenderer.js +0 -221
  57. package/src/viewer/scene/mesh/pick/PickMeshRenderer.js +0 -223
  58. package/src/viewer/scene/mesh/pick/PickTriangleRenderer.js +0 -200
  59. package/src/viewer/scene/mesh/shadow/ShadowRenderer.js +0 -214
  60. package/src/viewer/scene/model/RENDER_PASSES.js +0 -34
  61. package/src/viewer/scene/model/dtx/BindableDataTexture.js +0 -31
  62. package/src/viewer/scene/model/dtx/lines/DTXLinesBuffer.js +0 -27
  63. package/src/viewer/scene/model/dtx/lines/DTXLinesLayer.js +0 -897
  64. package/src/viewer/scene/model/dtx/lines/DTXLinesState.js +0 -46
  65. package/src/viewer/scene/model/dtx/lines/DTXLinesTextureFactory.js +0 -411
  66. package/src/viewer/scene/model/dtx/lines/dataTextureRamStats.js +0 -49
  67. package/src/viewer/scene/model/dtx/lines/rebucketPositions.js +0 -208
  68. package/src/viewer/scene/model/dtx/lines/renderers/DTXLinesColorRenderer.js +0 -389
  69. package/src/viewer/scene/model/dtx/lines/renderers/DTXLinesRenderers.js +0 -65
  70. package/src/viewer/scene/model/dtx/triangles/DTXTrianglesLayer.js +0 -1399
  71. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesBuffer.js +0 -46
  72. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesState.js +0 -250
  73. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesTextureFactory.js +0 -518
  74. package/src/viewer/scene/model/dtx/triangles/lib/dataTextureRamStats.js +0 -54
  75. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesColorRenderer.js +0 -639
  76. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesDepthRenderer.js +0 -464
  77. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesEdgesColorRenderer.js +0 -423
  78. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesEdgesRenderer.js +0 -431
  79. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesNormalsRenderer.js +0 -345
  80. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesOcclusionRenderer.js +0 -436
  81. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickDepthRenderer.js +0 -466
  82. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickMeshRenderer.js +0 -437
  83. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickNormalsFlatRenderer.js +0 -460
  84. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickNormalsRenderer.js +0 -437
  85. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesRenderers.js +0 -307
  86. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSilhouetteRenderer.js +0 -472
  87. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSnapInitRenderer.js +0 -474
  88. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSnapRenderer.js +0 -435
  89. package/src/viewer/scene/model/vbo/ScratchMemory.js +0 -63
  90. package/src/viewer/scene/model/vbo/VBORenderer.js +0 -664
  91. package/src/viewer/scene/model/vbo/batching/lines/VBOBatchingLinesLayer.js +0 -704
  92. package/src/viewer/scene/model/vbo/batching/lines/lib/VBOBatchingLinesBuffer.js +0 -19
  93. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLineSnapInitRenderer.js +0 -318
  94. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesColorRenderer.js +0 -114
  95. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesRenderers.js +0 -99
  96. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSilhouetteRenderer.js +0 -124
  97. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapInitRenderer.js +0 -313
  98. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapRenderer.js +0 -311
  99. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOSceneModelLineBatchingRenderer.js +0 -24
  100. package/src/viewer/scene/model/vbo/batching/points/VBOBatchingPointsLayer.js +0 -688
  101. package/src/viewer/scene/model/vbo/batching/points/VBOBatchingPointsRenderer.js +0 -24
  102. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsColorRenderer.js +0 -168
  103. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsOcclusionRenderer.js +0 -136
  104. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsPickDepthRenderer.js +0 -156
  105. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsPickMeshRenderer.js +0 -148
  106. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsRenderers.js +0 -144
  107. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsShadowRenderer.js +0 -107
  108. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSilhouetteRenderer.js +0 -146
  109. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapInitRenderer.js +0 -315
  110. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapRenderer.js +0 -305
  111. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesBuffer.js +0 -24
  112. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js +0 -1346
  113. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesColorRenderer.js +0 -131
  114. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesEmphasisRenderer.js +0 -135
  115. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesRenderer.js +0 -12
  116. package/src/viewer/scene/model/vbo/batching/triangles/renderers/Renderers.js +0 -403
  117. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesBatchingRenderer.js +0 -35
  118. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorRenderer.js +0 -252
  119. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorTextureRenderer.js +0 -294
  120. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesDepthRenderer.js +0 -115
  121. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesFlatColorRenderer.js +0 -246
  122. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesNormalsRenderer.js +0 -139
  123. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesOcclusionRenderer.js +0 -125
  124. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPBRRenderer.js +0 -561
  125. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickDepthRenderer.js +0 -144
  126. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickMeshRenderer.js +0 -130
  127. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickNormalsFlatRenderer.js +0 -121
  128. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickNormalsRenderer.js +0 -131
  129. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesShadowRenderer.js +0 -107
  130. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSilhouetteRenderer.js +0 -144
  131. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapInitRenderer.js +0 -313
  132. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapRenderer.js +0 -312
  133. package/src/viewer/scene/model/vbo/float16.js +0 -760
  134. package/src/viewer/scene/model/vbo/instancing/lines/VBOInstancingLinesLayer.js +0 -711
  135. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesColorRenderer.js +0 -148
  136. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesRenderer.js +0 -26
  137. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesRenderers.js +0 -139
  138. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSilhouetteRenderer.js +0 -127
  139. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapInitRenderer.js +0 -308
  140. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapRenderer.js +0 -299
  141. package/src/viewer/scene/model/vbo/instancing/points/VBOInstancingPointsLayer.js +0 -733
  142. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsColorRenderer.js +0 -174
  143. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsDepthRenderer.js +0 -156
  144. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsOcclusionRenderer.js +0 -149
  145. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsPickDepthRenderer.js +0 -170
  146. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsPickMeshRenderer.js +0 -152
  147. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsRenderer.js +0 -26
  148. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsRenderers.js +0 -174
  149. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsShadowRenderer.js +0 -119
  150. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSilhouetteRenderer.js +0 -154
  151. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapInitRenderer.js +0 -307
  152. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapRenderer.js +0 -293
  153. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingTrianglesRenderer.js +0 -150
  154. package/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js +0 -1289
  155. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesColorRenderer.js +0 -135
  156. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesEmphasisRenderer.js +0 -137
  157. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesRenderer.js +0 -12
  158. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/Renderers.js +0 -403
  159. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorRenderer.js +0 -267
  160. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorTextureRenderer.js +0 -299
  161. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesDepthRenderer.js +0 -126
  162. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesFlatColorRenderer.js +0 -255
  163. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesFlatNormalsRenderer.js +0 -130
  164. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesInstancingRenderer.js +0 -32
  165. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesNormalsRenderer.js +0 -138
  166. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesOcclusionRenderer.js +0 -128
  167. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPBRRenderer.js +0 -561
  168. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickDepthRenderer.js +0 -149
  169. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickMeshRenderer.js +0 -136
  170. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickNormalsFlatRenderer.js +0 -130
  171. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickNormalsRenderer.js +0 -145
  172. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesShadowRenderer.js +0 -113
  173. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSilhouetteRenderer.js +0 -148
  174. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapInitRenderer.js +0 -319
  175. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapRenderer.js +0 -310
  176. package/src/viewer/scene/webgl/Attribute.js +0 -23
  177. package/src/viewer/scene/webgl/Program.js +0 -181
  178. package/src/viewer/scene/webgl/RenderBufferManager.js +0 -34
  179. package/src/viewer/scene/webgl/Sampler.js +0 -18
  180. package/src/viewer/scene/webgl/Shader.js +0 -49
  181. package/src/viewer/scene/webgl/sao/SAODepthLimitedBlurRenderer.js +0 -324
  182. package/src/viewer/scene/webgl/sao/SAOOcclusionRenderer.js +0 -384
@@ -5,14 +5,27 @@ import {WEBGL_INFO} from '../webglInfo.js';
5
5
  import {Map} from "../utils/Map.js";
6
6
  import {PickResult} from "./PickResult.js";
7
7
  import {OcclusionTester} from "./occlusion/OcclusionTester.js";
8
- import {SAOOcclusionRenderer} from "./sao/SAOOcclusionRenderer.js";
9
8
  import {createRTCViewMat} from "../math/rtcCoords.js";
10
- import {SAODepthLimitedBlurRenderer} from "./sao/SAODepthLimitedBlurRenderer.js";
11
- import {RenderBufferManager} from "./RenderBufferManager.js";
9
+ import {RenderBuffer} from "./RenderBuffer.js";
12
10
  import {getExtension} from "./getExtension.js";
11
+ import {createProgramVariablesState} from "./WebGLRenderer.js";
12
+ import {ArrayBuf} from "./ArrayBuf.js";
13
+
14
+ const OCCLUSION_TEST_MODE = false;
13
15
 
14
16
  const vec3_0 = math.vec3([0,0,0]);
15
17
 
18
+ const iota = (n) => { const ret = [ ]; for (let i = 0; i < n; ++i) ret.push(i); return ret; };
19
+
20
+ const bitShiftScreenZ = math.vec4([1.0 / (256.0 * 256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0]);
21
+
22
+ const pixelToInt = pix => pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24);
23
+
24
+ const toWorldNormal = (n) => math.normalizeVec3(math.divVec3Scalar(n, math.MAX_INT, math.vec3()));
25
+ const toWorldPos = (p, origin, scale) => math.vec3([ p[0] * scale[0] + origin[0],
26
+ p[1] * scale[1] + origin[1],
27
+ p[2] * scale[2] + origin[2] ]);
28
+
16
29
  /**
17
30
  * @private
18
31
  */
@@ -20,7 +33,7 @@ const Renderer = function (scene, options) {
20
33
 
21
34
  options = options || {};
22
35
 
23
- const frameCtx = new FrameContext(scene);
36
+ const frameCtx = new FrameContext();
24
37
  const canvas = scene.canvas.canvas;
25
38
  /**
26
39
  * @type {WebGL2RenderingContext}
@@ -49,15 +62,344 @@ const Renderer = function (scene, options) {
49
62
  let pbrEnabled = true;
50
63
  let colorTextureEnabled = true;
51
64
 
52
- const renderBufferManager = new RenderBufferManager(scene);
65
+ const renderBufferManager = (function() {
66
+ const renderBuffersBasic = {};
67
+ const renderBuffersScaled = {};
68
+ return {
69
+ getRenderBuffer: (id, colorFormats, hasDepthTexture) => {
70
+ const renderBuffers = (scene.canvas.resolutionScale === 1.0) ? renderBuffersBasic : renderBuffersScaled;
71
+ if (! renderBuffers[id]) {
72
+ renderBuffers[id] = new RenderBuffer(gl, colorFormats, hasDepthTexture);
73
+ }
74
+ return renderBuffers[id];
75
+ },
76
+ destroy: () => {
77
+ Object.values(renderBuffersBasic ).forEach(buf => buf.destroy());
78
+ Object.values(renderBuffersScaled).forEach(buf => buf.destroy());
79
+ }
80
+ };
81
+ })();
82
+
83
+ const SAOProgram = (gl, name, programVariablesState, createOutColorDefinition) => {
84
+ const programVariables = programVariablesState.programVariables;
85
+
86
+ const uViewportInv = programVariables.createUniform("vec2", "uViewportInv");
87
+ const uCameraNear = programVariables.createUniform("float", "uCameraNear");
88
+ const uCameraFar = programVariables.createUniform("float", "uCameraFar");
89
+ const uDepthTexture = programVariables.createUniform("sampler2D", "uDepthTexture");
90
+
91
+ const uv = programVariables.createAttribute("vec2", "uv");
92
+ const vUV = programVariables.createVarying("vec2", "vUV", () => uv);
93
+ const outColor = programVariables.createOutput("vec4", "outColor");
94
+
95
+ const getOutColor = programVariables.createFragmentDefinition(
96
+ "getOutColor",
97
+ (name, src) => {
98
+ const getDepth = "getDepth";
99
+ src.push(`
100
+ float ${getDepth}(const in vec2 uv) {
101
+ return texture(${uDepthTexture}, uv).r;
102
+ }
103
+ `);
104
+ src.push(createOutColorDefinition(name, vUV, uViewportInv, uCameraNear, uCameraFar, getDepth));
105
+ });
106
+
107
+ const [program, errors] = programVariablesState.buildProgram(
108
+ gl,
109
+ name,
110
+ {
111
+ clipPos: `vec4(2.0 * ${uv} - 1.0, 0.0, 1.0)`,
112
+ appendFragmentOutputs: (src) => src.push(`${outColor} = ${getOutColor}();`)
113
+ });
114
+
115
+ if (errors) {
116
+ console.error(errors.join("\n"));
117
+ throw errors;
118
+ } else {
119
+ const uvs = new Float32Array([1,1, 0,1, 0,0, 1,0]);
120
+ const uvBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, uvs, uvs.length, 2, gl.STATIC_DRAW);
121
+
122
+ // Mitigation: if Uint8Array is used, the geometry is corrupted on OSX when using Chrome with data-textures
123
+ const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
124
+ const indicesBuf = new ArrayBuf(gl, gl.ELEMENT_ARRAY_BUFFER, indices, indices.length, 1, gl.STATIC_DRAW);
125
+
126
+ return {
127
+ destroy: program.destroy,
128
+ bind: (viewportSize, project, depthTexture) => {
129
+ program.bind();
130
+ uViewportInv.setInputValue([1 / viewportSize[0], 1 / viewportSize[1]]);
131
+ uCameraNear.setInputValue(project.near);
132
+ uCameraFar.setInputValue(project.far);
133
+ uDepthTexture.setInputValue(depthTexture);
134
+ uv.setInputValue(uvBuf);
135
+ },
136
+ draw: () => {
137
+ indicesBuf.bind();
138
+ gl.drawElements(gl.TRIANGLES, indicesBuf.numItems, indicesBuf.itemType, 0);
139
+ }
140
+ };
141
+ }
142
+ };
143
+
144
+ // SAO implementation inspired from previous SAO work in THREE.js by ludobaka / ludobaka.github.io and bhouston
145
+ const saoOcclusionRenderer = (function() {
146
+ let currentRenrerer = null;
147
+ let curNumSamples = null;
148
+ return {
149
+ destroy: () => currentRenrerer && currentRenrerer.destroy(),
150
+ render: (viewportSize, project, sao, depthTexture) => {
151
+ const numSamples = Math.floor(sao.numSamples);
152
+ if (curNumSamples !== numSamples) {
153
+ currentRenrerer && currentRenrerer.destroy();
154
+
155
+ const programVariablesState = createProgramVariablesState();
156
+
157
+ const programVariables = programVariablesState.programVariables;
158
+
159
+ const uProjectMatrix = programVariables.createUniform("mat4", "uProjectMatrix");
160
+ const uInvProjMatrix = programVariables.createUniform("mat4", "uInvProjMatrix");
161
+ const uPerspective = programVariables.createUniform("bool", "uPerspective");
162
+ const uScale = programVariables.createUniform("float", "uScale");
163
+ const uIntensity = programVariables.createUniform("float", "uIntensity");
164
+ const uBias = programVariables.createUniform("float", "uBias");
165
+ const uKernelRadius = programVariables.createUniform("float", "uKernelRadius");
166
+ const uMinResolution = programVariables.createUniform("float", "uMinResolution");
167
+ const uRandomSeed = programVariables.createUniform("float", "uRandomSeed");
168
+
169
+ const program = SAOProgram(
170
+ gl,
171
+ "SAOOcclusionRenderer",
172
+ programVariablesState,
173
+ (name, vUV, uViewportInv, uCameraNear, uCameraFar, getDepth) => {
174
+ return `
175
+ #define EPSILON 1e-6
176
+ #define PI 3.14159265359
177
+ #define PI2 6.28318530718
178
+ #define NUM_SAMPLES ${numSamples}
179
+ #define NUM_RINGS 4
180
+
181
+ const vec3 packFactors = vec3(256. * 256. * 256., 256. * 256., 256.);
182
+
183
+ vec4 packFloatToRGBA(const in float v) {
184
+ vec4 r = vec4(fract(v * packFactors), v);
185
+ r.yzw -= r.xyz / 256.;
186
+ return r * 256. / 255.;
187
+ }
188
+
189
+ highp float rand(const in vec2 uv) {
190
+ const highp float a = 12.9898, b = 78.233, c = 43758.5453;
191
+ return fract(sin(mod(dot(uv, vec2(a, b)), PI)) * c);
192
+ }
193
+
194
+ vec3 getViewPos(const in vec2 screenPos, const in float depth) {
195
+ float near = ${uCameraNear};
196
+ float far = ${uCameraFar};
197
+ float viewZ = (${uPerspective}
198
+ ? ((near * far) / ((far - near) * depth - far))
199
+ : (depth * (near - far) - near));
200
+ float clipW = ${uProjectMatrix}[2][3] * viewZ + ${uProjectMatrix}[3][3];
201
+ return (${uInvProjMatrix} * (clipW * vec4((vec3(screenPos, depth) - 0.5) * 2.0, 1.0))).xyz;
202
+ }
203
+
204
+ vec4 ${name}() {
205
+ float centerDepth = ${getDepth}(${vUV});
206
+ if (centerDepth >= (1.0 - EPSILON)) {
207
+ discard;
208
+ }
209
+
210
+ vec3 centerViewPosition = getViewPos(${vUV}, centerDepth);
211
+ float scaleDividedByCameraFar = ${uScale} / ${uCameraFar};
212
+ float minResolutionMultipliedByCameraFar = ${uMinResolution} * ${uCameraFar};
213
+ vec3 centerViewNormal = normalize(cross(dFdx(centerViewPosition), dFdy(centerViewPosition)));
214
+
215
+ vec2 radiusStep = ${uKernelRadius} * ${uViewportInv} / float(NUM_SAMPLES);
216
+ vec2 radius = radiusStep;
217
+ const float angleStep = PI2 * float(NUM_RINGS) / float(NUM_SAMPLES);
218
+ float angle = PI2 * rand(${vUV} + ${uRandomSeed});
219
+
220
+ float occlusionSum = 0.0;
221
+ float weightSum = 0.0;
222
+
223
+ for (int i = 0; i < NUM_SAMPLES; i++) {
224
+ vec2 sampleUv = ${vUV} + vec2(cos(angle), sin(angle)) * radius;
225
+ radius += radiusStep;
226
+ angle += angleStep;
227
+
228
+ float sampleDepth = ${getDepth}(sampleUv);
229
+ if (sampleDepth >= (1.0 - EPSILON)) {
230
+ continue;
231
+ }
232
+
233
+ vec3 sampleViewPosition = getViewPos(sampleUv, sampleDepth);
234
+ vec3 viewDelta = sampleViewPosition - centerViewPosition;
235
+ float scaledScreenDistance = scaleDividedByCameraFar * length(viewDelta);
236
+ occlusionSum += max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - ${uBias}) / (1.0 + scaledScreenDistance * scaledScreenDistance );
237
+ weightSum += 1.0;
238
+ }
239
+
240
+ return packFloatToRGBA(1.0 - occlusionSum * ${uIntensity} / weightSum);
241
+ }`;
242
+ });
243
+
244
+ currentRenrerer = {
245
+ destroy: program.destroy,
246
+ render: (viewportSize, project, sao, depthTexture) => {
247
+ program.bind(viewportSize, project, depthTexture);
248
+
249
+ uProjectMatrix.setInputValue(project.matrix);
250
+ uInvProjMatrix.setInputValue(project.inverseMatrix);
251
+ uPerspective.setInputValue(project.type === "Perspective");
252
+ uScale.setInputValue(sao.scale * project.far / 5);
253
+ uIntensity.setInputValue(sao.intensity);
254
+ uBias.setInputValue(sao.bias);
255
+ uKernelRadius.setInputValue(sao.kernelRadius);
256
+ uMinResolution.setInputValue(sao.minResolution);
257
+ uRandomSeed.setInputValue(Math.random());
258
+
259
+ program.draw();
260
+ }
261
+ };
262
+
263
+ curNumSamples = numSamples;
264
+ }
265
+
266
+ currentRenrerer.render(viewportSize, project, sao, depthTexture);
267
+ }
268
+ };
269
+ })();
270
+
271
+ const saoDepthLimitedBlurRenderer = (function() {
272
+ const blurStdDev = 4;
273
+ const blurDepthCutoff = 0.01;
274
+ const KERNEL_RADIUS = 16;
275
+
276
+ const createSampleOffsets = (uvIncrement) => {
277
+ const offsets = [];
278
+ for (let i = 0; i <= KERNEL_RADIUS + 1; i++) {
279
+ offsets.push(uvIncrement[0] * i);
280
+ offsets.push(uvIncrement[1] * i);
281
+ }
282
+ return new Float32Array(offsets);
283
+ };
284
+
285
+ const sampleOffsetsVer = createSampleOffsets([0, 1]);
286
+ const sampleOffsetsHor = createSampleOffsets([1, 0]);
287
+
288
+ const gaussian = (i, stdDev) => Math.exp(-(i * i) / (2.0 * (stdDev * stdDev))) / (Math.sqrt(2.0 * Math.PI) * stdDev);
289
+ const sampleWeights = new Float32Array(iota(KERNEL_RADIUS + 1).map(i => gaussian(i, blurStdDev))); // TODO: Optimize
290
+
291
+ const programVariablesState = createProgramVariablesState();
53
292
 
54
- let snapshotBound = false;
293
+ const programVariables = programVariablesState.programVariables;
55
294
 
56
- const bindOutputFrameBuffer = null;
57
- const unbindOutputFrameBuffer = null;
295
+ const uDepthCutoff = programVariables.createUniform("float", "uDepthCutoff");
296
+ const uSampleOffsets = programVariables.createUniformArray("vec2", "uSampleOffsets", KERNEL_RADIUS + 1);
297
+ const uSampleWeights = programVariables.createUniformArray("float", "uSampleWeights", KERNEL_RADIUS + 1);
58
298
 
59
- const saoOcclusionRenderer = new SAOOcclusionRenderer(scene);
60
- const saoDepthLimitedBlurRenderer = new SAODepthLimitedBlurRenderer(scene);
299
+ const uOcclusionTex = programVariables.createUniform("sampler2D", "uOcclusionTex");
300
+
301
+ const program = SAOProgram(
302
+ gl,
303
+ "SAODepthLimitedBlurRenderer",
304
+ programVariablesState,
305
+ (name, vUV, uViewportInv, uCameraNear, uCameraFar, getDepth) => {
306
+ return `
307
+ #define EPSILON 1e-6
308
+
309
+ const vec3 packFactors = vec3(256. * 256. * 256., 256. * 256., 256.);
310
+
311
+ vec4 packFloatToRGBA(const in float v) {
312
+ vec4 r = vec4(fract(v * packFactors), v);
313
+ r.yzw -= r.xyz / 256.;
314
+ return r * 256. / 255.;
315
+ }
316
+
317
+ float getOcclusion(const in vec2 uv) {
318
+ vec4 v = texture(${uOcclusionTex}, uv);
319
+ return dot(floor(v * 255.0 + 0.5) / 255.0, 255. / 256. / vec4(packFactors, 1.)); // unpackRGBAToFloat
320
+ }
321
+
322
+ float getViewZ(const in float depth) {
323
+ return (${uCameraNear} * ${uCameraFar}) / ((${uCameraFar} - ${uCameraNear}) * depth - ${uCameraFar});
324
+ }
325
+
326
+ vec4 ${name}() {
327
+ float centerDepth = ${getDepth}(${vUV});
328
+ if (centerDepth >= (1.0 - EPSILON)) {
329
+ discard;
330
+ }
331
+
332
+ float centerViewZ = getViewZ(centerDepth);
333
+ bool rBreak = false;
334
+ bool lBreak = false;
335
+
336
+ float weightSum = ${uSampleWeights}[0];
337
+ float occlusionSum = getOcclusion(${vUV}) * weightSum;
338
+
339
+ for (int i = 1; i <= ${KERNEL_RADIUS}; i++) {
340
+ float sampleWeight = ${uSampleWeights}[i];
341
+ vec2 sampleUVOffset = ${uSampleOffsets}[i] * ${uViewportInv};
342
+
343
+ if (! rBreak) {
344
+ vec2 rSampleUV = ${vUV} + sampleUVOffset;
345
+ if (abs(centerViewZ - getViewZ(${getDepth}(rSampleUV))) > ${uDepthCutoff}) {
346
+ rBreak = true;
347
+ } else {
348
+ occlusionSum += getOcclusion(rSampleUV) * sampleWeight;
349
+ weightSum += sampleWeight;
350
+ }
351
+ }
352
+
353
+ if (! lBreak) {
354
+ vec2 lSampleUV = ${vUV} - sampleUVOffset;
355
+ if (abs(centerViewZ - getViewZ(${getDepth}(lSampleUV))) > ${uDepthCutoff}) {
356
+ lBreak = true;
357
+ } else {
358
+ occlusionSum += getOcclusion(lSampleUV) * sampleWeight;
359
+ weightSum += sampleWeight;
360
+ }
361
+ }
362
+ }
363
+
364
+ return packFloatToRGBA(occlusionSum / weightSum);
365
+ }`;
366
+ });
367
+
368
+ return {
369
+ destroy: program.destroy,
370
+ render: (viewportSize, project, direction, depthTexture, occlusionTexture) => {
371
+ program.bind(viewportSize, project, depthTexture);
372
+
373
+ uDepthCutoff.setInputValue(blurDepthCutoff);
374
+ uSampleOffsets.setInputValue((direction === 0) ? sampleOffsetsHor : sampleOffsetsVer);
375
+ uSampleWeights.setInputValue(sampleWeights);
376
+ uOcclusionTex.setInputValue(occlusionTexture);
377
+
378
+ program.draw();
379
+ }
380
+ };
381
+ })();
382
+
383
+ const getSceneCameraViewParams = (function() {
384
+ let params = null; // scene.camera not defined yet
385
+ return function() {
386
+ if (! params) {
387
+ const camera = scene.camera;
388
+ params = {
389
+ get eye() { return camera.eye; },
390
+ get far() { return camera.project.far; },
391
+ get projMatrix() { return camera.projMatrix; },
392
+ get viewMatrix() { return camera.viewMatrix; },
393
+ get viewNormalMatrix() { return camera.viewNormalMatrix; }
394
+ };
395
+ }
396
+ return params;
397
+ };
398
+ })();
399
+
400
+ const getNearPlaneHeight = (camera, drawingBufferHeight) => ((camera.projection === "ortho")
401
+ ? 1.0
402
+ : (drawingBufferHeight / (2 * Math.tan(0.5 * camera.perspective.fov * Math.PI / 180.0))));
61
403
 
62
404
  this.scene = scene;
63
405
 
@@ -109,19 +451,6 @@ const Renderer = function (scene, options) {
109
451
  imageDirty = true;
110
452
  };
111
453
 
112
- this.webglContextLost = function () {
113
- };
114
-
115
- this.webglContextRestored = function (gl) {
116
-
117
- // renderBufferManager.webglContextRestored(gl);
118
-
119
- saoOcclusionRenderer.init();
120
- saoDepthLimitedBlurRenderer.init();
121
-
122
- imageDirty = true;
123
- };
124
-
125
454
  /**
126
455
  * Inserts a drawable into this renderer.
127
456
  * @private
@@ -201,13 +530,7 @@ const Renderer = function (scene, options) {
201
530
  const backgroundColor = scene.canvas.backgroundColorFromAmbientLight ? this.lights.getAmbientColorAndIntensity() : scene.canvas.backgroundColor;
202
531
  gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0);
203
532
  }
204
- if (bindOutputFrameBuffer) {
205
- bindOutputFrameBuffer(params.pass);
206
- }
207
533
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
208
- if (unbindOutputFrameBuffer) {
209
- unbindOutputFrameBuffer(params.pass);
210
- }
211
534
  };
212
535
 
213
536
  /**
@@ -223,14 +546,10 @@ const Renderer = function (scene, options) {
223
546
  * Renders inserted drawables.
224
547
  * @private
225
548
  */
226
- this.render = function (params) {
227
- params = params || {};
228
- if (params.force) {
229
- imageDirty = true;
230
- }
549
+ this.render = function(pass, clear) {
231
550
  updateDrawlist();
232
551
  if (imageDirty) {
233
- draw(params);
552
+ draw(pass, clear);
234
553
  stats.frame.frameCount++;
235
554
  imageDirty = false;
236
555
  }
@@ -238,243 +557,175 @@ const Renderer = function (scene, options) {
238
557
 
239
558
  function updateDrawlist() { // Prepares state-sorted array of drawables from maps of inserted drawables
240
559
  if (drawableListDirty) {
241
- buildDrawableList();
560
+ Object.values(drawableTypeInfo).forEach(drawableInfo => {
561
+ const drawableListPreCull = drawableInfo.drawableListPreCull;
562
+ let lenDrawableList = 0;
563
+ Object.values(drawableInfo.drawableMap).forEach(drawable => { drawableListPreCull[lenDrawableList++] = drawable; });
564
+ drawableListPreCull.length = lenDrawableList;
565
+ });
242
566
  drawableListDirty = false;
243
567
  stateSortDirty = true;
244
568
  }
245
569
  if (stateSortDirty) {
246
- sortDrawableList();
570
+ let lenDrawableList = 0;
571
+ Object.values(drawableTypeInfo).forEach(drawableInfo => {
572
+ drawableInfo.drawableListPreCull.forEach(drawable => { postSortDrawableList[lenDrawableList++] = drawable; });
573
+ });
574
+ postSortDrawableList.length = lenDrawableList;
575
+ postSortDrawableList.sort((a, b) => a.renderOrder - b.renderOrder);
247
576
  stateSortDirty = false;
248
577
  imageDirty = true;
249
578
  }
250
579
  if (imageDirty) { // Image is usually dirty because the camera moved
251
- cullDrawableList();
252
- }
253
- }
254
-
255
- function buildDrawableList() {
256
- for (let type in drawableTypeInfo) {
257
- if (drawableTypeInfo.hasOwnProperty(type)) {
258
- const drawableInfo = drawableTypeInfo[type];
259
- const drawableMap = drawableInfo.drawableMap;
260
- const drawableListPreCull = drawableInfo.drawableListPreCull;
261
- let lenDrawableList = 0;
262
- for (let id in drawableMap) {
263
- if (drawableMap.hasOwnProperty(id)) {
264
- drawableListPreCull[lenDrawableList++] = drawableMap[id];
580
+ let lenDrawableList = 0;
581
+ let lenUiList = 0;
582
+ postSortDrawableList.forEach(drawable => {
583
+ drawable.rebuildRenderFlags();
584
+ if (!drawable.renderFlags.culled) {
585
+ if (drawable.isUI) {
586
+ uiDrawableList[lenUiList++] = drawable;
587
+ } else {
588
+ postCullDrawableList[lenDrawableList++] = drawable;
265
589
  }
266
590
  }
267
- drawableListPreCull.length = lenDrawableList;
268
- }
591
+ });
592
+ postCullDrawableList.length = lenDrawableList;
593
+ uiDrawableList.length = lenUiList;
269
594
  }
270
595
  }
271
596
 
272
- function sortDrawableList() {
273
- let lenDrawableList = 0;
274
- for (let type in drawableTypeInfo) {
275
- if (drawableTypeInfo.hasOwnProperty(type)) {
276
- const drawableInfo = drawableTypeInfo[type];
277
- const drawableListPreCull = drawableInfo.drawableListPreCull;
278
- for (let i = 0, len = drawableListPreCull.length; i < len; i++) {
279
- const drawable = drawableListPreCull[i];
280
- postSortDrawableList[lenDrawableList++] = drawable;
281
- }
282
- }
283
- }
284
- postSortDrawableList.length = lenDrawableList;
285
- postSortDrawableList.sort((a, b) => {
286
- return a.renderOrder - b.renderOrder;
287
- });
288
- }
289
-
290
- function cullDrawableList() {
291
- let lenDrawableList = 0;
292
- let lenUiList = 0;
293
- for (let i = 0, len = postSortDrawableList.length; i < len; i++) {
294
- const drawable = postSortDrawableList[i];
295
- drawable.rebuildRenderFlags();
296
- if (!drawable.renderFlags.culled) {
297
- if (drawable.isUI) {
298
- uiDrawableList[lenUiList++] = drawable;
299
- } else {
300
- postCullDrawableList[lenDrawableList++] = drawable;
301
- }
302
- }
303
- }
304
- postCullDrawableList.length = lenDrawableList;
305
- uiDrawableList.length = lenUiList;
306
- }
307
-
308
- function draw(params) {
597
+ function draw(pass, clear) {
309
598
 
310
599
  const sao = scene.sao;
600
+ const occlusionTexture = saoEnabled && sao.possible && (sao.numSamples >= 1) && drawSAOBuffers(pass);
601
+
602
+ scene._lightsState.lights.forEach(light => {
603
+
604
+ if (light.castsShadow) {
605
+ const shadowRenderBuf = light.getShadowRenderBuf();
606
+ shadowRenderBuf.bind();
607
+
608
+ frameCtx.reset();
609
+ frameCtx.backfaces = true;
610
+ frameCtx.frontface = true;
611
+ frameCtx.viewParams.viewMatrix = light.getShadowViewMatrix();
612
+ frameCtx.viewParams.projMatrix = light.getShadowProjMatrix();
613
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(scene.camera, gl.drawingBufferHeight);
614
+
615
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
616
+
617
+ gl.clearColor(0, 0, 0, 1);
618
+ gl.enable(gl.DEPTH_TEST);
619
+ gl.disable(gl.BLEND);
620
+
621
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
622
+
623
+ Object.values(drawableTypeInfo).forEach(
624
+ drawableInfo => {
625
+ drawableInfo.drawableList.forEach(
626
+ drawable => {
627
+ if ((drawable.visible !== false) && drawable.castsShadow && drawable.drawShadow) {
628
+ if (drawable.renderFlags.colorOpaque) { // Transparent objects don't cast shadows (yet)
629
+ drawable.drawShadow(frameCtx);
630
+ }
631
+ }
632
+ });
633
+ });
311
634
 
312
- if (saoEnabled && sao.possible) {
313
- drawSAOBuffers(params);
314
- }
635
+ shadowRenderBuf.unbind();
636
+ }
637
+ });
315
638
 
316
- drawShadowMaps();
639
+ // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
640
+ // for (let ii = 0; ii < numVertexAttribs; ii++) {
641
+ // gl.disableVertexAttribArray(ii);
642
+ // }
643
+ //
644
+ shadowsDirty = false;
317
645
 
318
- drawColor(params);
646
+ drawColor(pass, clear, occlusionTexture);
319
647
  }
320
648
 
321
- function drawSAOBuffers(params) {
649
+ function drawSAOBuffers(pass) {
322
650
 
323
651
  const sao = scene.sao;
324
652
 
325
- // Render depth buffer
326
-
327
- const saoDepthRenderBuffer = renderBufferManager.getRenderBuffer("saoDepth", {
328
- depthTexture: true
329
- });
653
+ const size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
330
654
 
655
+ // Render depth buffer
656
+ const saoDepthRenderBuffer = renderBufferManager.getRenderBuffer("saoDepth", [], true);
657
+ saoDepthRenderBuffer.setSize(size);
331
658
  saoDepthRenderBuffer.bind();
332
- saoDepthRenderBuffer.clear();
333
- drawDepth(params);
334
- saoDepthRenderBuffer.unbind();
335
-
336
- // Render occlusion buffer
337
-
338
- const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion");
339
-
340
- occlusionRenderBuffer1.bind();
341
- occlusionRenderBuffer1.clear();
342
- saoOcclusionRenderer.render(saoDepthRenderBuffer);
343
- occlusionRenderBuffer1.unbind();
344
-
345
- if (sao.blur) {
346
-
347
- // Horizontally blur occlusion buffer 1 into occlusion buffer 2
348
-
349
- const occlusionRenderBuffer2 = renderBufferManager.getRenderBuffer("saoOcclusion2");
350
-
351
- occlusionRenderBuffer2.bind();
352
- occlusionRenderBuffer2.clear();
353
- saoDepthLimitedBlurRenderer.render(saoDepthRenderBuffer, occlusionRenderBuffer1, 0);
354
- occlusionRenderBuffer2.unbind();
355
-
356
- // Vertically blur occlusion buffer 2 back into occlusion buffer 1
357
-
358
- occlusionRenderBuffer1.bind();
359
- occlusionRenderBuffer1.clear();
360
- saoDepthLimitedBlurRenderer.render(saoDepthRenderBuffer, occlusionRenderBuffer2, 1);
361
- occlusionRenderBuffer1.unbind();
362
- }
363
- }
364
-
365
- function drawDepth(params) {
659
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
366
660
 
367
661
  frameCtx.reset();
368
- frameCtx.pass = params.pass;
662
+ frameCtx.pass = pass;
663
+ frameCtx.viewParams = getSceneCameraViewParams();
664
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(scene.camera, gl.drawingBufferHeight);
369
665
 
370
666
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
371
-
372
667
  gl.clearColor(0, 0, 0, 0);
373
668
  gl.enable(gl.DEPTH_TEST);
374
669
  gl.frontFace(gl.CCW);
375
670
  gl.enable(gl.CULL_FACE);
376
671
  gl.depthMask(true);
672
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
377
673
 
378
- if (params.clear !== false) {
379
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
380
- }
381
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
382
-
383
- const drawable = postCullDrawableList[i];
384
-
385
- if (drawable.culled === true || drawable.visible === false || !drawable.drawDepth || !drawable.saoEnabled) {
386
- continue;
387
- }
388
-
389
- if (drawable.renderFlags.colorOpaque) {
674
+ postCullDrawableList.forEach(drawable => {
675
+ if (!drawable.culled && drawable.visible && drawable.drawDepth && drawable.saoEnabled && drawable.renderFlags.colorOpaque) {
390
676
  drawable.drawDepth(frameCtx);
391
677
  }
392
- }
393
-
394
- // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
395
- // for (let ii = 0; ii < numVertexAttribs; ii++) {
396
- // gl.disableVertexAttribArray(ii);
397
- // }
398
-
399
- }
400
-
401
- function drawShadowMaps() {
402
-
403
- let lights = scene._lightsState.lights;
404
-
405
- for (let i = 0, len = lights.length; i < len; i++) {
406
- const light = lights[i];
407
- if (!light.castsShadow) {
408
- continue;
409
- }
410
- drawShadowMap(light);
411
- }
412
-
413
- // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174
414
- // for (let ii = 0; ii < numVertexAttribs; ii++) {
415
- // gl.disableVertexAttribArray(ii);
416
- // }
417
- //
418
- shadowsDirty = false;
419
- }
420
-
421
- function drawShadowMap(light) {
422
-
423
- const castsShadow = light.castsShadow;
424
-
425
- if (!castsShadow) {
426
- return;
427
- }
428
-
429
- const shadowRenderBuf = light.getShadowRenderBuf();
430
-
431
- if (!shadowRenderBuf) {
432
- return;
433
- }
678
+ });
434
679
 
435
- shadowRenderBuf.bind();
680
+ saoDepthRenderBuffer.unbind();
436
681
 
437
- frameCtx.reset();
682
+ const depthTexture = saoDepthRenderBuffer.depthTexture;
438
683
 
439
- frameCtx.backfaces = true;
440
- frameCtx.frontface = true;
441
- frameCtx.shadowViewMatrix = light.getShadowViewMatrix();
442
- frameCtx.shadowProjMatrix = light.getShadowProjMatrix();
684
+ // Render occlusion buffer
443
685
 
444
- gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
686
+ const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion");
687
+ occlusionRenderBuffer1.setSize(size);
688
+ occlusionRenderBuffer1.bind();
445
689
 
690
+ gl.viewport(0, 0, size[0], size[1]);
446
691
  gl.clearColor(0, 0, 0, 1);
447
- gl.enable(gl.DEPTH_TEST);
692
+ gl.disable(gl.DEPTH_TEST);
448
693
  gl.disable(gl.BLEND);
694
+ gl.frontFace(gl.CCW);
695
+ gl.clear(gl.COLOR_BUFFER_BIT);
449
696
 
450
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
697
+ saoOcclusionRenderer.render(size, scene.camera.project, sao, depthTexture);
451
698
 
452
- for (let type in drawableTypeInfo) {
699
+ occlusionRenderBuffer1.unbind();
453
700
 
454
- if (drawableTypeInfo.hasOwnProperty(type)) {
701
+ if (sao.blur) {
702
+ const occlusionRenderBuffer2 = renderBufferManager.getRenderBuffer("saoOcclusion2");
703
+ occlusionRenderBuffer2.setSize(size);
455
704
 
456
- const drawableInfo = drawableTypeInfo[type];
457
- const drawableList = drawableInfo.drawableList;
705
+ const blurSAO = (src, dst, direction) => {
706
+ dst.bind();
458
707
 
459
- for (let i = 0, len = drawableList.length; i < len; i++) {
708
+ gl.viewport(0, 0, size[0], size[1]);
709
+ gl.clearColor(0, 0, 0, 1);
710
+ gl.enable(gl.DEPTH_TEST);
711
+ gl.disable(gl.BLEND);
712
+ gl.frontFace(gl.CCW);
713
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
460
714
 
461
- const drawable = drawableList[i];
715
+ const project = scene.camera.project;
716
+ saoDepthLimitedBlurRenderer.render(size, project, direction, depthTexture, src.colorTextures[0]);
462
717
 
463
- if (drawable.visible === false || !drawable.castsShadow || !drawable.drawShadow) {
464
- continue;
465
- }
718
+ dst.unbind();
719
+ };
466
720
 
467
- if (drawable.renderFlags.colorOpaque) { // Transparent objects don't cast shadows (yet)
468
- drawable.drawShadow(frameCtx);
469
- }
470
- }
471
- }
721
+ blurSAO(occlusionRenderBuffer1, occlusionRenderBuffer2, 0); // horizontally
722
+ blurSAO(occlusionRenderBuffer2, occlusionRenderBuffer1, 1); // vertically
472
723
  }
473
724
 
474
- shadowRenderBuf.unbind();
725
+ return occlusionRenderBuffer1.colorTextures[0];
475
726
  }
476
727
 
477
- function drawColor(params) {
728
+ function drawColor(pass, clear, occlusionTexture) {
478
729
 
479
730
  const normalDrawSAOBin = [];
480
731
  const normalEdgesOpaqueBin = [];
@@ -500,10 +751,12 @@ const Renderer = function (scene, options) {
500
751
  const ambientColorAndIntensity = scene._lightsState.getAmbientColorAndIntensity();
501
752
 
502
753
  frameCtx.reset();
503
- frameCtx.pass = params.pass;
754
+ frameCtx.pass = pass;
504
755
  frameCtx.withSAO = false;
505
756
  frameCtx.pbrEnabled = pbrEnabled && !!scene.pbrEnabled;
506
757
  frameCtx.colorTextureEnabled = colorTextureEnabled && !!scene.colorTextureEnabled;
758
+ frameCtx.viewParams = getSceneCameraViewParams();
759
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(scene.camera, gl.drawingBufferHeight);
507
760
 
508
761
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
509
762
 
@@ -522,15 +775,9 @@ const Renderer = function (scene, options) {
522
775
 
523
776
  frameCtx.lineWidth = 1;
524
777
 
525
- const saoPossible = scene.sao.possible;
526
-
527
- if (saoEnabled && saoPossible) {
528
- const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion");
529
- frameCtx.occlusionTexture = occlusionRenderBuffer1 ? occlusionRenderBuffer1.getTexture() : null;
530
- } else {
531
- frameCtx.occlusionTexture = null;
532
-
533
- }
778
+ const sao = scene.sao;
779
+ frameCtx.saoParams = [gl.drawingBufferWidth, gl.drawingBufferHeight, scene.sao.blendCutoff, scene.sao.blendFactor];
780
+ frameCtx.occlusionTexture = occlusionTexture;
534
781
 
535
782
  let i;
536
783
  let len;
@@ -538,11 +785,7 @@ const Renderer = function (scene, options) {
538
785
 
539
786
  const startTime = Date.now();
540
787
 
541
- if (bindOutputFrameBuffer) {
542
- bindOutputFrameBuffer(params.pass);
543
- }
544
-
545
- if (params.clear !== false) {
788
+ if (clear) {
546
789
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
547
790
  }
548
791
 
@@ -582,7 +825,7 @@ const Renderer = function (scene, options) {
582
825
  const renderFlags = drawable.renderFlags;
583
826
 
584
827
  if (renderFlags.colorOpaque) {
585
- if (saoEnabled && saoPossible && drawable.saoEnabled) {
828
+ if (drawable.saoEnabled && occlusionTexture) {
586
829
  normalDrawSAOBin[normalDrawSAOBinLen++] = drawable;
587
830
  } else {
588
831
  drawable.drawColorOpaque(frameCtx);
@@ -723,7 +966,7 @@ const Renderer = function (scene, options) {
723
966
  // Transparent color fill
724
967
 
725
968
  if (normalFillTransparentBinLen > 0) {
726
- const eye = frameCtx.pickOrigin || scene.camera.eye;
969
+ const eye = frameCtx.viewParams.eye;
727
970
  normalFillTransparentBin.length = normalFillTransparentBinLen; // normalFillTransparentBin reused by renderDrawables calls, so needs to be truncated if necessary
728
971
  const byDist = normalFillTransparentBin.map(d => ({ drawable: d, distSq: math.distVec3(d.origin || vec3_0, eye) }));
729
972
  byDist.sort((a, b) => b.distSq - a.distSq);
@@ -901,12 +1144,30 @@ const Renderer = function (scene, options) {
901
1144
  for (let ii = 0; ii < numVertexAttribs; ii++) {
902
1145
  gl.disableVertexAttribArray(ii);
903
1146
  }
904
-
905
- if (unbindOutputFrameBuffer) {
906
- unbindOutputFrameBuffer(params.pass);
907
- }
908
1147
  }
909
1148
 
1149
+ const resetPickFrameCtx = (canvasPos, clipTransformDiv, camera, eye, projMatrix, viewMatrix, frameCtx) => {
1150
+ frameCtx.reset();
1151
+ frameCtx.backfaces = true;
1152
+ frameCtx.frontface = true; // "ccw"
1153
+
1154
+ frameCtx.viewParams.eye = eye;
1155
+ frameCtx.viewParams.projMatrix = projMatrix;
1156
+ frameCtx.viewParams.viewMatrix = viewMatrix;
1157
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(camera, gl.drawingBufferHeight);
1158
+
1159
+ const resolutionScale = scene.canvas.resolutionScale;
1160
+ frameCtx.pickClipPos = [
1161
+ canvasPos ? ( 2 * canvasPos[0] * resolutionScale / gl.drawingBufferWidth - 1) : 0,
1162
+ canvasPos ? (1 - 2 * canvasPos[1] * resolutionScale / gl.drawingBufferHeight) : 0
1163
+ ];
1164
+
1165
+ frameCtx.pickClipPosInv = [
1166
+ gl.drawingBufferWidth / clipTransformDiv,
1167
+ gl.drawingBufferHeight / clipTransformDiv
1168
+ ];
1169
+ };
1170
+
910
1171
  /**
911
1172
  * Picks an Entity.
912
1173
  * @private
@@ -914,11 +1175,18 @@ const Renderer = function (scene, options) {
914
1175
  this.pick = (function () {
915
1176
 
916
1177
  const tempVec3a = math.vec3();
1178
+ const tempVec3b = math.vec3();
1179
+ const tempVec4a = math.vec4();
1180
+ const tempVec4b = math.vec4();
1181
+ const tempVec4c = math.vec4();
1182
+ const tempVec4d = math.vec4();
1183
+ const tempVec4e = math.vec4();
917
1184
  const tempMat4a = math.mat4();
918
1185
  const tempMat4b = math.mat4();
1186
+ const tempMat4c = math.mat4();
1187
+ const tempMat4d = math.mat4();
919
1188
 
920
- const randomVec3 = math.vec3();
921
- const up = math.vec3([0, 1, 0]);
1189
+ const upVec = math.vec3([0, 1, 0]);
922
1190
  const _pickResult = new PickResult();
923
1191
 
924
1192
  const nearAndFar = math.vec2();
@@ -930,16 +1198,22 @@ const Renderer = function (scene, options) {
930
1198
  const worldSurfacePos = math.vec3();
931
1199
  const worldSurfaceNormal = math.vec3();
932
1200
 
1201
+ const pickBuffer = new RenderBuffer(gl);
1202
+ pickBuffer.setSize([1, 1]);
1203
+
1204
+ const pickNormalBuffer = new RenderBuffer(gl, [gl.RGBA32I]);
1205
+ pickNormalBuffer.setSize([3, 3]);
1206
+
933
1207
  return function (params, pickResult = _pickResult) {
934
1208
 
935
1209
  pickResult.reset();
936
1210
 
937
1211
  updateDrawlist();
938
1212
 
939
- let look;
940
1213
  let pickViewMatrix = null;
941
1214
  let pickProjMatrix = null;
942
1215
  let projection = null;
1216
+ const camera = scene.camera;
943
1217
 
944
1218
  pickResult.pickSurface = params.pickSurface;
945
1219
 
@@ -948,12 +1222,12 @@ const Renderer = function (scene, options) {
948
1222
  canvasPos[0] = params.canvasPos[0];
949
1223
  canvasPos[1] = params.canvasPos[1];
950
1224
 
951
- pickViewMatrix = scene.camera.viewMatrix;
952
- pickProjMatrix = scene.camera.projMatrix;
953
- projection = scene.camera.projection;
1225
+ pickViewMatrix = camera.viewMatrix;
1226
+ pickProjMatrix = camera.projMatrix;
1227
+ projection = camera.projection;
954
1228
 
955
- nearAndFar[0] = scene.camera.project.near;
956
- nearAndFar[1] = scene.camera.project.far;
1229
+ nearAndFar[0] = camera.project.near;
1230
+ nearAndFar[1] = camera.project.far;
957
1231
 
958
1232
  pickResult.canvasPos = params.canvasPos;
959
1233
 
@@ -965,33 +1239,37 @@ const Renderer = function (scene, options) {
965
1239
  if (params.matrix) {
966
1240
 
967
1241
  pickViewMatrix = params.matrix;
968
- pickProjMatrix = scene.camera.projMatrix;
969
- projection = scene.camera.projection;
1242
+ pickProjMatrix = camera.projMatrix;
1243
+ projection = camera.projection;
970
1244
 
971
- nearAndFar[0] = scene.camera.project.near;
972
- nearAndFar[1] = scene.camera.project.far;
1245
+ nearAndFar[0] = camera.project.near;
1246
+ nearAndFar[1] = camera.project.far;
973
1247
 
974
1248
  } else {
975
1249
 
976
1250
  worldRayOrigin.set(params.origin || [0, 0, 0]);
977
1251
  worldRayDir.set(params.direction || [0, 0, 1]);
978
1252
 
979
- look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a);
980
-
981
- randomVec3[0] = Math.random();
982
- randomVec3[1] = Math.random();
983
- randomVec3[2] = Math.random();
1253
+ const look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a);
1254
+ const up = tempVec3b;
984
1255
 
985
- math.normalizeVec3(randomVec3);
986
- math.cross3Vec3(worldRayDir, randomVec3, up);
1256
+ if (Math.abs(math.dotVec3(worldRayDir, upVec)) > (1 - 1e-6)) { // worldRayDir aligned with Y axis
1257
+ up[0] = 0;
1258
+ up[1] = 0;
1259
+ up[2] = Math.sign(worldRayDir[1]);
1260
+ } else {
1261
+ math.cross3Vec3(worldRayDir, upVec, up);
1262
+ math.cross3Vec3(up, worldRayDir, up);
1263
+ math.normalizeVec3(up, up);
1264
+ }
987
1265
 
988
1266
  pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b);
989
- // pickProjMatrix = scene.camera.projMatrix;
990
- pickProjMatrix = scene.camera.ortho.matrix;
1267
+ // pickProjMatrix = camera.projMatrix;
1268
+ pickProjMatrix = camera.ortho.matrix;
991
1269
  projection = "ortho";
992
1270
 
993
- nearAndFar[0] = scene.camera.ortho.near;
994
- nearAndFar[1] = scene.camera.ortho.far;
1271
+ nearAndFar[0] = camera.ortho.near;
1272
+ nearAndFar[1] = camera.ortho.far;
995
1273
 
996
1274
  pickResult.origin = worldRayOrigin;
997
1275
  pickResult.direction = worldRayDir;
@@ -1001,23 +1279,42 @@ const Renderer = function (scene, options) {
1001
1279
  canvasPos[1] = canvas.clientHeight * 0.5;
1002
1280
  }
1003
1281
 
1004
- for (let type in drawableTypeInfo) {
1005
- if (drawableTypeInfo.hasOwnProperty(type)) {
1006
- const drawableList = drawableTypeInfo[type].drawableList;
1007
- for (let i = 0, len = drawableList.length; i < len; i++) {
1008
- const drawable = drawableList[i];
1009
- if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
1010
- drawable.setPickMatrices(pickViewMatrix, pickProjMatrix);
1011
- }
1012
- }
1013
- }
1014
- }
1282
+ pickBuffer.bind();
1015
1283
 
1016
- const pickBuffer = renderBufferManager.getRenderBuffer("pick", {size: [1, 1]});
1284
+ const resetFrameCtx = (clipTransformDiv) => resetPickFrameCtx(canvasPos, clipTransformDiv, camera, pickResult.origin || camera.eye, pickProjMatrix || camera.projMatrix, pickViewMatrix || camera.viewMatrix, frameCtx);
1017
1285
 
1018
- pickBuffer.bind();
1286
+ // gpuPickPickable
1287
+ resetFrameCtx(1);
1288
+ frameCtx.pickInvisible = !!params.pickInvisible;
1289
+
1290
+ gl.viewport(0, 0, 1, 1);
1291
+ gl.depthMask(true);
1292
+ gl.enable(gl.DEPTH_TEST);
1293
+ gl.disable(gl.CULL_FACE);
1294
+ gl.disable(gl.BLEND);
1295
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1296
+
1297
+ const includeEntityIds = params.includeEntityIds;
1298
+ const excludeEntityIds = params.excludeEntityIds;
1299
+
1300
+ const renderDrawables = function(drawables) {
1301
+ drawables.forEach(drawable => {
1302
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawPickMesh // TODO: push this logic into drawable
1303
+ && ((! includeEntityIds) || includeEntityIds[drawable.id])
1304
+ && ((! excludeEntityIds) || (! excludeEntityIds[drawable.id]))) {
1305
+ drawable.drawPickMesh(frameCtx);
1306
+ }
1307
+ });
1308
+ };
1309
+
1310
+ renderDrawables(postCullDrawableList);
1311
+ if (uiDrawableList.length > 0) {
1312
+ gl.clear(gl.DEPTH_BUFFER_BIT);
1313
+ renderDrawables(uiDrawableList);
1314
+ }
1019
1315
 
1020
- const pickable = gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult);
1316
+ const pickID = pixelToInt(pickBuffer.read(0, 0));
1317
+ const pickable = (pickID >= 0) && pickIDs.items[pickID];
1021
1318
 
1022
1319
  if (!pickable) {
1023
1320
  pickBuffer.unbind();
@@ -1037,265 +1334,108 @@ const Renderer = function (scene, options) {
1037
1334
 
1038
1335
  if (pickable.canPickTriangle && pickable.canPickTriangle()) {
1039
1336
 
1040
- gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1041
-
1042
- pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, projection, pickResult);
1337
+ if (pickable.drawPickTriangles) {
1338
+ resetFrameCtx(1);
1339
+ // frameCtx.pickInvisible = !!params.pickInvisible;
1043
1340
 
1044
- pickResult.pickSurfacePrecision = false;
1341
+ gl.viewport(0, 0, 1, 1);
1342
+ gl.clearColor(0, 0, 0, 0);
1343
+ gl.enable(gl.DEPTH_TEST);
1344
+ gl.disable(gl.CULL_FACE);
1345
+ gl.disable(gl.BLEND);
1346
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1045
1347
 
1046
- } else {
1047
-
1048
- if (pickable.canPickWorldPos && pickable.canPickWorldPos()) {
1049
-
1050
- gpuPickWorldPos(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult);
1051
-
1052
- if (params.pickSurfaceNormal !== false) {
1053
- gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1054
- }
1348
+ pickable.drawPickTriangles(frameCtx);
1055
1349
 
1056
- pickResult.pickSurfacePrecision = false;
1350
+ pickResult.primIndex = 3 * pixelToInt(pickBuffer.read(0, 0)); // Convert from triangle number to first vertex in indices
1057
1351
  }
1058
- }
1059
- }
1060
- pickBuffer.unbind();
1061
- pickResult.entity = pickedEntity;
1062
- return pickResult;
1063
- };
1064
- })();
1065
-
1066
- function gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult) {
1067
-
1068
- const resolutionScale = scene.canvas.resolutionScale;
1069
-
1070
- frameCtx.reset();
1071
- frameCtx.backfaces = true;
1072
- frameCtx.frontface = true; // "ccw"
1073
- frameCtx.pickOrigin = pickResult.origin;
1074
- frameCtx.pickViewMatrix = pickViewMatrix;
1075
- frameCtx.pickProjMatrix = pickProjMatrix;
1076
- frameCtx.pickInvisible = !!params.pickInvisible;
1077
- frameCtx.pickClipPos = [
1078
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1079
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1080
- ];
1081
-
1082
- gl.viewport(0, 0, 1, 1);
1083
- gl.depthMask(true);
1084
- gl.enable(gl.DEPTH_TEST);
1085
- gl.disable(gl.CULL_FACE);
1086
- gl.disable(gl.BLEND);
1087
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1088
-
1089
- const includeEntityIds = params.includeEntityIds;
1090
- const excludeEntityIds = params.excludeEntityIds;
1091
-
1092
- const renderDrawables = function(drawables) {
1093
- for (let i = 0, len = drawables.length; i < len; i++) {
1094
- const drawable = drawables[i];
1095
- if (drawable.culled === true || drawable.visible === false) {
1096
- continue;
1097
- }
1098
- if (!drawable.drawPickMesh || (params.pickInvisible !== true && drawable.visible === false) || drawable.pickable === false) {
1099
- continue;
1100
- }
1101
- if (includeEntityIds && !includeEntityIds[drawable.id]) { // TODO: push this logic into drawable
1102
- continue;
1103
- }
1104
- if (excludeEntityIds && excludeEntityIds[drawable.id]) {
1105
- continue;
1106
- }
1107
- drawable.drawPickMesh(frameCtx);
1108
- }
1109
- };
1110
-
1111
- renderDrawables(postCullDrawableList);
1112
- if (uiDrawableList.length > 0) {
1113
- gl.clear(gl.DEPTH_BUFFER_BIT);
1114
- renderDrawables(uiDrawableList);
1115
- }
1116
-
1117
- const pix = pickBuffer.read(0, 0);
1118
- const pickID = pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24);
1119
-
1120
- if (pickID < 0) {
1121
- return;
1122
- }
1123
-
1124
- const pickable = pickIDs.items[pickID];
1125
-
1126
- return pickable;
1127
- }
1128
-
1129
- function gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1130
-
1131
- if (!pickable.drawPickTriangles) {
1132
- return;
1133
- }
1134
-
1135
- const resolutionScale = scene.canvas.resolutionScale;
1136
-
1137
- frameCtx.reset();
1138
- frameCtx.backfaces = true;
1139
- frameCtx.frontface = true; // "ccw"
1140
- frameCtx.pickOrigin = pickResult.origin;
1141
- frameCtx.pickViewMatrix = pickViewMatrix; // Can be null
1142
- frameCtx.pickProjMatrix = pickProjMatrix; // Can be null
1143
- // frameCtx.pickInvisible = !!params.pickInvisible;
1144
- frameCtx.pickClipPos = [
1145
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1146
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1147
- ];
1148
1352
 
1149
- gl.viewport(0, 0, 1, 1);
1150
-
1151
- gl.clearColor(0, 0, 0, 0);
1152
- gl.enable(gl.DEPTH_TEST);
1153
- gl.disable(gl.CULL_FACE);
1154
- gl.disable(gl.BLEND);
1155
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1156
-
1157
- pickable.drawPickTriangles(frameCtx);
1158
-
1159
- const pix = pickBuffer.read(0, 0);
1160
-
1161
- let primIndex = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256);
1162
-
1163
- primIndex *= 3; // Convert from triangle number to first vertex in indices
1164
-
1165
- pickResult.primIndex = primIndex;
1166
- }
1167
-
1168
- const gpuPickWorldPos = (function () {
1169
-
1170
- const tempVec4a = math.vec4();
1171
- const tempVec4b = math.vec4();
1172
- const tempVec4c = math.vec4();
1173
- const tempVec4d = math.vec4();
1174
- const tempVec4e = math.vec4();
1175
- const tempMat4a = math.mat4();
1176
- const tempMat4b = math.mat4();
1177
- const tempMat4c = math.mat4();
1178
-
1179
- return function (pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult) {
1180
-
1181
- const resolutionScale = scene.canvas.resolutionScale;
1182
-
1183
- frameCtx.reset();
1184
- frameCtx.backfaces = true;
1185
- frameCtx.frontface = true; // "ccw"
1186
- frameCtx.pickOrigin = pickResult.origin;
1187
- frameCtx.pickViewMatrix = pickViewMatrix;
1188
- frameCtx.pickProjMatrix = pickProjMatrix;
1189
- frameCtx.pickZNear = nearAndFar[0];
1190
- frameCtx.pickZFar = nearAndFar[1];
1191
- frameCtx.pickElementsCount = pickable.pickElementsCount;
1192
- frameCtx.pickElementsOffset = pickable.pickElementsOffset;
1193
- frameCtx.pickClipPos = [
1194
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1195
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1196
- ];
1353
+ pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, projection, pickResult);
1197
1354
 
1198
- gl.viewport(0, 0, 1, 1);
1355
+ pickResult.pickSurfacePrecision = false;
1199
1356
 
1200
- gl.clearColor(0, 0, 0, 0);
1201
- gl.depthMask(true);
1202
- gl.enable(gl.DEPTH_TEST);
1203
- gl.disable(gl.CULL_FACE);
1204
- gl.disable(gl.BLEND);
1205
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1357
+ } else {
1206
1358
 
1207
- pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths
1359
+ if (pickable.canPickWorldPos && pickable.canPickWorldPos()) {
1208
1360
 
1209
- const pix = pickBuffer.read(0, 0);
1361
+ // pickWorldPos
1362
+ resetFrameCtx(1);
1363
+ frameCtx.viewParams.near = nearAndFar[0];
1364
+ frameCtx.viewParams.far = nearAndFar[1];
1210
1365
 
1211
- const screenZ = unpackDepth(pix); // Get screen-space Z at the given canvas coords
1366
+ gl.viewport(0, 0, 1, 1);
1212
1367
 
1213
- // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top
1214
- const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2);
1215
- const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2);
1368
+ gl.clearColor(0, 0, 0, 0);
1369
+ gl.depthMask(true);
1370
+ gl.enable(gl.DEPTH_TEST);
1371
+ gl.disable(gl.CULL_FACE);
1372
+ gl.disable(gl.BLEND);
1373
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1216
1374
 
1217
- const origin = pickable.origin;
1218
- let pvMat;
1375
+ pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths
1219
1376
 
1220
- if (origin) {
1221
- const rtcPickViewMat = createRTCViewMat(pickViewMatrix, origin, tempMat4a);
1222
- pvMat = math.mulMat4(pickProjMatrix, rtcPickViewMat, tempMat4b);
1377
+ const screenZ = math.dotVec4(pickBuffer.read(0, 0), bitShiftScreenZ);
1223
1378
 
1224
- } else {
1225
- pvMat = math.mulMat4(pickProjMatrix, pickViewMatrix, tempMat4b);
1226
- }
1379
+ // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top
1380
+ const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2);
1381
+ const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2);
1227
1382
 
1228
- const pvMatInverse = math.inverseMat4(pvMat, tempMat4c);
1383
+ const origin = pickable.origin;
1229
1384
 
1230
- tempVec4a[0] = x;
1231
- tempVec4a[1] = y;
1232
- tempVec4a[2] = -1;
1233
- tempVec4a[3] = 1;
1385
+ const pvMatInverse = math.inverseMat4(math.mulMat4(pickProjMatrix, (origin ? createRTCViewMat(pickViewMatrix, origin, tempMat4a) : pickViewMatrix), tempMat4c), tempMat4d);
1234
1386
 
1235
- let world1 = math.transformVec4(pvMatInverse, tempVec4a);
1236
- world1 = math.mulVec4Scalar(world1, 1 / world1[3]);
1387
+ const toWorld = (z, dst) => {
1388
+ dst[0] = x;
1389
+ dst[1] = y;
1390
+ dst[2] = z;
1391
+ dst[3] = 1;
1392
+ math.transformVec4(pvMatInverse, dst, dst);
1393
+ return math.mulVec4Scalar(dst, 1 / dst[3]);
1394
+ };
1237
1395
 
1238
- tempVec4b[0] = x;
1239
- tempVec4b[1] = y;
1240
- tempVec4b[2] = 1;
1241
- tempVec4b[3] = 1;
1396
+ const world1 = toWorld(-1, tempVec4a);
1397
+ const world2 = toWorld( 1, tempVec4b);
1242
1398
 
1243
- let world2 = math.transformVec4(pvMatInverse, tempVec4b);
1244
- world2 = math.mulVec4Scalar(world2, 1 / world2[3]);
1399
+ const dir = math.subVec3(world2, world1, tempVec4c);
1400
+ const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e);
1245
1401
 
1246
- const dir = math.subVec3(world2, world1, tempVec4c);
1247
- const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e);
1402
+ if (origin) {
1403
+ math.addVec3(worldPos, origin);
1404
+ }
1248
1405
 
1249
- if (origin) {
1250
- math.addVec3(worldPos, origin);
1251
- }
1406
+ pickResult.worldPos = worldPos;
1252
1407
 
1253
- pickResult.worldPos = worldPos;
1254
- }
1255
- })();
1408
+ if (params.pickSurfaceNormal !== false) {
1409
+ // gpuPickWorldNormal
1410
+ resetFrameCtx(3);
1256
1411
 
1257
- function drawSnapInit(frameCtx) {
1258
- frameCtx.snapPickLayerParams = [];
1259
- frameCtx.snapPickLayerNumber = 0;
1412
+ pickNormalBuffer.bind();
1260
1413
 
1261
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1414
+ gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]);
1415
+ gl.enable(gl.DEPTH_TEST);
1416
+ gl.disable(gl.CULL_FACE);
1417
+ gl.disable(gl.BLEND);
1418
+ gl.clear(gl.DEPTH_BUFFER_BIT);
1419
+ gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0]));
1262
1420
 
1263
- const drawable = postCullDrawableList[i];
1421
+ pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals
1264
1422
 
1265
- if (drawable.drawSnapInit) {
1266
- if (!drawable.culled && drawable.visible && drawable.pickable) {
1267
- drawable.drawSnapInit(frameCtx);
1268
- }
1269
- }
1270
- }
1271
- return frameCtx.snapPickLayerParams;
1272
- }
1423
+ const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1273
1424
 
1274
- function drawSnap(frameCtx) {
1275
- frameCtx.snapPickLayerParams = frameCtx.snapPickLayerParams || [];
1276
- frameCtx.snapPickLayerNumber = frameCtx.snapPickLayerParams.length;
1277
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1425
+ pickNormalBuffer.unbind();
1278
1426
 
1279
- const drawable = postCullDrawableList[i];
1427
+ pickResult.worldNormal = toWorldNormal(pix);
1428
+ }
1280
1429
 
1281
- if (drawable.drawSnapInit) {
1282
- if (drawable.drawSnap) {
1283
- if (!drawable.culled && drawable.visible && drawable.pickable) {
1284
- drawable.drawSnap(frameCtx);
1430
+ pickResult.pickSurfacePrecision = false;
1285
1431
  }
1286
1432
  }
1287
1433
  }
1288
- }
1289
- return frameCtx.snapPickLayerParams;
1290
- }
1291
-
1292
- function getClipPosX(pos, size) {
1293
- return 2 * (pos / size) - 1;
1294
- }
1295
-
1296
- function getClipPosY(pos, size) {
1297
- return 1 - 2 * (pos / size);
1298
- }
1434
+ pickBuffer.unbind();
1435
+ pickResult.entity = pickedEntity;
1436
+ return pickResult;
1437
+ };
1438
+ })();
1299
1439
 
1300
1440
  /**
1301
1441
  * @param {[number, number]} canvasPos
@@ -1309,6 +1449,18 @@ const Renderer = function (scene, options) {
1309
1449
 
1310
1450
  const _pickResult = new PickResult();
1311
1451
 
1452
+ const getVertexPickBuffer = (function() {
1453
+ const cache = { };
1454
+ return (snapRadiusInPixels) => {
1455
+ if (! (snapRadiusInPixels in cache)) {
1456
+ const buf = new RenderBuffer(gl, [gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI], true);
1457
+ buf.setSize([2 * snapRadiusInPixels + 1, 2 * snapRadiusInPixels + 1]);
1458
+ cache[snapRadiusInPixels] = buf;
1459
+ }
1460
+ return cache[snapRadiusInPixels];
1461
+ };
1462
+ })();
1463
+
1312
1464
  return function (params, pickResult = _pickResult) {
1313
1465
 
1314
1466
  const {canvasPos, origin, direction, snapRadius, snapToVertex, snapToEdge} = params;
@@ -1317,40 +1469,21 @@ const Renderer = function (scene, options) {
1317
1469
  return this.pick({canvasPos, pickSurface: true});
1318
1470
  }
1319
1471
 
1320
- const resolutionScale = scene.canvas.resolutionScale;
1321
-
1322
- frameCtx.reset();
1323
- frameCtx.backfaces = true;
1324
- frameCtx.frontface = true; // "ccw"
1325
- frameCtx.pickZNear = scene.camera.project.near;
1326
- frameCtx.pickZFar = scene.camera.project.far;
1327
-
1472
+ const camera = scene.camera;
1328
1473
  const snapRadiusInPixels = snapRadius || 30;
1329
-
1330
- const vertexPickBuffer = renderBufferManager.getRenderBuffer(
1331
- `uniquePickColors-aabs-${snapRadiusInPixels}`,
1332
- {
1333
- depthTexture: true,
1334
- size: [
1335
- 2 * snapRadiusInPixels + 1,
1336
- 2 * snapRadiusInPixels + 1,
1337
- ]
1338
- }
1339
- );
1340
-
1341
- frameCtx.snapVectorA = [
1342
- canvasPos ? getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth) : 0,
1343
- canvasPos ? getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight) : 0,
1344
- ];
1345
-
1346
- frameCtx.snapInvVectorAB = [
1347
- gl.drawingBufferWidth / (2 * snapRadiusInPixels),
1348
- gl.drawingBufferHeight / (2 * snapRadiusInPixels),
1349
- ];
1474
+ const viewMatrix = (canvasPos
1475
+ ? camera.viewMatrix
1476
+ : math.lookAtMat4v(
1477
+ origin,
1478
+ math.addVec3(origin, direction, math.vec3()),
1479
+ math.vec3([0, 1, 0]),
1480
+ math.mat4()));
1481
+ resetPickFrameCtx(canvasPos, 2 * snapRadiusInPixels, camera, camera.eye, camera.projMatrix, viewMatrix, frameCtx);
1350
1482
 
1351
1483
  // Bind and clear the snap render target
1352
1484
 
1353
- vertexPickBuffer.bind(gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI);
1485
+ const vertexPickBuffer = getVertexPickBuffer(snapRadiusInPixels);
1486
+ vertexPickBuffer.bind();
1354
1487
  gl.viewport(0, 0, vertexPickBuffer.size[0], vertexPickBuffer.size[1]);
1355
1488
  gl.enable(gl.DEPTH_TEST);
1356
1489
  gl.frontFace(gl.CCW);
@@ -1363,64 +1496,59 @@ const Renderer = function (scene, options) {
1363
1496
  gl.clearBufferiv(gl.COLOR, 1, new Int32Array([0, 0, 0, 0]));
1364
1497
  gl.clearBufferuiv(gl.COLOR, 2, new Uint32Array([0, 0, 0, 0]));
1365
1498
 
1366
- //////////////////////////////////
1367
- // Set view and proj mats for VBO renderers
1368
- ///////////////////////////////////////
1369
-
1370
- frameCtx.pickViewMatrix = (canvasPos
1371
- ? scene.camera.viewMatrix
1372
- : math.lookAtMat4v(
1373
- origin,
1374
- math.addVec3(origin, direction, math.vec3()),
1375
- math.vec3([0, 1, 0]),
1376
- math.mat4()));
1377
-
1378
- const pickProjMatrix = scene.camera.projMatrix;
1379
-
1380
- for (let type in drawableTypeInfo) {
1381
- if (drawableTypeInfo.hasOwnProperty(type)) {
1382
- const drawableList = drawableTypeInfo[type].drawableList;
1383
- for (let i = 0, len = drawableList.length; i < len; i++) {
1384
- const drawable = drawableList[i];
1385
- if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
1386
- drawable.setPickMatrices(frameCtx.pickViewMatrix, pickProjMatrix);
1387
- }
1388
- }
1389
- }
1390
- }
1391
-
1392
1499
  // a) init z-buffer
1393
1500
  gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
1394
- const layerParamsSurface = drawSnapInit(frameCtx);
1501
+
1502
+ frameCtx.snapPickLayerParams = [];
1503
+ frameCtx.snapPickLayerParams.push(null); // This recreates previous situation, which relied on snapPickLayerNumber
1504
+ postCullDrawableList.forEach(drawable => {
1505
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawSnapInit) {
1506
+ drawable.drawSnapInit(frameCtx);
1507
+ }
1508
+ });
1509
+
1510
+ const layerParamsSurface = frameCtx.snapPickLayerParams;
1395
1511
 
1396
1512
  // b) snap-pick
1397
- const layerParamsSnap = []
1398
- frameCtx.snapPickLayerParams = layerParamsSnap;
1513
+ frameCtx.snapPickLayerParams = [];
1399
1514
 
1400
1515
  gl.depthMask(false);
1401
1516
  gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
1402
1517
 
1403
- if (snapToVertex && snapToEdge) {
1404
- frameCtx.snapMode = "edge";
1405
- drawSnap(frameCtx);
1406
-
1407
- frameCtx.snapMode = "vertex";
1408
- frameCtx.snapPickLayerNumber++;
1409
-
1410
- drawSnap(frameCtx);
1411
- } else {
1412
- frameCtx.snapMode = snapToVertex ? "vertex" : "edge";
1518
+ const drawSnap = (snapMode) => {
1519
+ frameCtx.snapMode = snapMode;
1520
+ frameCtx.snapPickLayerParams.push(null); // This recreates previous situation, which relied on snapPickLayerNumber
1521
+ postCullDrawableList.forEach(drawable => {
1522
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawSnap) {
1523
+ drawable.drawSnap(frameCtx);
1524
+ }
1525
+ });
1526
+ };
1413
1527
 
1414
- drawSnap(frameCtx);
1528
+ if (snapToEdge) {
1529
+ drawSnap("edge");
1530
+ }
1531
+ if (snapToVertex) {
1532
+ drawSnap("vertex");
1415
1533
  }
1416
1534
 
1417
1535
  gl.depthMask(true);
1418
1536
 
1537
+ const layerParamsSnap = frameCtx.snapPickLayerParams;
1538
+
1419
1539
  // Read and decode the snapped coordinates
1540
+ const readSnapPickBuffer = (source, arrayType, glType) => {
1541
+ const w = vertexPickBuffer.buffer.width;
1542
+ const h = vertexPickBuffer.buffer.height;
1543
+ const pix = new arrayType(4 * w * h);
1544
+ gl.readBuffer(source);
1545
+ gl.readPixels(0, 0, w, h, gl.RGBA_INTEGER, glType, pix, 0);
1546
+ return pix;
1547
+ };
1420
1548
 
1421
- const snapPickResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1422
- const snapPickNormalResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4, 1);
1423
- const snapPickIdResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.UNSIGNED_INT, Uint32Array, 4, 2);
1549
+ const snapPickResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT0, Int32Array, gl.INT);
1550
+ const snapPickNormalResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT1, Int32Array, gl.INT);
1551
+ const snapPickIdResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT2, Uint32Array, gl.UNSIGNED_INT);
1424
1552
 
1425
1553
  vertexPickBuffer.unbind();
1426
1554
 
@@ -1439,195 +1567,76 @@ const Renderer = function (scene, options) {
1439
1567
 
1440
1568
  if (pickResultMiddleXY[3] !== 0) {
1441
1569
  const pickedLayerParmasSurface = layerParamsSurface[Math.abs(pickResultMiddleXY[3]) % layerParamsSurface.length];
1442
- const origin = pickedLayerParmasSurface.origin;
1443
- const scale = pickedLayerParmasSurface.coordinateScale;
1444
- worldPos = [
1445
- pickResultMiddleXY[0] * scale[0] + origin[0],
1446
- pickResultMiddleXY[1] * scale[1] + origin[1],
1447
- pickResultMiddleXY[2] * scale[2] + origin[2],
1448
- ];
1449
- worldNormal = math.normalizeVec3([
1450
- pickNormalResultMiddleXY[0] / math.MAX_INT,
1451
- pickNormalResultMiddleXY[1] / math.MAX_INT,
1452
- pickNormalResultMiddleXY[2] / math.MAX_INT,
1453
- ]);
1454
-
1455
- const pickID =
1456
- pickPickableResultMiddleXY[0]
1457
- + (pickPickableResultMiddleXY[1] << 8)
1458
- + (pickPickableResultMiddleXY[2] << 16)
1459
- + (pickPickableResultMiddleXY[3] << 24);
1460
-
1461
- pickable = pickIDs.items[pickID];
1570
+ worldPos = toWorldPos(pickResultMiddleXY, pickedLayerParmasSurface.origin, pickedLayerParmasSurface.coordinateScale);
1571
+ worldNormal = toWorldNormal(pickNormalResultMiddleXY);
1572
+
1573
+ pickable = pickIDs.items[pixelToInt(pickPickableResultMiddleXY)];
1462
1574
  }
1463
1575
 
1464
1576
  // result 2) hi-precision snapped (to vertex/edge) world position
1465
1577
 
1466
- let snapPickResult = [];
1467
-
1578
+ const snapPickResult = [ ];
1468
1579
  for (let i = 0; i < snapPickResultArray.length; i += 4) {
1469
- if (snapPickResultArray[i + 3] > 0) {
1580
+ const layerNumber = snapPickResultArray[i + 3];
1581
+ if (layerNumber > 0) {
1470
1582
  const pixelNumber = Math.floor(i / 4);
1471
1583
  const w = vertexPickBuffer.size[0];
1472
1584
  const x = pixelNumber % w - Math.floor(w / 2);
1473
1585
  const y = Math.floor(pixelNumber / w) - Math.floor(w / 2);
1474
- const dist = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
1475
1586
  snapPickResult.push({
1476
- x,
1477
- y,
1478
- dist,
1479
- isVertex: snapToVertex && snapToEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapToVertex,
1480
- result: [
1481
- snapPickResultArray[i + 0],
1482
- snapPickResultArray[i + 1],
1483
- snapPickResultArray[i + 2],
1484
- snapPickResultArray[i + 3],
1485
- ],
1486
- normal: [
1487
- snapPickNormalResultArray[i + 0],
1488
- snapPickNormalResultArray[i + 1],
1489
- snapPickNormalResultArray[i + 2],
1490
- snapPickNormalResultArray[i + 3],
1491
- ],
1492
- id: [
1493
- snapPickIdResultArray[i + 0],
1494
- snapPickIdResultArray[i + 1],
1495
- snapPickIdResultArray[i + 2],
1496
- snapPickIdResultArray[i + 3],
1497
- ]
1587
+ dist: math.lenVec2([ x, y ]),
1588
+ isVertex: (snapToVertex && snapToEdge) ? (layerNumber > layerParamsSnap.length / 2) : snapToVertex,
1589
+ result: snapPickResultArray.subarray(i, i+4),
1590
+ normal: snapPickNormalResultArray.subarray(i, i+4),
1591
+ id: snapPickIdResultArray.subarray(i, i+4)
1498
1592
  });
1499
1593
  }
1500
1594
  }
1501
1595
 
1502
- let snappedWorldPos = null;
1503
- let snappedWorldNormal = null;
1504
- let snappedPickable = null;
1505
- let snapType = null;
1596
+ const getPickedEntity = pickable => (pickable && pickable.delegatePickedEntity) ? pickable.delegatePickedEntity() : pickable;
1506
1597
 
1507
1598
  if (snapPickResult.length > 0) {
1508
- // vertex snap first, then edge snap
1509
- snapPickResult.sort((a, b) => {
1510
- if (a.isVertex !== b.isVertex) {
1511
- return a.isVertex ? -1 : 1;
1512
- } else {
1513
- return a.dist - b.dist;
1514
- }
1515
- });
1516
-
1517
- snapType = snapPickResult[0].isVertex ? "vertex" : "edge";
1518
- const snapPick = snapPickResult[0].result;
1519
- const snapPickNormal = snapPickResult[0].normal;
1520
- const snapPickId = snapPickResult[0].id;
1521
-
1599
+ // closest vertex snap first, then closest edge snap
1600
+ const res = snapPickResult.reduce((a,b) => ((((a.isVertex-b.isVertex) || (b.dist-a.dist)) > 0) ? a : b));
1601
+ const snapPick = res.result;
1522
1602
  const pickedLayerParmas = layerParamsSnap[snapPick[3]];
1603
+ const snappedWorldPos = toWorldPos(snapPick, pickedLayerParmas.origin, pickedLayerParmas.coordinateScale);
1604
+ const snappedCanvasPos = camera.projectWorldPos(snappedWorldPos);
1605
+
1606
+ pickResult.reset();
1607
+ pickResult.snappedToEdge = !res.isVertex;
1608
+ pickResult.snappedToVertex = res.isVertex;
1609
+ pickResult.worldPos = snappedWorldPos;
1610
+ pickResult.worldNormal = toWorldNormal(res.normal);
1611
+ pickResult.entity = getPickedEntity(pickIDs.items[pixelToInt(res.id)]);
1612
+ pickResult.canvasPos = canvasPos || (worldPos && camera.projectWorldPos(worldPos)) || snappedCanvasPos;
1613
+ pickResult.snappedCanvasPos = snappedCanvasPos;
1614
+ return pickResult;
1615
+
1616
+ } else if (worldPos) {
1617
+
1618
+ pickResult.reset();
1619
+ pickResult.snappedToEdge = false;
1620
+ pickResult.snappedToVertex = false;
1621
+ pickResult.worldPos = worldPos;
1622
+ pickResult.worldNormal = worldNormal;
1623
+ pickResult.entity = getPickedEntity(pickable);
1624
+ pickResult.canvasPos = canvasPos || camera.projectWorldPos(worldPos);
1625
+ pickResult.snappedCanvasPos = canvasPos;
1626
+ return pickResult;
1523
1627
 
1524
- const origin = pickedLayerParmas.origin;
1525
- const scale = pickedLayerParmas.coordinateScale;
1526
-
1527
- snappedWorldNormal = math.normalizeVec3([
1528
- snapPickNormal[0] / math.MAX_INT,
1529
- snapPickNormal[1] / math.MAX_INT,
1530
- snapPickNormal[2] / math.MAX_INT,
1531
- ]);
1532
-
1533
- snappedWorldPos = [
1534
- snapPick[0] * scale[0] + origin[0],
1535
- snapPick[1] * scale[1] + origin[1],
1536
- snapPick[2] * scale[2] + origin[2],
1537
- ];
1538
-
1539
- snappedPickable = pickIDs.items[
1540
- snapPickId[0]
1541
- + (snapPickId[1] << 8)
1542
- + (snapPickId[2] << 16)
1543
- + (snapPickId[3] << 24)
1544
- ];
1545
- }
1546
-
1547
- if (null === worldPos && null == snappedWorldPos) { // If neither regular pick or snap pick, return null
1628
+ } else {
1548
1629
  return null;
1549
1630
  }
1550
-
1551
- let snappedCanvasPos = null;
1552
-
1553
- if (null !== snappedWorldPos) {
1554
- snappedCanvasPos = scene.camera.projectWorldPos(snappedWorldPos);
1555
- }
1556
-
1557
- const snappedEntity = (snappedPickable && snappedPickable.delegatePickedEntity) ? snappedPickable.delegatePickedEntity() : snappedPickable;
1558
- if (!snappedEntity && pickable) {
1559
- pickable = pickable.delegatePickedEntity ? pickable.delegatePickedEntity() : pickable;
1560
- }
1561
-
1562
- pickResult.reset();
1563
- pickResult.snappedToEdge = (snapType === "edge");
1564
- pickResult.snappedToVertex = (snapType === "vertex");
1565
- pickResult.worldPos = snappedWorldPos || worldPos;
1566
- pickResult.worldNormal = snappedWorldNormal || worldNormal;
1567
- pickResult.entity = snappedEntity || pickable;
1568
- pickResult.canvasPos = canvasPos || scene.camera.projectWorldPos(worldPos || snappedWorldPos);
1569
- pickResult.snappedCanvasPos = snappedCanvasPos || canvasPos;
1570
-
1571
- return pickResult;
1572
1631
  };
1573
1632
  })();
1574
1633
 
1575
- function unpackDepth(depthZ) {
1576
- const vec = [depthZ[0] / 256.0, depthZ[1] / 256.0, depthZ[2] / 256.0, depthZ[3] / 256.0];
1577
- const bitShift = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0];
1578
- return math.dotVec4(vec, bitShift);
1579
- }
1580
-
1581
- function gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1582
-
1583
- const resolutionScale = scene.canvas.resolutionScale;
1584
-
1585
- frameCtx.reset();
1586
- frameCtx.backfaces = true;
1587
- frameCtx.frontface = true; // "ccw"
1588
- frameCtx.pickOrigin = pickResult.origin;
1589
- frameCtx.pickViewMatrix = pickViewMatrix;
1590
- frameCtx.pickProjMatrix = pickProjMatrix;
1591
- frameCtx.pickClipPos = [
1592
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1593
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight),
1594
- ];
1595
-
1596
- const pickNormalBuffer = renderBufferManager.getRenderBuffer("pick-normal", {size: [3, 3]});
1597
-
1598
- pickNormalBuffer.bind(gl.RGBA32I);
1599
-
1600
- gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]);
1601
-
1602
- gl.enable(gl.DEPTH_TEST);
1603
- gl.disable(gl.CULL_FACE);
1604
- gl.disable(gl.BLEND);
1605
- gl.clear(gl.DEPTH_BUFFER_BIT);
1606
- gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0]));
1607
-
1608
- pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals
1609
-
1610
- const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1611
-
1612
- pickNormalBuffer.unbind();
1613
-
1614
- const worldNormal = [
1615
- pix[0] / math.MAX_INT,
1616
- pix[1] / math.MAX_INT,
1617
- pix[2] / math.MAX_INT,
1618
- ];
1619
-
1620
- math.normalizeVec3(worldNormal);
1621
-
1622
- pickResult.worldNormal = worldNormal;
1623
- }
1624
-
1625
1634
  /**
1626
1635
  * Adds a {@link Marker} for occlusion testing.
1627
1636
  * @param marker
1628
1637
  */
1629
1638
  this.addMarker = function (marker) {
1630
- this._occlusionTester = this._occlusionTester || new OcclusionTester(scene, renderBufferManager);
1639
+ this._occlusionTester = this._occlusionTester || new OcclusionTester(scene);
1631
1640
  this._occlusionTester.addMarker(marker);
1632
1641
  scene.occlusionTestCountdown = 0;
1633
1642
  };
@@ -1658,11 +1667,17 @@ const Renderer = function (scene, options) {
1658
1667
 
1659
1668
  updateDrawlist();
1660
1669
 
1661
- this._occlusionTester.bindRenderBuf();
1670
+ const readPixelBuf = (! OCCLUSION_TEST_MODE) && renderBufferManager.getRenderBuffer("occlusionReadPix");
1671
+ if (readPixelBuf) {
1672
+ readPixelBuf.setSize([gl.drawingBufferWidth, gl.drawingBufferHeight]);
1673
+ readPixelBuf.bind();
1674
+ }
1662
1675
 
1663
1676
  frameCtx.reset();
1664
1677
  frameCtx.backfaces = true;
1665
1678
  frameCtx.frontface = true; // "ccw"
1679
+ frameCtx.viewParams = getSceneCameraViewParams();
1680
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(scene.camera, gl.drawingBufferHeight);
1666
1681
 
1667
1682
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1668
1683
  gl.clearColor(0, 0, 0, 0);
@@ -1671,121 +1686,143 @@ const Renderer = function (scene, options) {
1671
1686
  gl.disable(gl.BLEND);
1672
1687
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1673
1688
 
1674
-
1675
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1676
-
1677
- const drawable = postCullDrawableList[i];
1678
-
1679
- if (!drawable.drawOcclusion || drawable.culled === true || drawable.visible === false || drawable.pickable === false) { // TODO: Option to exclude transparent?
1680
- continue;
1689
+ postCullDrawableList.forEach(drawable => {
1690
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawOcclusion) { // TODO: Option to exclude transparent?
1691
+ drawable.drawOcclusion(frameCtx);
1681
1692
  }
1693
+ });
1682
1694
 
1683
- drawable.drawOcclusion(frameCtx);
1684
- }
1685
-
1686
- this._occlusionTester.drawMarkers(frameCtx);
1687
- this._occlusionTester.doOcclusionTest(); // Updates Marker "visible" properties
1688
- this._occlusionTester.unbindRenderBuf();
1689
- }
1690
- };
1695
+ this._occlusionTester.drawMarkers();
1691
1696
 
1692
- /**
1693
- * Read pixels from the renderer's current output. Performs a force-render first.
1694
- * @param pixels
1695
- * @param colors
1696
- * @param len
1697
- * @param opaqueOnly
1698
- * @private
1699
- */
1700
- this.readPixels = function (pixels, colors, len, opaqueOnly) {
1701
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1702
- snapshotBuffer.bind();
1703
- snapshotBuffer.clear();
1704
- this.render({force: true, opaqueOnly: opaqueOnly});
1705
- let color;
1706
- let i;
1707
- let j;
1708
- let k;
1709
- for (i = 0; i < len; i++) {
1710
- j = i * 2;
1711
- k = i * 4;
1712
- color = snapshotBuffer.read(pixels[j], pixels[j + 1]);
1713
- colors[k] = color[0];
1714
- colors[k + 1] = color[1];
1715
- colors[k + 2] = color[2];
1716
- colors[k + 3] = color[3];
1717
- }
1718
- snapshotBuffer.unbind();
1719
- imageDirty = true;
1720
- };
1721
-
1722
- /**
1723
- * Enter snapshot mode.
1724
- *
1725
- * Switches rendering to a hidden snapshot canvas.
1726
- *
1727
- * Exit snapshot mode using endSnapshot().
1728
- */
1729
- this.beginSnapshot = function (params = {}) {
1730
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1731
- if (params.width && params.height) {
1732
- snapshotBuffer.setSize([params.width, params.height]);
1733
- }
1734
- snapshotBuffer.bind();
1735
- snapshotBuffer.clear();
1736
- snapshotBound = true;
1737
- };
1738
-
1739
- /**
1740
- * When in snapshot mode, renders a frame of the current Scene state to the snapshot canvas.
1741
- */
1742
- this.renderSnapshot = function () {
1743
- if (!snapshotBound) {
1744
- return;
1697
+ if (readPixelBuf) {
1698
+ const resolutionScale = scene.canvas.resolutionScale;
1699
+ this._occlusionTester.doOcclusionTest( // Updates Marker "visible" properties
1700
+ (x, y) => readPixelBuf.read(Math.round(resolutionScale * x), Math.round(resolutionScale * y)));
1701
+ readPixelBuf.unbind();
1702
+ }
1745
1703
  }
1746
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1747
- snapshotBuffer.clear();
1748
- this.render({force: true, opaqueOnly: false});
1749
- imageDirty = true;
1750
1704
  };
1751
1705
 
1752
- /**
1753
- * When in snapshot mode, gets an image of the snapshot canvas.
1754
- *
1755
- * @private
1756
- * @returns {String} The image data URI.
1757
- */
1758
- this.readSnapshot = function (params) {
1759
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1760
- return snapshotBuffer.readImage(params);
1761
- };
1706
+ this.snapshot = (() => {
1707
+ let snapshotBound = false;
1708
+ const snapshotBuffer = new RenderBuffer(gl);
1709
+ let snapshotCanvas = null;
1710
+
1711
+ return {
1712
+ /**
1713
+ * Read pixels from the renderer's current output. Performs a force-render first.
1714
+ * @param pixels
1715
+ * @param colors
1716
+ * @param len
1717
+ * @private
1718
+ */
1719
+ readPixels: (pixels, colors, len) => {
1720
+ snapshotBuffer.bind();
1721
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1722
+ imageDirty = true;
1723
+ this.render();
1724
+ for (let i = 0; i < len; i++) {
1725
+ const j = i * 2;
1726
+ const k = i * 4;
1727
+ const color = snapshotBuffer.read(pixels[j], pixels[j + 1]);
1728
+ colors[k] = color[0];
1729
+ colors[k + 1] = color[1];
1730
+ colors[k + 2] = color[2];
1731
+ colors[k + 3] = color[3];
1732
+ }
1733
+ snapshotBuffer.unbind();
1734
+ imageDirty = true;
1735
+ },
1736
+
1737
+ /**
1738
+ * Enter snapshot mode.
1739
+ *
1740
+ * Switches rendering to a hidden snapshot canvas.
1741
+ *
1742
+ * Exit snapshot mode using endSnapshot().
1743
+ */
1744
+ beginSnapshot: () => {
1745
+ snapshotBuffer.setSize([gl.drawingBufferWidth, gl.drawingBufferHeight]);
1746
+ snapshotBuffer.bind();
1747
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1748
+ snapshotBound = true;
1749
+ },
1750
+
1751
+ /**
1752
+ * Returns an HTMLCanvas containing an image of the snapshot canvas.
1753
+ *
1754
+ * - The HTMLCanvas has a CanvasRenderingContext2D.
1755
+ * - Expects the caller to draw more things on the HTMLCanvas (annotations etc).
1756
+ *
1757
+ * @returns {HTMLCanvasElement}
1758
+ */
1759
+ renderSnapshotToCanvas: () => {
1760
+ const width = snapshotBuffer.buffer.width;
1761
+ const height = snapshotBuffer.buffer.height;
1762
+
1763
+ if ((! snapshotCanvas) || (snapshotCanvas.width !== width) || (snapshotCanvas.height !== height)) {
1764
+ snapshotCanvas = (function() {
1765
+ const canvas = document.createElement('canvas');
1766
+ const context = canvas.getContext('2d');
1767
+ canvas.width = width;
1768
+ canvas.height = height;
1769
+ const pixelData = new Uint8Array(width * height * 4);
1770
+ const imageData = context.createImageData(width, height);
1771
+
1772
+ return {
1773
+ width: width,
1774
+ height: height,
1775
+ getUpdatedCanvas: () => {
1776
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelData);
1777
+
1778
+ // flip verically
1779
+ const halfHeight = Math.floor(height / 2);
1780
+ const bytesPerRow = width * 4;
1781
+ const temp = new Uint8Array(bytesPerRow);
1782
+ for (let y = 0; y < halfHeight; ++y) {
1783
+ const topOffset = y * bytesPerRow;
1784
+ const bottomOffset = (height - y - 1) * bytesPerRow;
1785
+ temp.set(pixelData.subarray(topOffset, topOffset + bytesPerRow));
1786
+ pixelData.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
1787
+ pixelData.set(temp, bottomOffset);
1788
+ }
1789
+
1790
+ imageData.data.set(pixelData);
1791
+
1792
+ context.resetTransform(); // Prevents strange scale-accumulation effect with html2canvas
1793
+ context.putImageData(imageData, 0, 0);
1794
+
1795
+ return canvas;
1796
+ }
1797
+ };
1798
+ })();
1799
+ }
1762
1800
 
1763
- /**
1764
- * Returns an HTMLCanvas containing an image of the snapshot canvas.
1765
- *
1766
- * - The HTMLCanvas has a CanvasRenderingContext2D.
1767
- * - Expects the caller to draw more things on the HTMLCanvas (annotations etc).
1768
- *
1769
- * @returns {HTMLCanvasElement}
1770
- */
1771
- this.readSnapshotAsCanvas = function () {
1772
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1773
- return snapshotBuffer.readImageAsCanvas();
1774
- };
1801
+ if (snapshotBound) {
1802
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1803
+ imageDirty = true;
1804
+ this.render();
1805
+ imageDirty = true;
1806
+ }
1775
1807
 
1776
- /**
1777
- * Exists snapshot mode.
1778
- *
1779
- * Switches rendering back to the main canvas.
1780
- */
1781
- this.endSnapshot = function () {
1782
- if (!snapshotBound) {
1783
- return;
1784
- }
1785
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1786
- snapshotBuffer.unbind();
1787
- snapshotBound = false;
1788
- };
1808
+ return snapshotCanvas.getUpdatedCanvas();
1809
+ },
1810
+
1811
+ /**
1812
+ * Exists snapshot mode.
1813
+ *
1814
+ * Switches rendering back to the main canvas.
1815
+ */
1816
+ endSnapshot: () => {
1817
+ if (snapshotBound) {
1818
+ snapshotBuffer.unbind();
1819
+ snapshotBound = false;
1820
+ }
1821
+ imageDirty = true;
1822
+ this.render();
1823
+ }
1824
+ };
1825
+ })();
1789
1826
 
1790
1827
  /**
1791
1828
  * Destroys this renderer.