Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: playcanvas/engine
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.19.1
Choose a base ref
...
head repository: playcanvas/engine
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.19.2
Choose a head ref
  • 9 commits
  • 32 files changed
  • 6 contributors

Commits on May 29, 2026

  1. fix(particle): null module-scope vars after construction to prevent m…

    …emory leak (#8809)
    Skullheadx authored and Martin Valigursky committed May 29, 2026
    Configuration menu
    Copy the full SHA
    62c484d View commit details
    Browse the repository at this point in the history

Commits on Jun 1, 2026

  1. feat(examples): share dialog with state-encoded URLs (#8796)

    * feat(examples): persist app state in url hash
    
    Adds examples/src/app/url-state.mjs and wires it into Sidebar, Menu,
    MainLayout, Example, CodeEditor, and DeviceSelector so that UI state
    (sidebar collapsed, filter, fullscreen, mobile panel, control overrides,
    device, selected file) survives reloads and is shareable via the hash.
    
    State is serialized one-way as a base64 JSON blob in ?s=. Recipient
    reads it on initial load only; browser back/forward and manual URL
    edits do not sync mid-session.
    
    Control overrides use a 2s settle-window heuristic so async-init
    observer mutations (e.g. gsplat lod-streaming's assetListLoader.load
    callback) update the local baseline rather than polluting the URL.
    URL-provided overrides are re-applied if the example tries to clobber
    them during the window.
    
    * perf(examples): deflate + base64url url-state for smaller share links
    
    Wraps the JSON payload in fflate's deflateSync before base64url-encoding,
    and shortens the top-level keys (device->d, ui->u, controls->c) before
    serialization. For a worst-case gsplat default-state dump this cuts the
    ?s= from 500 to 338 chars (~32%); typical user-diff payloads drop from
    ~128 to ~98 chars.
    
    base64url avoids the +/= chars that URLSearchParams URL-decodes
    incorrectly on read, so the encoded value round-trips cleanly through
    window.location.hash without manual escaping.
    
    * fix(examples): recursive leaf-level diff for url controls
    
    Top-level diff was dumping entire nested control trees (e.g. the orbit
    example's `attr` namespace) when only one leaf changed. Now walks both
    baseline and current in parallel and emits flat dot-paths for the
    specific leaves that differ.
    
    Also switches baseline capture to a one-shot snapshot at settle-window
    end (via setTimeout) so async-init values like orbit's `data.set('attr',
    { ...defaults })` after `app.start()` are folded into baseline cleanly
    instead of being tracked path-by-path.
    
    Re-apply logic for URL overrides during settle now handles the case
    where the example writes a parent of the overridden path (e.g. URL has
    `attr.rotateSpeed=0.1` and example does `data.set('attr', { whole
    defaults })` — every overridden leaf under `attr.` is re-applied).
    
    For the user's orbit case (2 changed leaves under attr): payload drops
    from ~280 to ~138 chars.
    
    * feat(examples): copy-to-clipboard share button + crawler-friendly share page
    
    Replace the legacy tweet button with a share button that copies a state-
    encoded URL to the clipboard. Inline SVG share-2 glyph (Feather), flex-
    centered to match the icon-font buttons, with orange-flash + checkmark
    swap feedback for 1.5s after a successful copy.
    
    buildShareUrl now targets `${origin}/share/<category>_<example>/?s=...`
    so shared links flow through the crawler-friendly share page. Falls back
    to the canonical hash URL on the index route.
    
    Rewrite templates/share.html from a meta-refresh redirect into a thin
    SPA-bootstrap page:
    - per-example twitter:card + og:* meta tags
    - absolute asset paths so they resolve from /share/<slug>/
    - inline script history.replaceState's to /#/<path>?s=... before the
      bundle boots, forwarding the ?s= state; recipient lands directly on
      the SPA with no visible interstitial.
    
    Fixes the previous template's broken twitter:url (`playcanvas.github.io/
    <path>` 404'd since the SPA uses hash routing) and meta-refresh dropping
    the ?s= state.
    
    The share-page origin is resolved at build time from `VERCEL_URL`
    (every Vercel preview/production deploy gets its own self-referential
    meta), with `SHARE_ORIGIN` env override and a `playcanvas.vercel.app`
    fallback for local prod-target builds. Dev server passes the request's
    own scheme+host so localhost browsing yields localhost meta tags.
    
    Rename `utils/build-shared.mjs` -> `utils/build-examples.mjs` (the
    module exports the whole example-build pipeline, not just shared
    utilities). Update importers in build-prod, vite-dev-server, thumbnails.
    Split writeShareHtml into createShareHtml (returns string) +
    writeShareHtml (writes to disk) so the dev server can render the page
    inline without staging dist/.
    
    Drop the production-only gate on share-page generation so dist/share/*
    exists for local preview builds too. Add a /share/<slug>/ route to the
    vite dev server using createShareHtml.
    
    Add `#shareButton.selected` to the existing menu .selected CSS rule so
    the click feedback actually shows.
    
    * chore(examples): drop unrelated changes from url-state branch
    
    Revert the gizmo snap-increment override logic and colorAlpha removal in
    misc/editor and the stray comment removal in gaussian-splatting/lod-
    streaming. Neither was needed for url-state persistence or the share
    flow; keeping the PR to the minimal diff for that task.
    
    * feat(examples): share dialog with social buttons and copy link
    
    Replace the immediate-copy share button with a "Share this page" modal
    modelled on super-splat's. Click the share icon → dialog with title +
    close, four platform-colored social buttons (Facebook / Reddit / X /
    LinkedIn) that open the platform's intent URL in a popup, a "OR COPY
    LINK" divider, a read-only URL input, and a Copy button that shows a
    "Copied" state for ~1.5s.
    
    ShareDialog.mjs is a new self-contained component. It calls buildShare-
    Url() lazily on each open so the URL reflects current state, derives a
    "<example name> - PlayCanvas Examples" title from the hash path, opens
    each social intent via window.open(..., 'noopener,noreferrer,width=600,
    height=400'), and closes on backdrop click, X button, or Escape.
    
    Styling keys off the examples overlay palette so the dialog reads as a
    native surface, not a bolt-on: rgba(54, 67, 70, 0.95) panel, 6px radius,
    14px title, 40px brand-colored socials (4px radius), 30px input + copy
    button — all in line with the description/credits overlays and the 32px
    menu rhythm above.
    
    * fix(examples): capture control baseline early on user interaction
    
    The 2s settle window was meant to let example async-init `data.set` calls
    land in the baseline before user diffs start tracking. But if the user
    changes a control inside that window (e.g. pasting a Scene.url into
    gaussian-splatting/lod-streaming right after the page loads), the
    modified value gets folded into the baseline at settle end and so never
    appears in the shared URL — even though subsequent changes outside the
    window do.
    
    Capture the baseline as soon as the user interacts with #controlPanel
    (via document-level capture-phase pointerdown/focusin), whichever comes
    first. Async-init writes that fire before the user touches anything
    still flow into the baseline as intended; anything after the first
    interaction is treated as a real user diff.
    
    * Revert "fix(examples): capture control baseline early on user interaction"
    
    This reverts commit d33f3b4.
    
    * fix(examples): lod-streaming respects share-URL Scene.url override
    
    app.start() fires 'start' synchronously, which in turn fires the
    exampleLoad event and runs Example.mjs's applyControlState — all before
    the example reaches its explicit loadGsplat call. applyControlState
    writes the share-URL state value into observer.url, but the example's
    url:set handler isn't registered yet, so the load isn't triggered. Then
    the explicit `await loadGsplat(paramUrl || null)` ignores the observer
    value and loads the default.
    
    Read the observer's url (populated by applyControlState OR by the
    example's own paramUrl-driven data.set) for the initial load instead of
    hardcoding paramUrl. Other cases (no override, ?url= hash, future user
    edits via the url:set handler) are unchanged.
    kpal81xd committed Jun 1, 2026
    Configuration menu
    Copy the full SHA
    0282eba View commit details
    Browse the repository at this point in the history

Commits on Jun 3, 2026

  1. feat(webgpu): support HTML-in-Canvas textures via copyElementImageToT…

    …exture (#8810)
    
    * feat(webgpu): support HTML-in-Canvas textures via copyElementImageToTexture
    
    Adds WebGPU support for the HTML-in-Canvas API, previously WebGL-only.
    Generic HTML elements can now be used as live texture sources on the
    WebGPU backend.
    
    - WebGPU device reports supportsHtmlTextures = true when the browser
      exposes queue.copyElementImageToTexture
    - WebGPU 2D texture uploads route generic HTML elements through a new
      copyElementImageToTexture path
    - Updated html-texture and html-texture-configurator examples
    
    * update
    
    ---------
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    mvaligursky and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    ef37e84 View commit details
    Browse the repository at this point in the history
  2. fix: update HTML-in-Canvas texture upload to new API (#8828)

    WebGPU copyElementImageToTexture now takes source and destination
    dictionaries instead of positional args. WebGL texElementImage2D now
    takes (target, internalformat, element), dropping level/srcFormat/destType.
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    mvaligursky and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    90802d4 View commit details
    Browse the repository at this point in the history
  3. docs: replace opaque any/* types with real types in public API refere…

    …nce (#8811)
    
    Two public, reference-visible members documented their JSDoc types as the opaque `*`/`any`, which renders as a meaningless `any` in the generated TypeDoc API reference:
    
    - `RenderTarget.getColorBuffer(index)` - `index` is an array index, typed `{*}` -> `{number}`.
    - `InputConsumer.update()` - the base method discards the frame and returns nothing, so the bogus `@returns {any}` is dropped (per the engine convention of omitting `@returns` when nothing is returned); the `InputController` subclass override already documents its `{Pose}` return.
    
    Both compile cleanly (build:types/test:types); the generated declarations now read `getColorBuffer(index: number): Texture` and `InputConsumer.update(...): void` / `InputController.update(...): Pose`.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    2 people authored and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    2e4455a View commit details
    Browse the repository at this point in the history
  4. docs: fix @param names/types that contradict the function signatures (#…

    …8813)
    
    Four JSDoc `@param` blocks documented names (and in two cases types) that
    did not match the actual parameters, surfaced by enabling jsdoc/check-param-names
    (currently off in the shared config):
    
    - `Morph._createTexture` documented `levels` before `arrayLength`, but the
      signature is `(name, format, arrayLength, levels)` - so the emitted types were
      swapped (`arrayLength` as `{Array}`, `levels` as `{number}`). Reordered to match.
    - `XrViews.update` documented `xrView` (`{XRView}`); the parameter is `xrViews`
      and is iterated as an array, so corrected to `{XRView[]} xrViews`.
    - `WebgpuXrBridge.getFramebufferSize` / `getViewport` documented `frame`; the
      parameter is the intentionally-unused `_frame`. Matched the docs to it.
    
    Verified: jsdoc/check-param-names reports 0 mismatches, lint clean,
    build:types/test:types pass.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    2 people authored and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    537521a View commit details
    Browse the repository at this point in the history
  5. docs: elevate the Entity class reference to peak quality (#8814)

    First page in a page-by-page effort to bring flagship API reference pages up to
    the standard already set by the best pages (e.g. GSplatComponent, Vec3).
    
    - Rewrites the Entity class overview into an authored introduction: the mental
      model (a scene-graph node plus a set of components), the two facets it combines
      (transform from GraphNode + components), and the typical workflow
      (addComponent / accessor / removeComponent / enabled / destroy), with tight
      cross-links to Component, GraphNode, AppBase#root and the common components.
    - Adds two canonical, runnable class-level `@example`s (create a camera entity
      and add it to the scene; parent/child transform inheritance).
    - Fixes an accuracy bug: `findComponent()` was typed `{Component}` but actually
      returns `null` when nothing is found (via `GraphNode#findOne`) - now
      `{Component|null}`.
    
    The 21 component accessors are intentionally left terse, and the other methods
    were already at the bar.
    
    Verified: lint clean, docs build with 0 errors and no new warnings, build:types
    and test:types pass (`findComponent(type: string): Component | null`).
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    2 people authored and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    c90406c View commit details
    Browse the repository at this point in the history
  6. fix(gsplat): apply material changes made in frame:ready the same frame (

    #8830)
    
    Material state changed from a frame:ready event listener (e.g.
    app.scene.gsplat.antiAlias = ...) was silently dropped: the renderer's
    per-frame material sync ran before the event fired, and frameEnd()
    cleared the material dirty flag before any later frameUpdate could
    observe it. Move frameUpdate() to after fireFrameReadyEvent() so such
    changes are synced and applied the same frame.
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    mvaligursky and Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    9cc0291 View commit details
    Browse the repository at this point in the history
  7. 2.19.2

    Martin Valigursky committed Jun 3, 2026
    Configuration menu
    Copy the full SHA
    3f52bb8 View commit details
    Browse the repository at this point in the history
Loading