@rydr/game-sdk 8.14.0 → 8.16.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.
Files changed (63) hide show
  1. package/dist/protocol/identity.d.ts +39 -0
  2. package/dist/protocol/identity.d.ts.map +1 -1
  3. package/dist/protocol/version.d.ts +1 -1
  4. package/dist/protocol/version.js +1 -1
  5. package/dist/protocol/worlds.d.ts +19 -0
  6. package/dist/protocol/worlds.d.ts.map +1 -1
  7. package/dist/protocol/worlds.js.map +1 -1
  8. package/dist/three/README.md +132 -4
  9. package/dist/three/controller/index.d.ts +2 -0
  10. package/dist/three/controller/index.d.ts.map +1 -1
  11. package/dist/three/controller/index.js +2 -0
  12. package/dist/three/controller/index.js.map +1 -1
  13. package/dist/three/controller/joycon-designs.d.ts +15 -1
  14. package/dist/three/controller/joycon-designs.d.ts.map +1 -1
  15. package/dist/three/controller/joycon-designs.js +301 -17
  16. package/dist/three/controller/joycon-designs.js.map +1 -1
  17. package/dist/three/controller/joycon-object.d.ts +216 -0
  18. package/dist/three/controller/joycon-object.d.ts.map +1 -0
  19. package/dist/three/controller/joycon-object.js +578 -0
  20. package/dist/three/controller/joycon-object.js.map +1 -0
  21. package/dist/three/controller/joycon-spec.d.ts +116 -4
  22. package/dist/three/controller/joycon-spec.d.ts.map +1 -1
  23. package/dist/three/controller/joycon-spec.js +128 -22
  24. package/dist/three/controller/joycon-spec.js.map +1 -1
  25. package/dist/three/controller/press-pulse.d.ts +89 -0
  26. package/dist/three/controller/press-pulse.d.ts.map +1 -0
  27. package/dist/three/controller/press-pulse.js +309 -0
  28. package/dist/three/controller/press-pulse.js.map +1 -0
  29. package/dist/three/controller/three-pad-renderer.d.ts +11 -22
  30. package/dist/three/controller/three-pad-renderer.d.ts.map +1 -1
  31. package/dist/three/controller/three-pad-renderer.js +49 -247
  32. package/dist/three/controller/three-pad-renderer.js.map +1 -1
  33. package/dist/three/rider/three-rider-rig.d.ts +12 -0
  34. package/dist/three/rider/three-rider-rig.d.ts.map +1 -1
  35. package/dist/three/rider/three-rider-rig.js +101 -6
  36. package/dist/three/rider/three-rider-rig.js.map +1 -1
  37. package/dist/ui/README.md +133 -7
  38. package/dist/ui/controller/controller-pad.d.ts +105 -2
  39. package/dist/ui/controller/controller-pad.d.ts.map +1 -1
  40. package/dist/ui/controller/controller-pad.js +347 -16
  41. package/dist/ui/controller/controller-pad.js.map +1 -1
  42. package/dist/ui/controller/renderer.d.ts +13 -0
  43. package/dist/ui/controller/renderer.d.ts.map +1 -1
  44. package/dist/ui/controller/renderer.js.map +1 -1
  45. package/dist/ui/controller/styles.js +50 -1
  46. package/dist/ui/controller/styles.js.map +1 -1
  47. package/dist/ui/hud-placement.d.ts +81 -0
  48. package/dist/ui/hud-placement.d.ts.map +1 -0
  49. package/dist/ui/hud-placement.js +74 -0
  50. package/dist/ui/hud-placement.js.map +1 -0
  51. package/dist/ui/index.d.ts +1 -0
  52. package/dist/ui/index.d.ts.map +1 -1
  53. package/dist/ui/index.js +1 -0
  54. package/dist/ui/index.js.map +1 -1
  55. package/dist/ui/rider-rig.d.ts +108 -10
  56. package/dist/ui/rider-rig.d.ts.map +1 -1
  57. package/dist/ui/rider-rig.js +299 -48
  58. package/dist/ui/rider-rig.js.map +1 -1
  59. package/dist/world-runtime.d.ts +11 -1
  60. package/dist/world-runtime.d.ts.map +1 -1
  61. package/dist/world-runtime.js +20 -3
  62. package/dist/world-runtime.js.map +1 -1
  63. package/package.json +4 -2
