@xeokit/xeokit-sdk 2.6.84 → 2.6.85-renderers-refactor-v2

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 (183) hide show
  1. package/dist/xeokit-sdk.cjs.js +13077 -44659
  2. package/dist/xeokit-sdk.es.js +13078 -44658
  3. package/dist/xeokit-sdk.es5.js +1402 -3502
  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/plugins/lib/ui/index.js +12 -0
  9. package/src/viewer/Viewer.js +55 -103
  10. package/src/viewer/scene/canvas/Canvas.js +3 -43
  11. package/src/viewer/scene/geometry/ReadableGeometry.js +0 -25
  12. package/src/viewer/scene/lights/CubeTexture.js +0 -13
  13. package/src/viewer/scene/lights/DirLight.js +2 -1
  14. package/src/viewer/scene/lights/PointLight.js +2 -1
  15. package/src/viewer/scene/materials/Texture.js +0 -9
  16. package/src/viewer/scene/math/rtcCoords.js +1 -38
  17. package/src/viewer/scene/mesh/Mesh.js +454 -212
  18. package/src/viewer/scene/mesh/draw/DrawShaderSource.js +305 -1502
  19. package/src/viewer/scene/mesh/draw/LambertShaderSource.js +35 -0
  20. package/src/viewer/scene/mesh/emphasis/EmphasisShaderSource.js +34 -0
  21. package/src/viewer/scene/mesh/occlusion/OcclusionShaderSource.js +9 -157
  22. package/src/viewer/scene/mesh/pick/PickMeshShaderSource.js +21 -150
  23. package/src/viewer/scene/mesh/pick/PickTriangleShaderSource.js +12 -124
  24. package/src/viewer/scene/mesh/shadow/ShadowShaderSource.js +22 -97
  25. package/src/viewer/scene/model/SceneModel.js +245 -861
  26. package/src/viewer/scene/model/SceneModelEntity.js +4 -4
  27. package/src/viewer/scene/model/SceneModelMesh.js +3 -3
  28. package/src/viewer/scene/model/layer/DTXLayer.js +940 -0
  29. package/src/viewer/scene/model/layer/Layer.js +786 -0
  30. package/src/viewer/scene/model/layer/VBOLayer.js +1019 -0
  31. package/src/viewer/scene/model/layer/programs/ColorProgram.js +35 -0
  32. package/src/viewer/scene/model/layer/programs/ColorTextureProgram.js +46 -0
  33. package/src/viewer/scene/model/layer/programs/DepthProgram.js +11 -0
  34. package/src/viewer/scene/model/layer/programs/EdgesProgram.js +12 -0
  35. package/src/viewer/scene/model/layer/programs/FlatColorProgram.js +24 -0
  36. package/src/viewer/scene/model/layer/programs/OcclusionProgram.js +12 -0
  37. package/src/viewer/scene/model/layer/programs/PBRProgram.js +180 -0
  38. package/src/viewer/scene/model/layer/programs/PickDepthProgram.js +30 -0
  39. package/src/viewer/scene/model/layer/programs/PickMeshProgram.js +12 -0
  40. package/src/viewer/scene/model/layer/programs/PickNormalsProgram.js +18 -0
  41. package/src/viewer/scene/model/layer/programs/ShadowProgram.js +23 -0
  42. package/src/viewer/scene/model/layer/programs/SilhouetteProgram.js +15 -0
  43. package/src/viewer/scene/model/layer/programs/SnapProgram.js +28 -0
  44. package/src/viewer/scene/scene/Scene.js +21 -38
  45. package/src/viewer/scene/webgl/ArrayBuf.js +5 -20
  46. package/src/viewer/scene/webgl/FrameContext.js +14 -86
  47. package/src/viewer/scene/webgl/RenderBuffer.js +62 -262
  48. package/src/viewer/scene/webgl/Renderer.js +880 -843
  49. package/src/viewer/scene/webgl/WebGLRenderer.js +612 -0
  50. package/src/viewer/scene/webgl/occlusion/OcclusionLayer.js +60 -132
  51. package/src/viewer/scene/webgl/occlusion/OcclusionTester.js +167 -328
  52. package/src/viewer/scene/mesh/draw/DrawRenderer.js +0 -996
  53. package/src/viewer/scene/mesh/emphasis/EmphasisEdgesRenderer.js +0 -259
  54. package/src/viewer/scene/mesh/emphasis/EmphasisEdgesShaderSource.js +0 -159
  55. package/src/viewer/scene/mesh/emphasis/EmphasisFillRenderer.js +0 -286
  56. package/src/viewer/scene/mesh/emphasis/EmphasisFillShaderSource.js +0 -262
  57. package/src/viewer/scene/mesh/occlusion/OcclusionRenderer.js +0 -221
  58. package/src/viewer/scene/mesh/pick/PickMeshRenderer.js +0 -223
  59. package/src/viewer/scene/mesh/pick/PickTriangleRenderer.js +0 -200
  60. package/src/viewer/scene/mesh/shadow/ShadowRenderer.js +0 -214
  61. package/src/viewer/scene/model/RENDER_PASSES.js +0 -34
  62. package/src/viewer/scene/model/dtx/BindableDataTexture.js +0 -31
  63. package/src/viewer/scene/model/dtx/lines/DTXLinesBuffer.js +0 -27
  64. package/src/viewer/scene/model/dtx/lines/DTXLinesLayer.js +0 -897
  65. package/src/viewer/scene/model/dtx/lines/DTXLinesState.js +0 -46
  66. package/src/viewer/scene/model/dtx/lines/DTXLinesTextureFactory.js +0 -411
  67. package/src/viewer/scene/model/dtx/lines/dataTextureRamStats.js +0 -49
  68. package/src/viewer/scene/model/dtx/lines/rebucketPositions.js +0 -208
  69. package/src/viewer/scene/model/dtx/lines/renderers/DTXLinesColorRenderer.js +0 -389
  70. package/src/viewer/scene/model/dtx/lines/renderers/DTXLinesRenderers.js +0 -65
  71. package/src/viewer/scene/model/dtx/triangles/DTXTrianglesLayer.js +0 -1399
  72. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesBuffer.js +0 -46
  73. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesState.js +0 -250
  74. package/src/viewer/scene/model/dtx/triangles/lib/DTXTrianglesTextureFactory.js +0 -518
  75. package/src/viewer/scene/model/dtx/triangles/lib/dataTextureRamStats.js +0 -54
  76. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesColorRenderer.js +0 -639
  77. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesDepthRenderer.js +0 -464
  78. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesEdgesColorRenderer.js +0 -423
  79. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesEdgesRenderer.js +0 -431
  80. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesNormalsRenderer.js +0 -345
  81. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesOcclusionRenderer.js +0 -436
  82. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickDepthRenderer.js +0 -466
  83. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickMeshRenderer.js +0 -437
  84. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickNormalsFlatRenderer.js +0 -460
  85. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesPickNormalsRenderer.js +0 -437
  86. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesRenderers.js +0 -307
  87. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSilhouetteRenderer.js +0 -472
  88. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSnapInitRenderer.js +0 -474
  89. package/src/viewer/scene/model/dtx/triangles/renderers/DTXTrianglesSnapRenderer.js +0 -435
  90. package/src/viewer/scene/model/vbo/ScratchMemory.js +0 -63
  91. package/src/viewer/scene/model/vbo/VBORenderer.js +0 -664
  92. package/src/viewer/scene/model/vbo/batching/lines/VBOBatchingLinesLayer.js +0 -704
  93. package/src/viewer/scene/model/vbo/batching/lines/lib/VBOBatchingLinesBuffer.js +0 -19
  94. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLineSnapInitRenderer.js +0 -318
  95. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesColorRenderer.js +0 -114
  96. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesRenderers.js +0 -99
  97. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSilhouetteRenderer.js +0 -124
  98. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapInitRenderer.js +0 -313
  99. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOBatchingLinesSnapRenderer.js +0 -311
  100. package/src/viewer/scene/model/vbo/batching/lines/renderers/VBOSceneModelLineBatchingRenderer.js +0 -24
  101. package/src/viewer/scene/model/vbo/batching/points/VBOBatchingPointsLayer.js +0 -688
  102. package/src/viewer/scene/model/vbo/batching/points/VBOBatchingPointsRenderer.js +0 -24
  103. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsColorRenderer.js +0 -168
  104. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsOcclusionRenderer.js +0 -136
  105. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsPickDepthRenderer.js +0 -156
  106. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsPickMeshRenderer.js +0 -148
  107. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsRenderers.js +0 -144
  108. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsShadowRenderer.js +0 -107
  109. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSilhouetteRenderer.js +0 -146
  110. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapInitRenderer.js +0 -315
  111. package/src/viewer/scene/model/vbo/batching/points/renderers/VBOBatchingPointsSnapRenderer.js +0 -305
  112. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesBuffer.js +0 -24
  113. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js +0 -1346
  114. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesColorRenderer.js +0 -131
  115. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesEmphasisRenderer.js +0 -135
  116. package/src/viewer/scene/model/vbo/batching/triangles/renderers/EdgesRenderer.js +0 -12
  117. package/src/viewer/scene/model/vbo/batching/triangles/renderers/Renderers.js +0 -403
  118. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesBatchingRenderer.js +0 -35
  119. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorRenderer.js +0 -252
  120. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorTextureRenderer.js +0 -294
  121. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesDepthRenderer.js +0 -115
  122. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesFlatColorRenderer.js +0 -246
  123. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesNormalsRenderer.js +0 -139
  124. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesOcclusionRenderer.js +0 -125
  125. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPBRRenderer.js +0 -561
  126. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickDepthRenderer.js +0 -144
  127. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickMeshRenderer.js +0 -130
  128. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickNormalsFlatRenderer.js +0 -121
  129. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesPickNormalsRenderer.js +0 -131
  130. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesShadowRenderer.js +0 -107
  131. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSilhouetteRenderer.js +0 -144
  132. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapInitRenderer.js +0 -313
  133. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesSnapRenderer.js +0 -312
  134. package/src/viewer/scene/model/vbo/float16.js +0 -760
  135. package/src/viewer/scene/model/vbo/instancing/lines/VBOInstancingLinesLayer.js +0 -711
  136. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesColorRenderer.js +0 -148
  137. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesRenderer.js +0 -26
  138. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesRenderers.js +0 -139
  139. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSilhouetteRenderer.js +0 -127
  140. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapInitRenderer.js +0 -308
  141. package/src/viewer/scene/model/vbo/instancing/lines/renderers/VBOInstancingLinesSnapRenderer.js +0 -299
  142. package/src/viewer/scene/model/vbo/instancing/points/VBOInstancingPointsLayer.js +0 -733
  143. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsColorRenderer.js +0 -174
  144. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsDepthRenderer.js +0 -156
  145. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsOcclusionRenderer.js +0 -149
  146. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsPickDepthRenderer.js +0 -170
  147. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsPickMeshRenderer.js +0 -152
  148. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsRenderer.js +0 -26
  149. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsRenderers.js +0 -174
  150. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsShadowRenderer.js +0 -119
  151. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSilhouetteRenderer.js +0 -154
  152. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapInitRenderer.js +0 -307
  153. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingPointsSnapRenderer.js +0 -293
  154. package/src/viewer/scene/model/vbo/instancing/points/renderers/VBOInstancingTrianglesRenderer.js +0 -150
  155. package/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js +0 -1289
  156. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesColorRenderer.js +0 -135
  157. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesEmphasisRenderer.js +0 -137
  158. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/EdgesRenderer.js +0 -12
  159. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/Renderers.js +0 -403
  160. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorRenderer.js +0 -267
  161. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorTextureRenderer.js +0 -299
  162. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesDepthRenderer.js +0 -126
  163. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesFlatColorRenderer.js +0 -255
  164. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesFlatNormalsRenderer.js +0 -130
  165. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesInstancingRenderer.js +0 -32
  166. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesNormalsRenderer.js +0 -138
  167. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesOcclusionRenderer.js +0 -128
  168. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPBRRenderer.js +0 -561
  169. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickDepthRenderer.js +0 -149
  170. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickMeshRenderer.js +0 -136
  171. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickNormalsFlatRenderer.js +0 -130
  172. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesPickNormalsRenderer.js +0 -145
  173. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesShadowRenderer.js +0 -113
  174. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSilhouetteRenderer.js +0 -148
  175. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapInitRenderer.js +0 -319
  176. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesSnapRenderer.js +0 -310
  177. package/src/viewer/scene/webgl/Attribute.js +0 -23
  178. package/src/viewer/scene/webgl/Program.js +0 -181
  179. package/src/viewer/scene/webgl/RenderBufferManager.js +0 -34
  180. package/src/viewer/scene/webgl/Sampler.js +0 -18
  181. package/src/viewer/scene/webgl/Shader.js +0 -49
  182. package/src/viewer/scene/webgl/sao/SAODepthLimitedBlurRenderer.js +0 -324
  183. 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
  const byDist = normalFillTransparentBin.map(d => ({ drawable: d, distSq: math.distVec3(d.origin || vec3_0, eye) }));
