@skenora/editor 0.1.2

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 (78) hide show
  1. package/README.md +51 -0
  2. package/dist/commands/annotation-commands.d.ts +12 -0
  3. package/dist/commands/annotation-commands.d.ts.map +1 -0
  4. package/dist/commands/annotation-commands.js +37 -0
  5. package/dist/commands/annotation-commands.js.map +1 -0
  6. package/dist/commands/camera-entity-commands.d.ts +12 -0
  7. package/dist/commands/camera-entity-commands.d.ts.map +1 -0
  8. package/dist/commands/camera-entity-commands.js +60 -0
  9. package/dist/commands/camera-entity-commands.js.map +1 -0
  10. package/dist/commands/camera-path-commands.d.ts +9 -0
  11. package/dist/commands/camera-path-commands.d.ts.map +1 -0
  12. package/dist/commands/camera-path-commands.js +95 -0
  13. package/dist/commands/camera-path-commands.js.map +1 -0
  14. package/dist/commands/flow-commands.d.ts +10 -0
  15. package/dist/commands/flow-commands.d.ts.map +1 -0
  16. package/dist/commands/flow-commands.js +86 -0
  17. package/dist/commands/flow-commands.js.map +1 -0
  18. package/dist/commands/geometry-commands.d.ts +8 -0
  19. package/dist/commands/geometry-commands.d.ts.map +1 -0
  20. package/dist/commands/geometry-commands.js +85 -0
  21. package/dist/commands/geometry-commands.js.map +1 -0
  22. package/dist/commands/material-assignment.d.ts +7 -0
  23. package/dist/commands/material-assignment.d.ts.map +1 -0
  24. package/dist/commands/material-assignment.js +69 -0
  25. package/dist/commands/material-assignment.js.map +1 -0
  26. package/dist/commands/material-commands.d.ts +13 -0
  27. package/dist/commands/material-commands.d.ts.map +1 -0
  28. package/dist/commands/material-commands.js +182 -0
  29. package/dist/commands/material-commands.js.map +1 -0
  30. package/dist/commands/model-import-commands.d.ts +6 -0
  31. package/dist/commands/model-import-commands.d.ts.map +1 -0
  32. package/dist/commands/model-import-commands.js +120 -0
  33. package/dist/commands/model-import-commands.js.map +1 -0
  34. package/dist/commands/particle-commands.d.ts +13 -0
  35. package/dist/commands/particle-commands.d.ts.map +1 -0
  36. package/dist/commands/particle-commands.js +47 -0
  37. package/dist/commands/particle-commands.js.map +1 -0
  38. package/dist/commands/primitive-commands.d.ts +18 -0
  39. package/dist/commands/primitive-commands.d.ts.map +1 -0
  40. package/dist/commands/primitive-commands.js +24 -0
  41. package/dist/commands/primitive-commands.js.map +1 -0
  42. package/dist/commands/procedural-commands.d.ts +13 -0
  43. package/dist/commands/procedural-commands.d.ts.map +1 -0
  44. package/dist/commands/procedural-commands.js +20 -0
  45. package/dist/commands/procedural-commands.js.map +1 -0
  46. package/dist/commands/scene-commands.d.ts +16 -0
  47. package/dist/commands/scene-commands.d.ts.map +1 -0
  48. package/dist/commands/scene-commands.js +146 -0
  49. package/dist/commands/scene-commands.js.map +1 -0
  50. package/dist/commands/shape-commands.d.ts +24 -0
  51. package/dist/commands/shape-commands.d.ts.map +1 -0
  52. package/dist/commands/shape-commands.js +95 -0
  53. package/dist/commands/shape-commands.js.map +1 -0
  54. package/dist/commands/texture-animation-commands.d.ts +5 -0
  55. package/dist/commands/texture-animation-commands.d.ts.map +1 -0
  56. package/dist/commands/texture-animation-commands.js +23 -0
  57. package/dist/commands/texture-animation-commands.js.map +1 -0
  58. package/dist/core/candidate-commit.d.ts +59 -0
  59. package/dist/core/candidate-commit.d.ts.map +1 -0
  60. package/dist/core/candidate-commit.js +54 -0
  61. package/dist/core/candidate-commit.js.map +1 -0
  62. package/dist/core/commands.d.ts +20 -0
  63. package/dist/core/commands.d.ts.map +1 -0
  64. package/dist/core/commands.js +282 -0
  65. package/dist/core/commands.js.map +1 -0
  66. package/dist/core/editor-session.d.ts +92 -0
  67. package/dist/core/editor-session.d.ts.map +1 -0
  68. package/dist/core/editor-session.js +496 -0
  69. package/dist/core/editor-session.js.map +1 -0
  70. package/dist/index.d.ts +18 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +18 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/properties/property-descriptors.d.ts +58 -0
  75. package/dist/properties/property-descriptors.d.ts.map +1 -0
  76. package/dist/properties/property-descriptors.js +832 -0
  77. package/dist/properties/property-descriptors.js.map +1 -0
  78. package/package.json +34 -0
