ldr 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,8 +27,15 @@ export declare class Library {
27
27
  alpha: number;
28
28
  }>;
29
29
  /** Flattened triangle mesh: positions + color code per tri (16 = inherit); edges as line segments.
30
- * `cond` stores type-5 lines as 12 floats each: endpoint0, endpoint1, control0, control1 (for future conditional-line rendering). */
31
- mesh(name: string): PartMesh;
30
+ * `cond` stores type-5 lines as 12 floats each: endpoint0, endpoint1, control0, control1.
31
+ * Default = what a renderer without !TEXMAP support draws (`0 !:` geometry skipped, FALLBACK geometry drawn).
32
+ * `{ texmap: true }` = TEXMAP-aware geometry (START..FALLBACK incl. `0 !:` lines, FALLBACK..END skipped) plus `uv` / `texture`. */
33
+ mesh(name: string, opts?: {
34
+ texmap?: boolean;
35
+ }): PartMesh;
36
+ /** Texture PNG bytes for a !TEXMAP name: an MPD `!DATA` block in `doc` first, then the LDraw search path with a
37
+ * `textures/` prefix (parts/, p/, models/, official then unofficial), then without it. Case-insensitive. */
38
+ texture(name: string, doc?: Doc): Uint8Array | null;
32
39
  private emit;
33
40
  /** Flatten a model into part instances. 24 handled at render (edge color). Alias parts resolve to canonical names. */
34
41
  flatten(doc: Doc): Instance[];
