Skip to content

Sync upstream develop (2026-10-07): UI redesign part 4, Component SDK C modules, 0.22.0, asset browser fixes - #27

Merged
miogds merged 8 commits into
developfrom
sync/upstream_develop_1007
Oct 7, 2026
Merged

miogds merged 8 commits into
developfrom
sync/upstream_develop_1007

Conversation

@miogds

@miogds miogds commented Oct 7, 2026

Copy link
Copy Markdown
Member

Brings the fork's develop up to upstream develop at 2de8117: seven commits.

One conflict, in Package.swift: the fork keeps its dependencies (the fork engine's develop and UntoldGaussianTwins on fork-engine); upstream's note of the release pin moves to 0.22.0. InspectorView.swift, EditorView.swift and UntoldEditorApp.swift merged by themselves.

1261 tests pass, 1 skipped as before.

miogds and others added 8 commits October 5, 2026 06:41
…oldengine#147)

* [Feature] Selection: several entities at once, each with its box, one gizmo for them all and one undo step

The selection could hold one entity. It now holds several, in the order
they were selected; the last one stays the one the Inspector names, and
the many places that set the one selected entity still select it alone.

- SelectionManager keeps selectedEntities beside selectedEntity, with
  selectEntities(_:) and toggleSelection(of:). SelectionDelegate gains
  didSelectEntities and didToggleEntity, with default implementations.
- One gizmo for all of them: it stands in the middle of their boxes and
  moves each by the same distance, turns them round it and scales them
  and their distance from it, each in its parent's measure. Entities
  under another selected one follow their parent. After a drag the
  gizmo goes back to the middle.
- A drag is one undo step, "Transform N Entities".
- Each selected entity shows its box: the highlight pass draws one per
  entity from a unit box, in the entity's own space, so the boxes follow
  what the gizmo moves with nothing rebuilt. An entity that draws nothing
  gets a small box where it stands. With one entity selected nothing
  changes.
- F frames all of them.

Nothing selects several yet: the next two commits bring the rectangle
and the Shift click in the viewport, then the hierarchy and the
Inspector.

* [Feature] Viewport: a rectangle selects what stands inside it, and a Shift click adds an entity or takes it out

A drag of the left button that takes no gizmo handle draws a dashed
rectangle over the viewport, and its release selects what stands
inside it.

- An entity is inside when all of its box is: every corner in front of
  the camera and within the rectangle. What reaches out of it is left
  out, so the floor under what the rectangle is drawn around, or the
  wall behind it, is not selected however much of it shows. A light or
  an entity written in code, which has no box, is inside where it
  stands.
- Of what draws meshes, only what is seen is selected: what stands
  behind a wall or under a floor is left out though it is inside. An
  editor-only pass tells: at the release it draws the scene's meshes
  into a texture of entity numbers the size of the rectangle and a
  compute kernel gathers which of them show, about a millisecond for a
  whole viewport. A locked entity hides what is behind it and is not
  selected; a hidden one hides nothing. When the pass cannot run, being
  inside is enough.
- With Command held the rectangle selects the assets those entities
  belong to, as a Command click does. With nothing inside, the
  selection is cleared.
- A Shift click adds the entity under the pointer to the selection or
  takes it out; a Shift drag with a selection still moves that entity.
- No rectangle is drawn while playing, over a locked preview or with
  the editor disabled, and a canvas that loses the keyboard takes it
  away without selecting.
- The hints gain "Add or remove: Shift Click", and "Select" says
  "Click or drag".

* [Feature] Hierarchy and Inspector: several entities selected

The panels follow a selection of several entities.

- Hierarchy: every selected row is highlighted and the footer counts
  them. A Shift or Command click on a row adds it to the selection or
  takes it out; a plain click selects it alone, as before. Deleting an
  entity from the menu leaves the rest selected.
- From Play to Stop the hierarchy selects nothing, as the viewport does
  not, and the footer says so.
- Inspector: with several selected it says how many, unless it is
  pinned to an entity. With one it shows that entity as before.
- H hides every selected entity.

* [Test] The highlight box test looks where no edge is, at any pixel density

test_anEntityThatDrawsNothing_hasItsBoxWhereItStands checked that the
box is drawn as lines by looking at the middle of its near face. The
box stands to the right of the view, so its far face shows through the
near one a little to the left, and the far face's right edge passes two
pixels from that middle where a point of the window is one pixel, as
on the CI runner: the test found a line there and failed. On a display
with two pixels per point the edge is four pixels away and it passed.

It now looks between that edge and the near face's own, clear of both,
and says so when a box is too few pixels wide to tell. Checked at both
sizes.

* [Patch] Rectangle selection: only what reaches into the rectangle is drawn to tell what is seen

At each release of the rectangle the pass that tells what is seen drew
every mesh of the scene, and the selection asked whether an entity can
be selected before it asked where the entity stands. Both grew with the
scene: over 100,000 entities one release held the main thread for 625
ms in a debug build and 236 ms in a release one. Nearly all of that
came before the wait for the GPU, which took about 20 ms.

An entity outside the rectangle neither shows in it nor hides anything
in it, so it is left out of the pass now. MarqueeGeometry.Frustum is
made once for a rectangle and tells a box inside, across or outside it,
by the box the engine culls by. Where an entity stands is asked first,
and the rest only of what the rectangle reaches. The same release takes
150 ms and 36 ms, and the pass draws 90 of the 100,000 entities.

What is selected is the same: a test compares both ways on 120 cubes,
some hiding others, over five rectangles, and another tries 3,000 boxes
to see that none told outside shows in its rectangle.

* [Patch] Gizmo on several entities: only those with both transforms, and no made-up place in the world

A scale of several entities took an entity's place under its parent for
its place in the world when the entity had no world transform, which is
wrong under a parent that was moved or turned. A turn took the gizmo's
own place. The engine gives an entity its two transforms together and
takes them away together, so neither was reached, but nothing said so
where the gizmo's entities are chosen.

takesGizmo and gizmoTransformTargets now ask for both transforms, with
hasBothTransforms, and the two fallbacks are gone: the place in the
world is always the one the engine keeps.

---------

Co-authored-by: Javier Segura <javier@miolabs.com>
…t only CShaderTypes (untoldengine#148)

* [Feature] Component SDK: pass the module map of every C module the engine was built with

The engine is built without library evolution, so a plugin that imports UntoldEngine
makes the compiler load every module the engine imports. The editor passed the module
map of CShaderTypes and nothing else, so one more C module on the engine side (a C
target of its own, or one that comes with a Swift package such as swift-atomics) broke
every plugin build with "missing required module".

An editor run from source now passes the module map of every C target it was built
with, CShaderTypes first:

- The module map the build system generated for the target, beside the CShaderTypes
  one, in both SwiftPM layouts and in Xcode's.
- For a target that brings its own module.modulemap, the one in its package. The
  packages of a build are the ones SwiftPM's workspace-state.json lists: a cloned one in
  `checkouts` (SwiftPM) or `SourcePackages/checkouts` (Xcode), one used by path where
  it is.

A target counts when it left objects in the build, so a target the engine declares and
the editor does not use is not passed, and a target that is gone from its package and
left its objects behind is not passed either. A built C target without any module map
is reported in the Plugins tab and the console, because the compiler would only name
the missing module.

A packaged editor passes the C modules its sdk.json lists under "cModules"; an sdk.json
without the list has CShaderTypes alone.

With the engine as it is the arguments do not change.

* [Feature] Component SDK: package every Swift and C module the editor was built with

The app bundle shipped three Swift modules by name and the CShaderTypes headers, found
at a fixed path in the engine's checkout. A plugin compiled by a packaged editor needs
whatever the engine imports, so the bundle now ships what the editor was built with:

- Every Swift module of the build except the editor's own (the engine, the kit, a
  plugin the editor links, and a module of another package that one of them imports,
  such as Atomics).
- Every C module of the build, by the rule the editor follows when run from source:
  scripts/copy-component-sdk-c-modules.py copies the public headers of each into
  ComponentSDK/<Module>/ with a module map that names them by relative path. For a
  generated module map that is the same file the script wrote for CShaderTypes; a
  target's own module map is shipped as it is.
- sdk.json lists the C modules under "cModules", which is what the editor passes.

verify-component-sdk.sh compiles its fixture with the module maps sdk.json lists,
refuses a module map that names a path outside the SDK, and checks the exported symbols
of every Swift module of the SDK, not only the engine's and the kit's.

With the engine as it is the bundle is the same, but for the new key in sdk.json. Two
side effects: CShaderTypes is found when the engine is used by path, where it was
missing before, and the warning about UntoldGaussianTwins.swiftmodule on an editor that
does not link it is gone.

The tests run the script on build folders shaped like the real ones and check that it
ships the modules the editor finds when run from source, and that a packaged editor
passes each of them.

---------

Co-authored-by: Javier Segura <javier@miolabs.com>
* [Chores] Fixed warnigns in build and test

* [Chores] Formatted files
* [Patch] Hide cook-source folders in asset browser, reveal via context menu

Source folders sharing a stem with a runtime asset (e.g. bl/ for bl.untoldpack) cluttered the listing; they're now hidden and reachable through a "Reveal Source Folder" menu item.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [Chores] Formatted files

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…URLs (untoldengine#155)

The editor no longer serves demo/asset-pack content from AWS, so launch
straight into the full editor instead of the Explore gallery, hide the
Explore tab in the Asset Browser, and remove the CloudFront URLs from
the demo and asset-pack catalogs. Updated tests to match the now-empty
catalogs.
…'s C modules, release 0.22.0, the asset browser's cook-source folders, the intro gallery skipped (untoldengine#147, untoldengine#148, untoldengine#149, untoldengine#150, untoldengine#151, untoldengine#154, untoldengine#155)

# Conflicts:
#	Package.swift
@miogds
miogds merged commit 004dc44 into develop Oct 7, 2026
@miogds
miogds deleted the sync/upstream_develop_1007 branch October 7, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants