@nakednous/host 0.0.6 → 0.0.8
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/README.md +1 -1
- package/dist/index.js +78 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ dependency first and an options object last, and registers with the context so
|
|
|
79
79
|
| `host.handle(opts)` | `Handle` — a draggable core constraint (`SPHERE` · `PLANE` · `AXIS` · `DIAL` · `VIEW`, or a contract object); `update()`, `value(out, opts)`, snap, hover, cancel, `from` |
|
|
80
80
|
| `host.router(handles, opts)` | `PointerRouter` — one shared pick over overlapping handles, nearest proxy wins; shared hover |
|
|
81
81
|
| `host.cameraHelm(cam, opts)` · `host.poseHelm(opts)` | a core `PoseHelm` on the players: body-fly a camera state, or integrate into a pose in `WORLD` · `EYE` · `SELF` · a mat4 and `bind()` a target |
|
|
82
|
-
| `host.poseTrack(opts)` · `host.cameraTrack(cam, opts)` | core tracks ticked while they play; `{ handles }` builds `TrackHandles`, one handle per keyframe |
|
|
82
|
+
| `host.poseTrack(opts)` · `host.cameraTrack(cam, opts)` | core tracks ticked while they play; `{ handles }` builds `TrackHandles`, one handle per keyframe. `add(depth)` — the transport panel's `+` — authors where the camera is looking: a pose track places the pose at the centre of the installed camera's frustum, `depth` `0`…`1` from the near plane to the far one, aimed along that camera (the view bag's matrices; never deduplicated), a camera track captures its camera state, a placement depth being nothing a camera keyframe could use. `remove()` — the transport panel's `−` — retracts the last keyframe, one per call; `remove(i)` is the core track's own |
|
|
83
83
|
| `host.hid(opts)` · `host.gamepad(opts)` | rate streams — `lin[3]` / `ang[3]` in raw device units, `bind(helm)` feeds it each tick; `available`, `connected` |
|
|
84
84
|
| `host.image(url)` · `host.video(opts)` · `host.raster(draw, w, h)` | texture sources — an `ImageBitmap`, a hidden `<video>` from a file or the camera with a `ready` promise, a Canvas2D drawing |
|
|
85
85
|
| `host.mesh(url, opts)` | one mesh — the arrays shape `{ position, indices, normal?, tangent?, texcoord?, color?, joints?, weights? }` with its `bounds` `{ min, max, center, diag }` beside them — from an OBJ file, a one-part glTF 2.0 file (`.glb`, `.gltf`), or any format through `{ parse: (arrayBuffer) => arrays }`; what a bridge's buffer upload takes as it is, and `size / mesh.bounds.diag` fits any file to `size`. A file of several parts rejects, naming `model`. `{ format }` when the URL does not say. **`normals`**: left out, the file's normals are kept and a mesh without any gets smooth ones; `'smooth'` always recomputes them, summed per position, so a faceted file or a texture seam smooths too; `'flat'` always recomputes them on the flattened mesh (every triangle owning its vertices: more vertices, indices counting up); `false` computes nothing. The procedure, its limits and sources are tree's `mesh` module's; glTF viewers compute flat normals for a file without any — here that is `'flat'` |
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WEBGL, cameraView, cameraProj, mat4Mul, mat4Invert, PLANE, createConstraint, WORLD, AXIS, DIAL, POINT, mapDirection, pixelRatio, rayHitSphere, unproject, SPHERE, dirFromAzEl, DIRECTION, mapLocation, MATRIX, PoseHelm, cameraToPose, EYE, SELF, qToMat4, cameraEye, cameraFromPose, CameraTrack, PoseTrack, qFromAxisAngle, mat4ToTransform, meshBounds, meshFlatten, meshNormals, meshGroups, SCREEN, cameraCopy, createCamera, coastAlive, coastStep, cameraOrbit, cameraPan, cameraDolly } from '@nakednous/tree';
|
|
1
|
+
import { WEBGL, cameraView, cameraProj, mat4Mul, mat4Invert, PLANE, createConstraint, WORLD, AXIS, DIAL, POINT, mapDirection, pixelRatio, rayHitSphere, unproject, SPHERE, dirFromAzEl, DIRECTION, mapLocation, MATRIX, PoseHelm, cameraToPose, EYE, SELF, qToMat4, cameraEye, cameraFromPose, CameraTrack, PoseTrack, qFromAxisAngle, NDC, mat4ToTransform, meshBounds, meshFlatten, meshNormals, meshGroups, SCREEN, cameraCopy, createCamera, coastAlive, coastStep, cameraOrbit, cameraPan, cameraDolly } from '@nakednous/tree';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file The view bag — the matrices every pick, solve, label and orbit reads.
|
|
@@ -1300,11 +1300,69 @@ function poseHelm(host, opts) {
|
|
|
1300
1300
|
* either decorates the track with TrackHandles: one VIEW handle per
|
|
1301
1301
|
* draggable keyframe field (pos / eye / center), an optional rot DIAL per
|
|
1302
1302
|
* keyframe about a declared axis, all on one router.
|
|
1303
|
+
*
|
|
1304
|
+
* add(depth) — the transport panel's + button, which passes its depth slider
|
|
1305
|
+
* (ui's contract) — authors a keyframe where the camera is looking: on a pose
|
|
1306
|
+
* track the pose lands at the centre of the installed camera's frustum at
|
|
1307
|
+
* that depth, aimed along the camera's own eye frame; on a camera track the
|
|
1308
|
+
* camera state is captured, the depth having nothing to place. A panel whose
|
|
1309
|
+
* depth row is hidden passes no depth, which reads the panel's default, 0.5.
|
|
1310
|
+
* The frame is the host's view bag — the matrices the last setCamera
|
|
1311
|
+
* installed — so the placement is the same on every bridge and needs no
|
|
1312
|
+
* renderer here. remove() is the panel's −: no argument retracts the last
|
|
1313
|
+
* keyframe, one per call, an index staying the core track's own remove.
|
|
1303
1314
|
*/
|
|
1304
1315
|
|
|
1305
1316
|
|
|
1317
|
+
// ── Placement — the + button's depth ───────────────────────────────────────
|
|
1318
|
+
//
|
|
1319
|
+
// The transport panel hands add() a depth in [0, 1] and expects a keyframe in
|
|
1320
|
+
// front of the camera. Writer-free placement: unproject the frustum's centre
|
|
1321
|
+
// through the view bag's P · V inverse and aim the pose along the bag's eye
|
|
1322
|
+
// frame (E = V⁻¹, its columns right / up / back). The depth is NDC-linear, as
|
|
1323
|
+
// the p5 transport's was — 0 the near plane's centre, 1 the far plane's — so
|
|
1324
|
+
// every bridge's own ndcZMin maps it without the caller knowing the API.
|
|
1325
|
+
|
|
1326
|
+
const _at = [0, 0, 0]; // the unprojected frustum centre
|
|
1327
|
+
const _dir = [0, 0, 0]; // the camera's forward
|
|
1328
|
+
const _up = [0, 0, 0]; // the camera's up
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* Append a pose at the centre of the installed camera's frustum, `depth` of
|
|
1332
|
+
* the way from the near plane to the far one.
|
|
1333
|
+
*
|
|
1334
|
+
* @param {object} host
|
|
1335
|
+
* @param {function} add The track's core add, already bound.
|
|
1336
|
+
* @param {number} [depth] NDC-linear depth in [0, 1]; clamped, absent or NaN
|
|
1337
|
+
* reads the panel's own default, 0.5.
|
|
1338
|
+
* @returns {boolean} false when no camera is installed (a stale view bag).
|
|
1339
|
+
*/
|
|
1340
|
+
function _addInFront(host, add, depth) {
|
|
1341
|
+
const view = host.view;
|
|
1342
|
+
if (!view || view.stale) return false;
|
|
1343
|
+
const d = Number.isFinite(depth) ? (depth < 0 ? 0 : depth > 1 ? 1 : depth) : 0.5;
|
|
1344
|
+
const ndcZ = view.ndcZMin + d * (1 - view.ndcZMin);
|
|
1345
|
+
mapLocation(_at, 0, 0, ndcZ, NDC, WORLD, view, view.vp, view.ndcZMin);
|
|
1346
|
+
const e = view.mat4Eye; // columns: right · up · back · eye
|
|
1347
|
+
_dir[0] = -e[8]; _dir[1] = -e[9]; _dir[2] = -e[10];
|
|
1348
|
+
_up[0] = e[4]; _up[1] = e[5]; _up[2] = e[6];
|
|
1349
|
+
add({ pos: [_at[0], _at[1], _at[2]], rot: { dir: _dir, up: _up } }, { deduplicate: false });
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1306
1353
|
// ── Players ────────────────────────────────────────────────────────────────
|
|
1307
1354
|
|
|
1355
|
+
// remove(): the panel's −, retracting the last authored keyframe — one per
|
|
1356
|
+
// click, either kind of track. An index stays the core track's own remove, so
|
|
1357
|
+
// a caller editing by index is unaffected; an empty track removes nothing.
|
|
1358
|
+
function _wireRemove(track) {
|
|
1359
|
+
const coreRemove = track.remove.bind(track);
|
|
1360
|
+
track.remove = function (index) {
|
|
1361
|
+
return coreRemove(index == null ? track.keyframes.length - 1 : index);
|
|
1362
|
+
};
|
|
1363
|
+
return track;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1308
1366
|
function _wirePoseTrack(host, track) {
|
|
1309
1367
|
let player = null;
|
|
1310
1368
|
track._onActivate = () => {
|
|
@@ -1324,8 +1382,22 @@ function poseTrack(host, opts) {
|
|
|
1324
1382
|
const o = opts || {};
|
|
1325
1383
|
const track = new PoseTrack();
|
|
1326
1384
|
_wirePoseTrack(host, track);
|
|
1385
|
+
|
|
1386
|
+
// add(depth): the transport's + (see the module header). A spec, or an array
|
|
1387
|
+
// of specs, is the core track's own; no argument — the panel with its depth
|
|
1388
|
+
// row hidden — places at the panel's default depth, 0.5.
|
|
1389
|
+
const coreAdd = track.add.bind(track);
|
|
1390
|
+
let warned = false;
|
|
1391
|
+
track.add = function (spec, addOpts) {
|
|
1392
|
+
if (spec != null && typeof spec !== 'number') return coreAdd(spec, addOpts);
|
|
1393
|
+
if (!_addInFront(host, coreAdd, spec) && !warned) {
|
|
1394
|
+
warned = true;
|
|
1395
|
+
console.warn('[host] poseTrack: add(depth) places a pose against the installed camera, and the view bag is stale — no keyframe added. Call the bridge\'s setCamera first.');
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1327
1399
|
if (o.handles) track.handles = new TrackHandles(host, track, o.handles, false);
|
|
1328
|
-
return track;
|
|
1400
|
+
return _wireRemove(track);
|
|
1329
1401
|
}
|
|
1330
1402
|
|
|
1331
1403
|
/**
|
|
@@ -1344,7 +1416,9 @@ function cameraTrack(host, cam, opts) {
|
|
|
1344
1416
|
|
|
1345
1417
|
const coreAdd = track.add.bind(track);
|
|
1346
1418
|
track.add = function (spec, addOpts) {
|
|
1347
|
-
|
|
1419
|
+
// No spec, or the transport panel's depth: a camera keyframe *is* the
|
|
1420
|
+
// camera, so there is nothing a placement depth could move — capture.
|
|
1421
|
+
if (spec == null || typeof spec === 'number') {
|
|
1348
1422
|
if (!state) return;
|
|
1349
1423
|
spec = state;
|
|
1350
1424
|
} else if (Array.isArray(spec)) {
|
|
@@ -1373,7 +1447,7 @@ function cameraTrack(host, cam, opts) {
|
|
|
1373
1447
|
track._onDeactivate = () => { host.players.remove(player); apply(); };
|
|
1374
1448
|
|
|
1375
1449
|
if (o.handles) track.handles = new TrackHandles(host, track, o.handles, true);
|
|
1376
|
-
return track;
|
|
1450
|
+
return _wireRemove(track);
|
|
1377
1451
|
}
|
|
1378
1452
|
|
|
1379
1453
|
// ── TrackHandles ───────────────────────────────────────────────────────────
|