@gjsify/example-dom-three-loader-ldraw 0.1.7 → 0.37.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.
Files changed (37) hide show
  1. package/README.md +78 -0
  2. package/dist/browser.js +8558 -33688
  3. package/dist/gjs.js +3967 -47953
  4. package/dist/gjs.node.mjs +4381 -0
  5. package/dist/index.html +1 -0
  6. package/dist/webgl.css +7 -32
  7. package/package.json +51 -21
  8. package/src/assets/models/ldraw/officialLibrary/CAlicense.txt +248 -0
  9. package/src/assets/models/ldraw/officialLibrary/CAreadme.txt +81 -0
  10. package/src/assets/models/ldraw/officialLibrary/Readme.txt +125 -0
  11. package/src/assets/models/ldraw/officialLibrary/models/10174-1-ImperialAT-ST-UCS.mpd_Packed.mpd +32672 -0
  12. package/src/assets/models/ldraw/officialLibrary/models/1621-1-LunarMPVVehicle.mpd_Packed.mpd +12465 -0
  13. package/src/assets/models/ldraw/officialLibrary/models/30023-1-Lighthouse.ldr_Packed.mpd +4364 -0
  14. package/src/assets/models/ldraw/officialLibrary/models/30051-1-X-wingFighter-Mini.mpd_Packed.mpd +4500 -0
  15. package/src/assets/models/ldraw/officialLibrary/models/30054-1-AT-ST-Mini.mpd_Packed.mpd +6449 -0
  16. package/src/assets/models/ldraw/officialLibrary/models/4489-1-AT-AT-Mini.mpd_Packed.mpd +7242 -0
  17. package/src/assets/models/ldraw/officialLibrary/models/4494-1-Imperial Shuttle-Mini.mpd_Packed.mpd +5530 -0
  18. package/src/assets/models/ldraw/officialLibrary/models/4838-1-MiniVehicles.mpd_Packed.mpd +4880 -0
  19. package/src/assets/models/ldraw/officialLibrary/models/4915-1-MiniConstruction.mpd_Packed.mpd +6481 -0
  20. package/src/assets/models/ldraw/officialLibrary/models/4918-1-MiniFlyers.mpd_Packed.mpd +5518 -0
  21. package/src/assets/models/ldraw/officialLibrary/models/5935-1-IslandHopper.mpd_Packed.mpd +17313 -0
  22. package/src/assets/models/ldraw/officialLibrary/models/6965-1-TIEIntercep_4h4MXk5.mpd_Packed.mpd +2839 -0
  23. package/src/assets/models/ldraw/officialLibrary/models/6966-1-JediStarfighter-Mini.mpd_Packed.mpd +4087 -0
  24. package/src/assets/models/ldraw/officialLibrary/models/7140-1-X-wingFighter.mpd_Packed.mpd +28548 -0
  25. package/src/assets/models/ldraw/officialLibrary/models/889-1-RadarTruck.mpd_Packed.mpd +8429 -0
  26. package/src/assets/models/ldraw/officialLibrary/models/car.ldr_Packed.mpd +5969 -0
  27. package/src/browser/browser-main.ts +4 -0
  28. package/src/browser/browser.ts +199 -0
  29. package/src/browser/index.html +11 -0
  30. package/src/browser/webgl.css +28 -0
  31. package/src/gjs/gjs.ts +31 -0
  32. package/src/gjs/ldraw-window.blp +87 -0
  33. package/src/gjs/ldraw-window.ts +132 -0
  34. package/src/models.ts +28 -0
  35. package/src/three-demo.ts +242 -0
  36. package/dist/adwaita-sans-400-VCQV646T.ttf +0 -0
  37. package/dist/browser.css +0 -10
