tosijs-3d 0.7.0-beta.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1213 -0
- package/LICENSE +190 -0
- package/Migration.md +102 -0
- package/README.md +56 -0
- package/dist/ambient-budget.d.ts +32 -0
- package/dist/ambient-budget.d.ts.map +1 -1
- package/dist/ambient-budget.js +39 -1
- package/dist/ambient-budget.js.map +1 -1
- package/dist/ambient-leaves.js +1 -1
- package/dist/ambient-leaves.js.map +1 -1
- package/dist/asset-url.js +1 -1
- package/dist/asset-url.js.map +1 -1
- package/dist/atmosphere.js +1 -1
- package/dist/atmosphere.js.map +1 -1
- package/dist/b3d-aircraft.d.ts +81 -0
- package/dist/b3d-aircraft.d.ts.map +1 -1
- package/dist/b3d-aircraft.js +215 -26
- package/dist/b3d-aircraft.js.map +1 -1
- package/dist/b3d-ambient.d.ts.map +1 -1
- package/dist/b3d-ambient.js +14 -6
- package/dist/b3d-ambient.js.map +1 -1
- package/dist/b3d-clouds.d.ts.map +1 -1
- package/dist/b3d-clouds.js +24 -5
- package/dist/b3d-clouds.js.map +1 -1
- package/dist/b3d-collisions.d.ts.map +1 -1
- package/dist/b3d-collisions.js +138 -0
- package/dist/b3d-collisions.js.map +1 -1
- package/dist/b3d-controllable.d.ts.map +1 -1
- package/dist/b3d-controllable.js +21 -4
- package/dist/b3d-controllable.js.map +1 -1
- package/dist/b3d-death.d.ts.map +1 -1
- package/dist/b3d-death.js +5 -0
- package/dist/b3d-death.js.map +1 -1
- package/dist/b3d-destroyable.js +1 -1
- package/dist/b3d-destroyable.js.map +1 -1
- package/dist/b3d-launcher.d.ts +46 -1
- package/dist/b3d-launcher.d.ts.map +1 -1
- package/dist/b3d-launcher.js +118 -10
- package/dist/b3d-launcher.js.map +1 -1
- package/dist/b3d-library.d.ts +39 -0
- package/dist/b3d-library.d.ts.map +1 -1
- package/dist/b3d-library.js +98 -18
- package/dist/b3d-library.js.map +1 -1
- package/dist/b3d-panel.d.ts.map +1 -1
- package/dist/b3d-panel.js +9 -1
- package/dist/b3d-panel.js.map +1 -1
- package/dist/b3d-primitives.d.ts +41 -0
- package/dist/b3d-primitives.d.ts.map +1 -1
- package/dist/b3d-primitives.js +53 -24
- package/dist/b3d-primitives.js.map +1 -1
- package/dist/b3d-probe.js +1 -1
- package/dist/b3d-probe.js.map +1 -1
- package/dist/b3d-quality.js +1 -1
- package/dist/b3d-quality.js.map +1 -1
- package/dist/b3d-radar-blip.d.ts.map +1 -1
- package/dist/b3d-radar-blip.js +5 -0
- package/dist/b3d-radar-blip.js.map +1 -1
- package/dist/b3d-radar.d.ts.map +1 -1
- package/dist/b3d-radar.js +76 -0
- package/dist/b3d-radar.js.map +1 -1
- package/dist/b3d-shadows.d.ts +18 -0
- package/dist/b3d-shadows.d.ts.map +1 -1
- package/dist/b3d-shadows.js +31 -2
- package/dist/b3d-shadows.js.map +1 -1
- package/dist/b3d-skybox.d.ts.map +1 -1
- package/dist/b3d-skybox.js +7 -1
- package/dist/b3d-skybox.js.map +1 -1
- package/dist/b3d-star-system.d.ts.map +1 -1
- package/dist/b3d-star-system.js +12 -0
- package/dist/b3d-star-system.js.map +1 -1
- package/dist/b3d-svg-plane.d.ts +40 -0
- package/dist/b3d-svg-plane.d.ts.map +1 -1
- package/dist/b3d-svg-plane.js +224 -19
- package/dist/b3d-svg-plane.js.map +1 -1
- package/dist/b3d-terrain.d.ts +1 -0
- package/dist/b3d-terrain.d.ts.map +1 -1
- package/dist/b3d-terrain.js +41 -1
- package/dist/b3d-terrain.js.map +1 -1
- package/dist/b3d-utils.d.ts +101 -0
- package/dist/b3d-utils.d.ts.map +1 -1
- package/dist/b3d-utils.js +141 -4
- package/dist/b3d-utils.js.map +1 -1
- package/dist/b3d-warhead.d.ts.map +1 -1
- package/dist/b3d-warhead.js +39 -14
- package/dist/b3d-warhead.js.map +1 -1
- package/dist/b3d-water.d.ts.map +1 -1
- package/dist/b3d-water.js +16 -0
- package/dist/b3d-water.js.map +1 -1
- package/dist/biome-chart.js +1 -1
- package/dist/biome-chart.js.map +1 -1
- package/dist/biome-plugin.js +1 -1
- package/dist/biome-plugin.js.map +1 -1
- package/dist/box.js +1 -1
- package/dist/box.js.map +1 -1
- package/dist/carve.js +1 -1
- package/dist/cloud-shadows.d.ts.map +1 -1
- package/dist/cloud-shadows.js +14 -7
- package/dist/cloud-shadows.js.map +1 -1
- package/dist/control-input.js +1 -1
- package/dist/control-input.js.map +1 -1
- package/dist/destroyable-behavior.js +1 -1
- package/dist/destroyable-behavior.js.map +1 -1
- package/dist/flow-layout.js +1 -1
- package/dist/flow-layout.js.map +1 -1
- package/dist/formations.js +1 -1
- package/dist/formations.js.map +1 -1
- package/dist/frame-panel.d.ts.map +1 -1
- package/dist/frame-panel.js +5 -0
- package/dist/frame-panel.js.map +1 -1
- package/dist/galaxy-data.js +1 -1
- package/dist/galaxy-data.js.map +1 -1
- package/dist/gamepad-focus.js +2 -2
- package/dist/gamepad-focus.js.map +1 -1
- package/dist/gamepad-svg.js +1 -1
- package/dist/gamepad-svg.js.map +1 -1
- package/dist/glass-gamepad.d.ts +17 -4
- package/dist/glass-gamepad.d.ts.map +1 -1
- package/dist/glass-gamepad.js +56 -16
- package/dist/glass-gamepad.js.map +1 -1
- package/dist/hardware-gamepad.js +1 -1
- package/dist/hardware-gamepad.js.map +1 -1
- package/dist/hud-math.js +1 -1
- package/dist/hud-math.js.map +1 -1
- package/dist/icon-data.d.ts.map +1 -1
- package/dist/icon-data.js +6 -0
- package/dist/icon-data.js.map +1 -1
- package/dist/icon-name.js +1 -1
- package/dist/icon-name.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/key-layout.js +1 -1
- package/dist/key-layout.js.map +1 -1
- package/dist/landform.d.ts +3 -1
- package/dist/landform.d.ts.map +1 -1
- package/dist/landform.js +4 -2
- package/dist/landform.js.map +1 -1
- package/dist/make-mesh.d.ts +109 -0
- package/dist/make-mesh.d.ts.map +1 -0
- package/dist/make-mesh.js +229 -0
- package/dist/make-mesh.js.map +1 -0
- package/dist/medium.js +1 -1
- package/dist/mersenne-twister.js +1 -1
- package/dist/mersenne-twister.js.map +1 -1
- package/dist/min-sim-conformance.js +1 -1
- package/dist/min-sim-conformance.js.map +1 -1
- package/dist/model-transform.d.ts.map +1 -1
- package/dist/model-transform.js +58 -0
- package/dist/model-transform.js.map +1 -1
- package/dist/patch-field.js +1 -1
- package/dist/patch-field.js.map +1 -1
- package/dist/perf-probe.js +1 -1
- package/dist/perf-probe.js.map +1 -1
- package/dist/popup-chrome.d.ts +45 -0
- package/dist/popup-chrome.d.ts.map +1 -0
- package/dist/popup-chrome.js +85 -0
- package/dist/popup-chrome.js.map +1 -0
- package/dist/popup-surface.d.ts +119 -0
- package/dist/popup-surface.d.ts.map +1 -0
- package/dist/popup-surface.js +634 -0
- package/dist/popup-surface.js.map +1 -0
- package/dist/portal-transform.js +1 -1
- package/dist/prefab.js +1 -1
- package/dist/prefab.js.map +1 -1
- package/dist/radar.js +1 -1
- package/dist/radar.js.map +1 -1
- package/dist/rounded-rect.d.ts +37 -0
- package/dist/rounded-rect.d.ts.map +1 -0
- package/dist/rounded-rect.js +159 -0
- package/dist/rounded-rect.js.map +1 -0
- package/dist/shadow-decal.d.ts.map +1 -1
- package/dist/shadow-decal.js +3 -5
- package/dist/shadow-decal.js.map +1 -1
- package/dist/slope-profile.js +1 -1
- package/dist/slope-profile.js.map +1 -1
- package/dist/spatial-transform.js +1 -1
- package/dist/spatial-transform.js.map +1 -1
- package/dist/surface.js +1 -1
- package/dist/surface.js.map +1 -1
- package/dist/table-layout.d.ts.map +1 -1
- package/dist/table-layout.js +11 -1
- package/dist/table-layout.js.map +1 -1
- package/dist/text-edit.js +1 -1
- package/dist/text-edit.js.map +1 -1
- package/dist/tosi-b3d.d.ts +132 -5
- package/dist/tosi-b3d.d.ts.map +1 -1
- package/dist/tosi-b3d.js +977 -113
- package/dist/tosi-b3d.js.map +1 -1
- package/dist/touch-gamepad.d.ts.map +1 -1
- package/dist/touch-gamepad.js +5 -1
- package/dist/touch-gamepad.js.map +1 -1
- package/dist/virtual-gamepad.d.ts +1 -1
- package/dist/virtual-gamepad.js +2 -2
- package/dist/virtual-gamepad.js.map +1 -1
- package/dist/w3d-theme.d.ts +3 -0
- package/dist/w3d-theme.d.ts.map +1 -1
- package/dist/w3d-theme.js +18 -1
- package/dist/w3d-theme.js.map +1 -1
- package/dist/widget-box.js +1 -1
- package/dist/widget-box.js.map +1 -1
- package/dist/widgets3d-layout.js +1 -1
- package/dist/widgets3d-layout.js.map +1 -1
- package/dist/widgets3d.d.ts.map +1 -1
- package/dist/widgets3d.js +35 -2
- package/dist/widgets3d.js.map +1 -1
- package/dist/world-contract.js +1 -1
- package/dist/world-contract.js.map +1 -1
- package/dist/world-store.js +1 -1
- package/dist/world-store.js.map +1 -1
- package/dist/world-topology.js +1 -1
- package/dist/world-topology.js.map +1 -1
- package/dist/xr-gamepad.js +1 -1
- package/dist/xr-gamepad.js.map +1 -1
- package/package.json +10 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1213 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<!--{ "order": 3 }-->
|
|
4
|
+
|
|
5
|
+
All notable changes to **tosijs-3d**. This project is pre-1.0 (`0.x`), so minor
|
|
6
|
+
versions may carry breaking peer-dependency changes — each is called out in a
|
|
7
|
+
**⚠️ Breaking** block in its version section below, with what a consumer must do.
|
|
8
|
+
|
|
9
|
+
## 0.7.0
|
|
10
|
+
|
|
11
|
+
> **Prereleases, and why the numbering looks backwards.** `0.7.0-rc.1` was cut
|
|
12
|
+
> first, then work continued as `-beta.1…beta.6`. Semver sorts beta BELOW rc, so
|
|
13
|
+
> `bun add tosijs-3d@next` wrote `^0.7.0-beta.6` and RESOLVED BACKWARDS to the
|
|
14
|
+
> older rc.1 — silently, and `bun update` even reported the downgrade as an
|
|
15
|
+
> upgrade. **Publishing 0.7.0 is what un-inverts the channel**, since it sorts
|
|
16
|
+
> above every prerelease.
|
|
17
|
+
>
|
|
18
|
+
> The rule taken from it: **a prerelease channel never moves backwards** — the
|
|
19
|
+
> successor to `-rc.N` is `-rc.N+1`, never a beta. If you pinned any
|
|
20
|
+
> `^0.7.0-beta.*` or `0.7.0-rc.1`, move to `0.7.0`.
|
|
21
|
+
>
|
|
22
|
+
> - **0.7.0-beta.6** (2026-08-17) — carved landforms (the sdf-lattice page, the
|
|
23
|
+
> volcano cutaway, strata, 3D volcanic veins), portal math, and four
|
|
24
|
+
> manta-reported fixes: the explode+library crash that seized the scene (#24),
|
|
25
|
+
> mutual death eating the panel (#25), blasts blind to library destroyables
|
|
26
|
+
> (#28), and pitch authority becoming configurable and asymmetric (#26).
|
|
27
|
+
> - **0.7.0-beta.5** (2026-08-15) — `MediumOptics` + `fogLayerFor`
|
|
28
|
+
> (⚠️ experimental, not load-bearing: water publishes its optics but still
|
|
29
|
+
> computes its own fog layer). Otherwise identical to beta.4 — the rest is
|
|
30
|
+
> design notes.
|
|
31
|
+
> - **0.7.0-beta.4** (2026-08-15) — the `medium` primitive (space/air/water/
|
|
32
|
+
> mercury, plane or sphere), medium-aware projectiles closing #13, and pause
|
|
33
|
+
> marked EXPERIMENTAL pending a headset.
|
|
34
|
+
> - **0.7.0-beta.3** (2026-08-15) — pause (backgrounding, start-paused, the VR
|
|
35
|
+
> entry gesture, a centred panel), the stick-sign contract test + `kind`, and
|
|
36
|
+
> four more manta reports: #17/#18 ambient spawn bias, #19 authored projectile
|
|
37
|
+
> meshes, #22 destroyable library models. Plus the HUD circle's portrait
|
|
38
|
+
> sizing and both panels' camera/viewport fixes.
|
|
39
|
+
> - **0.7.0-beta.2** (2026-08-15) — adds #13's missile-guide composition, the
|
|
40
|
+
> gear gadget for the glass gamepad, and the live `fade`. #14 investigated and
|
|
41
|
+
> NOT changed: the glass source measures correct on desktop from four
|
|
42
|
+
> independent angles, so no sign was flipped (see the issue).
|
|
43
|
+
> - **0.7.0-beta.1** (2026-08-14) — everything below, including the four
|
|
44
|
+
> manta-recon issues fixed that day: #9 (crashed aircraft eats input) and the
|
|
45
|
+
> `b3d-death` latching bug behind it, #10 (`aircraftMapping` ignored its
|
|
46
|
+
> config), #11 (probe and ambient watchdog measuring during load), #12 (the sky
|
|
47
|
+
> ignored the water you were under).
|
|
48
|
+
|
|
49
|
+
**Aircraft that fly like aircraft, and the substrate for volumetric terrain.**
|
|
50
|
+
No peer-dependency changes, but this is a **behaviour** release: the throttle,
|
|
51
|
+
the right stick and the on-screen gamepad all work differently, and one
|
|
52
|
+
attribute was renamed. Read the ⚠️ Breaking block before upgrading a scene that
|
|
53
|
+
was tuned around the old feel.
|
|
54
|
+
|
|
55
|
+
### ⚠️ Breaking
|
|
56
|
+
|
|
57
|
+
- **`canonicalize` now drops the CONTENT node's scene transform, so a model
|
|
58
|
+
placed away from the origin in its source file lands differently.** The
|
|
59
|
+
contract always said it "drops the node's SCENE transform" and it only ever
|
|
60
|
+
zeroed the node it was handed — on the glTF path that is `__root__`, the
|
|
61
|
+
loader's own wrapper — while the authored object underneath kept its scene
|
|
62
|
+
placement.
|
|
63
|
+
|
|
64
|
+
**Who is affected:** anyone whose model sits somewhere other than the origin
|
|
65
|
+
inside its `.glb` AND who compensated for the resulting offset. The
|
|
66
|
+
compensation is now doubled.
|
|
67
|
+
|
|
68
|
+
**How it shows up:** the control node used to sit a fixed distance from the
|
|
69
|
+
airframe it steers (1.7 m for our own scout), so collision rays started beside
|
|
70
|
+
the model and a `_centerOfGravity` marker measured against the control node
|
|
71
|
+
folded that offset into the PIVOT. The perverse consequence: a marker authored
|
|
72
|
+
dead on the centreline — the documented way — installed a pivot 1.75 m out,
|
|
73
|
+
while an odd-looking hand-tuned marker silently netted out correct. Our scout
|
|
74
|
+
was re-exported "properly" and started crashing on every bank.
|
|
75
|
+
|
|
76
|
+
**Migration:** author content in the model's LOCAL frame, as
|
|
77
|
+
`CLAUDE.md` has always said, and delete compensating offsets. If a vehicle
|
|
78
|
+
now flies or pivots oddly, look for a marker or a placement that was tuned
|
|
79
|
+
against the old behaviour before assuming a regression. Models authored at the
|
|
80
|
+
origin (the common case) are unaffected.
|
|
81
|
+
|
|
82
|
+
- **Angles in the AUTHORING surface are DEGREES.** "We should use degrees
|
|
83
|
+
everywhere. Not even mathematicians visualize radians." Two APIs took radians
|
|
84
|
+
and now take degrees: `landform`'s `gulley`/`cover` `heading`, and the library
|
|
85
|
+
rotation below. **Migration:** multiply by `180 / Math.PI`.
|
|
86
|
+
Pure math modules (`spatial-transform`, `guidance`, `portal-transform`,
|
|
87
|
+
`fly-by-wire`'s internals) keep radians deliberately — that is mathematics,
|
|
88
|
+
not a dial an author turns. And `b3d-skybox`'s `azimuth` is neither: it is
|
|
89
|
+
Babylon's 0–1 fraction of a turn, passed straight through, now documented as
|
|
90
|
+
such rather than left to be guessed at.
|
|
91
|
+
- **⚠️ `library.instantiate()` / `library.make.*` rotation: WAS IGNORED, now
|
|
92
|
+
works — and is DEGREES.** The single most disruptive change in this release,
|
|
93
|
+
because it is the only one that can move a scene which previously looked
|
|
94
|
+
**correct**. Everything else here either errors or looks obviously wrong.
|
|
95
|
+
|
|
96
|
+
Two things happened to one option:
|
|
97
|
+
|
|
98
|
+
1. **It never applied on the default path.** `instantiate()` wrote
|
|
99
|
+
`result.rotation`, but Babylon's glTF loader always assigns a
|
|
100
|
+
`rotationQuaternion`, and a `TransformNode` ignores `.rotation` while one is
|
|
101
|
+
present. Measured on a real model: `ry: 0`, `ry: 140` and `ry: -90` gave
|
|
102
|
+
**bit-identical** orientations — you got the GLB's own baked rotation
|
|
103
|
+
whatever you asked for. `position` worked, which is precisely what made it
|
|
104
|
+
look wired up. (`canonical: true` was unaffected throughout.)
|
|
105
|
+
2. **It is now degrees**, matching `AbstractMesh`'s `rx`/`ry`/`rz` and
|
|
106
|
+
`el.make.*`. The library was the only surface in the framework that
|
|
107
|
+
disagreed, and a bare number is valid in either unit — so it failed by
|
|
108
|
+
producing a different orientation rather than an error. It got past us in
|
|
109
|
+
this repo's own collision demo, where `ry: 140` meant 140 radians.
|
|
110
|
+
|
|
111
|
+
**Migration.** If you passed rotation and it _appeared_ to work, it was not
|
|
112
|
+
your value doing it — something else was (baked rotation in the model, a parent
|
|
113
|
+
transform, a compensating tweak downstream), and that compensation is now
|
|
114
|
+
**doubled up**. Check every placement before calling it a regression. If you
|
|
115
|
+
passed rotation and gave up because nothing happened, it works now: your values
|
|
116
|
+
are radians, so multiply by `180 / Math.PI`. If you never passed rotation,
|
|
117
|
+
nothing changes. Quickest triage: grep for `instantiate(` and `make.` with an
|
|
118
|
+
`r[xyz]` key — no hits means this does not apply to you.
|
|
119
|
+
|
|
120
|
+
See `Migration.md`, which ships inside the package.
|
|
121
|
+
|
|
122
|
+
- **`b3dPatch` / `B3dPatch` are removed.** The volumetric-patch element stitched
|
|
123
|
+
an SDF extraction into the heightfield and never worked: two surfaces meeting
|
|
124
|
+
at a grazing angle have no well-defined boundary, so every fix aimed at one
|
|
125
|
+
crossing created another elsewhere. It shipped EXPERIMENTAL and undemoed, and
|
|
126
|
+
the design that replaces it (TUNNEL-DESIGN.md) removes the boundary rather
|
|
127
|
+
than reconciling it. The pure modules it was built on — `carve.*`,
|
|
128
|
+
`sdf-lattice`, `patch-field` — are **kept**: they are the substrate the
|
|
129
|
+
replacement uses.
|
|
130
|
+
|
|
131
|
+
- **`hudChase` → `hudChaseOff`, with the polarity inverted.** The old attribute
|
|
132
|
+
is gone (no alias — HTML boolean semantics can't express a default-true flag,
|
|
133
|
+
which is why the negative name exists). It shipped in 0.6.2's `dist`, and
|
|
134
|
+
because tosijs props end in an index signature a stale `hudChase` **compiles
|
|
135
|
+
and is silently ignored**, so this will not error for you.
|
|
136
|
+
- `hudChase: true` → delete it; the chase HUD is now the default.
|
|
137
|
+
- `hudChase: false` → `hudChaseOff: true`.
|
|
138
|
+
- Chase view now shows the HUD **without the artificial horizon** (which
|
|
139
|
+
only tells the truth from inside the cockpit).
|
|
140
|
+
- **The glass gamepad hides itself** once a **keyboard or hardware gamepad** is
|
|
141
|
+
used, returning after `idleSeconds` (10) of silence. The POINTER never fades
|
|
142
|
+
it — not touch, not mouse, not trackpad: the pad is operable by pointer, so
|
|
143
|
+
fading on pointer movement made it vanish exactly as you reached for it.
|
|
144
|
+
Set `fade="off"` to pin it — worth knowing if you screenshot or demo.
|
|
145
|
+
- **The right stick is the camera**, not aux roll. `strafe` still sums into
|
|
146
|
+
roll, so a custom mapping can restore a dedicated roll axis.
|
|
147
|
+
- **`carve` is a NAMESPACE, not 13 bare exports.** New in this cycle, so this
|
|
148
|
+
affects only the handful of people who took `0.7.0-rc.1` off the `next` tag:
|
|
149
|
+
that tarball exported `applyCarve`, `sphere`, `capsule`, `tube`, `box`,
|
|
150
|
+
`union`, `smoothUnion`, `flange`, `subtract`, `intersect`, `roughen`, `warp`
|
|
151
|
+
and `shaft` at top level; they now live on `carve.*`. Coming from the rc you
|
|
152
|
+
get a link error, which is the intended loud failure — `box` in particular
|
|
153
|
+
shadowed the UI container of the same name.
|
|
154
|
+
- `import { sphere, tube } from 'tosijs-3d'` → `import { carve } from
|
|
155
|
+
'tosijs-3d'`, then `carve.sphere(…)`, `carve.tube(…)`.
|
|
156
|
+
- Types stay top-level: `Carve`, `NoiseOptions`, `Vec3Like`.
|
|
157
|
+
- **`getNames()` returns different strings.** Behaviour suffixes no longer
|
|
158
|
+
leak into the public name, so `Hull_collideMesh.model` lists as `Hull` and
|
|
159
|
+
`instantiate('Hull')` resolves it (fixes #7, reported by manta-recon).
|
|
160
|
+
Exact and `.model` matches still win first, so existing calls that used the
|
|
161
|
+
raw name keep working — but code that PARSED the returned string, or
|
|
162
|
+
compared it to an authored name with its suffix, needs updating.
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
|
|
166
|
+
- **Re-seat is now a two-step, confirmed gesture — and it finally works.**
|
|
167
|
+
Re-seating takes your CURRENT head yaw as forward, but you had to LOOK AT the
|
|
168
|
+
button to press it, so a panel that had drifted left re-seated you facing
|
|
169
|
+
left: the gesture defeated itself exactly when you needed it. Pressing
|
|
170
|
+
**Re-seat** now raises a prompt pinned to the **face frame** (head-locked, so
|
|
171
|
+
it stays readable WHILE you turn your head — the one case that earns
|
|
172
|
+
face-locking): _1. Look comfortably ahead. 2. Pull right trigger to reseat.
|
|
173
|
+
Or, pull left trigger to cancel._ The yaw is captured on the trigger, from
|
|
174
|
+
wherever you are looking THEN.
|
|
175
|
+
- **`reseatFreeze`** (new `<tosi-b3d>` attribute, `'on' | 'off'`, default
|
|
176
|
+
`'on'`) freezes the clock while the prompt is up, so you are not shot mid
|
|
177
|
+
re-seat. **Set `'off'` for multiplayer** — freezing is a decision a local
|
|
178
|
+
world can make and a networked one cannot honour. The dialog always gates
|
|
179
|
+
YOUR input (`b3d.suppressInput`), which is the half that works in any
|
|
180
|
+
topology, so the trigger that confirms never also fires your gun.
|
|
181
|
+
- `b3d.freeze(on)` and `b3d.suppressInput(on)` are public: a modal that
|
|
182
|
+
borrows a control you also play with can use them. `freeze()` stops the
|
|
183
|
+
clock without raising the pause panel and without pause's resume semantics
|
|
184
|
+
— a scene paused underneath stays paused.
|
|
185
|
+
- **The package is licensed** — **Apache-2.0**, with a LICENSE file, plus
|
|
186
|
+
`description`, `homepage` and `bugs`. Every release since 0.2.1 shipped with
|
|
187
|
+
no licence at all, which legally means all-rights-reserved: SCA tooling flags
|
|
188
|
+
an unknown-licence dependency, and adopters consuming this as a `file:` dep
|
|
189
|
+
had nothing to point at. Apache-2.0 rather than MIT/BSD for the explicit
|
|
190
|
+
patent grant and retaliation clause.
|
|
191
|
+
- **A `startPaused` scene renders its own fog, not Babylon's default** (#31). A
|
|
192
|
+
paused frame still RENDERS — that is the point, the panel has to be visible —
|
|
193
|
+
so everything the render reads has to be correct. `_updateFog` sat past the
|
|
194
|
+
paused branch's early return and `startPaused` pauses in the same synchronous
|
|
195
|
+
setup block, so a scene that booted paused had never run it once.
|
|
196
|
+
- **Entering VR unpauses a paused scene.** Putting the headset on is the resume
|
|
197
|
+
— and a world you deliberately stepped into has no reason to be frozen. It
|
|
198
|
+
also removed a broken state: entering while paused showed no pause panel
|
|
199
|
+
in-session, so you arrived in a stopped world with no visible way out. You can
|
|
200
|
+
still pause in VR from the scene panel's transport toggle.
|
|
201
|
+
- **Camera-relative dialogs are no longer buried by the world.** The respawn and
|
|
202
|
+
pause panels could end up inside terrain — you die on a hillside and the panel
|
|
203
|
+
offering you a way out is in the hill. They now render in group 1 (drawn after
|
|
204
|
+
the scene, depth auto-cleared between groups), so a modal is always readable.
|
|
205
|
+
World-anchored panels and popups keep normal depth sorting.
|
|
206
|
+
- **VR altitude on the BUMPERS** — left down, right up, analog (squeeze
|
|
207
|
+
pressure, so you can ease onto a height). Same hand logic as brake/accelerate
|
|
208
|
+
on the triggers. The right stick's vertical still flies, but it doubles as
|
|
209
|
+
panel scroll, so in any scene with a scrollable panel open — most demos — the
|
|
210
|
+
stick scrolled and the rig did not move, and altitude appeared to be missing
|
|
211
|
+
entirely. Two dedicated buttons cannot be stolen by a panel.
|
|
212
|
+
- **Pause/resume in the standard scene panel.** Pause was a one-way door: the
|
|
213
|
+
pause panel offers _Continue_, so it was a state you could LEAVE and never
|
|
214
|
+
ENTER — nothing in the panel stopped a running scene, and `startPaused` is an
|
|
215
|
+
initial condition, not a control. The icon bar now carries a transport toggle
|
|
216
|
+
(`play` when paused, `pause` when running — it shows what pressing it DOES,
|
|
217
|
+
not what the scene currently is). It is a panel GADGET, so it appears in the
|
|
218
|
+
flat overlay **and** the in-VR panel from one definition, and in a headset it
|
|
219
|
+
is the only way to stop the world at all.
|
|
220
|
+
- **Status surfaces in the theme** — `--w3d-info`, `--w3d-warning`,
|
|
221
|
+
`--w3d-error` (`w3dTheme.info` / `.warning` / `.error`). Backgrounds, not text
|
|
222
|
+
colours: a status panel in a dark theme is a tinted SURFACE that `text` still
|
|
223
|
+
reads on, and the bright hue you would put on a label is unreadable behind
|
|
224
|
+
one. Opaque on purpose, so the panel underneath cannot show through the
|
|
225
|
+
message interrupting you.
|
|
226
|
+
- **`markUiMesh()` / `isNoCollide()`** (`b3d-utils`) — mark a mesh as UI:
|
|
227
|
+
pointer-pickable, but invisible to COLLISION. See the phantom-collision fix
|
|
228
|
+
below for why those are different questions. The first of the collision
|
|
229
|
+
GROUPS described in the new `COLLISION-DESIGN.md`.
|
|
230
|
+
- **`xrFrame`** on `<tosi-b3d-svg-plane>` — when `cameraRelative` and in a
|
|
231
|
+
headset, ride an XR reference frame instead of the head camera. `'body'`
|
|
232
|
+
(damped yaw) keeps a panel in front of you without jittering on every head
|
|
233
|
+
movement; leave it unset for a HUD that should stay head-locked.
|
|
234
|
+
- **A diagnostic ring buffer** — `b3d.logDebug(tag, event)`,
|
|
235
|
+
`b3d.debugCapture(tag)`, `b3d.debugLog`. There is no console in a headset and
|
|
236
|
+
`window.rAF` is suspended in-session, so this records structured events IN the
|
|
237
|
+
page for readback afterwards. Plus always-on Perf-panel rows: **aircraft
|
|
238
|
+
ground** (what the ground ray is calling ground, and a full crash report) and
|
|
239
|
+
**xr input** (per-hand thumbstick axes, rig pose, movement scale).
|
|
240
|
+
|
|
241
|
+
- **Popups are their own SURFACES — `el.openPopup()`** (`popup-surface`). A menu,
|
|
242
|
+
a dropdown's list or a debug readout spawns a second plane above its opener
|
|
243
|
+
rather than being laid out inside its SVG. Owned popups travel and die with
|
|
244
|
+
their opener; `tearOff()` promotes one to world space preserving pose, after
|
|
245
|
+
which it stays where you put it — and **dragging an owned popup tears it off**,
|
|
246
|
+
like pulling a tab out of a window. Click-to-front, `modal` (blocks pointer
|
|
247
|
+
access to what's behind it, never the camera), a title-bar grip so the rest of
|
|
248
|
+
the panel can stay interactive, and injected move/close glyphs.
|
|
249
|
+
|
|
250
|
+
This retired a constraint we had recorded as absolute: a dropdown "MUST grow
|
|
251
|
+
the panel's layout … because a popover won't rasterize into the VR texture".
|
|
252
|
+
True of a popover inside ONE texture; the escape is another texture on another
|
|
253
|
+
plane. See `UI-DESIGN-NOTES.md`.
|
|
254
|
+
|
|
255
|
+
- **`rounded-rect` + `cornerRadius` / `transparent` on `b3d-svg-plane`** — rounded
|
|
256
|
+
corners as GEOMETRY so a UI panel can be **opaque**. A transparent mesh is not
|
|
257
|
+
depth-written, so Babylon re-sorts it per frame by camera distance and
|
|
258
|
+
near-coplanar panels flip order as you orbit: it looks exactly like z-fighting
|
|
259
|
+
and no amount of correct depth ordering fixes it. Three quads plus four
|
|
260
|
+
quarter-disc fans, 22 triangles at 4 segments. **The general rule: in 3D,
|
|
261
|
+
express shape as geometry and reserve alpha for things that are genuinely
|
|
262
|
+
see-through.**
|
|
263
|
+
|
|
264
|
+
- **`el.make.*`** (`make-mesh`) — Babylon primitives with the forgettable parts
|
|
265
|
+
done: material from `color`/`glow`, `register()` so the sun and reflection
|
|
266
|
+
probes see it, and `computeWorldMatrix` (a mesh positioned but never rendered
|
|
267
|
+
has no world matrix, so a ray cast this frame finds it AT THE ORIGIN and
|
|
268
|
+
answers confidently and wrongly). A Proxy forwarding to `MeshBuilder`, so all
|
|
269
|
+
26 shapes are reachable plus whatever Babylon adds later.
|
|
270
|
+
|
|
271
|
+
- **`library.make.<name>()`** — a library's contents as callable names,
|
|
272
|
+
`lib.make.scout({ y: 1 })`. Same option vocabulary as `el.make.*`.
|
|
273
|
+
|
|
274
|
+
- **`glow` / `glowColor` on `b3dBox` / `b3dSphere`** — self-illumination as a
|
|
275
|
+
fraction of `color`. Both primitives now share one `primitiveMaterial`, which
|
|
276
|
+
is how the sphere came to be missing `glow` ninety seconds after the box got
|
|
277
|
+
it.
|
|
278
|
+
|
|
279
|
+
- **`B3d.BABYLON` (static) and `BABYLON` re-exported from the barrel.** Babylon
|
|
280
|
+
is a PEER dependency, so a consumer importing `@babylonjs/core` separately can
|
|
281
|
+
end up with a second copy — two `Vector3` classes that fail `instanceof`
|
|
282
|
+
against each other. Taking it from the library that already holds one is the
|
|
283
|
+
guarantee.
|
|
284
|
+
|
|
285
|
+
- **`shadowNormalBias` / `shadowBias` on `b3dSun`**, defaulting to `0.05`.
|
|
286
|
+
Babylon's CSM ships `normalBias: 0`, which is not a tuned value for a large
|
|
287
|
+
receiver: a 40×40 ground self-shadowed into a dense stipple across its whole
|
|
288
|
+
surface. Measured at 0 / 0.02 / 0.05 on a live scene.
|
|
289
|
+
|
|
290
|
+
- **`b3d-collisions` gains its first live demo** — hail on the scout, impacts
|
|
291
|
+
found by a SWEPT ray, each marked with a disc lying on the surface plus a
|
|
292
|
+
stalk along the normal. Ground hits count too, in a cooler colour.
|
|
293
|
+
|
|
294
|
+
- **`b3d-aircraft` gains `chasePitchFollow` / `chasePitchLag`** — how much of the
|
|
295
|
+
nose's pitch the chase camera inherits. `0` (default) keeps the level pivot;
|
|
296
|
+
`1` is as if the camera were bolted to the airframe. A dial rather than a
|
|
297
|
+
reparent, because the flat pivot is what fixed the jittery chase, and the lag
|
|
298
|
+
term is what passes the intention while dropping the per-frame wobble.
|
|
299
|
+
|
|
300
|
+
- **`b3d-aircraft` gains `maxPitch` / `maxDive`** (#26). Pitch authority was a
|
|
301
|
+
module constant at a symmetric 35° — a gentle airliner descent, unreachable by
|
|
302
|
+
any adopter, and unable to express a craft that should fall out of the sky
|
|
303
|
+
readily and climb reluctantly. `maxDive` defaults to `maxPitch`, so nothing
|
|
304
|
+
changes unless asked.
|
|
305
|
+
|
|
306
|
+
- **⚠️ EXPERIMENTAL — `portalTransform`**: see-through portal math (virtual
|
|
307
|
+
camera pose, the oblique clip plane, a step-test crossing, and recursion
|
|
308
|
+
budgeted by a falloff CURVE rather than a hard depth limit — linear, geometric
|
|
309
|
+
or accelerating, so first-bounce quality and pass count stop being welded
|
|
310
|
+
together).
|
|
311
|
+
- **Sedimentary strata** in the volcanic rock (`strata`, `strataScale`,
|
|
312
|
+
`strataTilt`), visible on any cut face — only possible because the plate noise
|
|
313
|
+
is now sampled in 3D, since bedding means nothing to a shader that knows only
|
|
314
|
+
x/z.
|
|
315
|
+
- **Carved landforms** — the `sdf-lattice` page is now a real doc page with the
|
|
316
|
+
theory (SDF composition, marching cubes vs dual contouring vs surface nets, and
|
|
317
|
+
why this uses surface nets), a **volcano in cross-section** with lava tubes and
|
|
318
|
+
a sweeping cutaway, and a **volumetric-vs-heightfield comparison** with a bore
|
|
319
|
+
punched through an authored ridge. `demo-utils.volumetricDemo` is the shared
|
|
320
|
+
fixture behind both.
|
|
321
|
+
|
|
322
|
+
- **⚠️ EXPERIMENTAL — `portalTransform`**, the pure math for see-through
|
|
323
|
+
portals: `portalCamera` (where the render-target camera goes so a doorway
|
|
324
|
+
shows another place), `clipPlaneFor` (the oblique near plane, without which
|
|
325
|
+
the far room leaks into the doorway), `crossedPortal` (a _step_ test — a jeep
|
|
326
|
+
is past a doorway within one frame, and a context swap one frame late is one
|
|
327
|
+
frame of the wrong world), and `depthLimit`/`attenuationAt` for recursion.
|
|
328
|
+
|
|
329
|
+
Recursion terminates the way real mirrors do — each traversal is slightly
|
|
330
|
+
lossy, so the tunnel darkens instead of hitting a hard cutoff that pops.
|
|
331
|
+
Calibration worth knowing: **real glass is ~95% transmissive**, which is why
|
|
332
|
+
mirror corridors look infinite — at 0.95 it takes 77 bounces to fall below 2%.
|
|
333
|
+
So the physics gives the right look and a useless budget; the device cap is
|
|
334
|
+
load-bearing, and a portal that should visibly fade out has to be markedly
|
|
335
|
+
dirtier than glass (~0.5–0.7). That trade is documented rather than hidden
|
|
336
|
+
behind a physical-sounding parameter.
|
|
337
|
+
|
|
338
|
+
The falloff is a **curve, not a ratio** — `geometricFalloff`, `linearFalloff`
|
|
339
|
+
(state the level count instead of solving for it: `0.1` means exactly ten) and
|
|
340
|
+
`acceleratingFalloff` (the loss grows with depth — dirtier where nobody
|
|
341
|
+
looks). That decoupling is the point: geometric-at-0.95 and
|
|
342
|
+
accelerating-from-0.05 keep an identical 95% at the first bounce, the one
|
|
343
|
+
anyone actually inspects, but cost **77 levels versus 5**.
|
|
344
|
+
|
|
345
|
+
No renderer yet: applying the clip plane, the passes and stencil framing are
|
|
346
|
+
deliberately out of scope for a pure module.
|
|
347
|
+
|
|
348
|
+
- **⚠️ EXPERIMENTAL — `MediumOptics` + `fogLayerFor`** (MEDIUM-DESIGN.md §3).
|
|
349
|
+
A medium can describe how it LOOKS (colour, density, murk-with-depth,
|
|
350
|
+
visibility floor), and one derivation turns that into a fog layer for the
|
|
351
|
+
compositor that already exists. Weight is `submergence`, so the fog and
|
|
352
|
+
anything else keyed off the same medium cannot disagree about where the
|
|
353
|
+
surface is — that disagreement is what produced the fogged-sky vs
|
|
354
|
+
transparent-window conflict.
|
|
355
|
+
|
|
356
|
+
**Not load-bearing yet, on purpose.** `b3d-water` publishes its optics but
|
|
357
|
+
still computes its own fog layer, because that path is verified and swapping
|
|
358
|
+
it is a visual change that wants an eye on it. What's proven so far is the
|
|
359
|
+
falsifier from the design doc: one derivation reproduces water's numbers
|
|
360
|
+
(including thickening with depth), a cloud bank's soft edge, and vacuum
|
|
361
|
+
contributing nothing. Three hand-rolled contributions collapse onto it _in
|
|
362
|
+
the tests_; collapsing them in the code is the next step.
|
|
363
|
+
|
|
364
|
+
- **Projectiles understand medium** (#13 — ⚠️ EXPERIMENTAL, unflown). A round
|
|
365
|
+
now picks up drag from whatever it is passing through (`dragAt`, blended
|
|
366
|
+
across the band so entry is a ramp), and gains three options that turn the
|
|
367
|
+
same round into four weapons:
|
|
368
|
+
|
|
369
|
+
- `whenCrossing(kind, medium, at)` — the entry splash, the breakout plume,
|
|
370
|
+
the audio. Yours to dress; the engine only tells you it happened.
|
|
371
|
+
- `detonateDepth` — a **depth charge**: a fuse on depth rather than impact.
|
|
372
|
+
- `stayIn: 'water'` — a **torpedo**, for which the surface is a ceiling. Held
|
|
373
|
+
just inside rather than reflected: a round that bounces off water reads as a
|
|
374
|
+
skipping stone, which is a different weapon.
|
|
375
|
+
|
|
376
|
+
A **sub-launched missile** needs no option at all — it is a `whenCrossing`
|
|
377
|
+
handler that swaps its own behaviour on `'exited'`. That the four fall out of
|
|
378
|
+
three small options is the argument for the primitive being shared rather than
|
|
379
|
+
per-weapon.
|
|
380
|
+
|
|
381
|
+
- **[medium](https://3d.tosijs.net/medium/) — the substance you're moving
|
|
382
|
+
through**, pure and Babylon-free (#13, and the seam #3/#15/#16 were all
|
|
383
|
+
converging on). It answers three questions and owns nothing else: how deep am
|
|
384
|
+
I (`depthIn`, signed — depth and altitude are one measurement), how much of me
|
|
385
|
+
is in it (`submergence`, smoothed across a band), and did I just cross the
|
|
386
|
+
surface (`crossing`, a _step_ test, because at 200 m/s a round is above the
|
|
387
|
+
water one frame and 3 m under it the next).
|
|
388
|
+
|
|
389
|
+
- **Two geometries**: `plane` (a sea, a fog bank, the top of a mercury vat)
|
|
390
|
+
and `sphere` (a planet's ocean or atmosphere, the edge of space). A plane is
|
|
391
|
+
NOT a very large sphere — at a 6371 km radius the arithmetic loses the
|
|
392
|
+
centimetres that decide whether you're above or below a wave.
|
|
393
|
+
- `innermost` resolves space → air → water the way you'd say it aloud, and
|
|
394
|
+
vacuum is simply the absence of a match. `dragAt` blends a projectile's drag
|
|
395
|
+
coefficient across the band, which is why depth charges and torpedoes need
|
|
396
|
+
no new integrator.
|
|
397
|
+
- **`b3d.media` / `b3d.addMedium()`**, and `b3d-water` registers itself — so
|
|
398
|
+
nothing has to re-derive where the surface is. Two subsystems deriving that
|
|
399
|
+
separately is exactly how the fogged sky and the transparent window ended up
|
|
400
|
+
disagreeing (#12/#15).
|
|
401
|
+
|
|
402
|
+
- **`b3d-ambient` spawns where you're LOOKING and where you're GOING** (#17,
|
|
403
|
+
#18): `lookAhead` (0.35 of `radius`, along the view) and `lead` (0.25s of
|
|
404
|
+
travel, along motion), capped by `speedCap`. A camera sees a frustum, not a
|
|
405
|
+
sphere, so a box centred on the eye births most of its particles behind and
|
|
406
|
+
beside you — they live and are culled unseen, paying full budget for a
|
|
407
|
+
fraction of the result — and at speed you outrun the box entirely, emptying
|
|
408
|
+
the view ahead exactly when you want it fullest. Velocity is measured from the
|
|
409
|
+
camera's own displacement, so nothing has to be plumbed in; in a chase view
|
|
410
|
+
that's the right signal anyway. Set either to 0 for the old centred box.
|
|
411
|
+
|
|
412
|
+
- **`b3d-destroyable` takes `library` + `meshName`** (#22) — a static,
|
|
413
|
+
destroyable thing that uses a library mesh is most of what populates a level,
|
|
414
|
+
and there was no route to it: `b3d-loader` takes a `url` (which also loses the
|
|
415
|
+
canonical frame), `b3d-aircraft` gets the frame right but flies. Absent
|
|
416
|
+
`library`, you still get the placeholder cube, so nothing existing changes.
|
|
417
|
+
The model is left **pickable** on purpose — damage resolves from the warhead
|
|
418
|
+
gathering destroyables near the detonation, not from which mesh the ray hit,
|
|
419
|
+
so the model can simply be the target and the hidden-cube-plus-skin dance
|
|
420
|
+
isn't needed.
|
|
421
|
+
- **`spawnProjectile`/`spawnMissile` take a `mesh`** (#19) — draw the round as
|
|
422
|
+
an authored model, with the engine still owning motion, collision, lifetime
|
|
423
|
+
and disposal. It is also **oriented along velocity**, which a sphere never
|
|
424
|
+
needed: the engine moved a sphere, a sphere has no facing, so a modelled
|
|
425
|
+
missile flew sideways. Yaw and pitch only — a round has no reason to bank.
|
|
426
|
+
Supplied meshes and their children are made non-pickable, like the default.
|
|
427
|
+
|
|
428
|
+
- **`panelFitWidth(fov, aspect, z, want, fill)`** — how wide a camera-relative
|
|
429
|
+
panel may be and stay on screen. Shared because there are two such panels and
|
|
430
|
+
a constant copied into both is a constant that will disagree with itself.
|
|
431
|
+
|
|
432
|
+
- **PAUSE — ⚠️ EXPERIMENTAL.** The flat path is confirmed on a real browser
|
|
433
|
+
(start paused, continue, background, return, repeat) but the VR path — the
|
|
434
|
+
Continue tap carrying the user gesture into `enterXRAsync`, and removing the
|
|
435
|
+
headset pausing — is **unvalidated**, and it is the reason the feature exists.
|
|
436
|
+
Treat the API as unsettled until it's been through a headset; nothing else
|
|
437
|
+
depends on it.
|
|
438
|
+
|
|
439
|
+
`b3d.pause(reason)` / `.resume()` / `.togglePause()` / `.paused`,
|
|
440
|
+
a centred in-scene pause panel, and the attributes around them:
|
|
441
|
+
|
|
442
|
+
- `pauseWhenHidden` (default **on**) — backgrounding the tab holds the scene,
|
|
443
|
+
so a player returns to a held frame and a panel rather than to a world that
|
|
444
|
+
carried on without them. Suppressed while immersive, since some browsers
|
|
445
|
+
report the page hidden during an XR session.
|
|
446
|
+
- `startPaused` — come up paused: the "press Start" shape.
|
|
447
|
+
- `enterXrOnResume` — resume enters VR, and leaving VR pauses. **This is why
|
|
448
|
+
starting paused matters**: `enterXRAsync` requires a user gesture, so a
|
|
449
|
+
scene that tried to enter XR on load would simply be refused. The Continue
|
|
450
|
+
tap is the gesture, which makes "leave it paused, put the headset on, press
|
|
451
|
+
Continue" the only shape that reliably works.
|
|
452
|
+
- `pausePanel(host, resume)` — replace the default rows (a title and Continue)
|
|
453
|
+
with your own: a title screen, settings, a "you were away" summary. `resume`
|
|
454
|
+
is handed in, because a pause panel with no way out is the failure this
|
|
455
|
+
feature exists to prevent.
|
|
456
|
+
- `pause` / `resume` / `orientation` events. `pause` carries a `reason`
|
|
457
|
+
(`'user' | 'hidden' | 'xr' | 'start'`) so a game can tell "the player asked"
|
|
458
|
+
from "the tab went away". `orientation` is derived from the **viewport**
|
|
459
|
+
(`innerWidth`/`innerHeight` swapping), not from `screen.orientation` — that
|
|
460
|
+
API is patchy on iOS, so a scene depending on it hears nothing on the
|
|
461
|
+
devices that rotate most; it's still read for `type`/`angle` when present,
|
|
462
|
+
as detail rather than trigger. Fires only when the classification actually
|
|
463
|
+
flips, so dragging a desktop window edge isn't a rotation.
|
|
464
|
+
|
|
465
|
+
Rendering continues while paused — the panel has to be drawn and picked —
|
|
466
|
+
but nothing advances: no combat tick, no fog, no `update` hook, and
|
|
467
|
+
controllables read empty input, so a held stick can't steer a stopped world.
|
|
468
|
+
Orientation changes are **reported, not acted on**: a rotating phone is
|
|
469
|
+
sometimes a pause and sometimes nothing, and only the game knows which. (Tilt
|
|
470
|
+
is a separate, permissioned API on iOS and deliberately untouched.)
|
|
471
|
+
|
|
472
|
+
- **The stick sign contract is now a test** (`stick-sign.test.ts`) — every
|
|
473
|
+
source must report **up/forward as positive**, and all of them must agree.
|
|
474
|
+
This was documented in three places and enforced in none, which is what let
|
|
475
|
+
#14 run for a day: a source that disagrees is internally consistent, so only
|
|
476
|
+
a player with two devices notices, and what they report is "the framework is
|
|
477
|
+
broken". Verified by deliberately flipping the touch and hardware signs in
|
|
478
|
+
turn and watching it fail. `STICK_UP_IS_POSITIVE` names the convention.
|
|
479
|
+
- **`GamepadSource.kind`** — a stable `'keyboard' | 'hardware' | 'touch' | 'xr'
|
|
480
|
+
| 'glass'` marker, because the obvious alternative fails silently: an adopter
|
|
481
|
+
matched our glass pad with `constructor.name`, their bundler mangled it, the
|
|
482
|
+
lookup never matched, and every experiment built on it was a no-op that
|
|
483
|
+
looked like a result. Class names are not API.
|
|
484
|
+
|
|
485
|
+
- **A gamepad gadget in the gear panel** — one tap to pin the glass pad visible
|
|
486
|
+
or hand it back to the auto-hide. The fade is production-correct and
|
|
487
|
+
development-hostile: once a mouse or trackpad is present the pad goes away and
|
|
488
|
+
doesn't come back, so checking it on a laptop meant switching Chrome into
|
|
489
|
+
responsive mode. It sits in the same icon bar as the debug tools, so it works
|
|
490
|
+
in a headset too. `B3dGamepad.setFade()` / `.hidden` are the API behind it.
|
|
491
|
+
|
|
492
|
+
- **`b3d.sceneBusy`** and **`terrain.busy`** — is the world still building? Any
|
|
493
|
+
frame-rate judgement taken while these are true is a measurement of the loading,
|
|
494
|
+
not of the device.
|
|
495
|
+
- **`b3d.ambientPoolScale` is now readable AND settable**, so a game can say "that
|
|
496
|
+
was the loading screen, try again" instead of reaching into two privates (#11).
|
|
497
|
+
- **`aircraftMapping` honours its config** — it accepted one and had zero
|
|
498
|
+
references to it. `AircraftMappingConfig` adds `invertPitch` (the arcade pitch
|
|
499
|
+
convention), `invertRoll` and `invertCameraY`. Meaning belongs in the mapping:
|
|
500
|
+
every source passes through it, so one setting can't leave the keyboard inverted
|
|
501
|
+
and the glass gamepad not (#10, manta-recon).
|
|
502
|
+
|
|
503
|
+
- **The pure flight model is exported**: `flyByWireStep`, `regime`,
|
|
504
|
+
`targetVelocity`, `chaseVelocity`, **`equilibriumSpeed`** and the
|
|
505
|
+
`FlyByWireConfig`/`Command`/`State` types. `equilibriumSpeed` is what the
|
|
506
|
+
HUD's new set-point mark is drawn from — a mission planner wants the same
|
|
507
|
+
number, and until now it was unreachable from the package.
|
|
508
|
+
- **`isIgnored(name)`** — one matcher for the `-ignore` convention, used by the
|
|
509
|
+
loader, the library and `publicName`.
|
|
510
|
+
|
|
511
|
+
- **[carve](https://3d.tosijs.net/carve/)** — the cave vocabulary, exported as
|
|
512
|
+
the **`carve.*`** namespace (see ⚠️ Breaking): `carve.sphere`,
|
|
513
|
+
`carve.capsule`, `carve.tube`, `carve.box`, `carve.smoothUnion` (fillets a
|
|
514
|
+
junction so a passage flares into a chamber), `carve.subtract`,
|
|
515
|
+
`carve.intersect`, plus the two perturbations that stop a carve looking
|
|
516
|
+
carved: `carve.roughen` (texture the wall, silhouette stays) and
|
|
517
|
+
`carve.warp` (bend the space so nothing reads as a primitive).
|
|
518
|
+
- **[sdf-lattice](https://3d.tosijs.net/sdf-lattice/)** — surface-nets
|
|
519
|
+
extraction over ONE global hash-jittered lattice, so chunks weld
|
|
520
|
+
bit-identically and cross-tile/cross-LOD seams are unrepresentable rather
|
|
521
|
+
than stitched. Includes the chunk-weld proof as a test.
|
|
522
|
+
- **[patch-field](https://3d.tosijs.net/patch-field/)** — `landform`'s
|
|
523
|
+
volumetric sibling: `(x, y, z, d) => d'` carving composed against the
|
|
524
|
+
terrain's own hooked height sampler.
|
|
525
|
+
- **`landform`** gains `gulley` (a height FORCING function ending in a
|
|
526
|
+
predictable cliff), `cover` (forced ground over a tunnel so it stays
|
|
527
|
+
buried), and `pad` from 0.6.2's family.
|
|
528
|
+
- **`b3d-terrain`**: `patchMask`/`patches` hooks, `biomeLapseRate` (the lapse
|
|
529
|
+
is coupled to vertical scale — a 340m world on the small-world default
|
|
530
|
+
renders entirely as snow), and a public `heightSampler()`.
|
|
531
|
+
- **Landing gear, found by NAME** — AnimationGroups mentioning "gear" and
|
|
532
|
+
"retract" are cycled from height above ground, with hysteresis and an
|
|
533
|
+
optional `gearSound`. The animation is SCRUBBED, not played, because glTF
|
|
534
|
+
animations arrive with a cyclic loop mode and a played group can snap back
|
|
535
|
+
to frame 0.
|
|
536
|
+
- **`arcDashArray`** + `hud.setMeterMarks` — reference marks on any meter,
|
|
537
|
+
drawn on the meter's own arc. Used for the throttle set point, sea level,
|
|
538
|
+
and the ground beneath you.
|
|
539
|
+
- **`equilibriumSpeed(cfg, throttle, afterburner)`** — where a lever will
|
|
540
|
+
settle.
|
|
541
|
+
- **`sceneDelta(scene)` is now actually exported.** 0.6.2 announced it and
|
|
542
|
+
shipped it unreachable — `dist/index.d.ts` had no such symbol — so anyone
|
|
543
|
+
following those notes hit a link error. It's the correct frame delta for
|
|
544
|
+
anything ticking inside a scene observer (see 0.6.2's note on
|
|
545
|
+
`getDeltaTime`), and the six doc demos that still taught the broken idiom
|
|
546
|
+
now use it.
|
|
547
|
+
|
|
548
|
+
### Changed
|
|
549
|
+
|
|
550
|
+
- **tosijs-ui devDependency → ^1.9.8**, for serve-time gzip on the dev server:
|
|
551
|
+
it was sending the 10.7 MB bundle uncompressed even to clients asking for
|
|
552
|
+
gzip, so over a thin link a doc page showed its pre-rendered HTML and then
|
|
553
|
+
never hydrated — the bundle never finished arriving. 2.56 MB now. Doc-site
|
|
554
|
+
tooling only; nothing in the published library changes.
|
|
555
|
+
|
|
556
|
+
- **⚠️ The aircraft throttle is a LEVER, not a speed setpoint.** It commands
|
|
557
|
+
an equilibrium: a climb settles at a new lower speed instead of stalling,
|
|
558
|
+
and lowering the nose returns you to the speed you had, untouched. Full
|
|
559
|
+
lever is MILITARY thrust; afterburner lights only while the trigger is held
|
|
560
|
+
past a detent and drops back when released. Releasing the trigger holds the
|
|
561
|
+
setting, so speed no longer self-sustains at idle — set the lever where you
|
|
562
|
+
want to cruise.
|
|
563
|
+
- **⚠️ The right stick is the CAMERA**, not aux roll: it orbits the chase
|
|
564
|
+
camera around the aircraft and turns the pilot's head in the cockpit,
|
|
565
|
+
springing back on release. (`strafe` still sums into roll for anyone who
|
|
566
|
+
maps a dedicated axis.)
|
|
567
|
+
- Hover gained a brake (the trigger's negative half sheds speed to a stop)
|
|
568
|
+
and slow reverse (nose-up lean), with the trigger staying purely vertical
|
|
569
|
+
so stopping never fights altitude. `hoverCeiling` 50 → 140.
|
|
570
|
+
- Speed gauges read 0..100% of the FULL envelope, afterburner included.
|
|
571
|
+
- Terrain defaults: bigger gross features carrying less gross amplitude, with
|
|
572
|
+
the detail layer doing real work — the old defaults read as pudding.
|
|
573
|
+
- `worldV` defaults to 0.25: `CylinderSampler` reflects v, so `worldV = 0`
|
|
574
|
+
put every scene ON a mirror plane with a seam running to the horizon.
|
|
575
|
+
|
|
576
|
+
### Fixed
|
|
577
|
+
|
|
578
|
+
- **Phantom collisions: an aircraft "collided with something it was nowhere
|
|
579
|
+
near".** Flying high, banking in cockpit view, you died instantly — in any
|
|
580
|
+
scene, terrain or not. The impact sweep was hitting the **spatial UI panel**
|
|
581
|
+
floating in front of the cockpit and treating it as terrain, and that sweep
|
|
582
|
+
crashes on ANY hit above `crashSpeed` with no slope test. It only bit in
|
|
583
|
+
cockpit view (that is where the camera, and the panels riding its reference
|
|
584
|
+
frames, sit inside a ~2m sweep) and fired on bank (which swings the velocity
|
|
585
|
+
vector into the panel). A panel must stay `isPickable` — that is how a
|
|
586
|
+
controller ray targets it — so the fix separates the two questions:
|
|
587
|
+
`markUiMesh` marks it collision-invisible, and both aircraft predicates filter
|
|
588
|
+
it.
|
|
589
|
+
- **Collision rays fired from `node.position`.** Under a `_centerOfGravity`
|
|
590
|
+
pivot that is the STANCE ORIGIN, not where the airframe is — the model swings
|
|
591
|
+
about the CoG under attitude, so banking moved the aircraft out from under its
|
|
592
|
+
own rays. Both rays now go through the world matrix, which is bit-identical to
|
|
593
|
+
`node.position` when there is no pivot.
|
|
594
|
+
- **`canonicalize` did not drop the CONTENT node's scene transform** — see
|
|
595
|
+
⚠️ Breaking above. This is also why the scout's centre of gravity appeared
|
|
596
|
+
wrong.
|
|
597
|
+
- **Ambient effects could never recover from a pool of zero.** The watchdog
|
|
598
|
+
bailed one line before the recovery path written for exactly that state, so a
|
|
599
|
+
scene that shed its garnish to nothing stayed dead for the session — no
|
|
600
|
+
leaves, bubbles or motes on hardware that had been drawing them a second
|
|
601
|
+
earlier.
|
|
602
|
+
- **VR: exiting dropped the flat camera's altitude.** Babylon carries the walked
|
|
603
|
+
headset pose back into the non-XR camera, which for an orbit camera recomputes
|
|
604
|
+
a low angle — you left VR looking at the scene from the floor. The orbit state
|
|
605
|
+
is now snapshotted on entry and restored on exit; free/walkable cameras keep
|
|
606
|
+
the carry-back.
|
|
607
|
+
- **VR: orbit demos dropped you in facing a random direction.** Both piloted
|
|
608
|
+
paths seeded the rig's yaw against your head; free locomotion never did, so a
|
|
609
|
+
demo with no piloted entity left the panel behind you and the subject out of
|
|
610
|
+
sight. Now seeded on the first frame with a real viewer pose, facing what the
|
|
611
|
+
flat camera was looking at.
|
|
612
|
+
- **VR: re-seat did nothing in an aircraft's chase view.** Three camera paths
|
|
613
|
+
capture yaw separately and re-seat only re-armed two; toggling cockpit↔chase
|
|
614
|
+
appeared to "fix" it because the view-change path re-armed the third.
|
|
615
|
+
- **VR: locomotion was scale-blind.** Free-fly walked at a fixed 2.5 m/s, which
|
|
616
|
+
in a 512-unit landscape is ~0.5% of the scene per second — indistinguishable
|
|
617
|
+
from a broken stick, while turning (angular, therefore scale-free) felt fine.
|
|
618
|
+
Speed now scales with the orbit camera's radius; small demos are unchanged.
|
|
619
|
+
- **VR: the grid floor sat at the world origin**, so in any scene whose subject
|
|
620
|
+
is elsewhere it read as a mystery grid off in the distance, visible only in
|
|
621
|
+
VR. It now sits under the subject.
|
|
622
|
+
- **VR: the respawn panel was pinned to your face and jittered** with every head
|
|
623
|
+
movement. It rides the `body` frame now.
|
|
624
|
+
- **`bun run typecheck` was red at the release commit** — a one-argument call to
|
|
625
|
+
a three-argument `canonicalize` in a test, invisible to `bun test` (which
|
|
626
|
+
strips types) and to the build tsconfig (which excludes test files).
|
|
627
|
+
|
|
628
|
+
- **Shadow acne on large receivers** — see `shadowNormalBias` above. It was also
|
|
629
|
+
why scenes looked dull: the stipple darkened roughly half the ground.
|
|
630
|
+
|
|
631
|
+
- **`b3d-svg-plane` cleared a parent it never set.** Its `cameraRelative` sync ran
|
|
632
|
+
`mesh.parent = null` unconditionally whenever the flag was off, so anything
|
|
633
|
+
parented to a plane was silently torn off a frame later, with no error. Same
|
|
634
|
+
shape as the pause bug below: **a guard must record what you DID, not observe
|
|
635
|
+
what state the object is in.**
|
|
636
|
+
|
|
637
|
+
- **The glass gamepad no longer fades when you move the mouse.** It is operable
|
|
638
|
+
by pointer — `pointerdown` drives the sticks — so it vanished exactly as you
|
|
639
|
+
reached for it. Only a keypress or a physical gamepad fades it now.
|
|
640
|
+
|
|
641
|
+
- **`publicName` leaked loader and Blender artifacts.** The library picker showed
|
|
642
|
+
`building_collideCylinder_primitive0`; `_primitiveN` is the glTF loader
|
|
643
|
+
splitting a multi-material mesh, and Blender's `.001` sat AFTER the behaviour
|
|
644
|
+
suffix so no `endsWith` matched it. `.001` is preserved (it is a different
|
|
645
|
+
object) while the annotation is removed.
|
|
646
|
+
|
|
647
|
+
- **`ry` is DEGREES, and now says so.** `AbstractMesh.render()` multiplies
|
|
648
|
+
rx/ry/rz by `DEG_TO_RAD`; nothing documented it, and the b3d pause demo had
|
|
649
|
+
been adding radians per second for its whole life — about one revolution per
|
|
650
|
+
ten minutes, which read as "the cube doesn't spin".
|
|
651
|
+
|
|
652
|
+
- **Impacts now report the surface NORMAL, not just the point** (#29) —
|
|
653
|
+
`spawnProjectile`/`spawnMissile` take `whenImpact({ point, normal, mesh })`.
|
|
654
|
+
The swept ray already computed the normal and the launcher was throwing it
|
|
655
|
+
away, so anything that wanted an oriented effect (a scorch mark, a dent, a
|
|
656
|
+
ricochet) had to re-cast the same ray to get it back. `normal`/`mesh` are
|
|
657
|
+
`null` when there is genuinely no surface — a depth fuse in open water, a
|
|
658
|
+
timed round in mid-air. The old `onImpact(point)` still fires with a one-shot
|
|
659
|
+
deprecation warning; it was renamed off the `on*` prefix because such a key
|
|
660
|
+
becomes an `addEventListener` call the moment the shape is lifted onto an
|
|
661
|
+
element, and then silently never runs.
|
|
662
|
+
- **Pause didn't pause** (#30) — an adopter measured a player travelling 66.58 m
|
|
663
|
+
during a 3-second pause with `paused === true` throughout. Two clocks leaked,
|
|
664
|
+
not one. `sceneDelta` accepted a published frame delta only when `> 0`, so the
|
|
665
|
+
one value meaning "stopped" was the one value it discarded, falling back to
|
|
666
|
+
the live engine clock — everything on the render observable (projectiles,
|
|
667
|
+
water, ambient) kept running. And `B3dControllable` runs its own `Date.now`
|
|
668
|
+
clock that a stopped scene delta cannot reach: it was fed empty input, but
|
|
669
|
+
with no input an aircraft COASTS, which is indistinguishable from cruising. A
|
|
670
|
+
paused scene now publishes a delta of zero and controllables halt outright.
|
|
671
|
+
- **Blasts could not see library-backed destroyables** (#28) — a third
|
|
672
|
+
consequence of `library`. `detonateWarhead` resolved targets with
|
|
673
|
+
`scene.getMeshByName`, which searches meshes only, so a `library`
|
|
674
|
+
destroyable's TransformNode root was filtered out of every AOE. manta measured
|
|
675
|
+
it at 4 of 4 targets. It now uses the element's own node, which was always
|
|
676
|
+
right there.
|
|
677
|
+
|
|
678
|
+
- **`library` + `explode` on a destroyable was a guaranteed crash** (#24) — and a
|
|
679
|
+
nasty one. The exploder read vertex buffers off whatever it was handed, and a
|
|
680
|
+
library instance's root is a geometry-less transform node. Worse than a throw:
|
|
681
|
+
it happened inside a render observer, so every observer after it was skipped —
|
|
682
|
+
camera follow, flight integration, all of it — and the scene stopped advancing
|
|
683
|
+
while input kept responding. It presents as "the game seized but the controls
|
|
684
|
+
still work". `explodeMesh` now takes a `TransformNode` and shatters the
|
|
685
|
+
descendants that have geometry (a multi-part model comes apart properly instead
|
|
686
|
+
of one piece shattering while the rest hangs in the air), warns once rather than
|
|
687
|
+
throwing when there is nothing to shatter, and the death path wraps the call
|
|
688
|
+
because a cosmetic must never be able to stop the frame loop.
|
|
689
|
+
- **Mutual death produced no death panel** (#25). The already-dying _recovery_
|
|
690
|
+
added in 0.7.0 ran before the "only OUR death matters" filter, so any other
|
|
691
|
+
entity dying while the player was dead tore the panel down — ram an enemy and
|
|
692
|
+
you both die, and you got nothing. Relevance is checked first now.
|
|
693
|
+
|
|
694
|
+
- **Volcanic veins were absent from every vertical surface** (3D Worley), the
|
|
695
|
+
**rock stopped warming** at dark brown so a fully volcanic face read as black,
|
|
696
|
+
**verticals could never exceed stage 2** so the top of the ladder was
|
|
697
|
+
unreachable on a cliff or cut face, and the **glow pulse touched only the
|
|
698
|
+
molten channel** — a fraction of a fraction, which is invisible in practice.
|
|
699
|
+
|
|
700
|
+
- **Volcanic veins were missing from every vertical surface.** The plate pattern
|
|
701
|
+
was sampled from world **XZ only**, so a cliff or a cut face barely moves
|
|
702
|
+
through the noise — its xz coordinate is near-constant across the surface —
|
|
703
|
+
and you got vertical streaks instead of a vein web. Now sampled with a true
|
|
704
|
+
**3D Worley**, which costs the same neighbourhood as blending three 2D
|
|
705
|
+
projections (27 cells either way, a 3-wide hash instead of 2-wide) and is
|
|
706
|
+
strictly better: no projection seams, no ghosting where planes blend, and a
|
|
707
|
+
cut face shows the veins' real cross-sections because the veins are genuinely
|
|
708
|
+
three-dimensional. Only the volcanism branch reaches it, so non-volcanic
|
|
709
|
+
surfaces pay nothing.
|
|
710
|
+
|
|
711
|
+
- **The HUD circle dominated a portrait viewport.** It was already sized off the
|
|
712
|
+
_smaller_ dimension — that part was right — but a single percentage of the
|
|
713
|
+
small side means 36% of the width in landscape and **70% of it in portrait**,
|
|
714
|
+
the same rule with twice the visual weight. It's now capped at half that
|
|
715
|
+
fraction of the long side as well, so turning the device doesn't change how
|
|
716
|
+
much of the screen the circle eats: measured 490px (was 499) at 1400x713, and
|
|
717
|
+
265px (was 350) at 500x757. `hudSizePx` in `hud-math.ts`, unit-tested.
|
|
718
|
+
|
|
719
|
+
- **`b3d-death`'s Respawn panel had both of the pause panel's problems**, and at
|
|
720
|
+
a worse moment. Its spectator camera was attached to the canvas, so a tap
|
|
721
|
+
meant for Respawn also drove the camera (Tonio, on a phone: _"the view
|
|
722
|
+
pivots"_), and it used the same fixed 1.1-unit width that overflows a portrait
|
|
723
|
+
viewport. The camera is now set with `{ attach: false }` — the orbit still
|
|
724
|
+
runs, it's driven by an observable, you just can't wrestle it — and the width
|
|
725
|
+
comes from the camera's FOV and aspect via the shared `panelFitWidth`.
|
|
726
|
+
|
|
727
|
+
- **The pause panel fought the camera for your tap.** It lives in the scene, so
|
|
728
|
+
a press on it is also a press on the canvas, and the camera's input got it
|
|
729
|
+
too: on a phone the first attempt to press Continue read as a pinch-zoom,
|
|
730
|
+
which moved the camera through the panel and hid it, so the button had to be
|
|
731
|
+
un-zoomed back into reach. The camera is now detached while the panel is up
|
|
732
|
+
(which is also what "paused" ought to mean) and restored on resume.
|
|
733
|
+
- **The pause panel was wider than a phone's viewport.** Sized at a fixed 1.1
|
|
734
|
+
world units, it fits a 16:9 monitor and overflows a portrait phone — at the
|
|
735
|
+
default FOV only ~0.86 units are visible across, so the edges and the button
|
|
736
|
+
sat off-screen. It now derives its width from the camera's real FOV and
|
|
737
|
+
aspect. `panel-fit.test.ts` pins it, including the portrait case that broke.
|
|
738
|
+
|
|
739
|
+
- **`B3dGamepad.fade` was read once at connect**, so toggling it at runtime did
|
|
740
|
+
nothing in either direction — including from a `scenePanel` toggle, which is
|
|
741
|
+
this repo's own recommended way to expose a tweakable. A settings control that
|
|
742
|
+
silently does nothing is worse than no control. Now evaluated live, and
|
|
743
|
+
`setFade()` reconciles on the tap rather than on the next pointer move.
|
|
744
|
+
|
|
745
|
+
- **A guided missile consumed the caller's `guide` hook** (#13), so a game could
|
|
746
|
+
give medium behaviour — water drag, a depth floor — to a dumb shell but not to
|
|
747
|
+
a missile. The seeker is now a named function the caller's hook composes with,
|
|
748
|
+
and the caller runs **last** so it can constrain what homing asked for: the
|
|
749
|
+
seeker wants to go fast, the water says no. `MissileOpts.guide` is new.
|
|
750
|
+
|
|
751
|
+
- **The sky ignored the water you were under** (#12, manta-recon: _"can't see
|
|
752
|
+
anything underwater except for the skybox"_). A skybox is built
|
|
753
|
+
`applyFog = false` with `infiniteDistance = true` — right in air, where a long
|
|
754
|
+
fog layer would swallow it — so submerged, the fog worked on everything except
|
|
755
|
+
the thing filling most of the screen. `b3d-water` now turns the sky's fog on
|
|
756
|
+
while the camera is under it, keyed off the same band weight as the fog layer
|
|
757
|
+
so the two can't disagree about where the surface is, and hands it back on the
|
|
758
|
+
way out. Note the trap it set: turning `underwaterFog` DOWN made it worse,
|
|
759
|
+
because the murk had been the only thing disguising an unfogged sky.
|
|
760
|
+
|
|
761
|
+
- **The device probe measured the machine while it was still loading, then cached
|
|
762
|
+
the verdict for 30 days** (#11, manta-recon). It went out on `setTimeout(…, 0)`
|
|
763
|
+
— one task, not one idle moment — so the benchmark ran during terrain build and
|
|
764
|
+
shader compile. It times work against a fixed reference and calls anything under
|
|
765
|
+
~1.67× the medium baseline **low**, so the machine that loads the biggest scene
|
|
766
|
+
contends the most and tiers worst: an M5 Max holding 120fps was being given
|
|
767
|
+
low-tier budgets for terrain, shadows, reflections and ambient at once. It now
|
|
768
|
+
waits for ~1s of settled frames (`sceneBusy`), gives up after 30s, and caches a
|
|
769
|
+
measurement it had to take under load with a short life so the next visit
|
|
770
|
+
re-measures.
|
|
771
|
+
- **Ambient was shed during the loading screen and never came back** (#11). The
|
|
772
|
+
watchdog's 10s warmup was a fixed timer where the question is "has the scene
|
|
773
|
+
settled" — a streaming world loads for longer, so the one-way ratchet fired on
|
|
774
|
+
frames that said nothing about the hardware and ambient stayed at zero for the
|
|
775
|
+
session. The warmup clock now runs only while the scene is quiet.
|
|
776
|
+
|
|
777
|
+
- **`b3d-death` latched after the first death if the game respawned by its own
|
|
778
|
+
route.** `_dying` is cleared only by `resume()`, which only the panel's
|
|
779
|
+
Respawn button calls — and that button doesn't exist without a `respawn`
|
|
780
|
+
callback. So a scene that respawns from its own `crash` listener (a
|
|
781
|
+
documented pattern) left the component stuck, and it then swallowed every
|
|
782
|
+
later death: no panel, no focus release, welded to the wreck. That is the
|
|
783
|
+
exact failure this component exists to prevent, one level up. A death from an
|
|
784
|
+
entity that isn't the wreck it's holding now tears down and handles the new
|
|
785
|
+
one, and a panel built with no way out says so in the console. (Found by
|
|
786
|
+
manta-recon crashing a respawned aircraft.)
|
|
787
|
+
|
|
788
|
+
- **`Foo_ignore` was half-honoured**: the loader disposed only the hyphen form
|
|
789
|
+
while `publicName` stripped only the underscore one, so an underscore-form
|
|
790
|
+
node survived the load AND collapsed to `Foo` — colliding with a real `Foo`,
|
|
791
|
+
which made `instantiate('Foo')` resolve to whichever came first. Both
|
|
792
|
+
separators now go through `isIgnored`.
|
|
793
|
+
- **`spinner()` in the demo helpers** still used `engine.getDeltaTime()`, so on
|
|
794
|
+
any page importing it (the water demo among them) crates spun ~4× slow at the
|
|
795
|
+
default `frameRate: 30` — the one call site the `sceneDelta` sweep missed.
|
|
796
|
+
|
|
797
|
+
- **A crashed aircraft releases input focus so a respawn can take over** (#9). A wreck keeps input
|
|
798
|
+
focus and ignores it, so with no `<tosi-b3d-death>` in the scene the player
|
|
799
|
+
held a dead controller — which reads as broken controls rather than as
|
|
800
|
+
dying. The crash now releases focus if nothing else handled the event.
|
|
801
|
+
- **`recenter()` reset `worldV` to 0**, which is the `CylinderSampler` mirror
|
|
802
|
+
plane — undoing the new default and putting a seam through the world, hours
|
|
803
|
+
into a session where nobody would connect the two.
|
|
804
|
+
|
|
805
|
+
- **Tile skirts hung 344m through tunnels** — depth came from world amplitude
|
|
806
|
+
rather than the tile's own relief.
|
|
807
|
+
- `_centerOfGravity` markers, cave shading (`interior` as a depth ramp,
|
|
808
|
+
flooding by `waterTable`, `noWater`), volcanism confined to its caldera.
|
|
809
|
+
|
|
810
|
+
## 0.7.0-rc.1
|
|
811
|
+
|
|
812
|
+
Published to npm under the `next` tag on 2026-08-13, superseded by 0.7.0.
|
|
813
|
+
Everything in 0.7.0 above applies, with two differences that matter if you
|
|
814
|
+
installed it:
|
|
815
|
+
|
|
816
|
+
- It exported the 13 `carve` functions as **bare top-level names**; 0.7.0 moves
|
|
817
|
+
them to `carve.*` (see ⚠️ Breaking).
|
|
818
|
+
- It did **not** export `sceneDelta`, despite the notes promising it — the
|
|
819
|
+
`getDeltaTime`-in-a-scene-observer fix was unusable from the package.
|
|
820
|
+
|
|
821
|
+
It was tagged **before** the pre-release review ran, deliberately, to get it
|
|
822
|
+
into an adopter's hands sooner. The review found no blocker in the shipped
|
|
823
|
+
behaviour; what it found were the two release-artifact gaps above.
|
|
824
|
+
|
|
825
|
+
## 0.6.2
|
|
826
|
+
|
|
827
|
+
Terrain gets **volcanism** and **authored landforms**, clouds get real shapes, and a
|
|
828
|
+
systemic timing bug that made half the engine run in slow motion is fixed. Additive
|
|
829
|
+
only — no API removals, no peer-dependency changes. (The full terrain-system upgrade
|
|
830
|
+
lands as 0.7.0 once its remaining pieces are in.)
|
|
831
|
+
|
|
832
|
+
### Added
|
|
833
|
+
|
|
834
|
+
- **Volcanism intensity ladder** ([biome-plugin](https://3d.tosijs.net/biome-plugin/)) —
|
|
835
|
+
the `volcanism` dial now climbs stages rather than scaling one look: near-black basalt
|
|
836
|
+
with dark-brown voronoi seams → dark-brown rock with glowing seams → patchy open lava.
|
|
837
|
+
Verticals lag one stage behind horizontals (lava pools flat; cliff faces drain and
|
|
838
|
+
crust over), veins widen as the ladder climbs so pools form by veins merging, and the
|
|
839
|
+
glow is subtly animated (spatially-phased pulse + drifting 3D-noise churn — never a
|
|
840
|
+
global blink). Water pushes the ladder down half a stage and mutes the glow with depth
|
|
841
|
+
instead of cancelling it: smouldering veins under shallow water.
|
|
842
|
+
- **`volcanicPalette`** + `LAVA_PALETTE` / `CRYOVOLCANIC_PALETTE` — the seven ladder
|
|
843
|
+
colours are data, so a frozen world venting molten _water_ (pale ice rock, teal veins,
|
|
844
|
+
glowing blue-white melt) is a palette swap, not a new shader.
|
|
845
|
+
- **Local volcanic provinces** — `b3dTerrain.provinceField = (x, z) => 0..1` marks
|
|
846
|
+
_this_ island volcanic independently of the global dial. Sampled per tile vertex in
|
|
847
|
+
origin-stable coordinates and carried to the shader in the colour buffer's (visually
|
|
848
|
+
inert) alpha channel.
|
|
849
|
+
- **[landform](https://3d.tosijs.net/landform/)** — authored landforms FORCED through
|
|
850
|
+
the terrain noise via the new `b3dTerrain.landform` hook, where
|
|
851
|
+
[slope-profile](https://3d.tosijs.net/slope-profile/)s only _remap_ it. `volcano()`
|
|
852
|
+
and `impactCrater()` each return a matched pair — the height shape **and** the
|
|
853
|
+
volcanism province that makes it glow — so a volcano goes anywhere and an explosion
|
|
854
|
+
leaves a glowing crater. `pad()` claims flat ground for cities and bases;
|
|
855
|
+
`composeLandforms` / `mergeProvinces` chain them. Pure, deterministic, unit-tested.
|
|
856
|
+
- **`_centerOfGravity` model convention** — a child node with that suffix declares where
|
|
857
|
+
a vehicle pivots in flight, while the root origin stays its on-ground stance point, so
|
|
858
|
+
one model conveys both how it flies and how it plants.
|
|
859
|
+
`findCenterOfGravity`/`applyCenterOfGravity` are exported; `b3d-aircraft` applies it
|
|
860
|
+
on both load paths.
|
|
861
|
+
- **`b3dClouds({ model })`** — an authored GLB mesh becomes the cloud lobe (normalized
|
|
862
|
+
on load, bottom-aligned), and clouds are now built as **clusters** of 2–4 overlapping
|
|
863
|
+
lobes laid out by size rank (biggest low, smallest on top straddling the pair) with
|
|
864
|
+
aligned bases, since cumulus condense at a level.
|
|
865
|
+
- **`B3d.frameDelta`** + **`sceneDelta(scene)`** — the authoritative inter-render delta
|
|
866
|
+
(see Fixed).
|
|
867
|
+
|
|
868
|
+
### Fixed
|
|
869
|
+
|
|
870
|
+
- **Everything ticking in a scene observer ran in slow motion.** Babylon's
|
|
871
|
+
`getDeltaTime()` measures the engine's rAF tick, but scene observers only fire when
|
|
872
|
+
`scene.render()` runs — and `<tosi-b3d>` throttles rendering to `frameRate`. At
|
|
873
|
+
`frameRate: 60` on a 120Hz display that's **half speed**; at the default 30, a
|
|
874
|
+
**quarter**. Measured live: a dropped bomb inherited exactly 50% of the aircraft's
|
|
875
|
+
velocity. B3d now publishes the real delta and 14 call sites use it — projectiles,
|
|
876
|
+
missiles, turret, radar, warhead FX, spawner, clouds, ambient, death orbit, exploder,
|
|
877
|
+
star / star-system / planet / black-hole.
|
|
878
|
+
- **`canonicalize` applied a spurious 180° yaw**, flying correctly-authored (Blender
|
|
879
|
+
−Y-forward) models backwards. Babylon reads node-local data raw — the handedness flip
|
|
880
|
+
lives only on the discarded `__root__` — so the collapse now applies **no rotation**,
|
|
881
|
+
it only cleans. Pinned against real content in `model-frame.test.ts`.
|
|
882
|
+
- **Weapons inherited a phantom velocity.** `this.velocity` is only the aircraft's
|
|
883
|
+
hover/ground integrator and reads zero in wing-borne flight; weapons now inherit the
|
|
884
|
+
true world velocity, so bombs fall with you and cannon rounds lead correctly.
|
|
885
|
+
- **Weapon muzzles are computed through the world matrix**, so shots leave the visible
|
|
886
|
+
airframe under any attitude or centre-of-gravity pivot.
|
|
887
|
+
- **Flying into a cliff face passed through it** — the ground check was a single
|
|
888
|
+
downward ray, which reports the valley floor while a wall fills the windscreen.
|
|
889
|
+
Airborne frames now also sweep along the velocity; steep contacts crash.
|
|
890
|
+
- **Death could strand you in the wreck.** Charring the wreck cloned a material carrying
|
|
891
|
+
an unregistered plugin, Babylon threw, and the sequence aborted _after_ the explosion
|
|
892
|
+
but _before_ releasing input focus — no spectate camera, no respawn panel. Material
|
|
893
|
+
plugins are now registered for cloning, and death's exit is shielded so cosmetics can
|
|
894
|
+
never block it again.
|
|
895
|
+
- Volcano calderas are flat-floored basins with the melt confined to the floor and a
|
|
896
|
+
crusted rim (a smoothed shading normal can't make the rim read as open lava).
|
|
897
|
+
- Library instances carry their animations, retargeted onto the instance.
|
|
898
|
+
|
|
899
|
+
## 0.6.1
|
|
900
|
+
|
|
901
|
+
First consumer-reported fixes — both filed by **manta-recon** (tosijs-3d's first
|
|
902
|
+
external adopter) with root-cause analyses read from the 0.6.0 dist. No API changes.
|
|
903
|
+
|
|
904
|
+
### Fixed
|
|
905
|
+
|
|
906
|
+
- **Aircraft chase camera never created when focus adoption lands after a fast
|
|
907
|
+
mesh load** ([#1](https://github.com/tonioloewald/tosijs-3d/issues/1)). Both
|
|
908
|
+
sides deferred to the other: `setupMesh` skipped the camera when
|
|
909
|
+
`inputProvider` wasn't set yet, and `inputFocus` early-returned for any
|
|
910
|
+
self-managed entity — an ordering hole between them left the player flying
|
|
911
|
+
blind on `default-camera`. The focus side now nudges entities using the
|
|
912
|
+
deferral pattern (idempotent `setupFollowCamera`), so whichever side runs
|
|
913
|
+
last completes the setup.
|
|
914
|
+
- **fly-by-wire: zero-speed deadlock above `hoverCeiling`**
|
|
915
|
+
([#2](https://github.com/tonioloewald/tosijs-3d/issues/2)). At full nose-up,
|
|
916
|
+
pitch decay (`diveBoost·sin(pitch)`) could exceed thrust, pinning speed at
|
|
917
|
+
the 0 clamp with the throttle held — a craft frozen mid-air. Held thrust is
|
|
918
|
+
now floored to make progress below `vtolSpeed` (a nose-high climb-out labors
|
|
919
|
+
instead of freezing); the throttle-released zoom-climb stall — the deliberate
|
|
920
|
+
"hard way into high-altitude hover" — is untouched.
|
|
921
|
+
|
|
922
|
+
## 0.6.0
|
|
923
|
+
|
|
924
|
+
Cut as rc.1 → rc.3 (each reviewed; rc consumers see the per-rc sections in git
|
|
925
|
+
history), finalized after the nine-lens review's follow-up fixes landed.
|
|
926
|
+
|
|
927
|
+
The **SVG UI surface** release: a first-class, VR-ready UI substrate — container,
|
|
928
|
+
overlay, widgets seam, table, keyboard — plus the interaction contracts that make
|
|
929
|
+
the same surface work identically in the DOM, on a 3D plane, and under an XR
|
|
930
|
+
controller ray. No peer-dependency changes.
|
|
931
|
+
|
|
932
|
+
### Added
|
|
933
|
+
|
|
934
|
+
- **`box`** — the flow container: blocks + inline items + wrapped text, background/
|
|
935
|
+
border/radius, resize-with-reflow, scroll regions, a coordinate-based event model
|
|
936
|
+
(press/activate, raw capturing `handlePointer` for drags), and D-pad focus
|
|
937
|
+
traversal with a visible ring.
|
|
938
|
+
- **`surface`** — content box + overlay layer: cascade **menus** and persistent
|
|
939
|
+
draggable/closable **panels**, `placePopup` flip/clamp positioning.
|
|
940
|
+
- **`widget-box`** — the seam that lets `widgets3d` controls (sliders, toggles…)
|
|
941
|
+
live inside a `box`/`surface` unchanged.
|
|
942
|
+
- **`table` / `table-layout`** — sticky header, virtualized body, drag-to-scroll,
|
|
943
|
+
icon **selection** (see `selection`), D-pad row traversal with the escape
|
|
944
|
+
contract.
|
|
945
|
+
- **`keyboard` / `key-layout` / `text-edit`** — the on-screen typing surface for a
|
|
946
|
+
headset: long-press accent strips (sticky on lift, generous ray-tolerant taps),
|
|
947
|
+
spacebar caret-trackpad (slow taps still type), pressed-key tint, hold-signifier
|
|
948
|
+
glyphs (▾ accents, ↔ caret drag), per-key D-pad focus; `inputField` with
|
|
949
|
+
code-point-correct editing and the **receiver-caret** model (lit = where text
|
|
950
|
+
lands, dim = not; `setActive`/`onFocus` for host exclusivity). `key-layout` grew
|
|
951
|
+
`numpad`/`dial`/`email`/`url` modes, gap-absorbing multi-unit keys (equal units ⇒
|
|
952
|
+
equal width), and vertical key spans (the numpad's tall enter).
|
|
953
|
+
- **`gamepad-focus`** — the D-pad → focus wire: edge-triggered with typematic
|
|
954
|
+
repeat (pure, testable `createFocusPulse`), menu/A activates, and `claim` scopes
|
|
955
|
+
one physical pad to the last-touched UI when several live on a page.
|
|
956
|
+
- **Inner-focus protocol** on `BoxChild`/`Widget3d` — `focusMove(dx, dy) → boolean`
|
|
957
|
+
(false = focus escaped, host moves on), `focusActivate`, `focusClear`, `setState`
|
|
958
|
+
reflection — so a composite widget (the keyboard) is one child but many focus
|
|
959
|
+
stops.
|
|
960
|
+
- **`panelScene`** — dual-presentation in two lines: textured plane + orbit camera
|
|
961
|
+
- pick routing (mouse AND XR-controller ray) → `handlePointer`, with one gesture
|
|
962
|
+
contract: a `claim(x, y)` policy for orbit-vs-UI, gestures collected on a stable
|
|
963
|
+
catcher quad via the **pick ray** (screen coordinates don't exist in a headset),
|
|
964
|
+
mapped through the gesture-start frame so a target that rescales its own plane
|
|
965
|
+
stays stable, camera-yield during claimed presses, off-plane release ends the
|
|
966
|
+
gesture.
|
|
967
|
+
- **`svgIcons.resize`** glyph (from tosijs-ui), used by the resizable-box demo's
|
|
968
|
+
in-corner grip.
|
|
969
|
+
|
|
970
|
+
### Changed
|
|
971
|
+
|
|
972
|
+
- **`b3dSvgPlane`** routes the texture's alpha to the mesh (`opacityTexture`) —
|
|
973
|
+
transparent svg regions (outside rounded corners) are transparent on the plane,
|
|
974
|
+
not an opaque black substrate.
|
|
975
|
+
- Pick-forwarded events (`pointerEvents: 'on'`) carry a **synthetic pointerId**,
|
|
976
|
+
never the physical pointer's — a listener's `setPointerCapture` on a forwarded
|
|
977
|
+
event can no longer hijack the real mouse/ray stream.
|
|
978
|
+
- Panel demos are side-by-side DOM + 3D (50/50) throughout, built on `panelScene`.
|
|
979
|
+
|
|
980
|
+
### Fixed
|
|
981
|
+
|
|
982
|
+
- **The lost-pointerup chain**: demos passing `pointerEvents: false` were silently
|
|
983
|
+
running with forwarding ON (tosijs discards wrong-typed prop writes — filed as
|
|
984
|
+
tosijs#24); the forwarded events carried the real pointerId, so the flat mirror
|
|
985
|
+
captured the physical mouse and the canvas stopped receiving pointerups. Fixed at
|
|
986
|
+
every layer, and routing is now **self-healing**: a fresh down while a gesture is
|
|
987
|
+
outstanding flushes the stale gesture (keyboard/box/surface), so a lost up costs
|
|
988
|
+
nothing instead of wedging the session.
|
|
989
|
+
- **`SvgTexture`**: a failed rasterize no longer freezes the plane for the session
|
|
990
|
+
— the busy latch always releases and the frame retries on the next tick.
|
|
991
|
+
- Keyboard: accent strips stay inside the keyboard's rect (top row opens below the
|
|
992
|
+
key); a strip tap can't fall through to the key behind it; releasing on the held
|
|
993
|
+
key with jitter goes sticky instead of inserting a random accent; focus follows
|
|
994
|
+
the click (ring on the tapped key); Space presses the focused key.
|
|
995
|
+
- Arrow keys no longer orbit demo cameras (`ArcRotateCameraKeyboardMoveInput`
|
|
996
|
+
removed — arrows drive the UI).
|
|
997
|
+
|
|
998
|
+
### Added (post-rc, from the review follow-ups)
|
|
999
|
+
|
|
1000
|
+
- **`ui.*` namespace** — the SVG UI family's value exports live on one
|
|
1001
|
+
container (`ui.box`, `ui.table`, `ui.keyboard`, …): common nouns don't leak
|
|
1002
|
+
from the barrel. Types stay top-level. Migration from any rc:
|
|
1003
|
+
`import { ui } from 'tosijs-3d'` and destructure.
|
|
1004
|
+
- `Box.interactiveAt` / `Surface.interactiveAt`, and `panelScene`'s **default
|
|
1005
|
+
claim policy** asks them — pressing a button/panel claims the gesture,
|
|
1006
|
+
pressing static prose orbits the camera, consistently across every demo.
|
|
1007
|
+
- `panelGesture` + `uvToViewBox` + `planeLocalToViewBox` exported pure (the
|
|
1008
|
+
gesture contract is unit-pinned); named option types (`TableOptions`,
|
|
1009
|
+
`KeyboardOptions`, `InputFieldOptions`, `PanelSceneOptions`,
|
|
1010
|
+
`GamepadFocusOptions`); `panelScene` takes `updateInterval`; `w3dTheme`
|
|
1011
|
+
(one place reads the `--w3d-*` variables).
|
|
1012
|
+
|
|
1013
|
+
### Fixed (post-rc)
|
|
1014
|
+
|
|
1015
|
+
- `table.focusMove` speaks the protocol's `(dx, dy)` (rc.1 trapped D-pad focus
|
|
1016
|
+
in a hosted table); menu leaf-select no longer destroys persistent panels
|
|
1017
|
+
(`Surface.closeMenus`).
|
|
1018
|
+
- box hover no longer sticks on raw children; a claimless `gamepadFocus`
|
|
1019
|
+
instance is no longer starved (click-away releases the claim).
|
|
1020
|
+
- Table scrolling within the window is transform-only and hover restyles
|
|
1021
|
+
surgically (the virtualized body no longer rebuilds per drag-move);
|
|
1022
|
+
`textBlock` caches its wrap per width.
|
|
1023
|
+
- `SvgTexture` warns once per instance when rasterizes fail (self-healing had
|
|
1024
|
+
made it silent); deploy/tunnel host reads `TOSIJS_DEPLOY_HOST` instead of a
|
|
1025
|
+
committed address.
|
|
1026
|
+
|
|
1027
|
+
### ⚠️ Changed
|
|
1028
|
+
|
|
1029
|
+
- **`B3d.onResize()` / `B3dHud.onResize()` → `handleResize()`** (the `on*`
|
|
1030
|
+
prefix is reserved by the element-creator's listener sugar). A 0.5.x
|
|
1031
|
+
consumer calling `onResize()` must call `handleResize()`.
|
|
1032
|
+
|
|
1033
|
+
## 0.5.2
|
|
1034
|
+
|
|
1035
|
+
### ⚠️ Breaking — peer dependency (`tosijs` ≥ 1.7.8)
|
|
1036
|
+
|
|
1037
|
+
- **`tosijs` peer range `^1.6.9` → `^1.7.8`.** Consumers must be on `tosijs`
|
|
1038
|
+
**≥ 1.7.8**; a project on `1.6.x` will hit `ERESOLVE`. (1.7.6/1.7.7 are
|
|
1039
|
+
**deprecated on npm** — a `parts`-resolution regression; 1.7.8 fixes it, and
|
|
1040
|
+
tosijs-ui 1.7.4 requires it.) As with 0.5.1, this ships under a **patch**
|
|
1041
|
+
deliberately — `0.x` narrative versioning, with `0.5.0` reserved for the
|
|
1042
|
+
playable-game cut — so it's flagged here rather than forced to a minor.
|
|
1043
|
+
- _Dev/build only, no consumer action:_ `tosijs-ui` `1.6.23` → `1.7.4`, and
|
|
1044
|
+
`chokidar` added to `devDependencies` (tosijs-ui 1.7.x's shipped doc-site
|
|
1045
|
+
pipeline imports it at runtime but declares it dev-only — filed upstream).
|
|
1046
|
+
|
|
1047
|
+
### Added
|
|
1048
|
+
|
|
1049
|
+
- **`svgIcons`** (`svg-icons.ts`) — an SVG **icon proxy** (`svgIcons.<name>()` →
|
|
1050
|
+
an SVG `ElementCreator`) over a focused, **generated** icon set
|
|
1051
|
+
(`icons/{color,stroked,filled}/*.svg` → `icon-data.ts` via tosijs-ui's
|
|
1052
|
+
`tosijs-make-icons`; `bun run icons`). A composition-suffix subset
|
|
1053
|
+
(rotate/flip/scale/translate/opacity/stroke-width/colour) + the generator's
|
|
1054
|
+
directional redirects + hand-authored aliases (`iconAliases`, e.g.
|
|
1055
|
+
`moreHorizontal`). ~30 icons incl. `tosijs3d`, `xrColor`, `tosiXr`, keyboard,
|
|
1056
|
+
checkbox/stats/status marks.
|
|
1057
|
+
- **`iconGlyph`** — a **texture-safe** icon primitive (explicit-colour SVG `<g>`)
|
|
1058
|
+
for embedding icons in in-scene `widgets3d` / `SvgTexture` UI, where
|
|
1059
|
+
`currentColor` and CSS vars don't resolve in the raster.
|
|
1060
|
+
- **`flowLayout`** (`flow-layout.ts`) — a pure **CSS block/inline-block flow**
|
|
1061
|
+
layout core: the substrate for a first-class SVG UI surface that lives in the
|
|
1062
|
+
DOM and on a 3D texture. Babylon/DOM-free, unit-tested.
|
|
1063
|
+
- **`b3d.snapshot()`** — capture the current view as a PNG **data URL**
|
|
1064
|
+
(resolution-independent, via Babylon's render-target screenshot; works flat,
|
|
1065
|
+
including in-scene panels).
|
|
1066
|
+
- **`icon-name.ts`** — the pure composition-suffix parser behind `svgIcons`.
|
|
1067
|
+
|
|
1068
|
+
### Changed
|
|
1069
|
+
|
|
1070
|
+
- **Scene chrome → icon lozenge.** The top-left toolbar is now one rounded pill
|
|
1071
|
+
with two `svgIcons` buttons (settings + `xrColor` Enter-VR); the scene-panel `×`
|
|
1072
|
+
close is a `close` icon. The doc-browser **header logo** = `tosiXr` and the
|
|
1073
|
+
**favicon** = the teal cube, both via tosijs-ui 1.7.4's new `logo` option.
|
|
1074
|
+
- **README** gained a 280px logo and its stale `xinjs-3d` links were fixed.
|
|
1075
|
+
|
|
1076
|
+
### Fixed
|
|
1077
|
+
|
|
1078
|
+
- **keyboard icon** rendering — the generator strips `fill-rule` from non-`color`
|
|
1079
|
+
icons, so its evenodd key-holes broke; moved to `color/` with `currentColor`
|
|
1080
|
+
(filed upstream). `iconGlyph` bakes `currentColor` to an explicit colour so it
|
|
1081
|
+
renders in the texture raster too.
|
|
1082
|
+
- **Lozenge visible without `:has()`** — it defaulted to `display:none` and
|
|
1083
|
+
revealed via `:has()`, so a browser lacking `:has()` (older Firefox) never
|
|
1084
|
+
showed the toolbar. Now default-visible; `:has()` only suppresses the empty-pill
|
|
1085
|
+
flash.
|
|
1086
|
+
- The svg-icons "composition suffixes" snippet is a **static** code block (was an
|
|
1087
|
+
erroring live example).
|
|
1088
|
+
|
|
1089
|
+
## 0.5.1
|
|
1090
|
+
|
|
1091
|
+
### ⚠️ Breaking — renamed public methods (`on*` → `add*Listener` / `handle*`)
|
|
1092
|
+
|
|
1093
|
+
No peer-dependency changes this release. The break is a **method rename**: tosijs's
|
|
1094
|
+
`elementCreator` treats any `on<Event>`-named member as `addEventListener` sugar and
|
|
1095
|
+
**shadows** a component method of the same name (it now fires a live warning, and it was a
|
|
1096
|
+
latent bug — the handler silently never ran). So the multi-listener subscribe methods and the
|
|
1097
|
+
lifecycle hooks moved off the `on*` prefix. The new names are also more honest: the subscribe
|
|
1098
|
+
methods **push to a listener list** (add/remove semantics), not a single-handler setter.
|
|
1099
|
+
|
|
1100
|
+
| Old (0.5.0) | New (0.5.1) | On |
|
|
1101
|
+
| -------------------------------------- | -------------------------------------------- | ---------------------------------- |
|
|
1102
|
+
| `onSceneAddition` / `offSceneAddition` | `addSceneListener` / `removeSceneListener` | `B3d` (scene owner) |
|
|
1103
|
+
| `onOriginShift` / `offOriginShift` | `addOriginListener` / `removeOriginListener` | `B3d` (scene owner) |
|
|
1104
|
+
| `onGainFocus` / `onLoseFocus` | `handleGainFocus` / `handleLoseFocus` | `B3dControllable` |
|
|
1105
|
+
| `onButton` | `handleButton` | `TouchGamepadSource`, `B3dGamepad` |
|
|
1106
|
+
|
|
1107
|
+
**What a consumer must do:** if you wrote a custom scene child that called
|
|
1108
|
+
`owner.onSceneAddition(cb)` / `owner.onOriginShift(cb)`, or subclassed `B3dControllable` and
|
|
1109
|
+
overrode `onGainFocus`/`onLoseFocus`, or a gamepad source with an `onButton` callback, rename to
|
|
1110
|
+
the new members. It's a pure rename — signatures are unchanged. Built-in components are all
|
|
1111
|
+
migrated; this only affects code you wrote against these surfaces.
|
|
1112
|
+
|
|
1113
|
+
### Added
|
|
1114
|
+
|
|
1115
|
+
- **Coordinate-free `MinSimApi`** (`world-contract.ts` §8) — a world-simulation boundary where
|
|
1116
|
+
**coordinates never cross the membrane**: a driver (an AI narrative engine, a scripted demo)
|
|
1117
|
+
sees _topology + a qualitative distance ladder_, never `x/y/z`. Places, portals, a 7-rung
|
|
1118
|
+
proximity ladder (`same-spot`…`present`, `elsewhere` for a different place), a `SchematicView`
|
|
1119
|
+
("where am I", exits, contents-with-rung), and `route()`. `WorldStore` now implements it
|
|
1120
|
+
alongside its existing surface — the sim keeps real geometry sim-private and answers only in
|
|
1121
|
+
qualities.
|
|
1122
|
+
- **`world-topology.ts`** — the pure, Babylon-free, deterministic spatial math behind the
|
|
1123
|
+
surface: `proximityRung(distance, extent)` (a distance in, an adjective out; bands scale with
|
|
1124
|
+
a place's `extent`), `rungNominal` (the inverse, for placement), `routePortals` (cheapest
|
|
1125
|
+
portal path, bidirectional, locked portals impassable — Dijkstra, deterministic tie-break),
|
|
1126
|
+
`containmentPath` (root→here breadcrumb). Unit-tested without a store or an engine.
|
|
1127
|
+
- **`min-sim-conformance.ts`** — a **framework-agnostic shared conformance kit**
|
|
1128
|
+
(`runMinSimConformance(makeApi, harness)`) that pins the contract behaviour identically for
|
|
1129
|
+
any `MinSimApi` implementation. It imports no test runner (so it ships in the library) and
|
|
1130
|
+
takes the `describe`/`test`/`expect` harness as an argument — the same kit runs in this repo
|
|
1131
|
+
and in a driver's repo (the Ariosto use case), proving both stores behave the same at the seam.
|
|
1132
|
+
- **Clouds drift with wind** — the cloud layer now moves with the wind vector, so the sky is
|
|
1133
|
+
alive rather than a still image.
|
|
1134
|
+
|
|
1135
|
+
### Changed
|
|
1136
|
+
|
|
1137
|
+
- **Demo cameras** migrated to a shared `demo-utils` `orbitCam` helper (tilt-clamped so an orbit
|
|
1138
|
+
can't dip below the horizon) instead of 24 hand-rolled per-demo cameras. Authoring-only — no
|
|
1139
|
+
library-API impact.
|
|
1140
|
+
|
|
1141
|
+
### Fixed
|
|
1142
|
+
|
|
1143
|
+
- **Cloud whiteout** now behaves correctly: it ramps to full **earlier** (less "snaps on at the
|
|
1144
|
+
moment of entry", denser once inside), **blots out the sky** (not just scene geometry), and
|
|
1145
|
+
**mutes projected cloud shadows** under the whiteout (fogged fragments no longer darken through
|
|
1146
|
+
the fog).
|
|
1147
|
+
- **`b3d-controller`** `player` now defaults to `false`. tosijs began **throwing** on an
|
|
1148
|
+
`initAttributes` boolean that defaults to `true` (an absent HTML boolean attribute is `false`,
|
|
1149
|
+
so a `true` default can never turn on) — the old `player: true` default made the controller
|
|
1150
|
+
throw at construction and silently never wire input.
|
|
1151
|
+
|
|
1152
|
+
## 0.5.0
|
|
1153
|
+
|
|
1154
|
+
### ⚠️ Breaking — peer dependencies
|
|
1155
|
+
|
|
1156
|
+
- **Babylon.js 8 → 9.** Peer range for `@babylonjs/core`, `@babylonjs/gui`,
|
|
1157
|
+
`@babylonjs/loaders`, `@babylonjs/materials` moved `^8` → `^9`. **Consumers must
|
|
1158
|
+
upgrade their Babylon to `9.x`.** A project still on Babylon 8 will hit an
|
|
1159
|
+
`ERESOLVE` on install.
|
|
1160
|
+
- **Jolt physics `^1.0` → `^1.1`** (Jolt 5.6.0 — the friction model moved). Only
|
|
1161
|
+
relevant if you use `<tosi-b3d-physics>`; retune friction if grip/slide changed.
|
|
1162
|
+
- **Packaging fix:** `@babylonjs/*` and `jolt-physics` are now **peer-only** (they
|
|
1163
|
+
were previously declared in both `dependencies` and `peerDependencies`). A hard
|
|
1164
|
+
Babylon dependency would nest a _second_ engine copy in a consumer holding a
|
|
1165
|
+
direct Babylon dep — silently breaking `instanceof`, engine singletons, and
|
|
1166
|
+
plugin registration. No action needed by consumers; this just makes install
|
|
1167
|
+
resolution correct.
|
|
1168
|
+
|
|
1169
|
+
### Added
|
|
1170
|
+
|
|
1171
|
+
- **Clouds** (`<tosi-b3d-clouds>`) — an opaque blob cloud layer you can _fly into_
|
|
1172
|
+
and lose the world inside (a fog whiteout, not a texture). A `coverage` weather
|
|
1173
|
+
dial from wisps to thunderheads, and `insideCloud` so a cloud is a _tactic_
|
|
1174
|
+
(break a radar lock, shake a pursuer).
|
|
1175
|
+
- **Projected cloud shadows** (`cloud-shadows.ts`) — the field painted top-down
|
|
1176
|
+
into one texture, sampled by world position in a material plugin, so shadows
|
|
1177
|
+
conform to terrain and fall on elevated receivers (the aircraft) too. Reusable.
|
|
1178
|
+
- **Shadow decals** (`shadow-decal.ts`) — a reusable soft blob-shadow for a single
|
|
1179
|
+
caster (character, vehicle, dropped item).
|
|
1180
|
+
- **Death** (`<tosi-b3d-death>`) — death's exit: explode + burning wreckage,
|
|
1181
|
+
release input, a third-person spectate shot, then a respawn panel. Flat and VR.
|
|
1182
|
+
- **Encounters** — `<tosi-b3d-spawner>` (seeded, "same seed, same battles"),
|
|
1183
|
+
`formations.ts` (pure placement math), and a named `prefab` registry with
|
|
1184
|
+
`spawnPrefab` for the set-dressing a death or spawn drops.
|
|
1185
|
+
- **Ambient effects** (`<tosi-b3d-ambient>`) — device-budgeted garnish (motes,
|
|
1186
|
+
bubbles, tumbling leaves) that competes for one pool and switches **off** rather
|
|
1187
|
+
than thinning into a lie. Pure budget allocator in `ambient-budget.ts`.
|
|
1188
|
+
- **Atmosphere** (`atmosphere.ts`) — pure fog compositing so underwater / cloud /
|
|
1189
|
+
space whiteouts layer over the base fog instead of fighting it.
|
|
1190
|
+
- **`setGameplayCamera`** — an XR-safe viewpoint affordance (move the rig in a
|
|
1191
|
+
headset, swap the camera when flat), so chase/death/vehicle transitions stop
|
|
1192
|
+
breaking in VR.
|
|
1193
|
+
- **`PLATFORM.md`** — the platform bet (stay on the web, don't abstract the
|
|
1194
|
+
renderer; the real risk of the Android XR / Vision Pro shift is _input_).
|
|
1195
|
+
|
|
1196
|
+
### Changed
|
|
1197
|
+
|
|
1198
|
+
- **Aircraft** — the stick is dead on the ground (only the throttle lifts off);
|
|
1199
|
+
you can't crash-land until you've actually cleared the pad; chase camera banks
|
|
1200
|
+
smoothly and no longer jitters on throttle.
|
|
1201
|
+
- **Water** follows the camera as an endless sea; **terrain** auto-centers so a sea
|
|
1202
|
+
at `y=0` floods the valleys.
|
|
1203
|
+
- **HUD** survives respawn.
|
|
1204
|
+
- Babylon `8.56 → 9.16`, Jolt `1.0 → 1.1`, tosijs-ui `1.6.22 → 1.6.23`, tosijs
|
|
1205
|
+
`1.6.8 → 1.6.9` (dev-server/haltija reliability), haltija `→ 1.4.0`.
|
|
1206
|
+
|
|
1207
|
+
### Fixed
|
|
1208
|
+
|
|
1209
|
+
- Death explosion/wreck FX land at the real wreck; a respawn is no longer charred
|
|
1210
|
+
(char a cloned material, not the shared library one).
|
|
1211
|
+
- The aircraft ground-ray no longer picks a cloud blob as "ground" (mid-air
|
|
1212
|
+
"crash"); cloud whiteout is opaque under LINEAR fog and builds from every
|
|
1213
|
+
direction; the live-debug timer is cleared on scene disconnect (no leak).
|