@gjsify/example-dom-three-loader-ldraw 0.1.7 → 0.38.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 +8628 -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,4 @@
1
+ // Standalone browser entry point — runs the LDraw demo fullscreen.
2
+ import { mount } from './browser.js';
3
+
4
+ mount(document.body);
@@ -0,0 +1,199 @@
1
+ // Browser UI for three-loader-ldraw example.
2
+ // Mirrors the GJS/Adwaita UI using @gjsify/adwaita-web.
3
+ // Ported from refs/three/examples/webgl_loader_ldraw.html
4
+ // Original: MIT license, three.js authors (https://threejs.org)
5
+ // This software uses the LDraw Parts Library (http://www.ldraw.org), CC BY 2.0.
6
+
7
+ import '@gjsify/adwaita-web';
8
+ import '@gjsify/adwaita-web/style.css';
9
+ import type { AdwOverlaySplitView } from '@gjsify/adwaita-web';
10
+ import { start, MODEL_LIST, DEFAULT_MODEL_INDEX, type LDrawDemo } from '../three-demo.js';
11
+
12
+ export interface MountOptions {
13
+ assetBase?: string;
14
+ }
15
+
16
+ /**
17
+ * What the website's `<ShowcaseEmbed>` holds on to: it pauses a demo that
18
+ * scrolls out of view, so an always-animating scene does not keep a GPU busy
19
+ * off-screen.
20
+ */
21
+ export interface ShowcaseHandle {
22
+ pause(): void;
23
+ resume(): void;
24
+ readonly isPaused: boolean;
25
+ }
26
+
27
+ export function mount(container: HTMLElement, options?: MountOptions): ShowcaseHandle {
28
+ const { assetBase } = options ?? {};
29
+
30
+ const win = document.createElement('adw-window');
31
+ win.setAttribute('width', '1100');
32
+ win.setAttribute('height', '700');
33
+
34
+ const headerBar = document.createElement('adw-header-bar');
35
+ headerBar.setAttribute('title', 'LDraw Loader');
36
+
37
+ // A showcase has two hosts — the standalone page and the website embed — and only the former
38
+ // loads `browser/webgl.css`, so the layout has to live here rather than in that stylesheet.
39
+ const splitView = document.createElement('adw-overlay-split-view') as AdwOverlaySplitView;
40
+ splitView.setAttribute('min-sidebar-width', '280');
41
+ splitView.setAttribute('max-sidebar-width', '400');
42
+ splitView.setAttribute('sidebar-width-fraction', '0.30');
43
+ splitView.setAttribute('show-sidebar', '');
44
+
45
+ // Inline styles for the same reason as the GL container below.
46
+ const sidebarContent = document.createElement('div');
47
+ sidebarContent.setAttribute('slot', 'sidebar');
48
+ sidebarContent.className = 'adw-sidebar-content';
49
+ sidebarContent.style.cssText = 'padding:12px;display:flex;flex-direction:column;gap:12px';
50
+
51
+ const modelGroup = document.createElement('adw-preferences-group');
52
+ modelGroup.setAttribute('title', 'Model');
53
+
54
+ const modelRow = document.createElement('adw-combo-row');
55
+ modelRow.setAttribute('title', 'Model');
56
+ modelRow.setAttribute('items', JSON.stringify(MODEL_LIST.map((m) => m.name)));
57
+ modelRow.setAttribute('selected', String(DEFAULT_MODEL_INDEX));
58
+
59
+ modelGroup.append(modelRow);
60
+
61
+ // Rendering group
62
+ const renderGroup = document.createElement('adw-preferences-group');
63
+ renderGroup.setAttribute('title', 'Rendering');
64
+
65
+ const flatColorsRow = document.createElement('adw-switch-row');
66
+ flatColorsRow.setAttribute('title', 'Flat Colors');
67
+
68
+ const mergeModelRow = document.createElement('adw-switch-row');
69
+ mergeModelRow.setAttribute('title', 'Merge Model');
70
+
71
+ const smoothNormalsRow = document.createElement('adw-switch-row');
72
+ smoothNormalsRow.setAttribute('title', 'Smooth Normals');
73
+ smoothNormalsRow.setAttribute('active', '');
74
+
75
+ renderGroup.append(flatColorsRow, mergeModelRow, smoothNormalsRow);
76
+
77
+ // Display group
78
+ const displayGroup = document.createElement('adw-preferences-group');
79
+ displayGroup.setAttribute('title', 'Display');
80
+
81
+ const buildingStepRow = document.createElement('adw-spin-row');
82
+ buildingStepRow.setAttribute('title', 'Building Step');
83
+ buildingStepRow.setAttribute('min', '0');
84
+ buildingStepRow.setAttribute('max', '0');
85
+ buildingStepRow.setAttribute('step', '1');
86
+ buildingStepRow.setAttribute('value', '0');
87
+
88
+ const displayLinesRow = document.createElement('adw-switch-row');
89
+ displayLinesRow.setAttribute('title', 'Display Lines');
90
+ displayLinesRow.setAttribute('active', '');
91
+
92
+ const conditionalLinesRow = document.createElement('adw-switch-row');
93
+ conditionalLinesRow.setAttribute('title', 'Conditional Lines');
94
+ conditionalLinesRow.setAttribute('active', '');
95
+
96
+ displayGroup.append(buildingStepRow, displayLinesRow, conditionalLinesRow);
97
+
98
+ sidebarContent.append(modelGroup, renderGroup, displayGroup);
99
+
100
+ // GL container (content slot) — inline styles so the showcase
101
+ // is self-contained and works regardless of host CSS.
102
+ const glContainer = document.createElement('div');
103
+ glContainer.setAttribute('slot', 'content');
104
+ glContainer.id = 'gl-area-container';
105
+ glContainer.style.cssText = 'flex:1;position:relative;min-width:0;min-height:0';
106
+
107
+ const canvas = document.createElement('canvas');
108
+ canvas.id = 'webgl-canvas';
109
+ canvas.style.cssText = 'display:block;width:100%;height:100%;position:absolute;inset:0';
110
+ glContainer.append(canvas);
111
+
112
+ splitView.append(sidebarContent, glContainer);
113
+ win.append(headerBar, splitView);
114
+ container.append(win);
115
+
116
+ // Sync canvas size
117
+ new ResizeObserver(() => {
118
+ canvas.width = glContainer.clientWidth;
119
+ canvas.height = glContainer.clientHeight;
120
+ }).observe(glContainer);
121
+ canvas.width = glContainer.clientWidth;
122
+ canvas.height = glContainer.clientHeight;
123
+
124
+ // Start three.js
125
+ const demo = start(canvas, { assetBase }, (numSteps) => {
126
+ // Update building step range when model loads
127
+ buildingStepRow.setAttribute('max', String(numSteps - 1));
128
+ // oxlint-disable-next-line typescript/no-explicit-any -- adw-spin-row is a custom element with no TypeScript type for .value property
129
+ (buildingStepRow as any).value = numSteps - 1;
130
+ });
131
+
132
+ connectControls(
133
+ demo,
134
+ modelRow as AdwRow,
135
+ flatColorsRow as AdwRow,
136
+ mergeModelRow as AdwRow,
137
+ smoothNormalsRow as AdwRow,
138
+ buildingStepRow as AdwRow,
139
+ displayLinesRow as AdwRow,
140
+ conditionalLinesRow as AdwRow,
141
+ );
142
+
143
+ return {
144
+ pause: () => demo.pause(),
145
+ resume: () => demo.resume(),
146
+ get isPaused() {
147
+ return demo.isPaused;
148
+ },
149
+ };
150
+ }
151
+
152
+ // Adwaita web components expose custom properties (.selected, .active, .value) not in HTMLElement types.
153
+ type AdwRow = HTMLElement & Record<string, unknown>;
154
+
155
+ function connectControls(
156
+ demo: LDrawDemo,
157
+ modelRow: AdwRow,
158
+ flatColorsRow: AdwRow,
159
+ mergeModelRow: AdwRow,
160
+ smoothNormalsRow: AdwRow,
161
+ buildingStepRow: AdwRow,
162
+ displayLinesRow: AdwRow,
163
+ conditionalLinesRow: AdwRow,
164
+ ) {
165
+ modelRow.addEventListener('notify::selected', () => {
166
+ demo.effectController.modelIndex = modelRow.selected as number;
167
+ demo.reloadObject(true);
168
+ });
169
+
170
+ flatColorsRow.addEventListener('notify::active', () => {
171
+ demo.effectController.flatColors = flatColorsRow.active as boolean;
172
+ demo.reloadObject(false);
173
+ });
174
+
175
+ mergeModelRow.addEventListener('notify::active', () => {
176
+ demo.effectController.mergeModel = mergeModelRow.active as boolean;
177
+ demo.reloadObject(false);
178
+ });
179
+
180
+ smoothNormalsRow.addEventListener('notify::active', () => {
181
+ demo.effectController.smoothNormals = smoothNormalsRow.active as boolean;
182
+ demo.reloadObject(false);
183
+ });
184
+
185
+ buildingStepRow.addEventListener('notify::value', () => {
186
+ demo.effectController.buildingStep = buildingStepRow.value as number;
187
+ demo.updateVisibility();
188
+ });
189
+
190
+ displayLinesRow.addEventListener('notify::active', () => {
191
+ demo.effectController.displayLines = displayLinesRow.active as boolean;
192
+ demo.updateVisibility();
193
+ });
194
+
195
+ conditionalLinesRow.addEventListener('notify::active', () => {
196
+ demo.effectController.conditionalLines = conditionalLinesRow.active as boolean;
197
+ demo.updateVisibility();
198
+ });
199
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>LDraw Loader — gjsify</title>
6
+ <link rel="stylesheet" href="./webgl.css" type="text/css" />
7
+ <link rel="stylesheet" href="./browser.css" type="text/css" />
8
+ <script src="./browser.js" type="module" defer></script>
9
+ </head>
10
+ <body></body>
11
+ </html>
@@ -0,0 +1,28 @@
1
+ /* Page chrome for the STANDALONE `index.html` only — how the window is centred
2
+ on an otherwise empty page. The window's own layout (sidebar vs. GL area) is
3
+ deliberately NOT here: `browser.ts` builds an <adw-overlay-split-view> and
4
+ inline-styles the GL container, because the website embeds `mount()` without
5
+ ever loading this file, and a layout that lives here is a layout the embed
6
+ does not get. */
7
+
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ html, body {
15
+ width: 100%;
16
+ height: 100%;
17
+ overflow: hidden;
18
+ background: #2d2d2d;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ }
23
+
24
+ adw-window {
25
+ max-width: 100%;
26
+ max-height: 100%;
27
+ }
28
+
package/src/gjs/gjs.ts ADDED
@@ -0,0 +1,31 @@
1
+ // GJS/Adwaita entry point for three-loader-ldraw example.
2
+ // Ported from refs/three/examples/webgl_loader_ldraw.html
3
+ // Original: MIT license, three.js authors (https://threejs.org)
4
+ // This software uses the LDraw Parts Library (http://www.ldraw.org), CC BY 2.0.
5
+
6
+ import Adw from 'gi://Adw?version=1';
7
+ import Gio from 'gi://Gio?version=2.0';
8
+
9
+ import { installDevtools } from '@gjsify/devtools';
10
+ import { LDrawWindow } from './ldraw-window.js';
11
+
12
+ const app = new Adw.Application({
13
+ application_id: 'gjsify.examples.three-loader-ldraw',
14
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
15
+ });
16
+
17
+ // Opt-in devtools control plane — a no-op unless GJSIFY_DEVTOOLS is set, so
18
+ // production runs are unaffected. It is how the rendered model gets verified
19
+ // without a human: the model arrives through an async loader, so "the window
20
+ // opened" proves nothing about what is in the GLArea.
21
+ app.connect('startup', () => installDevtools(app));
22
+
23
+ app.connect('activate', () => {
24
+ let win = app.get_active_window();
25
+ if (!win) win = new LDrawWindow(app);
26
+ win.present();
27
+ });
28
+
29
+ // runAsync (NOT the sync run()): the GJS-recommended lifecycle, and the required
30
+ // one on the `--app node` reverse bridge where a sync run() deadlocks.
31
+ await app.runAsync([]);
@@ -0,0 +1,87 @@
1
+ using Gtk 4.0;
2
+ using Adw 1;
3
+
4
+ template $LDrawWindow: Adw.ApplicationWindow {
5
+ default-width: 1100;
6
+ default-height: 700;
7
+ title: "LDraw Loader";
8
+
9
+ content: Gtk.Box {
10
+ orientation: vertical;
11
+
12
+ Adw.HeaderBar {}
13
+
14
+ Gtk.Box {
15
+ orientation: horizontal;
16
+ hexpand: true;
17
+ vexpand: true;
18
+
19
+ Gtk.ScrolledWindow {
20
+ width-request: 320;
21
+ hexpand: false;
22
+ hscrollbar-policy: never;
23
+
24
+ Gtk.Box {
25
+ orientation: vertical;
26
+ spacing: 12;
27
+ margin-top: 12;
28
+ margin-bottom: 12;
29
+ margin-start: 12;
30
+ margin-end: 12;
31
+
32
+ Adw.PreferencesGroup {
33
+ title: "Model";
34
+
35
+ Adw.ComboRow modelRow {
36
+ title: "Model";
37
+ }
38
+ }
39
+
40
+ Adw.PreferencesGroup {
41
+ title: "Rendering";
42
+
43
+ Adw.SwitchRow flatColorsRow {
44
+ title: "Flat Colors";
45
+ }
46
+
47
+ Adw.SwitchRow mergeModelRow {
48
+ title: "Merge Model";
49
+ }
50
+
51
+ Adw.SwitchRow smoothNormalsRow {
52
+ title: "Smooth Normals";
53
+ active: true;
54
+ }
55
+ }
56
+
57
+ Adw.PreferencesGroup {
58
+ title: "Display";
59
+
60
+ Adw.SpinRow buildingStepRow {
61
+ title: "Building Step";
62
+ }
63
+
64
+ Adw.SwitchRow displayLinesRow {
65
+ title: "Display Lines";
66
+ active: true;
67
+ }
68
+
69
+ Adw.SwitchRow conditionalLinesRow {
70
+ title: "Conditional Lines";
71
+ active: true;
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ Gtk.Separator {
78
+ orientation: vertical;
79
+ }
80
+
81
+ Gtk.Box glAreaContainer {
82
+ hexpand: true;
83
+ vexpand: true;
84
+ }
85
+ }
86
+ };
87
+ }
@@ -0,0 +1,132 @@
1
+ // Adwaita window for the LDraw loader example.
2
+ // Uses a Blueprint template for the UI layout and WebGLBridge for WebGL.
3
+ // Ported from refs/three/examples/webgl_loader_ldraw.html
4
+ // Original: MIT license, three.js authors (https://threejs.org)
5
+ // This software uses the LDraw Parts Library (http://www.ldraw.org), CC BY 2.0.
6
+
7
+ import GObject from 'gi://GObject?version=2.0';
8
+ import Gtk from 'gi://Gtk?version=4.0';
9
+ import Adw from 'gi://Adw?version=1';
10
+ import { WebGLBridge } from '@gjsify/webgl';
11
+ import { start, MODEL_LIST, DEFAULT_MODEL_INDEX, type LDrawDemo } from '../three-demo.js';
12
+ import Template from './ldraw-window.blp';
13
+
14
+ export class LDrawWindow extends Adw.ApplicationWindow {
15
+ declare private _glAreaContainer: Gtk.Box;
16
+ declare private _modelRow: Adw.ComboRow;
17
+ declare private _flatColorsRow: Adw.SwitchRow;
18
+ declare private _mergeModelRow: Adw.SwitchRow;
19
+ declare private _smoothNormalsRow: Adw.SwitchRow;
20
+ declare private _buildingStepRow: Adw.SpinRow;
21
+ declare private _displayLinesRow: Adw.SwitchRow;
22
+ declare private _conditionalLinesRow: Adw.SwitchRow;
23
+
24
+ static {
25
+ GObject.registerClass(
26
+ {
27
+ GTypeName: 'LDrawWindow',
28
+ Template,
29
+ InternalChildren: [
30
+ 'glAreaContainer',
31
+ 'modelRow',
32
+ 'flatColorsRow',
33
+ 'mergeModelRow',
34
+ 'smoothNormalsRow',
35
+ 'buildingStepRow',
36
+ 'displayLinesRow',
37
+ 'conditionalLinesRow',
38
+ ],
39
+ },
40
+ this,
41
+ );
42
+ }
43
+
44
+ constructor(application: Adw.Application) {
45
+ super({ application });
46
+
47
+ // Model ComboRow
48
+ this._modelRow.set_model(Gtk.StringList.new(MODEL_LIST.map((m) => m.name)));
49
+ this._modelRow.set_selected(DEFAULT_MODEL_INDEX);
50
+
51
+ // Building step SpinRow (initial range, updated on model load)
52
+ this._buildingStepRow.set_adjustment(
53
+ new Gtk.Adjustment({
54
+ lower: 0,
55
+ upper: 0,
56
+ step_increment: 1,
57
+ value: 0,
58
+ }),
59
+ );
60
+
61
+ // Create and insert WebGL widget
62
+ const glArea = new WebGLBridge();
63
+ glArea.set_hexpand(true);
64
+ glArea.set_vexpand(true);
65
+ glArea.installGlobals();
66
+ this._glAreaContainer.append(glArea);
67
+
68
+ Object.defineProperty(globalThis, 'innerWidth', {
69
+ get: () => glArea.get_allocated_width(),
70
+ configurable: true,
71
+ });
72
+ Object.defineProperty(globalThis, 'innerHeight', {
73
+ get: () => glArea.get_allocated_height(),
74
+ configurable: true,
75
+ });
76
+
77
+ glArea.onReady((canvas) => {
78
+ // The bundle's own directory, derived in URI SPACE rather than by
79
+ // pasting `file://` onto a native path. `GLib.filename_from_uri()`
80
+ // hands back a PLATFORM path, which on win32 is `C:\…\dist` — and
81
+ // `file://${that}/` makes `C:` the URI hostname, so every model
82
+ // fetch is refused for an invalid host. Same defect the pixel
83
+ // showcase paid for in ce4eb6aa1; keep the two spellings identical.
84
+ const assetBase = new URL('./', import.meta.url).href;
85
+
86
+ const demo = start(canvas, { assetBase }, (numSteps) => {
87
+ // Update building step range on model load
88
+ const adj = this._buildingStepRow.get_adjustment();
89
+ adj.set_upper(numSteps - 1);
90
+ adj.set_value(numSteps - 1);
91
+ });
92
+ this.connectControls(demo);
93
+ });
94
+ }
95
+
96
+ private connectControls(demo: LDrawDemo) {
97
+ this._modelRow.connect('notify::selected', () => {
98
+ demo.effectController.modelIndex = this._modelRow.selected;
99
+ demo.reloadObject(true);
100
+ });
101
+
102
+ this._flatColorsRow.connect('notify::active', () => {
103
+ demo.effectController.flatColors = this._flatColorsRow.active;
104
+ demo.reloadObject(false);
105
+ });
106
+
107
+ this._mergeModelRow.connect('notify::active', () => {
108
+ demo.effectController.mergeModel = this._mergeModelRow.active;
109
+ demo.reloadObject(false);
110
+ });
111
+
112
+ this._smoothNormalsRow.connect('notify::active', () => {
113
+ demo.effectController.smoothNormals = this._smoothNormalsRow.active;
114
+ demo.reloadObject(false);
115
+ });
116
+
117
+ this._buildingStepRow.connect('notify::value', () => {
118
+ demo.effectController.buildingStep = this._buildingStepRow.get_value();
119
+ demo.updateVisibility();
120
+ });
121
+
122
+ this._displayLinesRow.connect('notify::active', () => {
123
+ demo.effectController.displayLines = this._displayLinesRow.active;
124
+ demo.updateVisibility();
125
+ });
126
+
127
+ this._conditionalLinesRow.connect('notify::active', () => {
128
+ demo.effectController.conditionalLines = this._conditionalLinesRow.active;
129
+ demo.updateVisibility();
130
+ });
131
+ }
132
+ }
package/src/models.ts ADDED
@@ -0,0 +1,28 @@
1
+ // Shared model list for the LDraw viewer.
2
+ // LDraw.org CC BY 2.0 Parts Library.
3
+
4
+ export interface ModelEntry {
5
+ name: string;
6
+ file: string;
7
+ }
8
+
9
+ export const MODEL_LIST: readonly ModelEntry[] = [
10
+ { name: 'Car', file: 'models/car.ldr_Packed.mpd' },
11
+ { name: 'Lunar Vehicle', file: 'models/1621-1-LunarMPVVehicle.mpd_Packed.mpd' },
12
+ { name: 'Radar Truck', file: 'models/889-1-RadarTruck.mpd_Packed.mpd' },
13
+ { name: 'Trailer', file: 'models/4838-1-MiniVehicles.mpd_Packed.mpd' },
14
+ { name: 'Bulldozer', file: 'models/4915-1-MiniConstruction.mpd_Packed.mpd' },
15
+ { name: 'Helicopter', file: 'models/4918-1-MiniFlyers.mpd_Packed.mpd' },
16
+ { name: 'Plane', file: 'models/5935-1-IslandHopper.mpd_Packed.mpd' },
17
+ { name: 'Lighthouse', file: 'models/30023-1-Lighthouse.ldr_Packed.mpd' },
18
+ { name: 'X-Wing mini', file: 'models/30051-1-X-wingFighter-Mini.mpd_Packed.mpd' },
19
+ { name: 'AT-ST mini', file: 'models/30054-1-AT-ST-Mini.mpd_Packed.mpd' },
20
+ { name: 'AT-AT mini', file: 'models/4489-1-AT-AT-Mini.mpd_Packed.mpd' },
21
+ { name: 'Shuttle', file: 'models/4494-1-Imperial Shuttle-Mini.mpd_Packed.mpd' },
22
+ { name: 'TIE Interceptor', file: 'models/6965-1-TIEIntercep_4h4MXk5.mpd_Packed.mpd' },
23
+ { name: 'Star fighter', file: 'models/6966-1-JediStarfighter-Mini.mpd_Packed.mpd' },
24
+ { name: 'X-Wing', file: 'models/7140-1-X-wingFighter.mpd_Packed.mpd' },
25
+ { name: 'AT-ST', file: 'models/10174-1-ImperialAT-ST-UCS.mpd_Packed.mpd' },
26
+ ] as const;
27
+
28
+ export const DEFAULT_MODEL_INDEX = 0; // Car