CesiumGS / CesiumGS/cesium-native

Failed textures from glb files referred from i3dm files

Open Beginner friendly
#1,378 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
623
Forks
277
PR merge metrics
No merged PRs in 30d

Description

Commit fc6b169a3 (Nov 26, 2025, "Read a glTF file's external data before postprocessing") seems to have introduced a bug causing missing external textures referred from .glb files that in turn are referred from .i3dm files in a sub-folder.

See the Cesium for Unreal forum topic here: https://community.cesium.com/t/i3dm-textures-fail/46363

Not being a Cesium developer myself, I turned to Claude who describes it like this:

**What the old code did correctly:**
After getGltf() returned, it explicitly called GltfReader::resolveExternalData(..., baseUri, ...) — passing baseUri (the .glb's absolute URL) as the base for resolving the .glb's own external resources.

**What the refactor broke:**
The new code eliminated that explicit resolveExternalData call because BinaryToGltfConverter::convert now calls readGltfAndExternalData internally. But the lambda still passed the original assetFetcher (with baseUrl = the i3dm's URL) to BinaryToGltfConverter::convert — so the .glb's external references (textures, buffers) were resolved against the i3dm's directory instead of the .glb's directory.

I tried attaching Claude's modified I3dmToGltfConverter.cpp but it didn't work. But here's the alternative getGltf implementation:
```
auto getGltf = [&]() -> CesiumAsync::Future {
if (header.gltfFormat == 0) {
// Recursively fetch and read the glTF content.
return assetFetcher.get(baseUri).thenImmediately(
[options, assetFetcher, baseUri](AssetFetcherResult&& assetFetcherResult)
-> CesiumAsync::Future {
if (assetFetcherResult.errorList.hasErrors()) {
GltfConverterResult errorResult;
errorResult.errors.merge(assetFetcherResult.errorList);
return assetFetcher.asyncSystem.createResolvedFuture(
std::move(errorResult));
}
// Use baseUri (the .glb's URL) as the base for resolving the
// .glb's own external references (textures, buffers, etc.).
// Using the original assetFetcher.baseUrl would resolve them
// relative to the i3dm's location instead.
AssetFetcher glbFetcher = assetFetcher;
glbFetcher.baseUrl = baseUri;
return BinaryToGltfConverter::convert(
assetFetcherResult.bytes,
options,
glbFetcher);
});
} else {
return BinaryToGltfConverter::convert(gltfData, options, assetFetcher);
}
};
```

Sorry for this newbie issue description. I should probably have created a PR instead, but lack of experience and time forced me to do it this way. I hope someone can introduce this easy fix.

Contributor guide

Open the contributing guide

Research direction

Start in I3dmToGltfConverter.cpp at the getGltf lambda and compare how the GLB fetcher’s base URL is passed to BinaryToGltfConverter::convert. Reproduce the nested i3dm/GLB case described in the issue or forum topic, then verify that external textures and buffers are resolved relative to the GLB URL rather than the i3dm directory.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.