Summary
After editing an element with editor.edit(), the edited geometry lives in a generated delta model attached as a child of the parent model. FastModelPicker.renderPickPass() hides each model root before rendering it individually, which means the delta model never gets rendered into the picker buffer (Three.js will not render a child whose ancestor has visible = false). This causes picking/highlighting to fail on edited elements until editor.save(modelId) is called.
Forum thread (Antonio González Viegas confirmed it as a bug and asked me to file here):
https://people.thatopen.com/c/ask-the-community/edited-element-stays-in-delta-model-after-transform-edit-causing-picking-highlight-issues-unless-editor-save-is-called
Versions
@thatopen/fragments@3.4.5
@thatopen/components@3.4.5
@thatopen/components-front@3.4.3
three@0.183.2
Steps to reproduce
- Load a Fragment model.
- Select an element via
editor.getElements(modelId, [localId]).
- Move it with TransformControls and apply through
element.setMeshes(mesh) → element.getRequests() → fragments.editor.edit(modelId, requests).
- Call
fragments.update(true).
- Try to click/pick the edited element.
Expected
Edited element should remain pickable and highlight correctly without calling editor.save().
Actual
- Edited element is rendered through the delta model but appears visually inconsistent ("multicolored").
- Picking/highlighting passes through the edited element or behaves incorrectly.
- Calling
editor.save(modelId) fixes it (delta is committed back), but causes a model reload and visible flicker.
Root cause analysis
In @thatopen/fragments, the delta model is attached under the parent model object:
parentModel.object.add(deltaModel.object)
In @thatopen/components, FastModelPicker.renderPickPass() builds a map of fragments.list model objects, hides every model root, and then renders each one individually:
for (const obj of objectsByModel.values()) {
obj.visible = false
}
for (const [byte, modelId] of byteToModel) {
const obj = objectsByModel.get(modelId)
obj.visible = true
renderer.render(scene, camera)
obj.visible = false
}
When the picker reaches the delta model, the parent model root is still hidden. Three.js does not render children whose ancestor is hidden, so the delta model is not drawn into the picker buffer.
Workaround
Re-parent the delta model directly to the scene whenever it's added:
function keepDeltaModelPickable(model, scene) {
if (!model.isDeltaModel || !model.object) return
const attach = () => {
if (model.object.parent !== scene) scene.add(model.object)
}
attach()
window.setTimeout(attach, 0)
window.setTimeout(attach, 50)
}
// hook on fragments.list.onItemSet and on each model.tiles.onItemSet
After applying this, picking/highlighting works correctly without needing editor.save().
Suggested fixes (either should work)
- Attach delta models to the scene root instead of under the parent model object in
@thatopen/fragments.
- Account for hidden ancestors in
FastModelPicker.renderPickPass() — temporarily unhide the chain to the picked model, or render delta models in their own pass.
Maintainers are better placed to pick the right path.
Summary
After editing an element with
editor.edit(), the edited geometry lives in a generated delta model attached as a child of the parent model.FastModelPicker.renderPickPass()hides each model root before rendering it individually, which means the delta model never gets rendered into the picker buffer (Three.js will not render a child whose ancestor hasvisible = false). This causes picking/highlighting to fail on edited elements untileditor.save(modelId)is called.Forum thread (Antonio González Viegas confirmed it as a bug and asked me to file here):
https://people.thatopen.com/c/ask-the-community/edited-element-stays-in-delta-model-after-transform-edit-causing-picking-highlight-issues-unless-editor-save-is-called
Versions
@thatopen/fragments@3.4.5@thatopen/components@3.4.5@thatopen/components-front@3.4.3three@0.183.2Steps to reproduce
editor.getElements(modelId, [localId]).element.setMeshes(mesh)→element.getRequests()→fragments.editor.edit(modelId, requests).fragments.update(true).Expected
Edited element should remain pickable and highlight correctly without calling
editor.save().Actual
editor.save(modelId)fixes it (delta is committed back), but causes a model reload and visible flicker.Root cause analysis
In
@thatopen/fragments, the delta model is attached under the parent model object:In
@thatopen/components,FastModelPicker.renderPickPass()builds a map offragments.listmodel objects, hides every model root, and then renders each one individually:When the picker reaches the delta model, the parent model root is still hidden. Three.js does not render children whose ancestor is hidden, so the delta model is not drawn into the picker buffer.
Workaround
Re-parent the delta model directly to the scene whenever it's added:
After applying this, picking/highlighting works correctly without needing
editor.save().Suggested fixes (either should work)
@thatopen/fragments.FastModelPicker.renderPickPass()— temporarily unhide the chain to the picked model, or render delta models in their own pass.Maintainers are better placed to pick the right path.