fix: enable export of scenes with KTX2 compressed textures#5142
fix: enable export of scenes with KTX2 compressed textures#5142diegoteran merged 3 commits intogoogle:masterfrom
Conversation
Call setTextureUtils() on GLTFExporter to decompress CompressedTexture instances before serialization. The decompress wrapper preserves the original texture's flipY to prevent orientation issues during export. Fixes google#5010.
diegoteran
left a comment
There was a problem hiding this comment.
I tested this change locally and the model disappears once the export is requested.
This is a regression, can you look into this?
Thank you
Replace Three.js's decompress() with a custom implementation that renders compressed textures to a WebGLRenderTarget instead of the renderer's main canvas. This avoids corrupting model-viewer's shared renderer state, which caused the model to disappear after export. The custom decompressTexture() function: - Uses an off-screen WebGLRenderTarget (no canvas corruption) - Saves and restores renderer toneMapping state - Handles sRGB color space conversion in the shader - Preserves the original texture's flipY property Also strengthen the KTX2 export test to verify the scene remains intact (materials, dimensions) after export, before re-importing.
|
Thank you for catching that @diegoteran, and sorry for the delayed fix! Root cause: The original implementation used Three.js's built-in WebGLTextureUtils.decompress(), which renders to the renderer's main canvas. This caused three issues:
Fix: Replaced decompress() with a custom decompressTexture() that renders to a WebGLRenderTarget instead of the main canvas. This avoids all renderer state corruption, handles sRGB color space conversion correctly in the shader, and properly disposes GPU resources. Also strengthened the test to verify the scene remains intact after export (materials and dimensions unchanged) before re-importing. Tested across all CI browser contexts (2x WebKit, 2x Chromium) and manually verified the full export/re-import round trip with a production KTX2 model.
|
diegoteran
left a comment
There was a problem hiding this comment.
Hey Douglas, thanks for the fix! I do appreciate the effort and descriptive messages you add.
Can we simplify the KTX2 export by passing null as the renderer to Three's decompress() instead of using the custom shader approach?
Passing null forces Three to create a temporary, isolated WebGL context, which avoids the canvas corruption without needing custom code in our repo. This would allow us to drop the custom decompressTexture function entirely. The trade-off is a performance hit for creating the context, but since export is a one-off action, it seems worth it to reduce maintenance.
…ted context Replace custom decompressTexture() (shaders, render targets, pixel readback) with Three.js's built-in decompress() from WebGLTextureUtils, passing no renderer so Three creates and disposes a temporary WebGL context. This avoids corrupting model-viewer's shared canvas without maintaining custom GPU code.
|
Hello again, hopefully this is our last round on this fingers crossed! The commit passed all tests locally, and worked as intended when tested manually on the same production model as I used previously. Changes: Replaced the custom decompressTexture() with Three.js's built-in decompress() from WebGLTextureUtils, passing no renderer so it creates and disposes a temporary WebGL context. This drops ~87 lines of custom shader/render target code. As you noted, the minor perf hit is negligible for a one-off export action |

Reference Issue
Fixes #5010 — Cannot export scene with KTX2 compressed model
Summary
exportScene()fails when the scene contains KTX2 compressed textures becauseGLTFExporterencountersCompressedTextureinstances it can't serialize. Three.js requiressetTextureUtils()to be called with adecompressfunction to handle these. This was noted as a known limitation in #5141.This PR wires up Three.js's
WebGLTextureUtils.decompress()on the exporter, reusing model-viewer's existing shared renderer. The wrapper also preserves the original texture'sflipYproperty, whichWebGLTextureUtils.decompress()doesn't copy — without this, exported KTX2 textures would be vertically flipped sinceCompressedTextureusesflipY = falsewhile the decompressedCanvasTexturedefaults totrue.Changes
scene-graph.ts: CallsetTextureUtils()onGLTFExporterwith a decompress wrapper that preservesflipYtexture-spec.ts: Added round-trip test — create KTX2 texture, export, re-import, verify model loadsTesting