728
971
  byDist.sort((a, b) => b.distSq - a.distSq);
729
972
  for (i = 0; i < normalFillTransparentBinLen; i++) {
@@ -900,12 +1143,30 @@ const Renderer = function (scene, options) {
900
1143
  for (let ii = 0; ii < numVertexAttribs; ii++) {
901
1144
  gl.disableVertexAttribArray(ii);
902
1145
  }
903
-
904
- if (unbindOutputFrameBuffer) {
905
- unbindOutputFrameBuffer(params.pass);
906
- }
907
1146
  }
908
1147
 
1148
+ const resetPickFrameCtx = (canvasPos, clipTransformDiv, camera, eye, projMatrix, viewMatrix, frameCtx) => {
1149
+ frameCtx.reset();
1150
+ frameCtx.backfaces = true;
1151
+ frameCtx.frontface = true; // "ccw"
1152
+
1153
+ frameCtx.viewParams.eye = eye;
1154
+ frameCtx.viewParams.projMatrix = projMatrix;
1155
+ frameCtx.viewParams.viewMatrix = viewMatrix;
1156
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(camera, gl.drawingBufferHeight);
1157
+
1158
+ const resolutionScale = scene.canvas.resolutionScale;
1159
+ frameCtx.pickClipPos = [
1160
+ canvasPos ? ( 2 * canvasPos[0] * resolutionScale / gl.drawingBufferWidth - 1) : 0,
1161
+ canvasPos ? (1 - 2 * canvasPos[1] * resolutionScale / gl.drawingBufferHeight) : 0
1162
+ ];
1163
+
1164
+ frameCtx.pickClipPosInv = [
1165
+ gl.drawingBufferWidth / clipTransformDiv,
1166
+ gl.drawingBufferHeight / clipTransformDiv
1167
+ ];
1168
+ };
1169
+
909
1170
  /**
910
1171
  * Picks an Entity.
911
1172
  * @private
@@ -913,11 +1174,18 @@ const Renderer = function (scene, options) {
913
1174
  this.pick = (function () {
914
1175
 
915
1176
  const tempVec3a = math.vec3();
1177
+ const tempVec3b = math.vec3();
1178
+ const tempVec4a = math.vec4();
1179
+ const tempVec4b = math.vec4();
1180
+ const tempVec4c = math.vec4();
1181
+ const tempVec4d = math.vec4();
1182
+ const tempVec4e = math.vec4();
916
1183
  const tempMat4a = math.mat4();
917
1184
  const tempMat4b = math.mat4();
1185
+ const tempMat4c = math.mat4();
1186
+ const tempMat4d = math.mat4();
918
1187
 
919
- const randomVec3 = math.vec3();
920
- const up = math.vec3([0, 1, 0]);
1188
+ const upVec = math.vec3([0, 1, 0]);
921
1189
  const _pickResult = new PickResult();
922
1190
 
923
1191
  const nearAndFar = math.vec2();
@@ -929,16 +1197,22 @@ const Renderer = function (scene, options) {
929
1197
  const worldSurfacePos = math.vec3();
930
1198
  const worldSurfaceNormal = math.vec3();
931
1199
 
1200
+ const pickBuffer = new RenderBuffer(gl);
1201
+ pickBuffer.setSize([1, 1]);
1202
+
1203
+ const pickNormalBuffer = new RenderBuffer(gl, [gl.RGBA32I]);
1204
+ pickNormalBuffer.setSize([3, 3]);
1205
+
932
1206
  return function (params, pickResult = _pickResult) {
933
1207
 
934
1208
  pickResult.reset();
935
1209
 
936
1210
  updateDrawlist();
937
1211
 
938
- let look;
939
1212
  let pickViewMatrix = null;
940
1213
  let pickProjMatrix = null;
941
1214
  let projection = null;
1215
+ const camera = scene.camera;
942
1216
 
943
1217
  pickResult.pickSurface = params.pickSurface;
944
1218
 
@@ -947,12 +1221,12 @@ const Renderer = function (scene, options) {
947
1221
  canvasPos[0] = params.canvasPos[0];
948
1222
  canvasPos[1] = params.canvasPos[1];
949
1223
 
950
- pickViewMatrix = scene.camera.viewMatrix;
951
- pickProjMatrix = scene.camera.projMatrix;
952
- projection = scene.camera.projection;
1224
+ pickViewMatrix = camera.viewMatrix;
1225
+ pickProjMatrix = camera.projMatrix;
1226
+ projection = camera.projection;
953
1227
 
954
- nearAndFar[0] = scene.camera.project.near;
955
- nearAndFar[1] = scene.camera.project.far;
1228
+ nearAndFar[0] = camera.project.near;
1229
+ nearAndFar[1] = camera.project.far;
956
1230
 
957
1231
  pickResult.canvasPos = params.canvasPos;
958
1232
 
@@ -964,33 +1238,37 @@ const Renderer = function (scene, options) {
964
1238
  if (params.matrix) {
965
1239
 
966
1240
  pickViewMatrix = params.matrix;
967
- pickProjMatrix = scene.camera.projMatrix;
968
- projection = scene.camera.projection;
1241
+ pickProjMatrix = camera.projMatrix;
1242
+ projection = camera.projection;
969
1243
 
970
- nearAndFar[0] = scene.camera.project.near;
971
- nearAndFar[1] = scene.camera.project.far;
1244
+ nearAndFar[0] = camera.project.near;
1245
+ nearAndFar[1] = camera.project.far;
972
1246
 
973
1247
  } else {
974
1248
 
975
1249
  worldRayOrigin.set(params.origin || [0, 0, 0]);
976
1250
  worldRayDir.set(params.direction || [0, 0, 1]);
977
1251
 
978
- look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a);
979
-
980
- randomVec3[0] = Math.random();
981
- randomVec3[1] = Math.random();
982
- randomVec3[2] = Math.random();
1252
+ const look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a);
1253
+ const up = tempVec3b;
983
1254
 
984
- math.normalizeVec3(randomVec3);
985
- math.cross3Vec3(worldRayDir, randomVec3, up);
1255
+ if (Math.abs(math.dotVec3(worldRayDir, upVec)) > (1 - 1e-6)) { // worldRayDir aligned with Y axis
1256
+ up[0] = 0;
1257
+ up[1] = 0;
1258
+ up[2] = Math.sign(worldRayDir[1]);
1259
+ } else {
1260
+ math.cross3Vec3(worldRayDir, upVec, up);
1261
+ math.cross3Vec3(up, worldRayDir, up);
1262
+ math.normalizeVec3(up, up);
1263
+ }
986
1264
 
987
1265
  pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b);
988
- // pickProjMatrix = scene.camera.projMatrix;
989
- pickProjMatrix = scene.camera.ortho.matrix;
1266
+ // pickProjMatrix = camera.projMatrix;
1267
+ pickProjMatrix = camera.ortho.matrix;
990
1268
  projection = "ortho";
991
1269
 
992
- nearAndFar[0] = scene.camera.ortho.near;
993
- nearAndFar[1] = scene.camera.ortho.far;
1270
+ nearAndFar[0] = camera.ortho.near;
1271
+ nearAndFar[1] = camera.ortho.far;
994
1272
 
995
1273
  pickResult.origin = worldRayOrigin;
996
1274
  pickResult.direction = worldRayDir;
@@ -1000,23 +1278,42 @@ const Renderer = function (scene, options) {
1000
1278
  canvasPos[1] = canvas.clientHeight * 0.5;
1001
1279
  }
1002
1280
 
1003
- for (let type in drawableTypeInfo) {
1004
- if (drawableTypeInfo.hasOwnProperty(type)) {
1005
- const drawableList = drawableTypeInfo[type].drawableList;
1006
- for (let i = 0, len = drawableList.length; i < len; i++) {
1007
- const drawable = drawableList[i];
1008
- if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
1009
- drawable.setPickMatrices(pickViewMatrix, pickProjMatrix);
1010
- }
1011
- }
1012
- }
1013
- }
1281
+ pickBuffer.bind();
1014
1282
 
1015
- const pickBuffer = renderBufferManager.getRenderBuffer("pick", {size: [1, 1]});
1283
+ const resetFrameCtx = (clipTransformDiv) => resetPickFrameCtx(canvasPos, clipTransformDiv, camera, pickResult.origin || camera.eye, pickProjMatrix || camera.projMatrix, pickViewMatrix || camera.viewMatrix, frameCtx);
1016
1284
 
1017
- pickBuffer.bind();
1285
+ // gpuPickPickable
1286
+ resetFrameCtx(1);
1287
+ frameCtx.pickInvisible = !!params.pickInvisible;
1288
+
1289
+ gl.viewport(0, 0, 1, 1);
1290
+ gl.depthMask(true);
1291
+ gl.enable(gl.DEPTH_TEST);
1292
+ gl.disable(gl.CULL_FACE);
1293
+ gl.disable(gl.BLEND);
1294
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1295
+
1296
+ const includeEntityIds = params.includeEntityIds;
1297
+ const excludeEntityIds = params.excludeEntityIds;
1298
+
1299
+ const renderDrawables = function(drawables) {
1300
+ drawables.forEach(drawable => {
1301
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawPickMesh // TODO: push this logic into drawable
1302
+ && ((! includeEntityIds) || includeEntityIds[drawable.id])
1303
+ && ((! excludeEntityIds) || (! excludeEntityIds[drawable.id]))) {
1304
+ drawable.drawPickMesh(frameCtx);
1305
+ }
1306
+ });
1307
+ };
1308
+
1309
+ renderDrawables(postCullDrawableList);
1310
+ if (uiDrawableList.length > 0) {
1311
+ gl.clear(gl.DEPTH_BUFFER_BIT);
1312
+ renderDrawables(uiDrawableList);
1313
+ }
1018
1314
 
1019
- const pickable = gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult);
1315
+ const pickID = pixelToInt(pickBuffer.read(0, 0));
1316
+ const pickable = (pickID >= 0) && pickIDs.items[pickID];
1020
1317
 
1021
1318
  if (!pickable) {
1022
1319
  pickBuffer.unbind();
@@ -1036,265 +1333,108 @@ const Renderer = function (scene, options) {
1036
1333
 
1037
1334
  if (pickable.canPickTriangle && pickable.canPickTriangle()) {
1038
1335
 
1039
- gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1040
-
1041
- pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, projection, pickResult);
1336
+ if (pickable.drawPickTriangles) {
1337
+ resetFrameCtx(1);
1338
+ // frameCtx.pickInvisible = !!params.pickInvisible;
1042
1339
 
1043
- pickResult.pickSurfacePrecision = false;
1340
+ gl.viewport(0, 0, 1, 1);
1341
+ gl.clearColor(0, 0, 0, 0);
1342
+ gl.enable(gl.DEPTH_TEST);
1343
+ gl.disable(gl.CULL_FACE);
1344
+ gl.disable(gl.BLEND);
1345
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1044
1346
 
1045
- } else {
1046
-
1047
- if (pickable.canPickWorldPos && pickable.canPickWorldPos()) {
1048
-
1049
- gpuPickWorldPos(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult);
1050
-
1051
- if (params.pickSurfaceNormal !== false) {
1052
- gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult);
1053
- }
1347
+ pickable.drawPickTriangles(frameCtx);
1054
1348
 
1055
- pickResult.pickSurfacePrecision = false;
1349
+ pickResult.primIndex = 3 * pixelToInt(pickBuffer.read(0, 0)); // Convert from triangle number to first vertex in indices
1056
1350
  }
1057
- }
1058
- }
1059
- pickBuffer.unbind();
1060
- pickResult.entity = pickedEntity;
1061
- return pickResult;
1062
- };
1063
- })();
1064
-
1065
- function gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult) {
1066
-
1067
- const resolutionScale = scene.canvas.resolutionScale;
1068
-
1069
- frameCtx.reset();
1070
- frameCtx.backfaces = true;
1071
- frameCtx.frontface = true; // "ccw"
1072
- frameCtx.pickOrigin = pickResult.origin;
1073
- frameCtx.pickViewMatrix = pickViewMatrix;
1074
- frameCtx.pickProjMatrix = pickProjMatrix;
1075
- frameCtx.pickInvisible = !!params.pickInvisible;
1076
- frameCtx.pickClipPos = [
1077
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1078
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1079
- ];
1080
-
1081
- gl.viewport(0, 0, 1, 1);
1082
- gl.depthMask(true);
1083
- gl.enable(gl.DEPTH_TEST);
1084
- gl.disable(gl.CULL_FACE);
1085
- gl.disable(gl.BLEND);
1086
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1087
-
1088
- const includeEntityIds = params.includeEntityIds;
1089
- const excludeEntityIds = params.excludeEntityIds;
1090
-
1091
- const renderDrawables = function(drawables) {
1092
- for (let i = 0, len = drawables.length; i < len; i++) {
1093
- const drawable = drawables[i];
1094
- if (drawable.culled === true || drawable.visible === false) {
1095
- continue;
1096
- }
1097
- if (!drawable.drawPickMesh || (params.pickInvisible !== true && drawable.visible === false) || drawable.pickable === false) {
1098
- continue;
1099
- }
1100
- if (includeEntityIds && !includeEntityIds[drawable.id]) { // TODO: push this logic into drawable
1101
- continue;
1102
- }
1103
- if (excludeEntityIds && excludeEntityIds[drawable.id]) {
1104
- continue;
1105
- }
1106
- drawable.drawPickMesh(frameCtx);
1107
- }
1108
- };
1109
-
1110
- renderDrawables(postCullDrawableList);
1111
- if (uiDrawableList.length > 0) {
1112
- gl.clear(gl.DEPTH_BUFFER_BIT);
1113
- renderDrawables(uiDrawableList);
1114
- }
1115
-
1116
- const pix = pickBuffer.read(0, 0);
1117
- const pickID = pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24);
1118
-
1119
- if (pickID < 0) {
1120
- return;
1121
- }
1122
-
1123
- const pickable = pickIDs.items[pickID];
1124
-
1125
- return pickable;
1126
- }
1127
-
1128
- function gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1129
-
1130
- if (!pickable.drawPickTriangles) {
1131
- return;
1132
- }
1133
-
1134
- const resolutionScale = scene.canvas.resolutionScale;
1135
-
1136
- frameCtx.reset();
1137
- frameCtx.backfaces = true;
1138
- frameCtx.frontface = true; // "ccw"
1139
- frameCtx.pickOrigin = pickResult.origin;
1140
- frameCtx.pickViewMatrix = pickViewMatrix; // Can be null
1141
- frameCtx.pickProjMatrix = pickProjMatrix; // Can be null
1142
- // frameCtx.pickInvisible = !!params.pickInvisible;
1143
- frameCtx.pickClipPos = [
1144
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1145
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1146
- ];
1147
1351
 
1148
- gl.viewport(0, 0, 1, 1);
1149
-
1150
- gl.clearColor(0, 0, 0, 0);
1151
- gl.enable(gl.DEPTH_TEST);
1152
- gl.disable(gl.CULL_FACE);
1153
- gl.disable(gl.BLEND);
1154
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1155
-
1156
- pickable.drawPickTriangles(frameCtx);
1157
-
1158
- const pix = pickBuffer.read(0, 0);
1159
-
1160
- let primIndex = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256);
1161
-
1162
- primIndex *= 3; // Convert from triangle number to first vertex in indices
1163
-
1164
- pickResult.primIndex = primIndex;
1165
- }
1166
-
1167
- const gpuPickWorldPos = (function () {
1168
-
1169
- const tempVec4a = math.vec4();
1170
- const tempVec4b = math.vec4();
1171
- const tempVec4c = math.vec4();
1172
- const tempVec4d = math.vec4();
1173
- const tempVec4e = math.vec4();
1174
- const tempMat4a = math.mat4();
1175
- const tempMat4b = math.mat4();
1176
- const tempMat4c = math.mat4();
1177
-
1178
- return function (pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult) {
1179
-
1180
- const resolutionScale = scene.canvas.resolutionScale;
1181
-
1182
- frameCtx.reset();
1183
- frameCtx.backfaces = true;
1184
- frameCtx.frontface = true; // "ccw"
1185
- frameCtx.pickOrigin = pickResult.origin;
1186
- frameCtx.pickViewMatrix = pickViewMatrix;
1187
- frameCtx.pickProjMatrix = pickProjMatrix;
1188
- frameCtx.pickZNear = nearAndFar[0];
1189
- frameCtx.pickZFar = nearAndFar[1];
1190
- frameCtx.pickElementsCount = pickable.pickElementsCount;
1191
- frameCtx.pickElementsOffset = pickable.pickElementsOffset;
1192
- frameCtx.pickClipPos = [
1193
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1194
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight)
1195
- ];
1352
+ pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, projection, pickResult);
1196
1353
 
1197
- gl.viewport(0, 0, 1, 1);
1354
+ pickResult.pickSurfacePrecision = false;
1198
1355
 
1199
- gl.clearColor(0, 0, 0, 0);
1200
- gl.depthMask(true);
1201
- gl.enable(gl.DEPTH_TEST);
1202
- gl.disable(gl.CULL_FACE);
1203
- gl.disable(gl.BLEND);
1204
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1356
+ } else {
1205
1357
 
1206
- pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths
1358
+ if (pickable.canPickWorldPos && pickable.canPickWorldPos()) {
1207
1359
 
1208
- const pix = pickBuffer.read(0, 0);
1360
+ // pickWorldPos
1361
+ resetFrameCtx(1);
1362
+ frameCtx.viewParams.near = nearAndFar[0];
1363
+ frameCtx.viewParams.far = nearAndFar[1];
1209
1364
 
1210
- const screenZ = unpackDepth(pix); // Get screen-space Z at the given canvas coords
1365
+ gl.viewport(0, 0, 1, 1);
1211
1366
 
1212
- // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top
1213
- const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2);
1214
- const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2);
1367
+ gl.clearColor(0, 0, 0, 0);
1368
+ gl.depthMask(true);
1369
+ gl.enable(gl.DEPTH_TEST);
1370
+ gl.disable(gl.CULL_FACE);
1371
+ gl.disable(gl.BLEND);
1372
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1215
1373
 
1216
- const origin = pickable.origin;
1217
- let pvMat;
1374
+ pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths
1218
1375
 
1219
- if (origin) {
1220
- const rtcPickViewMat = createRTCViewMat(pickViewMatrix, origin, tempMat4a);
1221
- pvMat = math.mulMat4(pickProjMatrix, rtcPickViewMat, tempMat4b);
1376
+ const screenZ = math.dotVec4(pickBuffer.read(0, 0), bitShiftScreenZ);
1222
1377
 
1223
- } else {
1224
- pvMat = math.mulMat4(pickProjMatrix, pickViewMatrix, tempMat4b);
1225
- }
1378
+ // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top
1379
+ const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2);
1380
+ const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2);
1226
1381
 
1227
- const pvMatInverse = math.inverseMat4(pvMat, tempMat4c);
1382
+ const origin = pickable.origin;
1228
1383
 
1229
- tempVec4a[0] = x;
1230
- tempVec4a[1] = y;
1231
- tempVec4a[2] = -1;
1232
- tempVec4a[3] = 1;
1384
+ const pvMatInverse = math.inverseMat4(math.mulMat4(pickProjMatrix, (origin ? createRTCViewMat(pickViewMatrix, origin, tempMat4a) : pickViewMatrix), tempMat4c), tempMat4d);
1233
1385
 
1234
- let world1 = math.transformVec4(pvMatInverse, tempVec4a);
1235
- world1 = math.mulVec4Scalar(world1, 1 / world1[3]);
1386
+ const toWorld = (z, dst) => {
1387
+ dst[0] = x;
1388
+ dst[1] = y;
1389
+ dst[2] = z;
1390
+ dst[3] = 1;
1391
+ math.transformVec4(pvMatInverse, dst, dst);
1392
+ return math.mulVec4Scalar(dst, 1 / dst[3]);
1393
+ };
1236
1394
 
1237
- tempVec4b[0] = x;
1238
- tempVec4b[1] = y;
1239
- tempVec4b[2] = 1;
1240
- tempVec4b[3] = 1;
1395
+ const world1 = toWorld(-1, tempVec4a);
1396
+ const world2 = toWorld( 1, tempVec4b);
1241
1397
 
1242
- let world2 = math.transformVec4(pvMatInverse, tempVec4b);
1243
- world2 = math.mulVec4Scalar(world2, 1 / world2[3]);
1398
+ const dir = math.subVec3(world2, world1, tempVec4c);
1399
+ const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e);
1244
1400
 
1245
- const dir = math.subVec3(world2, world1, tempVec4c);
1246
- const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e);
1401
+ if (origin) {
1402
+ math.addVec3(worldPos, origin);
1403
+ }
1247
1404
 
1248
- if (origin) {
1249
- math.addVec3(worldPos, origin);
1250
- }
1405
+ pickResult.worldPos = worldPos;
1251
1406
 
1252
- pickResult.worldPos = worldPos;
1253
- }
1254
- })();
1407
+ if (params.pickSurfaceNormal !== false) {
1408
+ // gpuPickWorldNormal
1409
+ resetFrameCtx(3);
1255
1410
 
1256
- function drawSnapInit(frameCtx) {
1257
- frameCtx.snapPickLayerParams = [];
1258
- frameCtx.snapPickLayerNumber = 0;
1411
+ pickNormalBuffer.bind();
1259
1412
 
1260
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1413
+ gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]);
1414
+ gl.enable(gl.DEPTH_TEST);
1415
+ gl.disable(gl.CULL_FACE);
1416
+ gl.disable(gl.BLEND);
1417
+ gl.clear(gl.DEPTH_BUFFER_BIT);
1418
+ gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0]));
1261
1419
 
1262
- const drawable = postCullDrawableList[i];
1420
+ pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals
1263
1421
 
1264
- if (drawable.drawSnapInit) {
1265
- if (!drawable.culled && drawable.visible && drawable.pickable) {
1266
- drawable.drawSnapInit(frameCtx);
1267
- }
1268
- }
1269
- }
1270
- return frameCtx.snapPickLayerParams;
1271
- }
1422
+ const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1272
1423
 
1273
- function drawSnap(frameCtx) {
1274
- frameCtx.snapPickLayerParams = frameCtx.snapPickLayerParams || [];
1275
- frameCtx.snapPickLayerNumber = frameCtx.snapPickLayerParams.length;
1276
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1424
+ pickNormalBuffer.unbind();
1277
1425
 
1278
- const drawable = postCullDrawableList[i];
1426
+ pickResult.worldNormal = toWorldNormal(pix);
1427
+ }
1279
1428
 
1280
- if (drawable.drawSnapInit) {
1281
- if (drawable.drawSnap) {
1282
- if (!drawable.culled && drawable.visible && drawable.pickable) {
1283
- drawable.drawSnap(frameCtx);
1429
+ pickResult.pickSurfacePrecision = false;
1284
1430
  }
1285
1431
  }
1286
1432
  }
1287
- }
1288
- return frameCtx.snapPickLayerParams;
1289
- }
1290
-
1291
- function getClipPosX(pos, size) {
1292
- return 2 * (pos / size) - 1;
1293
- }
1294
-
1295
- function getClipPosY(pos, size) {
1296
- return 1 - 2 * (pos / size);
1297
- }
1433
+ pickBuffer.unbind();
1434
+ pickResult.entity = pickedEntity;
1435
+ return pickResult;
1436
+ };
1437
+ })();
1298
1438
 
1299
1439
  /**
1300
1440
  * @param {[number, number]} canvasPos
@@ -1308,6 +1448,18 @@ const Renderer = function (scene, options) {
1308
1448
 
1309
1449
  const _pickResult = new PickResult();
1310
1450
 
1451
+ const getVertexPickBuffer = (function() {
1452
+ const cache = { };
1453
+ return (snapRadiusInPixels) => {
1454
+ if (! (snapRadiusInPixels in cache)) {
1455
+ const buf = new RenderBuffer(gl, [gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI], true);
1456
+ buf.setSize([2 * snapRadiusInPixels + 1, 2 * snapRadiusInPixels + 1]);
1457
+ cache[snapRadiusInPixels] = buf;
1458
+ }
1459
+ return cache[snapRadiusInPixels];
1460
+ };
1461
+ })();
1462
+
1311
1463
  return function (params, pickResult = _pickResult) {
1312
1464
 
1313
1465
  const {canvasPos, origin, direction, snapRadius, snapToVertex, snapToEdge} = params;
@@ -1316,40 +1468,21 @@ const Renderer = function (scene, options) {
1316
1468
  return this.pick({canvasPos, pickSurface: true});
1317
1469
  }
1318
1470
 
1319
- const resolutionScale = scene.canvas.resolutionScale;
1320
-
1321
- frameCtx.reset();
1322
- frameCtx.backfaces = true;
1323
- frameCtx.frontface = true; // "ccw"
1324
- frameCtx.pickZNear = scene.camera.project.near;
1325
- frameCtx.pickZFar = scene.camera.project.far;
1326
-
1471
+ const camera = scene.camera;
1327
1472
  const snapRadiusInPixels = snapRadius || 30;
1328
-
1329
- const vertexPickBuffer = renderBufferManager.getRenderBuffer(
1330
- `uniquePickColors-aabs-${snapRadiusInPixels}`,
1331
- {
1332
- depthTexture: true,
1333
- size: [
1334
- 2 * snapRadiusInPixels + 1,
1335
- 2 * snapRadiusInPixels + 1,
1336
- ]
1337
- }
1338
- );
1339
-
1340
- frameCtx.snapVectorA = [
1341
- canvasPos ? getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth) : 0,
1342
- canvasPos ? getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight) : 0,
1343
- ];
1344
-
1345
- frameCtx.snapInvVectorAB = [
1346
- gl.drawingBufferWidth / (2 * snapRadiusInPixels),
1347
- gl.drawingBufferHeight / (2 * snapRadiusInPixels),
1348
- ];
1473
+ const viewMatrix = (canvasPos
1474
+ ? camera.viewMatrix
1475
+ : math.lookAtMat4v(
1476
+ origin,
1477
+ math.addVec3(origin, direction, math.vec3()),
1478
+ math.vec3([0, 1, 0]),
1479
+ math.mat4()));
1480
+ resetPickFrameCtx(canvasPos, 2 * snapRadiusInPixels, camera, camera.eye, camera.projMatrix, viewMatrix, frameCtx);
1349
1481
 
1350
1482
  // Bind and clear the snap render target
1351
1483
 
1352
- vertexPickBuffer.bind(gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI);
1484
+ const vertexPickBuffer = getVertexPickBuffer(snapRadiusInPixels);
1485
+ vertexPickBuffer.bind();
1353
1486
  gl.viewport(0, 0, vertexPickBuffer.size[0], vertexPickBuffer.size[1]);
1354
1487
  gl.enable(gl.DEPTH_TEST);
1355
1488
  gl.frontFace(gl.CCW);
@@ -1362,64 +1495,59 @@ const Renderer = function (scene, options) {
1362
1495
  gl.clearBufferiv(gl.COLOR, 1, new Int32Array([0, 0, 0, 0]));
1363
1496
  gl.clearBufferuiv(gl.COLOR, 2, new Uint32Array([0, 0, 0, 0]));
1364
1497
 
1365
- //////////////////////////////////
1366
- // Set view and proj mats for VBO renderers
1367
- ///////////////////////////////////////
1368
-
1369
- frameCtx.pickViewMatrix = (canvasPos
1370
- ? scene.camera.viewMatrix
1371
- : math.lookAtMat4v(
1372
- origin,
1373
- math.addVec3(origin, direction, math.vec3()),
1374
- math.vec3([0, 1, 0]),
1375
- math.mat4()));
1376
-
1377
- const pickProjMatrix = scene.camera.projMatrix;
1378
-
1379
- for (let type in drawableTypeInfo) {
1380
- if (drawableTypeInfo.hasOwnProperty(type)) {
1381
- const drawableList = drawableTypeInfo[type].drawableList;
1382
- for (let i = 0, len = drawableList.length; i < len; i++) {
1383
- const drawable = drawableList[i];
1384
- if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture
1385
- drawable.setPickMatrices(frameCtx.pickViewMatrix, pickProjMatrix);
1386
- }
1387
- }
1388
- }
1389
- }
1390
-
1391
1498
  // a) init z-buffer
1392
1499
  gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
1393
- const layerParamsSurface = drawSnapInit(frameCtx);
1500
+
1501
+ frameCtx.snapPickLayerParams = [];
1502
+ frameCtx.snapPickLayerParams.push(null); // This recreates previous situation, which relied on snapPickLayerNumber
1503
+ postCullDrawableList.forEach(drawable => {
1504
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawSnapInit) {
1505
+ drawable.drawSnapInit(frameCtx);
1506
+ }
1507
+ });
1508
+
1509
+ const layerParamsSurface = frameCtx.snapPickLayerParams;
1394
1510
 
1395
1511
  // b) snap-pick
1396
- const layerParamsSnap = []
1397
- frameCtx.snapPickLayerParams = layerParamsSnap;
1512
+ frameCtx.snapPickLayerParams = [];
1398
1513
 
1399
1514
  gl.depthMask(false);
1400
1515
  gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
1401
1516
 
1402
- if (snapToVertex && snapToEdge) {
1403
- frameCtx.snapMode = "edge";
1404
- drawSnap(frameCtx);
1405
-
1406
- frameCtx.snapMode = "vertex";
1407
- frameCtx.snapPickLayerNumber++;
1408
-
1409
- drawSnap(frameCtx);
1410
- } else {
1411
- frameCtx.snapMode = snapToVertex ? "vertex" : "edge";
1517
+ const drawSnap = (snapMode) => {
1518
+ frameCtx.snapMode = snapMode;
1519
+ frameCtx.snapPickLayerParams.push(null); // This recreates previous situation, which relied on snapPickLayerNumber
1520
+ postCullDrawableList.forEach(drawable => {
1521
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawSnap) {
1522
+ drawable.drawSnap(frameCtx);
1523
+ }
1524
+ });
1525
+ };
1412
1526
 
1413
- drawSnap(frameCtx);
1527
+ if (snapToEdge) {
1528
+ drawSnap("edge");
1529
+ }
1530
+ if (snapToVertex) {
1531
+ drawSnap("vertex");
1414
1532
  }
1415
1533
 
1416
1534
  gl.depthMask(true);
1417
1535
 
1536
+ const layerParamsSnap = frameCtx.snapPickLayerParams;
1537
+
1418
1538
  // Read and decode the snapped coordinates
1539
+ const readSnapPickBuffer = (source, arrayType, glType) => {
1540
+ const w = vertexPickBuffer.buffer.width;
1541
+ const h = vertexPickBuffer.buffer.height;
1542
+ const pix = new arrayType(4 * w * h);
1543
+ gl.readBuffer(source);
1544
+ gl.readPixels(0, 0, w, h, gl.RGBA_INTEGER, glType, pix, 0);
1545
+ return pix;
1546
+ };
1419
1547
 
1420
- const snapPickResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1421
- const snapPickNormalResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4, 1);
1422
- const snapPickIdResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.UNSIGNED_INT, Uint32Array, 4, 2);
1548
+ const snapPickResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT0, Int32Array, gl.INT);
1549
+ const snapPickNormalResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT1, Int32Array, gl.INT);
1550
+ const snapPickIdResultArray = readSnapPickBuffer(gl.COLOR_ATTACHMENT2, Uint32Array, gl.UNSIGNED_INT);
1423
1551
 
1424
1552
  vertexPickBuffer.unbind();
1425
1553
 
@@ -1438,195 +1566,76 @@ const Renderer = function (scene, options) {
1438
1566
 
1439
1567
  if (pickResultMiddleXY[3] !== 0) {
1440
1568
  const pickedLayerParmasSurface = layerParamsSurface[Math.abs(pickResultMiddleXY[3]) % layerParamsSurface.length];
1441
- const origin = pickedLayerParmasSurface.origin;
1442
- const scale = pickedLayerParmasSurface.coordinateScale;
1443
- worldPos = [
1444
- pickResultMiddleXY[0] * scale[0] + origin[0],
1445
- pickResultMiddleXY[1] * scale[1] + origin[1],
1446
- pickResultMiddleXY[2] * scale[2] + origin[2],
1447
- ];
1448
- worldNormal = math.normalizeVec3([
1449
- pickNormalResultMiddleXY[0] / math.MAX_INT,
1450
- pickNormalResultMiddleXY[1] / math.MAX_INT,
1451
- pickNormalResultMiddleXY[2] / math.MAX_INT,
1452
- ]);
1453
-
1454
- const pickID =
1455
- pickPickableResultMiddleXY[0]
1456
- + (pickPickableResultMiddleXY[1] << 8)
1457
- + (pickPickableResultMiddleXY[2] << 16)
1458
- + (pickPickableResultMiddleXY[3] << 24);
1459
-
1460
- pickable = pickIDs.items[pickID];
1569
+ worldPos = toWorldPos(pickResultMiddleXY, pickedLayerParmasSurface.origin, pickedLayerParmasSurface.coordinateScale);
1570
+ worldNormal = toWorldNormal(pickNormalResultMiddleXY);
1571
+
1572
+ pickable = pickIDs.items[pixelToInt(pickPickableResultMiddleXY)];
1461
1573
  }
1462
1574
 
1463
1575
  // result 2) hi-precision snapped (to vertex/edge) world position
1464
1576
 
1465
- let snapPickResult = [];
1466
-
1577
+ const snapPickResult = [ ];
1467
1578
  for (let i = 0; i < snapPickResultArray.length; i += 4) {
1468
- if (snapPickResultArray[i + 3] > 0) {
1579
+ const layerNumber = snapPickResultArray[i + 3];
1580
+ if (layerNumber > 0) {
1469
1581
  const pixelNumber = Math.floor(i / 4);
1470
1582
  const w = vertexPickBuffer.size[0];
1471
1583
  const x = pixelNumber % w - Math.floor(w / 2);
1472
1584
  const y = Math.floor(pixelNumber / w) - Math.floor(w / 2);
1473
- const dist = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
1474
1585
  snapPickResult.push({
1475
- x,
1476
- y,
1477
- dist,
1478
- isVertex: snapToVertex && snapToEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapToVertex,
1479
- result: [
1480
- snapPickResultArray[i + 0],
1481
- snapPickResultArray[i + 1],
1482
- snapPickResultArray[i + 2],
1483
- snapPickResultArray[i + 3],
1484
- ],
1485
- normal: [
1486
- snapPickNormalResultArray[i + 0],
1487
- snapPickNormalResultArray[i + 1],
1488
- snapPickNormalResultArray[i + 2],
1489
- snapPickNormalResultArray[i + 3],
1490
- ],
1491
- id: [
1492
- snapPickIdResultArray[i + 0],
1493
- snapPickIdResultArray[i + 1],
1494
- snapPickIdResultArray[i + 2],
1495
- snapPickIdResultArray[i + 3],
1496
- ]
1586
+ dist: math.lenVec2([ x, y ]),
1587
+ isVertex: (snapToVertex && snapToEdge) ? (layerNumber > layerParamsSnap.length / 2) : snapToVertex,
1588
+ result: snapPickResultArray.subarray(i, i+4),
1589
+ normal: snapPickNormalResultArray.subarray(i, i+4),
1590
+ id: snapPickIdResultArray.subarray(i, i+4)
1497
1591
  });
1498
1592
  }
1499
1593
  }
1500
1594
 
1501
- let snappedWorldPos = null;
1502
- let snappedWorldNormal = null;
1503
- let snappedPickable = null;
1504
- let snapType = null;
1595
+ const getPickedEntity = pickable => (pickable && pickable.delegatePickedEntity) ? pickable.delegatePickedEntity() : pickable;
1505
1596
 
1506
1597
  if (snapPickResult.length > 0) {
1507
- // vertex snap first, then edge snap
1508
- snapPickResult.sort((a, b) => {
1509
- if (a.isVertex !== b.isVertex) {
1510
- return a.isVertex ? -1 : 1;
1511
- } else {
1512
- return a.dist - b.dist;
1513
- }
1514
- });
1515
-
1516
- snapType = snapPickResult[0].isVertex ? "vertex" : "edge";
1517
- const snapPick = snapPickResult[0].result;
1518
- const snapPickNormal = snapPickResult[0].normal;
1519
- const snapPickId = snapPickResult[0].id;
1520
-
1598
+ // closest vertex snap first, then closest edge snap
1599
+ const res = snapPickResult.reduce((a,b) => ((((a.isVertex-b.isVertex) || (b.dist-a.dist)) > 0) ? a : b));
1600
+ const snapPick = res.result;
1521
1601
  const pickedLayerParmas = layerParamsSnap[snapPick[3]];
1602
+ const snappedWorldPos = toWorldPos(snapPick, pickedLayerParmas.origin, pickedLayerParmas.coordinateScale);
1603
+ const snappedCanvasPos = camera.projectWorldPos(snappedWorldPos);
1604
+
1605
+ pickResult.reset();
1606
+ pickResult.snappedToEdge = !res.isVertex;
1607
+ pickResult.snappedToVertex = res.isVertex;
1608
+ pickResult.worldPos = snappedWorldPos;
1609
+ pickResult.worldNormal = toWorldNormal(res.normal);
1610
+ pickResult.entity = getPickedEntity(pickIDs.items[pixelToInt(res.id)]);
1611
+ pickResult.canvasPos = canvasPos || (worldPos && camera.projectWorldPos(worldPos)) || snappedCanvasPos;
1612
+ pickResult.snappedCanvasPos = snappedCanvasPos;
1613
+ return pickResult;
1614
+
1615
+ } else if (worldPos) {
1616
+
1617
+ pickResult.reset();
1618
+ pickResult.snappedToEdge = false;
1619
+ pickResult.snappedToVertex = false;
1620
+ pickResult.worldPos = worldPos;
1621
+ pickResult.worldNormal = worldNormal;
1622
+ pickResult.entity = getPickedEntity(pickable);
1623
+ pickResult.canvasPos = canvasPos || camera.projectWorldPos(worldPos);
1624
+ pickResult.snappedCanvasPos = canvasPos;
1625
+ return pickResult;
1522
1626
 
1523
- const origin = pickedLayerParmas.origin;
1524
- const scale = pickedLayerParmas.coordinateScale;
1525
-
1526
- snappedWorldNormal = math.normalizeVec3([
1527
- snapPickNormal[0] / math.MAX_INT,
1528
- snapPickNormal[1] / math.MAX_INT,
1529
- snapPickNormal[2] / math.MAX_INT,
1530
- ]);
1531
-
1532
- snappedWorldPos = [
1533
- snapPick[0] * scale[0] + origin[0],
1534
- snapPick[1] * scale[1] + origin[1],
1535
- snapPick[2] * scale[2] + origin[2],
1536
- ];
1537
-
1538
- snappedPickable = pickIDs.items[
1539
- snapPickId[0]
1540
- + (snapPickId[1] << 8)
1541
- + (snapPickId[2] << 16)
1542
- + (snapPickId[3] << 24)
1543
- ];
1544
- }
1545
-
1546
- if (null === worldPos && null == snappedWorldPos) { // If neither regular pick or snap pick, return null
1627
+ } else {
1547
1628
  return null;
1548
1629
  }
1549
-
1550
- let snappedCanvasPos = null;
1551
-
1552
- if (null !== snappedWorldPos) {
1553
- snappedCanvasPos = scene.camera.projectWorldPos(snappedWorldPos);
1554
- }
1555
-
1556
- const snappedEntity = (snappedPickable && snappedPickable.delegatePickedEntity) ? snappedPickable.delegatePickedEntity() : snappedPickable;
1557
- if (!snappedEntity && pickable) {
1558
- pickable = pickable.delegatePickedEntity ? pickable.delegatePickedEntity() : pickable;
1559
- }
1560
-
1561
- pickResult.reset();
1562
- pickResult.snappedToEdge = (snapType === "edge");
1563
- pickResult.snappedToVertex = (snapType === "vertex");
1564
- pickResult.worldPos = snappedWorldPos || worldPos;
1565
- pickResult.worldNormal = snappedWorldNormal || worldNormal;
1566
- pickResult.entity = snappedEntity || pickable;
1567
- pickResult.canvasPos = canvasPos || scene.camera.projectWorldPos(worldPos || snappedWorldPos);
1568
- pickResult.snappedCanvasPos = snappedCanvasPos || canvasPos;
1569
-
1570
- return pickResult;
1571
1630
  };
1572
1631
  })();
1573
1632
 
1574
- function unpackDepth(depthZ) {
1575
- const vec = [depthZ[0] / 256.0, depthZ[1] / 256.0, depthZ[2] / 256.0, depthZ[3] / 256.0];
1576
- const bitShift = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0];
1577
- return math.dotVec4(vec, bitShift);
1578
- }
1579
-
1580
- function gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) {
1581
-
1582
- const resolutionScale = scene.canvas.resolutionScale;
1583
-
1584
- frameCtx.reset();
1585
- frameCtx.backfaces = true;
1586
- frameCtx.frontface = true; // "ccw"
1587
- frameCtx.pickOrigin = pickResult.origin;
1588
- frameCtx.pickViewMatrix = pickViewMatrix;
1589
- frameCtx.pickProjMatrix = pickProjMatrix;
1590
- frameCtx.pickClipPos = [
1591
- getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth),
1592
- getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight),
1593
- ];
1594
-
1595
- const pickNormalBuffer = renderBufferManager.getRenderBuffer("pick-normal", {size: [3, 3]});
1596
-
1597
- pickNormalBuffer.bind(gl.RGBA32I);
1598
-
1599
- gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]);
1600
-
1601
- gl.enable(gl.DEPTH_TEST);
1602
- gl.disable(gl.CULL_FACE);
1603
- gl.disable(gl.BLEND);
1604
- gl.clear(gl.DEPTH_BUFFER_BIT);
1605
- gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0]));
1606
-
1607
- pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals
1608
-
1609
- const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4);
1610
-
1611
- pickNormalBuffer.unbind();
1612
-
1613
- const worldNormal = [
1614
- pix[0] / math.MAX_INT,
1615
- pix[1] / math.MAX_INT,
1616
- pix[2] / math.MAX_INT,
1617
- ];
1618
-
1619
- math.normalizeVec3(worldNormal);
1620
-
1621
- pickResult.worldNormal = worldNormal;
1622
- }
1623
-
1624
1633
  /**
1625
1634
  * Adds a {@link Marker} for occlusion testing.
1626
1635
  * @param marker
1627
1636
  */
1628
1637
  this.addMarker = function (marker) {
1629
- this._occlusionTester = this._occlusionTester || new OcclusionTester(scene, renderBufferManager);
1638
+ this._occlusionTester = this._occlusionTester || new OcclusionTester(scene);
1630
1639
  this._occlusionTester.addMarker(marker);
1631
1640
  scene.occlusionTestCountdown = 0;
1632
1641
  };
@@ -1657,11 +1666,17 @@ const Renderer = function (scene, options) {
1657
1666
 
1658
1667
  updateDrawlist();
1659
1668
 
1660
- this._occlusionTester.bindRenderBuf();
1669
+ const readPixelBuf = (! OCCLUSION_TEST_MODE) && renderBufferManager.getRenderBuffer("occlusionReadPix");
1670
+ if (readPixelBuf) {
1671
+ readPixelBuf.setSize([gl.drawingBufferWidth, gl.drawingBufferHeight]);
1672
+ readPixelBuf.bind();
1673
+ }
1661
1674
 
1662
1675
  frameCtx.reset();
1663
1676
  frameCtx.backfaces = true;
1664
1677
  frameCtx.frontface = true; // "ccw"
1678
+ frameCtx.viewParams = getSceneCameraViewParams();
1679
+ frameCtx.nearPlaneHeight = getNearPlaneHeight(scene.camera, gl.drawingBufferHeight);
1665
1680
 
1666
1681
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
1667
1682
  gl.clearColor(0, 0, 0, 0);
@@ -1670,121 +1685,143 @@ const Renderer = function (scene, options) {
1670
1685
  gl.disable(gl.BLEND);
1671
1686
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1672
1687
 
1673
-
1674
- for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
1675
-
1676
- const drawable = postCullDrawableList[i];
1677
-
1678
- if (!drawable.drawOcclusion || drawable.culled === true || drawable.visible === false || drawable.pickable === false) { // TODO: Option to exclude transparent?
1679
- continue;
1688
+ postCullDrawableList.forEach(drawable => {
1689
+ if (!drawable.culled && drawable.visible && drawable.pickable && drawable.drawOcclusion) { // TODO: Option to exclude transparent?
1690
+ drawable.drawOcclusion(frameCtx);
1680
1691
  }
1692
+ });
1681
1693
 
1682
- drawable.drawOcclusion(frameCtx);
1683
- }
1684
-
1685
- this._occlusionTester.drawMarkers(frameCtx);
1686
- this._occlusionTester.doOcclusionTest(); // Updates Marker "visible" properties
1687
- this._occlusionTester.unbindRenderBuf();
1688
- }
1689
- };
1694
+ this._occlusionTester.drawMarkers();
1690
1695
 
1691
- /**
1692
- * Read pixels from the renderer's current output. Performs a force-render first.
1693
- * @param pixels
1694
- * @param colors
1695
- * @param len
1696
- * @param opaqueOnly
1697
- * @private
1698
- */
1699
- this.readPixels = function (pixels, colors, len, opaqueOnly) {
1700
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1701
- snapshotBuffer.bind();
1702
- snapshotBuffer.clear();
1703
- this.render({force: true, opaqueOnly: opaqueOnly});
1704
- let color;
1705
- let i;
1706
- let j;
1707
- let k;
1708
- for (i = 0; i < len; i++) {
1709
- j = i * 2;
1710
- k = i * 4;
1711
- color = snapshotBuffer.read(pixels[j], pixels[j + 1]);
1712
- colors[k] = color[0];
1713
- colors[k + 1] = color[1];
1714
- colors[k + 2] = color[2];
1715
- colors[k + 3] = color[3];
1716
- }
1717
- snapshotBuffer.unbind();
1718
- imageDirty = true;
1719
- };
1720
-
1721
- /**
1722
- * Enter snapshot mode.
1723
- *
1724
- * Switches rendering to a hidden snapshot canvas.
1725
- *
1726
- * Exit snapshot mode using endSnapshot().
1727
- */
1728
- this.beginSnapshot = function (params = {}) {
1729
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1730
- if (params.width && params.height) {
1731
- snapshotBuffer.setSize([params.width, params.height]);
1732
- }
1733
- snapshotBuffer.bind();
1734
- snapshotBuffer.clear();
1735
- snapshotBound = true;
1736
- };
1737
-
1738
- /**
1739
- * When in snapshot mode, renders a frame of the current Scene state to the snapshot canvas.
1740
- */
1741
- this.renderSnapshot = function () {
1742
- if (!snapshotBound) {
1743
- return;
1696
+ if (readPixelBuf) {
1697
+ const resolutionScale = scene.canvas.resolutionScale;
1698
+ this._occlusionTester.doOcclusionTest( // Updates Marker "visible" properties
1699
+ (x, y) => readPixelBuf.read(Math.round(resolutionScale * x), Math.round(resolutionScale * y)));
1700
+ readPixelBuf.unbind();
1701
+ }
1744
1702
  }
1745
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1746
- snapshotBuffer.clear();
1747
- this.render({force: true, opaqueOnly: false});
1748
- imageDirty = true;
1749
1703
  };
1750
1704
 
1751
- /**
1752
- * When in snapshot mode, gets an image of the snapshot canvas.
1753
- *
1754
- * @private
1755
- * @returns {String} The image data URI.
1756
- */
1757
- this.readSnapshot = function (params) {
1758
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1759
- return snapshotBuffer.readImage(params);
1760
- };
1705
+ this.snapshot = (() => {
1706
+ let snapshotBound = false;
1707
+ const snapshotBuffer = new RenderBuffer(gl);
1708
+ let snapshotCanvas = null;
1709
+
1710
+ return {
1711
+ /**
1712
+ * Read pixels from the renderer's current output. Performs a force-render first.
1713
+ * @param pixels
1714
+ * @param colors
1715
+ * @param len
1716
+ * @private
1717
+ */
1718
+ readPixels: (pixels, colors, len) => {
1719
+ snapshotBuffer.bind();
1720
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1721
+ imageDirty = true;
1722
+ this.render();
1723
+ for (let i = 0; i < len; i++) {
1724
+ const j = i * 2;
1725
+ const k = i * 4;
1726
+ const color = snapshotBuffer.read(pixels[j], pixels[j + 1]);
1727
+ colors[k] = color[0];
1728
+ colors[k + 1] = color[1];
1729
+ colors[k + 2] = color[2];
1730
+ colors[k + 3] = color[3];
1731
+ }
1732
+ snapshotBuffer.unbind();
1733
+ imageDirty = true;
1734
+ },
1735
+
1736
+ /**
1737
+ * Enter snapshot mode.
1738
+ *
1739
+ * Switches rendering to a hidden snapshot canvas.
1740
+ *
1741
+ * Exit snapshot mode using endSnapshot().
1742
+ */
1743
+ beginSnapshot: () => {
1744
+ snapshotBuffer.setSize([gl.drawingBufferWidth, gl.drawingBufferHeight]);
1745
+ snapshotBuffer.bind();
1746
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1747
+ snapshotBound = true;
1748
+ },
1749
+
1750
+ /**
1751
+ * Returns an HTMLCanvas containing an image of the snapshot canvas.
1752
+ *
1753
+ * - The HTMLCanvas has a CanvasRenderingContext2D.
1754
+ * - Expects the caller to draw more things on the HTMLCanvas (annotations etc).
1755
+ *
1756
+ * @returns {HTMLCanvasElement}
1757
+ */
1758
+ renderSnapshotToCanvas: () => {
1759
+ const width = snapshotBuffer.buffer.width;
1760
+ const height = snapshotBuffer.buffer.height;
1761
+
1762
+ if ((! snapshotCanvas) || (snapshotCanvas.width !== width) || (snapshotCanvas.height !== height)) {
1763
+ snapshotCanvas = (function() {
1764
+ const canvas = document.createElement('canvas');
1765
+ const context = canvas.getContext('2d');
1766
+ canvas.width = width;
1767
+ canvas.height = height;
1768
+ const pixelData = new Uint8Array(width * height * 4);
1769
+ const imageData = context.createImageData(width, height);
1770
+
1771
+ return {
1772
+ width: width,
1773
+ height: height,
1774
+ getUpdatedCanvas: () => {
1775
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelData);
1776
+
1777
+ // flip verically
1778
+ const halfHeight = Math.floor(height / 2);
1779
+ const bytesPerRow = width * 4;
1780
+ const temp = new Uint8Array(bytesPerRow);
1781
+ for (let y = 0; y < halfHeight; ++y) {
1782
+ const topOffset = y * bytesPerRow;
1783
+ const bottomOffset = (height - y - 1) * bytesPerRow;
1784
+ temp.set(pixelData.subarray(topOffset, topOffset + bytesPerRow));
1785
+ pixelData.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
1786
+ pixelData.set(temp, bottomOffset);
1787
+ }
1788
+
1789
+ imageData.data.set(pixelData);
1790
+
1791
+ context.resetTransform(); // Prevents strange scale-accumulation effect with html2canvas
1792
+ context.putImageData(imageData, 0, 0);
1793
+
1794
+ return canvas;
1795
+ }
1796
+ };
1797
+ })();
1798
+ }
1761
1799
 
1762
- /**
1763
- * Returns an HTMLCanvas containing an image of the snapshot canvas.
1764
- *
1765
- * - The HTMLCanvas has a CanvasRenderingContext2D.
1766
- * - Expects the caller to draw more things on the HTMLCanvas (annotations etc).
1767
- *
1768
- * @returns {HTMLCanvasElement}
1769
- */
1770
- this.readSnapshotAsCanvas = function () {
1771
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1772
- return snapshotBuffer.readImageAsCanvas();
1773
- };
1800
+ if (snapshotBound) {
1801
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1802
+ imageDirty = true;
1803
+ this.render();
1804
+ imageDirty = true;
1805
+ }
1774
1806
 
1775
- /**
1776
- * Exists snapshot mode.
1777
- *
1778
- * Switches rendering back to the main canvas.
1779
- */
1780
- this.endSnapshot = function () {
1781
- if (!snapshotBound) {
1782
- return;
1783
- }
1784
- const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot");
1785
- snapshotBuffer.unbind();
1786
- snapshotBound = false;
1787
- };
1807
+ return snapshotCanvas.getUpdatedCanvas();
1808
+ },
1809
+
1810
+ /**
1811
+ * Exists snapshot mode.
1812
+ *
1813
+ * Switches rendering back to the main canvas.
1814
+ */
1815
+ endSnapshot: () => {
1816
+ if (snapshotBound) {
1817
+ snapshotBuffer.unbind();
1818
+ snapshotBound = false;
1819
+ }
1820
+ imageDirty = true;
1821
+ this.render();
1822
+ }
1823
+ };
1824
+ })();
1788
1825
 
1789
1826
  /**
1790
1827
  * Destroys this renderer.