Cesium for Unreal

repository·main·Indexed 22 days ago

https://github.com/cesiumgs/cesium-unreal

A plugin that integrates the 3D geospatial ecosystem into Unreal Engine, providing a high-accuracy WGS84 globe and support for streaming real-world 3D content such as terrain, imagery, and buildings using 3D Tiles and Cesium ion. The plugin supports multiple Unreal Engine versions (from 4.26 to 5.5) and provides development setup guides for Windows, Linux, and macOS.

Tokens
23K
Snippets
30
Records
96
Agent score
78%

What's inside Cesium for Unreal

  1. Ensure MSVC compiler compatibility for Cesium for Unreal

    main

    Compiling Cesium for Unreal requires that the vcpkg-based third-party dependencies, the cesium-native code, and the Cesium for Unreal plugin code itself are all compiled using the same (or compatible) versions of the MSVC compiler.

    Key Compatibility Rules:

    • The compiler version used to link must be the same or newer than the newest compiler used to build any of the .lib files or object files being linked.
    • For maximum compatibility, released versions of Cesium for Unreal should ideally be built with the exact MSVC version used by the Epic Games build farm for that specific Unreal Engine version (e.g., v14.38.33130 for UE 5.6).
    • You can install multiple toolchain versions simultaneously via the Visual Studio Installer by selecting specific versions under the "Individual Components" tab in "Compilers, build tools, and runtimes".
  2. Handle property changes with Custom Versions

    main

    When a UObject property is replaced by a different type or logic, use Custom Versions to resolve differences during the loading process. This involves using an enum (defined in CesiumCustomVersion.h) to track the plugin's state.

    When to use Serialize vs PostLoad

    • Serialize: Use this for simple properties like bools or enums. You can intercept the value during the load/save process.
    • PostLoad: Use this for properties that refer to other objects (e.g., pointers). These must be resolved in PostLoad because they may be overwritten later in the load process if handled in Serialize.

    Implementation Workflow

    1. Create a New Version: Add a new entry to the Versions enum in CesiumCustomVersion.h. Place it between the last custom version and VersionPlusOne.
    2. Deprecate the Old Property: Follow standard deprecation patterns and use [CoreRedirects] if necessary.
    3. Implement Logic:
      • In Serialize, use Ar.UsingCustomVersion(FCesiumCustomVersion::GUID) and Ar.CustomVer(...) to check the version and set the new property based on the old one.
      • In PostLoad, use GetLinkerCustomVersion(FCesiumCustomVersion::GUID) to check the version and resolve pointer-based properties.
    // Example: Handling a version change in Serialize
    void UCesiumWebMapTileServiceRasterOverlay::Serialize(FArchive& Ar) {
      Super::Serialize(Ar);
      Ar.UsingCustomVersion(FCesiumCustomVersion::GUID);
      const int32 CesiumVersion = Ar.CustomVer(FCesiumCustomVersion::GUID);
    
      if (CesiumVersion < FCesiumCustomVersion::WebMapTileServiceProjectionAsEnum) {
        this->Projection = this->UseWebMercatorProjection_DEPRECATED
            ? ECesiumWebMapTileServiceRasterOverlayProjection::WebMercator
            : ECesiumWebMapTileServiceRasterOverlayProjection::Geographic;
      }
    }
    
    // Example: Handling a version change in PostLoad for pointers
    void ACesium3DTileset::PostLoad() {
      Super::PostLoad();
    #if WITH_EDITOR
      const int32 CesiumVersion = this->GetLinkerCustomVersion(FCesiumCustomVersion::GUID);
      PRAGMA_DISABLE_DEPRECATION_WARNINGS
      if (CesiumVersion < FCesiumCustomVersion::CesiumIonServer) {
        this->CesiumIonServer = UCesiumIonServer::GetBackwardCompatibleServer(
            this->IonAssetEndpointUrl_DEPRECATED);
      }
      PRAGMA_ENABLE_DEPRECATION_WARNINGS
    #endif
    }
  3. Understand Property Tables and Encoding in v2.0

    main

    Property tables (from EXT_structural_metadata) define how metadata values apply to features.

    • Identification: Property tables are distinguished by their name. If no name is provided, the component uses the class name (e.g., a buildings class becomes a buildings property table).
    • Property Details: Each property includes details like type, normalized status, offset, and scale. These details inform how the component encodes values into textures.
    • Encoding Details: You can manually set how a property is encoded to a texture and how it is retrieved.
    • Fallback Behavior: If a property is listed in the description but missing from a specific model's property table, it falls back to default values (e.g., a missing scalar property will be encoded as all zeroes).
  4. Identify low use and fragmented use areas in Timings panel

    main

    When analyzing the Timings panel, look for these two patterns to find optimization opportunities:

    Low Use Areas

    These are regions where the application is running but not utilizing available resources (like background threads).

    • How to find: Check Game Frames in All Tracks, disable Compact Mode, set Depth Limit to 4 lanes, and zoom into periods where background workers are inactive.
    • Goal: Investigate why the CPU/threads are idle during these periods.

    Fragmented Use Areas hese are regions where threads are active but inefficiently.

    • How to find: Set Depth Limit to Unlimited and zoom into busy background workers.
    • What to look for: Gaps between work items where workers finish a task and then wait for more work.
    • Goal: Aim to 'squish' work together to minimize inactivity and reduce total execution duration.
  5. How multithreaded texture creation works in Cesium for Unreal

    main

    To optimize memory and performance, Cesium for Unreal uses a specialized system for texture creation that handles shared images across different tiles:

    • Shared Image Loading: When multiple tiles share a single image, ExtensionImageAssetUnreal::getOrCreate is called. It uses a mutex to ensure only one thread performs the actual load. Other threads receive a SharedFuture that resolves once the first thread completes the load, preventing redundant work and blocking.
    • Asynchronous GPU Uploads:
      • For RHI's supporting async texture creation (e.g., Direct3D 11 and 12), the system uses FCesiumPreCreatedRHITextureResource. The GPU upload is triggered immediately by the worker thread via RHIAsyncCreateTexture2D.
      • For other RHIs, it uses FCesiumCreateNewTextureResource, which queues a command to the render thread to perform the upload via RHICreateTexture.
    • Texture Resource Management: To allow the same pixel data to be used with different sampling settings (e.g., different mipmap or sRGB configurations) without duplicating memory, the system creates multiple FTextureResource instances (like FCesiumUseExistingTextureResource) that all reference a single underlying FRHITexture via reference counting.
  6. Core components of the Cesium for Unreal development environment

    main

    A full development environment for Cesium for Unreal consists of three primary components:

    1. cesium-native: Engine-independent libraries for 3D Tiles and geospatial functionality. Most plugin functionality is built upon these.
    2. cesium-unreal: The source code for the Cesium for Unreal plugin itself.
    3. An Unreal project: A project that consumes the plugin. The cesium-unreal-samples repository is recommended for quick starts and regression testing.

    Note: While cesium-native can be developed independently, modifications to it must be carefully checked for breaking changes in the cesium-unreal API or build process.

  7. Organize source code into Runtime and Editor folders

    main

    When developing with or extending Cesium for Unreal, understand the distinction between the Runtime and Editor source directories:

    • Editor folder: Contains elements used exclusively within the Unreal Editor (e.g., UI components or editor-only functionality). Warning: Do not place code here that is required for the application to run at runtime, as these files will not be included in a packaged build.
    • Runtime folder: Contains all logic required for the application to function. These classes are accessible to the Unreal Editor but are not exclusive to it.
  8. Expose properties and functions to Unreal Engine

    main

    To make C++ members accessible to the Unreal Editor or Blueprints, you must use specific macros:

    Properties (UPROPERTY)

    Use the UPROPERTY macro above member variables to expose them to the Editor UI or Blueprints.

    Functions (UFUNCTION)

    Use the UFUNCTION macro to make C++ functions accessible to Blueprints or as buttons in the Editor interface.

    Structs (USTRUCT)

    Use USTRUCT to expose C++ structs.

    • USTRUCTs are not managed by Garbage Collection.
    • Use USTRUCT(BlueprintType) to enable the Make node in Blueprints.
    • To allow properties to appear in a Break node, use BlueprintReadOnly or BlueprintReadWrite specifiers.

    Enums (UENUM)

    Use UENUM for enum classes used in a UObject context. These must be defined as an enum class of uint8 type.

    UENUM()
    enum class EMyEnum : uint8
    {
      ...
    };
  9. Use Public and Private subfolders within Runtime

    main

    The Runtime directory is further organized into Public and Private subfolders to manage API visibility:

    • Public folder: Should contain files related to the public API.
    • Private folder: Contains implementation details, private classes, and functions that are not part of the public interface.

    Dependency Rules:

    • Files in Private can reference files in Public.
    • Files in Public cannot reference files in Private.

    If a class in the Public folder requires a type defined in Private (for example, as a member variable), you should use a forward declaration to avoid the dependency. If a forward declaration is not possible, you must move the required class to the Public folder, even if it is not intended to be part of the public API.

  10. Understand Metadata Value Types in EXT_structural_metadata

    main

    In v2.0, ECesiumMetadataTrueType is deprecated. Metadata types are now defined by the FCesiumMetadataValueType struct, which models the EXT_structural_metadata specification.

    An FCesiumMetadataValueType consists of:

    1. ECesiumMetadataType: The type of the class property (e.g., Scalar, Vec2, String).
    2. ECesiumMetadataComponentType: The component type (e.g., Float32, Uint8), applicable to scalar, VECN, and MATN types. Set to None for others.
    3. bIsArray: A boolean indicating if the property is an array of the specified type.