Describe the bug 📝
Bug: OBCF.Hoverer only tracks the latest-loaded model in a multi-model scene
Environment:
• @thatopen/components : 3.4.8
• @thatopen/components-front : 3.4.4
• @thatopen/fragments : 3.4.7
Symptom:
When multiple FragmentsModel s are loaded into the same World , OBCF.Hoverer ( components.get(OBCF.Hoverer) ) only raises hover events for the model that was loaded/added last. Hovering geometry belonging to any earlier-loaded model produces no onHoverStarted / onHoverEnded callback at all — as if that geometry weren't part of the pickable scene.
Repro sketch:
const hoverer = components.get(OBCF.Hoverer);
hoverer.world = world;
hoverer.enabled = true;
hoverer.onHoverStarted.add((h) => console.log(h)); // never fires for model A once model B is loaded
Load model A, hover it → works. Load model B afterward, hover model A again → no event. Only model B responds to hover.
Workaround (what we did):
Disable Hoverer ( hoverer.enabled = false ) and drive hover manually with OBC.FastModelPickers , which correctly disambiguates across all loaded models:
const fastPickers = components.get(OBC.FastModelPickers);
const picker = fastPickers.get(world);
renderer.domElement.addEventListener("pointermove", () => {
requestAnimationFrame(async () => {
const item = await picker.getItemAt(); // { modelId, localId } | null, across ALL models
// apply your own highlight styling based on item
});
});
Throttle to one pick per animation frame ( requestAnimationFrame + a "scheduled" flag) instead of firing on every raw pointermove .
Cost / why this isn't a real fix, just a stopgap:
FastModelPicker.getItemAt() isn't a CPU raycast like Hoverer uses internally — it renders an offscreen id-encoded pass per loaded model to disambiguate ownership, then does a gl.readPixels() at the cursor to decode the hit. readPixels forces a GPU→CPU sync point (a pipeline stall), and the cost scales with the number of simultaneously loaded models. Even throttled to once per frame, this is measurably more expensive than the
Reproduction ▶️
No response
Steps to reproduce 🔢
Import one frag, hover over it and it shows. Then import another frag, then the hover works over the last imported file, but not the first. Issue is when you use the built in hover method. OBCF.Hoverer
System Info 💻
Environment:
• @thatopen/components : 3.4.8
• @thatopen/components-front : 3.4.4
• @thatopen/fragments : 3.4.7
Used Package Manager 📦
npm
Error Trace/Logs 📃
No response
Validations ✅
Describe the bug 📝
Bug: OBCF.Hoverer only tracks the latest-loaded model in a multi-model scene
Environment:
• @thatopen/components : 3.4.8
• @thatopen/components-front : 3.4.4
• @thatopen/fragments : 3.4.7
Symptom:
When multiple FragmentsModel s are loaded into the same World , OBCF.Hoverer ( components.get(OBCF.Hoverer) ) only raises hover events for the model that was loaded/added last. Hovering geometry belonging to any earlier-loaded model produces no onHoverStarted / onHoverEnded callback at all — as if that geometry weren't part of the pickable scene.
Repro sketch:
const hoverer = components.get(OBCF.Hoverer);
hoverer.world = world;
hoverer.enabled = true;
hoverer.onHoverStarted.add((h) => console.log(h)); // never fires for model A once model B is loaded
Load model A, hover it → works. Load model B afterward, hover model A again → no event. Only model B responds to hover.
Workaround (what we did):
Disable Hoverer ( hoverer.enabled = false ) and drive hover manually with OBC.FastModelPickers , which correctly disambiguates across all loaded models:
const fastPickers = components.get(OBC.FastModelPickers);
const picker = fastPickers.get(world);
renderer.domElement.addEventListener("pointermove", () => {
requestAnimationFrame(async () => {
});
});
Throttle to one pick per animation frame ( requestAnimationFrame + a "scheduled" flag) instead of firing on every raw pointermove .
Cost / why this isn't a real fix, just a stopgap:
FastModelPicker.getItemAt() isn't a CPU raycast like Hoverer uses internally — it renders an offscreen id-encoded pass per loaded model to disambiguate ownership, then does a gl.readPixels() at the cursor to decode the hit. readPixels forces a GPU→CPU sync point (a pipeline stall), and the cost scales with the number of simultaneously loaded models. Even throttled to once per frame, this is measurably more expensive than the
Reproduction▶️
No response
Steps to reproduce 🔢
Import one frag, hover over it and it shows. Then import another frag, then the hover works over the last imported file, but not the first. Issue is when you use the built in hover method. OBCF.Hoverer
System Info 💻
Used Package Manager 📦
npm
Error Trace/Logs 📃
No response
Validations ✅