diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableLines.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableLines.kt index 65bebcded..f80a74dce 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableLines.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableLines.kt @@ -71,6 +71,9 @@ open class DrawableLines protected constructor(): Drawable { // Use the leader's line width in screen pixels. program.loadLineWidth(lineWidth) + // Set cutoff to 10 to avoid possible cutoff at small values of miter length + program.loadMiterLengthCutoff(10f) + // Disable depth testing if requested. if (!enableDepthTest) dc.gl.disable(GL_DEPTH_TEST) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableShape.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableShape.kt index 824b20c88..f67c7fe39 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableShape.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableShape.kt @@ -41,8 +41,10 @@ open class DrawableShape protected constructor(): Drawable { // Use the draw context's modelview projection matrix, transformed to shape local coordinates. if (drawState.depthOffset != 0.0) { mvpMatrix.copy(dc.projection).offsetProjectionDepth(drawState.depthOffset) + program.loadClipDistance((mvpMatrix.m[11] / (mvpMatrix.m[10] - 1.0)).toFloat() / 2.0f) // nearPlane / 2.0f mvpMatrix.multiplyByMatrix(dc.modelview) } else { + program.loadClipDistance((dc.projection.m[11] / (dc.projection.m[10] - 1.0)).toFloat() / 2.0f) // nearPlane / 2.0f mvpMatrix.copy(dc.modelviewProjection) } mvpMatrix.multiplyByTranslation( @@ -73,9 +75,9 @@ open class DrawableShape protected constructor(): Drawable { program.enableOneVertexMode(false) program.loadScreen(dc.viewport.width.toFloat(), dc.viewport.height.toFloat()) dc.gl.vertexAttribPointer(0 /*pointA*/, 4, GL_FLOAT, false, 20, 0) - dc.gl.vertexAttribPointer(1 /*pointB*/, 4, GL_FLOAT, false, 20, 40) - dc.gl.vertexAttribPointer(2 /*pointC*/, 4, GL_FLOAT, false, 20, 80) - dc.gl.vertexAttribPointer(3 /*texCoord*/, 1, GL_FLOAT, false, 20, 56) + dc.gl.vertexAttribPointer(1 /*pointB*/, 4, GL_FLOAT, false, 20, 80) + dc.gl.vertexAttribPointer(2 /*pointC*/, 4, GL_FLOAT, false, 20, 160) + dc.gl.vertexAttribPointer(3 /*texCoord*/, 1, GL_FLOAT, false, 20, 96) } else { program.enableOneVertexMode(true) dc.gl.vertexAttribPointer(0 /*vertexPoint*/, 3, GL_FLOAT, false, drawState.vertexStride, 0) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableSurfaceShape.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableSurfaceShape.kt index 0022407bd..6539c5ebd 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableSurfaceShape.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawableSurfaceShape.kt @@ -120,6 +120,8 @@ open class DrawableSurfaceShape protected constructor(): Drawable { -terrainSector.minLatitude.inDegrees, 0.0 ) + program.loadClipDistance((textureMvpMatrix.m[11] / (textureMvpMatrix.m[10] - 1.0)).toFloat() / 2.0f) // set value here, but matrix is orthographic and shader clipping won't work as vertices projected orthographically always have .w == 1 + program.loadScreen(colorAttachment.width.toFloat(), colorAttachment.height.toFloat()) for (element in scratchList) { // Get the shape. val shape = element as DrawableSurfaceShape @@ -135,17 +137,13 @@ open class DrawableSurfaceShape protected constructor(): Drawable { shape.drawState.vertexOrigin.z ) program.loadModelviewProjection(mvpMatrix) + program.enableOneVertexMode(!shape.drawState.isLine) if (shape.drawState.isLine) { - program.enableOneVertexMode(false) - program.loadScreen(colorAttachment.width.toFloat(), colorAttachment.height.toFloat()) - dc.gl.vertexAttribPointer(0 /*pointA*/, 4, GL_FLOAT, false, 20, 0) - dc.gl.vertexAttribPointer(1 /*pointB*/, 4, GL_FLOAT, false, 20, 40) - dc.gl.vertexAttribPointer(2 /*pointC*/, 4, GL_FLOAT, false, 20, 80) - dc.gl.vertexAttribPointer(3 /*vertexTexCoord*/, 1, GL_FLOAT, false, 20, 56) + dc.gl.vertexAttribPointer(1 /*pointB*/, 4, GL_FLOAT, false, 20, 80) + dc.gl.vertexAttribPointer(2 /*pointC*/, 4, GL_FLOAT, false, 20, 160) + dc.gl.vertexAttribPointer(3 /*vertexTexCoord*/, 1, GL_FLOAT, false, 20, 96) } else { - program.enableOneVertexMode(true) - // Use the shape's vertex point attribute. dc.gl.vertexAttribPointer(0 /*vertexPoint*/, 3, GL_FLOAT, false, shape.drawState.vertexStride, 0) dc.gl.vertexAttribPointer(1 /*vertexPoint*/, 3, GL_FLOAT, false, shape.drawState.vertexStride, 0) @@ -210,12 +208,14 @@ open class DrawableSurfaceShape protected constructor(): Drawable { program.loadTexCoordMatrix(identityMatrix3) program.loadColor(color) program.loadOpacity(opacity) + program.loadScreen(dc.viewport.width.toFloat(), dc.viewport.height.toFloat()) // Use the draw context's modelview projection matrix, transformed to terrain local coordinates. val terrainOrigin = terrain.vertexOrigin mvpMatrix.copy(dc.modelviewProjection) mvpMatrix.multiplyByTranslation(terrainOrigin.x, terrainOrigin.y, terrainOrigin.z) program.loadModelviewProjection(mvpMatrix) + program.loadClipDistance(0.0f) // Draw the terrain as triangles. terrain.drawTriangles(dc) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/AbstractShaderProgram.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/AbstractShaderProgram.kt index 3b3959019..c0cf11719 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/AbstractShaderProgram.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/AbstractShaderProgram.kt @@ -79,7 +79,7 @@ abstract class AbstractShaderProgram: RenderResource { dc.gl.shaderSource(fs, programSources[FRAGMENT_SHADER]) dc.gl.compileShader(fs) - if (dc.gl.getShaderParameteri(vs, GL_COMPILE_STATUS) != GL_TRUE) { + if (dc.gl.getShaderParameteri(fs, GL_COMPILE_STATUS) != GL_TRUE) { val msg = dc.gl.getShaderInfoLog(fs) dc.gl.deleteShader(vs) dc.gl.deleteShader(fs) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/TriangleShaderProgram.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/TriangleShaderProgram.kt index 692ef5da8..a75542894 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/TriangleShaderProgram.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/program/TriangleShaderProgram.kt @@ -11,11 +11,12 @@ open class TriangleShaderProgram : AbstractShaderProgram() { """ uniform mat4 mvpMatrix; uniform float lineWidth; - uniform float invMiterLengthCutoff; - uniform vec2 screen; + uniform vec2 miterLengthCutoff; + uniform vec4 screen; uniform bool enableTexture; uniform bool enableOneVertexMode; uniform mat3 texCoordMatrix; + uniform float clipDistance; attribute vec4 pointA; attribute vec4 pointB; @@ -27,41 +28,65 @@ open class TriangleShaderProgram : AbstractShaderProgram() { void main() { if (enableOneVertexMode) { /* Transform the vertex position by the modelview-projection matrix. */ - gl_Position = mvpMatrix * vec4(pointA.xyz, 1.0); + gl_Position = mvpMatrix * pointA; } else { /* Transform the vertex position by the modelview-projection matrix. */ vec4 pointAScreen = mvpMatrix * vec4(pointA.xyz, 1); vec4 pointBScreen = mvpMatrix * vec4(pointB.xyz, 1); vec4 pointCScreen = mvpMatrix * vec4(pointC.xyz, 1); - float corner = pointB.w; + vec4 interpolationPoint = pointB.w < 0.0 ? pointAScreen : pointCScreen; // not a mistake, this should be assigned here - pointAScreen = pointAScreen / pointAScreen.w; - pointBScreen = pointBScreen / pointBScreen.w; - pointCScreen = pointCScreen / pointCScreen.w; + if (pointBScreen.w < 0.0) { + pointBScreen = mix(pointBScreen, interpolationPoint, clamp((clipDistance - pointBScreen.w)/(interpolationPoint.w - pointBScreen.w), 0.0, 1.0)); + if (pointB.w < 0.0) { + pointCScreen = pointBScreen; + } else { + pointAScreen = pointBScreen; + } + } + + if (pointAScreen.w < 0.0) { + pointAScreen = mix(pointAScreen, pointBScreen, clamp((clipDistance - pointAScreen.w)/(pointBScreen.w - pointAScreen.w), 0.0, 1.0)); + } + + if (pointCScreen.w < 0.0) { + pointCScreen = mix(pointCScreen, pointBScreen, clamp((clipDistance - pointCScreen.w)/(pointBScreen.w - pointCScreen.w), 0.0, 1.0)); + } - vec2 eps = vec2(2.0 / screen.x, 2.0 / screen.y); - eps *= 0.1; + pointAScreen.xy = pointAScreen.xy / pointAScreen.w; + pointBScreen.xy = pointBScreen.xy / pointBScreen.w; + pointCScreen.xy = pointCScreen.xy / pointCScreen.w; - if (all(lessThanEqual(abs(pointBScreen.xy - pointAScreen.xy), eps))) { + float eps = 0.2 * length(screen.zw); + + if (length(pointBScreen.xy - pointAScreen.xy) < eps) { pointAScreen.xy = pointBScreen.xy + normalize(pointBScreen.xy - pointCScreen.xy); } - if (all(lessThanEqual(abs(pointBScreen.xy - pointCScreen.xy), eps))) { + if (length(pointBScreen.xy - pointCScreen.xy) < eps) { pointCScreen.xy = pointBScreen.xy + normalize(pointBScreen.xy - pointAScreen.xy); } - if (all(lessThanEqual(abs(pointAScreen.xy - pointCScreen.xy), eps))) { + if (length(pointAScreen.xy - pointCScreen.xy) < eps) { pointCScreen.xy = pointBScreen.xy + normalize(pointBScreen.xy - pointAScreen.xy); } - vec2 AB = normalize(normalize(pointBScreen.xy - pointAScreen.xy) * screen); - vec2 BC = normalize(normalize(pointCScreen.xy - pointBScreen.xy) * screen); + vec2 AB = normalize((pointBScreen.xy - pointAScreen.xy) * screen.xy); + vec2 BC = normalize((pointCScreen.xy - pointBScreen.xy) * screen.xy); vec2 tangent = normalize(AB + BC); + vec2 point = normalize(AB - BC); vec2 miter = vec2(-tangent.y, tangent.x); vec2 normalA = vec2(-AB.y, AB.x); - float miterLength = 1.0 / max(dot(miter, normalA), invMiterLengthCutoff); + float miterLength = 1.0 / max(dot(miter, normalA), miterLengthCutoff.y); - gl_Position = pointBScreen; - gl_Position.xy = gl_Position.xy + (corner * miter * lineWidth * miterLength) / screen.xy; + float cornerX = sign(pointB.w); + float cornerY = (pointB.w - cornerX) * 2.0; + if (abs(miterLength - miterLengthCutoff.x) < eps && cornerY * dot(miter, point) > 0.0) { + // trim the corner + gl_Position.xy = pointBScreen.w * (pointBScreen.xy - (cornerX * cornerY * lineWidth * normalA) * screen.zw); + } else { + gl_Position.xy = pointBScreen.w * (pointBScreen.xy + (cornerY * miter * lineWidth * miterLength) * screen.zw); + } + gl_Position.zw = pointBScreen.zw; } /* Transform the vertex tex coord by the tex coord matrix. */ @@ -106,21 +131,23 @@ open class TriangleShaderProgram : AbstractShaderProgram() { protected val color = Color() protected var opacity = 1.0f protected var lineWidth = 1.0f - protected var invMiterLengthCutoff = 1.0f - protected var screenX = 0.0f - protected var screenY = 0.0f + protected var miterLengthCutoff = 2.0f // should be greater than 1.0 + protected var screenX = 1.0f + protected var screenY = 1.0f + protected var clipDistance = 0.0f protected var mvpMatrixId = KglUniformLocation.NONE protected var colorId = KglUniformLocation.NONE protected var opacityId = KglUniformLocation.NONE protected var lineWidthId = KglUniformLocation.NONE - protected var invMiterLengthCutoffId = KglUniformLocation.NONE + protected var miterLengthCutoffId = KglUniformLocation.NONE protected var screenId = KglUniformLocation.NONE protected var enablePickModeId = KglUniformLocation.NONE protected var enableTextureId = KglUniformLocation.NONE protected var enableOneVertexModeId = KglUniformLocation.NONE protected var texCoordMatrixId = KglUniformLocation.NONE protected var texSamplerId = KglUniformLocation.NONE + protected var clipDistanceId = KglUniformLocation.NONE private val array = FloatArray(16) override fun initProgram(dc: DrawContext) { @@ -136,10 +163,12 @@ open class TriangleShaderProgram : AbstractShaderProgram() { gl.uniform1f(opacityId, opacity) lineWidthId = gl.getUniformLocation(program, "lineWidth") gl.uniform1f(lineWidthId, lineWidth) - invMiterLengthCutoffId = gl.getUniformLocation(program, "invMiterLengthCutoff") - gl.uniform1f(invMiterLengthCutoffId, invMiterLengthCutoff) + miterLengthCutoffId = gl.getUniformLocation(program, "miterLengthCutoff") + gl.uniform2f(miterLengthCutoffId, miterLengthCutoff, 1f / miterLengthCutoff) screenId = gl.getUniformLocation(program, "screen") - gl.uniform2f(screenId, screenX, screenY) + gl.uniform4f(screenId, screenX, screenY, 1f / screenX, 1f / screenY) + clipDistanceId = gl.getUniformLocation(program, "clipDistance") + gl.uniform1f(clipDistanceId, clipDistance) enablePickModeId = gl.getUniformLocation(program, "enablePickMode") gl.uniform1i(enablePickModeId, if (enablePickMode) 1 else 0) @@ -209,9 +238,16 @@ open class TriangleShaderProgram : AbstractShaderProgram() { } fun loadMiterLengthCutoff(miterLengthCutoff : Float) { - if (this.invMiterLengthCutoff != 1.0f / miterLengthCutoff) { - this.invMiterLengthCutoff = 1.0f / miterLengthCutoff - gl.uniform1f(invMiterLengthCutoffId, invMiterLengthCutoff) + if (this.miterLengthCutoff != miterLengthCutoff) { + this.miterLengthCutoff = miterLengthCutoff + gl.uniform2f(miterLengthCutoffId, miterLengthCutoff, 1f / miterLengthCutoff) + } + } + + fun loadClipDistance(clipDistance : Float) { + if (this.clipDistance != clipDistance) { + this.clipDistance = clipDistance + gl.uniform1f(clipDistanceId, clipDistance) } } @@ -219,7 +255,7 @@ open class TriangleShaderProgram : AbstractShaderProgram() { if ((this.screenX != screenX) and (this.screenY != screenY) ) { this.screenX = screenX this.screenY = screenY - gl.uniform2f(screenId, this.screenX, this.screenY) + gl.uniform4f(screenId, this.screenX, this.screenY, 1f / screenX, 1f / screenY) } } } \ No newline at end of file diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Ellipse.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Ellipse.kt index f0448ca32..7053e5a50 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Ellipse.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Ellipse.kt @@ -18,6 +18,7 @@ import earth.worldwind.render.program.TriangleShaderProgram import earth.worldwind.util.Logger.ERROR import earth.worldwind.util.Logger.logMessage import earth.worldwind.util.kgl.* +import earth.worldwind.util.math.encodeOrientationVector import kotlin.jvm.JvmOverloads import kotlin.math.* @@ -163,7 +164,8 @@ open class Ellipse @JvmOverloads constructor( companion object { protected const val VERTEX_STRIDE = 5 - protected const val LINE_VERTEX_STRIDE = 10 + protected const val OUTLINE_LINE_SEGMENT_STRIDE = 4 * VERTEX_STRIDE + protected const val VERTICAL_LINE_SEGMENT_STRIDE = 4 * OUTLINE_LINE_SEGMENT_STRIDE // 4 points per 4 vertices per vertical line /** * The minimum number of intervals that will be used for geometry generation. */ @@ -427,9 +429,9 @@ open class Ellipse @JvmOverloads constructor( else FloatArray((activeIntervals + spineCount) * VERTEX_STRIDE) lineVertexIndex = 0 - lineVertexArray = if (isExtrude && !isSurfaceShape) FloatArray((activeIntervals + 3 + (activeIntervals + 1) * 4) * LINE_VERTEX_STRIDE) - else FloatArray((activeIntervals + 3) * LINE_VERTEX_STRIDE) - verticalVertexIndex = (activeIntervals + 3) * LINE_VERTEX_STRIDE + verticalVertexIndex = (activeIntervals + 3) * OUTLINE_LINE_SEGMENT_STRIDE + lineVertexArray = if (isExtrude && !isSurfaceShape) FloatArray((activeIntervals + 3) * OUTLINE_LINE_SEGMENT_STRIDE + (activeIntervals + 1) * VERTICAL_LINE_SEGMENT_STRIDE) + else FloatArray((activeIntervals + 3) * OUTLINE_LINE_SEGMENT_STRIDE) verticalElements.clear() outlineElements.clear() @@ -478,11 +480,11 @@ open class Ellipse @JvmOverloads constructor( firstLoc = Position(loc.latitude, loc.longitude, center.altitude) addLineVertex(rc, loc.latitude, loc.longitude, center.altitude, verticalVertexIndex, true) } - addLineVertex(rc, loc.latitude, loc.longitude, center.altitude, verticalVertexIndex, false) + addLineVertex(rc, loc.latitude, loc.longitude, center.altitude, verticalVertexIndex, true) } addLineVertex(rc, firstLoc.latitude, firstLoc.longitude, firstLoc.altitude, verticalVertexIndex, false) - addLineVertex(rc, firstLoc.latitude, firstLoc.longitude, firstLoc.altitude, verticalVertexIndex, true) + addLineVertex(rc, firstLoc.latitude, firstLoc.longitude, firstLoc.altitude, verticalVertexIndex, false) // Add the interior spine point vertices for (i in 0 until spineCount) { @@ -503,101 +505,188 @@ open class Ellipse @JvmOverloads constructor( } } protected open fun addLineVertex( - rc: RenderContext, latitude: Angle, longitude: Angle, altitude: Double, offset : Int, firstOrLast : Boolean + rc: RenderContext, latitude: Angle, longitude: Angle, altitude: Double, offset : Int, addIndices : Boolean ) { - val vertex = (lineVertexIndex / LINE_VERTEX_STRIDE - 1) * 2 + val vertex = lineVertexIndex / VERTEX_STRIDE val point = rc.geographicToCartesian(latitude, longitude, altitude, altitudeMode, scratchPoint) if (lineVertexIndex == 0) texCoord1d = 0.0 else texCoord1d += point.distanceTo(prevPoint) prevPoint.copy(point) + val upperLeftCorner = encodeOrientationVector(-1f, 1f) + val lowerLeftCorner = encodeOrientationVector(-1f, -1f) + val upperRightCorner = encodeOrientationVector(1f, 1f) + val lowerRightCorner = encodeOrientationVector(1f, -1f) if (isSurfaceShape) { lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = 1.0f + lineVertexArray[lineVertexIndex++] = upperLeftCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = lowerLeftCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = -1.0f + lineVertexArray[lineVertexIndex++] = upperRightCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() - if (!firstOrLast) { + + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = lowerRightCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + if (addIndices) { outlineElements.add(vertex) - outlineElements.add(vertex.inc()) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) } } else { lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = 1.0f + lineVertexArray[lineVertexIndex++] = upperLeftCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = -1.0f + lineVertexArray[lineVertexIndex++] = lowerLeftCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() - if (!firstOrLast) { + + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = upperRightCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = lowerRightCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + if (addIndices) { outlineElements.add(vertex) - outlineElements.add(vertex.inc()) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) } - if (isExtrude && !firstOrLast) { + if (isExtrude && addIndices) { val vertPoint = rc.geographicToCartesian(latitude, longitude, 0.0, altitudeMode, scratchVertPoint) - val index = verticalVertexIndex / LINE_VERTEX_STRIDE * 2 + val index = verticalVertexIndex / VERTEX_STRIDE + + // first vertices, that simulate pointA for next vertices + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + // first pointB lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + // second pointB lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f - lineVertexArray[verticalVertexIndex++] = 0f + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f - verticalElements.add(index) - verticalElements.add(index + 1) - verticalElements.add(index + 2) + // last vertices, that simulate pointC for previous vertices + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + // indices for triangles from firstPointB secondPointB verticalElements.add(index + 2) - verticalElements.add(index + 1) verticalElements.add(index + 3) + verticalElements.add(index + 4) + verticalElements.add(index + 4) + verticalElements.add(index + 3) + verticalElements.add(index + 5) } } } @@ -608,7 +697,6 @@ open class Ellipse @JvmOverloads constructor( var offsetVertexIndex = vertexIndex + offset var point = rc.geographicToCartesian(latitude, longitude, altitude, altitudeMode, scratchPoint) val texCoord2d = texCoord2d.copy(point).multiplyByMatrix(modelToTexCoord) - prevPoint.copy(point) if (isSurfaceShape) { vertexArray[vertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt index aff5c9ed8..33510e0e8 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt @@ -14,6 +14,7 @@ import earth.worldwind.render.image.WrapMode import earth.worldwind.render.program.TriangleShaderProgram import earth.worldwind.shape.PathType.* import earth.worldwind.util.kgl.* +import earth.worldwind.util.math.encodeOrientationVector import kotlin.jvm.JvmOverloads open class Path @JvmOverloads constructor( @@ -27,11 +28,14 @@ open class Path @JvmOverloads constructor( protected var vertexArray = FloatArray(0) protected var vertexIndex = 0 protected var verticalIndex = 0 + protected var extrudeVertexArray = FloatArray(0) protected var extrudeIndex = 0 // TODO Use ShortArray instead of mutableListOf to avoid unnecessary memory re-allocations protected val interiorElements = mutableListOf() protected val outlineElements = mutableListOf() protected val verticalElements = mutableListOf() + protected lateinit var extrudeVertexBufferKey: Any + protected lateinit var extrudeElementBufferKey: Any protected lateinit var vertexBufferKey: Any protected lateinit var elementBufferKey: Any protected val vertexOrigin = Vec3() @@ -43,7 +47,10 @@ open class Path @JvmOverloads constructor( private val intermediateLocation = Location() companion object { - protected const val VERTEX_STRIDE = 10 + protected const val VERTEX_STRIDE = 5 // 5 floats + protected const val EXTRUDE_SEGMENT_STRIDE = 2 * VERTEX_STRIDE // 2 vertices + protected const val OUTLINE_SEGMENT_STRIDE = 4 * VERTEX_STRIDE // 4 vertices + protected const val VERTICAL_SEGMENT_STRIDE = 4 * OUTLINE_SEGMENT_STRIDE // 4 points per 4 vertices per vertical line protected val defaultOutlineImageOptions = ImageOptions().apply { resamplingMode = ResamplingMode.NEAREST_NEIGHBOR wrapMode = WrapMode.REPEAT @@ -55,6 +62,7 @@ open class Path @JvmOverloads constructor( override fun reset() { super.reset() vertexArray = FloatArray(0) + extrudeVertexArray = FloatArray(0) interiorElements.clear() outlineElements.clear() verticalElements.clear() @@ -67,6 +75,8 @@ open class Path @JvmOverloads constructor( assembleGeometry(rc) vertexBufferKey = nextCacheKey() elementBufferKey = nextCacheKey() + extrudeVertexBufferKey = nextCacheKey() + extrudeElementBufferKey = nextCacheKey() } // Obtain a drawable form the render context pool, and compute distance to the render camera. @@ -84,7 +94,7 @@ open class Path @JvmOverloads constructor( val pool = rc.getDrawablePool() drawable = DrawableShape.obtain(pool) drawState = drawable.drawState - cameraDistance = cameraDistanceCartesian(rc, vertexArray, vertexArray.size, VERTEX_STRIDE, vertexOrigin) + cameraDistance = cameraDistanceCartesian(rc, vertexArray, vertexArray.size, OUTLINE_SEGMENT_STRIDE, vertexOrigin) } // Use triangles mode to draw lines @@ -100,9 +110,8 @@ open class Path @JvmOverloads constructor( // Assemble the drawable's OpenGL element buffer object. drawState.elementBuffer = rc.getBufferObject(elementBufferKey) { - val array = IntArray(interiorElements.size + outlineElements.size + verticalElements.size) + val array = IntArray(outlineElements.size + verticalElements.size) var index = 0 - for (element in interiorElements) array[index++] = element for (element in outlineElements) array[index++] = element for (element in verticalElements) array[index++] = element IntBufferObject(GL_ELEMENT_ARRAY_BUFFER, array) @@ -128,7 +137,7 @@ open class Path @JvmOverloads constructor( drawState.lineWidth(activeAttributes.outlineWidth + if (isSurfaceShape) 0.5f else 0f) drawState.drawElements( GL_TRIANGLE_STRIP, outlineElements.size, - GL_UNSIGNED_INT, interiorElements.size * Int.SIZE_BYTES + GL_UNSIGNED_INT, 0 ) } @@ -142,17 +151,7 @@ open class Path @JvmOverloads constructor( drawState.lineWidth(activeAttributes.outlineWidth) drawState.drawElements( GL_TRIANGLES, verticalElements.size, - GL_UNSIGNED_INT, (interiorElements.size + outlineElements.size) * Int.SIZE_BYTES - ) - } - - // Configure the drawable to display the shape's extruded interior. - if (activeAttributes.isDrawInterior && isExtrude) { - drawState.color(if (rc.isPickMode) pickColor else activeAttributes.interiorColor) - drawState.opacity(if (rc.isPickMode) 1f else rc.currentLayer.opacity) - drawState.drawElements( - GL_TRIANGLE_STRIP, interiorElements.size, - GL_UNSIGNED_INT, 0 + GL_UNSIGNED_INT, outlineElements.size * Int.SIZE_BYTES ) } @@ -165,6 +164,48 @@ open class Path @JvmOverloads constructor( // Enqueue the drawable for processing on the OpenGL thread. if (isSurfaceShape) rc.offerSurfaceDrawable(drawable, 0.0 /*zOrder*/) else rc.offerShapeDrawable(drawable, cameraDistance) + + // Configure the drawable to display the shape's extruded interior. + if (activeAttributes.isDrawInterior && isExtrude && !isSurfaceShape) { + val pool = rc.getDrawablePool() + val drawableExtrusion = DrawableShape.obtain(pool) + val drawStateExtrusion = drawableExtrusion.drawState + + drawStateExtrusion.isLine = false + + // Use the basic GLSL program to draw the shape. + drawStateExtrusion.program = rc.getShaderProgram { TriangleShaderProgram() } + + // Assemble the drawable's OpenGL vertex buffer object. + drawStateExtrusion.vertexBuffer = rc.getBufferObject(extrudeVertexBufferKey) { + FloatBufferObject(GL_ARRAY_BUFFER, extrudeVertexArray, extrudeVertexArray.size) + } + + // Assemble the drawable's OpenGL element buffer object. + drawStateExtrusion.elementBuffer = rc.getBufferObject(extrudeElementBufferKey) { + val array = IntArray(interiorElements.size) + var index = 0 + for (element in interiorElements) array[index++] = element + IntBufferObject(GL_ELEMENT_ARRAY_BUFFER, array) + } + + drawStateExtrusion.color(if (rc.isPickMode) pickColor else activeAttributes.interiorColor) + drawStateExtrusion.opacity(if (rc.isPickMode) 1f else rc.currentLayer.opacity) + drawStateExtrusion.drawElements( + GL_TRIANGLE_STRIP, interiorElements.size, + GL_UNSIGNED_INT, 0 + ) + + // Configure the drawable according to the shape's attributes. + drawStateExtrusion.texture(null) + drawStateExtrusion.vertexOrigin.copy(vertexOrigin) + drawStateExtrusion.vertexStride = VERTEX_STRIDE * 4 // stride in bytes + drawStateExtrusion.enableCullFace = false + drawStateExtrusion.enableDepthTest = activeAttributes.isDepthTest + drawStateExtrusion.enableDepthWrite = activeAttributes.isDepthWrite + + rc.offerShapeDrawable(drawableExtrusion, cameraDistance) + } } protected open fun mustAssembleGeometry(rc: RenderContext) = vertexArray.isEmpty() @@ -174,39 +215,42 @@ open class Path @JvmOverloads constructor( val vertexCount = if (maximumIntermediatePoints <= 0 || pathType == LINEAR) positions.size else if (positions.isNotEmpty()) positions.size + (positions.size - 1) * maximumIntermediatePoints else 0 + // Separate vertex array for interior polygon + extrudeIndex = 0; + extrudeVertexArray = if(isExtrude && !isSurfaceShape) FloatArray((vertexCount + 2) * EXTRUDE_SEGMENT_STRIDE) else FloatArray(0) + interiorElements.clear() + // Clear the shape's vertex array and element arrays. These arrays will accumulate values as the shapes's // geometry is assembled. vertexIndex = 0 - verticalIndex = if (isExtrude && !isSurfaceShape) (vertexCount + 2) * VERTEX_STRIDE else 0 - extrudeIndex = if (isExtrude && !isSurfaceShape) verticalIndex + (positions.size * 4) * VERTEX_STRIDE else 0 - vertexArray = if (isExtrude && !isSurfaceShape) FloatArray(verticalIndex + extrudeIndex + (vertexCount + 2) * VERTEX_STRIDE) - else FloatArray((vertexCount + 2) * VERTEX_STRIDE) - interiorElements.clear() + verticalIndex = if (isExtrude && !isSurfaceShape) (vertexCount + 2) * OUTLINE_SEGMENT_STRIDE else 0 + vertexArray = if (isExtrude && !isSurfaceShape) FloatArray(verticalIndex + positions.size * VERTICAL_SEGMENT_STRIDE) + else FloatArray((vertexCount + 2) * OUTLINE_SEGMENT_STRIDE) outlineElements.clear() verticalElements.clear() // Add the first vertex. var begin = positions[0] + addVertex(rc, begin.latitude, begin.longitude, begin.altitude, true /*intermediate*/, true) addVertex(rc, begin.latitude, begin.longitude, begin.altitude, false /*intermediate*/, true) - addVertex(rc, begin.latitude, begin.longitude, begin.altitude, false /*intermediate*/, false) // Add the remaining vertices, inserting vertices along each edge as indicated by the path's properties. for (idx in 1 until positions.size) { val end = positions[idx] addIntermediateVertices(rc, begin, end) - addVertex(rc, end.latitude, end.longitude, end.altitude, false /*intermediate*/, false) + addVertex(rc, end.latitude, end.longitude, end.altitude, false /*intermediate*/, idx != (positions.size - 1)) begin = end } - addVertex(rc, begin.latitude, begin.longitude, begin.altitude,false /*intermediate*/, true) + addVertex(rc, begin.latitude, begin.longitude, begin.altitude,true /*intermediate*/, false) // Compute the shape's bounding box or bounding sector from its assembled coordinates. if (isSurfaceShape) { boundingSector.setEmpty() - boundingSector.union(vertexArray, vertexIndex, VERTEX_STRIDE) + boundingSector.union(vertexArray, vertexIndex, OUTLINE_SEGMENT_STRIDE) boundingSector.translate(vertexOrigin.y /*latitude*/, vertexOrigin.x /*longitude*/) boundingBox.setToUnitBox() // Surface/geographic shape bounding box is unused } else { - boundingBox.setToPoints(vertexArray, vertexIndex, VERTEX_STRIDE) + boundingBox.setToPoints(vertexArray, vertexIndex, OUTLINE_SEGMENT_STRIDE) boundingBox.translate(vertexOrigin.x, vertexOrigin.y, vertexOrigin.z) boundingSector.setEmpty() // Cartesian shape bounding sector is unused } @@ -240,16 +284,16 @@ open class Path @JvmOverloads constructor( RHUMB_LINE -> begin.rhumbLocation(azimuth, dist, loc) else -> {} } - addVertex(rc, loc.latitude, loc.longitude, alt, true /*intermediate*/, false /*firstOrLast*/) + addVertex(rc, loc.latitude, loc.longitude, alt, true /*intermediate*/, true /*addIndices*/) dist += deltaDist alt += deltaAlt } } protected open fun addVertex( - rc: RenderContext, latitude: Angle, longitude: Angle, altitude: Double, intermediate: Boolean, firstOrLast : Boolean + rc: RenderContext, latitude: Angle, longitude: Angle, altitude: Double, intermediate: Boolean, addIndices : Boolean ) { - val vertex = (vertexIndex / VERTEX_STRIDE - 1) * 2 + val vertex = vertexIndex / VERTEX_STRIDE val point = rc.geographicToCartesian(latitude, longitude, altitude, altitudeMode, point) if (vertexIndex == 0) { if (isSurfaceShape) vertexOrigin.set(longitude.inDegrees, latitude.inDegrees, altitude) @@ -259,115 +303,201 @@ open class Path @JvmOverloads constructor( texCoord1d += point.distanceTo(prevPoint) } prevPoint.copy(point) + val upperLeftCorner = encodeOrientationVector(-1f, 1f) + val lowerLeftCorner = encodeOrientationVector(-1f, -1f) + val upperRightCorner = encodeOrientationVector(1f, 1f) + val lowerRightCorner = encodeOrientationVector(1f, -1f) if (isSurfaceShape) { vertexArray[vertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (altitude - vertexOrigin.z).toFloat() - vertexArray[vertexIndex++] = 1.0f + vertexArray[vertexIndex++] = upperLeftCorner vertexArray[vertexIndex++] = texCoord1d.toFloat() vertexArray[vertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (altitude - vertexOrigin.z).toFloat() - vertexArray[vertexIndex++] = -1.0f + vertexArray[vertexIndex++] = lowerLeftCorner vertexArray[vertexIndex++] = texCoord1d.toFloat() - if (!firstOrLast) { + + vertexArray[vertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + vertexArray[vertexIndex++] = (altitude - vertexOrigin.z).toFloat() + vertexArray[vertexIndex++] = upperRightCorner + vertexArray[vertexIndex++] = texCoord1d.toFloat() + + vertexArray[vertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + vertexArray[vertexIndex++] = (altitude - vertexOrigin.z).toFloat() + vertexArray[vertexIndex++] = lowerRightCorner + vertexArray[vertexIndex++] = texCoord1d.toFloat() + + if (addIndices) { outlineElements.add(vertex) - outlineElements.add(vertex.inc()) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) } } else { vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[vertexIndex++] = 1.0f + vertexArray[vertexIndex++] = upperLeftCorner vertexArray[vertexIndex++] = texCoord1d.toFloat() + vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[vertexIndex++] = -1.0f + vertexArray[vertexIndex++] = lowerLeftCorner vertexArray[vertexIndex++] = texCoord1d.toFloat() - if (!firstOrLast) { + + vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[vertexIndex++] = upperRightCorner + vertexArray[vertexIndex++] = texCoord1d.toFloat() + + vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[vertexIndex++] = lowerRightCorner + vertexArray[vertexIndex++] = texCoord1d.toFloat() + + if (addIndices) { outlineElements.add(vertex) - outlineElements.add(vertex.inc()) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) } if (isExtrude) { val vertPoint = rc.geographicToCartesian(latitude, longitude, 0.0, altitudeMode, verticalPoint) + val extrudeVertex = extrudeIndex / VERTEX_STRIDE + + extrudeVertexArray[extrudeIndex++] = (point.x - vertexOrigin.x).toFloat() + extrudeVertexArray[extrudeIndex++] = (point.y - vertexOrigin.y).toFloat() + extrudeVertexArray[extrudeIndex++] = (point.z - vertexOrigin.z).toFloat() + extrudeVertexArray[extrudeIndex++] = 0f /*unused*/ + extrudeVertexArray[extrudeIndex++] = 0f /*unused*/ + + extrudeVertexArray[extrudeIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + extrudeVertexArray[extrudeIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + extrudeVertexArray[extrudeIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + extrudeVertexArray[extrudeIndex++] = 0f /*unused*/ + extrudeVertexArray[extrudeIndex++] = 0f /*unused*/ + + interiorElements.add(extrudeVertex) + interiorElements.add(extrudeVertex + 1) + + if (!intermediate) { + val index = verticalIndex / VERTEX_STRIDE + + // first vertices, that simulate pointA for next vertices + vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = upperLeftCorner + vertexArray[verticalIndex++] = 0.0f - val extrudeVertex = (extrudeIndex / VERTEX_STRIDE - 1) * 2 - - vertexArray[extrudeIndex++] = (point.x - vertexOrigin.x).toFloat() - vertexArray[extrudeIndex++] = (point.y - vertexOrigin.y).toFloat() - vertexArray[extrudeIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[extrudeIndex++] = 0f - vertexArray[extrudeIndex++] = 0f + vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = lowerLeftCorner + vertexArray[verticalIndex++] = 0.0f - vertexArray[extrudeIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() - vertexArray[extrudeIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() - vertexArray[extrudeIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - vertexArray[extrudeIndex++] = 0f - vertexArray[extrudeIndex++] = 0f + vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = upperRightCorner + vertexArray[verticalIndex++] = 0.0f - if (!firstOrLast) { - interiorElements.add(extrudeVertex) - interiorElements.add(extrudeVertex.inc()) - } + vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = lowerRightCorner + vertexArray[verticalIndex++] = 0.0f - if (!intermediate && !firstOrLast) { - val index = verticalIndex / VERTEX_STRIDE * 2 + // first pointB vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = 1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = upperLeftCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = -1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = lowerLeftCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = 1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = upperRightCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (point.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = -1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = lowerRightCorner + vertexArray[verticalIndex++] = 0.0f + // second pointB vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = 1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = upperLeftCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = -1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = lowerLeftCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = 1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = upperRightCorner + vertexArray[verticalIndex++] = 0.0f vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - vertexArray[verticalIndex++] = -1f - vertexArray[verticalIndex++] = 0f + vertexArray[verticalIndex++] = lowerRightCorner + vertexArray[verticalIndex++] = 0.0f - verticalElements.add(index) - verticalElements.add(index + 1) - verticalElements.add(index + 2) + // last vertices, that simulate pointC for previous vertices + vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = upperLeftCorner + vertexArray[verticalIndex++] = 0.0f + + vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = lowerLeftCorner + vertexArray[verticalIndex++] = 0.0f + + vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = upperRightCorner + vertexArray[verticalIndex++] = 0.0f + + vertexArray[verticalIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + vertexArray[verticalIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + vertexArray[verticalIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + vertexArray[verticalIndex++] = lowerRightCorner + vertexArray[verticalIndex++] = 0.0f + + // indices for triangles from firstPointB secondPointB verticalElements.add(index + 2) - verticalElements.add(index + 1) verticalElements.add(index + 3) + verticalElements.add(index + 4) + verticalElements.add(index + 4) + verticalElements.add(index + 3) + verticalElements.add(index + 5) } } } diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Placemark.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Placemark.kt index 52f6e8fc9..f173755c8 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Placemark.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Placemark.kt @@ -14,6 +14,7 @@ import earth.worldwind.render.image.ImageSource import earth.worldwind.render.program.BasicShaderProgram import earth.worldwind.render.program.TriangleShaderProgram import earth.worldwind.util.math.boundingRectForUnitSquare +import earth.worldwind.util.math.encodeOrientationVector import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic import kotlin.math.abs @@ -452,52 +453,54 @@ open class Placemark @JvmOverloads constructor( drawable.program = rc.getShaderProgram { TriangleShaderProgram() } var vertexIndex = 0 + val upperLeftCorner = encodeOrientationVector(-1f, 1f) + val lowerLeftCorner = encodeOrientationVector(-1f, -1f) drawable.vertexPoints[vertexIndex++] = (placePoint.x - groundPoint.x).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.y - groundPoint.y).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.z - groundPoint.z).toFloat() - drawable.vertexPoints[vertexIndex++] = 1f + drawable.vertexPoints[vertexIndex++] = upperLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = (placePoint.x - groundPoint.x).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.y - groundPoint.y).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.z - groundPoint.z).toFloat() - drawable.vertexPoints[vertexIndex++] = -1f + drawable.vertexPoints[vertexIndex++] = lowerLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = (placePoint.x - groundPoint.x).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.y - groundPoint.y).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.z - groundPoint.z).toFloat() - drawable.vertexPoints[vertexIndex++] = 1f + drawable.vertexPoints[vertexIndex++] = upperLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = (placePoint.x - groundPoint.x).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.y - groundPoint.y).toFloat() drawable.vertexPoints[vertexIndex++] = (placePoint.z - groundPoint.z).toFloat() - drawable.vertexPoints[vertexIndex++] = -1f + drawable.vertexPoints[vertexIndex++] = lowerLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f - drawable.vertexPoints[vertexIndex++] = 1f + drawable.vertexPoints[vertexIndex++] = upperLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f - drawable.vertexPoints[vertexIndex++] = -1f + drawable.vertexPoints[vertexIndex++] = lowerLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f - drawable.vertexPoints[vertexIndex++] = 1f + drawable.vertexPoints[vertexIndex++] = upperLeftCorner drawable.vertexPoints[vertexIndex++] = 0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f drawable.vertexPoints[vertexIndex++] = 0.0f - drawable.vertexPoints[vertexIndex++] = -1f + drawable.vertexPoints[vertexIndex++] = lowerLeftCorner drawable.vertexPoints[vertexIndex] = 0f // Compute the drawable's modelview-projection matrix, relative to the placemark's ground point. diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt index a494f5355..9e59bfd8e 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt @@ -20,6 +20,7 @@ import earth.worldwind.util.Logger.logMessage import earth.worldwind.util.glu.GLU import earth.worldwind.util.glu.GLUtessellatorCallbackAdapter import earth.worldwind.util.kgl.* +import earth.worldwind.util.math.encodeOrientationVector import kotlin.jvm.JvmOverloads open class Polygon @JvmOverloads constructor( @@ -69,7 +70,8 @@ open class Polygon @JvmOverloads constructor( companion object { protected const val VERTEX_STRIDE = 5 - protected const val LINE_VERTEX_STRIDE = 10 + protected const val OUTLINE_LINE_SEGMENT_STRIDE = 4 * VERTEX_STRIDE + protected const val VERTICAL_LINE_SEGMENT_STRIDE = 4 * OUTLINE_LINE_SEGMENT_STRIDE // 4 points per 4 vertices per vertical line protected val defaultInteriorImageOptions = ImageOptions().apply { wrapMode = WrapMode.REPEAT } protected val defaultOutlineImageOptions = ImageOptions().apply { wrapMode = WrapMode.REPEAT @@ -316,7 +318,7 @@ open class Polygon @JvmOverloads constructor( if (noIntermediatePoints) { vertexCount += p.size - lineVertexCount += (p.size + 2) + lineVertexCount += (p.size + 2) // +2 is for point A and point C at the start and end if line strip verticalVertexCount += p.size } else if (p.isNotEmpty() && p[0] == p[p.size - 1]) { vertexCount += p.size + (p.size - 1) * maximumIntermediatePoints @@ -338,9 +340,9 @@ open class Polygon @JvmOverloads constructor( topElements.clear() sideElements.clear() lineVertexIndex = 0 - verticalVertexIndex = lineVertexCount * LINE_VERTEX_STRIDE - lineVertexArray = if (isExtrude && !isSurfaceShape) FloatArray(lineVertexCount * LINE_VERTEX_STRIDE + verticalVertexCount * 4 * LINE_VERTEX_STRIDE) - else FloatArray(lineVertexCount * LINE_VERTEX_STRIDE) + verticalVertexIndex = lineVertexCount * OUTLINE_LINE_SEGMENT_STRIDE + lineVertexArray = if (isExtrude && !isSurfaceShape) FloatArray(lineVertexCount * OUTLINE_LINE_SEGMENT_STRIDE + verticalVertexCount * VERTICAL_LINE_SEGMENT_STRIDE) + else FloatArray(lineVertexCount * OUTLINE_LINE_SEGMENT_STRIDE) outlineElements.clear() verticalElements.clear() @@ -363,22 +365,22 @@ open class Polygon @JvmOverloads constructor( var begin = positions[0] addVertex(rc, begin.latitude, begin.longitude, begin.altitude, VERTEX_ORIGINAL /*type*/) - addLineVertex(rc, begin.latitude, begin.longitude, begin.altitude, true, false) - addLineVertex(rc, begin.latitude, begin.longitude, begin.altitude, false, false) + addLineVertex(rc, begin.latitude, begin.longitude, begin.altitude, true, true) + addLineVertex(rc, begin.latitude, begin.longitude, begin.altitude, false, true) // Add the remaining boundary vertices, tessellating each edge as indicated by the polygon's properties. for (idx in 1 until positions.size) { val end = positions[idx] addIntermediateVertices(rc, begin, end) addVertex(rc, end.latitude, end.longitude, end.altitude, VERTEX_ORIGINAL /*type*/) - addLineVertex(rc, end.latitude, end.longitude, end.altitude, false, true) + addLineVertex(rc, end.latitude, end.longitude, end.altitude, false, if (idx == (positions.size - 1)) end != positions[0] else true) // check if there is implicit closing edge begin = end } // Tessellate the implicit closing edge if the boundary is not already closed. if (begin != positions[0]) { addIntermediateVertices(rc, begin, positions[0]) - addLineVertex(rc, positions[0].latitude, positions[0].longitude, positions[0].altitude, true, true) + addLineVertex(rc, positions[0].latitude, positions[0].longitude, positions[0].altitude, true, false) addLineVertex(rc, positions[0].latitude, positions[0].longitude, positions[0].altitude, true, false) } else { addLineVertex(rc, begin.latitude, begin.longitude, begin.altitude, true, false) @@ -479,108 +481,207 @@ open class Polygon @JvmOverloads constructor( protected open fun addLineVertex( rc: RenderContext, latitude: Angle, longitude: Angle, altitude: Double, isIntermediate : Boolean, addIndices : Boolean ) { - val vertex = (lineVertexIndex / LINE_VERTEX_STRIDE - 1) * 2 - var point = rc.geographicToCartesian(latitude, longitude, altitude, altitudeMode, point) + val vertex = lineVertexIndex / VERTEX_STRIDE + val point = rc.geographicToCartesian(latitude, longitude, altitude, altitudeMode, point) if (lineVertexIndex == 0) texCoord1d = 0.0 else texCoord1d += point.distanceTo(prevPoint) prevPoint.copy(point) + val upperLeftCorner = encodeOrientationVector(-1f, 1f) + val lowerLeftCorner = encodeOrientationVector(-1f, -1f) + val upperRightCorner = encodeOrientationVector(1f, 1f) + val lowerRightCorner = encodeOrientationVector(1f, -1f) if (isSurfaceShape) { lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = 1.0f + lineVertexArray[lineVertexIndex++] = upperLeftCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = lowerLeftCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = upperRightCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + lineVertexArray[lineVertexIndex++] = (longitude.inDegrees - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (altitude - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = -1.0f + lineVertexArray[lineVertexIndex++] = lowerRightCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() if (addIndices) { - outlineElements.add(vertex - 2) - outlineElements.add(vertex - 1) - outlineElements.add(vertex) + // indices for triangles made from this segment vertices outlineElements.add(vertex) - outlineElements.add(vertex - 1) outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 3) + // indices for triangles made from last vertices of this segment and first vertices of next segment + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) + outlineElements.add(vertex + 4) + outlineElements.add(vertex + 4) + outlineElements.add(vertex + 3) + outlineElements.add(vertex + 5) } } else { lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = 1.0f + lineVertexArray[lineVertexIndex++] = upperLeftCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[lineVertexIndex++] = -1.0f + lineVertexArray[lineVertexIndex++] = lowerLeftCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = upperRightCorner + lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() + + lineVertexArray[lineVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[lineVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[lineVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[lineVertexIndex++] = lowerRightCorner lineVertexArray[lineVertexIndex++] = texCoord1d.toFloat() if (addIndices) { - outlineElements.add(vertex - 2) - outlineElements.add(vertex - 1) - outlineElements.add(vertex) + // indices for triangles made from this segment vertices outlineElements.add(vertex) - outlineElements.add(vertex - 1) outlineElements.add(vertex + 1) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 1) + outlineElements.add(vertex + 3) + // indices for triangles made from last vertices of this segment and first vertices of next segment + outlineElements.add(vertex + 2) + outlineElements.add(vertex + 3) + outlineElements.add(vertex + 4) + outlineElements.add(vertex + 4) + outlineElements.add(vertex + 3) + outlineElements.add(vertex + 5) } if (isExtrude && !isIntermediate) { var vertPoint = Vec3() vertPoint = rc.geographicToCartesian(latitude, longitude, 0.0, altitudeMode, vertPoint) - val index = verticalVertexIndex / LINE_VERTEX_STRIDE * 2 + val index = verticalVertexIndex / VERTEX_STRIDE + + // first vertices, that simulate pointA for next vertices + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + // first pointB lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f + lineVertexArray[verticalVertexIndex++] = upperLeftCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f + lineVertexArray[verticalVertexIndex++] = upperRightCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (point.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (point.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (point.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f + lineVertexArray[verticalVertexIndex++] = lowerRightCorner lineVertexArray[verticalVertexIndex++] = 0.0f + // second pointB lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f + lineVertexArray[verticalVertexIndex++] = upperLeftCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = 1f + lineVertexArray[verticalVertexIndex++] = upperRightCorner lineVertexArray[verticalVertexIndex++] = 0.0f lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() - lineVertexArray[verticalVertexIndex++] = -1f + lineVertexArray[verticalVertexIndex++] = lowerRightCorner lineVertexArray[verticalVertexIndex++] = 0.0f - verticalElements.add(index) - verticalElements.add(index + 1) - verticalElements.add(index + 2) + // last vertices, that simulate pointC for previous vertices + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerLeftCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = upperRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + lineVertexArray[verticalVertexIndex++] = (vertPoint.x - vertexOrigin.x).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.y - vertexOrigin.y).toFloat() + lineVertexArray[verticalVertexIndex++] = (vertPoint.z - vertexOrigin.z).toFloat() + lineVertexArray[verticalVertexIndex++] = lowerRightCorner + lineVertexArray[verticalVertexIndex++] = 0.0f + + // indices for triangles from firstPointB secondPointB verticalElements.add(index + 2) - verticalElements.add(index + 1) verticalElements.add(index + 3) + verticalElements.add(index + 4) + verticalElements.add(index + 4) + verticalElements.add(index + 3) + verticalElements.add(index + 5) } } } diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/util/math/WWMath.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/util/math/WWMath.kt index 511c3467b..fb58723a3 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/util/math/WWMath.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/util/math/WWMath.kt @@ -107,4 +107,16 @@ fun isPowerOfTwo(value: Int) = value != 0 && value and value - 1 == 0 fun powerOfTwoCeiling(value: Int): Int { val pow = ceil(ln(value.toDouble()) / ln(2.0)).toInt() return 1 shl pow +} + +/** + * Packs vec2 into one float value. + * + * @param x x coordinate of vector should be in -1 or 1. + * @param y coordinate of vector should be in -1 or 1. + * + * @return the float value with encoded coordinates + */ +fun encodeOrientationVector(x: Float, y: Float) : Float { + return x + 0.5f * y; } \ No newline at end of file