CesiumGS / CesiumGS/cesium-native
Failed textures from glb files referred from i3dm files
- 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
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