// 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 `\ #version 300 es #define SHADER_NAME path-layer-vertex-shader in vec2 positions; in float instanceTypes; in vec3 instanceStartPositions; in vec3 instanceEndPositions; in vec3 instanceLeftPositions; in vec3 instanceRightPositions; in vec3 instanceLeftPositions64Low; in vec3 instanceStartPositions64Low; in vec3 instanceEndPositions64Low; in vec3 instanceRightPositions64Low; in float instanceStrokeWidths; in vec4 instanceColors; in vec3 instancePickingColors; uniform float widthScale; uniform float widthMinPixels; uniform float widthMaxPixels; uniform float jointType; uniform float capType; uniform float miterLimit; uniform bool billboard; uniform int widthUnits; uniform float opacity; out vec4 vColor; out vec2 vCornerOffset; out float vMiterLength; out vec2 vPathPosition; out float vPathLength; out float vJointType; const float EPSILON = 0.001; const vec3 ZERO_OFFSET = vec3(0.0); float flipIfTrue(bool flag) { return -(float(flag) * 2. - 1.); } vec3 getLineJoinOffset( vec3 prevPoint, vec3 currPoint, vec3 nextPoint, vec2 width ) { bool isEnd = positions.x > 0.0; 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); vec2 tangent = dirA + dirB; tangent = length(tangent) > 0. ? normalize(tangent) : perpA; vec2 miterVec = vec2(-tangent.y, tangent.x); vec2 dir = isEnd ? dirA : dirB; vec2 perp = isEnd ? perpA : perpB; float L = isEnd ? lenA : lenB; float sinHalfA = abs(dot(miterVec, perp)); float cosHalfA = abs(dot(dirA, miterVec)); float turnDirection = flipIfTrue(dirA.x * dirB.y >= dirA.y * dirB.x); float cornerPosition = sideOfPath * turnDirection; float miterSize = 1.0 / max(sinHalfA, EPSILON); 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); 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; if (isCap) { offsetVec = mix(perp * sideOfPath, dir * capType * 4.0 * flipIfTrue(isStartCap), isJoint); vJointType = capType; } else { vJointType = jointType; } 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); if (needsRotation) { offset = rotationMatrix * offset; } return offset; } 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.pickingColor = instancePickingColors; 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); geometry.worldPosition = currPosition; vec2 widthPixels = vec2(clamp( project_size_to_pixel(instanceStrokeWidths * widthScale, widthUnits), widthMinPixels, widthMaxPixels) / 2.0); vec3 width; if (billboard) { 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)); width = vec3(widthPixels, 0.0); DECKGL_FILTER_SIZE(width, geometry); vec3 offset = getLineJoinOffset( prevPositionScreen.xyz / prevPositionScreen.w, currPositionScreen.xyz / currPositionScreen.w, nextPositionScreen.xyz / nextPositionScreen.w, project_pixel_size_to_clipspace(width.xy) ); DECKGL_FILTER_GL_POSITION(currPositionScreen, geometry); gl_Position = vec4(currPositionScreen.xyz + offset * currPositionScreen.w, currPositionScreen.w); } else { prevPosition = project_position(prevPosition, prevPosition64Low); currPosition = project_position(currPosition, currPosition64Low); nextPosition = project_position(nextPosition, nextPosition64Low); width = vec3(project_pixel_size(widthPixels), 0.0); DECKGL_FILTER_SIZE(width, geometry); vec3 offset = getLineJoinOffset(prevPosition, currPosition, nextPosition, width.xy); geometry.position = vec4(currPosition + offset, 1.0); gl_Position = project_common_position_to_clipspace(geometry.position); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); } DECKGL_FILTER_COLOR(vColor, geometry); } `;