@@ -0,0 +1,242 @@
1
+ // Ported from refs/three/examples/webgl_loader_ldraw.html
2
+ // Original: MIT license, three.js authors (https://threejs.org)
3
+ // This software uses the LDraw Parts Library (http://www.ldraw.org), CC BY 2.0.
4
+ // Reimplemented for GJS and browser targets using @gjsify packages.
5
+
6
+ import * as THREE from 'three';
7
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
8
+ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
9
+ import { LDrawLoader } from 'three/addons/loaders/LDrawLoader.js';
10
+ import { LDrawUtils } from 'three/addons/utils/LDrawUtils.js';
11
+ import { LDrawConditionalLineMaterial } from 'three/addons/materials/LDrawConditionalLineMaterial.js';
12
+ import { MODEL_LIST, DEFAULT_MODEL_INDEX, type ModelEntry } from './models.js';
13
+
14
+ export { MODEL_LIST, DEFAULT_MODEL_INDEX, type ModelEntry };
15
+
16
+ export interface LDrawEffectController {
17
+ modelIndex: number;
18
+ flatColors: boolean;
19
+ mergeModel: boolean;
20
+ buildingStep: number;
21
+ smoothNormals: boolean;
22
+ displayLines: boolean;
23
+ conditionalLines: boolean;
24
+ }
25
+
26
+ export interface LDrawDemo {
27
+ readonly effectController: LDrawEffectController;
28
+ reloadObject(resetCamera: boolean): void;
29
+ updateVisibility(): void;
30
+ numBuildingSteps: number;
31
+ /** Stop the render loop — the website embed calls this when it scrolls out of view. */
32
+ pause(): void;
33
+ /** Restart the render loop after `pause()`. */
34
+ resume(): void;
35
+ readonly isPaused: boolean;
36
+ }
37
+
38
+ export interface StartOptions {
39
+ assetBase?: string;
40
+ }
41
+
42
+ export type OnModelLoaded = (numBuildingSteps: number) => void;
43
+
44
+ export function start(canvas: HTMLCanvasElement, options?: StartOptions, onModelLoaded?: OnModelLoaded): LDrawDemo {
45
+ const assetBase = options?.assetBase ?? './';
46
+ const ldrawPath = `${assetBase}assets/models/ldraw/officialLibrary/`;
47
+
48
+ // oxlint-disable-next-line typescript/no-explicit-any -- THREE.WebGLRenderer canvas option expects OffscreenCanvas; GJS canvas type is incompatible
49
+ const renderer = new THREE.WebGLRenderer({ canvas: canvas as any, antialias: true });
50
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
51
+ // Pass updateStyle=false: the canvas CSS width/height are owned by the host
52
+ // container (GTK allocation / browser flex layout), we only size the
53
+ // drawing buffer.
54
+ renderer.setSize(canvas.width, canvas.height, false);
55
+ // The last size handed to the renderer, tracked separately because `renderer.domElement` IS this
56
+ // canvas: the obvious `renderer.domElement.width !== w` compares a value with itself, leaving the
57
+ // resize branch dead and the camera aspect frozen at the first allocation.
58
+ let prevW = canvas.width;
59
+ let prevH = canvas.height;
60
+
61
+ const pmremGenerator = new THREE.PMREMGenerator(renderer);
62
+ const scene = new THREE.Scene();
63
+ scene.background = new THREE.Color(0xdeebed);
64
+ scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
65
+
66
+ const camera = new THREE.PerspectiveCamera(45, canvas.width / canvas.height, 1, 10000);
67
+ camera.position.set(150, 200, 250);
68
+
69
+ // oxlint-disable-next-line typescript/no-explicit-any -- OrbitControls domElement type is HTMLElement; GJS canvas type is incompatible
70
+ const controls = new OrbitControls(camera, canvas as any);
71
+ controls.enableDamping = true;
72
+
73
+ let model: THREE.Group | null = null;
74
+
75
+ const effectController: LDrawEffectController = {
76
+ modelIndex: DEFAULT_MODEL_INDEX,
77
+ flatColors: false,
78
+ mergeModel: false,
79
+ buildingStep: 0,
80
+ smoothNormals: true,
81
+ displayLines: true,
82
+ conditionalLines: true,
83
+ };
84
+
85
+ let numBuildingSteps = 0;
86
+
87
+ function updateObjectsVisibility() {
88
+ if (!model) return;
89
+ // oxlint-disable-next-line typescript/no-explicit-any -- THREE.Object3D.traverse callback; LDraw-specific properties (isConditionalLine, buildingStep) not in THREE types
90
+ model.traverse((c: any) => {
91
+ if (c.isLineSegments) {
92
+ if (c.isConditionalLine) {
93
+ c.visible = effectController.conditionalLines;
94
+ } else {
95
+ c.visible = effectController.displayLines;
96
+ }
97
+ } else if (c.isGroup) {
98
+ c.visible = c.userData.buildingStep <= effectController.buildingStep;
99
+ }
100
+ });
101
+ }
102
+
103
+ function reloadObject(resetCamera: boolean) {
104
+ if (model) {
105
+ scene.remove(model);
106
+ model = null;
107
+ }
108
+
109
+ const entry = MODEL_LIST[effectController.modelIndex];
110
+ if (!entry) return;
111
+
112
+ const lDrawLoader = new LDrawLoader();
113
+ lDrawLoader.setConditionalLineMaterial(LDrawConditionalLineMaterial);
114
+ // oxlint-disable-next-line typescript/no-explicit-any -- LDrawLoader.smoothNormals is a runtime property not in the three.js TypeScript types
115
+ (lDrawLoader as any).smoothNormals = effectController.smoothNormals && !effectController.flatColors;
116
+ lDrawLoader.setPath(ldrawPath);
117
+
118
+ lDrawLoader.load(
119
+ entry.file,
120
+ (group2: THREE.Group) => {
121
+ if (model) scene.remove(model);
122
+
123
+ model = group2;
124
+
125
+ // Flat colors: convert to MeshBasicMaterial (LEGO instruction look)
126
+ if (effectController.flatColors) {
127
+ // oxlint-disable-next-line typescript/no-explicit-any -- THREE.Object3D.traverse callback; LDraw-specific isMesh/material not guaranteed typed
128
+ model.traverse((c: any) => {
129
+ if (c.isMesh) {
130
+ if (Array.isArray(c.material)) {
131
+ c.material = c.material.map(convertMaterial);
132
+ } else {
133
+ c.material = convertMaterial(c.material);
134
+ }
135
+ }
136
+ });
137
+ }
138
+
139
+ if (effectController.mergeModel) model = LDrawUtils.mergeObject(model);
140
+
141
+ // Convert from LDraw coordinates: rotate 180 degrees around OX
142
+ model.rotation.x = Math.PI;
143
+ scene.add(model);
144
+
145
+ numBuildingSteps = ((model.userData as Record<string, unknown>).numBuildingSteps as number) ?? 1;
146
+ effectController.buildingStep = numBuildingSteps - 1;
147
+
148
+ updateObjectsVisibility();
149
+
150
+ const bbox = new THREE.Box3().setFromObject(model);
151
+ const size = bbox.getSize(new THREE.Vector3());
152
+ const radius = Math.max(size.x, Math.max(size.y, size.z)) * 0.5;
153
+
154
+ if (resetCamera) {
155
+ controls.target0.copy(bbox.getCenter(new THREE.Vector3()));
156
+ controls.position0.set(-2.3, 1, 2).multiplyScalar(radius).add(controls.target0);
157
+ controls.reset();
158
+ }
159
+
160
+ onModelLoaded?.(numBuildingSteps);
161
+ },
162
+ undefined,
163
+ (error: unknown) => {
164
+ // The onError handler is load-bearing: LDrawLoader routes failures into a promise
165
+ // chain, so without it a failed model is a silent rejection and the viewer just shows
166
+ // the empty background.
167
+ console.error(`[ldraw] loading ${ldrawPath}${entry.file} failed:`, error);
168
+ },
169
+ );
170
+ }
171
+
172
+ function convertMaterial(material: THREE.Material): THREE.MeshBasicMaterial {
173
+ const m = material as THREE.MeshStandardMaterial;
174
+ const newMat = new THREE.MeshBasicMaterial();
175
+ newMat.color.copy(m.color);
176
+ newMat.polygonOffset = m.polygonOffset;
177
+ newMat.polygonOffsetUnits = m.polygonOffsetUnits;
178
+ newMat.polygonOffsetFactor = m.polygonOffsetFactor;
179
+ newMat.opacity = m.opacity;
180
+ newMat.transparent = m.transparent;
181
+ newMat.depthWrite = m.depthWrite;
182
+ newMat.toneMapped = false;
183
+ return newMat;
184
+ }
185
+
186
+ function animate() {
187
+ controls.update();
188
+
189
+ // canvas.width/height are the host-owned drawing buffer: GTK allocation on GJS, a parent
190
+ // ResizeObserver in the browser.
191
+ const w = canvas.width;
192
+ const h = canvas.height;
193
+ if (w !== prevW || h !== prevH) {
194
+ prevW = w;
195
+ prevH = h;
196
+ renderer.setSize(w, h, false);
197
+ camera.aspect = w / h;
198
+ camera.updateProjectionMatrix();
199
+ }
200
+
201
+ renderer.render(scene, camera);
202
+ }
203
+
204
+ // requestAnimationFrame directly, which maps onto the GTK frame clock.
205
+ let animPending = false;
206
+ let paused = false;
207
+ function scheduleFrame() {
208
+ if (animPending || paused) return;
209
+ animPending = true;
210
+ requestAnimationFrame(() => {
211
+ animPending = false;
212
+ animate();
213
+ scheduleFrame();
214
+ });
215
+ }
216
+ scheduleFrame();
217
+
218
+ reloadObject(true);
219
+
220
+ return {
221
+ effectController,
222
+ reloadObject,
223
+ updateVisibility: updateObjectsVisibility,
224
+ pause() {
225
+ paused = true;
226
+ },
227
+ resume() {
228
+ if (!paused) return;
229
+ paused = false;
230
+ scheduleFrame();
231
+ },
232
+ get isPaused() {
233
+ return paused;
234
+ },
235
+ get numBuildingSteps() {
236
+ return numBuildingSteps;
237
+ },
238
+ set numBuildingSteps(v) {
239
+ numBuildingSteps = v;
240
+ },
241
+ };
242
+ }
Binary file
package/dist/browser.css DELETED
@@ -1,10 +0,0 @@
1
- /* ../../../packages/web/adwaita-fonts/400.css */
2
- @font-face {
3
- font-family: "Adwaita Sans";
4
- font-style: normal;
5
- font-display: swap;
6
- font-weight: 400;
7
- src: url("./adwaita-sans-400-VCQV646T.ttf") format("truetype");
8
- }
9
-
10
- /* ../../../packages/web/adwaita-fonts/index.css */