@@ -25,6 +25,7 @@ export declare class Library {
25
25
  export declare function splitModel(text: any, fallbackName?: string): {
26
26
  files: Map<string, string>;
27
27
  main: string | null;
28
+ data: Map<string, string>;
28
29
  };
29
30
  export declare function flatten(model: any, lib: any, onInstance: any): {
30
31
  missing: Map<any, any>;
package/dist/render.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as THREE from 'three';
2
+ /** `cond`: draw type-5 conditional lines when the pack carries them (pack v2, built with `cond: true`); 'idx' edges only. Default true.
3
+ * `condStuds`: also draw them on stud batches (stud silhouettes). Default true. */
2
4
  export type LdrParams = {
3
5
  pack: string;
4
6
  cullStuds: boolean;
@@ -6,6 +8,10 @@ export type LdrParams = {
6
8
  edges: boolean | 'idx' | 'ss';
7
9
  side: 'double' | 'front';
8
10
  uncertDouble?: boolean;
11
+ cond?: boolean;
12
+ condStuds?: boolean;
13
+ /** Draw textured (!TEXMAP) parts with their PNGs when the pack carries them (pack v2 "tex", `ldr pack --textures`). Default true. */
14
+ tex?: boolean;
9
15
  };
10
16
  export type Inst = {
11
17
  part: string;
@@ -21,8 +27,26 @@ export declare function loadPack(url: string): Promise<Pack>;
21
27
  /** Decode an uncompressed parts pack (4-byte LE header length, JSON header, then aligned typed-array payload). */
22
28
  export declare function decodePack(buf: ArrayBuffer): Pack;
23
29
  export declare function makeMaterial(side: THREE.Side): THREE.MeshStandardMaterial;
30
+ /** Material for textured (!TEXMAP) triangles: the LDraw colour as base, the PNG blended over it by its alpha (LDraw spec),
31
+ * and no texture outside the projection's [0,1] range. `map` should have flipY = false (v = 0 is the image's top row). */
32
+ export declare function makeTexturedMaterial(side: THREE.Side, map: THREE.Texture, wrapU?: boolean): THREE.MeshStandardMaterial;
33
+ /** Decode a pack's embedded PNG into a texture (async; the texture fills in when decoded). Needs `createImageBitmap` (browsers, workers). */
34
+ export declare function packTexture(pack: Pack, i: number): {
35
+ texture: THREE.Texture;
36
+ ready: Promise<void>;
37
+ };
24
38
  /** Edge lines for an InstancedMesh: welds + indexes `pos` (line-segment endpoints, part space) and shares the mesh's instanceMatrix buffer, so edges cost one extra draw and no extra per-instance memory. Keep `geometry.instanceCount` in sync with `im.count`. */
25
39
  export declare function createInstancedEdges(pos: ArrayLike<number>, im: THREE.InstancedMesh, material?: THREE.Material): THREE.LineSegments;
40
+ /** Expand a pack entry's conditional lines (pack v2 `condl` body / `condlu` underside) to part-space floats, stride 12:
41
+ * p0, p1, control0, control1. `which` = 'all' (default; body then underside), 'body' or 'under'.
42
+ * Returns null for v1 packs / parts without conditional lines. */
43
+ export declare function conditionalLines(pack: Pack, entry: any, which?: 'all' | 'body' | 'under'): Float32Array | null;
44
+ /** CPU mirror of the conditional-line vertex shader: true if the line p0-p1 is drawn for this model-view-projection matrix. */
45
+ export declare function conditionalLineVisible(cond: ArrayLike<number>, i: number, mvp: THREE.Matrix4): boolean;
46
+ /** Conditional (type-5) lines for an InstancedMesh, sharing its instanceMatrix buffer (one extra draw, no per-instance memory).
47
+ * `cond` is part-space stride 12 (p0, p1, c0, c1), e.g. from `conditionalLines(pack, pack.header.parts[name])`.
48
+ * GPU cost: 2 vertices x 13 floats per line (104 B/line), per part, not per instance. Keep `geometry.instanceCount` in sync with `im.count`. */
49
+ export declare function createInstancedConditionalEdges(cond: ArrayLike<number>, im: THREE.InstancedMesh, material?: THREE.Material): THREE.LineSegments;
26
50
  export declare function computeCulling(insts: Inst[], H: any): {
27
51
  studs: {
28
52
  kind: string;
@@ -38,6 +62,8 @@ export declare function buildLdrScene(insts: Inst[], pack: Pack, P: LdrParams, c
38
62
  setCull: (studs: boolean, under: boolean) => void;
39
63
  setSide: (front: boolean) => void;
40
64
  setEdges: (v: boolean) => void;
65
+ setCond: (v: boolean) => void;
41
66
  swapStuds: (alt: any) => () => void;
42
67
  box: THREE.Box3;
68
+ texturesReady: Promise<void>;
43
69
  };
package/dist/scene.d.ts CHANGED
@@ -5,6 +5,15 @@ export type ColorSource = Map<number, string | LDrawColor> | Record<number | str
5
5
  export interface LdrObjectOptions {
6
6
  /** Draw LDraw type-2 edge lines (indexed, sharing each part's instance matrices). Default true. */
7
7
  edges?: boolean;
8
+ /** Draw type-5 conditional lines (silhouettes of curved surfaces) when the pack carries them (`ldr pack --cond`, pack v2).
9
+ * Shares each part's instance matrices; follows `edges` on/off. Default true (no effect on packs without them). */
10
+ conditionalLines?: boolean;
11
+ /** Also draw conditional lines on studs (stud silhouettes). Default true. Round 17 (M4 Pro, Datsville 100k): stud lines are
12
+ * ~37% of conditional-line vertex work; turning them off saves ~3.7 ms/frame. Only read when the object is built. */
13
+ conditionalStudLines?: boolean;
14
+ /** Draw textured parts (printed / sticker parts using !TEXMAP) with their images when the pack carries them (`ldr pack --textures`).
15
+ * Default true; false draws the parts' FALLBACK geometry like a pack without textures. Only read when the object is built. */
16
+ textures?: boolean;
8
17
  /** Hide studs fully covered by a part above. Default true. */
9
18
  cullStuds?: boolean;
10
19
  /** Hide undersides (tubes) sitting fully on studs below. Default true. */
@@ -33,6 +42,11 @@ export interface LdrStats {
33
42
  undersTotal: number;
34
43
  undersHidden: number;
35
44
  buildMs: number;
45
+ /** Conditional-line batches (draw calls) created; 0 for packs without conditional lines. */
46
+ condBatches?: number;
47
+ /** Textured batches (one per part x texture) and distinct textures; 0 for packs without textures. */
48
+ texBatches?: number;
49
+ textures?: number;
36
50
  }
37
51
  export interface LdrObject {
38
52
  /** Add this to your scene. */
@@ -43,6 +57,10 @@ export interface LdrObject {
43
57
  /** Show instances up to and including `step` (null = all). Rebuilds batches; culling is recomputed for the partial model. */
44
58
  setStep(step: number | null): void;
45
59
  setEdges(on: boolean): void;
60
+ /** Resolves when the pack's textures (if any) are decoded and ready to render. Replaced by a new promise after `setStep`. */
61
+ texturesReady: Promise<void>;
62
+ /** Toggle conditional lines (only visible while edges are on). */
63
+ setConditionalLines(on: boolean): void;
46
64
  setCulling(studs: boolean, undersides: boolean): void;
47
65
  setSide(side: "front" | "double"): void;
48
66
  /** Bounding box in the object's parent space. */
package/dist/spec.d.ts CHANGED
@@ -9,7 +9,35 @@ export type BufExchg = {
9
9
  op: "STORE" | "RETRIEVE";
10
10
  buf: string;
11
11
  };
12
- export type Cmd = {
12
+ /** TEXMAP projection (LDraw !TEXMAP extension). Points are in the coordinates of the file containing the START/NEXT. */
13
+ export type TexMapMethod = "PLANAR" | "CYLINDRICAL" | "SPHERICAL";
14
+ export interface TexMap {
15
+ method: TexMapMethod;
16
+ p1: number[];
17
+ p2: number[];
18
+ p3: number[];
19
+ /** CYLINDRICAL: angular extent (degrees); SPHERICAL: horizontal extent (degrees). */
20
+ a?: number;
21
+ /** SPHERICAL: vertical extent (degrees). */
22
+ b?: number;
23
+ /** PNG name as written (resolve via `textures/<name>` then `<name>`, or an MPD `!DATA` block). */
24
+ texture: string;
25
+ glossmap?: string;
26
+ /** true for `!TEXMAP NEXT` (applies to the next type 1-5 line only). */
27
+ next?: boolean;
28
+ }
29
+ export type TexMapCmd = {
30
+ op: "START" | "NEXT" | "FALLBACK" | "END";
31
+ map?: TexMap;
32
+ };
33
+ /** Texture-mapping annotations on geometry (type 1-5) commands:
34
+ * `tex` = texture in effect for TEXMAP-aware renderers (START..FALLBACK or NEXT); `fallback` = line sits between
35
+ * FALLBACK and END, so TEXMAP-aware renderers skip it and everyone else draws it. */
36
+ export interface TexAnno {
37
+ tex?: TexMap;
38
+ fallback?: boolean;
39
+ }
40
+ export type Cmd = ({
13
41
  t: 0;
14
42
  text: string;
15
43
  meta?: string;
@@ -17,33 +45,76 @@ export type Cmd = {
17
45
  step?: boolean;
18
46
  rotstep?: RotStep;
19
47
  bufexchg?: BufExchg;
20
- } | {
48
+ /** `0 !TEXMAP ...` */
49
+ texmap?: TexMapCmd;
50
+ /** `0 !: <line>`: textured-only geometry, ignored by renderers without TEXMAP support (parsed here for those that have it). */
51
+ geom?: Cmd;
52
+ } & TexAnno) | ({
21
53
  t: 1;
22
54
  color: number;
23
55
  m: M;
24
56
  file: string;
25
- } | {
57
+ } & TexAnno) | ({
26
58
  t: 2 | 5;
27
59
  color: number;
28
60
  p: number[];
29
- } | {
61
+ } & TexAnno) | ({
30
62
  t: 3 | 4;
31
63
  color: number;
32
64
  p: number[];
33
- };
65
+ } & TexAnno);
34
66
  export interface LFile {
35
67
  name: string;
36
68
  cmds: Cmd[];
37
- movedTo?: string;
69
+ movedTo?: string; /** Every TEXMAP START/NEXT in this file, in order. */
70
+ texmaps?: TexMap[];
71
+ }
72
+ /** An MPD `0 !DATA` block (base64-decoded), e.g. an embedded PNG texture. */
73
+ export interface DataBlock {
74
+ name: string;
75
+ bytes: Uint8Array; /** "image/png" when the bytes carry the PNG signature. */
76
+ mime?: string;
38
77
  }
39
78
  export interface Doc {
40
79
  main: string;
41
- files: Map<string, LFile>;
80
+ files: Map<string, LFile>; /** MPD `!DATA` blocks keyed by normalized name. */
81
+ data?: Map<string, DataBlock>;
42
82
  }
43
83
  export declare const norm: (s: string) => string;
44
84
  /** Parse LDraw color token: decimal code, or direct color 0x2RRGGBB (and related 0xN…… forms). */
45
85
  export declare function parseColor(tok: string): number;
86
+ /** Parse the tail of `0 !TEXMAP START|NEXT <method> <params> <png> [GLOSSMAP <png>]`. Returns null when malformed. */
87
+ export declare function parseTexMap(tail: string, next?: boolean): TexMap | null;
46
88
  export declare function parse(src: string, name?: string): Doc;
89
+ /** Find an embedded texture in a parsed MPD: tries `name`, then without / with a `textures/` prefix (case-insensitive). */
90
+ export declare function resolveTexture(doc: Doc, name: string): DataBlock | null;
91
+ /**
92
+ * Texture coordinates of point `p` (same space as the map's points) per the LDraw !TEXMAP spec, with the sign and orientation
93
+ * conventions of LDView (the reference renderer the library's textured parts are checked against; the spec leaves them open).
94
+ * v = 0 is the image's top row for all three methods.
95
+ * PLANAR: u = distance from plane P1 (through p1, normal p1->p2) / |p2 - p1|, v likewise along p1->p3 (p1 = top-left corner).
96
+ * CYLINDRICAL: p1 bottom centre, p2 top centre, p3 on the bottom rim under the texture's centre. u = 0.5 + angle / a, the angle
97
+ * from p3's radial direction, right-handed about p1->p2; v = distance below the top plane / |p2 - p1| (0 at the top, 1 at the bottom;
98
+ * LDView 4.6 "cylindrical TEXMAP right side up").
99
+ * SPHERICAL: p1 centre, p2 = texture centre on the sphere, p3 fixes plane P1 (the texture's horizontal bisector). Its normal
100
+ * n = -((p1 - p2) x (p3 - p2)) is "north". u = 0.5 + longitude / a (angle within P1 from p1->p2, right-handed about n);
101
+ * v = 0.5 + latitude / b with latitude negative toward n, so north maps to the image's top. (LDView measures latitude after
102
+ * rotating the point to longitude 0, i.e. equirectangular, rather than the literal "projected to plane P2"; the official
103
+ * globe textures 61287p01..p04 are drawn for that.)
104
+ * `inside` is false when (u, v) falls outside [0, 1] (the spec leaves those points untextured: base colour shows).
105
+ * Use `texmapTriangleUV` for triangles: it unwraps angles across the seam and handles points on the axis / poles like LDView.
106
+ */
107
+ export declare function texmapUV(map: TexMap, p: number[]): {
108
+ u: number;
109
+ v: number;
110
+ inside: boolean;
111
+ };
112
+ /** (u, v) for the 3 corners of a triangle, flat [u0, v0, u1, v1, u2, v2]. Angular u is unwrapped relative to the first corner
113
+ * (so a triangle across the seam of a 360-degree map stays continuous); corners on the axis (cylindrical) or at a pole
114
+ * (spherical) take the average u of the other corners. */
115
+ export declare function texmapTriangleUV(map: TexMap, pts: number[][]): number[];
116
+ /** Transform a TexMap's points by an LDraw matrix (e.g. into part space when the START lives in a subfile). */
117
+ export declare function transformTexMap(map: TexMap, m: M): TexMap;
47
118
  export declare function mul(a: M, b: M): M;
48
119
  export declare const I: M;
49
120
  /** True if color is an LDraw direct color (0x2RRGGBB etc.), not a palette CODE. */
@@ -55,6 +126,11 @@ export interface PartMesh {
55
126
  edges: Float32Array;
56
127
  cond: Float32Array;
57
128
  nrm: Float32Array;
129
+ /** Only with `mesh(name, { texmap: true })`: per-vertex (u, v) from the active !TEXMAP (NaN where untextured). */
130
+ uv?: Float32Array;
131
+ /** Only with texmap: per-triangle index into `textures` (-1 = untextured). */
132
+ texture?: Int32Array;
133
+ textures?: string[];
58
134
  }
59
135
  export interface Instance {
60
136
  part: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldr",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "High-performance LEGO (LDraw) rendering for three.js: instanced parts packs, hidden stud/tube culling, low-detail studs, indexed edges, build steps.",
5
5
  "license": "MIT",
6
6
  "author": "Evan Bacon",