diff --git a/CHANGES.md b/CHANGES.md index fb36560b41a5..bed3437c7b1c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -26,6 +26,7 @@ Change Log * Fixed a bug with Draco encoded i3dm tiles, and loading two Draco models with the same url. [#6668](https://github.com/AnalyticalGraphicsInc/cesium/issues/6668) * Fixed terrain clipping when the camera was close to flat terrain and was using logarithmic depth. [#6701](https://github.com/AnalyticalGraphicsInc/cesium/pull/6701) * Fixed KML bug that constantly requested the same image if it failed to load. [#6710](https://github.com/AnalyticalGraphicsInc/cesium/pull/6710) +* Improved billboard and label rendering so they no longer sink into terrain when clamped to ground. [#6621](https://github.com/AnalyticalGraphicsInc/cesium/pull/6621) * Fixed an issue where KMLs containing a `colorMode` of `random` could return the exact same color on successive calls to `Color.fromRandom()`. ### 1.46.1 - 2018-06-01 diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index df8ad223d024..0baeef037daf 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -159,6 +159,9 @@ define([ this._imageWidth = undefined; this._imageHeight = undefined; + this._labelDimensions = undefined; + this._labelHorizontalOrigin = undefined; + var image = options.image; var imageId = options.imageId; if (defined(image)) { @@ -210,7 +213,8 @@ define([ var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = 13; var DISTANCE_DISPLAY_CONDITION = Billboard.DISTANCE_DISPLAY_CONDITION = 14; var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE = 15; - Billboard.NUMBER_OF_PROPERTIES = 16; + Billboard.TEXTURE_COORDINATE_BOUNDS = 16; + Billboard.NUMBER_OF_PROPERTIES = 17; function makeDirty(billboard, propertyChanged) { var billboardCollection = billboard._billboardCollection; diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index 2382486d757a..89bcb04985c7 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -84,8 +84,9 @@ define([ var SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX; var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX; var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX; - var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION_INDEX; + var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION; var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE; + var TEXTURE_COORDINATE_BOUNDS = Billboard.TEXTURE_COORDINATE_BOUNDS; var NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES; var attributeLocations; @@ -99,8 +100,9 @@ define([ eyeOffset : 5, // 4 bytes free scaleByDistance : 6, pixelOffsetScaleByDistance : 7, - distanceDisplayConditionAndDisableDepth : 8, - a_batchId : 9 + compressedAttribute3 : 8, + textureCoordinateBounds : 9, + a_batchId : 10 }; var attributeLocationsInstanced = { @@ -113,8 +115,9 @@ define([ eyeOffset : 6, // texture range in w scaleByDistance : 7, pixelOffsetScaleByDistance : 8, - distanceDisplayConditionAndDisableDepth : 9, - a_batchId : 10 + compressedAttribute3 : 9, + textureCoordinateBounds : 10, + a_batchId : 11 }; /** @@ -208,6 +211,9 @@ define([ this._shaderDisableDepthDistance = false; this._compiledShaderDisableDepthDistance = false; + this._shaderClampToGround = false; + this._compiledShaderClampToGround = false; + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxSize = 0.0; @@ -302,7 +308,8 @@ define([ BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX BufferUsage.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX - BufferUsage.STATIC_DRAW // DISTANCE_DISPLAY_CONDITION_INDEX + BufferUsage.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX + BufferUsage.STATIC_DRAW // TEXTURE_COORDINATE_BOUNDS ]; this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints @@ -732,10 +739,15 @@ define([ componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX] }, { - index : attributeLocations.distanceDisplayConditionAndDisableDepth, - componentsPerAttribute : 3, + index : attributeLocations.compressedAttribute3, + componentsPerAttribute : 4, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX] + }, { + index : attributeLocations.textureCoordinateBounds, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + usage : buffersUsage[TEXTURE_COORDINATE_BOUNDS] }]; // Instancing requires one non-instanced attribute. @@ -818,6 +830,7 @@ define([ var UPPER_BOUND = 32768.0; // 2^15 var LEFT_SHIFT16 = 65536.0; // 2^16 + var LEFT_SHIFT12 = 4096.0; // 2^12 var LEFT_SHIFT8 = 256.0; // 2^8 var LEFT_SHIFT7 = 128.0; var LEFT_SHIFT5 = 32.0; @@ -1020,6 +1033,9 @@ define([ var dimensions = billboardCollection._textureAtlas.texture.dimensions; var imageHeight = Math.round(defaultValue(billboard.height, dimensions.y * height)); billboardCollection._maxSize = Math.max(billboardCollection._maxSize, imageHeight); + var labelHorizontalOrigin = defaultValue(billboard._labelHorizontalOrigin, -2); + labelHorizontalOrigin += 2; + var compressed3 = imageHeight * LEFT_SHIFT2 + labelHorizontalOrigin; var red = Color.floatToByte(color.red); var green = Color.floatToByte(color.green); @@ -1036,13 +1052,13 @@ define([ if (billboardCollection._instanced) { i = billboard._index; - writer(i, compressed0, compressed1, compressed2, imageHeight); + writer(i, compressed0, compressed1, compressed2, compressed3); } else { i = billboard._index * 4; - writer(i + 0, compressed0, compressed1, compressed2, imageHeight); - writer(i + 1, compressed0, compressed1, compressed2, imageHeight); - writer(i + 2, compressed0, compressed1, compressed2, imageHeight); - writer(i + 3, compressed0, compressed1, compressed2, imageHeight); + writer(i + 0, compressed0, compressed1, compressed2, compressed3); + writer(i + 1, compressed0, compressed1, compressed2, compressed3); + writer(i + 2, compressed0, compressed1, compressed2, compressed3); + writer(i + 3, compressed0, compressed1, compressed2, compressed3); } } @@ -1158,9 +1174,9 @@ define([ } } - function writeDistanceDisplayConditionAndDepthDisable(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + function writeCompressedAttribute3(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { var i; - var writer = vafWriters[attributeLocations.distanceDisplayConditionAndDisableDepth]; + var writer = vafWriters[attributeLocations.compressedAttribute3]; var near = 0.0; var far = Number.MAX_VALUE; @@ -1176,6 +1192,10 @@ define([ } var disableDepthTestDistance = billboard.disableDepthTestDistance; + if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND && disableDepthTestDistance === 0.0 && billboardCollection._scene.context.depthTexture) { + disableDepthTestDistance = 2000.0; + } + disableDepthTestDistance *= disableDepthTestDistance; if (disableDepthTestDistance > 0.0) { billboardCollection._shaderDisableDepthDistance = true; @@ -1184,15 +1204,89 @@ define([ } } + var imageHeight; + var imageWidth; + + if (!defined(billboard._labelDimensions)) { + var height = 0; + var width = 0; + var index = billboard._imageIndex; + if (index !== -1) { + var imageRectangle = textureAtlasCoordinates[index]; + + //>>includeStart('debug', pragmas.debug); + if (!defined(imageRectangle)) { + throw new DeveloperError('Invalid billboard image index: ' + index); + } + //>>includeEnd('debug'); + + height = imageRectangle.height; + width = imageRectangle.width; + } + + imageHeight = Math.round(defaultValue(billboard.height, billboardCollection._textureAtlas.texture.dimensions.y * height)); + + var textureWidth = billboardCollection._textureAtlas.texture.width; + imageWidth = Math.round(defaultValue(billboard.width, textureWidth * width)); + } else { + imageWidth = billboard._labelDimensions.x; + imageHeight = billboard._labelDimensions.y; + } + + var w = Math.floor(CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT12)); + var h = Math.floor(CesiumMath.clamp(imageHeight, 0.0, LEFT_SHIFT12)); + var dimensions = w * LEFT_SHIFT12 + h; + + if (billboardCollection._instanced) { + i = billboard._index; + writer(i, near, far, disableDepthTestDistance, dimensions); + } else { + i = billboard._index * 4; + writer(i + 0, near, far, disableDepthTestDistance, dimensions); + writer(i + 1, near, far, disableDepthTestDistance, dimensions); + writer(i + 2, near, far, disableDepthTestDistance, dimensions); + writer(i + 3, near, far, disableDepthTestDistance, dimensions); + } + } + + function writeTextureCoordinateBounds(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) { + billboardCollection._shaderClampToGround = billboardCollection._scene.context.depthTexture; + } + var i; + var writer = vafWriters[attributeLocations.textureCoordinateBounds]; + + var minX = 0; + var minY = 0; + var width = 0; + var height = 0; + var index = billboard._imageIndex; + if (index !== -1) { + var imageRectangle = textureAtlasCoordinates[index]; + + //>>includeStart('debug', pragmas.debug); + if (!defined(imageRectangle)) { + throw new DeveloperError('Invalid billboard image index: ' + index); + } + //>>includeEnd('debug'); + + minX = imageRectangle.x; + minY = imageRectangle.y; + width = imageRectangle.width; + height = imageRectangle.height; + } + var maxX = minX + width; + var maxY = minY + height; + if (billboardCollection._instanced) { i = billboard._index; - writer(i, near, far, disableDepthTestDistance); + writer(i, minX, minY, maxX, maxY); } else { i = billboard._index * 4; - writer(i + 0, near, far, disableDepthTestDistance); - writer(i + 1, near, far, disableDepthTestDistance); - writer(i + 2, near, far, disableDepthTestDistance); - writer(i + 3, near, far, disableDepthTestDistance); + writer(i + 0, minX, minY, maxX, maxY); + writer(i + 1, minX, minY, maxX, maxY); + writer(i + 2, minX, minY, maxX, maxY); + writer(i + 3, minX, minY, maxX, maxY); } } @@ -1225,7 +1319,8 @@ define([ writeEyeOffset(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writePixelOffsetScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); - writeDistanceDisplayConditionAndDepthDisable(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeCompressedAttribute3(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeTextureCoordinateBounds(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeBatchId(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); } @@ -1421,8 +1516,12 @@ define([ writers.push(writePixelOffsetScaleByDistance); } - if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE]) { - writers.push(writeDistanceDisplayConditionAndDepthDisable); + if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE] || properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) { + writers.push(writeCompressedAttribute3); + } + + if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) { + writers.push(writeTextureCoordinateBounds); } var numWriters = writers.length; @@ -1540,7 +1639,8 @@ define([ (this._shaderTranslucencyByDistance !== this._compiledShaderTranslucencyByDistance) || (this._shaderPixelOffsetScaleByDistance !== this._compiledShaderPixelOffsetScaleByDistance) || (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayCondition) || - (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance)) { + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance) || + (this._shaderClampToGround !== this._compiledShaderClampToGround)) { vsSource = BillboardCollectionVS; fsSource = BillboardCollectionFS; @@ -1580,6 +1680,9 @@ define([ if (this._shaderDisableDepthDistance) { vs.defines.push('DISABLE_DEPTH_DISTANCE'); } + if (this._shaderClampToGround) { + vs.defines.push('CLAMP_TO_GROUND'); + } var vectorFragDefine = defined(this._batchTable) ? 'VECTOR_TILE' : ''; @@ -1588,6 +1691,9 @@ define([ defines : ['OPAQUE', vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : this._sp, @@ -1600,6 +1706,9 @@ define([ defines : ['TRANSLUCENT', vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._spTranslucent = ShaderProgram.replaceCache({ context : context, shaderProgram : this._spTranslucent, @@ -1614,6 +1723,9 @@ define([ defines : [vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : this._sp, @@ -1628,6 +1740,9 @@ define([ defines : [vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._spTranslucent = ShaderProgram.replaceCache({ context : context, shaderProgram : this._spTranslucent, @@ -1644,6 +1759,7 @@ define([ this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; + this._compiledShaderClampToGround = this._shaderClampToGround; } var commandList = frameState.commandList; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 5cdfa70bfee0..e6ac85dab71a 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -249,6 +249,7 @@ define([ billboard = labelCollection._billboardCollection.add({ collection : labelCollection }); + billboard._labelDimensions = new Cartesian2(); } glyph.billboard = billboard; } @@ -306,10 +307,9 @@ define([ var glyphLength = glyphs.length; var backgroundBillboard = label._backgroundBillboard; - var backgroundPadding = scratchBackgroundPadding; - Cartesian2.clone( + var backgroundPadding = Cartesian2.clone( (defined(backgroundBillboard) ? label._backgroundPadding : Cartesian2.ZERO), - backgroundPadding); + scratchBackgroundPadding); for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { if (text.charAt(glyphIndex) === '\n') { @@ -341,6 +341,14 @@ define([ var widthOffset = calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding); var lineSpacing = defaultLineSpacingPercent * maxLineHeight; var otherLinesHeight = lineSpacing * (numberOfLines - 1); + var totalLineWidth = maxLineWidth; + var totalLineHeight = maxLineHeight + otherLinesHeight; + + if (defined(backgroundBillboard)) { + totalLineWidth += (backgroundPadding.x * 2); + totalLineHeight += (backgroundPadding.y * 2); + backgroundBillboard._labelHorizontalOrigin = horizontalOrigin; + } glyphPixelOffset.x = widthOffset * scale * resolutionScale; glyphPixelOffset.y = 0; @@ -371,6 +379,9 @@ define([ if (defined(glyph.billboard)) { glyph.billboard._setTranslate(glyphPixelOffset); + glyph.billboard._labelDimensions.x = totalLineWidth; + glyph.billboard._labelDimensions.y = totalLineHeight; + glyph.billboard._labelHorizontalOrigin = horizontalOrigin; } //Compute the next x offset taking into acocunt the kerning performed @@ -405,8 +416,8 @@ define([ } glyphPixelOffset.y = glyphPixelOffset.y * scale * resolutionScale; - backgroundBillboard.width = maxLineWidth + (backgroundPadding.x * 2); - backgroundBillboard.height = maxLineHeight + otherLinesHeight + (backgroundPadding.y * 2); + backgroundBillboard.width = totalLineWidth; + backgroundBillboard.height = totalLineHeight; backgroundBillboard._setTranslate(glyphPixelOffset); } } diff --git a/Source/Shaders/BillboardCollectionFS.glsl b/Source/Shaders/BillboardCollectionFS.glsl index 6d9a66cdcb7d..e306c2735cf5 100644 --- a/Source/Shaders/BillboardCollectionFS.glsl +++ b/Source/Shaders/BillboardCollectionFS.glsl @@ -5,10 +5,47 @@ uniform vec4 u_highlightColor; #endif varying vec2 v_textureCoordinates; - varying vec4 v_pickColor; varying vec4 v_color; +#ifdef CLAMP_TO_GROUND +varying vec4 v_textureCoordinateBounds; // the min and max x and y values for the texture coordinates +varying vec4 v_originTextureCoordinateAndTranslate; // texture coordinate at the origin, billboard translate (used for label glyphs) +varying vec4 v_dimensionsAndImageSize; // dimensions of the bounding rectangle and the size of the image. The values will only be different for label glyphs +varying vec3 v_eyeDepthDistanceAndApplyTranslate; // The depth of the billboard and the disable depth test distance +varying mat2 v_rotationMatrix; + +float getGlobeDepth(vec2 adjustedST, vec2 depthLookupST) +{ + vec2 dimensions = v_dimensionsAndImageSize.xy; + vec2 imageSize = v_dimensionsAndImageSize.zw; + + vec2 lookupVector = imageSize * (depthLookupST - adjustedST); + lookupVector = v_rotationMatrix * lookupVector; + vec2 labelOffset = (dimensions - imageSize) * (depthLookupST - vec2(0.0, v_originTextureCoordinateAndTranslate.y)); // aligns label glyph with bounding rectangle. Will be zero for billboards because dimensions and imageSize will be equal + + vec2 translation = v_originTextureCoordinateAndTranslate.zw; + + if (v_eyeDepthDistanceAndApplyTranslate.z != 0.0) + { + // this is only needed for labels where the horizontal origin is not LEFT + // it moves the label back to where the "origin" should be since all label glyphs are set to HorizontalOrigin.LEFT + translation += (dimensions * v_originTextureCoordinateAndTranslate.xy * vec2(1.0, 0.0)); + } + + vec2 st = ((lookupVector - translation + labelOffset) + gl_FragCoord.xy) / czm_viewport.zw; + float logDepthOrDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, st)); + + if (logDepthOrDepth == 0.0) + { + return 0.0; // not on the globe + } + + vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, logDepthOrDepth); + return eyeCoordinate.z / eyeCoordinate.w; +} +#endif + void main() { vec4 color = texture2D(u_atlas, v_textureCoordinates) * v_color; @@ -38,7 +75,32 @@ void main() #ifdef VECTOR_TILE color *= u_highlightColor; #endif - gl_FragColor = color; + czm_writeLogDepth(); + +#ifdef CLAMP_TO_GROUND + if (v_eyeDepthDistanceAndApplyTranslate.x > -v_eyeDepthDistanceAndApplyTranslate.y) { + vec2 adjustedST = v_textureCoordinates - v_textureCoordinateBounds.xy; + adjustedST = adjustedST / vec2(v_textureCoordinateBounds.z - v_textureCoordinateBounds.x, v_textureCoordinateBounds.w - v_textureCoordinateBounds.y); + + float epsilonEyeDepth = v_eyeDepthDistanceAndApplyTranslate.x + czm_epsilon1; + float globeDepth1 = getGlobeDepth(adjustedST, v_originTextureCoordinateAndTranslate.xy); + + // negative values go into the screen + if (globeDepth1 != 0.0 && globeDepth1 > epsilonEyeDepth) + { + float globeDepth2 = getGlobeDepth(adjustedST, vec2(0.0, 1.0)); // top left corner + if (globeDepth2 != 0.0 && globeDepth2 > epsilonEyeDepth) + { + float globeDepth3 = getGlobeDepth(adjustedST, vec2(1.0, 1.0)); // top right corner + if (globeDepth3 != 0.0 && globeDepth3 > epsilonEyeDepth) + { + discard; + } + } + } + } +#endif + } diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index 9c6f537e5856..857d7368b59f 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -5,16 +5,26 @@ attribute vec4 positionHighAndScale; attribute vec4 positionLowAndRotation; attribute vec4 compressedAttribute0; // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates (texture offset) attribute vec4 compressedAttribute1; // aligned axis, translucency by distance, image width -attribute vec4 compressedAttribute2; // image height, color, pick color, size in meters, valid aligned axis, 13 bits free +attribute vec4 compressedAttribute2; // label horizontal origin, image height, color, pick color, size in meters, valid aligned axis, 13 bits free attribute vec4 eyeOffset; // eye offset in meters, 4 bytes free (texture range) attribute vec4 scaleByDistance; // near, nearScale, far, farScale attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale -attribute vec3 distanceDisplayConditionAndDisableDepth; // near, far, disableDepthTestDistance +attribute vec4 compressedAttribute3; // distance display condition near, far, disableDepthTestDistance, dimensions +#ifdef CLAMP_TO_GROUND +attribute vec4 textureCoordinateBounds; // the min and max x and y values for the texture coordinates +#endif #ifdef VECTOR_TILE attribute float a_batchId; #endif varying vec2 v_textureCoordinates; +#ifdef CLAMP_TO_GROUND +varying vec4 v_textureCoordinateBounds; +varying vec4 v_originTextureCoordinateAndTranslate; +varying vec4 v_dimensionsAndImageSize; +varying vec3 v_eyeDepthDistanceAndApplyTranslate; +varying mat2 v_rotationMatrix; +#endif varying vec4 v_pickColor; varying vec4 v_color; @@ -22,6 +32,7 @@ varying vec4 v_color; const float UPPER_BOUND = 32768.0; const float SHIFT_LEFT16 = 65536.0; +const float SHIFT_LEFT12 = 4096.0; const float SHIFT_LEFT8 = 256.0; const float SHIFT_LEFT7 = 128.0; const float SHIFT_LEFT5 = 32.0; @@ -29,6 +40,7 @@ const float SHIFT_LEFT3 = 8.0; const float SHIFT_LEFT2 = 4.0; const float SHIFT_LEFT1 = 2.0; +const float SHIFT_RIGHT12 = 1.0 / 4096.0; const float SHIFT_RIGHT8 = 1.0 / 256.0; const float SHIFT_RIGHT7 = 1.0 / 128.0; const float SHIFT_RIGHT5 = 1.0 / 32.0; @@ -36,57 +48,6 @@ const float SHIFT_RIGHT3 = 1.0 / 8.0; const float SHIFT_RIGHT2 = 1.0 / 4.0; const float SHIFT_RIGHT1 = 1.0 / 2.0; -vec4 addScreenSpaceOffset(vec4 positionEC, vec2 imageSize, float scale, vec2 direction, vec2 origin, vec2 translate, vec2 pixelOffset, vec3 alignedAxis, bool validAlignedAxis, float rotation, bool sizeInMeters) -{ - // Note the halfSize cannot be computed in JavaScript because it is sent via - // compressed vertex attributes that coerce it to an integer. - vec2 halfSize = imageSize * scale * czm_resolutionScale * 0.5; - halfSize *= ((direction * 2.0) - 1.0); - - vec2 originTranslate = origin * abs(halfSize); - -#if defined(ROTATION) || defined(ALIGNED_AXIS) - if (validAlignedAxis || rotation != 0.0) - { - float angle = rotation; - if (validAlignedAxis) - { - vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0); - angle += sign(-projectedAlignedAxis.x) * acos( sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) / - (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y) ); - } - - float cosTheta = cos(angle); - float sinTheta = sin(angle); - mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta); - halfSize = rotationMatrix * halfSize; - } -#endif - - if (sizeInMeters) - { - positionEC.xy += halfSize; - } - - float mpp = czm_metersPerPixel(positionEC); - - if (!sizeInMeters) - { - originTranslate *= mpp; - } - - positionEC.xy += originTranslate; - if (!sizeInMeters) - { - positionEC.xy += halfSize * mpp; - } - - positionEC.xy += translate * mpp; - positionEC.xy += (pixelOffset * czm_resolutionScale) * mpp; - - return positionEC; -} - void main() { // Modifying this shader may also require modifications to Billboard._computeScreenSpacePosition @@ -116,6 +77,9 @@ void main() origin.y = floor(compressed * SHIFT_RIGHT3); compressed -= origin.y * SHIFT_LEFT3; +#ifdef CLAMP_TO_GROUND + vec2 depthOrigin = origin.xy; +#endif origin -= vec2(1.0); float show = floor(compressed * SHIFT_RIGHT2); @@ -146,8 +110,28 @@ void main() translate.y -= UPPER_BOUND; temp = compressedAttribute1.x * SHIFT_RIGHT8; + float temp2 = floor(compressedAttribute2.w * SHIFT_RIGHT2); + + vec2 imageSize = vec2(floor(temp), temp2); + +#ifdef CLAMP_TO_GROUND + float labelHorizontalOrigin = floor(compressedAttribute2.w - (temp2 * SHIFT_LEFT2)); + float applyTranslate = 0.0; + if (labelHorizontalOrigin != 0.0) // is a billboard, so set apply translate to false + { + applyTranslate = 1.0; + labelHorizontalOrigin -= 2.0; + depthOrigin.x = labelHorizontalOrigin + 1.0; + } - vec2 imageSize = vec2(floor(temp), compressedAttribute2.w); + depthOrigin = vec2(1.0) - (depthOrigin * 0.5); + + temp = compressedAttribute3.w; + temp = temp * SHIFT_RIGHT12; + vec2 dimensions; + dimensions.y = (temp - floor(temp)) * SHIFT_LEFT12; + dimensions.x = floor(temp); +#endif #ifdef EYE_DISTANCE_TRANSLUCENCY vec4 translucencyByDistance; @@ -200,6 +184,12 @@ void main() vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); vec4 positionEC = czm_modelViewRelativeToEye * p; + +#ifdef CLAMP_TO_GROUND + float eyeDepth = positionEC.z; + v_rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0); +#endif + positionEC = czm_eyeOffset(positionEC, eyeOffset.xyz); positionEC.xyz *= show; @@ -246,15 +236,63 @@ void main() #endif #ifdef DISTANCE_DISPLAY_CONDITION - float nearSq = distanceDisplayConditionAndDisableDepth.x; - float farSq = distanceDisplayConditionAndDisableDepth.y; + float nearSq = compressedAttribute3.x; + float farSq = compressedAttribute3.y; if (lengthSq < nearSq || lengthSq > farSq) { positionEC.xyz = vec3(0.0); } #endif - positionEC = addScreenSpaceOffset(positionEC, imageSize, scale, direction, origin, translate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters); + // Note the halfSize cannot be computed in JavaScript because it is sent via + // compressed vertex attributes that coerce it to an integer. + vec2 halfSize = imageSize * scale * czm_resolutionScale * 0.5; + halfSize *= ((direction * 2.0) - 1.0); + + vec2 originTranslate = origin * abs(halfSize); + +#if defined(ROTATION) || defined(ALIGNED_AXIS) + if (validAlignedAxis || rotation != 0.0) + { + float angle = rotation; + if (validAlignedAxis) + { + vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0); + angle += sign(-projectedAlignedAxis.x) * acos( sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) / + (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y) ); + } + + float cosTheta = cos(angle); + float sinTheta = sin(angle); + mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta); + halfSize = rotationMatrix * halfSize; + + #ifdef CLAMP_TO_GROUND + v_rotationMatrix = rotationMatrix; + #endif + } +#endif + + if (sizeInMeters) + { + positionEC.xy += halfSize; + } + + float mpp = czm_metersPerPixel(positionEC); + + if (!sizeInMeters) + { + originTranslate *= mpp; + } + + positionEC.xy += originTranslate; + if (!sizeInMeters) + { + positionEC.xy += halfSize * mpp; + } + + positionEC.xy += translate * mpp; + positionEC.xy += (pixelOffset * czm_resolutionScale) * mpp; gl_Position = czm_projection * positionEC; v_textureCoordinates = textureCoordinates; @@ -263,7 +301,8 @@ void main() #endif #ifdef DISABLE_DEPTH_DISTANCE - float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z; + float disableDepthTestDistance = compressedAttribute3.z; + if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) { disableDepthTestDistance = czm_minimumDisableDepthTestDistance; @@ -285,6 +324,22 @@ void main() } #endif +#ifdef CLAMP_TO_GROUND + if (sizeInMeters) { + translate /= mpp; + dimensions /= mpp; + imageSize /= mpp; + } + v_eyeDepthDistanceAndApplyTranslate.x = eyeDepth; + v_eyeDepthDistanceAndApplyTranslate.y = disableDepthTestDistance; + v_eyeDepthDistanceAndApplyTranslate.z = applyTranslate; + v_originTextureCoordinateAndTranslate.xy = depthOrigin; + v_originTextureCoordinateAndTranslate.zw = translate; + v_textureCoordinateBounds = textureCoordinateBounds; + v_dimensionsAndImageSize.xy = dimensions * scale; + v_dimensionsAndImageSize.zw = imageSize * scale; +#endif + v_pickColor = pickColor; v_color = color;