WorldWind Kotlin 3D Virtual Globe API

repository·develop·Indexed 41 days ago

https://github.com/worldwindearth/worldwindkotlin

A 3D virtual globe API for Android, iOS, Web, and Java providing high-resolution terrain for visualizing geo-located information in 2D and 3D. Includes support for glTF and 3D Tiles pipelines with Draco compressed-mesh/point-cloud decoding and KHR_texture_basisu (KTX2/Basis-Universal) texture decoding.

Tokens
11.5K
Snippets
31
Records
56
Agent score
52%

What's inside WorldWind Kotlin

  1. Overview of KHR_texture_basisu (KTX2 / Basis-Universal) support

    develop

    The worldwind-formats-gltf-ktx2 module provides the KHR_texture_basisu (KTX2 / Basis-Universal) texture decoder for the WorldWind glTF and 3D Tiles pipeline. It supports multiple platforms:

    • JVM: Uses native libktx via JNI.
    • Android: Uses native libktx via JNI through assets/.
    • iOS: Uses Kotlin/Native cinterop (requires opt-in build flag).
    • Web (Kotlin/JS/Wasm): Uses THREE.js's bundled basis_transcoder WASM via npm.

    Bundle Size Note (Web): Even though the three package is a dependency, Webpack tree-shakes it. Only basis_transcoder.js and basis_transcoder.wasm (~250 KiB total) are included in the bundle; the full THREE.js rendering engine is not included.

  2. Draco compressed-mesh / point-cloud decoder overview

    develop

    The worldwind-formats-gltf-draco module provides a Draco decoder for WorldWind's glTF and 3D Tiles pipeline. It enables the decoding of compressed meshes and point clouds across multiple platforms via the installDracoDecoder() API.

    Platform Support:

    • JVM: Uses native libdraco_bridge via JNI.
    • Android: Uses native libdraco_bridge via JNI (loaded from assets/).
    • iOS: Uses Kotlin/Native cinterop.
    • Web (Kotlin/JS/Wasm): Uses the npm draco3d package with draco_decoder.wasm.
  3. Handle moko-resources stubs for iOS

    develop

    The moko-resources library uses lazy lookup via NSBundle.loadableBundle(...). If a required bundle is missing, the app will crash at launch with a kotlin.IllegalArgumentException.

    To prevent this, you must provide empty stub bundles for every moko module used. For this project, ensure the following directories (containing an Info.plist with matching CFBundleIdentifier) are included in Build Phases → Copy Bundle Resources:

    • MokoStubBundles/earth.worldwind.main.bundle/
    • MokoStubBundles/earth.worldwind.assets.main.bundle/
    • MokoStubBundles/earth.worldwind.tutorials.main.bundle/

    Note: Because the framework is static, these bundles are not automatically copied into the .app and must be manually added to the app target.

  4. Understand the two render paths in ProjectedMediaSurface

    develop

    The ProjectedMediaSurface uses two distinct internal methods to project media onto terrain:

    1. 2D-homography path (Default):

      • Triggered when imageProjection = null.
      • Uses a mat3 ground-to-image homography built from four ground-corner KLV tags.
      • Performs a per-fragment mat3 * vec3 calculation to determine image UVs.
      • Pros/Cons: Computationally cheap and exact on flat surfaces, but slightly inaccurate on significant terrain relief.
    2. 3D camera-frustum path (Opt-in):

      • Triggered when imageProjection = matrix4 is set.
      • Uses a per-fragment mat4 * vec4(world_position, 1) projection followed by an xy / w divide for image UVs.
      • Pros/Cons: Mathematically exact regardless of terrain relief. Note: This path is currently broken on JS/WebGL.
  5. How WorldWind Kotlin/Native integrates with SwiftUI

    develop

    The iOS project uses a SwiftUI shell to host a static Kotlin/Native framework. Key integration details include:

    • Module Naming: To avoid name collisions in the build graph, the app target uses PRODUCT_MODULE_NAME = WorldWindTutorialsApp, while the framework remains WorldWindTutorials.
    • Type Erasure (WorldWindow): Due to Kotlin/Native limitations, WorldWindow (a UIView subclass) cannot be imported directly as a Swift type. Swift code receives instances as plain UIView. To interact with the engine, use the top-level helpers in MainKt:
      • engineOf(window:)
      • requestRedrawOn(window:)
    • Tap Dispatch: Taps are routed through Tutorials.shared.screenTap(wwd:xPoints:yPoints:). The Kotlin side determines whether to perform a depth-pick or a ray-pick based on the active tutorial.
    • Static Linking: The framework is linked as a static framework with Embed = Do Not Embed. To ensure Swift can see types like WorldWind or BasicWorldWindowController, the iOS framework block must use export(project(":worldwind")) and the iosMain source set must declare :worldwind as api.
  6. Understand WorldWind bundled assets

    develop

    Default assets such as background imagery, night-time city lights, the star catalogue, and the EGM96 geoid grid are provided via the earth.worldwind:worldwind-assets artifact.

    Key details:

    • Automatic Resolution: The engine depends on this artifact, so it is resolved automatically. Layers like BackgroundLayer(), StarFieldLayer(), and AtmosphereLayer() work out-of-the-box without extra setup.
    • Version Independence: The assets artifact has its own versioning scheme that does not track the engine version.
    • Manual Pinning: You can pin a specific asset revision if required by explicitly declaring the dependency:
    implementation 'earth.worldwind:worldwind-assets:1.0.0'
  7. Configure Webpack for Kotlin/JS and Kotlin/Wasm consumers

    develop

    If you are using this module in a Kotlin/JS or Kotlin/Wasm environment, you must provide specific Webpack configuration snippets in your app module's webpack.config.d/ directory. This is necessary because the module requires the three package's basis transcoder (WASM), which has Node.js dependencies and requires specific asset handling.

    1. Stub Node.js dependencies

    Create a file named basis-node-stubs.js in webpack.config.d/ to prevent Webpack from failing on the Emscripten-generated code's references to fs, path, and crypto.

    2. Configure WASM asset loading

    Create a file named basis-wasm-asset.js in webpack.config.d/ to ensure basis_transcoder.wasm is emitted as a resource so the NativeKtx2 hook can locate it at runtime.

    Note: If you are also using worldwind-formats-gltf-draco, include both sets of snippets; Webpack will merge the fallbacks.

    // basis-node-stubs.js
    ;(function(config) {
        config.resolve = config.resolve || {};
        config.resolve.fallback = Object.assign({}, config.resolve.fallback, {
            fs: false,
            path: false,
            crypto: false,
        });
    })(config);
    
    // basis-wasm-asset.js
    ;(function(config) {
        config.module = config.module || {};
        config.module.rules = (config.module.rules || []).concat([
            {
                test: /three[\\/]examples[\\/]jsm[\\/]libs[\\/]basis[\\/].*\\.wasm$|/,
                type: 'asset/resource',
                generator: { filename: '[name][ext]' },
            },
        ]);
    })(config);
  8. Handle JS/WebGL limitations with ProjectedMediaSurface

    develop

    The ProjectedMediaSurface.imageProjection feature (3D camera-frustum projection for draping video onto terrain) is currently broken on JS/WebGL. While it works correctly on JVM and Android, on JS it renders as a tiny, incorrect patch.

    Recommended Workaround for JS Users: Do not attempt to use the 3D projection path on JS. Instead, leave imageProjection = null to fall back to the 2D-homography path. This path uses a mat3 ground-to-image homography derived from KLV tags and is mathematically exact on flat ground and sufficient for most use cases on relief.

    // On JS, use the default 2D-homography path
    projectedMediaSurface.imageProjection = null
  9. Install WorldWind Kotlin via Gradle

    develop

    To use WorldWind Kotlin in your project, add mavenCentral() to your repositories and include the earth.worldwind:worldwind dependency in your Gradle configuration. The worldwind-assets artifact is included automatically as a transitive dependency, providing default imagery and terrain data.

    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation 'earth.worldwind:worldwind:2.1.0'
    }
  10. Install WorldWind Kotlin development snapshots

    develop

    To access the latest development builds containing the newest features and bug fixes, add the Sonatype Maven snapshots repository and use a dynamic version for the dependency.

    repositories {
        maven("https://central.sonatype.com/repository/maven-snapshots/")
    }
    
    dependencies {
        implementation 'earth.worldwind:worldwind:+'
    }