Skip to content

Fix material settings not applying to GLTF models#5708

Open
evnchn wants to merge 1 commit intozauberzeug:mainfrom
evnchn:gltf-material-fix
Open

Fix material settings not applying to GLTF models#5708
evnchn wants to merge 1 commit intozauberzeug:mainfrom
evnchn:gltf-material-fix

Conversation

@evnchn
Copy link
Copy Markdown
Collaborator

@evnchn evnchn commented Feb 2, 2026

Motivation

Fix #3515, where GLTF models load asynchronously as Groups, so material settings were being ignored.

Particularly, there are 3 cases to resolve (from #3515 (comment)):

  1. Calling material() on a group (or GLTF object) does not work, because the JavaScript implementation of the material() method doesn't traverse children of groups.
  2. Calling material() on a group (or GLTF object) does not work, because the group is added first and their children are added later. So traversing the group doesn't help, because there are no children yet.
  3. Calling material() immediately after creating the GLTF object does not work, because the GLTF loader loads the object(s) asynchronously. Only when this is finished, material() can actually find the corresponding objects.

NOTE: right now the test sample I can only test the GLTF case, not the group case though.

Implementation

TL-DR: Now stores pending material info on Group objects and applies settings to all mesh children after the model loads.

Material handling improvements:

  • Refactored the material method: Now, if the target object is a group (e.g., a GLTF model), material settings are stored in pendingMaterialInfo and applied to all child meshes once the group is loaded. For non-group objects, material settings are applied directly.
  • Added the applyMaterialSettings method to encapsulate the logic for updating material properties and refactored existing code to use this method.

GLTF loading enhancements:

  • Modified the GLTF loader callback to check for pendingMaterialInfo on a group after loading, and apply material settings to all child meshes using applyMaterialSettings. This ensures that material updates requested before model load are not lost.

Progress

  • I chose a meaningful title that completes the sentence: "If applied, this PR will..."
  • The implementation is complete.
  • If this PR addresses a security issue, it has been coordinated via the security advisory process.
  • Pytests pending. Likely tough and may require pixel-counting...
  • Documentation is not necessary for a bugfix.

GLTF models load asynchronously as Groups, so material settings were
being ignored. Now stores pending material info on Group objects and
applies settings to all mesh children after the model loads.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@evnchn evnchn added the bug Type/scope: Incorrect behavior in existing functionality label Feb 2, 2026
@evnchn
Copy link
Copy Markdown
Collaborator Author

evnchn commented Feb 2, 2026

This indeed does not fix the group case:

    with ui_3d_scene.group().material('#00ffff', side='both'):
        ui_3d_scene.box(1, 1, 1)

Box still white...

@evnchn
Copy link
Copy Markdown
Collaborator Author

evnchn commented Feb 2, 2026

Not sure if we really should fix the group case. I got a fix in local which does it, but then I don't see a way to override the material once an object is in a group, which sounds hardly like a good idea.

@falkoschindler
Copy link
Copy Markdown
Contributor

I just added MREs for each of the three problems: #3515 (comment).

# Problem 1: Group material is not applied to child objects
with ui.scene() as scene:
    with scene.group() as group:
        scene.sphere()
    group.material('red')

# Problem 2: Group is still empty when created with material
with ui.scene() as scene:
    with scene.group().material('red'):
        scene.sphere()

# Problem 3: GLTF loader is not ready when applying material
avocado = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/Avocado/glTF-Binary/Avocado.glb'
with ui.scene() as scene:
    scene.gltf(avocado).material('red').scale(100)

At the moment, this PR only solves problem 3.

Solving problem 2 could be considered a breaking change, because it would turn this blue sphere red:

with scene.group().material('red'):
    scene.sphere().material('blue')

Instead we could agree that material is only applied to the currently existing children and you need to apply it later (like in the first example) if you want to color all children.

Problem 1 could probably be easily solved by traversing the group when applying material.

Overall I'm not 100% sure about the desired behavior. If Three.js doesn't automatically apply group material to children, maybe we shouldn't try doing it ourselves. Maybe there's a way to initialize objects with undefined material so that Three.js automatically applies their group's material (if any)?

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

Labels

bug Type/scope: Incorrect behavior in existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Double-sided material property not applied to objects loaded from gltf

2 participants