@glyphcss/core 0.0.1
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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +1181 -0
- package/dist/index.d.ts +1181 -0
- package/dist/index.js +3 -0
- package/package.json +52 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
declare const DEFAULT_PROJECTION: "cubic";
|
|
2
|
+
/**
|
|
3
|
+
* Mesh post-processing intent.
|
|
4
|
+
* - "lossless": preserve the authored surface while applying exact
|
|
5
|
+
* reductions such as interior culling and coplanar merge.
|
|
6
|
+
* - "lossy": allow bounded geometric approximation when it reduces the
|
|
7
|
+
* rendered polygon/DOM count.
|
|
8
|
+
*/
|
|
9
|
+
type MeshResolution = "lossless" | "lossy";
|
|
10
|
+
/**
|
|
11
|
+
* 3D point/vector, stored as a `[x, y, z]` tuple. Tuple (rather than
|
|
12
|
+
* `{x, y, z}`) for compact JSON: meshes serialize to thousands of vertices
|
|
13
|
+
* and the difference adds up. Destructure with `const [x, y, z] = v` when
|
|
14
|
+
* you need named axes.
|
|
15
|
+
*
|
|
16
|
+
* Polycss world space convention: +X right, +Y forward, +Z up.
|
|
17
|
+
*/
|
|
18
|
+
type Vec3 = [number, number, number];
|
|
19
|
+
/**
|
|
20
|
+
* 2D point/vector — `[u, v]`. Used for texture-atlas UV coordinates on
|
|
21
|
+
* polygons. Convention follows OBJ: u is horizontal (0=left, 1=right),
|
|
22
|
+
* v is vertical (0=bottom, 1=top). Renderers flip v when binding to raster
|
|
23
|
+
* image space whose Y-axis points down.
|
|
24
|
+
*/
|
|
25
|
+
type Vec2 = [number, number];
|
|
26
|
+
interface TextureTriangle {
|
|
27
|
+
vertices: [Vec3, Vec3, Vec3];
|
|
28
|
+
uvs: [Vec2, Vec2, Vec2];
|
|
29
|
+
/** Hex color string (`#rrggbb`) propagated from source model material. */
|
|
30
|
+
color?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Directional light — simulates a single distant source (sun, key light).
|
|
34
|
+
* Contributes Lambert shading scaled by `intensity`. `direction` is in
|
|
35
|
+
* scene-local coords and does not need to be pre-normalized.
|
|
36
|
+
* Mirrors three.js's `DirectionalLight`.
|
|
37
|
+
*/
|
|
38
|
+
interface GlyphcssDirectionalLight {
|
|
39
|
+
/** Direction the light shines TOWARD (typical convention). */
|
|
40
|
+
direction: Vec3;
|
|
41
|
+
/** Light tint, hex string. White by default. */
|
|
42
|
+
color?: string;
|
|
43
|
+
/** Scalar multiplier on the directional contribution. Default 1. */
|
|
44
|
+
intensity?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ambient light — uniform fill that adds to every polygon regardless of
|
|
48
|
+
* orientation. Mirrors three.js's `AmbientLight`. Decoupled from the
|
|
49
|
+
* directional contribution: the two add independently rather than
|
|
50
|
+
* splitting a fixed energy budget.
|
|
51
|
+
*/
|
|
52
|
+
interface GlyphcssAmbientLight {
|
|
53
|
+
/** Tint, hex string. White by default. */
|
|
54
|
+
color?: string;
|
|
55
|
+
/** Scalar multiplier on the ambient contribution. Default 0.4. */
|
|
56
|
+
intensity?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Material — paint configuration shareable across many polygons.
|
|
60
|
+
*
|
|
61
|
+
* In CSS terms, a material bundles the `background-image` source plus paint
|
|
62
|
+
* config. When a polygon references a material AND its UVs form an
|
|
63
|
+
* axis-aligned rectangle, polycss renders the polygon as an <i> with
|
|
64
|
+
* `background-image: url(material.texture)` directly — no per-polygon canvas
|
|
65
|
+
* rasterization, browser-cached texture, mounting / unmounting one polygon
|
|
66
|
+
* does not affect any other.
|
|
67
|
+
*
|
|
68
|
+
* Three.js parallel: combines THREE.Texture + a basic Material in one. CSS
|
|
69
|
+
* has no shader/sampler concerns, so the texture/material split from
|
|
70
|
+
* Three.js doesn't pay rent here.
|
|
71
|
+
*/
|
|
72
|
+
interface PolyMaterial {
|
|
73
|
+
/** Image source. Anything `background-image: url(...)` can use. */
|
|
74
|
+
texture: string;
|
|
75
|
+
/** Optional unique key (used by polycss to dedupe / cache). Caller can
|
|
76
|
+
* pass a stable string; if omitted, the material's identity is its object
|
|
77
|
+
* reference. */
|
|
78
|
+
key?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The single polygon type for polycss. N coplanar vertices in 3D space,
|
|
82
|
+
* CCW winding from outside. No bbox field, no shape discriminator, no
|
|
83
|
+
* input/output distinction — one type, used by parsers, by the merge
|
|
84
|
+
* pass, and by the renderer.
|
|
85
|
+
*/
|
|
86
|
+
interface Polygon {
|
|
87
|
+
/** N coplanar vertices in 3D space, CCW winding from outside. */
|
|
88
|
+
vertices: Vec3[];
|
|
89
|
+
/**
|
|
90
|
+
* Solid base color. Falls back to "#cccccc" when neither color nor
|
|
91
|
+
* texture is set.
|
|
92
|
+
*/
|
|
93
|
+
color?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Texture URL. When set with `uvs`, UV-mapped via affine; without
|
|
96
|
+
* `uvs`, single-tile fill. If the load fails, renderer falls back to
|
|
97
|
+
* `color` (or default gray).
|
|
98
|
+
*/
|
|
99
|
+
texture?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Shared material. When set, `material.texture` takes precedence over the
|
|
102
|
+
* inline `texture` field. If the polygon's UVs form an axis-aligned
|
|
103
|
+
* rectangle, polycss uses the direct CSS background-image path (no per-
|
|
104
|
+
* polygon canvas rasterization). Falls back to the atlas path otherwise.
|
|
105
|
+
*/
|
|
106
|
+
material?: PolyMaterial;
|
|
107
|
+
/**
|
|
108
|
+
* Per-vertex UV coords (0..1, OBJ convention with v=0 at bottom).
|
|
109
|
+
* Length MUST equal vertices.length when set; mismatched UVs are
|
|
110
|
+
* stripped by `normalizePolygons`.
|
|
111
|
+
*/
|
|
112
|
+
uvs?: Vec2[];
|
|
113
|
+
/**
|
|
114
|
+
* Renderer-internal source triangles for UV textures. Merge passes use this
|
|
115
|
+
* to reduce DOM planes while preserving per-triangle texture mapping in the
|
|
116
|
+
* generated atlas.
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
textureTriangles?: TextureTriangle[];
|
|
120
|
+
/**
|
|
121
|
+
* User-controlled metadata. Reflected to DOM as `data-*` attributes via
|
|
122
|
+
* stringification by the framework wrappers. Only string|number|boolean
|
|
123
|
+
* values are kept; other shapes are dropped by `normalizePolygons`.
|
|
124
|
+
*/
|
|
125
|
+
data?: Record<string, string | number | boolean>;
|
|
126
|
+
}
|
|
127
|
+
/** Rendering mode for `rasterize`. See README for tradeoffs. */
|
|
128
|
+
type RenderMode = "wireframe" | "solid" | "voxel";
|
|
129
|
+
/**
|
|
130
|
+
* Character ramp used by `solid` mode to map shaded intensity to a glyph.
|
|
131
|
+
* Index 0 = darkest (transparent / unset), last index = brightest.
|
|
132
|
+
*/
|
|
133
|
+
type CharRamp = string[];
|
|
134
|
+
/**
|
|
135
|
+
* Wireframe edge weight. Maps to glyph density in the rasterizer:
|
|
136
|
+
* 1 — thin (spokes, inner cage)
|
|
137
|
+
* 2 — normal (main cage edges)
|
|
138
|
+
* 3 — core (focal accents)
|
|
139
|
+
*/
|
|
140
|
+
type EdgeWeight = 1 | 2 | 3;
|
|
141
|
+
/** A single drawable edge in wireframe mode. */
|
|
142
|
+
interface WireframeEdge {
|
|
143
|
+
from: Vec3;
|
|
144
|
+
to: Vec3;
|
|
145
|
+
weight?: EdgeWeight;
|
|
146
|
+
/** Hex color string (`#rrggbb`) propagated from the adjacent triangle's material. */
|
|
147
|
+
color?: string;
|
|
148
|
+
}
|
|
149
|
+
/** Grid dimensions in character cells. */
|
|
150
|
+
interface GridSize {
|
|
151
|
+
cols: number;
|
|
152
|
+
rows: number;
|
|
153
|
+
/** Character cell aspect ratio (height / width). Typically ~2.0 for monospace. */
|
|
154
|
+
cellAspect: number;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A 3D anchor that should produce a 2D hitbox in the consumer's DOM.
|
|
158
|
+
* Consumers absolute-position a `<div>` at the projected cell, sized by
|
|
159
|
+
* `size` (in character cells). Pure-math: this module just projects.
|
|
160
|
+
*/
|
|
161
|
+
interface Hotspot {
|
|
162
|
+
id: string;
|
|
163
|
+
at: Vec3;
|
|
164
|
+
/** Hitbox size in cells. Default `[1, 1]`. */
|
|
165
|
+
size?: [number, number];
|
|
166
|
+
}
|
|
167
|
+
/** Result of projecting a single hotspot through the camera. */
|
|
168
|
+
interface HotspotCell {
|
|
169
|
+
id: string;
|
|
170
|
+
col: number;
|
|
171
|
+
row: number;
|
|
172
|
+
/** Camera-space Z. Useful for `z-index` / occlusion checks. */
|
|
173
|
+
depth: number;
|
|
174
|
+
/** False if behind the camera or off-grid. */
|
|
175
|
+
visible: boolean;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* normalizePolygons — validates a polygon list, drops degenerate inputs,
|
|
180
|
+
* triangulates non-coplanar N-gons, strips bad UVs, sanitizes data, and
|
|
181
|
+
* returns the cleaned polygons + a list of human-readable warnings.
|
|
182
|
+
*
|
|
183
|
+
* Validation rules are encoded here and covered by the normalization tests.
|
|
184
|
+
*
|
|
185
|
+
* Pure: no DOM, no I/O, deterministic. Bbox is NOT computed here — that's
|
|
186
|
+
* derived on demand by `buildSceneContext` / consumers.
|
|
187
|
+
*/
|
|
188
|
+
|
|
189
|
+
interface NormalizeResult {
|
|
190
|
+
polygons: Polygon[];
|
|
191
|
+
warnings: string[];
|
|
192
|
+
}
|
|
193
|
+
declare function normalizePolygons(input: Polygon[]): NormalizeResult;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Scene context — the top-level entry point that takes a polygon mesh
|
|
197
|
+
* (already normalized) and returns the data the framework wrappers need
|
|
198
|
+
* to render.
|
|
199
|
+
*
|
|
200
|
+
* No cube grid, no per-Z layer bucketing, no wall mask, no neighbor-based
|
|
201
|
+
* occlusion. Just a polygon list and a scene bbox.
|
|
202
|
+
*/
|
|
203
|
+
|
|
204
|
+
interface SceneBbox {
|
|
205
|
+
/** Minimum corner of the axis-aligned bounding box (inclusive). */
|
|
206
|
+
min: Vec3;
|
|
207
|
+
/** Maximum corner of the axis-aligned bounding box (inclusive). */
|
|
208
|
+
max: Vec3;
|
|
209
|
+
}
|
|
210
|
+
interface SceneContext {
|
|
211
|
+
/** Validated polygon list — the renderer iterates this. */
|
|
212
|
+
polygons: Polygon[];
|
|
213
|
+
/** Polygon-mesh bbox in world space. Used to size the scene container. */
|
|
214
|
+
sceneBbox: SceneBbox;
|
|
215
|
+
/** Warnings raised during normalization (already-applied fixes). */
|
|
216
|
+
warnings: string[];
|
|
217
|
+
}
|
|
218
|
+
interface SceneContextBuildArgs {
|
|
219
|
+
/**
|
|
220
|
+
* Polygon list. Pass parser output directly — `buildSceneContext` runs
|
|
221
|
+
* `normalizePolygons` for you.
|
|
222
|
+
*/
|
|
223
|
+
polygons: Polygon[];
|
|
224
|
+
/**
|
|
225
|
+
* If true, skip the normalize pass (caller has already validated). Useful
|
|
226
|
+
* when chaining `mergePolygons` after a manual `normalizePolygons` call.
|
|
227
|
+
*/
|
|
228
|
+
skipNormalize?: boolean;
|
|
229
|
+
}
|
|
230
|
+
interface SceneContextBuildResult {
|
|
231
|
+
context: SceneContext;
|
|
232
|
+
/**
|
|
233
|
+
* Mesh-bbox dimensions. Convenience copy of `context.sceneBbox` plus a
|
|
234
|
+
* `size` field (max - min) for callers that want a single number per axis.
|
|
235
|
+
*/
|
|
236
|
+
dimensions: {
|
|
237
|
+
sceneBbox: SceneBbox;
|
|
238
|
+
size: Vec3;
|
|
239
|
+
};
|
|
240
|
+
/** Warnings raised during normalization. Mirrors `context.warnings`. */
|
|
241
|
+
warnings: string[];
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Compute the axis-aligned bounding box across every vertex of every polygon.
|
|
245
|
+
* Returns a zero-extent bbox at origin for empty input — callers that care
|
|
246
|
+
* about that case should check `polygons.length` first.
|
|
247
|
+
*/
|
|
248
|
+
declare function computeSceneBbox(polygons: Polygon[]): SceneBbox;
|
|
249
|
+
declare function buildSceneContext(args: SceneContextBuildArgs): SceneContextBuildResult;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Polygon geometry helpers — pure math operating on Polygon vertices.
|
|
253
|
+
*
|
|
254
|
+
* After cube removal in Phase 2, this module carries small polygon-level
|
|
255
|
+
* helpers for downstream consumers (lighting, debug metrics, etc.). The
|
|
256
|
+
* cube / ramp / wedge / spike face emitters lived here in voxcss; they're gone.
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
interface PolygonFace {
|
|
260
|
+
/** Vertices in CCW-from-outside order. Same as Polygon.vertices. */
|
|
261
|
+
v: Vec3[];
|
|
262
|
+
/** Original polygon's color, if any (for lighting helpers). */
|
|
263
|
+
color?: string;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Surface a polygon as a single face. The returned array always has length 1;
|
|
267
|
+
* the indirection exists so callers that historically iterated faces (e.g.
|
|
268
|
+
* the manifold check, the canvas validator) can keep their loop shape.
|
|
269
|
+
*
|
|
270
|
+
* Returns an empty array for degenerate polygons (< 3 vertices).
|
|
271
|
+
*/
|
|
272
|
+
declare function polygonFaces(p: Polygon): PolygonFace[];
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Apply CSS-style chained `rotateX(rx) rotateY(ry) rotateZ(rz)` rotation
|
|
276
|
+
* to a 3D vector. Matches the matrix composition used by polycss mesh
|
|
277
|
+
* wrapper transforms (see `buildTransform` in each PolyMesh implementation).
|
|
278
|
+
*
|
|
279
|
+
* CSS composes `transform: rotateX(rx) rotateY(ry) rotateZ(rz)` as the
|
|
280
|
+
* matrix `M = Rx · Ry · Rz`, applied to a point as `M · p` — so Rz acts
|
|
281
|
+
* first on the point, then Ry, then Rx. Compound rotations only commute
|
|
282
|
+
* when axes coincide; getting the order wrong silently corrupts results
|
|
283
|
+
* for any two-axis combination.
|
|
284
|
+
*
|
|
285
|
+
* Angles in degrees.
|
|
286
|
+
*/
|
|
287
|
+
declare function rotateVec3(v: Vec3, rxDeg: number, ryDeg: number, rzDeg: number): Vec3;
|
|
288
|
+
/**
|
|
289
|
+
* Inverse of `rotateVec3` for the same rotation tuple — transforms a
|
|
290
|
+
* world-space vector into the mesh's local frame. Used by the baked
|
|
291
|
+
* atlas pipeline to inverse-rotate the directional light so the
|
|
292
|
+
* pre-multiplied Lambert shading stays correct after the mesh rotates,
|
|
293
|
+
* and by the dynamic-mode CSS-var override for the same reason.
|
|
294
|
+
*
|
|
295
|
+
* The inverse of `M = Rx · Ry · Rz` is `M⁻¹ = Rz⁻¹ · Ry⁻¹ · Rx⁻¹`, so
|
|
296
|
+
* Rx⁻¹ acts first on the vector, then Ry⁻¹, then Rz⁻¹.
|
|
297
|
+
*
|
|
298
|
+
* `rot` is `[rxDeg, ryDeg, rzDeg]` matching the mesh's CSS rotation prop.
|
|
299
|
+
*/
|
|
300
|
+
declare function inverseRotateVec3(v: Vec3, rot: Vec3): Vec3;
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Minimal quaternion helpers for composing rotations.
|
|
304
|
+
*
|
|
305
|
+
* Why we need quaternions: the public PolyMesh API exposes rotation as a
|
|
306
|
+
* Euler triple `[rx, ry, rz]` in degrees (drives CSS `rotateX rotateY
|
|
307
|
+
* rotateZ`, applied right-to-left). Euler triples don't compose by
|
|
308
|
+
* component addition — rotating Y after X must happen around the mesh's
|
|
309
|
+
* NEW local-Y axis, not world-Y. The transform-controls ring drag handler
|
|
310
|
+
* uses these helpers to compose around the mesh's local axis correctly:
|
|
311
|
+
*
|
|
312
|
+
* q_start = quatFromEulerXYZ(currentRotationDeg)
|
|
313
|
+
* q_delta = quatFromAxisAngle(localAxis, deltaRadians)
|
|
314
|
+
* q_new = quatMultiply(q_start, q_delta) // RIGHT-multiply = local frame
|
|
315
|
+
* next = eulerXYZFromQuat(q_new)
|
|
316
|
+
*
|
|
317
|
+
* Convention: "XYZ" Euler means the composed rotation matrix is
|
|
318
|
+
* `Rx(rx) · Ry(ry) · Rz(rz)`, which matches CSS `rotateX rotateY rotateZ`
|
|
319
|
+
* (right-to-left application to a point ⇒ Z first, then Y, then X).
|
|
320
|
+
*
|
|
321
|
+
* Quaternion format: `[w, x, y, z]` (real-first, like three.js's internal
|
|
322
|
+
* `_x/_y/_z/_w` reordered). Stored as plain tuples — no constructor or
|
|
323
|
+
* runtime allocations per drag.
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
/** Quaternion `[w, x, y, z]`, real component first. Unit-length is not
|
|
327
|
+
* enforced by the type — callers normalize when needed. */
|
|
328
|
+
type Quat = [number, number, number, number];
|
|
329
|
+
/** Identity quaternion. */
|
|
330
|
+
declare const QUAT_IDENTITY: Quat;
|
|
331
|
+
/** Hamilton product `q1 * q2`. Apply to a vector as `q v q⁻¹`. Right-
|
|
332
|
+
* multiplication composes the second rotation in the LOCAL frame of the
|
|
333
|
+
* first — that's the property the gizmo relies on for local-axis drag. */
|
|
334
|
+
declare function quatMultiply(q1: Quat, q2: Quat): Quat;
|
|
335
|
+
/** Quaternion from axis-angle. `axis` must be unit length (caller's
|
|
336
|
+
* responsibility — typically a CSS basis vector). `angleRad` in radians. */
|
|
337
|
+
declare function quatFromAxisAngle(axis: Vec3, angleRad: number): Quat;
|
|
338
|
+
/** Quaternion from Euler XYZ degrees — the order CSS `rotateX rotateY
|
|
339
|
+
* rotateZ` applies. Matches the composed matrix `Rx(rx)·Ry(ry)·Rz(rz)`. */
|
|
340
|
+
declare function quatFromEulerXYZ(eulerDeg: Vec3): Quat;
|
|
341
|
+
/** Euler XYZ degrees from a quaternion — inverse of `quatFromEulerXYZ`.
|
|
342
|
+
* Handles gimbal lock (|ry| → 90°) by collapsing rz onto rx. The output
|
|
343
|
+
* matches the convention used by CSS `rotateX rotateY rotateZ` so it can
|
|
344
|
+
* be written straight back into a PolyMesh rotation prop. */
|
|
345
|
+
declare function eulerXYZFromQuat(q: Quat): Vec3;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Base tile size in CSS pixels. One polycss world unit = BASE_TILE CSS
|
|
349
|
+
* pixels (pre-scale). Used to convert world-coordinate target values to
|
|
350
|
+
* CSS translations in the transform string.
|
|
351
|
+
*/
|
|
352
|
+
declare const BASE_TILE = 50;
|
|
353
|
+
interface AutoRotateConfig {
|
|
354
|
+
axis?: "x" | "y";
|
|
355
|
+
speed?: number;
|
|
356
|
+
pauseOnInteraction?: boolean;
|
|
357
|
+
}
|
|
358
|
+
type AutoRotateOption = boolean | number | AutoRotateConfig;
|
|
359
|
+
/**
|
|
360
|
+
* World-coordinate camera state (Three.js-style).
|
|
361
|
+
*
|
|
362
|
+
* `target` is the world point that should appear at the viewport centre.
|
|
363
|
+
* Polycss world axes: [0]=X (rows/south), [1]=Y (cols/east), [2]=Z (up).
|
|
364
|
+
*
|
|
365
|
+
* `pan`, `tilt`, and `depthOffset` are gone. Translations now live inside
|
|
366
|
+
* `target` so they happen BEFORE rotations — enabling correct world-space
|
|
367
|
+
* pan at any tilt angle.
|
|
368
|
+
*
|
|
369
|
+
* `distance` is the camera's pull-back from the target in CSS pixels.
|
|
370
|
+
* Increasing distance moves the camera farther from the target along the
|
|
371
|
+
* view axis (dolly out) — analogous to three.js's spherical radius.
|
|
372
|
+
* Default 0 keeps the legacy behaviour unchanged.
|
|
373
|
+
*/
|
|
374
|
+
interface CameraState {
|
|
375
|
+
target: Vec3;
|
|
376
|
+
rotX: number;
|
|
377
|
+
rotY: number;
|
|
378
|
+
zoom: number;
|
|
379
|
+
/** Camera pull-back from target in CSS pixels. Default 0. */
|
|
380
|
+
distance: number;
|
|
381
|
+
}
|
|
382
|
+
interface CameraStyleInput {
|
|
383
|
+
rows?: number;
|
|
384
|
+
cols?: number;
|
|
385
|
+
}
|
|
386
|
+
interface CameraHandle {
|
|
387
|
+
state: CameraState;
|
|
388
|
+
update(next: Partial<CameraState>): void;
|
|
389
|
+
getStyle(input?: CameraStyleInput): {
|
|
390
|
+
transform: string;
|
|
391
|
+
width: string;
|
|
392
|
+
height: string;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
declare function normalizeInvertMultiplier(value: number | boolean | undefined): number | undefined;
|
|
396
|
+
declare const DEFAULT_CAMERA_STATE: CameraState;
|
|
397
|
+
declare function createIsometricCamera(initial?: Partial<CameraState>): CameraHandle;
|
|
398
|
+
|
|
399
|
+
interface ParsedColor {
|
|
400
|
+
rgb: [number, number, number];
|
|
401
|
+
alpha: number;
|
|
402
|
+
}
|
|
403
|
+
declare function parseHexColor(value: string): ParsedColor | null;
|
|
404
|
+
declare function parseRgbColor(value: string): ParsedColor | null;
|
|
405
|
+
/** Parse hex or rgb/rgba color strings. Pure — no DOM. */
|
|
406
|
+
declare function parsePureColor(input: string): ParsedColor | null;
|
|
407
|
+
declare function clampChannel(value: number): number;
|
|
408
|
+
declare function formatColor(color: ParsedColor): string;
|
|
409
|
+
|
|
410
|
+
declare function parseColor(input: string): ParsedColor | null;
|
|
411
|
+
/**
|
|
412
|
+
* Lighten/darken a color by a flat per-channel delta. Used by the framework
|
|
413
|
+
* wrappers for tinted-overlay debug renderers; per-polygon Lambert shading
|
|
414
|
+
* goes through `computeShapeLighting` instead.
|
|
415
|
+
*/
|
|
416
|
+
declare function shadeColor(base: string, delta: number): string;
|
|
417
|
+
/**
|
|
418
|
+
* Per-polygon Lambert shading. Given a polygon's outward normal and the
|
|
419
|
+
* scene's lights, returns the shaded color as a CSS rgb string.
|
|
420
|
+
*
|
|
421
|
+
* Math (decoupled, three.js convention):
|
|
422
|
+
* tint = ambient.color · ambient.intensity
|
|
423
|
+
* + directional.color · directional.intensity · max(0, n · (−L))
|
|
424
|
+
* final = baseColor × tint
|
|
425
|
+
*
|
|
426
|
+
* Pass `directional` and/or `ambient` undefined to fall back to defaults
|
|
427
|
+
* (top-down white directional with intensity 1, white ambient with
|
|
428
|
+
* intensity 0.4) — useful for static SSR/validator renders.
|
|
429
|
+
*/
|
|
430
|
+
declare function computeShapeLighting(normal: Vec3, baseColor: string, directional?: GlyphcssDirectionalLight, ambient?: GlyphcssAmbientLight): string;
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Merge coplanar same-color adjacent triangles into N-vertex polygons.
|
|
434
|
+
*
|
|
435
|
+
* Each polygon is rendered as one atlas-backed DOM element — so a mesh whose
|
|
436
|
+
* triangles came from quads or pentagons collapses back into its original
|
|
437
|
+
* face count.
|
|
438
|
+
*
|
|
439
|
+
* - Geodesic spheres: ~half the triangles came from quad subdivisions
|
|
440
|
+
* - OBJ imports: many were quads/n-gons fan-triangulated by the importer
|
|
441
|
+
* - Hand-built dodecahedra: 36 triangles → 12 pentagons
|
|
442
|
+
*
|
|
443
|
+
* Algorithm:
|
|
444
|
+
* 1. For each input polygon, compute its plane (unit normal + signed
|
|
445
|
+
* distance from origin).
|
|
446
|
+
* 2. Build an undirected edge graph: every edge of every polygon indexes
|
|
447
|
+
* the polygons it belongs to.
|
|
448
|
+
* 3. Repeatedly walk shared edges and merge the two polygons sharing that
|
|
449
|
+
* edge if they pass the merge predicate (same color, near-coplanar,
|
|
450
|
+
* result is convex, edge is interior). Each merge replaces two
|
|
451
|
+
* polygons with one larger polygon and updates the edge index.
|
|
452
|
+
* 4. Iterate until no more merges fire — the fixed point grows triangles
|
|
453
|
+
* → quads → pentagons → … as far as the geometry allows.
|
|
454
|
+
*
|
|
455
|
+
* Polygons with < 3 vertices are passed through unchanged (the caller is
|
|
456
|
+
* expected to have run `normalizePolygons` first; this is a defensive copy).
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
declare function mergePolygons(input: Polygon[]): Polygon[];
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* dedupeOverlappingPolygons — drop polygons whose 3D footprint coincides
|
|
463
|
+
* with another polygon's, within an epsilon tolerance.
|
|
464
|
+
*
|
|
465
|
+
* Why this exists: modelers (and importers) often emit redundant geometry
|
|
466
|
+
* for the same visible surface — a doubled face on a wall, an inner shell
|
|
467
|
+
* coincident with an outer shell, or two N-gons that fan-triangulate the
|
|
468
|
+
* same region. Each duplicate is a real `<i>` element at render time:
|
|
469
|
+
* it costs DOM, Lambert math, atlas budget, AND it produces stacked
|
|
470
|
+
* shadow leaves that visibly multiply on the receiver (overlapping dark
|
|
471
|
+
* patches on the ground).
|
|
472
|
+
*
|
|
473
|
+
* This is a separate concern from `cullInteriorPolygons` (which removes
|
|
474
|
+
* polygons fully *enclosed* by other geometry, conservative against
|
|
475
|
+
* false positives) and from `mergePolygons` (which joins same-color
|
|
476
|
+
* coplanar polygons that share an edge). A polygon's exact twin
|
|
477
|
+
* doesn't share an edge with itself and isn't enclosed by anything —
|
|
478
|
+
* it slips through both passes.
|
|
479
|
+
*
|
|
480
|
+
* Algorithm:
|
|
481
|
+
* 1. Compute each polygon's plane (normal + signed offset along
|
|
482
|
+
* normal) and centroid.
|
|
483
|
+
* 2. Bucket polygons by quantized plane key (rounded normal direction
|
|
484
|
+
* with sign-folding so anti-parallel faces share a bucket, and
|
|
485
|
+
* rounded distance from origin along the unsigned normal axis).
|
|
486
|
+
* Polygons in different buckets cannot overlap.
|
|
487
|
+
* 3. Within each bucket, do an O(K²) pairwise check on at most K
|
|
488
|
+
* polygons. Two polygons overlap if their 2D projections onto
|
|
489
|
+
* the shared plane share a significant area fraction.
|
|
490
|
+
* 4. When a pair overlaps, drop one: prefer keeping the one whose
|
|
491
|
+
* normal points *away* from the mesh centroid (the "outward"
|
|
492
|
+
* face). For ties (truly identical orientation), keep the one
|
|
493
|
+
* with greater 2D area.
|
|
494
|
+
*
|
|
495
|
+
* Runs once at parse time in the same pipeline as mergePolygons. Zero
|
|
496
|
+
* cost at runtime — once it returns the polygon array is final and the
|
|
497
|
+
* dedup logic never executes again.
|
|
498
|
+
*/
|
|
499
|
+
|
|
500
|
+
/** Tunable thresholds. Default values are conservative — only catch
|
|
501
|
+
* duplicates that are visually identical surfaces (exact twins,
|
|
502
|
+
* back-to-back winding flips, nested polys on the same plane).
|
|
503
|
+
* Looser values are appropriate for shadow-casting purposes, where
|
|
504
|
+
* any polygons whose projections land in the same place can share a
|
|
505
|
+
* shadow without affecting the rendered model. */
|
|
506
|
+
interface DedupeOverlappingPolygonsOptions {
|
|
507
|
+
/** Maximum 1 - |dot(n_a, n_b)| for normals to count as "parallel".
|
|
508
|
+
* Default 1e-3 (strict — must be near-identical orientation).
|
|
509
|
+
* Looser values (~5e-2 ≈ 18° off) treat near-parallel normals as
|
|
510
|
+
* duplicates, useful for shadow dedup where small orientation
|
|
511
|
+
* differences project to nearly the same shadow shape. */
|
|
512
|
+
normalTolerance?: number;
|
|
513
|
+
/** Maximum signed-distance difference between two polygons' plane
|
|
514
|
+
* offsets (along their shared normal) to count as coplanar.
|
|
515
|
+
* Default 0.05 (world units). Looser values treat distinct
|
|
516
|
+
* parallel shells (e.g. an inner cavity wall behind an outer
|
|
517
|
+
* wall) as shadow-duplicates. */
|
|
518
|
+
distanceTolerance?: number;
|
|
519
|
+
/** Minimum overlap fraction (max of A-in-B and B-in-A vertex
|
|
520
|
+
* containment ratios) for a pair to count as a duplicate.
|
|
521
|
+
* Default 0.7. Lower (~0.4) is liberal; higher (~0.9) is strict. */
|
|
522
|
+
overlapFraction?: number;
|
|
523
|
+
}
|
|
524
|
+
/** Identify polygons that are duplicates within tolerance. Returns the
|
|
525
|
+
* set of indices into the input array that should be dropped (the
|
|
526
|
+
* losers of duplicate pairs). The "winner" of a pair is the polygon
|
|
527
|
+
* whose normal faces away from the mesh centroid (outward), with
|
|
528
|
+
* larger area as a tiebreaker.
|
|
529
|
+
*
|
|
530
|
+
* Exposed for callers that want to act on the index set directly —
|
|
531
|
+
* e.g. shadow casting can use a looser tolerance to skip shadow leaves
|
|
532
|
+
* for redundant casters without removing them from the renderable
|
|
533
|
+
* polygon set. */
|
|
534
|
+
declare function findOverlappingPolygonDuplicates(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Set<number>;
|
|
535
|
+
declare function dedupeOverlappingPolygons(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Polygon[];
|
|
536
|
+
|
|
537
|
+
interface CoverPlanarPolygonsOptions {
|
|
538
|
+
/** Smallest connected coplanar group worth attempting. Default 4. */
|
|
539
|
+
minGroupPolygons?: number;
|
|
540
|
+
/** Maximum candidate 2D axes tested per group. Default 8. */
|
|
541
|
+
maxCandidateAxes?: number;
|
|
542
|
+
/** Plane normal/distance tolerance in scene units. Default 1e-3. */
|
|
543
|
+
planeEpsilon?: number;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Re-cover flat same-color mesh regions with generated convex polygons.
|
|
547
|
+
*
|
|
548
|
+
* `mergePolygons` preserves source topology: it can only combine existing
|
|
549
|
+
* neighboring faces. This pass is more aggressive for solid-color planar
|
|
550
|
+
* regions: it projects each connected coplanar patch into 2D, covers the
|
|
551
|
+
* patch from its outer boundary, then lets `mergePolygons` collapse the
|
|
552
|
+
* generated cover into large rects/quads where possible.
|
|
553
|
+
*/
|
|
554
|
+
declare function coverPlanarPolygons(input: Polygon[], options?: CoverPlanarPolygonsOptions): Polygon[];
|
|
555
|
+
|
|
556
|
+
interface ApproximateMergeOptions {
|
|
557
|
+
maxAngleDeg?: number;
|
|
558
|
+
maxPlaneDisplacement?: number;
|
|
559
|
+
maxBoundaryDisplacement?: number;
|
|
560
|
+
isolatedPairs?: boolean;
|
|
561
|
+
}
|
|
562
|
+
interface OptimizeMeshPolygonsOptions {
|
|
563
|
+
/** Public quality/resolution intent. Defaults to "lossy". */
|
|
564
|
+
meshResolution?: MeshResolution;
|
|
565
|
+
/**
|
|
566
|
+
* Run the planar cover pass as an exact candidate for untextured coplanar
|
|
567
|
+
* regions. Defaults to true.
|
|
568
|
+
*/
|
|
569
|
+
rectCover?: boolean | CoverPlanarPolygonsOptions;
|
|
570
|
+
/**
|
|
571
|
+
* Lossy approximate merge settings. Ignored for lossless resolution.
|
|
572
|
+
* When omitted, lossy evaluates isolated-pair and small plane-group
|
|
573
|
+
* strategies, then chooses the lowest render-cost result with a near-cost
|
|
574
|
+
* preference for candidates that reduce detected internal gaps.
|
|
575
|
+
*/
|
|
576
|
+
approximateMerge?: boolean | ApproximateMergeOptions;
|
|
577
|
+
}
|
|
578
|
+
declare function optimizeMeshPolygons(polygons: Polygon[], options?: OptimizeMeshPolygonsOptions): Polygon[];
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* cullInteriorPolygons — remove polygons that are fully enclosed by other
|
|
582
|
+
* polygons of the same mesh and therefore never visible from any external
|
|
583
|
+
* camera direction.
|
|
584
|
+
*
|
|
585
|
+
* Algorithm: for each polygon p,
|
|
586
|
+
* 1. Sample K unit directions on the hemisphere above p's normal.
|
|
587
|
+
* 2. Cast a ray from a point just above p's centroid in each direction.
|
|
588
|
+
* 3. If at least one ray escapes without hitting any other polygon → p
|
|
589
|
+
* is potentially visible from some external camera → keep it.
|
|
590
|
+
* 4. If every ray hits another polygon → p is fully surrounded → cull it.
|
|
591
|
+
*
|
|
592
|
+
* Acceleration: flat-array SAH-built binary BVH with slab-test AABB traversal.
|
|
593
|
+
* All BVH data is stored in typed Float64Array / Int32Array for cache efficiency.
|
|
594
|
+
* Ray traversal visits only the subtrees whose AABBs the ray intersects.
|
|
595
|
+
*
|
|
596
|
+
* Runs once at parse time inside `loadMesh`, before `mergePolygons`. Zero
|
|
597
|
+
* runtime cost. Conservative by design — false negatives (failing to cull
|
|
598
|
+
* a truly hidden poly) are safe; false positives would be a visual bug.
|
|
599
|
+
*/
|
|
600
|
+
|
|
601
|
+
interface CullInteriorOptions {
|
|
602
|
+
/** Hemisphere ray samples per polygon. Higher = fewer false positives, slower. Default 12. */
|
|
603
|
+
samples?: number;
|
|
604
|
+
}
|
|
605
|
+
declare function cullInteriorPolygons(polygons: Polygon[], options?: CullInteriorOptions): Polygon[];
|
|
606
|
+
|
|
607
|
+
declare const CAMERA_BACKFACE_CULL_EPS = 0.00001;
|
|
608
|
+
declare const VOXEL_CAMERA_CULL_AXIS_EPS = 0.001;
|
|
609
|
+
declare const VOXEL_CAMERA_CULL_NORMAL_LIMIT = 6;
|
|
610
|
+
interface CameraCullRotation {
|
|
611
|
+
rotX: number;
|
|
612
|
+
rotY: number;
|
|
613
|
+
meshRotation?: Vec3;
|
|
614
|
+
}
|
|
615
|
+
interface CameraCullNormalGroup {
|
|
616
|
+
key: string;
|
|
617
|
+
normal: Vec3;
|
|
618
|
+
}
|
|
619
|
+
declare function polygonCssSurfaceNormal(polygon: Polygon): Vec3 | null;
|
|
620
|
+
declare function cameraFacingDepth(normal: Vec3, rotation: CameraCullRotation): number;
|
|
621
|
+
declare function normalFacesCamera(normal: Vec3, rotation: CameraCullRotation, depthThreshold?: number): boolean;
|
|
622
|
+
declare function polygonFacesCamera(polygon: Polygon, rotation: CameraCullRotation, depthThreshold?: number): boolean;
|
|
623
|
+
declare function cameraCullNormalKey(normal: Vec3): string;
|
|
624
|
+
declare function cameraCullNormalGroups(normals: Iterable<Vec3 | null | undefined>): CameraCullNormalGroup[];
|
|
625
|
+
declare function cameraCullNormalGroupsFromPolygons(polygons: readonly Polygon[]): CameraCullNormalGroup[];
|
|
626
|
+
declare function isAxisAlignedSurfaceNormal(normal: Vec3, axisEpsilon?: number): boolean;
|
|
627
|
+
declare function isVoxelCameraCullableNormalGroups(groups: readonly CameraCullNormalGroup[]): boolean;
|
|
628
|
+
declare function cameraCullVisibleSignature(groups: readonly CameraCullNormalGroup[], rotation: CameraCullRotation, depthThreshold?: number): string;
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Geometry for the three.js-style debug axes gizmo: three thin colored
|
|
632
|
+
* cuboids stretching along world-X, world-Y and world-Z. Mirrors the
|
|
633
|
+
* convention `red=X, green=Y, blue=Z`.
|
|
634
|
+
*
|
|
635
|
+
* Returned polygons are in the standard polycss world-space convention
|
|
636
|
+
* (`+X right, +Y forward, +Z up`). Wrap with the framework's PolyMesh /
|
|
637
|
+
* PolyScene equivalent to render.
|
|
638
|
+
*/
|
|
639
|
+
|
|
640
|
+
interface AxesHelperOptions {
|
|
641
|
+
/** Length of each axis bar in world units. */
|
|
642
|
+
size?: number;
|
|
643
|
+
/** Bar cross-section width as a fraction of `size`. */
|
|
644
|
+
thickness?: number;
|
|
645
|
+
/** When true, also draws bars in the −X / −Y / −Z direction. */
|
|
646
|
+
negative?: boolean;
|
|
647
|
+
/** X-axis bar color. */
|
|
648
|
+
xColor?: string;
|
|
649
|
+
/** Y-axis bar color. */
|
|
650
|
+
yColor?: string;
|
|
651
|
+
/** Z-axis bar color. */
|
|
652
|
+
zColor?: string;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Build the polygons for an AxesHelper-style gizmo. Three thin cuboids,
|
|
656
|
+
* one per world axis. Defaults match `<PolyAxesHelper>` in the framework
|
|
657
|
+
* packages.
|
|
658
|
+
*/
|
|
659
|
+
declare function axesHelperPolygons(options?: AxesHelperOptions): Polygon[];
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Geometry for a single 3D arrow: a thin axis-aligned cuboid shaft
|
|
663
|
+
* stretching from the origin along one signed axis, capped with a
|
|
664
|
+
* 4-sided pyramid head pointing further in that direction. Used as the
|
|
665
|
+
* drag handle for `<TransformControls>` — same primitive recipe as
|
|
666
|
+
* `axesHelperPolygons`, plus an arrowhead.
|
|
667
|
+
*
|
|
668
|
+
* Returned polygons are in standard polycss world space and intended
|
|
669
|
+
* to be wrapped in the framework's PolyMesh equivalent for rendering.
|
|
670
|
+
*/
|
|
671
|
+
|
|
672
|
+
interface ArrowPolygonsOptions {
|
|
673
|
+
/** World axis the arrow extends along: 0=X, 1=Y, 2=Z. */
|
|
674
|
+
axis: 0 | 1 | 2;
|
|
675
|
+
/** Direction along the axis: +1 (positive) or -1 (negative). Default +1. */
|
|
676
|
+
sign?: 1 | -1;
|
|
677
|
+
/** Length of the rectangular shaft along the axis. */
|
|
678
|
+
shaftLength?: number;
|
|
679
|
+
/** Half cross-section of the shaft (perpendicular to the axis). */
|
|
680
|
+
shaftHalfThickness?: number;
|
|
681
|
+
/** Length of the pyramid head along the axis (extends past the shaft). */
|
|
682
|
+
headLength?: number;
|
|
683
|
+
/** Half-extent of the pyramid base. */
|
|
684
|
+
headHalfThickness?: number;
|
|
685
|
+
/** Fill color. */
|
|
686
|
+
color?: string;
|
|
687
|
+
/** Emit the rectangular shaft polygons. Default `true`. Set `false` to
|
|
688
|
+
* render just the pyramid head — used by transform-control gizmos to
|
|
689
|
+
* declutter back-facing axes (only the head still identifies direction
|
|
690
|
+
* while the shaft would visually overlap the front-facing arrow). */
|
|
691
|
+
shaft?: boolean;
|
|
692
|
+
}
|
|
693
|
+
/** Build the polygons for one signed-axis arrow. */
|
|
694
|
+
declare function arrowPolygons(options: ArrowPolygonsOptions): Polygon[];
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Geometry for a flat ring (annulus) lying in the plane perpendicular
|
|
698
|
+
* to a chosen axis. Used as the rotation handle in
|
|
699
|
+
* `<TransformControls mode="rotate">` — three rings, one per axis,
|
|
700
|
+
* each draggable to rotate the target around that axis.
|
|
701
|
+
*
|
|
702
|
+
* The ring is a sequence of quad segments around a circle. We don't
|
|
703
|
+
* model a true torus (tube) — a flat annulus reads cleanly as a
|
|
704
|
+
* "rotation circle" and keeps the polygon count proportional to the
|
|
705
|
+
* `segments` knob.
|
|
706
|
+
*
|
|
707
|
+
* Returned polygons are in standard polycss world space and intended
|
|
708
|
+
* to be wrapped in the framework's PolyMesh equivalent for rendering.
|
|
709
|
+
*/
|
|
710
|
+
|
|
711
|
+
interface RingPolygonsOptions {
|
|
712
|
+
/** World axis the ring is perpendicular to: 0=X, 1=Y, 2=Z. The ring
|
|
713
|
+
* itself lies in the plane spanned by the other two axes. */
|
|
714
|
+
axis: 0 | 1 | 2;
|
|
715
|
+
/** Mid-radius of the ring (distance from center to the middle of
|
|
716
|
+
* the annulus band). */
|
|
717
|
+
radius: number;
|
|
718
|
+
/** Half-width of the annulus band — the ring spans `radius - half`
|
|
719
|
+
* to `radius + half`. */
|
|
720
|
+
halfThickness?: number;
|
|
721
|
+
/** Number of quad segments around the circle. Higher = smoother. */
|
|
722
|
+
segments?: number;
|
|
723
|
+
/** Fill color. */
|
|
724
|
+
color?: string;
|
|
725
|
+
}
|
|
726
|
+
/** Build the polygons for a flat ring (annulus). */
|
|
727
|
+
declare function ringPolygons(options: RingPolygonsOptions): Polygon[];
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* One square quad covering the bounding box of a ring (annulus) in the
|
|
731
|
+
* plane perpendicular to a chosen axis. Used by `<PolyTransformControls
|
|
732
|
+
* mode="rotate">` together with a CSS `mask: radial-gradient(...)` to
|
|
733
|
+
* render the visible donut, replacing the segmented quad-strip approach
|
|
734
|
+
* of `ringPolygons` with a single DOM element per ring.
|
|
735
|
+
*
|
|
736
|
+
* The caller is responsible for applying the mask CSS and using a donut-
|
|
737
|
+
* shaped hit-test (the quad's bounding rect alone would over-hit the
|
|
738
|
+
* inner hole). The recommended setup is to set the CSS custom property
|
|
739
|
+
* `--ring-inner-ratio` on the mesh element so the mask scales with the
|
|
740
|
+
* caller's chosen thickness ratio.
|
|
741
|
+
*/
|
|
742
|
+
|
|
743
|
+
interface RingQuadPolygonsOptions {
|
|
744
|
+
/** World axis the ring is perpendicular to: 0=X, 1=Y, 2=Z. The quad
|
|
745
|
+
* lies in the plane spanned by the other two axes. */
|
|
746
|
+
axis: 0 | 1 | 2;
|
|
747
|
+
/** Outer radius of the ring. The quad spans ±outerRadius in both
|
|
748
|
+
* in-plane axes. */
|
|
749
|
+
outerRadius: number;
|
|
750
|
+
/** Fill color. */
|
|
751
|
+
color?: string;
|
|
752
|
+
}
|
|
753
|
+
/** Build a single 4-vertex polygon (a square) bounding the ring's outer
|
|
754
|
+
* circle. CSS `mask` is expected to clip this to the donut shape at
|
|
755
|
+
* render time. */
|
|
756
|
+
declare function ringQuadPolygons(options: RingQuadPolygonsOptions): Polygon[];
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* A flat quad on one of the three axis-aligned planes, offset diagonally
|
|
760
|
+
* along the two in-plane axes. Used as a planar drag handle in
|
|
761
|
+
* `<PolyTransformControls>` — clicking and dragging this handle moves the
|
|
762
|
+
* attached mesh along two axes simultaneously (XY, XZ, or YZ), instead of
|
|
763
|
+
* the single-axis motion the arrow shafts provide.
|
|
764
|
+
*
|
|
765
|
+
* The polygon lives in standard polycss world space; wrap it in the
|
|
766
|
+
* framework's PolyMesh equivalent for rendering.
|
|
767
|
+
*/
|
|
768
|
+
|
|
769
|
+
interface PlanePolygonsOptions {
|
|
770
|
+
/** Axis perpendicular to the plane: 0 = YZ plane, 1 = XZ plane,
|
|
771
|
+
* 2 = XY plane. The quad lies on the OTHER two axes. */
|
|
772
|
+
axis: 0 | 1 | 2;
|
|
773
|
+
/** Half-extent of the quad along each in-plane axis. Default `0.4`. */
|
|
774
|
+
size?: number;
|
|
775
|
+
/** Center of the quad along the two in-plane axes. Pass a single number
|
|
776
|
+
* to use the same offset on both (positive places the handle in the
|
|
777
|
+
* +A/+B corner). Pass `[offsetA, offsetB]` to control each
|
|
778
|
+
* independently — sign flips move the handle to a different octant.
|
|
779
|
+
* `A = (axis+1)%3`, `B = (axis+2)%3`. Default `size * 2`. */
|
|
780
|
+
offset?: number | [number, number];
|
|
781
|
+
/** Position along the perpendicular axis. Default `0` (on the plane). */
|
|
782
|
+
along?: number;
|
|
783
|
+
/** Fill color. */
|
|
784
|
+
color?: string;
|
|
785
|
+
}
|
|
786
|
+
/** Build the polygons for one axis-aligned planar drag handle. */
|
|
787
|
+
declare function planePolygons(options: PlanePolygonsOptions): Polygon[];
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Geometry for a small solid-color octahedron — the marker shape used by
|
|
791
|
+
* `GlyphcssDirectionalLightHelper` to indicate where a directional light is
|
|
792
|
+
* shining from. Eight CCW-from-outside triangular faces, vertices at
|
|
793
|
+
* `center ± (size, 0, 0)` etc.
|
|
794
|
+
*/
|
|
795
|
+
|
|
796
|
+
interface OctahedronPolygonsOptions {
|
|
797
|
+
/** Center of the octahedron in world space. */
|
|
798
|
+
center: Vec3;
|
|
799
|
+
/** Half-extent (distance from center to each pole vertex). */
|
|
800
|
+
size: number;
|
|
801
|
+
/** Fill color applied to all eight faces. */
|
|
802
|
+
color?: string;
|
|
803
|
+
}
|
|
804
|
+
declare function octahedronPolygons(options: OctahedronPolygonsOptions): Polygon[];
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Unified parser return type. All polygon-emitting parsers (parseObj,
|
|
808
|
+
* parseGltf, the loadMesh dispatcher) return this exact shape.
|
|
809
|
+
*
|
|
810
|
+
* The asymmetric helper `parseMtl` returns its own `MtlParseResult` (it
|
|
811
|
+
* emits materials, not polygons) — see parseMtl.ts for the rationale.
|
|
812
|
+
*
|
|
813
|
+
* Lifecycle contract: callers MUST call `dispose()` when the result is no
|
|
814
|
+
* longer needed. Idempotent — safe to call on unmount even if `objectUrls`
|
|
815
|
+
* is empty (e.g. `parseObj`, where it's a no-op).
|
|
816
|
+
*/
|
|
817
|
+
|
|
818
|
+
interface ParseAnimationClip {
|
|
819
|
+
/** Stable numeric index in the source file's animation array. */
|
|
820
|
+
index: number;
|
|
821
|
+
/** Human-readable clip name. Falls back to `animation_N` when omitted. */
|
|
822
|
+
name: string;
|
|
823
|
+
/** Clip duration in seconds, derived from its sampler input accessors. */
|
|
824
|
+
duration: number;
|
|
825
|
+
/** Number of glTF animation channels in the clip. */
|
|
826
|
+
channelCount: number;
|
|
827
|
+
}
|
|
828
|
+
interface ParseAnimationController {
|
|
829
|
+
/** Animation clips exposed by the parsed mesh. Empty when none are usable. */
|
|
830
|
+
clips: ParseAnimationClip[];
|
|
831
|
+
/**
|
|
832
|
+
* Sample a clip at `timeSeconds` and return a fresh polygon list.
|
|
833
|
+
* `clip` accepts either the clip index or its name. Time wraps by duration.
|
|
834
|
+
*/
|
|
835
|
+
sample: (clip: number | string, timeSeconds: number) => Polygon[];
|
|
836
|
+
}
|
|
837
|
+
interface ParseResult {
|
|
838
|
+
/** The mesh, as a flat polygon list. Already vertex-permuted to polycss space. */
|
|
839
|
+
polygons: Polygon[];
|
|
840
|
+
/** Optional animation sampler for formats that carry timeline data. */
|
|
841
|
+
animation?: ParseAnimationController;
|
|
842
|
+
/**
|
|
843
|
+
* Blob/object URLs minted during parse (e.g. embedded GLB images). Pass-by-
|
|
844
|
+
* reference — the same array is exposed on the result for visibility, and
|
|
845
|
+
* `dispose()` revokes each one. Do NOT mutate this array externally.
|
|
846
|
+
*/
|
|
847
|
+
objectUrls: string[];
|
|
848
|
+
/**
|
|
849
|
+
* Idempotent — revokes object URLs. Safe to call on unmount, safe to call
|
|
850
|
+
* twice. Parsers without minted URLs (parseObj, parseMtl) supply a no-op.
|
|
851
|
+
*/
|
|
852
|
+
dispose: () => void;
|
|
853
|
+
/**
|
|
854
|
+
* Non-fatal warnings raised during parse. Empty for parsers that don't
|
|
855
|
+
* have a warning channel; populated when downstream `normalizePolygons`
|
|
856
|
+
* is invoked through the high-level pipeline.
|
|
857
|
+
*/
|
|
858
|
+
warnings: string[];
|
|
859
|
+
/** Optional format-specific metadata. */
|
|
860
|
+
metadata?: {
|
|
861
|
+
/** Triangle count after fan-triangulation (parseObj) or post-triangulation (parseGltf). */
|
|
862
|
+
triangleCount?: number;
|
|
863
|
+
/** Mesh names from the file (for glTF, from doc.meshes[].name). */
|
|
864
|
+
meshes?: string[];
|
|
865
|
+
/** Material names (in first-seen order). */
|
|
866
|
+
materials?: string[];
|
|
867
|
+
/** Animation clips from the file, mirrored from `animation.clips`. */
|
|
868
|
+
animations?: ParseAnimationClip[];
|
|
869
|
+
/** Source file size in bytes (for diagnostics). */
|
|
870
|
+
sourceBytes?: number;
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* GlyphcssAnimationMixer — three.js-shaped animation API for glyphcss.
|
|
876
|
+
*
|
|
877
|
+
* Mirrors three.js's AnimationMixer + AnimationAction surface closely enough
|
|
878
|
+
* that users familiar with drei's `useAnimations` can migrate without friction.
|
|
879
|
+
*
|
|
880
|
+
* Loop mode constants match three.js numeric values exactly:
|
|
881
|
+
* LoopOnce = 2200, LoopRepeat = 2201, LoopPingPong = 2202
|
|
882
|
+
*/
|
|
883
|
+
|
|
884
|
+
declare const LoopOnce: 2200;
|
|
885
|
+
declare const LoopRepeat: 2201;
|
|
886
|
+
declare const LoopPingPong: 2202;
|
|
887
|
+
type LoopMode = typeof LoopOnce | typeof LoopRepeat | typeof LoopPingPong;
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Minimal target interface the mixer requires. `GlyphcssMeshHandle` from both
|
|
891
|
+
* the glyphcss vanilla API and the React/Vue frameworks satisfies this
|
|
892
|
+
* structurally — no import needed.
|
|
893
|
+
*/
|
|
894
|
+
interface GlyphcssAnimationTarget {
|
|
895
|
+
setPolygons(polygons: Polygon[]): void;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Per-clip playback action. Mirrors three.js `AnimationAction` method surface.
|
|
899
|
+
* All mutating methods return `this` for chaining.
|
|
900
|
+
*/
|
|
901
|
+
interface GlyphcssAnimationAction {
|
|
902
|
+
/** Start playing (sets weight=1, resets time if not already playing). */
|
|
903
|
+
play(): GlyphcssAnimationAction;
|
|
904
|
+
/** Stop playing and reset time to 0. */
|
|
905
|
+
stop(): GlyphcssAnimationAction;
|
|
906
|
+
/** Reset time to 0 without stopping. */
|
|
907
|
+
reset(): GlyphcssAnimationAction;
|
|
908
|
+
/** Fade weight from 0 to 1 over `durationSeconds`. */
|
|
909
|
+
fadeIn(durationSeconds: number): GlyphcssAnimationAction;
|
|
910
|
+
/** Fade weight from current to 0 over `durationSeconds`. */
|
|
911
|
+
fadeOut(durationSeconds: number): GlyphcssAnimationAction;
|
|
912
|
+
/**
|
|
913
|
+
* Cross-fade from this action to `target` over `durationSeconds`.
|
|
914
|
+
* Fades this out and target in simultaneously.
|
|
915
|
+
*/
|
|
916
|
+
crossFadeTo(target: GlyphcssAnimationAction, durationSeconds: number): GlyphcssAnimationAction;
|
|
917
|
+
/**
|
|
918
|
+
* Cross-fade from `from` into this action over `durationSeconds`.
|
|
919
|
+
* Sugar for `from.fadeOut(d); this.fadeIn(d)`.
|
|
920
|
+
*/
|
|
921
|
+
crossFadeFrom(from: GlyphcssAnimationAction, durationSeconds: number): GlyphcssAnimationAction;
|
|
922
|
+
/** Set loop mode and repetition count. */
|
|
923
|
+
setLoop(mode: LoopMode, repetitions: number): GlyphcssAnimationAction;
|
|
924
|
+
/** Override the effective time scale. */
|
|
925
|
+
setEffectiveTimeScale(scale: number): GlyphcssAnimationAction;
|
|
926
|
+
/** Override the effective weight. */
|
|
927
|
+
setEffectiveWeight(weight: number): GlyphcssAnimationAction;
|
|
928
|
+
/** When true, the action freezes on the last frame after finishing. */
|
|
929
|
+
clampWhenFinished: boolean;
|
|
930
|
+
/** Playback speed multiplier. Default 1. */
|
|
931
|
+
timeScale: number;
|
|
932
|
+
/** Blend weight [0, 1]. Default 1. */
|
|
933
|
+
weight: number;
|
|
934
|
+
/** Current playback position in seconds. */
|
|
935
|
+
time: number;
|
|
936
|
+
/**
|
|
937
|
+
* When false, the action contributes 0 weight to the blend even if
|
|
938
|
+
* `weight > 0`. Time still advances. Default true.
|
|
939
|
+
*/
|
|
940
|
+
enabled: boolean;
|
|
941
|
+
/**
|
|
942
|
+
* When true, time does NOT advance on `mixer.update()` but the action
|
|
943
|
+
* remains active and contributes its current weight to the blend. Default false.
|
|
944
|
+
*/
|
|
945
|
+
paused: boolean;
|
|
946
|
+
/** Whether the action is currently playing. */
|
|
947
|
+
readonly isRunning: boolean;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Drives one or more `GlyphcssAnimationAction`s against a single mesh target.
|
|
951
|
+
* Mirrors the three.js `AnimationMixer` API.
|
|
952
|
+
*/
|
|
953
|
+
interface GlyphcssAnimationMixer {
|
|
954
|
+
/**
|
|
955
|
+
* Return the action for a clip (by index or name). Creates the action if it
|
|
956
|
+
* doesn't exist yet (lazy instantiation, same as three.js).
|
|
957
|
+
*/
|
|
958
|
+
clipAction(clip: number | string): GlyphcssAnimationAction;
|
|
959
|
+
/**
|
|
960
|
+
* Return an existing action without creating one. Returns null if the
|
|
961
|
+
* action hasn't been instantiated yet.
|
|
962
|
+
*/
|
|
963
|
+
existingAction(clip: number | string): GlyphcssAnimationAction | null;
|
|
964
|
+
/**
|
|
965
|
+
* Advance all active actions by `deltaSeconds` and apply the resulting
|
|
966
|
+
* polygon frame to the root target. Call this once per animation frame.
|
|
967
|
+
*/
|
|
968
|
+
update(deltaSeconds: number): void;
|
|
969
|
+
/** Stop all active actions. */
|
|
970
|
+
stopAllAction(): void;
|
|
971
|
+
/** Remove a cached action for `clip`. */
|
|
972
|
+
uncacheClip(clip: number | string): void;
|
|
973
|
+
/** Remove all cached actions for this mixer's root. */
|
|
974
|
+
uncacheRoot(): void;
|
|
975
|
+
}
|
|
976
|
+
declare function createGlyphcssAnimationMixer(root: GlyphcssAnimationTarget, controller: ParseAnimationController): GlyphcssAnimationMixer;
|
|
977
|
+
|
|
978
|
+
interface ObjParseOptions {
|
|
979
|
+
/**
|
|
980
|
+
* Largest mesh extent (in scene-space units). The mesh is uniformly
|
|
981
|
+
* scaled so its longest bbox dimension equals this. Default: 60.
|
|
982
|
+
*/
|
|
983
|
+
targetSize?: number;
|
|
984
|
+
/**
|
|
985
|
+
* Padding added to the bbox of every emitted polygon so they don't land
|
|
986
|
+
* at coordinate "0". Default: 1.
|
|
987
|
+
*/
|
|
988
|
+
gridShift?: number;
|
|
989
|
+
/**
|
|
990
|
+
* Color used for faces that have no `usemtl` in scope, or whose material
|
|
991
|
+
* name doesn't resolve via `materialColors`. Default: "#888888".
|
|
992
|
+
*/
|
|
993
|
+
defaultColor?: string;
|
|
994
|
+
/**
|
|
995
|
+
* Override map: material name → CSS color string. Falls back to:
|
|
996
|
+
* 1. The material name interpreted as a 6-char hex (e.g. "FF9800" → "#FF9800"),
|
|
997
|
+
* 2. Otherwise a slot from `palette` indexed by first-seen material order,
|
|
998
|
+
* 3. Otherwise `defaultColor`.
|
|
999
|
+
*/
|
|
1000
|
+
materialColors?: Record<string, string>;
|
|
1001
|
+
/**
|
|
1002
|
+
* Optional map: material name → texture URL. When set, every triangle
|
|
1003
|
+
* emitted under that material gets `texture` populated. The renderer
|
|
1004
|
+
* stamps the image across the triangle's local 2D plane.
|
|
1005
|
+
*/
|
|
1006
|
+
materialTextures?: Record<string, string>;
|
|
1007
|
+
/**
|
|
1008
|
+
* Palette used to assign colors to materials whose names aren't hex.
|
|
1009
|
+
* Each new non-hex material name takes the next palette slot.
|
|
1010
|
+
*/
|
|
1011
|
+
palette?: string[];
|
|
1012
|
+
/**
|
|
1013
|
+
* Names of `o <name>` objects to KEEP. When set, faces outside these
|
|
1014
|
+
* objects are dropped.
|
|
1015
|
+
*/
|
|
1016
|
+
includeObjects?: string[];
|
|
1017
|
+
/**
|
|
1018
|
+
* Names of `o <name>` objects to DROP. Applied after `includeObjects`.
|
|
1019
|
+
* Faces with no enclosing `o` line are kept unless `includeObjects` is set.
|
|
1020
|
+
*/
|
|
1021
|
+
excludeObjects?: string[];
|
|
1022
|
+
}
|
|
1023
|
+
declare function parseObj(text: string, options?: ObjParseOptions): ParseResult;
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Wavefront `.mtl` material file parser. Companion to parseObj — reads the
|
|
1027
|
+
* material library that ships next to a `.obj` and returns per-material
|
|
1028
|
+
* diffuse color (`Kd`) and optional diffuse texture map path (`map_Kd`).
|
|
1029
|
+
*
|
|
1030
|
+
* Usage:
|
|
1031
|
+
* const mtl = await fetch("/foo.mtl").then(r => r.text());
|
|
1032
|
+
* const { colors, textures } = parseMtl(mtl);
|
|
1033
|
+
* const obj = await fetch("/foo.obj").then(r => r.text());
|
|
1034
|
+
* const { polygons } = parseObj(obj, { materialColors: colors, materialTextures: textures });
|
|
1035
|
+
*
|
|
1036
|
+
* Texture paths are returned exactly as written in the .mtl — relative paths,
|
|
1037
|
+
* Windows backslashes etc. are not normalized. Callers are expected to
|
|
1038
|
+
* resolve them against the .mtl's base URL.
|
|
1039
|
+
*
|
|
1040
|
+
* NOTE: parseMtl intentionally returns its own `MtlParseResult` shape
|
|
1041
|
+
* (NOT the unified `ParseResult`). It's an asymmetric helper — it emits
|
|
1042
|
+
* materials, not polygons — and forcing it into ParseResult would mean
|
|
1043
|
+
* an empty `polygons[]` and a misleading `dispose()`.
|
|
1044
|
+
*/
|
|
1045
|
+
interface MtlParseResult {
|
|
1046
|
+
/** Material name → CSS hex color (from `Kd r g b`). */
|
|
1047
|
+
colors: Record<string, string>;
|
|
1048
|
+
/** Material name → texture path (from `map_Kd <path>`). Path is unresolved. */
|
|
1049
|
+
textures: Record<string, string>;
|
|
1050
|
+
}
|
|
1051
|
+
declare function parseMtl(text: string): MtlParseResult;
|
|
1052
|
+
|
|
1053
|
+
interface GltfParseOptions {
|
|
1054
|
+
/** Largest mesh extent (units). Mesh is uniformly scaled to fit. Default 60. */
|
|
1055
|
+
targetSize?: number;
|
|
1056
|
+
/** Padding offset (avoids coordinate "0"). Default 1. */
|
|
1057
|
+
gridShift?: number;
|
|
1058
|
+
/** Color used when a primitive has no material or no baseColorFactor. */
|
|
1059
|
+
defaultColor?: string;
|
|
1060
|
+
/**
|
|
1061
|
+
* Override map: glTF material name → CSS color string. Falls back to the
|
|
1062
|
+
* material's `pbrMetallicRoughness.baseColorFactor` if not in this map.
|
|
1063
|
+
*/
|
|
1064
|
+
materialColors?: Record<string, string>;
|
|
1065
|
+
/**
|
|
1066
|
+
* Which axis is "up" in the source mesh.
|
|
1067
|
+
* - "y" (default, glTF spec): cyclic permutation (x,y,z) → (z,x,y) so
|
|
1068
|
+
* +Y ends up on polycss's +Z (elevation).
|
|
1069
|
+
* - "z" (Blender-style, FBX2glTF often emits this): identity, no swap.
|
|
1070
|
+
* Pick "z" if the model lands on its side / lies down instead of
|
|
1071
|
+
* standing.
|
|
1072
|
+
*/
|
|
1073
|
+
upAxis?: "y" | "z";
|
|
1074
|
+
/**
|
|
1075
|
+
* For .gltf (non-binary) — resolve a glTF buffer URI to its bytes. The
|
|
1076
|
+
* built-in parser handles GLB binary chunks natively; .gltf files with
|
|
1077
|
+
* external .bin files need this.
|
|
1078
|
+
*/
|
|
1079
|
+
resolveBuffer?: (uri: string) => Promise<Uint8Array> | Uint8Array;
|
|
1080
|
+
/**
|
|
1081
|
+
* Base URL the source file lives at. Used to resolve external image URIs
|
|
1082
|
+
* (`doc.images[i].uri = "Textures/foo.png"`) against the GLB/glTF's
|
|
1083
|
+
* location. Without this, relative URIs would resolve against the page,
|
|
1084
|
+
* which 404s. Pass the same URL you fetched the file from.
|
|
1085
|
+
*/
|
|
1086
|
+
baseUrl?: string;
|
|
1087
|
+
}
|
|
1088
|
+
declare function parseGltf(input: ArrayBuffer | Uint8Array, options?: GltfParseOptions): ParseResult;
|
|
1089
|
+
|
|
1090
|
+
interface SolidTextureSampleOptions {
|
|
1091
|
+
/**
|
|
1092
|
+
* Set false to keep every textured polygon texture-backed. Defaults to true
|
|
1093
|
+
* when a browser-like Image + canvas environment is available.
|
|
1094
|
+
*/
|
|
1095
|
+
enabled?: boolean;
|
|
1096
|
+
/** Per-channel tolerance for declaring sampled texels uniform. Default 2. */
|
|
1097
|
+
colorTolerance?: number;
|
|
1098
|
+
/** Skip decoding very large textures for this optimization. Default 16 MP. */
|
|
1099
|
+
maxTexturePixels?: number;
|
|
1100
|
+
}
|
|
1101
|
+
declare function bakeSolidTextureSampledPolygons(polygons: Polygon[], options?: SolidTextureSampleOptions): Promise<Polygon[]>;
|
|
1102
|
+
declare function bakeSolidTextureSamples(result: ParseResult, options?: SolidTextureSampleOptions): Promise<ParseResult>;
|
|
1103
|
+
|
|
1104
|
+
interface VoxParseOptions {
|
|
1105
|
+
/**
|
|
1106
|
+
* Largest mesh extent (in scene-space units). The mesh is uniformly
|
|
1107
|
+
* scaled so its longest bbox dimension equals this. Default: 60.
|
|
1108
|
+
*/
|
|
1109
|
+
targetSize?: number;
|
|
1110
|
+
/**
|
|
1111
|
+
* Per-coordinate offset added after scaling. Keeps coordinates away from
|
|
1112
|
+
* zero (matching OBJ/glTF parsers). Default: 0 — vox already starts at
|
|
1113
|
+
* non-negative integers so zero makes sensible default.
|
|
1114
|
+
*/
|
|
1115
|
+
gridShift?: number;
|
|
1116
|
+
}
|
|
1117
|
+
declare function parseVox(buffer: ArrayBuffer, options?: VoxParseOptions): ParseResult;
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* loadMesh — high-level fetch+parse dispatcher. Picks the parser by file
|
|
1121
|
+
* extension, fetches the URL, runs the parser, returns the unified
|
|
1122
|
+
* `ParseResult`.
|
|
1123
|
+
*
|
|
1124
|
+
* Supported:
|
|
1125
|
+
* - `.obj` → text fetch + `parseObj`
|
|
1126
|
+
* - `.glb` → ArrayBuffer fetch + `parseGltf`
|
|
1127
|
+
* - `.gltf` → ArrayBuffer fetch + `parseGltf` (caller may pass `baseUrl`)
|
|
1128
|
+
* - `.vox` → ArrayBuffer fetch + `parseVox`
|
|
1129
|
+
*
|
|
1130
|
+
* `.mtl` is rejected — it's a material file, not a mesh. Use `parseMtl`
|
|
1131
|
+
* directly if you want to read materials.
|
|
1132
|
+
*
|
|
1133
|
+
* Other extensions throw. Future formats (STL, PLY) plug in here.
|
|
1134
|
+
*/
|
|
1135
|
+
|
|
1136
|
+
interface LoadMeshOptions {
|
|
1137
|
+
/**
|
|
1138
|
+
* Base URL for resolving relative texture/buffer URIs inside the mesh
|
|
1139
|
+
* (passed through to `parseGltf` for embedded image extraction). When
|
|
1140
|
+
* omitted, the URL passed to `loadMesh` is used as the base.
|
|
1141
|
+
*/
|
|
1142
|
+
baseUrl?: string;
|
|
1143
|
+
/**
|
|
1144
|
+
* Companion `.mtl` URL for OBJ files. When set, loadMesh fetches the
|
|
1145
|
+
* mtl, runs `parseMtl`, and threads `materialColors` + `materialTextures`
|
|
1146
|
+
* into `parseObj` — so the OBJ renders with its authored materials.
|
|
1147
|
+
* Texture paths inside the mtl are resolved against the mtl URL.
|
|
1148
|
+
* Ignored for `.glb` / `.gltf` (they carry materials inline).
|
|
1149
|
+
*/
|
|
1150
|
+
mtlUrl?: string;
|
|
1151
|
+
/** Forwarded to `parseObj` (merged with materials derived from `mtlUrl`). */
|
|
1152
|
+
objOptions?: ObjParseOptions;
|
|
1153
|
+
/** Forwarded to `parseGltf`. */
|
|
1154
|
+
gltfOptions?: GltfParseOptions;
|
|
1155
|
+
/** Forwarded to `parseVox`. */
|
|
1156
|
+
voxOptions?: VoxParseOptions;
|
|
1157
|
+
/**
|
|
1158
|
+
* Converts texture-backed faces whose UV samples are a uniform color into
|
|
1159
|
+
* solid-color polygons before culling/merging. This avoids atlas sprites for
|
|
1160
|
+
* low-poly models that use texture atlases as color swatches.
|
|
1161
|
+
*/
|
|
1162
|
+
solidTextureSamples?: boolean | SolidTextureSampleOptions;
|
|
1163
|
+
/**
|
|
1164
|
+
* Mesh optimization intent. Defaults to "lossy"; set "lossless" to keep
|
|
1165
|
+
* exact planar candidates only.
|
|
1166
|
+
*/
|
|
1167
|
+
meshResolution?: MeshResolution;
|
|
1168
|
+
}
|
|
1169
|
+
declare function loadMesh(url: string, options?: LoadMeshOptions): Promise<ParseResult>;
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Perspective projection identical to RadiantHero's: `persp = 4 / (z + 3)`.
|
|
1173
|
+
* Returns `[col, row, depth]` in grid space. `cellAspect` corrects for the
|
|
1174
|
+
* non-square character cell (height / width).
|
|
1175
|
+
*/
|
|
1176
|
+
declare function project(v: Vec3, cols: number, rows: number, cellAspect: number, cx?: number, cy?: number, scale?: number): [number, number, number];
|
|
1177
|
+
|
|
1178
|
+
/** Public: derive feature edges from a triangle list. `featureAngleDeg = 0` = all edges. */
|
|
1179
|
+
declare function trianglesToFeatureEdges(triangles: TextureTriangle[], featureAngleDeg?: number): WireframeEdge[];
|
|
1180
|
+
|
|
1181
|
+
export { type ApproximateMergeOptions, type ArrowPolygonsOptions, type AutoRotateConfig, type AutoRotateOption, type AxesHelperOptions, BASE_TILE, CAMERA_BACKFACE_CULL_EPS, type CameraCullNormalGroup, type CameraCullRotation, type CameraHandle, type CameraState, type CameraStyleInput, type CharRamp, type CoverPlanarPolygonsOptions, type CullInteriorOptions, DEFAULT_CAMERA_STATE, DEFAULT_PROJECTION, type DedupeOverlappingPolygonsOptions, type EdgeWeight, type GltfParseOptions, type GlyphcssAmbientLight, type GlyphcssAnimationAction, type ParseAnimationClip as GlyphcssAnimationClip, type GlyphcssAnimationMixer, type GlyphcssAnimationTarget, type GlyphcssDirectionalLight, type GridSize, type Hotspot, type HotspotCell, type LoadMeshOptions, type LoopMode, LoopOnce, LoopPingPong, LoopRepeat, type MeshResolution, type MtlParseResult, type NormalizeResult, type ObjParseOptions, type OctahedronPolygonsOptions, type OptimizeMeshPolygonsOptions, type ParseAnimationClip, type ParseAnimationController, type ParseResult, type ParsedColor, type PlanePolygonsOptions, type Polygon, type PolygonFace, QUAT_IDENTITY, type Quat, type RenderMode, type RingPolygonsOptions, type RingQuadPolygonsOptions, type SceneBbox, type SceneContext, type SceneContextBuildArgs, type SceneContextBuildResult, type SolidTextureSampleOptions, type TextureTriangle, VOXEL_CAMERA_CULL_AXIS_EPS, VOXEL_CAMERA_CULL_NORMAL_LIMIT, type Vec2, type Vec3, type VoxParseOptions, type WireframeEdge, arrowPolygons, axesHelperPolygons, bakeSolidTextureSampledPolygons, bakeSolidTextureSamples, buildSceneContext, cameraCullNormalGroups, cameraCullNormalGroupsFromPolygons, cameraCullNormalKey, cameraCullVisibleSignature, cameraFacingDepth, clampChannel, computeSceneBbox, computeShapeLighting, coverPlanarPolygons, createGlyphcssAnimationMixer, createIsometricCamera, cullInteriorPolygons, dedupeOverlappingPolygons, eulerXYZFromQuat, findOverlappingPolygonDuplicates, formatColor, inverseRotateVec3, isAxisAlignedSurfaceNormal, isVoxelCameraCullableNormalGroups, loadMesh, mergePolygons, normalFacesCamera, normalizeInvertMultiplier, normalizePolygons, octahedronPolygons, optimizeMeshPolygons, parseColor, parseGltf, parseHexColor, parseMtl, parseObj, parsePureColor, parseRgbColor, parseVox, planePolygons, polygonCssSurfaceNormal, polygonFaces, polygonFacesCamera, project, quatFromAxisAngle, quatFromEulerXYZ, quatMultiply, ringPolygons, ringQuadPolygons, rotateVec3, shadeColor, trianglesToFeatureEdges };
|