@particle-academy/fancy-3d-babylon 0.1.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.
@@ -0,0 +1,323 @@
1
+ import { Scene, Mesh, Vector3, DynamicTexture } from '@babylonjs/core';
2
+ import { WidgetSpec, SceneNode, WidgetAdapter, Scene as Scene$1 } from '@particle-academy/fancy-3d';
3
+
4
+ declare const TEX_SCALE = 2;
5
+ declare function paintWidget(ctx: CanvasRenderingContext2D, spec: WidgetSpec, w: number, h: number, selected: boolean): void;
6
+
7
+ /**
8
+ * 3D shape primitives for `@particle-academy/fancy-3d`.
9
+ *
10
+ * A primitive is a Babylon mesh that can host UI content on its surface —
11
+ * either a flat color, a freeform 2D paint callback, or a `WidgetSpec`
12
+ * rendered through the same painters as the Babylon adapter.
13
+ *
14
+ * Every primitive returns a `Mesh`, ready to position in your scene.
15
+ */
16
+
17
+ type PaintFn = (ctx: CanvasRenderingContext2D, width: number, height: number) => void;
18
+ type SurfaceContent = {
19
+ type: "color";
20
+ color: string;
21
+ } | {
22
+ type: "paint";
23
+ paint: PaintFn;
24
+ pixelWidth?: number;
25
+ pixelHeight?: number;
26
+ transparent?: boolean;
27
+ } | {
28
+ type: "widget";
29
+ widget: WidgetSpec;
30
+ pixelWidth?: number;
31
+ pixelHeight?: number;
32
+ selected?: boolean;
33
+ };
34
+ interface PanelOpts {
35
+ scene: Scene;
36
+ name?: string;
37
+ width: number;
38
+ height: number;
39
+ surface: SurfaceContent;
40
+ /** Babylon sideOrientation. Use `Mesh.DOUBLESIDE` for panels you want visible
41
+ * from both directions (e.g. self-luminous screens). Default is FRONTSIDE. */
42
+ sideOrientation?: number;
43
+ }
44
+ declare function createPanel(opts: PanelOpts): Mesh;
45
+ interface BillboardOpts extends PanelOpts {
46
+ /** When true, mesh.billboardMode = ALL — always faces the camera. */
47
+ faceCamera?: boolean;
48
+ }
49
+ declare function createBillboard(opts: BillboardOpts): Mesh;
50
+ interface BuildingOpts {
51
+ scene: Scene;
52
+ name?: string;
53
+ width: number;
54
+ height: number;
55
+ depth: number;
56
+ /** Surface applied to all 6 faces (cheap path). */
57
+ surface?: SurfaceContent;
58
+ /** Per-face surfaces; missing faces use `surface` or a default grey. */
59
+ faces?: Partial<Record<BoxFace, SurfaceContent>>;
60
+ }
61
+ /** Babylon box face indices (CreateBox): 0=front (-Z), 1=back (+Z), 2=right (+X), 3=left (-X), 4=top (+Y), 5=bottom (-Y). */
62
+ type BoxFace = "front" | "back" | "right" | "left" | "top" | "bottom";
63
+ declare function createBuilding(opts: BuildingOpts): Mesh;
64
+ interface PillarOpts {
65
+ scene: Scene;
66
+ name?: string;
67
+ thickness: number;
68
+ height: number;
69
+ surface?: SurfaceContent;
70
+ }
71
+ declare function createPillar(opts: PillarOpts): Mesh;
72
+ interface CylinderOpts {
73
+ scene: Scene;
74
+ name?: string;
75
+ radius: number;
76
+ height: number;
77
+ surface: SurfaceContent;
78
+ tessellation?: number;
79
+ }
80
+ declare function createCylinder(opts: CylinderOpts): Mesh;
81
+ interface CurvedPanelOpts {
82
+ scene: Scene;
83
+ name?: string;
84
+ width: number;
85
+ height: number;
86
+ arc: number;
87
+ surface: SurfaceContent;
88
+ tessellation?: number;
89
+ }
90
+ declare function createCurvedPanel(opts: CurvedPanelOpts): Mesh;
91
+ interface SphereOpts {
92
+ scene: Scene;
93
+ name?: string;
94
+ diameter: number;
95
+ surface: SurfaceContent;
96
+ segments?: number;
97
+ }
98
+ declare function createSphere(opts: SphereOpts): Mesh;
99
+ interface DiscOpts {
100
+ scene: Scene;
101
+ name?: string;
102
+ radius: number;
103
+ surface: SurfaceContent;
104
+ tessellation?: number;
105
+ }
106
+ declare function createDisc(opts: DiscOpts): Mesh;
107
+ interface Card3DOpts {
108
+ scene: Scene;
109
+ name?: string;
110
+ width: number;
111
+ height: number;
112
+ /** Extrusion depth. Defaults to a thin 0.06 — like real foam-board signage. */
113
+ depth?: number;
114
+ /** Surface for the front face (the visible UI). */
115
+ front: SurfaceContent;
116
+ /** Surface for the side and back faces. Defaults to a dark color. */
117
+ edge?: SurfaceContent;
118
+ }
119
+ declare function createCard3D(opts: Card3DOpts): Mesh;
120
+ interface SignOpts {
121
+ scene: Scene;
122
+ name?: string;
123
+ /** Panel dimensions. */
124
+ width: number;
125
+ height: number;
126
+ /** How tall the post is (from ground to bottom of panel). */
127
+ postHeight?: number;
128
+ /** Post thickness (square cross-section). */
129
+ postThickness?: number;
130
+ /** Panel surface (color, paint, or widget). */
131
+ surface: SurfaceContent;
132
+ /** Post color. */
133
+ postColor?: string;
134
+ }
135
+ interface SignResult {
136
+ /** Group containing the panel + post, parented at ground level. */
137
+ root: Mesh;
138
+ panel: Mesh;
139
+ post: Mesh;
140
+ }
141
+ declare function createSign(opts: SignOpts): SignResult;
142
+ interface MonitorOpts {
143
+ scene: Scene;
144
+ name?: string;
145
+ width: number;
146
+ height: number;
147
+ /** Front-to-back depth of the display chassis. Defaults to 0.18. */
148
+ depth?: number;
149
+ /** Bezel color. Defaults to near-black. */
150
+ bezel?: string;
151
+ /** Screen surface — color, paint, or widget. */
152
+ screen: SurfaceContent;
153
+ /** Optional kickstand height — when set, the monitor sits on a base. */
154
+ standHeight?: number;
155
+ }
156
+ interface MonitorResult {
157
+ root: Mesh;
158
+ body: Mesh;
159
+ screen: Mesh;
160
+ stand?: Mesh;
161
+ }
162
+ declare function createMonitor(opts: MonitorOpts): MonitorResult;
163
+ interface DecalOpts {
164
+ scene: Scene;
165
+ name?: string;
166
+ /** The mesh to project the decal onto. */
167
+ target: Mesh;
168
+ /** World-space position the projector sits at. */
169
+ position: Vector3;
170
+ /** Direction the projector points. Defaults to -Z. */
171
+ normal?: Vector3;
172
+ /** Size of the decal box. */
173
+ size: {
174
+ width: number;
175
+ height: number;
176
+ depth?: number;
177
+ };
178
+ /** Rotation around the projection axis (radians). */
179
+ angle?: number;
180
+ /** Surface to project. */
181
+ surface: SurfaceContent;
182
+ }
183
+ /**
184
+ * Projects a SurfaceContent onto `target`'s geometry. The decal conforms to
185
+ * the underlying surface — useful for signage on curved walls, logos on
186
+ * irregular terrain, graffiti on cylindrical surfaces, etc.
187
+ */
188
+ declare function createDecal(opts: DecalOpts): Mesh;
189
+ declare function createWidgetTexture(scene: Scene, name: string, widget: WidgetSpec, pixelWidth: number, pixelHeight: number, selected?: boolean): DynamicTexture;
190
+
191
+ /**
192
+ * Layout helpers — map 2D scene-node positions onto 3D arrangements.
193
+ *
194
+ * All layout functions take the same shape: a SceneNode, the Mesh that was
195
+ * built for it, the scene's bounds, and layout-specific options. They mutate
196
+ * `mesh.position` (and sometimes `mesh.rotation`) in place.
197
+ *
198
+ * Pair with `sceneBounds(scene)` from `./babylon` to compute the bounds.
199
+ */
200
+
201
+ interface Bounds {
202
+ minX: number;
203
+ maxX: number;
204
+ minY: number;
205
+ maxY: number;
206
+ }
207
+ interface GridLayoutOpts {
208
+ /** Pixel-to-world unit ratio. Defaults to 1/120. */
209
+ scale?: number;
210
+ /** World-space center of the grid. Defaults to (0, 0, 0). */
211
+ origin?: Vector3;
212
+ }
213
+ /**
214
+ * Direct 2D-to-3D mapping. DOM (x, y) becomes world (x, -y, 0) — the most
215
+ * literal layout. Useful when you want the scene to look identical to its
216
+ * DOM rendering, just rendered as floating panels.
217
+ */
218
+ declare function placeOnGrid(node: SceneNode, mesh: Mesh, bounds: Bounds, opts?: GridLayoutOpts): void;
219
+ interface WallLayoutOpts {
220
+ /** Distance from origin along the wall's normal. Defaults to 0. */
221
+ z?: number;
222
+ /** Spread along world X. Defaults to 10 world units. */
223
+ spreadX?: number;
224
+ /** Spread along world Y. Defaults to 4 world units. */
225
+ spreadY?: number;
226
+ /** Wall vertical center. Defaults to 1.5 world units. */
227
+ centerY?: number;
228
+ }
229
+ /**
230
+ * Hangs panels on a flat virtual wall, centered on the world origin and
231
+ * facing +Z.
232
+ */
233
+ declare function placeOnWall(node: SceneNode, mesh: Mesh, bounds: Bounds, opts?: WallLayoutOpts): void;
234
+ interface ArcLayoutOpts {
235
+ /** Cylinder radius. Defaults to 6. */
236
+ radius?: number;
237
+ /** Total arc spanned in radians. Defaults to π * 0.9 (~160°). */
238
+ arc?: number;
239
+ /** Vertical extent. Defaults to 6. */
240
+ height?: number;
241
+ /** Base Y of the arc. Defaults to 0. */
242
+ baseY?: number;
243
+ }
244
+ /**
245
+ * Wraps DOM x onto an angle around a vertical axis; DOM y becomes height.
246
+ * Same math as `placeOnCylinder` from `./babylon`, exposed here with a
247
+ * clearer name and consistent options shape.
248
+ */
249
+ declare function placeOnArc(node: SceneNode, mesh: Mesh, bounds: Bounds, opts?: ArcLayoutOpts): void;
250
+ interface PathLayoutOpts {
251
+ /** Waypoints along the path. */
252
+ waypoints: Vector3[];
253
+ /** Should each mesh face the next waypoint? Defaults to true. */
254
+ faceForward?: boolean;
255
+ /** Distribute by node index (0..n-1) instead of normalized x. Defaults to false. */
256
+ byIndex?: boolean;
257
+ /** Total node count when using `byIndex`. Defaults to inferred from bounds. */
258
+ nodeCount?: number;
259
+ }
260
+ /**
261
+ * Distributes nodes along a polyline. Each mesh sits on the path; if
262
+ * `faceForward` is true, each mesh's +Y rotation aligns with the path
263
+ * tangent.
264
+ */
265
+ declare function placeOnPath(node: SceneNode, mesh: Mesh, bounds: Bounds, opts: PathLayoutOpts): void;
266
+ interface SphereLayoutOpts {
267
+ /** Sphere radius. Defaults to 5. */
268
+ radius?: number;
269
+ /** World-space center. Defaults to (0, 0, 0). */
270
+ center?: Vector3;
271
+ /** Latitude span in radians (0..π). Defaults to π * 0.7 (avoids poles). */
272
+ latSpan?: number;
273
+ /** Longitude span in radians (0..2π). Defaults to 2π. */
274
+ lonSpan?: number;
275
+ }
276
+ /**
277
+ * DOM x → longitude, DOM y → latitude. Useful for "globe of widgets" or
278
+ * any kind of spherical immersive layout.
279
+ */
280
+ declare function placeOnSphere(node: SceneNode, mesh: Mesh, bounds: Bounds, opts?: SphereLayoutOpts): void;
281
+
282
+ /**
283
+ * BabylonJS adapter for `@particle-academy/fancy-3d`.
284
+ *
285
+ * Each widget kind paints onto a 2D HTMLCanvasElement that is uploaded to a
286
+ * Babylon `DynamicTexture`, which is then applied to a plane mesh sized to
287
+ * the widget's pixel rect. Edges become line meshes between node centers.
288
+ *
289
+ * `@babylonjs/core` is an OPTIONAL peer dependency — only consumers building
290
+ * 3D scenes need to install it.
291
+ */
292
+
293
+ interface BabylonAdapterDeps {
294
+ scene3D: Scene;
295
+ /** Pixel size for the widget (the scene node's `size` field). */
296
+ sizeFor(spec: WidgetSpec): {
297
+ w: number;
298
+ h: number;
299
+ };
300
+ }
301
+ declare function createBabylonAdapter(deps: BabylonAdapterDeps): WidgetAdapter<Mesh>;
302
+ /**
303
+ * "3D Desktop" layout — wraps the scene's 2D positions onto a partial
304
+ * cylinder facing the camera. DOM `x` becomes angle around the cylinder,
305
+ * DOM `y` becomes height.
306
+ */
307
+ declare function placeOnCylinder(node: SceneNode, mesh: Mesh, bounds: {
308
+ minX: number;
309
+ maxX: number;
310
+ minY: number;
311
+ maxY: number;
312
+ }, opts?: {
313
+ radius?: number;
314
+ arc?: number;
315
+ }): void;
316
+ declare function sceneBounds(scene: Scene$1): {
317
+ minX: number;
318
+ maxX: number;
319
+ minY: number;
320
+ maxY: number;
321
+ };
322
+
323
+ export { type ArcLayoutOpts, type BabylonAdapterDeps, type BillboardOpts, type BoxFace, type BuildingOpts, type Card3DOpts, type CurvedPanelOpts, type CylinderOpts, type DecalOpts, type DiscOpts, type GridLayoutOpts, type MonitorOpts, type MonitorResult, type PaintFn, type PanelOpts, type PathLayoutOpts, type PillarOpts, type SignOpts, type SignResult, type SphereLayoutOpts, type SphereOpts, type SurfaceContent, TEX_SCALE, type WallLayoutOpts, createBabylonAdapter, createBillboard, createBuilding, createCard3D, createCurvedPanel, createCylinder, createDecal, createDisc, createMonitor, createPanel, createPillar, createSign, createSphere, createWidgetTexture, paintWidget, placeOnArc, placeOnCylinder, placeOnGrid, placeOnPath, placeOnSphere, placeOnWall, sceneBounds };