@layoutit/polycss-react 0.2.6 → 0.2.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 +54 -7
- package/dist/PolyMesh-7fLCYk5L.d.cts +343 -0
- package/dist/PolyMesh-7fLCYk5L.d.ts +343 -0
- package/dist/index.cjs +102 -43
- package/dist/index.d.cts +36 -337
- package/dist/index.d.ts +36 -337
- package/dist/index.js +108 -47
- package/dist/three.cjs +4020 -0
- package/dist/three.d.cts +65 -0
- package/dist/three.d.ts +65 -0
- package/dist/three.js +4080 -0
- package/package.json +14 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { CSSProperties, MouseEventHandler, PointerEventHandler, FocusEventHandler, KeyboardEventHandler, ReactNode } from 'react';
|
|
3
|
+
import * as _layoutit_polycss_core from '@layoutit/polycss-core';
|
|
4
|
+
import { Vec3, PolyTextureImageSource, PolyTexturePresentation, Vec2, PolyMaterial, PolyDirectionalLight, PolyTextureLightingMode, TextureQuality, PolyTextureLeafSizing, PolyTextureImageRendering, PolyTextureBackend, PolyTextureProjection, LoadMeshOptions, Polygon, ParseResult, PolySeamBleed, MeshResolution } from '@layoutit/polycss-core';
|
|
5
|
+
|
|
6
|
+
/** Three.js-style transform props accepted by every PolyCSS component. */
|
|
7
|
+
interface TransformProps {
|
|
8
|
+
position?: Vec3;
|
|
9
|
+
scale?: number | Vec3;
|
|
10
|
+
rotation?: Vec3;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* DOM event handlers, ARIA, and style props forwarded to the rendered
|
|
14
|
+
* element by every Poly component.
|
|
15
|
+
*
|
|
16
|
+
* This is the DOM-native pitch: polygons are real DOM nodes you can
|
|
17
|
+
* target with CSS, attach event handlers to, and inspect in DevTools.
|
|
18
|
+
*/
|
|
19
|
+
interface DOMPassthroughProps {
|
|
20
|
+
className?: string;
|
|
21
|
+
style?: CSSProperties;
|
|
22
|
+
id?: string;
|
|
23
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
24
|
+
onDoubleClick?: MouseEventHandler<HTMLElement>;
|
|
25
|
+
onMouseEnter?: MouseEventHandler<HTMLElement>;
|
|
26
|
+
onMouseLeave?: MouseEventHandler<HTMLElement>;
|
|
27
|
+
onMouseMove?: MouseEventHandler<HTMLElement>;
|
|
28
|
+
onPointerDown?: PointerEventHandler<HTMLElement>;
|
|
29
|
+
onPointerUp?: PointerEventHandler<HTMLElement>;
|
|
30
|
+
onPointerEnter?: PointerEventHandler<HTMLElement>;
|
|
31
|
+
onPointerLeave?: PointerEventHandler<HTMLElement>;
|
|
32
|
+
onFocus?: FocusEventHandler<HTMLElement>;
|
|
33
|
+
onBlur?: FocusEventHandler<HTMLElement>;
|
|
34
|
+
onKeyDown?: KeyboardEventHandler<HTMLElement>;
|
|
35
|
+
tabIndex?: number;
|
|
36
|
+
role?: string;
|
|
37
|
+
"aria-label"?: string;
|
|
38
|
+
"aria-hidden"?: boolean;
|
|
39
|
+
pointerEvents?: "auto" | "none";
|
|
40
|
+
[dataAttr: `data-${string}`]: string | number | boolean | undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Props for the `<Poly>` component — the atomic polygon primitive.
|
|
44
|
+
*
|
|
45
|
+
* Extends TransformProps + DOMPassthroughProps with the polygon's own fields.
|
|
46
|
+
* This is the canonical Poly component API.
|
|
47
|
+
*/
|
|
48
|
+
interface PolyProps extends TransformProps, DOMPassthroughProps {
|
|
49
|
+
vertices: Vec3[];
|
|
50
|
+
color?: string;
|
|
51
|
+
texture?: string;
|
|
52
|
+
textureImageSource?: PolyTextureImageSource;
|
|
53
|
+
texturePresentation?: PolyTexturePresentation;
|
|
54
|
+
uvs?: Vec2[];
|
|
55
|
+
data?: Record<string, string | number | boolean>;
|
|
56
|
+
doubleSided?: boolean;
|
|
57
|
+
/** Shared material. When set AND the polygon's UVs form an axis-aligned
|
|
58
|
+
* rectangle, renders via `background-image` directly — no per-polygon
|
|
59
|
+
* canvas rasterization. Falls back to the atlas path otherwise. */
|
|
60
|
+
material?: PolyMaterial;
|
|
61
|
+
context?: {
|
|
62
|
+
tileSize?: number;
|
|
63
|
+
layerElevation?: number;
|
|
64
|
+
directionalLight?: PolyDirectionalLight;
|
|
65
|
+
textureLighting?: PolyTextureLightingMode;
|
|
66
|
+
textureQuality?: TextureQuality;
|
|
67
|
+
textureLeafSizing?: PolyTextureLeafSizing;
|
|
68
|
+
textureImageRendering?: PolyTextureImageRendering;
|
|
69
|
+
textureBackend?: PolyTextureBackend;
|
|
70
|
+
textureProjection?: PolyTextureProjection;
|
|
71
|
+
debugShowBackfaces?: boolean;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
};
|
|
74
|
+
/** Textured polygon lighting mode. Defaults to scene context, then "baked". */
|
|
75
|
+
textureLighting?: PolyTextureLightingMode;
|
|
76
|
+
/** Atlas bitmap budget and CSS sprite size. `"auto"` (default) uses a
|
|
77
|
+
* device-appropriate memory budget and desktop/mobile sprite sizing. */
|
|
78
|
+
textureQuality?: TextureQuality;
|
|
79
|
+
/** Atlas leaf CSS primitive sizing. Defaults to scene context, then canonical. */
|
|
80
|
+
textureLeafSizing?: PolyTextureLeafSizing;
|
|
81
|
+
/** Default image filtering for atlas and direct image texture leaves. */
|
|
82
|
+
textureImageRendering?: PolyTextureImageRendering;
|
|
83
|
+
/** Default texture backend request. Defaults to scene context, then "auto". */
|
|
84
|
+
textureBackend?: PolyTextureBackend;
|
|
85
|
+
/** Default texture projection request. Defaults to scene context, then "affine". */
|
|
86
|
+
textureProjection?: PolyTextureProjection;
|
|
87
|
+
/** Pre-computed shaded base color from the parent (optional override). */
|
|
88
|
+
baseColor?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type UseMeshOptions = LoadMeshOptions;
|
|
92
|
+
interface UseMeshResult {
|
|
93
|
+
polygons: Polygon[];
|
|
94
|
+
voxelSource: ParseResult["voxelSource"];
|
|
95
|
+
loading: boolean;
|
|
96
|
+
error: Error | null;
|
|
97
|
+
warnings: string[];
|
|
98
|
+
/** Manually trigger cleanup (also called on unmount automatically). */
|
|
99
|
+
dispose: () => void;
|
|
100
|
+
}
|
|
101
|
+
declare function usePolyMesh(src: string, options?: UseMeshOptions): UseMeshResult;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Pointer event API for <PolyMesh>. Mirrors @react-three/fiber's mesh
|
|
105
|
+
* event surface (handler names + payload shape) so devs migrating from
|
|
106
|
+
* three.js use the same mental model. PolyCSS is DOM-native so we get
|
|
107
|
+
* native pointer events for free — no raycaster, no canvas event
|
|
108
|
+
* synthesis.
|
|
109
|
+
*
|
|
110
|
+
* Diverges from r3f in two intentional ways:
|
|
111
|
+
* 1. Bubbling matches DOM (pointer hits the front element only). r3f
|
|
112
|
+
* replays events to occluded objects behind the front hit; doing
|
|
113
|
+
* that here would require running our own raycaster, which defeats
|
|
114
|
+
* the point of being DOM-native.
|
|
115
|
+
* 2. `face`, `uv`, `uv1`, `instanceId` are omitted — they're three.js
|
|
116
|
+
* BufferGeometry / InstancedMesh concepts with no polycss analogue.
|
|
117
|
+
* The polycss-native equivalent of `face` is the hit polygon's
|
|
118
|
+
* index, exposed via `polygon` (set when the underlying DOM target
|
|
119
|
+
* is an `<i>` polygon element).
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Imperative handle exposed by `<PolyMesh ref>`. Read-only view of the
|
|
124
|
+
* mesh's element + current transform + polygons. Mutation flows through
|
|
125
|
+
* controlled props (parent owns transform state) — matches three.js
|
|
126
|
+
* editor's pattern of keeping `selected` external to the object.
|
|
127
|
+
*/
|
|
128
|
+
interface PolyMeshHandle {
|
|
129
|
+
/** The `.polycss-mesh` wrapper div (null until mounted). */
|
|
130
|
+
readonly element: HTMLDivElement | null;
|
|
131
|
+
/** Identifier passed via the `id` prop, if any. */
|
|
132
|
+
readonly id?: string;
|
|
133
|
+
/** Current `position` prop value. */
|
|
134
|
+
getPosition(): Vec3 | undefined;
|
|
135
|
+
/** Current `rotation` prop value (Euler degrees). */
|
|
136
|
+
getRotation(): Vec3 | undefined;
|
|
137
|
+
/** Current `scale` prop value. */
|
|
138
|
+
getScale(): number | Vec3 | undefined;
|
|
139
|
+
/** Polygons currently being rendered (post-autoCenter). */
|
|
140
|
+
getPolygons(): Polygon[];
|
|
141
|
+
/**
|
|
142
|
+
* Replace the mesh polygons imperatively. Animated solid-triangle meshes use
|
|
143
|
+
* a stable DOM update path that mutates mounted leaves directly; unsupported
|
|
144
|
+
* topology falls back to the normal framework render path.
|
|
145
|
+
*/
|
|
146
|
+
setPolygons(polygons: Polygon[]): void;
|
|
147
|
+
/**
|
|
148
|
+
* Snapshot the current `rotation` prop as the new "baked rotation" and
|
|
149
|
+
* trigger an atlas re-rasterization with the directional light
|
|
150
|
+
* inverse-rotated into the mesh's local frame.
|
|
151
|
+
*
|
|
152
|
+
* Call this after a rotate-mode drag ends (i.e. on pointer release) —
|
|
153
|
+
* **not** on every pointermove during the drag. The visual wrapper already
|
|
154
|
+
* follows the live `rotation` prop smoothly; the atlas only needs to
|
|
155
|
+
* update once per committed rotation so it doesn't re-bake every frame.
|
|
156
|
+
*
|
|
157
|
+
* Math rationale: baked atlas tiles encode `baseColor × Lambert(worldNormal,
|
|
158
|
+
* worldLight)`. When the mesh wrapper rotates via CSS the world-space normal
|
|
159
|
+
* changes but the baked color does not, causing stale shading. Calling
|
|
160
|
+
* `rebakeAtlas()` inverse-rotates the world light into the mesh-local frame
|
|
161
|
+
* before re-running the atlas baker, so `dot(localNormal, localLight) ===
|
|
162
|
+
* dot(worldNormal, worldLight)` and the shading is correct again.
|
|
163
|
+
*
|
|
164
|
+
* In dynamic (`textureLighting="dynamic"`) mode this call is a no-op for
|
|
165
|
+
* shading purposes (dynamic mode re-evaluates per frame), but it is still
|
|
166
|
+
* safe to call.
|
|
167
|
+
*/
|
|
168
|
+
rebakeAtlas(): void;
|
|
169
|
+
/** Resolves when this mesh's current texture generation has usable backgrounds. */
|
|
170
|
+
whenTexturesReady(): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Mutate a single polygon in place and re-render the mesh. `target` is
|
|
173
|
+
* either a polygon reference (as returned by `getPolygons()`) or its index.
|
|
174
|
+
* `partial` fields are merged onto the polygon via `Object.assign`. Skips
|
|
175
|
+
* the merge pass — cheaper than a full prop change for targeted edits like
|
|
176
|
+
* color picker updates from an inspector UI. Silently no-ops when `target`
|
|
177
|
+
* is not found or the index is out of range.
|
|
178
|
+
*/
|
|
179
|
+
updatePolygon(target: Polygon | number, partial: Partial<Polygon>): void;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Pointer event payload delivered to <PolyMesh> handlers. Mirrors r3f's
|
|
183
|
+
* shape, minus raycaster-specific fields. See module docstring for the
|
|
184
|
+
* intentional divergences.
|
|
185
|
+
*/
|
|
186
|
+
interface PolyPointerEvent<E extends Event = PointerEvent> {
|
|
187
|
+
/** The mesh originally under the pointer (deepest hit). */
|
|
188
|
+
object: PolyMeshHandle;
|
|
189
|
+
/** The mesh whose handler is being invoked. Equal to `object` until
|
|
190
|
+
* ancestor bubbling is added (out of scope for v1). */
|
|
191
|
+
eventObject: PolyMeshHandle;
|
|
192
|
+
/** All meshes stacked under the pointer this moment, front-to-back.
|
|
193
|
+
* Computed via `document.elementsFromPoint` then filtered to
|
|
194
|
+
* registered `.polycss-mesh` ancestors. */
|
|
195
|
+
intersections: Array<{
|
|
196
|
+
object: PolyMeshHandle;
|
|
197
|
+
}>;
|
|
198
|
+
/** Pointer position in normalized device coords [-1, 1] relative to
|
|
199
|
+
* the camera viewport. (0,0) = viewport center. Falls back to (0,0)
|
|
200
|
+
* when the mesh is rendered outside a `<PolyCamera>`. */
|
|
201
|
+
pointer: {
|
|
202
|
+
x: number;
|
|
203
|
+
y: number;
|
|
204
|
+
};
|
|
205
|
+
/** Pixel distance from the most recent `pointerdown` to this event.
|
|
206
|
+
* 0 on pointerdown itself. Use to discriminate click-vs-drag. */
|
|
207
|
+
delta: number;
|
|
208
|
+
/** The underlying DOM event. */
|
|
209
|
+
nativeEvent: E;
|
|
210
|
+
/** Stops native bubbling. (Equivalent to `nativeEvent.stopPropagation()`
|
|
211
|
+
* today; reserved for future r3f-style bubbling above the wrapper.) */
|
|
212
|
+
stopPropagation(): void;
|
|
213
|
+
}
|
|
214
|
+
type PolyMouseEvent = PolyPointerEvent<MouseEvent>;
|
|
215
|
+
type PolyWheelEvent = PolyPointerEvent<WheelEvent>;
|
|
216
|
+
type PolyEventHandler<E extends Event = PointerEvent> = (event: PolyPointerEvent<E>) => void;
|
|
217
|
+
/**
|
|
218
|
+
* Pointer / mouse / wheel handlers accepted by `<PolyMesh>`. Names mirror
|
|
219
|
+
* r3f exactly. Provide any handler to opt the mesh into receiving events;
|
|
220
|
+
* absent handlers add zero overhead.
|
|
221
|
+
*/
|
|
222
|
+
interface InteractionProps {
|
|
223
|
+
onClick?: PolyEventHandler<MouseEvent>;
|
|
224
|
+
onContextMenu?: PolyEventHandler<MouseEvent>;
|
|
225
|
+
onDoubleClick?: PolyEventHandler<MouseEvent>;
|
|
226
|
+
onWheel?: PolyEventHandler<WheelEvent>;
|
|
227
|
+
onPointerDown?: PolyEventHandler<PointerEvent>;
|
|
228
|
+
onPointerUp?: PolyEventHandler<PointerEvent>;
|
|
229
|
+
onPointerMove?: PolyEventHandler<PointerEvent>;
|
|
230
|
+
onPointerOver?: PolyEventHandler<PointerEvent>;
|
|
231
|
+
onPointerOut?: PolyEventHandler<PointerEvent>;
|
|
232
|
+
onPointerEnter?: PolyEventHandler<PointerEvent>;
|
|
233
|
+
onPointerLeave?: PolyEventHandler<PointerEvent>;
|
|
234
|
+
onPointerCancel?: PolyEventHandler<PointerEvent>;
|
|
235
|
+
}
|
|
236
|
+
/** Walk up from `el` looking for the nearest registered mesh wrapper. */
|
|
237
|
+
declare function findPolyMeshHandle(el: Element | null): PolyMeshHandle | null;
|
|
238
|
+
/** Test whether `(clientX, clientY)` falls inside any polygon leaf
|
|
239
|
+
* child of `meshEl`'s post-3D bounding rect. Skips zero-area rects
|
|
240
|
+
* (happy-dom and pre-layout SSR return those). */
|
|
241
|
+
declare function pointInMeshElement(meshEl: HTMLElement, clientX: number, clientY: number): boolean;
|
|
242
|
+
/** Walk every registered `.polycss-mesh` in the document and return
|
|
243
|
+
* the first whose polygon bounding-rects contain `(clientX, clientY)`.
|
|
244
|
+
* An optional `filter` skips matched mesh elements (e.g. gizmos). */
|
|
245
|
+
declare function findMeshUnderPoint(clientX: number, clientY: number, filter?: (meshEl: HTMLElement) => boolean): PolyMeshHandle | null;
|
|
246
|
+
|
|
247
|
+
interface PolyMeshProps extends TransformProps, InteractionProps {
|
|
248
|
+
/** Stable identifier — exposed on the mesh handle and reflected as
|
|
249
|
+
* `data-poly-mesh-id` on the wrapper div. Use for selection lookups. */
|
|
250
|
+
id?: string;
|
|
251
|
+
/** URL to .obj / .glb / .gltf. Mutually exclusive with `polygons`. */
|
|
252
|
+
src?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Companion `.mtl` URL for OBJ models. When set, materials defined in
|
|
255
|
+
* the mtl (Kd colors, map_Kd textures) are applied to the loaded mesh.
|
|
256
|
+
* Ignored for GLB/GLTF (they carry materials inline).
|
|
257
|
+
*/
|
|
258
|
+
mtl?: string;
|
|
259
|
+
/** Pre-parsed polygons. Mutually exclusive with `src`. */
|
|
260
|
+
polygons?: Polygon[];
|
|
261
|
+
/** Optional `parseResult.voxelSource` companion for `.vox` meshes. When
|
|
262
|
+
* set alongside `polygons`, the direct voxel renderer fast path activates
|
|
263
|
+
* — emitting one `<b>` per visible voxel quad inside `.polycss-voxel-face`
|
|
264
|
+
* wrappers (matches vanilla's `scene.add(parseResult)` behaviour).
|
|
265
|
+
* Callers fetching via core's `loadMesh()` pass `parseResult.voxelSource`
|
|
266
|
+
* here so the fast path engages; callers fetching via PolyMesh's `src`
|
|
267
|
+
* prop get the same data wired through `useMesh` automatically. */
|
|
268
|
+
voxelSource?: _layoutit_polycss_core.ParseResult["voxelSource"];
|
|
269
|
+
/** Translate so mesh's bbox center is at local origin before applying `position`. */
|
|
270
|
+
autoCenter?: boolean;
|
|
271
|
+
/** Textured polygon lighting mode. Defaults to "baked". */
|
|
272
|
+
textureLighting?: PolyTextureLightingMode;
|
|
273
|
+
/** Atlas bitmap budget and CSS sprite size. `"auto"` (default) uses a
|
|
274
|
+
* device-appropriate memory budget (~4 MB mobile / ~16 MB desktop) and
|
|
275
|
+
* desktop/mobile sprite sizing. Numeric values 0.1..1 force an explicit
|
|
276
|
+
* raster scale and the 64px sprite. */
|
|
277
|
+
textureQuality?: TextureQuality;
|
|
278
|
+
/** Atlas leaf CSS primitive sizing. Defaults to scene context, then canonical. */
|
|
279
|
+
textureLeafSizing?: PolyTextureLeafSizing;
|
|
280
|
+
/** Default image filtering for atlas and direct image texture leaves. */
|
|
281
|
+
textureImageRendering?: PolyTextureImageRendering;
|
|
282
|
+
/** Default texture backend request. Defaults to scene context, then "auto". */
|
|
283
|
+
textureBackend?: PolyTextureBackend;
|
|
284
|
+
/** Default texture projection request. Defaults to scene context, then "affine". */
|
|
285
|
+
textureProjection?: PolyTextureProjection;
|
|
286
|
+
/** Solid seam overscan. `"auto"` computes a fitted per-edge amount from the polygon plan. */
|
|
287
|
+
seamBleed?: PolySeamBleed;
|
|
288
|
+
/**
|
|
289
|
+
* Hold the whole previous frame (geometry + texture) until the next atlas is
|
|
290
|
+
* decoded, then swap atomically — so a geometry edit never shows geometry
|
|
291
|
+
* before its texture. Best when edits arrive as discrete commits (no
|
|
292
|
+
* continuous drag). Defaults to false (bitmap streams in over live geometry).
|
|
293
|
+
*/
|
|
294
|
+
atomicAtlas?: boolean;
|
|
295
|
+
/** Fires when the displayed atlas frame swaps to a ready one (atomic mode). */
|
|
296
|
+
onFrameReady?: () => void;
|
|
297
|
+
/** Per-polygon override render, or static children mounted inside the mesh wrapper. */
|
|
298
|
+
children?: ((polygon: Polygon, index: number) => ReactNode) | ReactNode;
|
|
299
|
+
/** Loading slot — rendered while `src` is being fetched/parsed. */
|
|
300
|
+
fallback?: ReactNode;
|
|
301
|
+
/** Error slot — rendered if parse fails. Receives the Error. */
|
|
302
|
+
errorFallback?: (error: Error) => ReactNode;
|
|
303
|
+
/** Parser options forwarded to parseObj/parseGltf. */
|
|
304
|
+
parseOptions?: UseMeshOptions;
|
|
305
|
+
/** Mesh optimization intent. Defaults to "lossy"; set "lossless" to keep
|
|
306
|
+
* authored surface fidelity. Top-level prop wins over `parseOptions.meshResolution`
|
|
307
|
+
* when both are present. */
|
|
308
|
+
meshResolution?: MeshResolution;
|
|
309
|
+
/**
|
|
310
|
+
* When `true`, emits a per-mesh SVG shadow path in both lighting modes.
|
|
311
|
+
* Each casting polygon projects onto the scene ground plane along the
|
|
312
|
+
* directional light; overlapping outlines are merged into one silhouette.
|
|
313
|
+
* Defaults to `false`.
|
|
314
|
+
*/
|
|
315
|
+
castShadow?: boolean;
|
|
316
|
+
/**
|
|
317
|
+
* When `true`, this mesh acts as a shadow receiver. The scene's caster
|
|
318
|
+
* meshes (those with `castShadow=true`) project per-coplanar-face SVG
|
|
319
|
+
* shadows onto each visible surface of this mesh, matching Three.js's
|
|
320
|
+
* `mesh.receiveShadow` semantics. Disables the ground-shadow fallback
|
|
321
|
+
* for caster meshes — receivers handle shadow display. Defaults to `false`.
|
|
322
|
+
*/
|
|
323
|
+
receiveShadow?: boolean;
|
|
324
|
+
/**
|
|
325
|
+
* Per-mesh parametric-shadow detail, overriding the scene's
|
|
326
|
+
* `shadow.definition` for this mesh's cast/self shadow. Only used when the
|
|
327
|
+
* scene's `shadow.parametric` is true. Unset → inherit the scene definition.
|
|
328
|
+
*/
|
|
329
|
+
shadowDefinition?: number;
|
|
330
|
+
/**
|
|
331
|
+
* Apply mesh optimization (coplanar merge + interior cull) before
|
|
332
|
+
* rendering. Defaults to `true` — matches vanilla `scene.add`'s default.
|
|
333
|
+
* Set `false` for helper meshes (axes, light markers) whose geometry
|
|
334
|
+
* shouldn't be merged, or when the imperative `updatePolygon` API needs
|
|
335
|
+
* polygon refs to survive across updates.
|
|
336
|
+
*/
|
|
337
|
+
merge?: boolean;
|
|
338
|
+
className?: string;
|
|
339
|
+
style?: CSSProperties;
|
|
340
|
+
}
|
|
341
|
+
declare const PolyMesh: React.ForwardRefExoticComponent<PolyMeshProps & React.RefAttributes<PolyMeshHandle>>;
|
|
342
|
+
|
|
343
|
+
export { type DOMPassthroughProps as D, type InteractionProps as I, type PolyProps as P, type TransformProps as T, type UseMeshOptions as U, type PolyMeshProps as a, type PolyMeshHandle as b, type PolyEventHandler as c, PolyMesh as d, type PolyMouseEvent as e, type PolyPointerEvent as f, type PolyWheelEvent as g, type UseMeshResult as h, findMeshUnderPoint as i, findPolyMeshHandle as j, pointInMeshElement as p, usePolyMesh as u };
|
package/dist/index.cjs
CHANGED
|
@@ -2748,6 +2748,7 @@ function PolySceneInner({
|
|
|
2748
2748
|
rotY: _rotY,
|
|
2749
2749
|
zoom: _zoom,
|
|
2750
2750
|
directionalLight,
|
|
2751
|
+
pointLights,
|
|
2751
2752
|
ambientLight,
|
|
2752
2753
|
textureLighting = "baked",
|
|
2753
2754
|
textureQuality,
|
|
@@ -2805,17 +2806,19 @@ function PolySceneInner({
|
|
|
2805
2806
|
const sceneBbox = centerInputPolygons ? centerSceneBbox : renderSceneBbox;
|
|
2806
2807
|
const directionalForAtlas = textureLighting === "dynamic" ? void 0 : directionalLight;
|
|
2807
2808
|
const ambientForAtlas = textureLighting === "dynamic" ? void 0 : ambientLight;
|
|
2809
|
+
const pointLightsForAtlas = textureLighting === "dynamic" ? void 0 : pointLights;
|
|
2808
2810
|
const polyContext = (0, import_react14.useMemo)(() => {
|
|
2809
2811
|
const tileSize = 50;
|
|
2810
2812
|
return {
|
|
2811
2813
|
tileSize,
|
|
2812
2814
|
layerElevation: tileSize,
|
|
2813
2815
|
directionalLight: directionalForAtlas,
|
|
2816
|
+
pointLights: pointLightsForAtlas,
|
|
2814
2817
|
ambientLight: ambientForAtlas,
|
|
2815
2818
|
textureLighting,
|
|
2816
2819
|
seamBleed
|
|
2817
2820
|
};
|
|
2818
|
-
}, [directionalForAtlas, ambientForAtlas, textureLighting, seamBleed]);
|
|
2821
|
+
}, [directionalForAtlas, pointLightsForAtlas, ambientForAtlas, textureLighting, seamBleed]);
|
|
2819
2822
|
const autoCenterOffset = (0, import_react14.useMemo)(() => {
|
|
2820
2823
|
if (!autoCenter) return [0, 0, 0];
|
|
2821
2824
|
return [
|
|
@@ -3001,6 +3004,7 @@ function PolySceneInner({
|
|
|
3001
3004
|
() => ({
|
|
3002
3005
|
textureLighting,
|
|
3003
3006
|
directionalLight,
|
|
3007
|
+
pointLights,
|
|
3004
3008
|
ambientLight,
|
|
3005
3009
|
strategies,
|
|
3006
3010
|
seamBleed,
|
|
@@ -3017,7 +3021,7 @@ function PolySceneInner({
|
|
|
3017
3021
|
groundCssZ,
|
|
3018
3022
|
sceneEl
|
|
3019
3023
|
}),
|
|
3020
|
-
[textureLighting, directionalLight, ambientLight, strategies, seamBleed, textureLeafSizing, textureImageRendering, textureBackend, textureProjection, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
|
|
3024
|
+
[textureLighting, directionalLight, pointLights, ambientLight, strategies, seamBleed, textureLeafSizing, textureImageRendering, textureBackend, textureProjection, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
|
|
3021
3025
|
);
|
|
3022
3026
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3023
3027
|
"div",
|
|
@@ -3907,6 +3911,7 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
3907
3911
|
onFrameReady,
|
|
3908
3912
|
castShadow,
|
|
3909
3913
|
receiveShadow,
|
|
3914
|
+
shadowDefinition,
|
|
3910
3915
|
merge = true,
|
|
3911
3916
|
children,
|
|
3912
3917
|
fallback,
|
|
@@ -4175,6 +4180,18 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4175
4180
|
direction: (0, import_polycss_core16.inverseRotateVec3)(cssLight.direction, rot)
|
|
4176
4181
|
};
|
|
4177
4182
|
}, [effectiveDirectional, bakedRotation]);
|
|
4183
|
+
const bakedPointLights = (0, import_react16.useMemo)(() => {
|
|
4184
|
+
const pls = sceneCtx?.pointLights;
|
|
4185
|
+
if (!pls || pls.length === 0) return void 0;
|
|
4186
|
+
const pos = position ?? [0, 0, 0];
|
|
4187
|
+
const rot = bakedRotation ?? [0, 0, 0];
|
|
4188
|
+
const hasRot = rot[0] !== 0 || rot[1] !== 0 || rot[2] !== 0;
|
|
4189
|
+
return pls.map((pl) => {
|
|
4190
|
+
const rel = [pl.position[0] - pos[0], pl.position[1] - pos[1], pl.position[2] - pos[2]];
|
|
4191
|
+
const local = hasRot ? (0, import_polycss_core16.inverseRotateVec3)(rel, rot) : rel;
|
|
4192
|
+
return { ...pl, position: local };
|
|
4193
|
+
});
|
|
4194
|
+
}, [sceneCtx?.pointLights, position, bakedRotation]);
|
|
4178
4195
|
const lightOccludedPolyIndices = void 0;
|
|
4179
4196
|
const atlasPlans = (0, import_react16.useMemo)(
|
|
4180
4197
|
() => {
|
|
@@ -4193,6 +4210,7 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4193
4210
|
i,
|
|
4194
4211
|
{
|
|
4195
4212
|
directionalLight: bakedDirectional,
|
|
4213
|
+
pointLights: bakedPointLights,
|
|
4196
4214
|
ambientLight: effectiveAmbient,
|
|
4197
4215
|
seamBleed: seamBleedEdges?.has(i) ? effectiveSeamBleed : void 0,
|
|
4198
4216
|
seamEdges: seamBleedEdges?.get(i),
|
|
@@ -4202,7 +4220,7 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4202
4220
|
basisHints[i]
|
|
4203
4221
|
));
|
|
4204
4222
|
},
|
|
4205
|
-
[renderPolygon, directVoxelEnabled, polygons, bakedDirectional, effectiveAmbient, effectiveSeamBleed, lightOccludedPolyIndices]
|
|
4223
|
+
[renderPolygon, directVoxelEnabled, polygons, bakedDirectional, bakedPointLights, effectiveAmbient, effectiveSeamBleed, lightOccludedPolyIndices]
|
|
4206
4224
|
);
|
|
4207
4225
|
const textureAtlas = useTextureAtlas(
|
|
4208
4226
|
atlasPlans,
|
|
@@ -4248,23 +4266,32 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4248
4266
|
}
|
|
4249
4267
|
return s;
|
|
4250
4268
|
}, [atlasPlans, polygons]);
|
|
4269
|
+
const shadowCasterRegisteredRef = (0, import_react16.useRef)(false);
|
|
4270
|
+
const lastShadowPolyCountRef = (0, import_react16.useRef)(-1);
|
|
4251
4271
|
(0, import_react16.useEffect)(() => {
|
|
4252
|
-
if (!sceneRegisterShadowCaster) return;
|
|
4253
|
-
if (castShadow) {
|
|
4254
|
-
sceneRegisterShadowCaster(meshIdRef.current, {
|
|
4255
|
-
polygons,
|
|
4256
|
-
position: position ?? [0, 0, 0],
|
|
4257
|
-
scale,
|
|
4258
|
-
rotation,
|
|
4259
|
-
renderedPolygonIndices
|
|
4260
|
-
});
|
|
4261
|
-
} else {
|
|
4262
|
-
sceneRegisterShadowCaster(meshIdRef.current, null);
|
|
4263
|
-
}
|
|
4272
|
+
if (!sceneRegisterShadowCaster || !castShadow) return;
|
|
4264
4273
|
return () => {
|
|
4265
4274
|
sceneRegisterShadowCaster(meshIdRef.current, null);
|
|
4275
|
+
shadowCasterRegisteredRef.current = false;
|
|
4276
|
+
lastShadowPolyCountRef.current = -1;
|
|
4266
4277
|
};
|
|
4267
|
-
}, [sceneRegisterShadowCaster, castShadow
|
|
4278
|
+
}, [sceneRegisterShadowCaster, castShadow]);
|
|
4279
|
+
(0, import_react16.useEffect)(() => {
|
|
4280
|
+
if (!sceneRegisterShadowCaster || !castShadow) return;
|
|
4281
|
+
const followAnimation = sceneCtx?.shadow?.followAnimation ?? false;
|
|
4282
|
+
const topologyChanged = polygons.length !== lastShadowPolyCountRef.current;
|
|
4283
|
+
if (shadowCasterRegisteredRef.current && !followAnimation && !topologyChanged) return;
|
|
4284
|
+
lastShadowPolyCountRef.current = polygons.length;
|
|
4285
|
+
shadowCasterRegisteredRef.current = true;
|
|
4286
|
+
sceneRegisterShadowCaster(meshIdRef.current, {
|
|
4287
|
+
polygons,
|
|
4288
|
+
position: position ?? [0, 0, 0],
|
|
4289
|
+
scale,
|
|
4290
|
+
rotation,
|
|
4291
|
+
renderedPolygonIndices,
|
|
4292
|
+
shadowDefinition
|
|
4293
|
+
});
|
|
4294
|
+
}, [sceneRegisterShadowCaster, castShadow, polygons, position, scale, rotation, renderedPolygonIndices, shadowDefinition, sceneCtx?.shadow]);
|
|
4268
4295
|
const sceneRegisterShadowReceiver = sceneCtx?.registerShadowReceiver;
|
|
4269
4296
|
(0, import_react16.useEffect)(() => {
|
|
4270
4297
|
if (!sceneRegisterShadowReceiver) return;
|
|
@@ -4390,6 +4417,16 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4390
4417
|
if (!shadowCasters || shadowCasters.size === 0) return null;
|
|
4391
4418
|
const userLightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
|
|
4392
4419
|
const lightDir = (0, import_polycss_core16.worldDirectionToCss)(userLightDir);
|
|
4420
|
+
const dynamicShading = effectiveTextureLighting === "dynamic";
|
|
4421
|
+
const scenePoints = dynamicShading ? [] : sceneCtx?.pointLights ?? [];
|
|
4422
|
+
const allPointLightsCss = scenePoints.map((pl) => ({
|
|
4423
|
+
position: (0, import_polycss_core16.worldPositionToCss)(pl.position),
|
|
4424
|
+
color: pl.color,
|
|
4425
|
+
intensity: pl.intensity
|
|
4426
|
+
}));
|
|
4427
|
+
const shadowPointIndices = scenePoints.map((pl, i) => pl.castShadow ? i : -1).filter((i) => i >= 0);
|
|
4428
|
+
const runDirectionalShadow = !!sceneDirectionalLight?.direction && (sceneDirectionalLight.intensity ?? 1) > 0;
|
|
4429
|
+
const hasShadowPoints = shadowPointIndices.length > 0;
|
|
4393
4430
|
const shadowLift = sceneShadow?.lift ?? 1e-3;
|
|
4394
4431
|
const planes = (0, import_polycss_core16.prepareReceiverFacePlanes)(
|
|
4395
4432
|
polygons,
|
|
@@ -4402,18 +4439,17 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4402
4439
|
if (planes.length === 0) return null;
|
|
4403
4440
|
const casterInputs = [];
|
|
4404
4441
|
for (const [casterId, data] of shadowCasters) {
|
|
4405
|
-
const rendered = data.renderedPolygonIndices;
|
|
4406
4442
|
const items = (0, import_polycss_core16.prepareCasterPolyItems)(
|
|
4407
4443
|
data.polygons,
|
|
4408
4444
|
data.position,
|
|
4409
4445
|
data.scale,
|
|
4410
|
-
|
|
4446
|
+
() => true,
|
|
4411
4447
|
data.rotation ?? null
|
|
4412
4448
|
);
|
|
4413
4449
|
const isSelf = data.polygons === polygons;
|
|
4414
4450
|
const selfMap = isSelf ? selfShadowEdgeMap : void 0;
|
|
4415
4451
|
let edgeOwners;
|
|
4416
|
-
if (!isSelf && data.polygons.length >= 40) {
|
|
4452
|
+
if (!isSelf && (data.polygons.length >= 40 || hasShadowPoints)) {
|
|
4417
4453
|
const dposArr = data.position;
|
|
4418
4454
|
const drot = data.rotation ?? null;
|
|
4419
4455
|
const dsKey = JSON.stringify(data.scale ?? null);
|
|
@@ -4426,12 +4462,29 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4426
4462
|
}
|
|
4427
4463
|
edgeOwners = cachedOwners;
|
|
4428
4464
|
}
|
|
4465
|
+
let overrideSilhouette;
|
|
4466
|
+
let overridePointSilhouettes;
|
|
4467
|
+
if (sceneShadow?.parametric) {
|
|
4468
|
+
const def = data.shadowDefinition ?? sceneShadow.definition ?? 16;
|
|
4469
|
+
const result = (0, import_polycss_core16.buildParametricCasterOverride)({
|
|
4470
|
+
polysWorldVerts: items.map((it) => it.wv),
|
|
4471
|
+
lightDir,
|
|
4472
|
+
definition: def,
|
|
4473
|
+
isSelf,
|
|
4474
|
+
style: sceneShadow.style,
|
|
4475
|
+
pointLights: shadowPointIndices.map((i) => ({ position: allPointLightsCss[i].position, index: i }))
|
|
4476
|
+
});
|
|
4477
|
+
overrideSilhouette = result.overrideSilhouette;
|
|
4478
|
+
overridePointSilhouettes = result.overridePointSilhouettes;
|
|
4479
|
+
}
|
|
4429
4480
|
casterInputs.push({
|
|
4430
4481
|
id: casterId,
|
|
4431
4482
|
items,
|
|
4432
4483
|
selfShadowEdgeMap: selfMap,
|
|
4433
4484
|
edgeOwners,
|
|
4434
|
-
casterPolygonCount: data.polygons.length
|
|
4485
|
+
casterPolygonCount: data.polygons.length,
|
|
4486
|
+
overrideSilhouette,
|
|
4487
|
+
overridePointSilhouettes
|
|
4435
4488
|
});
|
|
4436
4489
|
}
|
|
4437
4490
|
const cameraState = cameraCtx?.store.getState().cameraState;
|
|
@@ -4440,27 +4493,31 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4440
4493
|
rotY: cameraState?.rotY ?? 45,
|
|
4441
4494
|
meshRotation: rotation
|
|
4442
4495
|
};
|
|
4443
|
-
const
|
|
4496
|
+
const faces = (0, import_polycss_core16.computeMergedReceiverShadows)({
|
|
4444
4497
|
receiverPlanes: planes,
|
|
4445
4498
|
receiverPolygons: polygons,
|
|
4446
4499
|
receiverHasTexture: polygons.some((p) => p.texture !== void 0),
|
|
4447
4500
|
casters: casterInputs,
|
|
4448
4501
|
lightDir,
|
|
4502
|
+
runDirectional: runDirectionalShadow,
|
|
4503
|
+
pointPasses: shadowPointIndices.map((i) => ({ lightPos: allPointLightsCss[i].position, index: i })),
|
|
4504
|
+
allPointLights: allPointLightsCss,
|
|
4449
4505
|
cameraRot,
|
|
4450
4506
|
ambientLight: sceneCtx?.ambientLight,
|
|
4451
4507
|
directionalLight: sceneDirectionalLight,
|
|
4452
4508
|
shadow: { color: sceneShadow?.color, opacity: sceneShadow?.opacity ?? 0.25, maxExtend: sceneShadow?.maxExtend }
|
|
4453
4509
|
});
|
|
4454
|
-
|
|
4510
|
+
if (faces.length === 0) return null;
|
|
4511
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: faces.map((fc) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4455
4512
|
"svg",
|
|
4456
4513
|
{
|
|
4457
4514
|
className: "polycss-shadow polycss-shadow-svg polycss-shadow-receiver",
|
|
4458
4515
|
"data-poly-shadow-type": "receiver",
|
|
4459
|
-
"data-poly-shadow-receiver-face":
|
|
4460
|
-
"data-poly-shadow-receiver-polys": JSON.stringify(
|
|
4461
|
-
width:
|
|
4462
|
-
height:
|
|
4463
|
-
viewBox: `0 0 ${
|
|
4516
|
+
"data-poly-shadow-receiver-face": fc.faceIndex,
|
|
4517
|
+
"data-poly-shadow-receiver-polys": JSON.stringify(fc.memberPolyIndices),
|
|
4518
|
+
width: fc.width,
|
|
4519
|
+
height: fc.height,
|
|
4520
|
+
viewBox: `0 0 ${fc.width} ${fc.height}`,
|
|
4464
4521
|
style: {
|
|
4465
4522
|
position: "absolute",
|
|
4466
4523
|
top: 0,
|
|
@@ -4470,25 +4527,27 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
|
|
|
4470
4527
|
transformOrigin: "0 0",
|
|
4471
4528
|
pointerEvents: "none",
|
|
4472
4529
|
willChange: "transform",
|
|
4473
|
-
|
|
4530
|
+
opacity: fc.svgOpacity,
|
|
4531
|
+
transform: fc.matrixCss
|
|
4474
4532
|
},
|
|
4475
|
-
children:
|
|
4476
|
-
"path",
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4533
|
+
children: [
|
|
4534
|
+
fc.baseFill && fc.baseD ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: fc.baseD, fill: fc.baseFill, fillRule: "nonzero" }) : null,
|
|
4535
|
+
fc.layers.map((layer, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
4536
|
+
"path",
|
|
4537
|
+
{
|
|
4538
|
+
d: layer.d,
|
|
4539
|
+
fill: layer.fill,
|
|
4540
|
+
fillRule: "nonzero",
|
|
4541
|
+
opacity: layer.opacity !== 1 ? layer.opacity.toFixed(4) : void 0,
|
|
4542
|
+
style: layer.multiply ? { mixBlendMode: "multiply" } : void 0
|
|
4543
|
+
},
|
|
4544
|
+
i
|
|
4545
|
+
))
|
|
4546
|
+
]
|
|
4488
4547
|
},
|
|
4489
|
-
`receiver-${
|
|
4548
|
+
`receiver-${fc.faceIndex}`
|
|
4490
4549
|
)) });
|
|
4491
|
-
}, [receiveShadow, shadowCasters, shadowCastersVersion, polygons, position, scale, rotation, sceneDirectionalLight, sceneShadow, sceneCtx?.ambientLight, cameraCtx?.store, cameraTick, selfShadowEdgeMap]);
|
|
4550
|
+
}, [receiveShadow, shadowCasters, shadowCastersVersion, polygons, position, scale, rotation, sceneDirectionalLight, sceneCtx?.pointLights, effectiveTextureLighting, sceneShadow, sceneCtx?.ambientLight, cameraCtx?.store, cameraTick, selfShadowEdgeMap]);
|
|
4492
4551
|
const portalSceneEl = sceneCtx?.sceneEl ?? null;
|
|
4493
4552
|
const portaledReceiverShadowSvgs = portalSceneEl && receiverShadowSvgs ? (0, import_react_dom.createPortal)(receiverShadowSvgs, portalSceneEl) : null;
|
|
4494
4553
|
setPolygonsImplRef.current = (nextPolygons) => {
|