ldr 0.4.0 → 0.4.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/README.md +157 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# ldr
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Render 100,000-brick LEGO® (LDraw) models in three.js at interactive frame rates.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
5
|
+
`ldr` compiles the parts a model uses into a compact binary **parts pack** ahead of time. At runtime it draws them with GPU instancing, hidden-stud and underside culling, low-detail studs, single-sided (BFC) faces and indexed edge lines. On an Apple M4 Pro that takes Datsville (392,514 bricks) from 410 ms to 97 ms per frame, and its load time from 24.7 s to 3.4 s.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/ldr) · MIT · ESM + TypeScript · `three` is a peer dependency
|
|
8
|
+
- **Spec-compliant LDraw reader:** `.ldr`, `.mpd` and `.dat`, including MPD-embedded parts, geometry drawn directly in models, BFC, `STEP`/`ROTSTEP`, `!TEXMAP` and `!DATA`.
|
|
9
|
+
- **`createLdrObject()`** returns a `THREE.Group` you can drop into any scene, plus a build-steps API.
|
|
10
|
+
- **`ldr pack` CLI and `buildPack()`** build parts packs from your LDraw library, compressed with zstd or gzip for serving.
|
|
11
|
+
- **Tree-shakeable** (`sideEffects: false`). The browser entry is `ldr`; the filesystem tools are in `ldr/node`.
|
|
9
12
|
|
|
10
13
|
## Install
|
|
11
14
|
|
|
@@ -13,15 +16,41 @@ High-performance LEGO® (LDraw) rendering for [three.js](https://threejs.org). R
|
|
|
13
16
|
bun add ldr three # or: npm i ldr three
|
|
14
17
|
```
|
|
15
18
|
|
|
19
|
+
You also need the LDraw parts library, which is not bundled, to *build* packs. Download `complete.zip` from <https://library.ldraw.org> and unzip it to an `ldraw/` folder containing `LDConfig.ldr`, `parts/` and `p/`. Apps only need the pack at runtime, not the library.
|
|
20
|
+
|
|
16
21
|
## Quickstart
|
|
17
22
|
|
|
18
|
-
1.
|
|
23
|
+
### 1. Build a parts pack (CLI, once at build time)
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
26
|
bunx ldr pack --ldraw ./ldraw -o public/car.pack.zst models/car.ldr
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
```
|
|
30
|
+
ldr pack --ldraw <dir> -o <out.bin|.zst|.gz> [model.ldr ...] [--part 3001.dat ...] [--all-parts]
|
|
31
|
+
[--studs low|full] [--prims full|low] [--no-weld] [--no-colors]
|
|
32
|
+
[--cond] [--textures] [--no-loose-geometry]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The pack holds only the parts your models reference, plus the LDConfig colour table. A `.zst` or `.gz` extension compresses the output.
|
|
36
|
+
|
|
37
|
+
### 2. …or build it from code (Node / Bun)
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { buildPack } from "ldr/node";
|
|
41
|
+
|
|
42
|
+
const { data, parts, missing } = buildPack({
|
|
43
|
+
ldrawRoot: "./ldraw",
|
|
44
|
+
models: ["models/car.ldr"], // paths, or { name, text }
|
|
45
|
+
studs: "low", // default
|
|
46
|
+
cond: false, // conditional lines (opt-in)
|
|
47
|
+
textures: false, // printed/sticker parts (opt-in)
|
|
48
|
+
});
|
|
49
|
+
await Bun.write("public/car.pack.bin", data); // raw; compress with zstd/gzip for serving
|
|
50
|
+
console.log(`${parts} parts`, missing.length ? `missing: ${missing}` : "");
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Render it (browser)
|
|
25
54
|
|
|
26
55
|
```ts
|
|
27
56
|
import * as THREE from "three";
|
|
@@ -29,97 +58,150 @@ import { parseModel, loadPack, createLdrObject } from "ldr";
|
|
|
29
58
|
|
|
30
59
|
const [text, pack] = await Promise.all([
|
|
31
60
|
fetch("/car.ldr").then((r) => r.text()),
|
|
32
|
-
loadPack("/car.pack.zst"),
|
|
61
|
+
loadPack("/car.pack.zst"), // .zst / .gz / .br / raw .bin
|
|
33
62
|
]);
|
|
63
|
+
|
|
34
64
|
const model = createLdrObject(parseModel(text, "car.ldr"), pack, {
|
|
35
65
|
edges: true, cullStuds: true, cullUndersides: true, side: "front", material: "plastic",
|
|
36
66
|
});
|
|
37
|
-
scene.add(model.object);
|
|
67
|
+
scene.add(model.object); // +Y up, 1 unit = 1 LDU (see `scale`)
|
|
38
68
|
|
|
39
|
-
//
|
|
40
|
-
model.setStep(0);
|
|
41
|
-
model.setStep(null);
|
|
69
|
+
// Build steps
|
|
70
|
+
model.setStep(0); // show step 0 … model.stepCount - 1
|
|
71
|
+
model.setStep(null); // show everything
|
|
72
|
+
const view = model.stepViews[3]; // ROTSTEP view for step 3 (or null)
|
|
42
73
|
```
|
|
43
74
|
|
|
44
|
-
|
|
75
|
+
Light the scene (or set `scene.environment`) as you would for any `MeshStandardMaterial` or `MeshPhysicalMaterial`.
|
|
45
76
|
|
|
46
|
-
##
|
|
77
|
+
## API reference
|
|
47
78
|
|
|
48
|
-
|
|
49
|
-
The LDraw library is licensed **CC BY 2.0** — if you ship packs built from it, credit "LDraw.org Parts Library" (CC BY 2.0) in your app.
|
|
50
|
-
|
|
51
|
-
Packs contain only the parts you ask for, so they're small: a 10k-brick Datsville section with 80 unique parts and the 100k section with 290 unique parts are 0.27 MB and 1.1 MB with zstd (1.3 / 3.3 MB gzip).
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
ldr pack --ldraw <dir> -o <out.bin|.zst|.gz> [model.ldr ...] [--part 3001.dat ...] [--all-parts]
|
|
55
|
-
[--studs low|full] [--prims full|low] [--no-weld] [--no-colors] [--cond] [--textures] [--no-loose-geometry]
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Or from code (Node/Bun):
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
import { buildPack } from "ldr/node";
|
|
62
|
-
const { data, parts, missing } = buildPack({ ldrawRoot: "./ldraw", models: ["car.ldr"], studs: "low" });
|
|
63
|
-
await Bun.write("car.pack.bin", data);
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
The pack embeds the LDConfig colour table by default, so no extra colour file is needed at runtime.
|
|
79
|
+
### `ldr` (browser-safe)
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
**Parsing and model**
|
|
69
82
|
|
|
70
|
-
|
|
83
|
+
| export | description |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `parse(text, name?, { geometryText? }) → Doc` | Parses LDraw text into files and commands. MPD-aware: `0 FILE`/`0 NOFILE` and `0 !DATA` blocks. |
|
|
86
|
+
| `flatten(doc, options?) → PartInstance[]` | Expands submodels into placed parts. Each instance has `part`, `color`, a 12-number matrix `m`, `step`, nested `steps`, `path` and the ROTSTEP `view` of its step. |
|
|
87
|
+
| `parseModel(text, name?, options?)` | `parse` followed by `flatten`. |
|
|
88
|
+
| `sequenceSteps(instances) → { count, stepOf, views }` | Gives each instance a global build-step index, sequencing nested submodel steps. `views` holds the ROTSTEP view per global step. |
|
|
89
|
+
| `parseLDConfig(text) → Map<code, LDrawColor>` | Reads a `!COLOUR` table into `{ code, name, hex, edge, alpha }` entries. |
|
|
90
|
+
| `fileKind(doc, name)` | Decides whether an MPD file is a part or a model: by its header type, otherwise by whether it has its own geometry. |
|
|
91
|
+
| `looseSteps` / `looseFile` / `looseSource` / `loosePartName` | A model's own type 2–5 geometry, per step, as synthetic parts named `<file>#<step>`. |
|
|
92
|
+
| `rotStepMatrix` / `stepViewMatrix` / `fileStepViews` | ROTSTEP views, following MLCad: `REL`, `ADD`, `ABS`, `END`. A ROTSTEP ends its step. |
|
|
93
|
+
| `parseTexMap` / `texmapUV` / `texmapTriangleUV` / `transformTexMap` / `resolveTexture` / `texWrapper` | `!TEXMAP` support (`PLANAR`, `CYLINDRICAL`, `SPHERICAL`). UV conventions follow LDView: v = 0 is the image's top row. |
|
|
94
|
+
| `parseColor`, `isDirectColor`, `colorToken`, `mul`, `invert`, `IDENTITY`, `norm`, `smoothNormals`, `hash32` | Low-level helpers. |
|
|
95
|
+
|
|
96
|
+
`FlattenOptions`:
|
|
97
|
+
|
|
98
|
+
| option | default | description |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `looseGeometry` | `true` | Emits a model file's own type 2–5 lines as synthetic parts `<file>#<step>`. LDraw allows geometry in any file. |
|
|
101
|
+
| `textures` | `false` | TEXMAP-aware view of the model. References wrapped by a model-level `!TEXMAP` get a `texPart`, and `!TEXMAP FALLBACK` references are skipped. Use with `textures: true` packs. |
|
|
102
|
+
| `resolveModel(name)` | – | Resolves an external reference to a `Doc`, such as a model loaded separately. Return `null` to treat it as a part. |
|
|
103
|
+
| `maxDepth` | `64` | Submodel nesting limit (cycle guard). |
|
|
104
|
+
|
|
105
|
+
MPD-embedded parts, such as Studio/OMR unofficial parts or stickers, become part instances (`embedded: true`) and override library parts of the same name.
|
|
106
|
+
|
|
107
|
+
**Packs and rendering**
|
|
71
108
|
|
|
72
109
|
| export | description |
|
|
73
110
|
|---|---|
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `loadPack(url) → Promise<Pack>` | Fetch + decompress (by extension) + decode a pack. |
|
|
81
|
-
| `decompressPack(bytes)` / `decodePack(buffer)` | Same, from bytes you already have (zstd/gzip auto-detected). |
|
|
82
|
-
| `createLdrObject(instances, pack, options?) → LdrObject` | High-level three.js object. |
|
|
83
|
-
| `createInstancedEdges(pos, instancedMesh, material?)` | Welded, indexed edge lines sharing an InstancedMesh's matrices (for custom batching). |
|
|
84
|
-
| `conditionalLines(pack, entry, which?)` / `createInstancedConditionalEdges(cond, instancedMesh, material?)` | Type-5 conditional lines from a `--cond` pack (stride 12: p0, p1, c0, c1), drawn by a vertex shader only where the control points fall on the same side of the projected line. |
|
|
85
|
-
| `makeTexturedMaterial(side, texture, wrapU?)` / `packTexture(pack, i)` / `texmapUV` / `texmapTriangleUV` | Printed / sticker parts from a `--textures` pack: a `!TEXMAP` UV material (image alpha blends over the part colour, untextured outside [0,1]) and the decoded embedded PNG. UV conventions follow LDView (v = 0 is the image's top row). |
|
|
111
|
+
| `loadPack(url) → Promise<Pack>` | Fetches, decompresses and decodes a pack. zstd and gzip are detected automatically. |
|
|
112
|
+
| `decompressPack(bytes)` / `decodePack(buffer)` | Same, from bytes you already have. |
|
|
113
|
+
| `createLdrObject(instances, pack, options?) → LdrObject` | High-level three.js object (see below). |
|
|
114
|
+
| `createInstancedEdges(pos, instancedMesh, material?)` | Welded, indexed edge lines that share an `InstancedMesh`'s instance matrices. |
|
|
115
|
+
| `conditionalLines(pack, entry, which?)` / `createInstancedConditionalEdges(cond, instancedMesh, material?)` / `conditionalLineVisible` | Type-5 conditional lines from a `cond` pack. A vertex shader keeps a segment only where both control points project to the same side of the line. |
|
|
116
|
+
| `makeTexturedMaterial(side, texture, wrapU?)` / `packTexture(pack, i)` | Printed and sticker parts from a `textures` pack: a UV material and the decoded embedded PNG. |
|
|
86
117
|
| `buildLdrScene`, `computeCulling`, `makeMaterial` | Low-level renderer used by the benchmarks. |
|
|
87
118
|
|
|
88
|
-
`LdrObjectOptions`:
|
|
89
|
-
|
|
90
|
-
`LdrObject`: `object`, `stats` (`instances`, `drawn`, `missing[]`, `studsHidden/Total`, `undersHidden/Total`, `buildMs`), `stepCount`, `stepViews` (ROTSTEP view per step, or `null`), `setStep(n | null)`, `setEdges(on)`, `setCulling(studs, undersides)`, `setSide(side)`, `getBox()`, `dispose()`.
|
|
119
|
+
`LdrObjectOptions`:
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
| option | default | description |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `edges` | `true` | LDraw type-2 edge lines, indexed and sharing each part's instance matrices. |
|
|
124
|
+
| `cullStuds` | `true` | Hides studs fully covered by a part above. |
|
|
125
|
+
| `cullUndersides` | `true` | Hides undersides (tubes) sitting fully on studs below. |
|
|
126
|
+
| `side` | `"front"` | `"front"` draws single-sided BFC faces; `"double"` draws both sides. Uncertified parts are always double-sided. |
|
|
127
|
+
| `material` | `"plastic"` | `"plastic"` is `MeshPhysicalMaterial` with clearcoat; `"standard"` is `MeshStandardMaterial`, the cheapest option. |
|
|
128
|
+
| `conditionalLines` | `true` | Conditional (silhouette) lines. Only takes effect with `cond` packs, and follows `edges`. |
|
|
129
|
+
| `conditionalStudLines` | `true` | Conditional lines on studs. `false` roughly halves the conditional-line cost on large scenes. |
|
|
130
|
+
| `textures` | `true` | Renders `!TEXMAP` images from `textures` packs; `false` draws the FALLBACK geometry. `object.texturesReady` resolves once the images are decoded. |
|
|
131
|
+
| `colors` | pack table | Colour override: a `Map`, a record, or `(code) => color`. |
|
|
132
|
+
| `fallbackColor` | `#A0A5A9` | Colour used for unknown codes. |
|
|
133
|
+
| `ldrawSpace` | `false` | `true` keeps native LDraw space (-Y up). |
|
|
134
|
+
| `scale` | `1` | Uniform scale (1 = 1 LDU; 1 stud = 20 LDU). |
|
|
135
|
+
| `step` | all | Initial build step (inclusive, 0-based). |
|
|
93
136
|
|
|
94
|
-
|
|
137
|
+
`LdrObject`: `object`, `stats` (`instances`, `drawn`, `missing[]`, `studsHidden/Total`, `undersHidden/Total`, `buildMs`, `condBatches`, `texBatches`, `textures`), `stepCount`, `stepViews`, `texturesReady`, `setStep(n | null)`, `setEdges(on)`, `setConditionalLines(on)`, `setCulling(studs, undersides)`, `setSide(side)`, `getBox(target?)`, `dispose()`.
|
|
95
138
|
|
|
96
|
-
|
|
139
|
+
### `ldr/node` (Node / Bun only)
|
|
97
140
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| model | renderer | load | frame | tris/frame | draw calls |
|
|
101
|
-
|---|---|---|---|---|---|
|
|
102
|
-
| Datsville 100k bricks | three.js `LDrawLoader`-geometry instancing | 19.5 s | 105.7 ms (9 fps) | 142.7M | 290 |
|
|
103
|
-
| Datsville 100k bricks | **ldr** (low studs, culling, single-sided) | **2.2 s** | **30.1 ms (33 fps)** | 59.1M | 506 |
|
|
104
|
-
| Datsville 10k bricks | three.js instancing | 13.5 s | 45.9 ms | 60.5M | 80 |
|
|
105
|
-
| Datsville 10k bricks | **ldr** (same) | **1.0 s** | **10.9 ms (92 fps)** | 21.0M | 131 |
|
|
106
|
-
| 100k-brick grid | **ldr** + edges | 0.67 s | 15.7 ms (64 fps) | 4.0M | 24 |
|
|
107
|
-
| 10k-brick grid | `LDrawLoader` | 6.6 s | 61.5 ms | 2.6M | 29,671 |
|
|
108
|
-
| 10k-brick grid | **ldr** + edges | 0.15 s | 2.2 ms | 0.4M | 24 |
|
|
141
|
+
Everything above, plus:
|
|
109
142
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
143
|
+
| export | description |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `buildPack(options) → { data, parts, studKinds, missing, warnings? }` | Builds a pack. Options: `ldrawRoot`, `models`, `parts`, `allParts`, `studs` (`"low"` \| `"full"`), `primitives` (`"full"` \| `"low"`), `weld` (`true`), `cond` (`false`), `textures` (`false`), `looseGeometry` (`true`), `colors` (`true`), `onProgress`. |
|
|
146
|
+
| `Library(root, unofficialRoot?)` | Filesystem LDraw resolver: `where`, `flatten`, `mesh`, `colors`, `texture`, `register`. Handles case-insensitive lookup and `~Moved to` aliases. |
|
|
147
|
+
| `Compiler`, `encodePack`, `writePack`, `loadColors` | Lower-level pack compiler. |
|
|
148
|
+
| `ResolverLibrary`, `splitModel`, `flattenWithLibrary`, `collectDeps` | The resolver that `buildPack` uses. |
|
|
113
149
|
|
|
114
|
-
##
|
|
150
|
+
## Benchmarks
|
|
115
151
|
|
|
116
|
-
|
|
117
|
-
- ROTSTEP is reported (`stepViews`, `instance.view`), not applied: rotating the camera per step is up to the app.
|
|
118
|
-
- `setStep` rebuilds batches (fast for instruction-sized models; culling is recomputed for the partial build).
|
|
119
|
-
- Coordinates: LDraw units (1 stud = 20 LDU); the object is rotated to +Y up unless `ldrawSpace: true`.
|
|
152
|
+
All numbers below were measured with the harness in [`bench/`](https://github.com/EvanBacon/ldr/tree/main/bench/) and are taken from the result files there.
|
|
120
153
|
|
|
121
|
-
|
|
154
|
+
- **Hardware and browser:** Apple **M4 Pro** MacBook Pro, Chrome with native GL (ANGLE → Metal), Bun 1.3.
|
|
155
|
+
- **Frame time:** the **median synced frame time** (`gl.finish` per frame) while orbiting the camera.
|
|
156
|
+
- **Load:** from fetch to the first rendered frame.
|
|
157
|
+
- **Baseline:** three.js geometry from `LDrawLoader` merged per part and drawn with `InstancedMesh`, which is the fastest stock approach. Stock `LDrawLoader` scenes crash the tab (OOM) at 50k bricks and above, and already on Datsville 10k.
|
|
158
|
+
- **ldr settings:** low-detail studs, welded pack, stud and underside culling, single-sided faces, no edges.
|
|
122
159
|
|
|
123
|
-
|
|
160
|
+
Sources: `bench/results/latest-mac-ablation.md`, `latest-mac.md`, and `research.md` §7.
|
|
124
161
|
|
|
125
|
-
|
|
162
|
+
| model | bricks | baseline frame | **ldr frame** | baseline load | **ldr load** |
|
|
163
|
+
|---|---|---|---|---|---|
|
|
164
|
+
| synthetic grid | 100,021 | 24.4 ms | **3.6 ms** | 0.2 s | 0.6 s |
|
|
165
|
+
| Datsville 10k | 10,000 | 45.9 ms | **10.9 ms** | 13.5 s | **1.0 s** |
|
|
166
|
+
| Datsville 100k | 100,000 | 105.7 ms | **30.1 ms** | 19.5 s | **2.2 s** |
|
|
167
|
+
| Datsville (full) | 392,514 | 409.6 ms | **97.2 ms** | 24.7 s | **3.4 s** |
|
|
168
|
+
|
|
169
|
+
How much each optimization contributes on Datsville 100k, in ms per frame: welding 108.1 → 64.6, stud culling → 50.8, underside culling → 41.7, low-detail studs → 31.0, single-sided faces → 30.1. Triangles per frame drop from 142.7M to 59.1M.
|
|
170
|
+
|
|
171
|
+
- **Pack size:** Datsville 100k (290 unique parts) is 22.4 MB raw, 3.13 MB gzip and **1.08 MB zstd**. Decoding the zstd pack in the browser (fzstd) takes **58 ms**.
|
|
172
|
+
- **Stock `LDrawLoader`:** loading the scene directly crashes the renderer (OOM) on the 50k, 100k and 250k grids and on Datsville 10k. The 10k grid loads in 6.6 s and draws at 61.5 ms per frame (29,671 draw calls), against 0.15 s and 2.2 ms with ldr + edges.
|
|
173
|
+
- **Demo app** (`demo/`; rAF frame median at 2880×1800 on the M4 Pro):
|
|
174
|
+
- These comparisons are between demo settings. "Before" has every effect on (shadows, AO, screen-space edges, PBR/IBL); "after" is the demo's big-model defaults, which turn those effects off above 50k bricks.
|
|
175
|
+
- Full Datsville: **1.7 → 9.9 fps**.
|
|
176
|
+
- Datsville 100k: **8 → 43.5 fps**.
|
|
177
|
+
- **Conditional lines** (opt-in `cond` packs) cost **+12–14 %** frame time on Datsville 10k and 100k (M4 Pro), and add 7–14 % to the zstd pack size.
|
|
178
|
+
- **Edges:** turning `edges` on costs more than that on dense scenes: Datsville 100k goes from 30.1 to 98.2 ms per frame. For very large models, consider `edges: false`.
|
|
179
|
+
- **Visual fidelity:** pixel diffs show culling and single-sided faces are lossless (≤ 0.009 % of pixels differ). Low-detail studs only change stud silhouettes.
|
|
180
|
+
|
|
181
|
+
Reproduce: `cd bench && bun run setup && bun run bench` (see [`bench/README.md`](https://github.com/EvanBacon/ldr/blob/main/bench/README.md)).
|
|
182
|
+
|
|
183
|
+
## LDraw spec compliance
|
|
184
|
+
|
|
185
|
+
- Line types 0–5. BFC: `CERTIFY`, `CCW`/`CW`, `INVERTNEXT`, `NOCLIP`, inverted matrices.
|
|
186
|
+
- MPD: `0 FILE` / `0 NOFILE`, and `0 !DATA` (base64 PNGs). An MPD file is a part or a model by its `!LDRAW_ORG` type, otherwise by whether it has its own geometry. Embedded parts override library parts.
|
|
187
|
+
- Geometry drawn directly in model files (type 2–5 lines outside parts), on by default.
|
|
188
|
+
- `0 STEP`, nested submodel step sequencing, `0 ROTSTEP` (`REL`/`ADD`/`ABS`/`END`), and `0 BUFEXCHG`.
|
|
189
|
+
- `!TEXMAP` `START`/`NEXT`/`FALLBACK`/`END`, with `PLANAR`, `CYLINDRICAL` and `SPHERICAL` projections (orientation matches LDView), and model-level `!TEXMAP` around part references.
|
|
190
|
+
- Colours: LDConfig `!COLOUR`, edge colours, main/edge colour inheritance (16/24), and direct colours (`0x2RRGGBB`).
|
|
191
|
+
- Case-insensitive, backslash-normalized file lookup; `~Moved to` alias parts.
|
|
192
|
+
|
|
193
|
+
## Limitations
|
|
194
|
+
|
|
195
|
+
- **Transparency:** transparent colours render opaque.
|
|
196
|
+
- **Opt-in pack features:** conditional lines need packs built with `cond`. Printed parts show their images only from `textures` packs (the embedded PNGs dominate the pack size); default packs draw the FALLBACK geometry.
|
|
197
|
+
- **ROTSTEP** is reported (`stepViews`, `instance.view`), not applied. Rotating the camera per step is up to your app.
|
|
198
|
+
- **`setStep`** rebuilds batches. That is fast for instruction-sized models, and culling is recomputed for the partial build.
|
|
199
|
+
- **Packs are static:** they hold only the parts they were built for. A model that uses parts missing from the pack lists them in `stats.missing`.
|
|
200
|
+
- **Occlusion culling and LOD:** there is no occlusion culling and no distance LOD yet. Only three.js frustum culling applies, per batch.
|
|
201
|
+
|
|
202
|
+
## Attribution and license
|
|
203
|
+
|
|
204
|
+
- **Code:** MIT © Evan Bacon.
|
|
205
|
+
- **LDraw parts:** packs you build contain geometry from the **LDraw.org Parts Library**, which is licensed **CC BY 2.0** (newer files CC BY 4.0, per file header). If you ship packs, credit "LDraw.org Parts Library (CC BY 2.0)" in your app. See <https://www.ldraw.org/legal-info>.
|
|
206
|
+
- **Datsville:** the benchmark model is by [mjhorvath/Datsville](https://github.com/mjhorvath/Datsville) (CC BY-SA 4.0). It is used for benchmarking only and is not redistributed.
|
|
207
|
+
- **Trademarks:** LEGO® is a trademark of the LEGO Group, which does not sponsor, authorize or endorse this project. LDraw™ is a trademark of the Estate of James Jessiman.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldr",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|