camera-framing-devices
camera-framing-devices
Camera-framing primitives · DEVICES + UI — the companion reel to camera-framing.pug. Same `<frame>` pose track, same parameters; the targets here are device rigs, their screens, browser windows and UI panels.
Read the rig's notes
Read camera-framing.pug first for the parameter catalogue (node · fill · fov ·
padding · offset · side · bounds · mesh · mesh_match · up · zoom · smooth).
This reel is about what those parameters DO once the target is a real rig.
THE ONE RULE THAT CHANGES EVERYTHING:
Framing a device frames its LIVE DISPLAY, not its casing and not its
bounding box.
Two mechanisms, both in packages/engine/lib/frame-shot-target.ts:
1. findMeshInNode — with no `mesh` given it prefers the largest surface the
runtime tagged as a live display, which is exactly the screen of an
<iphone> / <laptop> / <ipad> / <chrome>. Without that preference the raw
largest-area fallback picks the CASING: a laptop's unibody shell beats
its screen panel, and a tablet's aluminium body beats its glass. Largest
display wins, which is also why a <chrome> frames the whole browser
window rather than just its content pane (scene 04).
2. frameSurfaceBasis — for a live display it solves the shot's plate from
that surface's OWN UVs instead of assuming "local +Z is the front and
local X/Y are width and height". That assumption holds for a flat plane
and for almost no real device: a laptop's meshes sit under a net 90°
pitch with the lid leaning back on top of it, and a tablet's under the
opposite pitch with its display UVs rotated 180°. Both also carry their
screen height on the depth axis, so a naive solve gets the DISTANCE
wrong too, not just the angle. Scene 05 is that fix, on screen.
A UV-solved plate is also ORIENTED: its sign already points out of the
readable face. `side="auto"` therefore never second-guesses it — see scene
03, where a phone turned away from the camera still frames its glass.
WHAT THIS REEL DELIBERATELY DOES NOT DO: reach inside a rig. Devices are
authored ONLY as <iphone> / <ipad> / <laptop> / <chrome> — never a
hand-wired model — and framing never names a mesh inside one. It does not
need to: "frame this device" already means "frame its screen", and
bounds="union" already means "frame the whole product". The `mesh` /
`mesh_match` selectors are for YOUR OWN authored node ids inside a container
you built; the companion reel's scene 06 is their catalogue, and nothing
here wants them.
HOW TO FRAME A PART OF A UI. There are two halves to this, and only the
first one is a limitation:
· An item painted into a device's flat `<ui>` TEXTURE is not framable. It
is pixels in a canvas, not a node, so there are no world bounds for a
framed kf to solve against and `node="some-card"` never resolves.
· But a `<ui3d overlay>` nested in a device IS framable. It mounts a real
surface-anchored node flush on the glass, in the same pixel space as the
screen texture, and its items are true plane meshes with world bounds.
So the way to frame a REGION of a screen is to author that region as an
overlay panel and frame it by id. Scene 08 is the catalogue.
The overlay form:
iphone#phone(scale=1.2)
ui()
card(anchor=top-left w=512 h=1024 r=0 fill=#10162c)
ui3d#focus-row(overlay=screen lift=0.012 width=512 height=1024)
card(x=30 y=300 w=452 h=120 r=22 fill=#f2f4f9)
kf(time=3 node=focus-row bounds=union fill=70 side=front)
`overlay` is only a MARKER (any value works; `screen` is the convention). The
stage covers the WHOLE screen, so you pick the sub-region purely with the
child card's x / y / w / h in screen pixels.
STATE THE STAGE EXPLICITLY, and match the device: iphone 512x1024, laptop
1280x800, ipad 768x1024, chrome 1440x900. In principle the overlay's stage is
auto-pinned to the device's screen space (promoDeviceScreenOverlays, xml-scene
3621) — but the generic ui3d defaults already stamped stage 1920x1080 onto the
source by then (applyUi3dAuthoringDefaults, xml-scene 3130), and the pin only
fills a NULL, so it never fires. Leave the stage off and every coordinate
inside the overlay is silently interpreted against 1920x1080: a card authored
at w=452 on a phone renders about a quarter of the screen wide instead of
nearly full width. (This bites the existing reference scenes too — the
banners in promos/projects/overlay-scenes/ render at roughly 27% scale for
exactly this reason.) Still do NOT author width / height (clobbered with the
canvas size for a node source) or map_* (that is the texture UV transform).
The node's own position track is in stage px with +z out of the glass, so
"0 0 0" is flush and a card can fly off the screen and land back.
Framing one: use `bounds="union"` (measure the panel's whole subtree) and
`side="front"`. An overlay card is not a tagged display, so its plate is
unoriented and `auto` may flip toward the overview point. Keep the first
framed beat a couple of seconds in — overlays attach in a deferred job once
the model has loaded, and an empty box makes the kf fall back to a coordinate
pose. Expect the shot to come out looser than the numbers suggest: a <card>
pads its plane about 40px per side for shadow bleed, so its bounds run ~80px
wider and taller than the box you authored. On a full-width row that padding
makes the card WIDER than the screen, so the width term binds the solve and
`fill` stops behaving like "percent of frame height" — the rows below sit at
70 to read as the subject. Stack overlapping overlays with `lift` (0.012,
0.013, 0.014) and `render_order`.
The OTHER idiom is a free-standing <ui3d> panel mounted on nothing: its own
node, its own place in the world. Scene 06 frames three of them; scene 07
clusters small ones in front of a phone. Use overlays when the UI belongs to
the SCREEN, panels when it belongs to the SCENE.
AUTHORING TRAPS (same two as the companion reel, still true here):
· a `<g>` is an IDENTITY container — its position/rotation are folded into
an SVG child context, not applied as a 3D transform. Only use one as a
framing target (scenes 06, 07), never to place things. `<row>` is NOT a
`<g>`: a flow container is a normal node and its transform DOES apply
(scene 09).
· a `<rect>` with no `position` anchors SVG-style at its top-left.
· a label parked at the top of a scene is gone the moment the camera dives
into a screen — so beat labels here either ride the device's own UI or
sit on its glass, pre-rotated to the rig's yaw.
Scene map:
01 device → screen one <iphone> at 35° yaw, framed at 45 / 70 / 88
02 bounds on a rig the display panel alone vs bounds=union (whole laptop)
03 side on a screen a phone turned AWAY: auto still frames the glass
04 chrome + its UI the whole browser window, then parked with offset
05 tilted panels a laptop lid's lean and a tablet's rotated UVs
06 ui3d panels three free-standing UI surfaces, framed in turn
07 UI beside a device a panel cluster floating in front of a phone
08 UI ON a device <ui3d overlay> rows on the glass, framed one by one
09 device row framing flow-laid cells — zero authored coordinates
10 a MOVING device one framed hold; the pose re-solves every frame
11 INTO a website a yawed browser page dived into from the side, on DOF
Preview: /packages/runtime/index.html?project=promos/primitives/camera-framing/camera-framing-devices.promo- Topic
- camera
- Scenes
- 10
- Kind
- reference rig
//
Camera-framing primitives · DEVICES + UI — the companion reel to
camera-framing.pug. Same `<frame>` pose track, same parameters; the targets
here are device rigs, their screens, browser windows and UI panels.
Read camera-framing.pug first for the parameter catalogue (node · fill · fov ·
padding · offset · side · bounds · mesh · mesh_match · up · zoom · smooth).
This reel is about what those parameters DO once the target is a real rig.
THE ONE RULE THAT CHANGES EVERYTHING:
Framing a device frames its LIVE DISPLAY, not its casing and not its
bounding box.
Two mechanisms, both in packages/engine/lib/frame-shot-target.ts:
1. findMeshInNode — with no `mesh` given it prefers the largest surface the
runtime tagged as a live display, which is exactly the screen of an
<iphone> / <laptop> / <ipad> / <chrome>. Without that preference the raw
largest-area fallback picks the CASING: a laptop's unibody shell beats
its screen panel, and a tablet's aluminium body beats its glass. Largest
display wins, which is also why a <chrome> frames the whole browser
window rather than just its content pane (scene 04).
2. frameSurfaceBasis — for a live display it solves the shot's plate from
that surface's OWN UVs instead of assuming "local +Z is the front and
local X/Y are width and height". That assumption holds for a flat plane
and for almost no real device: a laptop's meshes sit under a net 90°
pitch with the lid leaning back on top of it, and a tablet's under the
opposite pitch with its display UVs rotated 180°. Both also carry their
screen height on the depth axis, so a naive solve gets the DISTANCE
wrong too, not just the angle. Scene 05 is that fix, on screen.
A UV-solved plate is also ORIENTED: its sign already points out of the
readable face. `side="auto"` therefore never second-guesses it — see scene
03, where a phone turned away from the camera still frames its glass.
WHAT THIS REEL DELIBERATELY DOES NOT DO: reach inside a rig. Devices are
authored ONLY as <iphone> / <ipad> / <laptop> / <chrome> — never a
hand-wired model — and framing never names a mesh inside one. It does not
need to: "frame this device" already means "frame its screen", and
bounds="union" already means "frame the whole product". The `mesh` /
`mesh_match` selectors are for YOUR OWN authored node ids inside a container
you built; the companion reel's scene 06 is their catalogue, and nothing
here wants them.
HOW TO FRAME A PART OF A UI. There are two halves to this, and only the
first one is a limitation:
· An item painted into a device's flat `<ui>` TEXTURE is not framable. It
is pixels in a canvas, not a node, so there are no world bounds for a
framed kf to solve against and `node="some-card"` never resolves.
· But a `<ui3d overlay>` nested in a device IS framable. It mounts a real
surface-anchored node flush on the glass, in the same pixel space as the
screen texture, and its items are true plane meshes with world bounds.
So the way to frame a REGION of a screen is to author that region as an
overlay panel and frame it by id. Scene 08 is the catalogue.
The overlay form:
iphone#phone(scale=1.2)
ui()
card(anchor=top-left w=512 h=1024 r=0 fill=#10162c)
ui3d#focus-row(overlay=screen lift=0.012 width=512 height=1024)
card(x=30 y=300 w=452 h=120 r=22 fill=#f2f4f9)
kf(time=3 node=focus-row bounds=union fill=70 side=front)
`overlay` is only a MARKER (any value works; `screen` is the convention). The
stage covers the WHOLE screen, so you pick the sub-region purely with the
child card's x / y / w / h in screen pixels.
STATE THE STAGE EXPLICITLY, and match the device: iphone 512x1024, laptop
1280x8