You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
6.9 KiB
GLSL
177 lines
6.9 KiB
GLSL
void clipLineSegmentToNearPlane(
|
|
vec3 p0,
|
|
vec3 p1,
|
|
out vec4 positionWC,
|
|
out bool clipped,
|
|
out bool culledByNearPlane,
|
|
out vec4 clippedPositionEC)
|
|
{
|
|
culledByNearPlane = false;
|
|
clipped = false;
|
|
|
|
vec3 p0ToP1 = p1 - p0;
|
|
float magnitude = length(p0ToP1);
|
|
vec3 direction = normalize(p0ToP1);
|
|
|
|
// Distance that p0 is behind the near plane. Negative means p0 is
|
|
// in front of the near plane.
|
|
float endPoint0Distance = czm_currentFrustum.x + p0.z;
|
|
|
|
// Camera looks down -Z.
|
|
// When moving a point along +Z: LESS VISIBLE
|
|
// * Points in front of the camera move closer to the camera.
|
|
// * Points behind the camrea move farther away from the camera.
|
|
// When moving a point along -Z: MORE VISIBLE
|
|
// * Points in front of the camera move farther away from the camera.
|
|
// * Points behind the camera move closer to the camera.
|
|
|
|
// Positive denominator: -Z, becoming more visible
|
|
// Negative denominator: +Z, becoming less visible
|
|
// Nearly zero: parallel to near plane
|
|
float denominator = -direction.z;
|
|
|
|
if (endPoint0Distance > 0.0 && abs(denominator) < czm_epsilon7)
|
|
{
|
|
// p0 is behind the near plane and the line to p1 is nearly parallel to
|
|
// the near plane, so cull the segment completely.
|
|
culledByNearPlane = true;
|
|
}
|
|
else if (endPoint0Distance > 0.0)
|
|
{
|
|
// p0 is behind the near plane, and the line to p1 is moving distinctly
|
|
// toward or away from it.
|
|
|
|
// t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction)
|
|
float t = endPoint0Distance / denominator;
|
|
if (t < 0.0 || t > magnitude)
|
|
{
|
|
// Near plane intersection is not between the two points.
|
|
// We already confirmed p0 is behind the naer plane, so now
|
|
// we know the entire segment is behind it.
|
|
culledByNearPlane = true;
|
|
}
|
|
else
|
|
{
|
|
// Segment crosses the near plane, update p0 to lie exactly on it.
|
|
p0 = p0 + t * direction;
|
|
|
|
// Numerical noise might put us a bit on the wrong side of the near plane.
|
|
// Don't let that happen.
|
|
p0.z = min(p0.z, -czm_currentFrustum.x);
|
|
|
|
clipped = true;
|
|
}
|
|
}
|
|
|
|
clippedPositionEC = vec4(p0, 1.0);
|
|
positionWC = czm_eyeToWindowCoordinates(clippedPositionEC);
|
|
}
|
|
|
|
vec4 getPolylineWindowCoordinatesEC(vec4 positionEC, vec4 prevEC, vec4 nextEC, float expandDirection, float width, bool usePrevious, out float angle)
|
|
{
|
|
// expandDirection +1 is to the _left_ when looking from positionEC toward nextEC.
|
|
|
|
#ifdef POLYLINE_DASH
|
|
// Compute the window coordinates of the points.
|
|
vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC);
|
|
vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC);
|
|
vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC);
|
|
|
|
// Determine the relative screen space direction of the line.
|
|
vec2 lineDir;
|
|
if (usePrevious) {
|
|
lineDir = normalize(positionWindow.xy - previousWindow.xy);
|
|
}
|
|
else {
|
|
lineDir = normalize(nextWindow.xy - positionWindow.xy);
|
|
}
|
|
angle = atan(lineDir.x, lineDir.y) - 1.570796327; // precomputed atan(1,0)
|
|
|
|
// Quantize the angle so it doesn't change rapidly between segments.
|
|
angle = floor(angle / czm_piOverFour + 0.5) * czm_piOverFour;
|
|
#endif
|
|
|
|
vec4 clippedPrevWC, clippedPrevEC;
|
|
bool prevSegmentClipped, prevSegmentCulled;
|
|
clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, clippedPrevWC, prevSegmentClipped, prevSegmentCulled, clippedPrevEC);
|
|
|
|
vec4 clippedNextWC, clippedNextEC;
|
|
bool nextSegmentClipped, nextSegmentCulled;
|
|
clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, clippedNextWC, nextSegmentClipped, nextSegmentCulled, clippedNextEC);
|
|
|
|
bool segmentClipped, segmentCulled;
|
|
vec4 clippedPositionWC, clippedPositionEC;
|
|
clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, clippedPositionWC, segmentClipped, segmentCulled, clippedPositionEC);
|
|
|
|
if (segmentCulled)
|
|
{
|
|
return vec4(0.0, 0.0, 0.0, 1.0);
|
|
}
|
|
|
|
vec2 directionToPrevWC = normalize(clippedPrevWC.xy - clippedPositionWC.xy);
|
|
vec2 directionToNextWC = normalize(clippedNextWC.xy - clippedPositionWC.xy);
|
|
|
|
// If a segment was culled, we can't use the corresponding direction
|
|
// computed above. We should never see both of these be true without
|
|
// `segmentCulled` above also being true.
|
|
if (prevSegmentCulled)
|
|
{
|
|
directionToPrevWC = -directionToNextWC;
|
|
}
|
|
else if (nextSegmentCulled)
|
|
{
|
|
directionToNextWC = -directionToPrevWC;
|
|
}
|
|
|
|
vec2 thisSegmentForwardWC, otherSegmentForwardWC;
|
|
if (usePrevious)
|
|
{
|
|
thisSegmentForwardWC = -directionToPrevWC;
|
|
otherSegmentForwardWC = directionToNextWC;
|
|
}
|
|
else
|
|
{
|
|
thisSegmentForwardWC = directionToNextWC;
|
|
otherSegmentForwardWC = -directionToPrevWC;
|
|
}
|
|
|
|
vec2 thisSegmentLeftWC = vec2(-thisSegmentForwardWC.y, thisSegmentForwardWC.x);
|
|
|
|
vec2 leftWC = thisSegmentLeftWC;
|
|
float expandWidth = width * 0.5;
|
|
|
|
// When lines are split at the anti-meridian, the position may be at the
|
|
// same location as the next or previous position, and we need to handle
|
|
// that to avoid producing NaNs.
|
|
if (!czm_equalsEpsilon(prevEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1) && !czm_equalsEpsilon(nextEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1))
|
|
{
|
|
vec2 otherSegmentLeftWC = vec2(-otherSegmentForwardWC.y, otherSegmentForwardWC.x);
|
|
|
|
vec2 leftSumWC = thisSegmentLeftWC + otherSegmentLeftWC;
|
|
float leftSumLength = length(leftSumWC);
|
|
leftWC = leftSumLength < czm_epsilon6 ? thisSegmentLeftWC : (leftSumWC / leftSumLength);
|
|
|
|
// The sine of the angle between the two vectors is given by the formula
|
|
// |a x b| = |a||b|sin(theta)
|
|
// which is
|
|
// float sinAngle = length(cross(vec3(leftWC, 0.0), vec3(-thisSegmentForwardWC, 0.0)));
|
|
// Because the z components of both vectors are zero, the x and y coordinate will be zero.
|
|
// Therefore, the sine of the angle is just the z component of the cross product.
|
|
vec2 u = -thisSegmentForwardWC;
|
|
vec2 v = leftWC;
|
|
float sinAngle = abs(u.x * v.y - u.y * v.x);
|
|
expandWidth = clamp(expandWidth / sinAngle, 0.0, width * 2.0);
|
|
}
|
|
|
|
vec2 offset = leftWC * expandDirection * expandWidth * czm_pixelRatio;
|
|
return vec4(clippedPositionWC.xy + offset, -clippedPositionWC.z, 1.0) * (czm_projection * clippedPositionEC).w;
|
|
}
|
|
|
|
vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)
|
|
{
|
|
vec4 positionEC = czm_modelViewRelativeToEye * position;
|
|
vec4 prevEC = czm_modelViewRelativeToEye * previous;
|
|
vec4 nextEC = czm_modelViewRelativeToEye * next;
|
|
return getPolylineWindowCoordinatesEC(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, angle);
|
|
}
|