@@ -0,0 +1,832 @@
1
+ import { DEFAULT_ATMOSPHERE_SKY_PARAMETERS, getMaterialBindingsForEntity, } from "@skenora/contracts";
2
+ import { duplicateEntityTreeCommand, removeEntityCommand, renameEntityCommand, reparentEntityCommand, setEntityEnabledCommand, setEntityVisibleCommand, setPropertyCommand, } from "../core/commands.js";
3
+ export const MIXED_VALUE = Object.freeze({ kind: "mixed" });
4
+ export class EditFacade {
5
+ #session;
6
+ #providers = new Set();
7
+ constructor(session) {
8
+ this.#session = session;
9
+ }
10
+ registerPropertyProvider(provider) {
11
+ this.#providers.add(provider);
12
+ return () => this.#providers.delete(provider);
13
+ }
14
+ describe(entityId) {
15
+ const entity = this.#session.snapshot.entities[entityId];
16
+ if (!entity)
17
+ return [];
18
+ return [
19
+ ...createCoreEntityDescriptors(entity),
20
+ ...createTypedEntityDescriptors(entity),
21
+ ...createMaterialDescriptors(this.#session.snapshot, entity),
22
+ ...[...this.#providers].flatMap((provider) => provider(this.#session.snapshot, entity)),
23
+ ];
24
+ }
25
+ describeMany(entityIds) {
26
+ const targets = [...new Set(entityIds)].filter((entityId) => this.#session.snapshot.entities[entityId]);
27
+ if (targets.length === 0)
28
+ return [];
29
+ const descriptorSets = targets.map((entityId) => this.describe(entityId));
30
+ const first = descriptorSets[0] ?? [];
31
+ return first.flatMap((descriptor) => {
32
+ const matches = descriptorSets.map((descriptors) => descriptors.find((candidate) => candidate.id === descriptor.id &&
33
+ candidate.kind === descriptor.kind &&
34
+ candidate.group === descriptor.group));
35
+ if (matches.some((candidate) => !candidate))
36
+ return [];
37
+ const values = matches.map((candidate) => candidate?.value);
38
+ const value = values.every((candidate) => propertyValuesEqual(candidate, values[0]))
39
+ ? descriptor.value
40
+ : MIXED_VALUE;
41
+ return [
42
+ {
43
+ ...descriptor,
44
+ paths: matches.flatMap((candidate) => candidate ? [candidate.path] : []),
45
+ value,
46
+ },
47
+ ];
48
+ });
49
+ }
50
+ describeScene() {
51
+ const document = this.#session.snapshot;
52
+ const skybox = document.environment.background.skybox;
53
+ const atmosphere = skybox?.source.kind === "procedural"
54
+ ? skybox.source.parameters
55
+ : undefined;
56
+ const skyboxDescriptors = skybox
57
+ ? [
58
+ {
59
+ id: "skybox-rotation",
60
+ label: "Sky rotation",
61
+ group: "environment",
62
+ kind: "number",
63
+ path: ["environment", "background", "skybox", "rotationY"],
64
+ value: skybox.rotationY ?? 0,
65
+ step: 0.01,
66
+ },
67
+ {
68
+ id: "skybox-blur",
69
+ label: "Sky blur",
70
+ group: "environment",
71
+ kind: "number",
72
+ path: ["environment", "background", "skybox", "blur"],
73
+ value: skybox.blur ?? 0,
74
+ min: 0,
75
+ max: 1,
76
+ step: 0.01,
77
+ },
78
+ {
79
+ id: "skybox-fog-participation",
80
+ label: "Sky fog participation",
81
+ group: "environment",
82
+ kind: "select",
83
+ path: ["environment", "background", "skybox", "fogParticipation"],
84
+ value: skybox.fogParticipation ?? "disabled",
85
+ options: [
86
+ { label: "Disabled", value: "disabled" },
87
+ { label: "Inherit scene fog", value: "inherit" },
88
+ ],
89
+ },
90
+ ]
91
+ : [];
92
+ const atmosphereDescriptors = atmosphere
93
+ ? createAtmosphereDescriptors(atmosphere)
94
+ : skybox?.source.kind === "procedural"
95
+ ? createAtmosphereDescriptors({})
96
+ : [];
97
+ return [
98
+ {
99
+ id: "environment-intensity",
100
+ label: "Environment intensity",
101
+ group: "environment",
102
+ kind: "number",
103
+ path: ["environment", "intensity"],
104
+ value: document.environment.intensity,
105
+ min: 0,
106
+ step: 0.05,
107
+ },
108
+ {
109
+ id: "environment-rotation",
110
+ label: "Environment rotation",
111
+ group: "environment",
112
+ kind: "number",
113
+ path: ["environment", "rotationY"],
114
+ value: document.environment.rotationY,
115
+ step: 0.01,
116
+ },
117
+ {
118
+ id: "background-color",
119
+ label: "Background",
120
+ group: "environment",
121
+ kind: "color",
122
+ path: ["environment", "background", "color"],
123
+ value: document.environment.background.color,
124
+ },
125
+ ...skyboxDescriptors,
126
+ ...atmosphereDescriptors,
127
+ {
128
+ id: "camera-fov",
129
+ label: "Camera FOV",
130
+ group: "general",
131
+ kind: "number",
132
+ path: ["camera", "fov"],
133
+ value: document.camera.fov ?? 0.8,
134
+ min: 0.1,
135
+ max: Math.PI - 0.1,
136
+ step: 0.01,
137
+ },
138
+ {
139
+ id: "post-bloom",
140
+ label: "Bloom",
141
+ group: "environment",
142
+ kind: "boolean",
143
+ path: ["postProcess", "bloom"],
144
+ value: document.postProcess.bloom,
145
+ },
146
+ {
147
+ id: "post-enabled",
148
+ label: "Post processing",
149
+ group: "environment",
150
+ kind: "boolean",
151
+ path: ["postProcess", "enabled"],
152
+ value: document.postProcess.enabled,
153
+ },
154
+ {
155
+ id: "post-exposure",
156
+ label: "Exposure",
157
+ group: "environment",
158
+ kind: "number",
159
+ path: ["postProcess", "exposure"],
160
+ value: document.postProcess.exposure,
161
+ min: 0,
162
+ step: 0.05,
163
+ },
164
+ {
165
+ id: "post-contrast",
166
+ label: "Contrast",
167
+ group: "environment",
168
+ kind: "number",
169
+ path: ["postProcess", "contrast"],
170
+ value: document.postProcess.contrast,
171
+ min: 0,
172
+ step: 0.05,
173
+ },
174
+ {
175
+ id: "fog-enabled",
176
+ label: "Fog",
177
+ group: "environment",
178
+ kind: "boolean",
179
+ path: ["fog", "enabled"],
180
+ value: document.fog.enabled,
181
+ },
182
+ {
183
+ id: "fog-mode",
184
+ label: "Fog mode",
185
+ group: "environment",
186
+ kind: "select",
187
+ path: ["fog", "mode"],
188
+ value: document.fog.mode,
189
+ options: [
190
+ { label: "Linear", value: "linear" },
191
+ { label: "Exponential", value: "exp" },
192
+ { label: "Exponential squared", value: "exp2" },
193
+ ],
194
+ },
195
+ {
196
+ id: "fog-color",
197
+ label: "Fog color",
198
+ group: "environment",
199
+ kind: "color",
200
+ path: ["fog", "color"],
201
+ value: document.fog.color,
202
+ },
203
+ {
204
+ id: "fog-density",
205
+ label: "Fog density",
206
+ group: "environment",
207
+ kind: "number",
208
+ path: ["fog", "density"],
209
+ value: document.fog.density,
210
+ min: 0,
211
+ step: 0.001,
212
+ },
213
+ {
214
+ id: "weather-enabled",
215
+ label: "Weather",
216
+ group: "environment",
217
+ kind: "boolean",
218
+ path: ["weather", "enabled"],
219
+ value: document.weather.enabled,
220
+ },
221
+ {
222
+ id: "weather-type",
223
+ label: "Weather type",
224
+ group: "environment",
225
+ kind: "select",
226
+ path: ["weather", "type"],
227
+ value: document.weather.type,
228
+ options: [
229
+ { label: "Rain", value: "rain" },
230
+ { label: "Snow", value: "snow" },
231
+ ],
232
+ },
233
+ {
234
+ id: "weather-intensity",
235
+ label: "Weather intensity",
236
+ group: "environment",
237
+ kind: "number",
238
+ path: ["weather", "intensity"],
239
+ value: document.weather.intensity,
240
+ min: 0,
241
+ step: 10,
242
+ },
243
+ {
244
+ id: "weather-wind",
245
+ label: "Weather wind",
246
+ group: "environment",
247
+ kind: "vector2",
248
+ path: ["weather", "wind"],
249
+ value: document.weather.wind,
250
+ step: 0.1,
251
+ },
252
+ {
253
+ id: "ground-enabled",
254
+ label: "Ground",
255
+ group: "environment",
256
+ kind: "boolean",
257
+ path: ["ground", "enabled"],
258
+ value: document.ground.enabled,
259
+ },
260
+ {
261
+ id: "ground-size",
262
+ label: "Ground size",
263
+ group: "environment",
264
+ kind: "number",
265
+ path: ["ground", "size"],
266
+ value: document.ground.size,
267
+ min: 0.01,
268
+ step: 0.5,
269
+ },
270
+ {
271
+ id: "ground-mode",
272
+ label: "Ground mode",
273
+ group: "environment",
274
+ kind: "select",
275
+ path: ["ground", "mode"],
276
+ value: document.ground.mode,
277
+ options: [
278
+ { label: "Solid", value: "solid" },
279
+ { label: "Shadow catcher", value: "shadow-catcher" },
280
+ ],
281
+ },
282
+ {
283
+ id: "ground-color",
284
+ label: "Ground color",
285
+ group: "environment",
286
+ kind: "color",
287
+ path: ["ground", "color"],
288
+ value: document.ground.color,
289
+ },
290
+ ];
291
+ }
292
+ edit(request) {
293
+ if (request.expectedRevision !== undefined &&
294
+ request.expectedRevision !== this.#session.revision) {
295
+ throw new Error(`Editor revision mismatch: expected ${request.expectedRevision}, current ${this.#session.revision}`);
296
+ }
297
+ const paths = request.paths?.length ? request.paths : [request.path];
298
+ const commands = paths.map((path) => setPropertyCommand(path, request.value, request.label ?? "Edit property"));
299
+ if (!request.mergeKey && commands.length === 1) {
300
+ const command = commands[0];
301
+ return command ? this.#session.execute(command) : null;
302
+ }
303
+ const transaction = this.#session.beginTransaction({
304
+ label: request.label ?? "Edit property",
305
+ ...(request.mergeKey === undefined ? {} : { mergeKey: request.mergeKey }),
306
+ ...(request.mergeWindowMs === undefined
307
+ ? {}
308
+ : { mergeWindowMs: request.mergeWindowMs }),
309
+ });
310
+ try {
311
+ for (const command of commands)
312
+ transaction.execute(command);
313
+ return transaction.commit();
314
+ }
315
+ catch (error) {
316
+ if (transaction.active)
317
+ transaction.cancel();
318
+ throw error;
319
+ }
320
+ }
321
+ rename(entityIds, name) {
322
+ return this.#session.transaction("Rename entities", entityIds.map((entityId) => renameEntityCommand(entityId, name)));
323
+ }
324
+ setEnabled(entityIds, enabled) {
325
+ return this.#session.transaction(enabled ? "Enable entities" : "Disable entities", entityIds.map((entityId) => setEntityEnabledCommand(entityId, enabled)));
326
+ }
327
+ setVisible(entityIds, visible) {
328
+ return this.#session.transaction(visible ? "Show entities" : "Hide entities", entityIds.map((entityId) => setEntityVisibleCommand(entityId, visible)));
329
+ }
330
+ setPosition(entityIds, position) {
331
+ return this.#setTransformPart(entityIds, "position", position);
332
+ }
333
+ setRotation(entityIds, rotation) {
334
+ return this.#setTransformPart(entityIds, "rotation", rotation);
335
+ }
336
+ setScale(entityIds, scaling) {
337
+ return this.#setTransformPart(entityIds, "scaling", scaling);
338
+ }
339
+ setParent(entityIds, parentId) {
340
+ return this.#session.transaction("Reparent entities", entityIds.map((entityId) => reparentEntityCommand(entityId, parentId)));
341
+ }
342
+ duplicate(entityIds) {
343
+ return this.#session.transaction("Duplicate entities", entityIds.map((entityId) => duplicateEntityTreeCommand(entityId)));
344
+ }
345
+ remove(entityIds) {
346
+ return this.#session.transaction("Remove entities", entityIds.map((entityId) => removeEntityCommand(entityId)));
347
+ }
348
+ #setTransformPart(entityIds, part, value) {
349
+ return this.#session.transaction(`Set entity ${part}`, entityIds.map((entityId) => setPropertyCommand(["entities", entityId, "transform", part], value, `Set ${part}`)));
350
+ }
351
+ }
352
+ export function isMixedValue(value) {
353
+ return (value !== null &&
354
+ typeof value === "object" &&
355
+ value.kind === "mixed");
356
+ }
357
+ function propertyValuesEqual(left, right) {
358
+ if (Object.is(left, right))
359
+ return true;
360
+ if (left === null ||
361
+ right === null ||
362
+ typeof left !== "object" ||
363
+ typeof right !== "object") {
364
+ return false;
365
+ }
366
+ return JSON.stringify(left) === JSON.stringify(right);
367
+ }
368
+ export function createTypedEntityDescriptors(entity) {
369
+ const root = ["entities", entity.id, "properties"];
370
+ if (entity.type === "camera") {
371
+ const value = entity.properties;
372
+ return [
373
+ {
374
+ id: "camera-projection",
375
+ label: "Projection",
376
+ group: "camera",
377
+ kind: "select",
378
+ path: [...root, "projection"],
379
+ value: value.projection ?? "perspective",
380
+ options: [
381
+ { label: "Perspective", value: "perspective" },
382
+ { label: "Orthographic", value: "orthographic" },
383
+ ],
384
+ },
385
+ numberDescriptor("camera-fov", "FOV", "camera", [...root, "fov"], value.fov ?? 0.8, 0.01, Math.PI - 0.01, 0.01),
386
+ numberDescriptor("camera-near", "Near clip", "camera", [...root, "minZ"], value.minZ ?? 0.01, 0.0001, undefined, 0.01),
387
+ numberDescriptor("camera-far", "Far clip", "camera", [...root, "maxZ"], value.maxZ ?? 10_000, 0.01, undefined, 1),
388
+ numberDescriptor("camera-speed", "Control speed", "camera", [...root, "speed"], value.speed ?? 1, 0, undefined, 0.1),
389
+ ...(value.projection === "orthographic"
390
+ ? [
391
+ numberDescriptor("camera-orthographic-size", "Orthographic size", "camera", [...root, "orthographicSize"], value.orthographicSize ?? 10, 0.01, undefined, 0.1),
392
+ ]
393
+ : []),
394
+ ];
395
+ }
396
+ if (entity.type === "particle") {
397
+ const value = entity.properties;
398
+ return [
399
+ {
400
+ id: "particle-emitter",
401
+ label: "Emitter",
402
+ group: "particle",
403
+ kind: "select",
404
+ path: [...root, "emitter"],
405
+ value: value.emitter ?? "point",
406
+ options: [
407
+ "point",
408
+ "box",
409
+ "sphere",
410
+ "directed-sphere",
411
+ "hemisphere",
412
+ "cone",
413
+ "directed-cone",
414
+ "cylinder",
415
+ "directed-cylinder",
416
+ ].map((item) => ({ label: item, value: item })),
417
+ },
418
+ numberDescriptor("particle-capacity", "Capacity", "particle", [...root, "capacity"], value.capacity ?? 2_000, 1, undefined, 100),
419
+ numberDescriptor("particle-rate", "Emit rate", "particle", [...root, "emitRate"], value.emitRate ?? 100, 0, undefined, 10),
420
+ numberDescriptor("particle-life-min", "Minimum lifetime", "particle", [...root, "minLifeTime"], value.minLifeTime ?? 0.5, 0, undefined, 0.1),
421
+ numberDescriptor("particle-life-max", "Maximum lifetime", "particle", [...root, "maxLifeTime"], value.maxLifeTime ?? 2, 0, undefined, 0.1),
422
+ numberDescriptor("particle-size-min", "Minimum size", "particle", [...root, "minSize"], value.minSize ?? 0.05, 0, undefined, 0.01),
423
+ numberDescriptor("particle-size-max", "Maximum size", "particle", [...root, "maxSize"], value.maxSize ?? 0.15, 0, undefined, 0.01),
424
+ {
425
+ id: "particle-gravity",
426
+ label: "Gravity",
427
+ group: "particle",
428
+ kind: "vector3",
429
+ path: [...root, "gravity"],
430
+ value: value.gravity ?? { x: 0, y: 0, z: 0 },
431
+ },
432
+ {
433
+ id: "particle-auto-start",
434
+ label: "Auto start",
435
+ group: "particle",
436
+ kind: "boolean",
437
+ path: [...root, "autoStart"],
438
+ value: value.autoStart ?? true,
439
+ },
440
+ ];
441
+ }
442
+ if (entity.type === "shape") {
443
+ const value = entity.properties;
444
+ return [
445
+ {
446
+ id: "shape-kind",
447
+ label: "Shape",
448
+ group: "shape",
449
+ kind: "select",
450
+ path: [...root, "shape"],
451
+ value: value.shape ?? "polygon",
452
+ options: [
453
+ "rectangle",
454
+ "circle",
455
+ "polygon",
456
+ "polyline",
457
+ "tube",
458
+ "wall",
459
+ "box",
460
+ ].map((item) => ({ label: item, value: item })),
461
+ },
462
+ {
463
+ id: "shape-closed",
464
+ label: "Closed",
465
+ group: "shape",
466
+ kind: "boolean",
467
+ path: [...root, "closed"],
468
+ value: value.closed ?? true,
469
+ },
470
+ {
471
+ id: "shape-fill",
472
+ label: "Fill color",
473
+ group: "shape",
474
+ kind: "color",
475
+ path: [...root, "fillColor"],
476
+ value: value.fillColor ?? { r: 0.2, g: 0.55, b: 1 },
477
+ },
478
+ numberDescriptor("shape-alpha", "Fill alpha", "shape", [...root, "fillAlpha"], value.fillAlpha ?? 0.35, 0, 1, 0.01),
479
+ {
480
+ id: "shape-stroke",
481
+ label: "Stroke color",
482
+ group: "shape",
483
+ kind: "color",
484
+ path: [...root, "strokeColor"],
485
+ value: value.strokeColor ?? { r: 0.3, g: 0.7, b: 1 },
486
+ },
487
+ ...(value.shape === "tube"
488
+ ? [
489
+ numberDescriptor("shape-tube-radius", "Tube radius", "shape", [...root, "tubeRadius"], value.tubeRadius ?? 0.05, 0.001, undefined, 0.01),
490
+ ]
491
+ : []),
492
+ ...(value.shape === "wall"
493
+ ? [
494
+ numberDescriptor("shape-wall-height", "Wall height", "shape", [...root, "wallHeight"], value.wallHeight ?? 1, 0.001, undefined, 0.1),
495
+ ]
496
+ : []),
497
+ ];
498
+ }
499
+ if (entity.type === "annotation") {
500
+ const value = entity.properties;
501
+ return [
502
+ {
503
+ id: "annotation-text",
504
+ label: "Text",
505
+ group: "annotation",
506
+ kind: "string",
507
+ path: [...root, "text"],
508
+ value: value.text ?? "",
509
+ },
510
+ {
511
+ id: "annotation-color",
512
+ label: "Color",
513
+ group: "annotation",
514
+ kind: "color",
515
+ path: [...root, "color"],
516
+ value: value.color ?? "#ffffff",
517
+ },
518
+ {
519
+ id: "annotation-offset",
520
+ label: "Offset",
521
+ group: "annotation",
522
+ kind: "vector3",
523
+ path: [...root, "offset"],
524
+ value: value.offset ?? { x: 0, y: 0, z: 0 },
525
+ },
526
+ numberDescriptor("annotation-priority", "Priority", "annotation", [...root, "priority"], value.priority ?? 0, undefined, undefined, 1),
527
+ ];
528
+ }
529
+ if (entity.type === "primitive") {
530
+ const value = entity.properties ?? {};
531
+ return [
532
+ {
533
+ id: "primitive-kind",
534
+ label: "Primitive",
535
+ group: "custom",
536
+ kind: "select",
537
+ path: [...root, "primitive"],
538
+ value: value.primitive ?? "box",
539
+ options: ["box", "sphere", "plane", "cylinder", "torus"].map((item) => ({ label: item, value: item })),
540
+ },
541
+ numberDescriptor("primitive-size", "Size", "custom", [...root, "size"], typeof value.size === "number" ? value.size : 1, 0.001, undefined, 0.1),
542
+ {
543
+ id: "primitive-color",
544
+ label: "Color",
545
+ group: "custom",
546
+ kind: "color",
547
+ path: [...root, "color"],
548
+ value: value.color ?? { r: 0.7, g: 0.72, b: 0.76 },
549
+ },
550
+ ];
551
+ }
552
+ if (entity.type === "procedural") {
553
+ const value = entity.properties;
554
+ return [
555
+ {
556
+ id: "procedural-color",
557
+ label: "Fallback color",
558
+ group: "custom",
559
+ kind: "color",
560
+ path: [...root, "color"],
561
+ value: value.color ?? { r: 0.7, g: 0.72, b: 0.76 },
562
+ },
563
+ ];
564
+ }
565
+ return [];
566
+ }
567
+ function numberDescriptor(id, label, group, path, value, min, max, step) {
568
+ return {
569
+ id,
570
+ label,
571
+ group,
572
+ kind: "number",
573
+ path,
574
+ value,
575
+ ...(min === undefined ? {} : { min }),
576
+ ...(max === undefined ? {} : { max }),
577
+ ...(step === undefined ? {} : { step }),
578
+ };
579
+ }
580
+ export function createMaterialDescriptors(document, entity) {
581
+ return getMaterialBindingsForEntity(document, entity.id).flatMap((binding) => {
582
+ const materialId = binding.materialId;
583
+ const material = document.materials[materialId];
584
+ if (!material)
585
+ return [];
586
+ const root = ["materials", materialId];
587
+ return [
588
+ ...(material.baseColor
589
+ ? [
590
+ {
591
+ id: `${materialId}-baseColor`,
592
+ label: `${binding.targetName ?? materialId} color`,
593
+ group: "material",
594
+ kind: "color",
595
+ path: [...root, "baseColor"],
596
+ value: material.baseColor,
597
+ },
598
+ ]
599
+ : []),
600
+ {
601
+ id: `${materialId}-metallic`,
602
+ label: "Metallic",
603
+ group: "material",
604
+ kind: "number",
605
+ path: [...root, "metallic"],
606
+ value: material.metallic ?? 0,
607
+ min: 0,
608
+ max: 1,
609
+ step: 0.01,
610
+ },
611
+ {
612
+ id: `${materialId}-roughness`,
613
+ label: "Roughness",
614
+ group: "material",
615
+ kind: "number",
616
+ path: [...root, "roughness"],
617
+ value: material.roughness ?? 1,
618
+ min: 0,
619
+ max: 1,
620
+ step: 0.01,
621
+ },
622
+ {
623
+ id: `${materialId}-alpha`,
624
+ label: "Alpha",
625
+ group: "material",
626
+ kind: "number",
627
+ path: [...root, "alpha"],
628
+ value: material.alpha ?? 1,
629
+ min: 0,
630
+ max: 1,
631
+ step: 0.01,
632
+ },
633
+ {
634
+ id: `${materialId}-emissive-intensity`,
635
+ label: "Emissive intensity",
636
+ group: "material",
637
+ kind: "number",
638
+ path: [...root, "emissiveIntensity"],
639
+ value: material.emissiveIntensity ?? 1,
640
+ min: 0,
641
+ step: 0.05,
642
+ },
643
+ {
644
+ id: `${materialId}-double-sided`,
645
+ label: "Double sided",
646
+ group: "material",
647
+ kind: "boolean",
648
+ path: [...root, "doubleSided"],
649
+ value: material.doubleSided ?? false,
650
+ },
651
+ ...(material.clearCoat
652
+ ? [
653
+ {
654
+ id: `${materialId}-clear-coat`,
655
+ label: "Clear coat",
656
+ group: "material",
657
+ kind: "boolean",
658
+ path: [...root, "clearCoat", "enabled"],
659
+ value: material.clearCoat.enabled,
660
+ },
661
+ {
662
+ id: `${materialId}-clear-coat-intensity`,
663
+ label: "Clear coat intensity",
664
+ group: "material",
665
+ kind: "number",
666
+ path: [...root, "clearCoat", "intensity"],
667
+ value: material.clearCoat.intensity,
668
+ min: 0,
669
+ max: 1,
670
+ step: 0.01,
671
+ },
672
+ ]
673
+ : []),
674
+ ];
675
+ });
676
+ }
677
+ function createAtmosphereDescriptors(parameters) {
678
+ const values = { ...DEFAULT_ATMOSPHERE_SKY_PARAMETERS, ...parameters };
679
+ const path = [
680
+ "environment",
681
+ "background",
682
+ "skybox",
683
+ "source",
684
+ "parameters",
685
+ ];
686
+ return [
687
+ {
688
+ id: "sky-luminance",
689
+ label: "Sky luminance",
690
+ group: "environment",
691
+ kind: "number",
692
+ path: [...path, "luminance"],
693
+ value: values.luminance,
694
+ min: 0,
695
+ max: 10,
696
+ step: 0.05,
697
+ },
698
+ {
699
+ id: "sky-turbidity",
700
+ label: "Sky turbidity",
701
+ group: "environment",
702
+ kind: "number",
703
+ path: [...path, "turbidity"],
704
+ value: values.turbidity,
705
+ min: 0,
706
+ max: 100,
707
+ step: 0.1,
708
+ },
709
+ {
710
+ id: "sky-rayleigh",
711
+ label: "Sky Rayleigh scattering",
712
+ group: "environment",
713
+ kind: "number",
714
+ path: [...path, "rayleigh"],
715
+ value: values.rayleigh,
716
+ min: 0,
717
+ max: 10,
718
+ step: 0.05,
719
+ },
720
+ {
721
+ id: "sky-mie-coefficient",
722
+ label: "Sky Mie coefficient",
723
+ group: "environment",
724
+ kind: "number",
725
+ path: [...path, "mieCoefficient"],
726
+ value: values.mieCoefficient,
727
+ min: 0,
728
+ max: 0.1,
729
+ step: 0.001,
730
+ },
731
+ {
732
+ id: "sky-mie-direction",
733
+ label: "Sky Mie direction",
734
+ group: "environment",
735
+ kind: "number",
736
+ path: [...path, "mieDirectionalG"],
737
+ value: values.mieDirectionalG,
738
+ min: 0,
739
+ max: 1,
740
+ step: 0.01,
741
+ },
742
+ {
743
+ id: "sky-sun-disc-direction",
744
+ label: "Sky sun-disc direction",
745
+ group: "environment",
746
+ kind: "vector3",
747
+ path: [...path, "sunDiskDirection"],
748
+ value: values.sunDiskDirection,
749
+ step: 0.01,
750
+ },
751
+ {
752
+ id: "sky-horizon-offset",
753
+ label: "Sky horizon offset",
754
+ group: "environment",
755
+ kind: "number",
756
+ path: [...path, "horizonOffset"],
757
+ value: values.horizonOffset,
758
+ step: 0.1,
759
+ },
760
+ {
761
+ id: "sky-up",
762
+ label: "Sky up axis",
763
+ group: "environment",
764
+ kind: "vector3",
765
+ path: [...path, "up"],
766
+ value: values.up,
767
+ step: 0.01,
768
+ },
769
+ {
770
+ id: "sky-dithering",
771
+ label: "Sky dithering",
772
+ group: "environment",
773
+ kind: "boolean",
774
+ path: [...path, "dithering"],
775
+ value: values.dithering,
776
+ },
777
+ ];
778
+ }
779
+ export function createCoreEntityDescriptors(entity) {
780
+ const root = ["entities", entity.id];
781
+ return [
782
+ {
783
+ id: "name",
784
+ label: "Name",
785
+ group: "general",
786
+ kind: "string",
787
+ path: [...root, "name"],
788
+ value: entity.name,
789
+ },
790
+ {
791
+ id: "enabled",
792
+ label: "Enabled",
793
+ group: "general",
794
+ kind: "boolean",
795
+ path: [...root, "enabled"],
796
+ value: entity.enabled,
797
+ },
798
+ {
799
+ id: "visible",
800
+ label: "Visible",
801
+ group: "general",
802
+ kind: "boolean",
803
+ path: [...root, "visible"],
804
+ value: entity.visible,
805
+ },
806
+ {
807
+ id: "position",
808
+ label: "Position",
809
+ group: "transform",
810
+ kind: "vector3",
811
+ path: [...root, "transform", "position"],
812
+ value: entity.transform.position,
813
+ },
814
+ {
815
+ id: "rotation",
816
+ label: "Rotation",
817
+ group: "transform",
818
+ kind: "vector3",
819
+ path: [...root, "transform", "rotation"],
820
+ value: entity.transform.rotation,
821
+ },
822
+ {
823
+ id: "scaling",
824
+ label: "Scaling",
825
+ group: "transform",
826
+ kind: "vector3",
827
+ path: [...root, "transform", "scaling"],
828
+ value: entity.transform.scaling,
829
+ },
830
+ ];
831
+ }
832
+ //# sourceMappingURL=property-descriptors.js.map