Visitar URL original
CompressedTextureLoader: Return cube maps as cube textures. by arodic · Pull Request #35084 · mrdoob/three.js · GitHub
Skip to content

CompressedTextureLoader: Return cube maps as cube textures. - #35084

Draft
arodic wants to merge 3 commits into
mrdoob:devfrom
arodic:fixes/compressed-texture-loader-cube
Draft

arodic wants to merge 3 commits into
mrdoob:devfrom
arodic:fixes/compressed-texture-loader-cube

Conversation

@arodic

@arodic arodic commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Description

CompressedTextureLoader returns cube maps (DDSLoader, PVRLoader) as a CompressedTexture with the faces in image, without flagging it as a cube texture. Code that checks isCubeTexture handles it as a 2D texture:

  • WebGLRenderer: as scene.background it is bound as a 2D texture, and WebGLTextures throws "Cannot read properties of undefined (reading 'length')" every frame. As envMap of a material that samples it directly, like MeshBasicMaterial, it is mirrored compared to CubeTexture, to CompressedCubeTexture and to the same texture on MeshStandardMaterial, since flipEnvMap depends on isCubeTexture and PMREM doesn't.
  • WebGPURenderer decides between 2D and cube textures by isCubeTexture, so it can't use these cube maps at all.

Fiddle (WebGLRenderer): https://jsfiddle.net/zqnj6Lts/6/
A DDS cube map as background and as envMap of two spheres. The canvas stays black. With params.background = false, the left sphere (MeshBasicMaterial) is mirrored compared to the right one (MeshStandardMaterial).

dev this PR
background: true image image
background: false image image

Whether the data hold a cube map is only known once the file has been parsed, so this PR turns the texture returned by load() into a compressed cube texture at that point: it sets isCubeTexture and isCompressedCubeTexture, as CompressedCubeTexture does, and CubeReflectionMapping unless the app has set a mapping. With the CompressedCubeTexture PR, WebGPURenderer renders these cube maps too.

The cube map reflections in webgl_loader_texture_dds are no longer mirrored, so this PR updates its screenshot. webgl_loader_texture_pvrtc changes the same way where PVRTC is supported; its screenshot stays, since the E2E browser doesn't support PVRTC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

📦 Bundle size

Full ESM build including core, minified and gzipped.

Before After Diff
Core 392.32
102.83
392.67
102.96
+351 B
+129 B
WebGL 778.64
194.36
778.99
194.50
+351 B
+134 B
WebGPU 1105.08
297.39
1105.43
297.52
+351 B
+133 B
WebGPU Nodes 1103.03
297.03
1103.38
297.16
+351 B
+132 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 518.86
124.64
518.86
124.64
+0 B
+0 B
WebGPU 783.40
209.62
783.40
209.62
+0 B
+0 B
WebGPU Nodes 729.51
196.19
729.51
196.19
+0 B
+0 B

Comment thread src/loaders/CompressedTextureLoader.js Outdated
function setCubeTexture( texture ) {

texture.isCubeTexture = true;
texture.isCompressedCubeTexture = true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid monkey-patching the is* flags.

If you want to return a compressed cube texture, use the respective CompressedCubeTexture.

@Mugen87 Mugen87 Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize now this is tricky because the loader directly hands over an instance of CompressedTexture in load(). The loader does not know at that point what type of texture is inside the texture file 🤦‍♂️.

We need a solution for this anyway since ideally we return instances of Texture for uncompressed data at some point, see #35082 (comment).

I think what KTX2Loader does is better. It does not return a texture in load(), only in the callback. Maybe we should do the same in CompressedTextureLoader.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on the monkey patching. Flags don't survive clone()

Passing the texture only to the callback sounds good. But removing the return value is breaking: map: loader.load( url ) works fine for 2D textures today, and FBXLoader / MTLLoader use the return value of a .dds handler.

How about a non-breaking first step?

  • loadAsync() resolves with the right class, CompressedCubeTexture for cube maps.
  • load() stays as is and warns deprecation when it loads a cube map. It monkey-patches the texture and points to loadAsync().
  • The DDS and PVRTC examples load their cube maps with loadAsync().

Dropping the return value could come next after few releases

wdyt?

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@arodic
arodic marked this pull request as draft October 19, 2026 15:48
@arodic
arodic force-pushed the fixes/compressed-texture-loader-cube branch from ff09c3c to 9f3029f Compare October 19, 2026 16:43

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants