Skip to content

Extension points for mesh- and material-related customizationsย #1658

@GhisBntly

Description

@GhisBntly

Feature

This is a proposal for API additions needed to implement mesh- and material-related customizations beyond the current cesium-unreal capabilities.

Our use case

We need to process the rendered cesium tiles with advanced materials that cannot be done with the current capabilities, and also because of a limitation of Unreal Engine shaders:

  • Our material shader needs to test an ID on each vertex to determine the shading: the ID is an unsigned 64bits integer, which Unreal shaders do not support, and we cannot simply cast to 32 bits because high-order bits contain relevant information.
  • Other external data not present in the tileset's metadata also need to be correlated with the ID to determine what shading characteristics to apply at any given time, which adds specific requirements:
    • The custom shading may need to change the material Blend Mode to Translucent
    • The custom shading may need the "total" bounding box in the scene (as far as the currently loaded tiles allow to determine) for each ID (which identifies a consistent sub-mesh). The rough estimate given by the first loaded and thus coarser tiles is usually sufficient.
      • (@me/Bentley) Is it really important? Including those as extra metadata in the generated tileset is probably preferrable, ultimately.

Given these requirements, this is the solution we developed, inspired from metadata-based material customization in cesium-unreal:

  • When tiles are loaded and Unreal meshes are built, we inspect each vertex to map its 64bits ID (found in the glTF tiles' metadata) to its 32bits FeatureID
  • We may need to force the use of BaseTranslucent instead of BaseOpaque as parent of the UDynamicMaterialInstance created by cesium-unreal
  • For each tile, we create textures which will be indexed by FeatureIDs (baked into the mesh UVs) in our custom material to apply the desired shading according to the external (non-tileset) data mentioned.
  • Managing the per-tile information for meshes, materials, textures and ID vs. FeatureIDs mappings means we need some basic notifications on tiles lifecycle: when a tile has finished loading, when its visibility changes, and when it is about to be unloaded.

Implementation in our cesium-unreal's fork

Translating to the cesium-unreal APIs, those are extension points we added (temp names from our forked implementation):

  1. In loadPrimitive, BakeFeatureIDsInVertexUVs = hook allowing API user to enforce the baking of FeatureIDs into a UV layer, without requiring a CesiumFeaturesMetadataComponent
    • The rationale is unclear, I guess we wanted to avoid creating a component because we didn't need any actual encoding of metadata, we only need to access the FeatureIDs in the UVs.
    • Also, we initially wanted to bake the FeatureID's on-demand, to avoid doing it for IDs that do not need it. But we failed to reliably edit the static mesh in-game (might not be possible) so we had to backtrack on that and now just bake for all meshes. The probability of having tiles with no need at all for FeatureID's in the UVs is probably low anyway.
    • Should we revert to using a CesiumFeaturesMetadataComponent or is there an advantage in performance and memory to be had by not creating this component? In that case, we could add a tileset flag to ensure this baking.
    • (@me/Bentley:WIP) we need the default FeatureID to be 0xFFFFFFFF (our shader relies on that) and not 0 as set in CesiumGltfComponent.cpp => moved to Metadata: Add a way to propagate the NullFeatureId to the GPUย #1690
  2. In loadPrimitive, bNeedsCPUAccess is passed to LODResources.VertexBuffers.PositionVertexBuffer.Init (idem index buffer) to allow access to vertex/index data after mesh creation:
    • (@me/Bentley) Check impact on memory footprint?
    • (@me/Bentley) Check if we can do without these buffer accesses (eg. compute Elements boundings boxes some other way? + check other usages). => we can, so the CPU access feature was dropped.
  3. In loadPrimitiveGameThreadPart, OnMeshConstructed = event for API users to subscribe to when a game object (UCesiumGltfPrimitiveComponent) is created
    • UCesiumGltfPrimitiveComponent is a Private interface: thus, we need either to make it Public, or pass some more parameters that we have found to be needed: native tile, material instance, CesiumGltf::MeshPrimitive, FCesiumPrimitiveFeatures, FCesiumPrimitiveMetadata, GltfToUnrealTexCoordMap
    • Our implementation needs that to: parse metadata, count FeatureID's per tile (for texture dimensions), compute per-Element BBoxes, and maintain its own structures of tiles/meshcomponents/materials (esp. for mapping to/from Elements)
  4. In loadPrimitiveGameThreadPart, CreateMaterial_GameThreadPart = hook to allow the API user to override the choice of BaseMaterial when creating a UCesiumGltfPrimitiveComponent's material instance: this is needed typically when the external data mandates that the material should be translucent, whereas cesium-unreal's criterions only would lead to an opaque material instance. Also needed to support other material customizations like our Glass material type, etc.
  5. OnTileConstructed, at the end of CreateOnGameThread = event allowing custom processing after all meshes of a tile have been created (in the game thread)
  6. OnVisibilityChanged, at the end of CreateOnGameThread = event allowing custom processing when a tile's visibility is changed by cesium's render selection algorithm
  7. BeforeTileDestruction = event allowing custom processing just before a tile's render content is unloaded

I guess I could have split into sub-issues, but I was unsure how many inter-dependencies the above items would end up having, and whether or not it would clarify or confuse discussions...

Looking forward to your input!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions