diff --git a/src/viewer/scene/webgl/Renderer.js b/src/viewer/scene/webgl/Renderer.js index 5d1fc437e..c959d779a 100644 --- a/src/viewer/scene/webgl/Renderer.js +++ b/src/viewer/scene/webgl/Renderer.js @@ -267,16 +267,6 @@ const Renderer = function (scene, options) { } function sortDrawableList() { - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { - const drawableInfo = drawableTypeInfo[type]; - if (drawableInfo.isStateSortable) { - drawableInfo.drawableListPreCull.sort(drawableInfo.stateSortCompare); - - drawableInfo.drawableList = drawableInfo.drawableListPreCull; - } - } - } let lenDrawableList = 0; for (let type in drawableTypeInfo) { if (drawableTypeInfo.hasOwnProperty(type)) { @@ -379,745 +369,779 @@ const Renderer = function (scene, options) { if (params.clear !== false) { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); } + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { - - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; - - for (let i = 0, len = drawableList.length; i < len; i++) { - - const drawable = drawableList[i]; + const drawable = postCullDrawableList[i]; - if (drawable.culled === true || drawable.visible === false || !drawable.drawDepth || !drawable.saoEnabled) { - continue; - } + if (drawable.culled === true || drawable.visible === false || !drawable.drawDepth || !drawable.saoEnabled) { + continue; + } - if (drawable.renderFlags.colorOpaque) { - drawable.drawDepth(frameCtx); - } - } + if (drawable.renderFlags.colorOpaque) { + drawable.drawDepth(frameCtx); } } - // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 - // for (let ii = 0; ii < numVertexAttribs; ii++) { - // gl.disableVertexAttribArray(ii); - // } + // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 + // for (let ii = 0; ii < numVertexAttribs; ii++) { + // gl.disableVertexAttribArray(ii); + // } - } +} - function drawShadowMaps() { +function drawShadowMaps() { - let lights = scene._lightsState.lights; + let lights = scene._lightsState.lights; - for (let i = 0, len = lights.length; i < len; i++) { - const light = lights[i]; - if (!light.castsShadow) { - continue; - } - drawShadowMap(light); + for (let i = 0, len = lights.length; i < len; i++) { + const light = lights[i]; + if (!light.castsShadow) { + continue; } - - // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 - // for (let ii = 0; ii < numVertexAttribs; ii++) { - // gl.disableVertexAttribArray(ii); - // } - // - shadowsDirty = false; + drawShadowMap(light); } - function drawShadowMap(light) { + // const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 + // for (let ii = 0; ii < numVertexAttribs; ii++) { + // gl.disableVertexAttribArray(ii); + // } + // + shadowsDirty = false; +} - const castsShadow = light.castsShadow; +function drawShadowMap(light) { - if (!castsShadow) { - return; - } + const castsShadow = light.castsShadow; - const shadowRenderBuf = light.getShadowRenderBuf(); + if (!castsShadow) { + return; + } - if (!shadowRenderBuf) { - return; - } + const shadowRenderBuf = light.getShadowRenderBuf(); - shadowRenderBuf.bind(); + if (!shadowRenderBuf) { + return; + } - frameCtx.reset(); + shadowRenderBuf.bind(); - frameCtx.backfaces = true; - frameCtx.frontface = true; - frameCtx.shadowViewMatrix = light.getShadowViewMatrix(); - frameCtx.shadowProjMatrix = light.getShadowProjMatrix(); + frameCtx.reset(); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + frameCtx.backfaces = true; + frameCtx.frontface = true; + frameCtx.shadowViewMatrix = light.getShadowViewMatrix(); + frameCtx.shadowProjMatrix = light.getShadowProjMatrix(); - gl.clearColor(0, 0, 0, 1); - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.clearColor(0, 0, 0, 1); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); - for (let type in drawableTypeInfo) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - if (drawableTypeInfo.hasOwnProperty(type)) { + for (let type in drawableTypeInfo) { - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; + if (drawableTypeInfo.hasOwnProperty(type)) { - for (let i = 0, len = drawableList.length; i < len; i++) { + const drawableInfo = drawableTypeInfo[type]; + const drawableList = drawableInfo.drawableList; - const drawable = drawableList[i]; + for (let i = 0, len = drawableList.length; i < len; i++) { - if (drawable.visible === false || !drawable.castsShadow || !drawable.drawShadow) { - continue; - } + const drawable = drawableList[i]; - if (drawable.renderFlags.colorOpaque) { // Transparent objects don't cast shadows (yet) - drawable.drawShadow(frameCtx); - } + if (drawable.visible === false || !drawable.castsShadow || !drawable.drawShadow) { + continue; + } + + if (drawable.renderFlags.colorOpaque) { // Transparent objects don't cast shadows (yet) + drawable.drawShadow(frameCtx); } } } - - shadowRenderBuf.unbind(); } - function drawColor(params) { + shadowRenderBuf.unbind(); +} - const normalDrawSAOBin = []; - const normalEdgesOpaqueBin = []; - const normalFillTransparentBin = []; - const normalEdgesTransparentBin = []; +function drawColor(params) { - const xrayedFillOpaqueBin = []; - const xrayEdgesOpaqueBin = []; - const xrayedFillTransparentBin = []; - const xrayEdgesTransparentBin = []; + const normalDrawSAOBin = []; + const normalEdgesOpaqueBin = []; + const normalFillTransparentBin = []; + const normalEdgesTransparentBin = []; - const highlightedFillOpaqueBin = []; - const highlightedEdgesOpaqueBin = []; - const highlightedFillTransparentBin = []; - const highlightedEdgesTransparentBin = []; + const xrayedFillOpaqueBin = []; + const xrayEdgesOpaqueBin = []; + const xrayedFillTransparentBin = []; + const xrayEdgesTransparentBin = []; - const selectedFillOpaqueBin = []; - const selectedEdgesOpaqueBin = []; - const selectedFillTransparentBin = []; - const selectedEdgesTransparentBin = []; + const highlightedFillOpaqueBin = []; + const highlightedEdgesOpaqueBin = []; + const highlightedFillTransparentBin = []; + const highlightedEdgesTransparentBin = []; + const selectedFillOpaqueBin = []; + const selectedEdgesOpaqueBin = []; + const selectedFillTransparentBin = []; + const selectedEdgesTransparentBin = []; - const ambientColorAndIntensity = scene._lightsState.getAmbientColorAndIntensity(); - frameCtx.reset(); - frameCtx.pass = params.pass; - frameCtx.withSAO = false; - frameCtx.pbrEnabled = pbrEnabled && !!scene.pbrEnabled; - frameCtx.colorTextureEnabled = colorTextureEnabled && !!scene.colorTextureEnabled; + const ambientColorAndIntensity = scene._lightsState.getAmbientColorAndIntensity(); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + frameCtx.reset(); + frameCtx.pass = params.pass; + frameCtx.withSAO = false; + frameCtx.pbrEnabled = pbrEnabled && !!scene.pbrEnabled; + frameCtx.colorTextureEnabled = colorTextureEnabled && !!scene.colorTextureEnabled; - if (canvasTransparent) { - gl.clearColor(0, 0, 0, 0); - } else { - const backgroundColor = scene.canvas.backgroundColorFromAmbientLight ? ambientColorAndIntensity : scene.canvas.backgroundColor; - gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0); - } + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - gl.enable(gl.DEPTH_TEST); - gl.frontFace(gl.CCW); - gl.enable(gl.CULL_FACE); - gl.depthMask(true); - gl.lineWidth(1); + if (canvasTransparent) { + gl.clearColor(0, 0, 0, 0); + } else { + const backgroundColor = scene.canvas.backgroundColorFromAmbientLight ? ambientColorAndIntensity : scene.canvas.backgroundColor; + gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0); + } - frameCtx.lineWidth = 1; + gl.enable(gl.DEPTH_TEST); + gl.frontFace(gl.CCW); + gl.enable(gl.CULL_FACE); + gl.depthMask(true); + gl.lineWidth(1); - const saoPossible = scene.sao.possible; + frameCtx.lineWidth = 1; - if (saoEnabled && saoPossible) { - const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion"); - frameCtx.occlusionTexture = occlusionRenderBuffer1 ? occlusionRenderBuffer1.getTexture() : null; - } else { - frameCtx.occlusionTexture = null; + const saoPossible = scene.sao.possible; - } + if (saoEnabled && saoPossible) { + const occlusionRenderBuffer1 = renderBufferManager.getRenderBuffer("saoOcclusion"); + frameCtx.occlusionTexture = occlusionRenderBuffer1 ? occlusionRenderBuffer1.getTexture() : null; + } else { + frameCtx.occlusionTexture = null; - let i; - let len; - let drawable; + } - const startTime = Date.now(); + let i; + let len; + let drawable; - if (bindOutputFrameBuffer) { - bindOutputFrameBuffer(params.pass); - } + const startTime = Date.now(); - if (params.clear !== false) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } + if (bindOutputFrameBuffer) { + bindOutputFrameBuffer(params.pass); + } - let normalDrawSAOBinLen = 0; - let normalEdgesOpaqueBinLen = 0; - let normalFillTransparentBinLen = 0; - let normalEdgesTransparentBinLen = 0; + if (params.clear !== false) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } - let xrayedFillOpaqueBinLen = 0; - let xrayEdgesOpaqueBinLen = 0; - let xrayedFillTransparentBinLen = 0; - let xrayEdgesTransparentBinLen = 0; + let normalDrawSAOBinLen = 0; + let normalEdgesOpaqueBinLen = 0; + let normalFillTransparentBinLen = 0; + let normalEdgesTransparentBinLen = 0; - let highlightedFillOpaqueBinLen = 0; - let highlightedEdgesOpaqueBinLen = 0; - let highlightedFillTransparentBinLen = 0; - let highlightedEdgesTransparentBinLen = 0; + let xrayedFillOpaqueBinLen = 0; + let xrayEdgesOpaqueBinLen = 0; + let xrayedFillTransparentBinLen = 0; + let xrayEdgesTransparentBinLen = 0; - let selectedFillOpaqueBinLen = 0; - let selectedEdgesOpaqueBinLen = 0; - let selectedFillTransparentBinLen = 0; - let selectedEdgesTransparentBinLen = 0; + let highlightedFillOpaqueBinLen = 0; + let highlightedEdgesOpaqueBinLen = 0; + let highlightedFillTransparentBinLen = 0; + let highlightedEdgesTransparentBinLen = 0; - //------------------------------------------------------------------------------------------------------ - // Render normal opaque solids, defer others to bins to render after - //------------------------------------------------------------------------------------------------------ + let selectedFillOpaqueBinLen = 0; + let selectedEdgesOpaqueBinLen = 0; + let selectedFillTransparentBinLen = 0; + let selectedEdgesTransparentBinLen = 0; - for (let i = 0, len = postCullDrawableList.length; i < len; i++) { + //------------------------------------------------------------------------------------------------------ + // Render normal opaque solids, defer others to bins to render after + //------------------------------------------------------------------------------------------------------ - drawable = postCullDrawableList[i]; + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { - if (drawable.culled === true || drawable.visible === false) { - continue; - } + drawable = postCullDrawableList[i]; - const renderFlags = drawable.renderFlags; + if (drawable.culled === true || drawable.visible === false) { + continue; + } - if (renderFlags.colorOpaque) { - if (saoEnabled && saoPossible && drawable.saoEnabled) { - normalDrawSAOBin[normalDrawSAOBinLen++] = drawable; - } else { - drawable.drawColorOpaque(frameCtx); - } - } + const renderFlags = drawable.renderFlags; - if (transparentEnabled) { - if (renderFlags.colorTransparent) { - normalFillTransparentBin[normalFillTransparentBinLen++] = drawable; - } + if (renderFlags.colorOpaque) { + if (saoEnabled && saoPossible && drawable.saoEnabled) { + normalDrawSAOBin[normalDrawSAOBinLen++] = drawable; + } else { + drawable.drawColorOpaque(frameCtx); } + } - if (renderFlags.xrayedSilhouetteTransparent) { - xrayedFillTransparentBin[xrayedFillTransparentBinLen++] = drawable; + if (transparentEnabled) { + if (renderFlags.colorTransparent) { + normalFillTransparentBin[normalFillTransparentBinLen++] = drawable; } + } - if (renderFlags.xrayedSilhouetteOpaque) { - xrayedFillOpaqueBin[xrayedFillOpaqueBinLen++] = drawable; - } + if (renderFlags.xrayedSilhouetteTransparent) { + xrayedFillTransparentBin[xrayedFillTransparentBinLen++] = drawable; + } - if (renderFlags.highlightedSilhouetteTransparent) { - highlightedFillTransparentBin[highlightedFillTransparentBinLen++] = drawable; - } + if (renderFlags.xrayedSilhouetteOpaque) { + xrayedFillOpaqueBin[xrayedFillOpaqueBinLen++] = drawable; + } - if (renderFlags.highlightedSilhouetteOpaque) { - highlightedFillOpaqueBin[highlightedFillOpaqueBinLen++] = drawable; - } + if (renderFlags.highlightedSilhouetteTransparent) { + highlightedFillTransparentBin[highlightedFillTransparentBinLen++] = drawable; + } - if (renderFlags.selectedSilhouetteTransparent) { - selectedFillTransparentBin[selectedFillTransparentBinLen++] = drawable; - } + if (renderFlags.highlightedSilhouetteOpaque) { + highlightedFillOpaqueBin[highlightedFillOpaqueBinLen++] = drawable; + } - if (renderFlags.selectedSilhouetteOpaque) { - selectedFillOpaqueBin[selectedFillOpaqueBinLen++] = drawable; - } + if (renderFlags.selectedSilhouetteTransparent) { + selectedFillTransparentBin[selectedFillTransparentBinLen++] = drawable; + } - if (drawable.edges && edgesEnabled) { - if (renderFlags.edgesOpaque) { - normalEdgesOpaqueBin[normalEdgesOpaqueBinLen++] = drawable; - } + if (renderFlags.selectedSilhouetteOpaque) { + selectedFillOpaqueBin[selectedFillOpaqueBinLen++] = drawable; + } - if (renderFlags.edgesTransparent) { - normalEdgesTransparentBin[normalEdgesTransparentBinLen++] = drawable; - } + if (drawable.edges && edgesEnabled) { + if (renderFlags.edgesOpaque) { + normalEdgesOpaqueBin[normalEdgesOpaqueBinLen++] = drawable; + } - if (renderFlags.selectedEdgesTransparent) { - selectedEdgesTransparentBin[selectedEdgesTransparentBinLen++] = drawable; - } + if (renderFlags.edgesTransparent) { + normalEdgesTransparentBin[normalEdgesTransparentBinLen++] = drawable; + } - if (renderFlags.selectedEdgesOpaque) { - selectedEdgesOpaqueBin[selectedEdgesOpaqueBinLen++] = drawable; - } + if (renderFlags.selectedEdgesTransparent) { + selectedEdgesTransparentBin[selectedEdgesTransparentBinLen++] = drawable; + } - if (renderFlags.xrayedEdgesTransparent) { - xrayEdgesTransparentBin[xrayEdgesTransparentBinLen++] = drawable; - } + if (renderFlags.selectedEdgesOpaque) { + selectedEdgesOpaqueBin[selectedEdgesOpaqueBinLen++] = drawable; + } - if (renderFlags.xrayedEdgesOpaque) { - xrayEdgesOpaqueBin[xrayEdgesOpaqueBinLen++] = drawable; - } + if (renderFlags.xrayedEdgesTransparent) { + xrayEdgesTransparentBin[xrayEdgesTransparentBinLen++] = drawable; + } - if (renderFlags.highlightedEdgesTransparent) { - highlightedEdgesTransparentBin[highlightedEdgesTransparentBinLen++] = drawable; - } + if (renderFlags.xrayedEdgesOpaque) { + xrayEdgesOpaqueBin[xrayEdgesOpaqueBinLen++] = drawable; + } - if (renderFlags.highlightedEdgesOpaque) { - highlightedEdgesOpaqueBin[highlightedEdgesOpaqueBinLen++] = drawable; - } + if (renderFlags.highlightedEdgesTransparent) { + highlightedEdgesTransparentBin[highlightedEdgesTransparentBinLen++] = drawable; + } + + if (renderFlags.highlightedEdgesOpaque) { + highlightedEdgesOpaqueBin[highlightedEdgesOpaqueBinLen++] = drawable; } } + } - //------------------------------------------------------------------------------------------------------ - // Render deferred bins - //------------------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------------------ + // Render deferred bins + //------------------------------------------------------------------------------------------------------ - // Opaque color with SAO + // Opaque color with SAO - if (normalDrawSAOBinLen > 0) { - frameCtx.withSAO = true; - for (i = 0; i < normalDrawSAOBinLen; i++) { - normalDrawSAOBin[i].drawColorOpaque(frameCtx); - } + if (normalDrawSAOBinLen > 0) { + frameCtx.withSAO = true; + for (i = 0; i < normalDrawSAOBinLen; i++) { + normalDrawSAOBin[i].drawColorOpaque(frameCtx); } + } - // Opaque edges + // Opaque edges - if (normalEdgesOpaqueBinLen > 0) { - for (i = 0; i < normalEdgesOpaqueBinLen; i++) { - normalEdgesOpaqueBin[i].drawEdgesColorOpaque(frameCtx); - } + if (normalEdgesOpaqueBinLen > 0) { + for (i = 0; i < normalEdgesOpaqueBinLen; i++) { + normalEdgesOpaqueBin[i].drawEdgesColorOpaque(frameCtx); } + } - // Opaque X-ray fill + // Opaque X-ray fill - if (xrayedFillOpaqueBinLen > 0) { - for (i = 0; i < xrayedFillOpaqueBinLen; i++) { - xrayedFillOpaqueBin[i].drawSilhouetteXRayed(frameCtx); - } + if (xrayedFillOpaqueBinLen > 0) { + for (i = 0; i < xrayedFillOpaqueBinLen; i++) { + xrayedFillOpaqueBin[i].drawSilhouetteXRayed(frameCtx); } + } - // Opaque X-ray edges + // Opaque X-ray edges - if (xrayEdgesOpaqueBinLen > 0) { - for (i = 0; i < xrayEdgesOpaqueBinLen; i++) { - xrayEdgesOpaqueBin[i].drawEdgesXRayed(frameCtx); - } + if (xrayEdgesOpaqueBinLen > 0) { + for (i = 0; i < xrayEdgesOpaqueBinLen; i++) { + xrayEdgesOpaqueBin[i].drawEdgesXRayed(frameCtx); } + } - // Transparent + // Transparent - if (xrayedFillTransparentBinLen > 0 || xrayEdgesTransparentBinLen > 0 || normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) { - gl.enable(gl.CULL_FACE); - gl.enable(gl.BLEND); - if (canvasTransparent) { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } else { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - frameCtx.backfaces = false; - if (!alphaDepthMask) { - gl.depthMask(false); - } + if (xrayedFillTransparentBinLen > 0 || xrayEdgesTransparentBinLen > 0 || normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) { + gl.enable(gl.CULL_FACE); + gl.enable(gl.BLEND); + if (canvasTransparent) { + gl.blendEquation(gl.FUNC_ADD); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } else { + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + frameCtx.backfaces = false; + if (!alphaDepthMask) { + gl.depthMask(false); + } - // Transparent color edges + // Transparent color edges - if (normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - if (normalEdgesTransparentBinLen > 0) { - for (i = 0; i < normalEdgesTransparentBinLen; i++) { - drawable = normalEdgesTransparentBin[i]; - drawable.drawEdgesColorTransparent(frameCtx); - } + if (normalFillTransparentBinLen > 0 || normalEdgesTransparentBinLen > 0) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + if (normalEdgesTransparentBinLen > 0) { + for (i = 0; i < normalEdgesTransparentBinLen; i++) { + drawable = normalEdgesTransparentBin[i]; + drawable.drawEdgesColorTransparent(frameCtx); } + } - // Transparent color fill + // Transparent color fill - if (normalFillTransparentBinLen > 0) { - for (i = 0; i < normalFillTransparentBinLen; i++) { - drawable = normalFillTransparentBin[i]; - drawable.drawColorTransparent(frameCtx); - } + if (normalFillTransparentBinLen > 0) { + for (i = 0; i < normalFillTransparentBinLen; i++) { + drawable = normalFillTransparentBin[i]; + drawable.drawColorTransparent(frameCtx); } + } - // Transparent X-ray edges + // Transparent X-ray edges - if (xrayEdgesTransparentBinLen > 0) { - for (i = 0; i < xrayEdgesTransparentBinLen; i++) { - xrayEdgesTransparentBin[i].drawEdgesXRayed(frameCtx); - } + if (xrayEdgesTransparentBinLen > 0) { + for (i = 0; i < xrayEdgesTransparentBinLen; i++) { + xrayEdgesTransparentBin[i].drawEdgesXRayed(frameCtx); } + } - // Transparent X-ray fill + // Transparent X-ray fill - if (xrayedFillTransparentBinLen > 0) { - for (i = 0; i < xrayedFillTransparentBinLen; i++) { - xrayedFillTransparentBin[i].drawSilhouetteXRayed(frameCtx); - } + if (xrayedFillTransparentBinLen > 0) { + for (i = 0; i < xrayedFillTransparentBinLen; i++) { + xrayedFillTransparentBin[i].drawSilhouetteXRayed(frameCtx); } + } - gl.disable(gl.BLEND); - if (!alphaDepthMask) { - gl.depthMask(true); - } + gl.disable(gl.BLEND); + if (!alphaDepthMask) { + gl.depthMask(true); } + } - // Opaque highlight + // Opaque highlight - if (highlightedFillOpaqueBinLen > 0 || highlightedEdgesOpaqueBinLen > 0) { - frameCtx.lastProgramId = null; - if (scene.highlightMaterial.glowThrough) { - gl.clear(gl.DEPTH_BUFFER_BIT); - } + if (highlightedFillOpaqueBinLen > 0 || highlightedEdgesOpaqueBinLen > 0) { + frameCtx.lastProgramId = null; + if (scene.highlightMaterial.glowThrough) { + gl.clear(gl.DEPTH_BUFFER_BIT); + } - // Opaque highlighted edges + // Opaque highlighted edges - if (highlightedEdgesOpaqueBinLen > 0) { - for (i = 0; i < highlightedEdgesOpaqueBinLen; i++) { - highlightedEdgesOpaqueBin[i].drawEdgesHighlighted(frameCtx); - } + if (highlightedEdgesOpaqueBinLen > 0) { + for (i = 0; i < highlightedEdgesOpaqueBinLen; i++) { + highlightedEdgesOpaqueBin[i].drawEdgesHighlighted(frameCtx); } + } - // Opaque highlighted fill + // Opaque highlighted fill - if (highlightedFillOpaqueBinLen > 0) { - for (i = 0; i < highlightedFillOpaqueBinLen; i++) { - highlightedFillOpaqueBin[i].drawSilhouetteHighlighted(frameCtx); - } + if (highlightedFillOpaqueBinLen > 0) { + for (i = 0; i < highlightedFillOpaqueBinLen; i++) { + highlightedFillOpaqueBin[i].drawSilhouetteHighlighted(frameCtx); } } + } - // Highlighted transparent + // Highlighted transparent - if (highlightedFillTransparentBinLen > 0 || highlightedEdgesTransparentBinLen > 0 || highlightedFillOpaqueBinLen > 0) { - frameCtx.lastProgramId = null; - if (scene.selectedMaterial.glowThrough) { - gl.clear(gl.DEPTH_BUFFER_BIT); - } - gl.enable(gl.BLEND); - if (canvasTransparent) { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - gl.enable(gl.CULL_FACE); + if (highlightedFillTransparentBinLen > 0 || highlightedEdgesTransparentBinLen > 0 || highlightedFillOpaqueBinLen > 0) { + frameCtx.lastProgramId = null; + if (scene.selectedMaterial.glowThrough) { + gl.clear(gl.DEPTH_BUFFER_BIT); + } + gl.enable(gl.BLEND); + if (canvasTransparent) { + gl.blendEquation(gl.FUNC_ADD); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + gl.enable(gl.CULL_FACE); - // Highlighted transparent edges + // Highlighted transparent edges - if (highlightedEdgesTransparentBinLen > 0) { - for (i = 0; i < highlightedEdgesTransparentBinLen; i++) { - highlightedEdgesTransparentBin[i].drawEdgesHighlighted(frameCtx); - } + if (highlightedEdgesTransparentBinLen > 0) { + for (i = 0; i < highlightedEdgesTransparentBinLen; i++) { + highlightedEdgesTransparentBin[i].drawEdgesHighlighted(frameCtx); } + } - // Highlighted transparent fill + // Highlighted transparent fill - if (highlightedFillTransparentBinLen > 0) { - for (i = 0; i < highlightedFillTransparentBinLen; i++) { - highlightedFillTransparentBin[i].drawSilhouetteHighlighted(frameCtx); - } + if (highlightedFillTransparentBinLen > 0) { + for (i = 0; i < highlightedFillTransparentBinLen; i++) { + highlightedFillTransparentBin[i].drawSilhouetteHighlighted(frameCtx); } - gl.disable(gl.BLEND); } + gl.disable(gl.BLEND); + } - // Selected opaque + // Selected opaque - if (selectedFillOpaqueBinLen > 0 || selectedEdgesOpaqueBinLen > 0) { - frameCtx.lastProgramId = null; - if (scene.selectedMaterial.glowThrough) { - gl.clear(gl.DEPTH_BUFFER_BIT); - } + if (selectedFillOpaqueBinLen > 0 || selectedEdgesOpaqueBinLen > 0) { + frameCtx.lastProgramId = null; + if (scene.selectedMaterial.glowThrough) { + gl.clear(gl.DEPTH_BUFFER_BIT); + } - // Selected opaque fill + // Selected opaque fill - if (selectedEdgesOpaqueBinLen > 0) { - for (i = 0; i < selectedEdgesOpaqueBinLen; i++) { - selectedEdgesOpaqueBin[i].drawEdgesSelected(frameCtx); - } + if (selectedEdgesOpaqueBinLen > 0) { + for (i = 0; i < selectedEdgesOpaqueBinLen; i++) { + selectedEdgesOpaqueBin[i].drawEdgesSelected(frameCtx); } + } - // Selected opaque edges + // Selected opaque edges - if (selectedFillOpaqueBinLen > 0) { - for (i = 0; i < selectedFillOpaqueBinLen; i++) { - selectedFillOpaqueBin[i].drawSilhouetteSelected(frameCtx); - } + if (selectedFillOpaqueBinLen > 0) { + for (i = 0; i < selectedFillOpaqueBinLen; i++) { + selectedFillOpaqueBin[i].drawSilhouetteSelected(frameCtx); } } + } - // Selected transparent + // Selected transparent - if (selectedFillTransparentBinLen > 0 || selectedEdgesTransparentBinLen > 0) { - frameCtx.lastProgramId = null; - if (scene.selectedMaterial.glowThrough) { - gl.clear(gl.DEPTH_BUFFER_BIT); - } - gl.enable(gl.CULL_FACE); - gl.enable(gl.BLEND); - if (canvasTransparent) { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } + if (selectedFillTransparentBinLen > 0 || selectedEdgesTransparentBinLen > 0) { + frameCtx.lastProgramId = null; + if (scene.selectedMaterial.glowThrough) { + gl.clear(gl.DEPTH_BUFFER_BIT); + } + gl.enable(gl.CULL_FACE); + gl.enable(gl.BLEND); + if (canvasTransparent) { + gl.blendEquation(gl.FUNC_ADD); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } - // Selected transparent edges + // Selected transparent edges - if (selectedEdgesTransparentBinLen > 0) { - for (i = 0; i < selectedEdgesTransparentBinLen; i++) { - selectedEdgesTransparentBin[i].drawEdgesSelected(frameCtx); - } + if (selectedEdgesTransparentBinLen > 0) { + for (i = 0; i < selectedEdgesTransparentBinLen; i++) { + selectedEdgesTransparentBin[i].drawEdgesSelected(frameCtx); } + } - // Selected transparent fill + // Selected transparent fill - if (selectedFillTransparentBinLen > 0) { - for (i = 0; i < selectedFillTransparentBinLen; i++) { - selectedFillTransparentBin[i].drawSilhouetteSelected(frameCtx); - } + if (selectedFillTransparentBinLen > 0) { + for (i = 0; i < selectedFillTransparentBinLen; i++) { + selectedFillTransparentBin[i].drawSilhouetteSelected(frameCtx); } - gl.disable(gl.BLEND); } + gl.disable(gl.BLEND); + } - const endTime = Date.now(); - const frameStats = stats.frame; - - frameStats.renderTime = (endTime - startTime) / 1000.0; - frameStats.drawElements = frameCtx.drawElements; - frameStats.drawArrays = frameCtx.drawArrays; - frameStats.useProgram = frameCtx.useProgram; - frameStats.bindTexture = frameCtx.bindTexture; - frameStats.bindArray = frameCtx.bindArray; + const endTime = Date.now(); + const frameStats = stats.frame; - const numTextureUnits = WEBGL_INFO.MAX_TEXTURE_IMAGE_UNITS; - for (let ii = 0; ii < numTextureUnits; ii++) { - gl.activeTexture(gl.TEXTURE0 + ii); - } - gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); - gl.bindTexture(gl.TEXTURE_2D, null); + frameStats.renderTime = (endTime - startTime) / 1000.0; + frameStats.drawElements = frameCtx.drawElements; + frameStats.drawArrays = frameCtx.drawArrays; + frameStats.useProgram = frameCtx.useProgram; + frameStats.bindTexture = frameCtx.bindTexture; + frameStats.bindArray = frameCtx.bindArray; - const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 - for (let ii = 0; ii < numVertexAttribs; ii++) { - gl.disableVertexAttribArray(ii); - } + const numTextureUnits = WEBGL_INFO.MAX_TEXTURE_IMAGE_UNITS; + for (let ii = 0; ii < numTextureUnits; ii++) { + gl.activeTexture(gl.TEXTURE0 + ii); + } + gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); + gl.bindTexture(gl.TEXTURE_2D, null); - if (unbindOutputFrameBuffer) { - unbindOutputFrameBuffer(params.pass); - } + const numVertexAttribs = WEBGL_INFO.MAX_VERTEX_ATTRIBS; // Fixes https://github.com/xeokit/xeokit-sdk/issues/174 + for (let ii = 0; ii < numVertexAttribs; ii++) { + gl.disableVertexAttribArray(ii); } - /** - * Picks an Entity. - * @private - */ - this.pick = (function () { + if (unbindOutputFrameBuffer) { + unbindOutputFrameBuffer(params.pass); + } +} - const tempVec3a = math.vec3(); - const tempMat4a = math.mat4(); - const tempMat4b = math.mat4(); +/** + * Picks an Entity. + * @private + */ +this.pick = (function () { - const randomVec3 = math.vec3(); - const up = math.vec3([0, 1, 0]); - const _pickResult = new PickResult(); + const tempVec3a = math.vec3(); + const tempMat4a = math.mat4(); + const tempMat4b = math.mat4(); - const nearAndFar = math.vec2(); + const randomVec3 = math.vec3(); + const up = math.vec3([0, 1, 0]); + const _pickResult = new PickResult(); - const canvasPos = math.vec3(); + const nearAndFar = math.vec2(); - const worldRayOrigin = math.vec3(); - const worldRayDir = math.vec3(); - const worldSurfacePos = math.vec3(); - const worldSurfaceNormal = math.vec3(); + const canvasPos = math.vec3(); - return function (params, pickResult = _pickResult) { + const worldRayOrigin = math.vec3(); + const worldRayDir = math.vec3(); + const worldSurfacePos = math.vec3(); + const worldSurfaceNormal = math.vec3(); - pickResult.reset(); + return function (params, pickResult = _pickResult) { - updateDrawlist(); + pickResult.reset(); - let look; - let pickViewMatrix = null; - let pickProjMatrix = null; + updateDrawlist(); - pickResult.pickSurface = params.pickSurface; + let look; + let pickViewMatrix = null; + let pickProjMatrix = null; - if (params.canvasPos) { + pickResult.pickSurface = params.pickSurface; - canvasPos[0] = params.canvasPos[0]; - canvasPos[1] = params.canvasPos[1]; + if (params.canvasPos) { - pickViewMatrix = scene.camera.viewMatrix; - pickProjMatrix = scene.camera.projMatrix; + canvasPos[0] = params.canvasPos[0]; + canvasPos[1] = params.canvasPos[1]; - pickResult.canvasPos = params.canvasPos; + pickViewMatrix = scene.camera.viewMatrix; + pickProjMatrix = scene.camera.projMatrix; - } else { + pickResult.canvasPos = params.canvasPos; - // Picking with arbitrary World-space ray - // Align camera along ray and fire ray through center of canvas + } else { - if (params.matrix) { + // Picking with arbitrary World-space ray + // Align camera along ray and fire ray through center of canvas - pickViewMatrix = params.matrix; - pickProjMatrix = scene.camera.projMatrix; + if (params.matrix) { - } else { + pickViewMatrix = params.matrix; + pickProjMatrix = scene.camera.projMatrix; - worldRayOrigin.set(params.origin || [0, 0, 0]); - worldRayDir.set(params.direction || [0, 0, 1]); + } else { - look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a); + worldRayOrigin.set(params.origin || [0, 0, 0]); + worldRayDir.set(params.direction || [0, 0, 1]); - randomVec3[0] = Math.random(); - randomVec3[1] = Math.random(); - randomVec3[2] = Math.random(); + look = math.addVec3(worldRayOrigin, worldRayDir, tempVec3a); - math.normalizeVec3(randomVec3); - math.cross3Vec3(worldRayDir, randomVec3, up); + randomVec3[0] = Math.random(); + randomVec3[1] = Math.random(); + randomVec3[2] = Math.random(); - pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b); - // pickProjMatrix = scene.camera.projMatrix; - pickProjMatrix = scene.camera.ortho.matrix; + math.normalizeVec3(randomVec3); + math.cross3Vec3(worldRayDir, randomVec3, up); - pickResult.origin = worldRayOrigin; - pickResult.direction = worldRayDir; - } + pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b); + // pickProjMatrix = scene.camera.projMatrix; + pickProjMatrix = scene.camera.ortho.matrix; - canvasPos[0] = canvas.clientWidth * 0.5; - canvasPos[1] = canvas.clientHeight * 0.5; + pickResult.origin = worldRayOrigin; + pickResult.direction = worldRayDir; } - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { - const drawableList = drawableTypeInfo[type].drawableList; - for (let i = 0, len = drawableList.length; i < len; i++) { - const drawable = drawableList[i]; - if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture - drawable.setPickMatrices(pickViewMatrix, pickProjMatrix); - } + canvasPos[0] = canvas.clientWidth * 0.5; + canvasPos[1] = canvas.clientHeight * 0.5; + } + + for (let type in drawableTypeInfo) { + if (drawableTypeInfo.hasOwnProperty(type)) { + const drawableList = drawableTypeInfo[type].drawableList; + for (let i = 0, len = drawableList.length; i < len; i++) { + const drawable = drawableList[i]; + if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture + drawable.setPickMatrices(pickViewMatrix, pickProjMatrix); } } } + } - const pickBuffer = renderBufferManager.getRenderBuffer("pick", {size: [1, 1]}); + const pickBuffer = renderBufferManager.getRenderBuffer("pick", {size: [1, 1]}); - pickBuffer.bind(); + pickBuffer.bind(); - const pickable = gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult); + const pickable = gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult); - if (!pickable) { - pickBuffer.unbind(); - return null; - } + if (!pickable) { + pickBuffer.unbind(); + return null; + } - const pickedEntity = (pickable.delegatePickedEntity) ? pickable.delegatePickedEntity() : pickable; + const pickedEntity = (pickable.delegatePickedEntity) ? pickable.delegatePickedEntity() : pickable; - if (!pickedEntity) { - pickBuffer.unbind(); - return null; - } - - if (params.pickSurface) { + if (!pickedEntity) { + pickBuffer.unbind(); + return null; + } - // GPU-based ray-picking + if (params.pickSurface) { - if (pickable.canPickTriangle && pickable.canPickTriangle()) { + // GPU-based ray-picking - gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult); + if (pickable.canPickTriangle && pickable.canPickTriangle()) { - pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, pickResult); + gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult); - pickResult.pickSurfacePrecision = false; + pickable.pickTriangleSurface(pickViewMatrix, pickProjMatrix, pickResult); - } else { + pickResult.pickSurfacePrecision = false; - if (pickable.canPickWorldPos && pickable.canPickWorldPos()) { + } else { - nearAndFar[0] = scene.camera.project.near; - nearAndFar[1] = scene.camera.project.far; + if (pickable.canPickWorldPos && pickable.canPickWorldPos()) { - gpuPickWorldPos(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult); + nearAndFar[0] = scene.camera.project.near; + nearAndFar[1] = scene.camera.project.far; - if (params.pickSurfaceNormal !== false) { - gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult); - } + gpuPickWorldPos(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult); - pickResult.pickSurfacePrecision = false; + if (params.pickSurfaceNormal !== false) { + gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult); } + + pickResult.pickSurfacePrecision = false; } } - pickBuffer.unbind(); - pickResult.entity = pickedEntity; - return pickResult; - }; - })(); + } + pickBuffer.unbind(); + pickResult.entity = pickedEntity; + return pickResult; + }; +})(); - function gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult) { +function gpuPickPickable(pickBuffer, canvasPos, pickViewMatrix, pickProjMatrix, params, pickResult) { - const resolutionScale = scene.canvas.resolutionScale; + const resolutionScale = scene.canvas.resolutionScale; - frameCtx.reset(); - frameCtx.backfaces = true; - frameCtx.frontface = true; // "ccw" - frameCtx.pickOrigin = pickResult.origin; - frameCtx.pickViewMatrix = pickViewMatrix; - frameCtx.pickProjMatrix = pickProjMatrix; - frameCtx.pickInvisible = !!params.pickInvisible; - frameCtx.pickClipPos = [ - getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), - getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), - ]; + frameCtx.reset(); + frameCtx.backfaces = true; + frameCtx.frontface = true; // "ccw" + frameCtx.pickOrigin = pickResult.origin; + frameCtx.pickViewMatrix = pickViewMatrix; + frameCtx.pickProjMatrix = pickProjMatrix; + frameCtx.pickInvisible = !!params.pickInvisible; + frameCtx.pickClipPos = [ + getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), + getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), + ]; - gl.viewport(0, 0, 1, 1); - gl.depthMask(true); - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.viewport(0, 0, 1, 1); + gl.depthMask(true); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - const includeEntityIds = params.includeEntityIds; - const excludeEntityIds = params.excludeEntityIds; + const includeEntityIds = params.includeEntityIds; + const excludeEntityIds = params.excludeEntityIds; - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; + const drawable = postCullDrawableList[i]; - for (let i = 0, len = drawableList.length; i < len; i++) { + if (drawable.culled === true || drawable.visible === false) { + continue; + } - const drawable = drawableList[i]; + if (!drawable.drawPickMesh || (params.pickInvisible !== true && drawable.visible === false) || drawable.pickable === false) { + continue; + } + if (includeEntityIds && !includeEntityIds[drawable.id]) { // TODO: push this logic into drawable + continue; + } + if (excludeEntityIds && excludeEntityIds[drawable.id]) { + continue; + } - if (!drawable.drawPickMesh || (params.pickInvisible !== true && drawable.visible === false) || drawable.pickable === false) { - continue; - } - if (includeEntityIds && !includeEntityIds[drawable.id]) { // TODO: push this logic into drawable - continue; - } - if (excludeEntityIds && excludeEntityIds[drawable.id]) { - continue; - } + drawable.drawPickMesh(frameCtx); + } - drawable.drawPickMesh(frameCtx); - } - } - } - const pix = pickBuffer.read(0, 0); - const pickID = pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24); + const pix = pickBuffer.read(0, 0); + const pickID = pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24); - if (pickID < 0) { - return; - } + if (pickID < 0) { + return; + } + + const pickable = pickIDs.items[pickID]; - const pickable = pickIDs.items[pickID]; + return pickable; +} - return pickable; +function gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) { + + if (!pickable.drawPickTriangles) { + return; } - function gpuPickTriangle(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) { + const resolutionScale = scene.canvas.resolutionScale; - if (!pickable.drawPickTriangles) { - return; - } + frameCtx.reset(); + frameCtx.backfaces = true; + frameCtx.frontface = true; // "ccw" + frameCtx.pickOrigin = pickResult.origin; + frameCtx.pickViewMatrix = pickViewMatrix; // Can be null + frameCtx.pickProjMatrix = pickProjMatrix; // Can be null + // frameCtx.pickInvisible = !!params.pickInvisible; + frameCtx.pickClipPos = [ + getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), + getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), + ]; + + gl.viewport(0, 0, 1, 1); + + gl.clearColor(0, 0, 0, 0); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + pickable.drawPickTriangles(frameCtx); + + const pix = pickBuffer.read(0, 0); + + let primIndex = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256); + + primIndex *= 3; // Convert from triangle number to first vertex in indices + + pickResult.primIndex = primIndex; +} + +const gpuPickWorldPos = (function () { + + const tempVec4a = math.vec4(); + const tempVec4b = math.vec4(); + const tempVec4c = math.vec4(); + const tempVec4d = math.vec4(); + const tempVec4e = math.vec4(); + const tempMat4a = math.mat4(); + const tempMat4b = math.mat4(); + const tempMat4c = math.mat4(); + + return function (pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult) { const resolutionScale = scene.canvas.resolutionScale; @@ -1125,9 +1149,12 @@ const Renderer = function (scene, options) { frameCtx.backfaces = true; frameCtx.frontface = true; // "ccw" frameCtx.pickOrigin = pickResult.origin; - frameCtx.pickViewMatrix = pickViewMatrix; // Can be null - frameCtx.pickProjMatrix = pickProjMatrix; // Can be null - // frameCtx.pickInvisible = !!params.pickInvisible; + frameCtx.pickViewMatrix = pickViewMatrix; + frameCtx.pickProjMatrix = pickProjMatrix; + frameCtx.pickZNear = nearAndFar[0]; + frameCtx.pickZFar = nearAndFar[1]; + frameCtx.pickElementsCount = pickable.pickElementsCount; + frameCtx.pickElementsOffset = pickable.pickElementsOffset; frameCtx.pickClipPos = [ getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), @@ -1136,651 +1163,599 @@ const Renderer = function (scene, options) { gl.viewport(0, 0, 1, 1); gl.clearColor(0, 0, 0, 0); + gl.depthMask(true); gl.enable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); gl.disable(gl.BLEND); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - pickable.drawPickTriangles(frameCtx); + pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths const pix = pickBuffer.read(0, 0); - let primIndex = pix[0] + (pix[1] * 256) + (pix[2] * 256 * 256) + (pix[3] * 256 * 256 * 256); - - primIndex *= 3; // Convert from triangle number to first vertex in indices - - pickResult.primIndex = primIndex; - } - - const gpuPickWorldPos = (function () { - - const tempVec4a = math.vec4(); - const tempVec4b = math.vec4(); - const tempVec4c = math.vec4(); - const tempVec4d = math.vec4(); - const tempVec4e = math.vec4(); - const tempMat4a = math.mat4(); - const tempMat4b = math.mat4(); - const tempMat4c = math.mat4(); - - return function (pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, nearAndFar, pickResult) { - - const resolutionScale = scene.canvas.resolutionScale; - - frameCtx.reset(); - frameCtx.backfaces = true; - frameCtx.frontface = true; // "ccw" - frameCtx.pickOrigin = pickResult.origin; - frameCtx.pickViewMatrix = pickViewMatrix; - frameCtx.pickProjMatrix = pickProjMatrix; - frameCtx.pickZNear = nearAndFar[0]; - frameCtx.pickZFar = nearAndFar[1]; - frameCtx.pickElementsCount = pickable.pickElementsCount; - frameCtx.pickElementsOffset = pickable.pickElementsOffset; - frameCtx.pickClipPos = [ - getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), - getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), - ]; - - gl.viewport(0, 0, 1, 1); + const screenZ = unpackDepth(pix); // Get screen-space Z at the given canvas coords - gl.clearColor(0, 0, 0, 0); - gl.depthMask(true); - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top + const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2); + const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2); - pickable.drawPickDepths(frameCtx); // Draw color-encoded fragment screen-space depths + const origin = pickable.origin; + let pvMat; - const pix = pickBuffer.read(0, 0); + if (origin) { + const rtcPickViewMat = createRTCViewMat(pickViewMatrix, origin, tempMat4a); + pvMat = math.mulMat4(pickProjMatrix, rtcPickViewMat, tempMat4b); - const screenZ = unpackDepth(pix); // Get screen-space Z at the given canvas coords - - // Calculate clip space coordinates, which will be in range of x=[-1..1] and y=[-1..1], with y=(+1) at top - const x = (canvasPos[0] - canvas.clientWidth / 2) / (canvas.clientWidth / 2); - const y = -(canvasPos[1] - canvas.clientHeight / 2) / (canvas.clientHeight / 2); + } else { + pvMat = math.mulMat4(pickProjMatrix, pickViewMatrix, tempMat4b); + } - const origin = pickable.origin; - let pvMat; + const pvMatInverse = math.inverseMat4(pvMat, tempMat4c); - if (origin) { - const rtcPickViewMat = createRTCViewMat(pickViewMatrix, origin, tempMat4a); - pvMat = math.mulMat4(pickProjMatrix, rtcPickViewMat, tempMat4b); + tempVec4a[0] = x; + tempVec4a[1] = y; + tempVec4a[2] = -1; + tempVec4a[3] = 1; - } else { - pvMat = math.mulMat4(pickProjMatrix, pickViewMatrix, tempMat4b); - } + let world1 = math.transformVec4(pvMatInverse, tempVec4a); + world1 = math.mulVec4Scalar(world1, 1 / world1[3]); - const pvMatInverse = math.inverseMat4(pvMat, tempMat4c); + tempVec4b[0] = x; + tempVec4b[1] = y; + tempVec4b[2] = 1; + tempVec4b[3] = 1; - tempVec4a[0] = x; - tempVec4a[1] = y; - tempVec4a[2] = -1; - tempVec4a[3] = 1; + let world2 = math.transformVec4(pvMatInverse, tempVec4b); + world2 = math.mulVec4Scalar(world2, 1 / world2[3]); - let world1 = math.transformVec4(pvMatInverse, tempVec4a); - world1 = math.mulVec4Scalar(world1, 1 / world1[3]); + const dir = math.subVec3(world2, world1, tempVec4c); + const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e); - tempVec4b[0] = x; - tempVec4b[1] = y; - tempVec4b[2] = 1; - tempVec4b[3] = 1; + if (origin) { + math.addVec3(worldPos, origin); + } - let world2 = math.transformVec4(pvMatInverse, tempVec4b); - world2 = math.mulVec4Scalar(world2, 1 / world2[3]); + pickResult.worldPos = worldPos; + } +})(); - const dir = math.subVec3(world2, world1, tempVec4c); - const worldPos = math.addVec3(world1, math.mulVec4Scalar(dir, screenZ, tempVec4d), tempVec4e); +function drawSnapInit(frameCtx) { + frameCtx.snapPickLayerParams = []; + frameCtx.snapPickLayerNumber = 0; - if (origin) { - math.addVec3(worldPos, origin); - } + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { - pickResult.worldPos = worldPos; - } - })(); + const drawable = postCullDrawableList[i]; - function drawSnapInit(frameCtx) { - frameCtx.snapPickLayerParams = []; - frameCtx.snapPickLayerNumber = 0; - for (let type in drawableTypeInfo) { - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; - for (let i = 0, len = drawableList.length; i < len; i++) { - const drawable = drawableList[i]; - if (drawable.drawSnapInit) { - if (!drawable.culled && drawable.visible && drawable.pickable) { - drawable.drawSnapInit(frameCtx); - } - } + if (drawable.drawSnapInit) { + if (!drawable.culled && drawable.visible && drawable.pickable) { + drawable.drawSnapInit(frameCtx); } } - return frameCtx.snapPickLayerParams; } + return frameCtx.snapPickLayerParams; +} - function drawSnap(frameCtx) { - frameCtx.snapPickLayerParams = frameCtx.snapPickLayerParams || []; - frameCtx.snapPickLayerNumber = frameCtx.snapPickLayerParams.length; - for (let type in drawableTypeInfo) { - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; - for (let i = 0, len = drawableList.length; i < len; i++) { - const drawable = drawableList[i]; - if (drawable.drawSnap) { - if (!drawable.culled && drawable.visible && drawable.pickable) { - drawable.drawSnap(frameCtx); - } +function drawSnap(frameCtx) { + frameCtx.snapPickLayerParams = frameCtx.snapPickLayerParams || []; + frameCtx.snapPickLayerNumber = frameCtx.snapPickLayerParams.length; + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { + + const drawable = postCullDrawableList[i]; + + if (drawable.drawSnapInit) { + if (drawable.drawSnap) { + if (!drawable.culled && drawable.visible && drawable.pickable) { + drawable.drawSnap(frameCtx); } } } - return frameCtx.snapPickLayerParams; } + return frameCtx.snapPickLayerParams; +} - function getClipPosX(pos, size) { - return 2 * (pos / size) - 1; - } +function getClipPosX(pos, size) { + return 2 * (pos / size) - 1; +} - function getClipPosY(pos, size) { - return 1 - 2 * (pos / size); - } +function getClipPosY(pos, size) { + return 1 - 2 * (pos / size); +} - /** - * @param {[number, number]} canvasPos - * @param {number} [snapRadiusInPixels=30] - * @param {boolean} [snapToVertex=true] - * @param {boolean} [snapToEdge=true] - * @param pickResult - * @returns {PickResult} - */ - this.snapPick = (function () { +/** + * @param {[number, number]} canvasPos + * @param {number} [snapRadiusInPixels=30] + * @param {boolean} [snapToVertex=true] + * @param {boolean} [snapToEdge=true] + * @param pickResult + * @returns {PickResult} + */ +this.snapPick = (function () { - const _pickResult = new PickResult(); + const _pickResult = new PickResult(); - return function (canvasPos, snapRadiusInPixels, snapToVertex, snapToEdge, pickResult = _pickResult) { + return function (canvasPos, snapRadiusInPixels, snapToVertex, snapToEdge, pickResult = _pickResult) { - if (!snapToVertex && !snapToEdge) { - return this.pick({canvasPos, pickSurface: true}); - } + if (!snapToVertex && !snapToEdge) { + return this.pick({canvasPos, pickSurface: true}); + } - const resolutionScale = scene.canvas.resolutionScale; + const resolutionScale = scene.canvas.resolutionScale; - frameCtx.reset(); - frameCtx.backfaces = true; - frameCtx.frontface = true; // "ccw" - frameCtx.pickZNear = scene.camera.project.near; - frameCtx.pickZFar = scene.camera.project.far; + frameCtx.reset(); + frameCtx.backfaces = true; + frameCtx.frontface = true; // "ccw" + frameCtx.pickZNear = scene.camera.project.near; + frameCtx.pickZFar = scene.camera.project.far; - snapRadiusInPixels = snapRadiusInPixels || 30; + snapRadiusInPixels = snapRadiusInPixels || 30; - const vertexPickBuffer = renderBufferManager.getRenderBuffer("uniquePickColors-aabs", { - depthTexture: true, - size: [ - 2 * snapRadiusInPixels + 1, - 2 * snapRadiusInPixels + 1, - ] - }); + const vertexPickBuffer = renderBufferManager.getRenderBuffer("uniquePickColors-aabs", { + depthTexture: true, + size: [ + 2 * snapRadiusInPixels + 1, + 2 * snapRadiusInPixels + 1, + ] + }); - frameCtx.snapVectorA = [ - getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), - getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), - ]; + frameCtx.snapVectorA = [ + getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), + getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), + ]; - frameCtx.snapInvVectorAB = [ - gl.drawingBufferWidth / (2 * snapRadiusInPixels), - gl.drawingBufferHeight / (2 * snapRadiusInPixels), - ]; + frameCtx.snapInvVectorAB = [ + gl.drawingBufferWidth / (2 * snapRadiusInPixels), + gl.drawingBufferHeight / (2 * snapRadiusInPixels), + ]; - // Bind and clear the snap render target + // Bind and clear the snap render target - vertexPickBuffer.bind(gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI); - gl.viewport(0, 0, vertexPickBuffer.size[0], vertexPickBuffer.size[1]); - gl.enable(gl.DEPTH_TEST); - gl.frontFace(gl.CCW); - gl.disable(gl.CULL_FACE); - gl.depthMask(true); - gl.disable(gl.BLEND); - gl.depthFunc(gl.LEQUAL); - gl.clear(gl.DEPTH_BUFFER_BIT); - gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0])); - gl.clearBufferiv(gl.COLOR, 1, new Int32Array([0, 0, 0, 0])); - gl.clearBufferuiv(gl.COLOR, 2, new Uint32Array([0, 0, 0, 0])); - - ////////////////////////////////// - // Set view and proj mats for VBO renderers - /////////////////////////////////////// - - const pickViewMatrix = scene.camera.viewMatrix; - const pickProjMatrix = scene.camera.projMatrix; - - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { - const drawableList = drawableTypeInfo[type].drawableList; - for (let i = 0, len = drawableList.length; i < len; i++) { - const drawable = drawableList[i]; - if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture - drawable.setPickMatrices(pickViewMatrix, pickProjMatrix); - } + vertexPickBuffer.bind(gl.RGBA32I, gl.RGBA32I, gl.RGBA8UI); + gl.viewport(0, 0, vertexPickBuffer.size[0], vertexPickBuffer.size[1]); + gl.enable(gl.DEPTH_TEST); + gl.frontFace(gl.CCW); + gl.disable(gl.CULL_FACE); + gl.depthMask(true); + gl.disable(gl.BLEND); + gl.depthFunc(gl.LEQUAL); + gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0])); + gl.clearBufferiv(gl.COLOR, 1, new Int32Array([0, 0, 0, 0])); + gl.clearBufferuiv(gl.COLOR, 2, new Uint32Array([0, 0, 0, 0])); + + ////////////////////////////////// + // Set view and proj mats for VBO renderers + /////////////////////////////////////// + + const pickViewMatrix = scene.camera.viewMatrix; + const pickProjMatrix = scene.camera.projMatrix; + + for (let type in drawableTypeInfo) { + if (drawableTypeInfo.hasOwnProperty(type)) { + const drawableList = drawableTypeInfo[type].drawableList; + for (let i = 0, len = drawableList.length; i < len; i++) { + const drawable = drawableList[i]; + if (drawable.setPickMatrices) { // Eg. SceneModel, which needs pre-loading into texture + drawable.setPickMatrices(pickViewMatrix, pickProjMatrix); } } } + } - // a) init z-buffer - gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]); - const layerParamsSurface = drawSnapInit(frameCtx); + // a) init z-buffer + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]); + const layerParamsSurface = drawSnapInit(frameCtx); - // b) snap-pick - const layerParamsSnap = [] - frameCtx.snapPickLayerParams = layerParamsSnap; + // b) snap-pick + const layerParamsSnap = [] + frameCtx.snapPickLayerParams = layerParamsSnap; - gl.depthMask(false); - gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.depthMask(false); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); - if (snapToVertex && snapToEdge) { - frameCtx.snapMode = "edge"; - drawSnap(frameCtx); + if (snapToVertex && snapToEdge) { + frameCtx.snapMode = "edge"; + drawSnap(frameCtx); - frameCtx.snapMode = "vertex"; - frameCtx.snapPickLayerNumber++; + frameCtx.snapMode = "vertex"; + frameCtx.snapPickLayerNumber++; - drawSnap(frameCtx); - } else { - frameCtx.snapMode = snapToVertex ? "vertex" : "edge"; + drawSnap(frameCtx); + } else { + frameCtx.snapMode = snapToVertex ? "vertex" : "edge"; - drawSnap(frameCtx); - } + drawSnap(frameCtx); + } - gl.depthMask(true); + gl.depthMask(true); - // Read and decode the snapped coordinates + // Read and decode the snapped coordinates - const snapPickResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4); - const snapPickNormalResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4, 1); - const snapPickIdResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.UNSIGNED_INT, Uint32Array, 4, 2); + const snapPickResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4); + const snapPickNormalResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.INT, Int32Array, 4, 1); + const snapPickIdResultArray = vertexPickBuffer.readArray(gl.RGBA_INTEGER, gl.UNSIGNED_INT, Uint32Array, 4, 2); - vertexPickBuffer.unbind(); + vertexPickBuffer.unbind(); - // result 1) regular hi-precision world position + // result 1) regular hi-precision world position - let worldPos = null; - let worldNormal = null; - let pickable = null; + let worldPos = null; + let worldNormal = null; + let pickable = null; - const middleX = snapRadiusInPixels; - const middleY = snapRadiusInPixels; - const middleIndex = (middleX * 4) + (middleY * vertexPickBuffer.size[0] * 4); - const pickResultMiddleXY = snapPickResultArray.slice(middleIndex, middleIndex + 4); - const pickNormalResultMiddleXY = snapPickNormalResultArray.slice(middleIndex, middleIndex + 4); - const pickPickableResultMiddleXY = snapPickIdResultArray.slice(middleIndex, middleIndex + 4); + const middleX = snapRadiusInPixels; + const middleY = snapRadiusInPixels; + const middleIndex = (middleX * 4) + (middleY * vertexPickBuffer.size[0] * 4); + const pickResultMiddleXY = snapPickResultArray.slice(middleIndex, middleIndex + 4); + const pickNormalResultMiddleXY = snapPickNormalResultArray.slice(middleIndex, middleIndex + 4); + const pickPickableResultMiddleXY = snapPickIdResultArray.slice(middleIndex, middleIndex + 4); - if (pickResultMiddleXY[3] !== 0) { - const pickedLayerParmasSurface = layerParamsSurface[Math.abs(pickResultMiddleXY[3]) % layerParamsSurface.length]; - const origin = pickedLayerParmasSurface.origin; - const scale = pickedLayerParmasSurface.coordinateScale; - worldPos = [ - pickResultMiddleXY[0] * scale[0] + origin[0], - pickResultMiddleXY[1] * scale[1] + origin[1], - pickResultMiddleXY[2] * scale[2] + origin[2], - ]; - worldNormal = math.normalizeVec3([ - pickNormalResultMiddleXY[0] / math.MAX_INT, - pickNormalResultMiddleXY[1] / math.MAX_INT, - pickNormalResultMiddleXY[2] / math.MAX_INT, - ]); - - const pickID = - pickPickableResultMiddleXY[0] - + (pickPickableResultMiddleXY[1] << 8) - + (pickPickableResultMiddleXY[2] << 16) - + (pickPickableResultMiddleXY[3] << 24); - - pickable = pickIDs.items[pickID]; + if (pickResultMiddleXY[3] !== 0) { + const pickedLayerParmasSurface = layerParamsSurface[Math.abs(pickResultMiddleXY[3]) % layerParamsSurface.length]; + const origin = pickedLayerParmasSurface.origin; + const scale = pickedLayerParmasSurface.coordinateScale; + worldPos = [ + pickResultMiddleXY[0] * scale[0] + origin[0], + pickResultMiddleXY[1] * scale[1] + origin[1], + pickResultMiddleXY[2] * scale[2] + origin[2], + ]; + worldNormal = math.normalizeVec3([ + pickNormalResultMiddleXY[0] / math.MAX_INT, + pickNormalResultMiddleXY[1] / math.MAX_INT, + pickNormalResultMiddleXY[2] / math.MAX_INT, + ]); + + const pickID = + pickPickableResultMiddleXY[0] + + (pickPickableResultMiddleXY[1] << 8) + + (pickPickableResultMiddleXY[2] << 16) + + (pickPickableResultMiddleXY[3] << 24); + + pickable = pickIDs.items[pickID]; + } + + // result 2) hi-precision snapped (to vertex/edge) world position + + let snapPickResult = []; + + for (let i = 0; i < snapPickResultArray.length; i += 4) { + if (snapPickResultArray[i + 3] > 0) { + const pixelNumber = Math.floor(i / 4); + const w = vertexPickBuffer.size[0]; + const x = pixelNumber % w - Math.floor(w / 2); + const y = Math.floor(pixelNumber / w) - Math.floor(w / 2); + const dist = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); + snapPickResult.push({ + x, + y, + dist, + isVertex: snapToVertex && snapToEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapToVertex, + result: [ + snapPickResultArray[i + 0], + snapPickResultArray[i + 1], + snapPickResultArray[i + 2], + snapPickResultArray[i + 3], + ], + normal: [ + snapPickNormalResultArray[i + 0], + snapPickNormalResultArray[i + 1], + snapPickNormalResultArray[i + 2], + snapPickNormalResultArray[i + 3], + ], + id: [ + snapPickIdResultArray[i + 0], + snapPickIdResultArray[i + 1], + snapPickIdResultArray[i + 2], + snapPickIdResultArray[i + 3], + ] + }); } + } + + let snappedWorldPos = null; + let snappedWorldNormal = null; + let snappedPickable = null; + let snapType = null; - // result 2) hi-precision snapped (to vertex/edge) world position - - let snapPickResult = []; - - for (let i = 0; i < snapPickResultArray.length; i += 4) { - if (snapPickResultArray[i + 3] > 0) { - const pixelNumber = Math.floor(i / 4); - const w = vertexPickBuffer.size[0]; - const x = pixelNumber % w - Math.floor(w / 2); - const y = Math.floor(pixelNumber / w) - Math.floor(w / 2); - const dist = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); - snapPickResult.push({ - x, - y, - dist, - isVertex: snapToVertex && snapToEdge ? snapPickResultArray[i + 3] > layerParamsSnap.length / 2 : snapToVertex, - result: [ - snapPickResultArray[i + 0], - snapPickResultArray[i + 1], - snapPickResultArray[i + 2], - snapPickResultArray[i + 3], - ], - normal: [ - snapPickNormalResultArray[i + 0], - snapPickNormalResultArray[i + 1], - snapPickNormalResultArray[i + 2], - snapPickNormalResultArray[i + 3], - ], - id: [ - snapPickIdResultArray[i + 0], - snapPickIdResultArray[i + 1], - snapPickIdResultArray[i + 2], - snapPickIdResultArray[i + 3], - ] - }); + if (snapPickResult.length > 0) { + // vertex snap first, then edge snap + snapPickResult.sort((a, b) => { + if (a.isVertex !== b.isVertex) { + return a.isVertex ? -1 : 1; + } else { + return a.dist - b.dist; } - } + }); - let snappedWorldPos = null; - let snappedWorldNormal = null; - let snappedPickable = null; - let snapType = null; - - if (snapPickResult.length > 0) { - // vertex snap first, then edge snap - snapPickResult.sort((a, b) => { - if (a.isVertex !== b.isVertex) { - return a.isVertex ? -1 : 1; - } else { - return a.dist - b.dist; - } - }); + snapType = snapPickResult[0].isVertex ? "vertex" : "edge"; + const snapPick = snapPickResult[0].result; + const snapPickNormal = snapPickResult[0].normal; + const snapPickId = snapPickResult[0].id; - snapType = snapPickResult[0].isVertex ? "vertex" : "edge"; - const snapPick = snapPickResult[0].result; - const snapPickNormal = snapPickResult[0].normal; - const snapPickId = snapPickResult[0].id; + const pickedLayerParmas = layerParamsSnap[snapPick[3]]; - const pickedLayerParmas = layerParamsSnap[snapPick[3]]; + const origin = pickedLayerParmas.origin; + const scale = pickedLayerParmas.coordinateScale; - const origin = pickedLayerParmas.origin; - const scale = pickedLayerParmas.coordinateScale; + snappedWorldNormal = math.normalizeVec3([ + snapPickNormal[0] / math.MAX_INT, + snapPickNormal[1] / math.MAX_INT, + snapPickNormal[2] / math.MAX_INT, + ]); - snappedWorldNormal = math.normalizeVec3([ - snapPickNormal[0] / math.MAX_INT, - snapPickNormal[1] / math.MAX_INT, - snapPickNormal[2] / math.MAX_INT, - ]); + snappedWorldPos = [ + snapPick[0] * scale[0] + origin[0], + snapPick[1] * scale[1] + origin[1], + snapPick[2] * scale[2] + origin[2], + ]; - snappedWorldPos = [ - snapPick[0] * scale[0] + origin[0], - snapPick[1] * scale[1] + origin[1], - snapPick[2] * scale[2] + origin[2], + snappedPickable = pickIDs.items[ + snapPickId[0] + + (snapPickId[1] << 8) + + (snapPickId[2] << 16) + + (snapPickId[3] << 24) ]; + } - snappedPickable = pickIDs.items[ - snapPickId[0] - + (snapPickId[1] << 8) - + (snapPickId[2] << 16) - + (snapPickId[3] << 24) - ]; - } + if (null === worldPos && null == snappedWorldPos) { // If neither regular pick or snap pick, return null + return null; + } - if (null === worldPos && null == snappedWorldPos) { // If neither regular pick or snap pick, return null - return null; - } + let snappedCanvasPos = null; - let snappedCanvasPos = null; + if (null !== snappedWorldPos) { + snappedCanvasPos = scene.camera.projectWorldPos(snappedWorldPos); + } - if (null !== snappedWorldPos) { - snappedCanvasPos = scene.camera.projectWorldPos(snappedWorldPos); - } + const snappedEntity = (snappedPickable && snappedPickable.delegatePickedEntity) ? snappedPickable.delegatePickedEntity() : snappedPickable; - const snappedEntity = (snappedPickable && snappedPickable.delegatePickedEntity) ? snappedPickable.delegatePickedEntity() : snappedPickable; - - pickResult.reset(); - pickResult.snappedToEdge = (snapType === "edge"); - pickResult.snappedToVertex = (snapType === "vertex"); - pickResult.worldPos = snappedWorldPos; - pickResult.worldNormal = snappedWorldNormal; - pickResult.entity = snappedEntity; - pickResult.canvasPos = canvasPos; - pickResult.snappedCanvasPos = snappedCanvasPos || canvasPos; - - return pickResult; - }; - })(); - - function unpackDepth(depthZ) { - const vec = [depthZ[0] / 256.0, depthZ[1] / 256.0, depthZ[2] / 256.0, depthZ[3] / 256.0]; - const bitShift = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0]; - return math.dotVec4(vec, bitShift); - } + pickResult.reset(); + pickResult.snappedToEdge = (snapType === "edge"); + pickResult.snappedToVertex = (snapType === "vertex"); + pickResult.worldPos = snappedWorldPos; + pickResult.worldNormal = snappedWorldNormal; + pickResult.entity = snappedEntity; + pickResult.canvasPos = canvasPos; + pickResult.snappedCanvasPos = snappedCanvasPos || canvasPos; - function gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) { + return pickResult; + }; +})(); - const resolutionScale = scene.canvas.resolutionScale; +function unpackDepth(depthZ) { + const vec = [depthZ[0] / 256.0, depthZ[1] / 256.0, depthZ[2] / 256.0, depthZ[3] / 256.0]; + const bitShift = [1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0]; + return math.dotVec4(vec, bitShift); +} - frameCtx.reset(); - frameCtx.backfaces = true; - frameCtx.frontface = true; // "ccw" - frameCtx.pickOrigin = pickResult.origin; - frameCtx.pickViewMatrix = pickViewMatrix; - frameCtx.pickProjMatrix = pickProjMatrix; - frameCtx.pickClipPos = [ - getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), - getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), - ]; +function gpuPickWorldNormal(pickBuffer, pickable, canvasPos, pickViewMatrix, pickProjMatrix, pickResult) { - const pickNormalBuffer = renderBufferManager.getRenderBuffer("pick-normal", {size: [3, 3]}); + const resolutionScale = scene.canvas.resolutionScale; - pickNormalBuffer.bind(gl.RGBA32I); + frameCtx.reset(); + frameCtx.backfaces = true; + frameCtx.frontface = true; // "ccw" + frameCtx.pickOrigin = pickResult.origin; + frameCtx.pickViewMatrix = pickViewMatrix; + frameCtx.pickProjMatrix = pickProjMatrix; + frameCtx.pickClipPos = [ + getClipPosX(canvasPos[0] * resolutionScale, gl.drawingBufferWidth), + getClipPosY(canvasPos[1] * resolutionScale, gl.drawingBufferHeight), + ]; - gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]); + const pickNormalBuffer = renderBufferManager.getRenderBuffer("pick-normal", {size: [3, 3]}); - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - gl.clear(gl.DEPTH_BUFFER_BIT); - gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0])); + pickNormalBuffer.bind(gl.RGBA32I); - pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals + gl.viewport(0, 0, pickNormalBuffer.size[0], pickNormalBuffer.size[1]); - const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clearBufferiv(gl.COLOR, 0, new Int32Array([0, 0, 0, 0])); - pickNormalBuffer.unbind(); + pickable.drawPickNormals(frameCtx); // Draw color-encoded fragment World-space normals - const worldNormal = [ - pix[0] / math.MAX_INT, - pix[1] / math.MAX_INT, - pix[2] / math.MAX_INT, - ]; + const pix = pickNormalBuffer.read(1, 1, gl.RGBA_INTEGER, gl.INT, Int32Array, 4); - math.normalizeVec3(worldNormal); + pickNormalBuffer.unbind(); - pickResult.worldNormal = worldNormal; - } + const worldNormal = [ + pix[0] / math.MAX_INT, + pix[1] / math.MAX_INT, + pix[2] / math.MAX_INT, + ]; - /** - * Adds a {@link Marker} for occlusion testing. - * @param marker - */ - this.addMarker = function (marker) { - this._occlusionTester = this._occlusionTester || new OcclusionTester(scene, renderBufferManager); - this._occlusionTester.addMarker(marker); - scene.occlusionTestCountdown = 0; - }; + math.normalizeVec3(worldNormal); - /** - * Notifies that a {@link Marker#worldPos} has updated. - * @param marker - */ - this.markerWorldPosUpdated = function (marker) { - this._occlusionTester.markerWorldPosUpdated(marker); - }; + pickResult.worldNormal = worldNormal; +} - /** - * Removes a {@link Marker} from occlusion testing. - * @param marker - */ - this.removeMarker = function (marker) { - this._occlusionTester.removeMarker(marker); - }; +/** + * Adds a {@link Marker} for occlusion testing. + * @param marker + */ +this.addMarker = function (marker) { + this._occlusionTester = this._occlusionTester || new OcclusionTester(scene, renderBufferManager); + this._occlusionTester.addMarker(marker); + scene.occlusionTestCountdown = 0; +}; - /** - * Performs an occlusion test for all added {@link Marker}s, updating - * their {@link Marker#visible} properties accordingly. - */ - this.doOcclusionTest = function () { +/** + * Notifies that a {@link Marker#worldPos} has updated. + * @param marker + */ +this.markerWorldPosUpdated = function (marker) { + this._occlusionTester.markerWorldPosUpdated(marker); +}; - if (this._occlusionTester && this._occlusionTester.needOcclusionTest) { +/** + * Removes a {@link Marker} from occlusion testing. + * @param marker + */ +this.removeMarker = function (marker) { + this._occlusionTester.removeMarker(marker); +}; - updateDrawlist(); +/** + * Performs an occlusion test for all added {@link Marker}s, updating + * their {@link Marker#visible} properties accordingly. + */ +this.doOcclusionTest = function () { - this._occlusionTester.bindRenderBuf(); + if (this._occlusionTester && this._occlusionTester.needOcclusionTest) { - frameCtx.reset(); - frameCtx.backfaces = true; - frameCtx.frontface = true; // "ccw" + updateDrawlist(); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - gl.clearColor(0, 0, 0, 0); - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this._occlusionTester.bindRenderBuf(); - for (let type in drawableTypeInfo) { - if (drawableTypeInfo.hasOwnProperty(type)) { - const drawableInfo = drawableTypeInfo[type]; - const drawableList = drawableInfo.drawableList; - for (let i = 0, len = drawableList.length; i < len; i++) { - const drawable = drawableList[i]; - if (!drawable.drawOcclusion || drawable.culled === true || drawable.visible === false || drawable.pickable === false) { // TODO: Option to exclude transparent? - continue; - } - - drawable.drawOcclusion(frameCtx); - } - } + frameCtx.reset(); + frameCtx.backfaces = true; + frameCtx.frontface = true; // "ccw" + + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + gl.clearColor(0, 0, 0, 0); + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + + for (let i = 0, len = postCullDrawableList.length; i < len; i++) { + + const drawable = postCullDrawableList[i]; + + if (!drawable.drawOcclusion || drawable.culled === true || drawable.visible === false || drawable.pickable === false) { // TODO: Option to exclude transparent? + continue; } - this._occlusionTester.drawMarkers(frameCtx); - this._occlusionTester.doOcclusionTest(); // Updates Marker "visible" properties - this._occlusionTester.unbindRenderBuf(); + drawable.drawOcclusion(frameCtx); } - }; - /** - * Read pixels from the renderer's current output. Performs a force-render first. - * @param pixels - * @param colors - * @param len - * @param opaqueOnly - * @private - */ - this.readPixels = function (pixels, colors, len, opaqueOnly) { - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - snapshotBuffer.bind(); - snapshotBuffer.clear(); - this.render({force: true, opaqueOnly: opaqueOnly}); - let color; - let i; - let j; - let k; - for (i = 0; i < len; i++) { - j = i * 2; - k = i * 4; - color = snapshotBuffer.read(pixels[j], pixels[j + 1]); - colors[k] = color[0]; - colors[k + 1] = color[1]; - colors[k + 2] = color[2]; - colors[k + 3] = color[3]; - } - snapshotBuffer.unbind(); - imageDirty = true; - }; + this._occlusionTester.drawMarkers(frameCtx); + this._occlusionTester.doOcclusionTest(); // Updates Marker "visible" properties + this._occlusionTester.unbindRenderBuf(); + } +}; - /** - * Enter snapshot mode. - * - * Switches rendering to a hidden snapshot canvas. - * - * Exit snapshot mode using endSnapshot(). - */ - this.beginSnapshot = function (params = {}) { - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - if (params.width && params.height) { - snapshotBuffer.setSize([params.width, params.height]); - } - snapshotBuffer.bind(); - snapshotBuffer.clear(); - snapshotBound = true; - }; +/** + * Read pixels from the renderer's current output. Performs a force-render first. + * @param pixels + * @param colors + * @param len + * @param opaqueOnly + * @private + */ +this.readPixels = function (pixels, colors, len, opaqueOnly) { + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + snapshotBuffer.bind(); + snapshotBuffer.clear(); + this.render({force: true, opaqueOnly: opaqueOnly}); + let color; + let i; + let j; + let k; + for (i = 0; i < len; i++) { + j = i * 2; + k = i * 4; + color = snapshotBuffer.read(pixels[j], pixels[j + 1]); + colors[k] = color[0]; + colors[k + 1] = color[1]; + colors[k + 2] = color[2]; + colors[k + 3] = color[3]; + } + snapshotBuffer.unbind(); + imageDirty = true; +}; - /** - * When in snapshot mode, renders a frame of the current Scene state to the snapshot canvas. - */ - this.renderSnapshot = function () { - if (!snapshotBound) { - return; - } - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - snapshotBuffer.clear(); - this.render({force: true, opaqueOnly: false}); - imageDirty = true; - }; +/** + * Enter snapshot mode. + * + * Switches rendering to a hidden snapshot canvas. + * + * Exit snapshot mode using endSnapshot(). + */ +this.beginSnapshot = function (params = {}) { + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + if (params.width && params.height) { + snapshotBuffer.setSize([params.width, params.height]); + } + snapshotBuffer.bind(); + snapshotBuffer.clear(); + snapshotBound = true; +}; - /** - * When in snapshot mode, gets an image of the snapshot canvas. - * - * @private - * @returns {String} The image data URI. - */ - this.readSnapshot = function (params) { - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - return snapshotBuffer.readImage(params); - }; +/** + * When in snapshot mode, renders a frame of the current Scene state to the snapshot canvas. + */ +this.renderSnapshot = function () { + if (!snapshotBound) { + return; + } + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + snapshotBuffer.clear(); + this.render({force: true, opaqueOnly: false}); + imageDirty = true; +}; - /** - * Returns an HTMLCanvas containing an image of the snapshot canvas. - * - * - The HTMLCanvas has a CanvasRenderingContext2D. - * - Expects the caller to draw more things on the HTMLCanvas (annotations etc). - * - * @returns {HTMLCanvasElement} - */ - this.readSnapshotAsCanvas = function () { - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - return snapshotBuffer.readImageAsCanvas(); - }; +/** + * When in snapshot mode, gets an image of the snapshot canvas. + * + * @private + * @returns {String} The image data URI. + */ +this.readSnapshot = function (params) { + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + return snapshotBuffer.readImage(params); +}; - /** - * Exists snapshot mode. - * - * Switches rendering back to the main canvas. - */ - this.endSnapshot = function () { - if (!snapshotBound) { - return; - } - const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); - snapshotBuffer.unbind(); - snapshotBound = false; - }; +/** + * Returns an HTMLCanvas containing an image of the snapshot canvas. + * + * - The HTMLCanvas has a CanvasRenderingContext2D. + * - Expects the caller to draw more things on the HTMLCanvas (annotations etc). + * + * @returns {HTMLCanvasElement} + */ +this.readSnapshotAsCanvas = function () { + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + return snapshotBuffer.readImageAsCanvas(); +}; - /** - * Destroys this renderer. - * @private - */ - this.destroy = function () { +/** + * Exists snapshot mode. + * + * Switches rendering back to the main canvas. + */ +this.endSnapshot = function () { + if (!snapshotBound) { + return; + } + const snapshotBuffer = renderBufferManager.getRenderBuffer("snapshot"); + snapshotBuffer.unbind(); + snapshotBound = false; +}; - drawableTypeInfo = {}; - drawables = {}; +/** + * Destroys this renderer. + * @private + */ +this.destroy = function () { - renderBufferManager.destroy(); + drawableTypeInfo = {}; + drawables = {}; - saoOcclusionRenderer.destroy(); - saoDepthLimitedBlurRenderer.destroy(); + renderBufferManager.destroy(); - if (this._occlusionTester) { - this._occlusionTester.destroy(); - } - }; + saoOcclusionRenderer.destroy(); + saoDepthLimitedBlurRenderer.destroy(); + + if (this._occlusionTester) { + this._occlusionTester.destroy(); + } }; +} +; export {Renderer};