@@ -87,5 +87,44 @@ export interface ScopedIdentity {
87
87
  * be server-side as well. Absent means {@link DEFAULT_ACCESS_TIER}.
88
88
  */
89
89
  accessTier?: AccessTier;
90
+ /**
91
+ * The event this machine is at, if any (see {@link EventContext}). Absent on an ordinary
92
+ * machine — which is almost always, so treat its presence as the exception and never require it.
93
+ *
94
+ * Read it as `if (identity.event)`, show `identity.event.label`, and do not look at the slug.
95
+ */
96
+ event?: EventContext;
97
+ }
98
+ /**
99
+ * The EVENT this machine is standing at, when it is standing at one (PLAT-1591).
100
+ *
101
+ * At an event RYDR takes a bike to a venue and strangers take turns on it. The shell already knows
102
+ * that — it is what lets someone with no riding account play, and what grants them a tier for the
103
+ * session (`platform/eventMode.ts`). This is the same fact, told to the game.
104
+ *
105
+ * ## It describes the MACHINE, not the rider
106
+ *
107
+ * The odd member of {@link ScopedIdentity}: everything else there is a property of the person, and
108
+ * this is a property of the bike they happen to be sitting on. It travels on the identity anyway,
109
+ * deliberately — the identity is rebuilt and re-pushed on every change, which is exactly the
110
+ * delivery a fact that can lapse mid-session needs (an event pass expires on a clock), and a second
111
+ * channel carrying one optional object would be a protocol message, a capability and a version bump
112
+ * to say something this one already says.
113
+ *
114
+ * ## One treatment for every event
115
+ *
116
+ * A game reads the {@link label} and draws it. It must NOT branch on the {@link slug}: the point of
117
+ * sending the name is that the next event is a change to the platform's event list and nothing
118
+ * else. A game with `if (event.slug === "bxl-crit")` in it has to be redeployed for every venue,
119
+ * which is the cost this field exists to avoid.
120
+ */
121
+ export interface EventContext {
122
+ /**
123
+ * Stable id, e.g. `bxl-crit`. Stamped on the runs recorded here, so it is the key a result can be
124
+ * attributed by. For LOGGING and keys — never for a branch, and never on screen.
125
+ */
126
+ slug: string;
127
+ /** What to put on screen, e.g. "BXL Crit". Human-facing, never parsed. */
128
+ label: string;
90
129
  }
91
130
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/protocol/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC;AAExD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAAmB,CAAC;AAStD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAGlF;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/protocol/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC;AAExD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAAmB,CAAC;AAStD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAGlF;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;CACf"}
@@ -9,5 +9,5 @@
9
9
  export declare const RYDR_PROTOCOL_VERSION: 32;
10
10
  /** Semver of this SDK build. Sent in the handshake for telemetry/debugging.
11
11
  * (Bumped to 2.0.0 by `npm version major` on release — see CHANGELOG [Unreleased].) */
12
- export declare const RYDR_SDK_VERSION = "8.14.0";
12
+ export declare const RYDR_SDK_VERSION = "8.16.0";
13
13
  //# sourceMappingURL=version.d.ts.map
@@ -217,5 +217,5 @@
217
217
  export const RYDR_PROTOCOL_VERSION = 32;
218
218
  /** Semver of this SDK build. Sent in the handshake for telemetry/debugging.
219
219
  * (Bumped to 2.0.0 by `npm version major` on release — see CHANGELOG [Unreleased].) */
220
- export const RYDR_SDK_VERSION = "8.14.0";
220
+ export const RYDR_SDK_VERSION = "8.16.0";
221
221
  //# sourceMappingURL=version.js.map
@@ -36,6 +36,20 @@ export interface WorldObjectGroup {
36
36
  name?: string;
37
37
  memberIds: string[];
38
38
  }
39
+ /**
40
+ * A transform on the **world root** — the whole world (base map, every placed object) moved and
41
+ * uniformly resized as one, on top of each object's own transform.
42
+ *
43
+ * Uniform only, and deliberately: a non-uniform world scale would shear every rotated prop. Scaling
44
+ * happens about the world origin and *then* the offset is added (`p' = p * scale + position`), which
45
+ * is exactly what a scaled+moved root node does. Absent (or identity) on worlds that never used it.
46
+ */
47
+ export interface WorldTransform {
48
+ /** World-space offset applied after scaling. Default `[0, 0, 0]`. */
49
+ position?: [number, number, number];
50
+ /** Uniform scale factor. Default `1`. */
51
+ scale?: number;
52
+ }
39
53
  /** Changes to a base map: small and self-describing. `baseMap` is a glb URL (or "" for none). */
