modules/layers/src/path-layer/path-layer-vertex.glsl.js (167 lines of code) (raw):

// Copyright (c) 2015 - 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. export default `\ #define SHADER_NAME path-layer-vertex-shader attribute vec2 positions; attribute float instanceTypes; attribute vec3 instanceStartPositions; attribute vec3 instanceEndPositions; attribute vec3 instanceLeftPositions; attribute vec3 instanceRightPositions; attribute vec3 instanceLeftPositions64Low; attribute vec3 instanceStartPositions64Low; attribute vec3 instanceEndPositions64Low; attribute vec3 instanceRightPositions64Low; attribute float instanceStrokeWidths; attribute vec4 instanceColors; attribute vec3 instancePickingColors; uniform float widthScale; uniform float widthMinPixels; uniform float widthMaxPixels; uniform float jointType; uniform float miterLimit; uniform bool billboard; uniform float opacity; varying vec4 vColor; varying vec2 vCornerOffset; varying float vMiterLength; varying vec2 vPathPosition; varying float vPathLength; const float EPSILON = 0.001; const vec3 ZERO_OFFSET = vec3(0.0); float flipIfTrue(bool flag) { return -(float(flag) * 2. - 1.); } // calculate line join positions vec3 lineJoin( vec3 prevPoint, vec3 currPoint, vec3 nextPoint, vec2 width ) { bool isEnd = positions.x > 0.0; // side of the segment - -1: left, 0: center, 1: right float sideOfPath = positions.y; float isJoint = float(sideOfPath == 0.0); vec3 deltaA3 = (currPoint - prevPoint); vec3 deltaB3 = (nextPoint - currPoint); mat3 rotationMatrix; bool needsRotation = !billboard && project_needs_rotation(currPoint, rotationMatrix); if (needsRotation) { deltaA3 = deltaA3 * rotationMatrix; deltaB3 = deltaB3 * rotationMatrix; } vec2 deltaA = deltaA3.xy / width; vec2 deltaB = deltaB3.xy / width; float lenA = length(deltaA); float lenB = length(deltaB); vec2 dirA = lenA > 0. ? normalize(deltaA) : vec2(0.0, 0.0); vec2 dirB = lenB > 0. ? normalize(deltaB) : vec2(0.0, 0.0); vec2 perpA = vec2(-dirA.y, dirA.x); vec2 perpB = vec2(-dirB.y, dirB.x); // tangent of the corner vec2 tangent = dirA + dirB; tangent = length(tangent) > 0. ? normalize(tangent) : perpA; // direction of the corner vec2 miterVec = vec2(-tangent.y, tangent.x); // direction of the segment vec2 dir = isEnd ? dirA : dirB; // direction of the extrusion vec2 perp = isEnd ? perpA : perpB; // length of the segment float L = isEnd ? lenA : lenB; // A = angle of the corner float sinHalfA = abs(dot(miterVec, perp)); float cosHalfA = abs(dot(dirA, miterVec)); // -1: right, 1: left float turnDirection = flipIfTrue(dirA.x * dirB.y >= dirA.y * dirB.x); // relative position to the corner: // -1: inside (smaller side of the angle) // 0: center // 1: outside (bigger side of the angle) float cornerPosition = sideOfPath * turnDirection; float miterSize = 1.0 / max(sinHalfA, EPSILON); // trim if inside corner extends further than the line segment miterSize = mix( min(miterSize, max(lenA, lenB) / max(cosHalfA, EPSILON)), miterSize, step(0.0, cornerPosition) ); vec2 offsetVec = mix(miterVec * miterSize, perp, step(0.5, cornerPosition)) * (sideOfPath + isJoint * turnDirection); // special treatment for start cap and end cap bool isStartCap = lenA == 0.0 || (!isEnd && (instanceTypes == 1.0 || instanceTypes == 3.0)); bool isEndCap = lenB == 0.0 || (isEnd && (instanceTypes == 2.0 || instanceTypes == 3.0)); bool isCap = isStartCap || isEndCap; // extend out a triangle to envelope the round cap if (isCap) { offsetVec = mix(perp * sideOfPath, dir * jointType * 4.0 * flipIfTrue(isStartCap), isJoint); } // Generate variables for fragment shader vPathLength = L; vCornerOffset = offsetVec; vMiterLength = dot(vCornerOffset, miterVec * turnDirection); vMiterLength = isCap ? isJoint : vMiterLength; vec2 offsetFromStartOfPath = vCornerOffset + deltaA * float(isEnd); vPathPosition = vec2( dot(offsetFromStartOfPath, perp), dot(offsetFromStartOfPath, dir) ); geometry.uv = vPathPosition; float isValid = step(instanceTypes, 3.5); vec3 offset = vec3(offsetVec * width * isValid, 0.0); DECKGL_FILTER_SIZE(offset, geometry); if (needsRotation) { offset = rotationMatrix * offset; } return currPoint + offset; } // In clipspace extrusion, if a line extends behind the camera, clip it to avoid visual artifacts void clipLine(inout vec4 position, vec4 refPosition) { if (position.w < EPSILON) { float r = (EPSILON - refPosition.w) / (position.w - refPosition.w); position = refPosition + (position - refPosition) * r; } } void main() { geometry.worldPosition = instanceStartPositions; geometry.worldPositionAlt = instanceEndPositions; geometry.pickingColor = instancePickingColors; vec2 widthPixels = vec2(clamp(project_size_to_pixel(instanceStrokeWidths * widthScale), widthMinPixels, widthMaxPixels) / 2.0); vColor = vec4(instanceColors.rgb, instanceColors.a * opacity); float isEnd = positions.x; vec3 prevPosition = mix(instanceLeftPositions, instanceStartPositions, isEnd); vec3 prevPosition64Low = mix(instanceLeftPositions64Low, instanceStartPositions64Low, isEnd); vec3 currPosition = mix(instanceStartPositions, instanceEndPositions, isEnd); vec3 currPosition64Low = mix(instanceStartPositions64Low, instanceEndPositions64Low, isEnd); vec3 nextPosition = mix(instanceEndPositions, instanceRightPositions, isEnd); vec3 nextPosition64Low = mix(instanceEndPositions64Low, instanceRightPositions64Low, isEnd); if (billboard) { // Extrude in clipspace vec4 prevPositionScreen = project_position_to_clipspace(prevPosition, prevPosition64Low, ZERO_OFFSET); vec4 currPositionScreen = project_position_to_clipspace(currPosition, currPosition64Low, ZERO_OFFSET, geometry.position); vec4 nextPositionScreen = project_position_to_clipspace(nextPosition, nextPosition64Low, ZERO_OFFSET); clipLine(prevPositionScreen, currPositionScreen); clipLine(nextPositionScreen, currPositionScreen); clipLine(currPositionScreen, mix(nextPositionScreen, prevPositionScreen, isEnd)); vec2 width = project_pixel_size_to_clipspace(widthPixels); vec3 pos = lineJoin( prevPositionScreen.xyz / prevPositionScreen.w, currPositionScreen.xyz / currPositionScreen.w, nextPositionScreen.xyz / nextPositionScreen.w, width ); gl_Position = vec4(pos * currPositionScreen.w, currPositionScreen.w); } else { // Extrude in commonspace prevPosition = project_position(prevPosition, prevPosition64Low); currPosition = project_position(currPosition, currPosition64Low); nextPosition = project_position(nextPosition, nextPosition64Low); vec2 width = project_pixel_size(widthPixels); vec4 pos = vec4( lineJoin(prevPosition, currPosition, nextPosition, width), 1.0); geometry.position = pos; gl_Position = project_common_position_to_clipspace(pos); } DECKGL_FILTER_GL_POSITION(gl_Position, geometry); DECKGL_FILTER_COLOR(vColor, geometry); } `;