[pull] master from google:master#171
Open
pull[bot] wants to merge 298 commits into
Open
Conversation
* fix firefox video framerate * refactor
* fix video texture on Firefox * fix Safari import maps
* fixed unload VideoTexture * fix lottie update
* Fix how "target" is handled * Support lights * Update config * Update screenshots * Update for lights again
* switch three to peer dep * update readme * updated faq
* Update Filament to 1.31.5 and re-render goldens * Run npm i * Bump Filament version and npm i
* updated three.js to r150 * update types
* updated to three.js r151 * r151.2 * updated goldens
* updated to three.js r151 * r151.2 * updated goldens * DO NOT USE LEGACY LIGHTS * fix two goldens
* Make peer dependency relationship clear in readme * Update README.md
) * prevent iOS WebGL memory leak by converting gainmap to DataTexture Converts the decoded gainmap from QuadRenderer to a standalone DataTexture using `result.toDataTexture()`. This allows the intermediate WebGLRenderTarget instances and their underlying WebGL framebuffers/textures to be completely disposed of immediately via `result.dispose(true)`. Includes a try-catch fallback to return the raw render target texture via `result.dispose(false)` if the environment does not support the conversion. This resolves iOS-specific WebGL context loss crashes and browser tab reloads occurring when decoding multiple UltraHDR / Gainmap environments in the "Lighting & Skybox" section. * fix(texture-utils): prevent iOS WebGL memory leak by converting gainmap to DataTexture Converts the decoded gainmap from QuadRenderer to a standalone DataTexture using `result.toDataTexture()`. This allows the intermediate WebGLRenderTarget instances and their underlying WebGL framebuffers/textures to be completely disposed of immediately via `result.dispose(true)`. Includes a try-catch fallback to return the raw render target texture via `result.dispose(false)` if the environment does not support the conversion. This resolves iOS-specific WebGL context loss crashes and browser tab reloads occurring when decoding multiple UltraHDR / Gainmap environments in the "Lighting & Skybox" section. Also adds: - A regression unit test in `TextureUtils-spec.ts` validating proper disposal of intermediate WebGLRenderTargets during gainmap decoding. * fix(texture-utils): resolve iOS WebGL memory leak on LDR/gainmap env map loading Converts the decoded gainmap from QuadRenderer to a standalone DataTexture using `result.toDataTexture()`, explicitly setting `needsUpdate = true` to trigger GPU WebGL texture upload of the pixel buffer. This allows the intermediate WebGLRenderTarget framebuffers and render buffers to be completely disposed of immediately via `result.dispose(true)`. This resolves a critical iOS-specific WebGL context loss crash loop when scrolling through galleries with multiple environment maps (the "Lighting & Skybox" examples). Implements a robust verification blank-check fallback: - Dynamically inspects the decoded DataTexture pixel buffer on the CPU. - Transparently falls back to the original render target texture if the converted DataTexture is completely black (frequent in headless testing environments with GPU-less software rendering). - Prevents overriding of the colorSpace to SRGBColorSpace on half-float decoded textures, which is invalid under standard WebGL. Also adds a regression unit test in `TextureUtils-spec.ts` to verify proper disposal of intermediate WebGLRenderTargets during gainmap decoding.
Mobile WebKit/Blink engines (such as Safari and Chrome on iOS) automatically zoom in the entire page viewport when a text or numeric input is focused if the computed font-size is strictly less than 16px. This page-level zoom remains locked even after the input is blurred, disrupting the layout and forcing users to manually pinch-to-zoom out to return to normal dimensions. This change fixes the issue globally for the documentation examples and provides educational guidelines for developers copying the patterns: 1. Systemic Styles Guard: Added a scoped style rule in examples.css setting 'font-size: 16px' for all inputs and selects inside floating '.controls' containers. This covers active widgets (e.g. Model Transformations, Post-Processing, variant swapping) across almost all example pages without impacting other standard utility forms. 2. Developer Guidance Tip: Appended an educational "Mobile WebUX Tip" callout under the Model Transformations demo text block inside scenegraph/index.html, explaining the 16px browser threshold quirk to developers who copy-paste the custom markup blocks for their own standalone applications. Verified layout presentation and viewport locking (locked at stable 1.0x scale) under small-screen interaction models. Playwright test suite passes cleanly.
…visualViewport layout for virtual keyboards (#5153)
…creen-space annotations (#5155)
This commit resolves separate layout overlap issues where large, multi-row control panels block the default bottom-right system AR button on narrow and high-density viewports (e.g., Samsung Galaxy S22 and Pixel 9 Pro Fold). Following a responsive custom AR button strategy, this change: 1. Adds a custom slotted 'View in AR' button to both the 'Model Transformations' (scenegraph/index.html) and 'Dynamic Scaling' (loading/index.html) examples. 2. Keeps their respective numeric/slider controls positioned at the default bottom-left. 3. Uses CSS Container Queries with a 700px width threshold to smoothly translate the custom AR button to the top-right corner on compact or high-density mobile viewports, ensuring complete visual separation and easy physical access. These changes have been visually validated side-by-side inside standalone simulated viewports for absolute layout robustness.
…inputs (#5156) On Samsung and Chrome mobile platforms, simple unconfigured inputs are analyzed by standard browser credentials heuristics, causing Gboard to incorrectly display sensitive password/payment/address autofill strip buttons on measurement values. This fix updates the model transform fields to use safe, explicit attributes: - type="number" and step="any" to prevent general text field heuristics - inputmode="decimal" to guarantee decimal numeric layout triggers - autocomplete="off", autocorrect="off", autocapitalize="none", spellcheck="false" parameters to disable autofill caching pipelines Additionally, this commit adds clean CSS overrides to suppress default browser increment/decrement spin arrows and enforce the compact 40px styling rules on desktop
…standard HTML <select> elements when active inside WebXR DOM Overlays. (#5157) In mobile browsers (such as Chrome on Android), standard <select> tags attempt to open OS-native dialog wheels or bottom sheets. Because these platform-native widgets reside outside the DOM tree, they cannot render correctly over the WebXR context. This results in the element's actual hit-test bounds shifting to the bottom center of the screen, leaving the visual dropdown field unresponsive to direct clicks, while tapping empty space at the bottom triggers the option wheel. To solve this, we created and integrated a standard-compliant, safe, and ultra-lightweight <custom-select> Web Component (custom-select.js) across all scene-graph examples: - Renders dropdown option items as custom DOM elements floating entirely within the DOM Overlay hierarchy, avoiding native platform widgets. - Safe Event Interception: Catches 'beforexrselect' on the component and triggers 'event.preventDefault()' to block touches from bubbling up as unintended background WebXR 3D scene gesture inputs. - High Performance: Leverages standard Shadow DOM <slot> elements with a native 'slotchange' listener to dynamically sync light-DOM option children (replacing heavy recursive MutationObservers). - Perfect Compatibility: Exposes native getters/setters for '.value', '.selectedIndex', and a native mapped array for the '.options' collection. This allows drop-in slot swaps with zero modifications to existing scripts. - Adjusts parent '.controls' container selectors to 'overflow: visible' to let floating option panels display cleanly without clipping. - Added an interactive desktop phone simulator page (select-repro.html) that models the touch bound displacement side-by-side with our custom fix. Files Added/Modified: - packages/modelviewer.dev/examples/scenegraph/custom-select.js (Added lightweight select component) - packages/modelviewer.dev/examples/scenegraph/select-repro.html (Added dual-state visual smartphone simulator) - packages/modelviewer.dev/examples/scenegraph/index.html (Integrated custom-select and updated controls overflow styles)
…s on Chrome 142 (#5158) In recent versions of Google Chrome on Android (specifically version 142.0.7444.159 with ARCore 1.51), the default exit (X) button in the upper right corner of the screen is completely unresponsive to direct screen taps while in AR mode. Touches on the button are bypassed, passing straight through as 3D WebXR target-select gestures (which trigger background model placement/movements) instead of exiting the AR session. Root Cause: In the Shadow DOM template styling, the absolute container `.slot.exit-webxr-ar-button` is set to `pointer-events: none` (to keep empty screen coordinates transparent to 3D room actions). It relies on a generic `.slot > *` rule to restore pointer events. However, because the default close anchor `#default-exit-webxr-ar-button` is nested inside a `<slot>` element, it is a grandchild rather than a direct child of the host container. In Chrome 142's updated event hit-testing pipeline, grandchild elements nested inside a parent with `pointer-events: none` are completely skipped if intermediate structural tags (such as the slot element itself) do not explicitly declare `pointer-events: auto`. This makes the visually visible exit button a physical "ghost" target to touch hit-testing. Solution: Apply explicit `pointer-events: auto` overrides to the exit button's slot structure: 1. Enable it directly on the intermediate `<slot>` tag to open the hit-test chain. 2. Target any custom user-slotted elements with a `::slotted(*)` pseudo-element rule. 3. Assert it directly on the default fallback close button `#default-exit-webxr-ar-button`. This guarantees that both the default exit button and custom user-defined close button slots are always interactive, while keeping the rest of the absolute overlay transparent to background 3D interactions.
- Enforce explicit trailing slashes on dynamic sidebar and documentation links to prevent "Please follow this link" redirect pages from loading on clashing path structures (Annotations and Animation). - Update IS_AR_QUICKLOOK_CANDIDATE to evaluate known iOS third-party browsers (Chrome, Firefox, Edge, etc.) via User Agent first, correcting an invalid WKWebView message-handler check and enabling AR options on standard iOS Chrome tabs.
When a hotspot's light DOM element is shifted in the children list (e.g., because another hotspot is deleted in a reactive UI loop), the browser detaches and re-attaches (re-slots) the element. While the core AnnotationMixin correctly reuses the existing Three.js Hotspot instance and increments its reference count, the newly attached DOM button element never receives the current visibility attributes (such as `data-visible`) because the hotspot's general visibility state has not transitioned. This leaves the re-slotted buttons in a hollow, invisible, or un-annotated state depending on CSS selectors matching :not([data-visible]). This patch updates the addHotspot path in the AnnotationMixin to immediately set the visibility attribute and dispatch the target `hotspot-visibility` custom event on the incoming node when its associated Hotspot object already exists. This guarantees that re-slotted elements inherit and render with the correct Three.js visibility states instantly.
…unds rather than camera scale (#5168)
`updateModelTransforms` accepted an `orientation` parameter but never used it, so `<extra-model orientation="...">` had no effect. Parse the value with `parseExpressions`/`normalizeUnit` (supporting `deg`/`rad` units) and apply it as an Euler rotation on the model's quaternion, using the same `$roll $pitch $yaw` → `Euler(pitch, yaw, roll, 'YXZ')` convention as `applyTransform` and the documented `orientation` attribute. Also fix the multimodel example: bare numbers are treated as radians by the unit parser, and the axis ordering follows roll/pitch/yaw, so update the orientation attributes to `"0deg 0deg 30deg"` / `"0deg 0deg -30deg"` (yaw = third term) to produce the intended 30° Y-axis rotation. Add a test covering both initial application and dynamic updates of the orientation attribute.
* fix: emit finished event for one-time animations
Two separate bugs prevented the 'finished' event from being dispatched
when an animation was played a single time (repetitions: 1):
1. Mixer event subscriptions were lost on model load (regression from
4.3.0). The multi-model refactor recreates the animation mixers on
every setSource(), but the mixer event subscriptions are registered
only once, when the animation feature is constructed — before any
model is loaded, while there are no mixers yet. They were never
re-applied to the mixers created later, so 'finished' (and 'loop')
were never dispatched. ModelScene now remembers its subscriptions and
attaches them to every mixer it creates.
2. Setting the animationName property and then calling play() looped
forever (present in all versions). Assigning animationName schedules
an asynchronous reactive update whose updated() re-triggers playback;
it did so with the default options (repetitions: Infinity /
LoopRepeat), overwriting a play({repetitions: 1}) issued in the same
tick, so the clip looped and never finished. Using the declarative
animation-name attribute avoided this only because the property never
changed. play() now records its options and the re-trigger reuses them
instead of resetting to an infinite loop.
Both paths are covered by regression tests in animation-spec.ts.
* docs: add "Play Once" animation example
Demonstrate playing an animation a single time on demand: set animationName, reset currentTime to 0, call play({repetitions: 1}), and re-enable the control on the 'finished' event. Doubles as a live test of the one-time-animation finished fix.
Several debug `console.log` calls were left in and ship in 4.3.x, spamming
the browser console on normal use: on every page load the model-viewer base
logs `[onExtraModelChanged]`, `IntersectionObserver fired!`,
`[$updateSource] called!` and `[$updateSource] BAILING OUT EARLY!`, the AR
flow logs `Attempting to present in AR with ...`, and ARRenderer logs
`[processInput] Setting goalPosition.y ...`.
Remove these developer debug logs. The genuine error diagnostic in
Renderer.ts ("could not acquire 2d context") is left untouched.
`updateModelTransforms` parsed `<extra-model offset="...">` with a plain `split`/`Number`, so only unitless numbers (meters) worked. Parse the value with `parseExpressions`/`normalizeUnit` instead, mirroring the `orientation` handling, so each component may carry a length unit (`m`/`cm`/`mm`) while bare numbers continue to be treated as meters (e.g. `offset="-2.0 0 0"` and `offset="-200cm 0 0"` are equivalent). Document offset unit support and the existing roll/pitch/yaw orientation convention on the multimodel example, and update the property JSDoc. Add a test covering unit-suffixed offsets and that bare numbers stay in meters.
…and update environment setup (#5174)
* prevent parallel deployment collisions in documentation workflow * ci: cache Playwright browsers and add concurrency to unit-tests
Fixes #5186. ### Summary & Root Cause When an `<extra-model>` child element was removed from `<model-viewer>` (e.g. via `element.remove()`), the DOM child change triggered a `slotchange` event, which invoked `this[$updateSource]()`. However, because the primary model had already finished loading, `this.loaded` remained `true`. Inside `[$updateSource]()`, an early return check (`if (this.loaded || ...)`) caused the update to bail out before invoking `scene.setSource()`. As a result, the removed `<extra-model>`'s 3D mesh was orphaned and remained rendered inside the Three.js scene graph. ### Changes Made * **`model-viewer-base.ts`**: * Updated `[$onSlotChange]` to reset `this[$loaded] = false;` and `this[$loadedTime] = 0;` whenever the list of `<extra-model>` URLs or element count changes. This allows `[$updateSource]()` to proceed past the load guard and properly synchronize the Three.js scene graph with the DOM. * Added explicit handling for the edge case where all primary and extra models are removed (`this.src == null && extraModels.length === 0`), directly invoking `this[$scene].reset()` and clearing loaded state. * **`ModelScene.ts`**: * Updated `reset()` to ensure `this.extraUrls = [];` is properly cleared during scene resets. * **`extra-model-spec.ts`**:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot]
Can you help keep this open source service alive? 💖 Please sponsor : )