40
54
  export interface WorldMapDelta {
41
55
  baseMap: string;
@@ -43,6 +57,11 @@ export interface WorldMapDelta {
43
57
  addedObjects: WorldAddedObject[];
44
58
  /** Object groups (optional — absent on worlds saved before grouping existed). */
45
59
  groups?: WorldObjectGroup[];
60
+ /**
61
+ * Optional world-root transform — scales/moves the entire world as one (see {@link WorldTransform}).
62
+ * Applied by `applyWorld`; absent on worlds saved before it existed.
63
+ */
64
+ worldTransform?: WorldTransform;
46
65
  }
47
66
  /** Optional lighting/fog for a world. */
48
67
  export interface WorldEnv {
@@ -1 +1 @@
1
- {"version":3,"file":"worlds.d.ts","sourceRoot":"","sources":["../../src/protocol/worlds.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,kGAAkG;AAClG,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,iFAAiF;IACjF,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,aAAa,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,kGAAkG;AAClG,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,SAAS,CAAC"}
1
+ {"version":3,"file":"worlds.d.ts","sourceRoot":"","sources":["../../src/protocol/worlds.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,kGAAkG;AAClG,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,iFAAiF;IACjF,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,aAAa,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,kGAAkG;AAClG,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"worlds.js","sourceRoot":"","sources":["../../src/protocol/worlds.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgFH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"worlds.js","sourceRoot":"","sources":["../../src/protocol/worlds.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoGH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC"}
@@ -29,7 +29,8 @@ import { loadWorld, mergeByMaterial, PerfOverlay, ScreenAnchor } from "@rydr/gam
29
29
  | `auditScene` | fn | One-shot string report of a scene's draw calls / triangles / materials. |
30
30
  | `perfMark` / `perfMeasure` / `perfReport` / `perfAvg` / `perfPeak` | fn | Lightweight per-frame CPU timing marks + rolling stats. |
31
31
  | `createThreePadRenderer` | fn | Draw the controller as a **3D Joy-Con pair** in any `@rydr/game-sdk/ui` controller view. |
32
- | `buildJoyconModel` | fn | The Joy-Con `THREE.Group` on its own, if you want it in your own scene. |
32
+ | `createJoyconObject` | fn | The Joy-Con as an **object in your own scene** — model, lighting and every animation, no canvas. |
33
+ | `PAD_VIEWS` | const | Named orientations — `front`, `angled`, `triggers` — instead of angles in radians. |
33
34
  | `glyphTexture` | fn | A canvas-drawn letter as a `CanvasTexture` (no font file — this package ships no assets). |
34
35
 
35
36
  ---
@@ -170,7 +171,47 @@ mountControllerMap(host, {
170
171
  the shell because it has no way to draw a face you can't see; the 3D model puts them where they are
171
172
  and tilts the pad forward to reveal them. This is the reason the 3D pad exists.
172
173
  - **A press is real travel.** The button moves along the viewing axis and the stick leans, instead of
173
- a shape dropping onto its own drop shadow.
174
+ a shape dropping onto its own drop shadow. The travel is *animated* — fast down, slower back with a
175
+ small spring overshoot — because an instant displacement is not something an eye can catch: it puts
176
+ the key at rest on one frame and at the bottom on the next, with no motion in between. `pressTravel`
177
+ (default `0.07`, ÷ shell width) sets how deep it goes; raise it when the pad is read from far away or
178
+ nearly head-on, where the viewing angle leaves even less of the travel on screen.
179
+
180
+ **The press pulse.** Travel alone does not read — it happens along the viewing axis, so the pad's tilt
181
+ flattens it to almost nothing, leaving only the colour change, which is a *state* rather than an
182
+ *event*. So a white circle marks the instant a key goes down, on the key's own surface, in the scene
183
+ (it tilts, foreshortens and rolls with the pad — a DOM circle over the canvas would not):
184
+
185
+ ```ts
186
+ createThreePadRenderer(layout, {
187
+ pressPulse: "wave", // "wave" (default) · "flash" · "sonar" · "none"
188
+ pressHold: "echo", // while HELD: "echo" (default) · "collar" · "none"
189
+ pressPulseMs: 500, // lifetime of one pulse
190
+ pressPulseScale: 1.4, // multiplier on its radius
191
+ pressTravel: 0.07, // how deep the key itself sinks, ÷ shell width
192
+ });
193
+ ```
194
+
195
+ - `"wave"` — a ring leaves the key's edge, swells to about twice its size and fades. The phone-tap
196
+ reading; legible even on the smallest key.
197
+ - `"flash"` — a filled disc, wider than the key, closing back onto it as it fades. Quieter, and it
198
+ never spills onto a neighbour — which matters in the four-button diamond.
199
+ - `"sonar"` — two staggered rings, repeating slowly while the key is HELD. The most visible at a
200
+ distance and the only one that says "still down"; also the noisiest when a combo fires several keys
201
+ at once.
202
+
203
+ **While a key is HELD**, `pressHold` says what it keeps saying — a separate axis, because "a key went
204
+ down" and "a key is still down" are two different things, and any style can want either answer.
205
+ `"echo"` repeats the pulse every 700 ms at under half strength: no new vocabulary, the held key just
206
+ keeps saying the same word under its breath. `"collar"` parks one ring just outside the key's edge and
207
+ breathes it until release — a continuous state marked by a continuous sign, and the calmest option when
208
+ several keys are held at once. `"none"` says nothing. Note the cost: anything but `"none"` animates for
209
+ as long as the key is held, so the pad cannot go fully idle during a hold — a frame of work while the
210
+ rider is holding a button, not while they are doing nothing.
211
+
212
+ It is deliberately *not* the same white as `hint`, which already means **do this** as a slow endless
213
+ throb: the pulse fires once, is brief, and grows. Nothing in the hint vocabulary grows. Compare all
214
+ three live with `npm run pad3d` (panel on the right).
174
215
 
175
216
  **Joy-Con only.** Other brands warn and draw the Joy-Con anyway. They keep using the flat pad, which
176
217
  is not going away — the two renderers coexist.
@@ -186,6 +227,74 @@ replaces — you do not need to give it a height.
186
227
  control's box in viewport pixels on either renderer (projected from the camera in 3D). That's what the
187
228
  map's leader lines and the spotlight's captions use.
188
229
 
230
+ **Naming the angle.** `view` takes a named orientation instead of a pitch in radians:
231
+
232
+ ```ts
233
+ createThreePadRenderer(layout, { view: "triggers", triggerTilt: 0 });
234
+ ```
235
+
236
+ - `"front"` — head-on. The face keys, the d-pad and the sticks at their true relative sizes; the
237
+ triggers are edge-on and effectively invisible, so this is the wrong view for teaching one.
238
+ - `"angled"` — the three-quarter reading view, and the **default** everywhere. What every existing
239
+ caller already got; nothing changes if you don't pass `view`.
240
+ - `"triggers"` — rolled well forward, looking down onto the crown. `ZL`/`ZR`/`L`/`R` are the subject.
241
+ Pair it with `triggerTilt: 0`: the automatic roll a lit trigger provokes exists to *reach* this view
242
+ from `"angled"`, and applying it again from here tips the pad past flat.
243
+
244
+ An explicit `tilt`/`yaw` still wins over the preset, so a view is a starting point, not a cage. The
245
+ angles themselves live in `PAD_VIEWS`.
246
+
247
+ **In a game's HUD.** Pinning the pad into a corner of play is the `ui` side of this — `place`, `width`
248
+ and `highlight` on `mountControllerPad`, with this renderer passed as `renderer`. See the
249
+ `@rydr/game-sdk/ui` README, and `npm run pad-hud` for a dev page over a fake game screen.
250
+
251
+ ## `createJoyconObject` — the pad in YOUR scene
252
+
253
+ `createThreePadRenderer` frames the pad in its own canvas, which is right for a controls screen or a
254
+ HUD corner and wrong for a pad that belongs to the **world**: sitting on a table, mounted on a wall,
255
+ floating beside the rider, turning with the camera and occluded by the scenery. That is this:
256
+
257
+ ```ts
258
+ import { createJoyconObject } from "@rydr/game-sdk/three";
259
+
260
+ const pad = createJoyconObject({ design: "noir", view: "triggers", triggerTilt: 0 });
261
+ pad.addLights(scene); // lighting is part of the art direction — take the design's own
262
+ pad.group.position.set(0, 2.6, 0);
263
+ pad.group.scale.setScalar(0.55);
264
+ scene.add(pad.group);
265
+
266
+ pad.driver.setPartState("rt", "hint"); // "press this"
267
+
268
+ function frame(now) { // YOUR loop
269
+ pad.step(now);
270
+ renderer.render(scene, camera);
271
+ }
272
+ ```
273
+
274
+ **You own the loop.** The object has no `requestAnimationFrame` of its own, because your game already
275
+ has one and a second would render behind it. Call `step(now)` once a frame with the timestamp you
276
+ already have; it returns `true` while anything is still animating. Skip the call and the pad still
277
+ *paints* — colour is immediate — but nothing *moves*: keys teleport and pulses never fade.
278
+
279
+ **You own the yaw.** `group` is yours to place, scale and rotate about Y. The reading tilt and the
280
+ trigger roll live on an inner pivot (`tiltGroup`), so they never fight you for `rotation.x`.
281
+
282
+ **Lighting comes with it.** `addLights(scene)` installs the art direction's own rig. Skip it only if
283
+ your scene's lighting is deliberately taking over — the materials are standard PBR, so with no lights
284
+ at all the pad renders black.
285
+
286
+ **It is the same pad.** `createThreePadRenderer` is now a thin shell around this object — canvas,
287
+ camera, framing, projection — so both paths animate through one implementation. A press cannot look
288
+ one way in a HUD and another way in the world.
289
+
290
+ `driver` is the same `PadDriver` the flat pad and every controller view speak, so anything that can
291
+ drive one can drive this: `setPartState(part, "off" | "hint" | "on" | "done")`, `setStick`,
292
+ `setAvailable`, `clearAll`. `nodeOf(part)` hands back a key's `Object3D` for hanging your own things
293
+ off it. `dispose()` releases geometry, materials and textures.
294
+
295
+ See it with `npm run pad-hud` — one page showing the HUD and the in-world object at once, driven by
296
+ the same keyboard.
297
+
189
298
  ## `createThreeRiderRig` — the rider on their trainer, in 3D
190
299
 
191
300
  A second renderer for `mountRiderRig` from `@rydr/game-sdk/ui`, the same way `createThreePadRenderer`
@@ -222,8 +331,27 @@ rather than merely darker.
222
331
 
223
332
  **What it costs:** a WebGL context. The renderer draws **on demand — one frame per `setPose`, with no
224
333
  animation loop of its own** — because a game calling `rig.tick(dt)` already has a loop. But it is
225
- still a second context beside your own scene, so prefer the flat renderer in-game and reach for this
226
- one where the picture *is* the content: onboarding, a store page, a "how this works" panel.
334
+ still a second context beside your own scene, so keep it to **one** rig per screen, and reach for the
335
+ flat renderer when the rig is decoration rather than the subject.
336
+
337
+ **As a game HUD piece.** The rig can be pinned into a corner of play at any size — `width` and `place`
338
+ on `mountRiderRig`, see `@rydr/game-sdk/ui`. Turn the ground off when you do: it is painted in the
339
+ design's own background colour, which is invisible on a page that uses that colour and a dark slab
340
+ across the play field over a game.
341
+
342
+ ```ts
343
+ import { threeRiderRig } from "@rydr/game-sdk/three";
344
+
345
+ mountRiderRig(gameRoot, {
346
+ ftp: session.identity.ftp,
347
+ width: 300,
348
+ gauge: "portrait", // narrow and tall, for a screen edge
349
+ place: { corner: "bottom-right", margin: 24 },
350
+ renderer: threeRiderRig({ floor: false }),
351
+ });
352
+ ```
353
+
354
+ `npm run rig-hud` is that, live, with a slider per knob and the matching snippet printed back.
227
355
 
228
356
  **The rider is a real procedural character**, from `@rydr/game-sdk/three`'s `character/` — skeleton,
229
357
  proportions and five art directions, lifted from the platform's `showcase/player3d` (see the
@@ -6,7 +6,9 @@
6
6
  */
7
7
  export * from "./glyph-texture.js";
8
8
  export * from "./three-pad-renderer.js";
9
+ export * from "./press-pulse.js";
9
10
  export * from "./joycon-spec.js";
10
11
  export * from "./joycon-shoulder-mesh.js";
11
12
  export * from "./joycon-designs.js";
13
+ export * from "./joycon-object.js";
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/three/controller/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/three/controller/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -6,7 +6,9 @@
6
6
  */
7
7
  export * from "./glyph-texture.js";
8
8
  export * from "./three-pad-renderer.js";
9
+ export * from "./press-pulse.js";
9
10
  export * from "./joycon-spec.js";
10
11
  export * from "./joycon-shoulder-mesh.js";
11
12
  export * from "./joycon-designs.js";
13
+ export * from "./joycon-object.js";
12
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/three/controller/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/three/controller/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -11,6 +11,8 @@
11
11
  * be read from across a room, which is the actual use case.
12
12
  * {@link buildHoloJoycon} "Holo" — translucent shell with lit edges and emissive controls, in
13
13
  * the SDK's own accent language. Reads as UI rather than as an object.
14
+ * {@link buildNoirJoycon} "Noir" — "Toy" repainted: both shells black, white keys with dark
15
+ * lettering, black shoulders with light lettering.
14
16
  *
15
17
  * Each returns a `JoyconBuild`: the group to add to a scene, plus the per-control nodes a driver would
16
18
  * later need, plus everything to dispose. None of them is wired to input yet — that comes once one is
@@ -30,7 +32,7 @@ export interface JoyconBuild {
30
32
  dispose(): void;
31
33
  }[];
32
34
  }
33
- export type JoyconDesignId = "product" | "toy" | "holo";
35
+ export type JoyconDesignId = "product" | "toy" | "holo" | "noir";
34
36
  export interface JoyconDesign {
35
37
  id: JoyconDesignId;
36
38
  name: string;
@@ -44,6 +46,18 @@ export interface JoyconDesign {
44
46
  }
45
47
  export declare function buildProductJoycon(): JoyconBuild;
46
48
  export declare function buildToyJoycon(): JoyconBuild;
49
+ /**
50
+ * "Noir" — {@link buildToyJoycon}'s proportions, in black and white.
51
+ *
52
+ * Same chunky volumes, same oversized high-relief controls, same flat matte surfaces: everything that
53
+ * makes the toy direction readable at two metres is kept, and ONLY the palette changes. Both shells go
54
+ * black, the keys go white, and the shoulder row goes black again.
55
+ *
56
+ * Which means the pad carries lettering on two opposite backgrounds at once — dark letters on the white
57
+ * face keys, light letters on the black shoulders — and that is the whole reason {@link Skin.shoulderInk}
58
+ * exists. With a single ink one of the two sets vanishes into what it is printed on.
59
+ */
60
+ export declare function buildNoirJoycon(): JoyconBuild;
47
61
  export declare function buildHoloJoycon(): JoyconBuild;
48
62
  export declare const JOYCON_DESIGNS: readonly JoyconDesign[];
49
63
  //# sourceMappingURL=joycon-designs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"joycon-designs.d.ts","sourceRoot":"","sources":["../../../src/three/controller/joycon-designs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmB/B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IACnB,gGAAgG;IAChG,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtC,4CAA4C;IAC5C,MAAM,EAAE;QAAE,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,cAAc,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,WAAW,CAAC;IACrB,4EAA4E;IAC5E,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAChC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwmBD,wBAAgB,kBAAkB,IAAI,WAAW,CAgBhD;AAID,wBAAgB,cAAc,IAAI,WAAW,CA0B5C;AAMD,wBAAgB,eAAe,IAAI,WAAW,CA4C7C;AAID,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAgEjD,CAAC"}
1
+ {"version":3,"file":"joycon-designs.d.ts","sourceRoot":"","sources":["../../../src/three/controller/joycon-designs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmB/B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IACnB,gGAAgG;IAChG,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACtC,4CAA4C;IAC5C,MAAM,EAAE;QAAE,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,cAAc,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,WAAW,CAAC;IACrB,4EAA4E;IAC5E,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAChC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAy2BD,wBAAgB,kBAAkB,IAAI,WAAW,CAgBhD;AAID,wBAAgB,cAAc,IAAI,WAAW,CA0B5C;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,WAAW,CA8B7C;AAMD,wBAAgB,eAAe,IAAI,WAAW,CA4C7C;AAID,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAyFjD,CAAC"}