Skip to content

Commit

Permalink
Apply vertical exaggeration to all objects in space (placemarks, 3d s…
Browse files Browse the repository at this point in the history
…hapes, camera)
  • Loading branch information
EMaksymenko committed Jan 26, 2025
1 parent e1868c1 commit 47d1dd6
Show file tree
Hide file tree
Showing 17 changed files with 57 additions and 81 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ repositories {
}
dependencies {
implementation 'earth.worldwind:worldwind:1.6.9'
implementation 'earth.worldwind:worldwind:1.6.11'
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ buildscript {

allprojects {
group = "earth.worldwind"
version = "1.6.9"
version = "1.6.11"

extra.apply {
set("minSdk", 21)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,8 @@ class CameraControlFragment: BasicGlobeFragment() {
position.altitude = position.altitude.coerceIn(minAltitude, maxAltitude)

// Check if camera altitude is not under the surface
val elevation = if (wwd.engine.globe.is2D) COLLISION_THRESHOLD else wwd.engine.globe.getElevation(
position.latitude, position.longitude
) * wwd.engine.verticalExaggeration + COLLISION_THRESHOLD
val elevation = if (wwd.engine.globe.is2D) COLLISION_THRESHOLD
else wwd.engine.globe.getElevation(position.latitude, position.longitude) + COLLISION_THRESHOLD
if (elevation > position.altitude) position.altitude = elevation

// Apply modified absolute position back to camera
Expand Down
32 changes: 7 additions & 25 deletions worldwind/src/commonMain/kotlin/earth/worldwind/WorldWind.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,6 @@ open class WorldWind @JvmOverloads constructor(
}
field = value
}
/**
* Vertical exaggeration (VE) is a scale that is used to emphasize vertical features, which might be too small
* to identify relative to the horizontal scale.
*/
var verticalExaggeration = 1.0
set(value) {
require(value > 0) {
Logger.logMessage(
Logger.ERROR, "WorldWind", "setVerticalExaggeration", "invalidVerticalExaggeration"
)
}
field = value
}
/**
* Atmosphere altitude above ellipsoid. Used to control when objects are clipped by the far plain behind the globe.
*/
Expand Down Expand Up @@ -227,9 +214,8 @@ open class WorldWind @JvmOverloads constructor(

// Check if camera altitude is not under the surface
val position = camera.position
val elevation = if (globe.is2D) COLLISION_THRESHOLD else globe.getElevation(
position.latitude, position.longitude
) * verticalExaggeration + COLLISION_THRESHOLD
val elevation = if (globe.is2D) COLLISION_THRESHOLD
else globe.getElevation(position.latitude, position.longitude) + COLLISION_THRESHOLD
if (elevation > position.altitude) {
// Set camera altitude above the surface
position.altitude = elevation
Expand Down Expand Up @@ -440,7 +426,6 @@ open class WorldWind @JvmOverloads constructor(
cameraPosition.latitude, cameraPosition.longitude, cameraPosition.altitude, rc.cameraPoint
)
rc.renderResourceCache = renderResourceCache
rc.verticalExaggeration = verticalExaggeration
rc.densityFactor = densityFactor
rc.atmosphereAltitude = atmosphereAltitude
rc.globeState = globe.state
Expand Down Expand Up @@ -539,7 +524,7 @@ open class WorldWind @JvmOverloads constructor(
protected open fun computeViewingTransform(projection: Matrix4, modelview: Matrix4) {
// Compute the clip plane distances. The near distance is set to a large value that does not clip the globe's
// surface. The far distance is set to the smallest value that does not clip the atmosphere.
val eyeAltitude = globe.getAbsolutePosition(camera.position, camera.altitudeMode).altitude
val eyeAltitude = globe.getAbsolutePosition(camera.position, camera.altitudeMode).altitude * globe.verticalExaggeration
val eyeHorizon = globe.horizonDistance(eyeAltitude)
val atmosphereHorizon = globe.horizonDistance(atmosphereAltitude)

Expand All @@ -555,7 +540,7 @@ open class WorldWind @JvmOverloads constructor(
// Prevent the near clip plane from intersecting the terrain.
val distanceToSurface = if (globe.is2D) eyeAltitude else eyeAltitude - globe.getElevation(
camera.position.latitude, camera.position.longitude
) * verticalExaggeration
) * globe.verticalExaggeration
if (distanceToSurface > 0) {
val tanHalfFov = tan(0.5 * camera.fieldOfView.inRadians)
val maxNearDistance = distanceToSurface / (2 * sqrt(2 * tanHalfFov * tanHalfFov + 1))
Expand Down Expand Up @@ -607,14 +592,11 @@ open class WorldWind @JvmOverloads constructor(
position.latitude, position.longitude, position.altitude, result
)
AltitudeMode.CLAMP_TO_GROUND -> globe.geographicToCartesianTransform(
position.latitude, position.longitude, globe.getElevation(
position.latitude, position.longitude
) * verticalExaggeration, result
position.latitude, position.longitude, globe.getElevation(position.latitude, position.longitude), result
)
AltitudeMode.RELATIVE_TO_GROUND -> globe.geographicToCartesianTransform(
position.latitude, position.longitude, (position.altitude + globe.getElevation(
position.latitude, position.longitude
)) * verticalExaggeration, result
position.latitude, position.longitude,
position.altitude + globe.getElevation(position.latitude, position.longitude), result
)
}
return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ open class BoundingBox {
heights[6] = minHeight
heights[8] = minHeight
val points = scratchPoints
globe.geographicToCartesianGrid(sector, NUM_LAT, NUM_LON, heights, 1.0f, null, points)
globe.geographicToCartesianGrid(sector, NUM_LAT, NUM_LON, heights, null, points)

// Compute the local coordinate axes. Since we know this box is bounding a geographic sector, we use the
// local coordinate axes at its centroid as the box axes. Using these axes results in a box that has +-10%
Expand Down
25 changes: 20 additions & 5 deletions worldwind/src/commonMain/kotlin/earth/worldwind/globe/Globe.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import earth.worldwind.globe.geoid.EGM96Geoid
import earth.worldwind.globe.geoid.Geoid
import earth.worldwind.globe.projection.GeographicProjection
import earth.worldwind.globe.projection.Wgs84Projection
import earth.worldwind.util.Logger
import kotlin.math.PI
import kotlin.math.sin
import kotlin.math.sqrt
Expand Down Expand Up @@ -57,7 +58,7 @@ open class Globe(
/**
* Current globe state.
*/
val state get() = State(ellipsoid, projection.displayName)
val state get() = State(ellipsoid, projection.displayName, verticalExaggeration)
/**
* The globe offset in 2D continuous projection. Center is the default for 3D.
*/
Expand All @@ -72,6 +73,19 @@ open class Globe(
}
}
protected var offsetValue = 0.0
/**
* Vertical exaggeration (VE) is a scale that is used to emphasize vertical features, which might be too small
* to identify relative to the horizontal scale.
*/
var verticalExaggeration = 1.0
set(value) {
require(value > 0) {
Logger.logMessage(
Logger.ERROR, "Globe", "setVerticalExaggeration", "invalidVerticalExaggeration"
)
}
field = value
}

/**
* An offset to apply to this globe when translating between Geographic positions and Cartesian points.
Expand All @@ -82,7 +96,7 @@ open class Globe(
/**
* Used to compare states during rendering to determine whether globe-state dependent cached values must be updated.
*/
data class State(private val ellipsoid: Ellipsoid, private val projectionName: String)
data class State(private val ellipsoid: Ellipsoid, private val projectionName: String, private val ve: Double)

/**
* Indicates the radius in meters of the globe's ellipsoid at a specified location.
Expand Down Expand Up @@ -116,16 +130,16 @@ open class Globe(
* @return the result argument, set to the computed Cartesian coordinates
*/
fun geographicToCartesian(latitude: Angle, longitude: Angle, altitude: Double, result: Vec3) =
projection.geographicToCartesian(ellipsoid, latitude, longitude, altitude, offsetValue, result)
projection.geographicToCartesian(ellipsoid, latitude, longitude, altitude * verticalExaggeration, offsetValue, result)

fun geographicToCartesianNormal(latitude: Angle, longitude: Angle, result: Vec3) =
projection.geographicToCartesianNormal(ellipsoid, latitude, longitude, result)

fun geographicToCartesianTransform(latitude: Angle, longitude: Angle, altitude: Double, result: Matrix4) =
projection.geographicToCartesianTransform(ellipsoid, latitude, longitude, altitude, result)
projection.geographicToCartesianTransform(ellipsoid, latitude, longitude, altitude * verticalExaggeration, result)

fun geographicToCartesianGrid(
sector: Sector, numLat: Int, numLon: Int, height: FloatArray?, verticalExaggeration: Float,
sector: Sector, numLat: Int, numLon: Int, height: FloatArray?,
origin: Vec3?, result: FloatArray, rowOffset: Int = 0, rowStride: Int = 0
) = projection.geographicToCartesianGrid(
ellipsoid, sector, numLat, numLon, height, verticalExaggeration,
Expand All @@ -150,6 +164,7 @@ open class Globe(
fun cartesianToGeographic(x: Double, y: Double, z: Double, result: Position) =
projection.cartesianToGeographic(ellipsoid, x, y, z, offsetValue, result).also {
if (is2D) result.longitude = result.longitude.normalize180()
result.altitude /= verticalExaggeration
}

fun cartesianToLocalTransform(x: Double, y: Double, z: Double, result: Matrix4) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface GeographicProjection {

fun geographicToCartesianGrid(
ellipsoid: Ellipsoid, sector: Sector, numLat: Int, numLon: Int, height: FloatArray?,
verticalExaggeration: Float, origin: Vec3?, offset: Double, result: FloatArray, rowOffset: Int, rowStride: Int
verticalExaggeration: Double, origin: Vec3?, offset: Double, result: FloatArray, rowOffset: Int, rowStride: Int
): FloatArray

fun geographicToCartesianBorder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ open class MercatorProjection : GeographicProjection {
}

override fun geographicToCartesianGrid(
ellipsoid: Ellipsoid, sector: Sector, numLat: Int, numLon: Int, height: FloatArray?, verticalExaggeration: Float,
ellipsoid: Ellipsoid, sector: Sector, numLat: Int, numLon: Int, height: FloatArray?, verticalExaggeration: Double,
origin: Vec3?, offset: Double, result: FloatArray, rowOffset: Int, rowStride: Int
): FloatArray {
require(numLat >= 1 && numLon >= 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ open class Wgs84Projection: GeographicProjection {
}

override fun geographicToCartesianGrid(
ellipsoid: Ellipsoid, sector: Sector, numLat: Int, numLon: Int, height: FloatArray?, verticalExaggeration: Float,
ellipsoid: Ellipsoid, sector: Sector, numLat: Int, numLon: Int, height: FloatArray?, verticalExaggeration: Double,
origin: Vec3?, offset: Double, result: FloatArray, rowOffset: Int, rowStride: Int
): FloatArray {
require(numLat >= 1 && numLon >= 1) {
Expand Down Expand Up @@ -169,7 +169,7 @@ open class Wgs84Projection: GeographicProjection {
val rpm = eqr / sqrt(1.0 - ec2 * sinLat * sinLat)
var colIndex = rowIndex
for (lonIndex in 0 until numLon) {
val hgt = if (height != null) (height[elevIndex++] * verticalExaggeration).toDouble() else 0.0
val hgt = if (height != null) height[elevIndex++] * verticalExaggeration else 0.0
result[colIndex++] = ((hgt + rpm) * cosLat * sinLon[lonIndex] - xOffset).toFloat()
result[colIndex++] = ((hgt + rpm * (1.0 - ec2)) * sinLat - yOffset).toFloat()
result[colIndex++] = ((hgt + rpm) * cosLat * cosLon[lonIndex] - zOffset).toFloat()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ open class TerrainTile(sector: Sector, level: Level, row: Int, column: Int): Til
*/
protected val minTerrainElevation = -Short.MAX_VALUE.toFloat()
protected var heightTimestamp = 0L
protected var verticalExaggeration = 0.0f
protected var globeState: Globe.State? = null
protected var globeOffset: Globe.Offset? = null
var sortOrder = 0.0
Expand Down Expand Up @@ -52,23 +51,20 @@ open class TerrainTile(sector: Sector, level: Level, row: Int, column: Int): Til
}
updateHeightBufferKey()
}
val ve = rc.verticalExaggeration.toFloat()
val state = rc.globeState
val offset = rc.globe.offset
if (timestamp != heightTimestamp || ve != verticalExaggeration || state != globeState || offset != globeOffset) {
val borderHeight = minTerrainElevation * ve
if (timestamp != heightTimestamp || state != globeState || offset != globeOffset) {
val rowStride = (tileWidth + 2) * 3
globe.geographicToCartesian(sector.centroidLatitude, sector.centroidLongitude, 0.0, origin)
globe.geographicToCartesianGrid(
sector, tileWidth, tileHeight, heightGrid, ve, origin, points, rowStride + 3, rowStride
sector, tileWidth, tileHeight, heightGrid, origin, points, rowStride + 3, rowStride
)
globe.geographicToCartesianBorder(
sector, tileWidth + 2, tileHeight + 2, borderHeight, origin, points
sector, tileWidth + 2, tileHeight + 2, minTerrainElevation, origin, points
)
updatePointBufferKey()
}
heightTimestamp = timestamp
verticalExaggeration = ve
globeState = state
globeOffset = offset
sortOrder = drawSortOrder(rc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,8 @@ open class AtmosphereLayer: AbstractLayer("Atmosphere") {
val altitudes = FloatArray(count)
altitudes.fill(altitude)
val points = FloatArray(count * 3)
rc.globe.geographicToCartesianGrid(
fullSphereSector, numLat, numLon, altitudes, 1.0f, null, points
)
// Ignore vertical exaggeration for atmosphere
rc.globe.projection.geographicToCartesianGrid(rc.globe.ellipsoid, fullSphereSector, numLat, numLon, altitudes, 1.0, null, 0.0, points, 0, 0)
return points
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) {
private var lastCameraHeading = 0.0
private var lastCameraTilt = 0.0
private var lastFOV = 0.0
private var lastVerticalExaggeration = 0.0
private var lastGlobeState: Globe.State? = null
private var lastGlobeOffset: Globe.Offset? = null

Expand Down Expand Up @@ -198,7 +197,6 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) {
* @return true if the graticule should be updated.
*/
private fun needsToUpdate(rc: RenderContext): Boolean {
if (lastVerticalExaggeration != rc.verticalExaggeration) return true
if (abs(lastCameraHeading - rc.camera.heading.inDegrees) > 1) return true
if (abs(lastCameraTilt - rc.camera.tilt.inDegrees) > 1) return true
if (abs(lastFOV - rc.camera.fieldOfView.inDegrees) > 1) return true
Expand All @@ -213,7 +211,6 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) {
lastFOV = rc.camera.fieldOfView.inDegrees
lastCameraHeading = rc.camera.heading.inDegrees
lastCameraTilt = rc.camera.tilt.inDegrees
lastVerticalExaggeration = rc.verticalExaggeration
lastGlobeState = rc.globeState
}

Expand All @@ -240,11 +237,9 @@ abstract class AbstractGraticuleLayer(name: String): AbstractLayer(name) {
}

fun getSurfacePoint(rc: RenderContext, latitude: Angle, longitude: Angle): Vec3 {
if (!rc.terrain.surfacePoint(latitude, longitude, surfacePoint))
rc.globe.geographicToCartesian(
latitude, longitude, rc.globe.getElevation(latitude, longitude)
* rc.verticalExaggeration, surfacePoint
)
if (!rc.terrain.surfacePoint(latitude, longitude, surfacePoint)) rc.globe.geographicToCartesian(
latitude, longitude, rc.globe.getElevation(latitude, longitude), surfacePoint
)
return surfacePoint
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ abstract class AbstractSurfaceRenderable(sector: Sector, displayName: String? =
protected val extent by lazy { BoundingBox() }
protected val heightLimits by lazy { FloatArray(2) }
protected var heightLimitsTimestamp = 0L
protected var extentExaggeration = 0.0f
protected var extentGlobeState: Globe.State? = null
protected var extentGlobeOffset: Globe.Offset? = null
protected val extentSector = Sector()
Expand All @@ -20,16 +19,13 @@ abstract class AbstractSurfaceRenderable(sector: Sector, displayName: String? =
if (timestamp != heightLimitsTimestamp) {
if (globe.is2D) heightLimits.fill(0f) else calcHeightLimits(globe)
}
val ve = rc.verticalExaggeration.toFloat()
val state = rc.globeState
val offset = rc.globe.offset
if (timestamp != heightLimitsTimestamp || ve != extentExaggeration
|| state != extentGlobeState || offset != extentGlobeOffset || extentSector != sector) {
val minHeight = heightLimits[0] * ve
val maxHeight = heightLimits[1] * ve
if (timestamp != heightLimitsTimestamp || state != extentGlobeState || offset != extentGlobeOffset || extentSector != sector) {
val minHeight = heightLimits[0]
val maxHeight = heightLimits[1]
extent.setToSector(sector, globe, minHeight, maxHeight)
heightLimitsTimestamp = timestamp
extentExaggeration = ve
extentGlobeState = state
extentGlobeOffset = offset
extentSector.copy(sector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ open class RenderContext {
lateinit var camera: Camera
lateinit var renderResourceCache: RenderResourceCache
var densityFactor = 1f
var verticalExaggeration = 1.0
var horizonDistance = 0.0
var atmosphereAltitude = 0.0
var viewingDistance = 0.0
Expand Down Expand Up @@ -73,7 +72,6 @@ open class RenderContext {

open fun reset() {
densityFactor = 1f
verticalExaggeration = 1.0
horizonDistance = 0.0
atmosphereAltitude = 0.0
viewingDistance = 0.0
Expand Down Expand Up @@ -290,21 +288,21 @@ open class RenderContext {
latitude: Angle, longitude: Angle, altitude: Double, altitudeMode: AltitudeMode, result: Vec3
): Vec3 {
when (altitudeMode) {
ABSOLUTE -> globe.geographicToCartesian(latitude, longitude, altitude * verticalExaggeration, result)
ABSOLUTE -> globe.geographicToCartesian(latitude, longitude, altitude, result)
CLAMP_TO_GROUND -> if (!terrain.surfacePoint(latitude, longitude, result)) globe.run {
// Use elevation model height as a fallback
val elevation = getElevation(latitude, longitude)
geographicToCartesian(latitude, longitude, elevation * verticalExaggeration, result)
geographicToCartesian(latitude, longitude, elevation, result)
}
RELATIVE_TO_GROUND -> if (terrain.surfacePoint(latitude, longitude, result)) {
// Offset along the normal vector at the terrain surface point.
if (altitude != 0.0) globe.geographicToCartesianNormal(latitude, longitude, scratchVector).also {
result.add(scratchVector.multiply(altitude))
result.add(scratchVector.multiply(altitude * globe.verticalExaggeration))
}
} else globe.run {
// Use elevation model height as a fallback
val elevation = altitude + getElevation(latitude, longitude)
geographicToCartesian(latitude, longitude, elevation * verticalExaggeration, result)
geographicToCartesian(latitude, longitude, elevation, result)
}
}
return result
Expand Down
Loading

0 comments on commit 47d1dd6

Please sign in to comment.