@volter/editor-blender 0.1.8 → 0.1.10
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/contributions/blender-header-menus.tsx +4 -9
- package/contributions/blender-icons.source.mjs +74 -6
- package/contributions/blender-outliner-authoring.ts +10 -21
- package/contributions/blender-palette.source.mjs +16 -5
- package/contributions/blender-properties-view.tsx +28 -9
- package/contributions/blender-runtime-skin.ts +6 -0
- package/contributions/blender-runtime.document.tsx +219 -23
- package/contributions/blender.command.ts +5 -1
- package/contributions/blender.environment.ts +32 -0
- package/contributions/blender.icons.json +22 -2
- package/contributions/blender.icons.traced.json +28 -0
- package/contributions/blender.keymap.ts +33 -0
- package/contributions/blender.palette.json +6 -0
- package/contributions/blender.status.tsx +2 -6
- package/contributions/blender.style.ts +30 -89
- package/contributions/model.layout.ts +7 -7
- package/contributions/models.finder.ts +16 -0
- package/contributions/sculpt.layout.ts +3 -0
- package/contributions/shading.layout.ts +3 -0
- package/contributions/studiolights/world/city.exr +0 -0
- package/contributions/studiolights/world/courtyard.exr +0 -0
- package/contributions/studiolights/world/forest.exr +0 -0
- package/contributions/studiolights/world/interior.exr +0 -0
- package/contributions/studiolights/world/license.txt +15 -0
- package/contributions/studiolights/world/night.exr +0 -0
- package/contributions/studiolights/world/studio.exr +0 -0
- package/contributions/studiolights/world/sunrise.exr +0 -0
- package/contributions/studiolights/world/sunset.exr +0 -0
- package/contributions/uv-editing.layout.ts +3 -0
- package/dist-node/serving.mjs +624 -0
- package/host/blender-runtime-host.ts +11 -1
- package/node/blender-mcp.ts +2 -2
- package/package.json +20 -10
- package/serving/blender-routes.ts +427 -0
- package/serving/blender-wasm-artifact.ts +449 -0
- package/serving/index.ts +10 -0
- package/src/layouts.tsx +4 -5
- package/src/presentation.ts +111 -0
- package/src/regions.ts +59 -0
|
@@ -20,19 +20,35 @@
|
|
|
20
20
|
*
|
|
21
21
|
* WITHOUT AN ENTRY it is still the document `blender-start` presents into: a
|
|
22
22
|
* project that lists no `.blend` of its own gets one Model document at the
|
|
23
|
-
* standing `blender:runtime` address (`
|
|
23
|
+
* standing `blender:runtime` address (`models.finder.ts` lists it), and
|
|
24
24
|
* the session opens its default `models/model.blend`.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
// How the `model` stage this document builds behaves (its starting presentation).
|
|
28
|
+
import { blenderViewFieldOfView } from '../src/presentation';
|
|
27
29
|
import { blenderModelView } from '@volter/blender-engine/browser/three/blender-runtime-view';
|
|
28
30
|
import type { ToolContributionProps, ToolDocumentToolbar } from '@volter/editor-sdk/contributions';
|
|
29
31
|
import { editorHost } from '@volter/editor-sdk/host';
|
|
30
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
DOCUMENT_STUDIO_PRESET,
|
|
34
|
+
subscribeViewportPresentation,
|
|
35
|
+
viewPresentation,
|
|
36
|
+
} from '@volter/editor-sdk/kit/viewport-presentation';
|
|
37
|
+
import {
|
|
38
|
+
type MouseEvent as ReactMouseEvent,
|
|
39
|
+
type PointerEvent as ReactPointerEvent,
|
|
40
|
+
useCallback,
|
|
41
|
+
useEffect,
|
|
42
|
+
useRef,
|
|
43
|
+
useState,
|
|
44
|
+
useSyncExternalStore,
|
|
45
|
+
} from 'react';
|
|
31
46
|
import * as THREE from 'three';
|
|
32
|
-
import { bindModelDocument, openModelDocumentBlend } from '../host/blender-runtime-host';
|
|
47
|
+
import { bindModelDocument, blenderExecute, openModelDocumentBlend } from '../host/blender-runtime-host';
|
|
33
48
|
import { BlenderObjectModeHeader } from './blender-header-menus';
|
|
34
49
|
import { createBlenderOutlinerAuthoring } from './blender-outliner-authoring';
|
|
35
50
|
import { blenderSkin } from './blender-runtime-skin';
|
|
51
|
+
import { onViewportStages, viewportStages } from '@volter/editor-threejs/viewport-door';
|
|
36
52
|
|
|
37
53
|
export const point = 'workspace.document';
|
|
38
54
|
export const title = 'Blender Model';
|
|
@@ -78,6 +94,51 @@ export const inspectorBuiltins: readonly string[] = [];
|
|
|
78
94
|
// Timeline binds a skeleton into the same presented graph and there is exactly
|
|
79
95
|
// one set of presented objects (`blender-runtime-skin.ts`).
|
|
80
96
|
const view = blenderModelView;
|
|
97
|
+
/** THE SUBJECT LINE'S FRAME IS THE ONE ON SCREEN: the skin's playhead, which during playback
|
|
98
|
+
* runs ahead of `frame_current` (written once, on pause), as Blender's own `(frame)` does. */
|
|
99
|
+
const subjectOfView = () => {
|
|
100
|
+
const playhead = blenderSkin.playhead();
|
|
101
|
+
return view.subjectLine(playhead === null ? null : Math.floor(playhead));
|
|
102
|
+
};
|
|
103
|
+
const gridScale = (worldPerDevicePixel: number) => view.gridUnitName(worldPerDevicePixel);
|
|
104
|
+
/** BLENDER'S CAMERA VIEW (`blender-runtime-camera-view.ts`): which camera, and how it fits. */
|
|
105
|
+
const cameraView = {
|
|
106
|
+
camera: () => view.cameraViewCamera(),
|
|
107
|
+
zoom: view.cameraViewZoom,
|
|
108
|
+
pan: view.cameraViewPan,
|
|
109
|
+
showing: (camera: string | null) => view.setCameraViewShowing(camera),
|
|
110
|
+
// A LOCKED camera view moves the camera (`ED_view3d_camera_lock_sync`: its scale kept); the
|
|
111
|
+
// navigation's end is the one step Blender's history records.
|
|
112
|
+
setPose: (
|
|
113
|
+
camera: string,
|
|
114
|
+
position: readonly [number, number, number],
|
|
115
|
+
quaternion: readonly [number, number, number, number],
|
|
116
|
+
final: boolean,
|
|
117
|
+
) => {
|
|
118
|
+
const { location, rotation } = view.blenderPose(position, quaternion);
|
|
119
|
+
return blenderExecute(
|
|
120
|
+
'from mathutils import Matrix, Quaternion, Vector\n' +
|
|
121
|
+
`import bpy\nobject = bpy.data.objects[${JSON.stringify(camera)}]\n` +
|
|
122
|
+
`object.matrix_world = Matrix.LocRotScale(Vector((${location.join(', ')})), ` +
|
|
123
|
+
`Quaternion((${rotation.join(', ')})), object.matrix_world.to_scale())`,
|
|
124
|
+
final,
|
|
125
|
+
'Lock Camera to View',
|
|
126
|
+
).then(
|
|
127
|
+
(answer) => {
|
|
128
|
+
if (answer.error !== null)
|
|
129
|
+
editorHost().console.error(`Blender refused the camera's pose: ${answer.error}`, 'blender-camera');
|
|
130
|
+
},
|
|
131
|
+
(error: unknown) =>
|
|
132
|
+
editorHost().console.error(`The camera's pose was not written to Blender: ${String(error)}`, 'blender-camera'),
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
view: (
|
|
136
|
+
camera: string,
|
|
137
|
+
region: { readonly width: number; readonly height: number },
|
|
138
|
+
zoom: number,
|
|
139
|
+
offset: readonly [number, number],
|
|
140
|
+
) => view.cameraView(camera, region, zoom, offset),
|
|
141
|
+
};
|
|
81
142
|
|
|
82
143
|
export default function BlenderModelDocument(props: ToolContributionProps) {
|
|
83
144
|
const { active, document, documentId, notify, publishContext } = props;
|
|
@@ -151,6 +212,30 @@ function BlenderModelViewport({
|
|
|
151
212
|
[],
|
|
152
213
|
);
|
|
153
214
|
const blend = document?.source?.path;
|
|
215
|
+
// Re-read on the engine's frames, the skin's publications and every drawn frame of this stage
|
|
216
|
+
// (playback moves the playhead with no publication); the snapshot is a string, so only a
|
|
217
|
+
// changed line re-renders.
|
|
218
|
+
const subscribeSubject = useCallback(
|
|
219
|
+
(listener: () => void) => {
|
|
220
|
+
const stopView = view.onChange(listener);
|
|
221
|
+
const stopSkin = blenderSkin.subscribe(listener);
|
|
222
|
+
let stopFrame: (() => void) | null = null;
|
|
223
|
+
const bind = (): void => {
|
|
224
|
+
stopFrame?.();
|
|
225
|
+
stopFrame = viewportStages().find((one) => one.documentId === documentId)?.onFrame(listener) ?? null;
|
|
226
|
+
};
|
|
227
|
+
bind();
|
|
228
|
+
const stopStages = onViewportStages(bind);
|
|
229
|
+
return () => {
|
|
230
|
+
stopView();
|
|
231
|
+
stopSkin();
|
|
232
|
+
stopStages();
|
|
233
|
+
stopFrame?.();
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
[documentId],
|
|
237
|
+
);
|
|
238
|
+
const subject = useSyncExternalStore(subscribeSubject, subjectOfView);
|
|
154
239
|
/**
|
|
155
240
|
* THE INSPECTION OVERLAYS ARE HELPERS, and the Helpers menu owns them
|
|
156
241
|
* (WORK.md §Blender in the tab is Blender, "Inspection parity", I4).
|
|
@@ -167,18 +252,17 @@ function BlenderModelViewport({
|
|
|
167
252
|
*/
|
|
168
253
|
useEffect(() => {
|
|
169
254
|
if (!documentId) return;
|
|
170
|
-
const { viewport } = editorHost();
|
|
171
255
|
const groups = view.overlayGroups();
|
|
172
256
|
const apply = (): void => {
|
|
173
|
-
const stage =
|
|
257
|
+
const stage = viewportStages().find((one) => one.documentId === documentId);
|
|
174
258
|
if (!stage) return;
|
|
175
259
|
for (const { kind, object } of groups) stage.setHelper(kind, object);
|
|
176
260
|
};
|
|
177
261
|
apply();
|
|
178
|
-
const stop =
|
|
262
|
+
const stop = onViewportStages(apply);
|
|
179
263
|
return () => {
|
|
180
264
|
stop();
|
|
181
|
-
const stage =
|
|
265
|
+
const stage = viewportStages().find((one) => one.documentId === documentId);
|
|
182
266
|
for (const { kind } of groups) stage?.setHelper(kind, null);
|
|
183
267
|
};
|
|
184
268
|
}, [documentId]);
|
|
@@ -210,14 +294,125 @@ function BlenderModelViewport({
|
|
|
210
294
|
detach?.();
|
|
211
295
|
};
|
|
212
296
|
}, [documentId]);
|
|
297
|
+
/**
|
|
298
|
+
* BLENDER'S RENDERED SHADING IS THE SCENE'S OWN LIGHT. When this stage's view lights by the
|
|
299
|
+
* `scene` (the Rendered shading cell, or the Lighting row's Scene), the presenter holds the viewport
|
|
300
|
+
* in the lighting its render photographs with (`BlenderRuntimeView.holdRendered`): the
|
|
301
|
+
* scene's lights, its World, `hide_render` and shadows, through the camera the stage draws
|
|
302
|
+
* with, re-applied from the stage's frame loop when that camera or the World changes. Any
|
|
303
|
+
* other source returns it to modeling. The stage itself draws a `scene` source unlit
|
|
304
|
+
* (`standard-viewport-dressing.ts`), because Blender's lights live in the presenter.
|
|
305
|
+
*/
|
|
306
|
+
useEffect(() => {
|
|
307
|
+
if (!documentId) return;
|
|
308
|
+
const stageOf = () => viewportStages().find((one) => one.documentId === documentId);
|
|
309
|
+
let framed: ReturnType<typeof stageOf> = undefined;
|
|
310
|
+
let stopFrame: (() => void) | null = null;
|
|
311
|
+
// One getter for the life of the effect, so holding again is not a change; the stand-in is
|
|
312
|
+
// for the moment a stage has left and `onStages` has not yet released the hold.
|
|
313
|
+
const standIn = new THREE.PerspectiveCamera();
|
|
314
|
+
const drawCamera = (): THREE.Camera => stageOf()?.rig().drawCamera() ?? standIn;
|
|
315
|
+
const apply = (): void => {
|
|
316
|
+
const stage = stageOf();
|
|
317
|
+
if (stage !== framed) {
|
|
318
|
+
stopFrame?.();
|
|
319
|
+
stopFrame = stage ? stage.onFrame(() => view.refreshRendered()) : null;
|
|
320
|
+
framed = stage;
|
|
321
|
+
}
|
|
322
|
+
const { lighting } = viewPresentation(documentId);
|
|
323
|
+
view.holdRendered(lighting.source === 'scene' && stage ? drawCamera : null);
|
|
324
|
+
// BLENDER'S SOLID IS BLENDER'S OWN FUNCTION: while the stage lights by Blender's studio, the
|
|
325
|
+
// presenter draws every surface by it (`blender-workbench-material.ts`).
|
|
326
|
+
view.setWorkbench(lighting.source === 'studio' && lighting.studioPreset === DOCUMENT_STUDIO_PRESET.id);
|
|
327
|
+
};
|
|
328
|
+
apply();
|
|
329
|
+
const stopPresentation = subscribeViewportPresentation(apply);
|
|
330
|
+
const stopStages = onViewportStages(apply);
|
|
331
|
+
return () => {
|
|
332
|
+
stopPresentation();
|
|
333
|
+
stopStages();
|
|
334
|
+
stopFrame?.();
|
|
335
|
+
view.holdRendered(null);
|
|
336
|
+
view.setWorkbench(false);
|
|
337
|
+
};
|
|
338
|
+
}, [documentId]);
|
|
339
|
+
/**
|
|
340
|
+
* SHIFT+RIGHT-CLICK PLACES THE 3D CURSOR, Blender's own chord for
|
|
341
|
+
* `view3d.cursor3d` (`blender_default.py`, `params.cursor_set_event`). The
|
|
342
|
+
* press is taken before the stage's orbit sees it, and a press that moves
|
|
343
|
+
* is not a click. A press that never came back (released outside the page,
|
|
344
|
+
* cancelled) is dropped by the next press or the cancel, so it can never
|
|
345
|
+
* swallow a later orbit's release. The drawn cursor moves at once (`placeCursor`); Blender
|
|
346
|
+
* then writes `Scene.cursor` with no history step, as its operator pushes
|
|
347
|
+
* no undo.
|
|
348
|
+
*/
|
|
349
|
+
const cursorPress = useRef<{ id: number; x: number; y: number } | null>(null);
|
|
350
|
+
const cursorChord = (event: ReactPointerEvent | ReactMouseEvent): boolean =>
|
|
351
|
+
event.button === 2 && event.shiftKey && event.target instanceof HTMLCanvasElement;
|
|
352
|
+
const onPointerDownCapture = (event: ReactPointerEvent): void => {
|
|
353
|
+
if (!cursorChord(event)) {
|
|
354
|
+
cursorPress.current = null;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
event.stopPropagation();
|
|
359
|
+
cursorPress.current = { id: event.pointerId, x: event.clientX, y: event.clientY };
|
|
360
|
+
// A real pointer is captured so its release comes back here; a synthetic one cannot be.
|
|
361
|
+
if (event.isTrusted) (event.target as Element).setPointerCapture(event.pointerId);
|
|
362
|
+
};
|
|
363
|
+
const dropCursorPress = (): void => {
|
|
364
|
+
cursorPress.current = null;
|
|
365
|
+
};
|
|
366
|
+
const onPointerUpCapture = (event: ReactPointerEvent): void => {
|
|
367
|
+
const press = cursorPress.current;
|
|
368
|
+
if (press === null || event.pointerId !== press.id || event.button !== 2) return;
|
|
369
|
+
cursorPress.current = null;
|
|
370
|
+
event.preventDefault();
|
|
371
|
+
event.stopPropagation();
|
|
372
|
+
if (Math.hypot(event.clientX - press.x, event.clientY - press.y) > 3) return;
|
|
373
|
+
const placed = view.placeCursor(event.clientX, event.clientY, event.target);
|
|
374
|
+
if (placed === null) return;
|
|
375
|
+
const [x, y, z] = placed.location;
|
|
376
|
+
const [qw, qx, qy, qz] = placed.rotation;
|
|
377
|
+
void blenderExecute(
|
|
378
|
+
'from mathutils import Matrix, Quaternion, Vector\n' +
|
|
379
|
+
`bpy.context.scene.cursor.matrix = Matrix.LocRotScale(Vector((${x}, ${y}, ${z})), ` +
|
|
380
|
+
`Quaternion((${qw}, ${qx}, ${qy}, ${qz})), None)`,
|
|
381
|
+
false,
|
|
382
|
+
'Set 3D Cursor',
|
|
383
|
+
).then(
|
|
384
|
+
(answer) => {
|
|
385
|
+
if (answer.error !== null)
|
|
386
|
+
editorHost().console.error(`Blender refused the 3D cursor: ${answer.error}`, 'blender-cursor');
|
|
387
|
+
},
|
|
388
|
+
(error: unknown) =>
|
|
389
|
+
editorHost().console.error(`The 3D cursor was not written to Blender: ${String(error)}`, 'blender-cursor'),
|
|
390
|
+
);
|
|
391
|
+
};
|
|
392
|
+
const onContextMenuCapture = (event: ReactMouseEvent): void => {
|
|
393
|
+
if (cursorChord(event)) event.preventDefault();
|
|
394
|
+
};
|
|
213
395
|
if (!documentId) return null;
|
|
214
396
|
const Surface = surfaces.Object3DAuthoring;
|
|
215
397
|
return (
|
|
398
|
+
<div
|
|
399
|
+
style={{ display: 'contents' }}
|
|
400
|
+
onPointerDownCapture={onPointerDownCapture}
|
|
401
|
+
onPointerUpCapture={onPointerUpCapture}
|
|
402
|
+
onPointerCancelCapture={dropCursorPress}
|
|
403
|
+
onLostPointerCaptureCapture={dropCursorPress}
|
|
404
|
+
onContextMenuCapture={onContextMenuCapture}
|
|
405
|
+
>
|
|
216
406
|
<Surface
|
|
217
407
|
active={active ?? true}
|
|
218
408
|
documentId={documentId}
|
|
219
409
|
sourcePath={blend ?? 'blender:runtime'}
|
|
220
410
|
displayName={document?.label ?? 'Model'}
|
|
411
|
+
// THE OVERLAY'S SUBJECT AND GRID LINES ARE BLENDER'S: the engine composes the subject from
|
|
412
|
+
// the scene (`session.py` `_subject_line`) and names the grid's step in the scene's units.
|
|
413
|
+
{...(subject === null ? {} : { subject })}
|
|
414
|
+
gridScale={gridScale}
|
|
415
|
+
cameraView={cameraView}
|
|
221
416
|
build={build}
|
|
222
417
|
audit={false}
|
|
223
418
|
// ON A MODEL DOCUMENT THE HIERARCHY IS BLENDER'S OUTLINER, for the same
|
|
@@ -230,12 +425,20 @@ function BlenderModelViewport({
|
|
|
230
425
|
// "Inspection parity", I3).
|
|
231
426
|
authoring={createBlenderOutlinerAuthoring}
|
|
232
427
|
cameraDirection={[0.8187, 0.4458, 0.3617]}
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
// view is 18.39 units from a 2 m cube (`region_3d.view_distance` at
|
|
236
|
-
// `--factory-startup`), about a third of the size a bare fit gives, which
|
|
237
|
-
// is the number `openingFit` was written for and nothing was passing.
|
|
428
|
+
// FOR A FILE THAT SAVED NO 3D VIEW: Blender's factory Modeling direction, standing back
|
|
429
|
+
// three fits.
|
|
238
430
|
openingFit={3}
|
|
431
|
+
// WHERE BLENDER OPENS THE FILE: its own saved 3D View, when it holds one; the direction and
|
|
432
|
+
// fit above are the fallback for a file that saved none.
|
|
433
|
+
openingView={view.savedView()}
|
|
434
|
+
// The file's own lens, as the document's presentation (Blender's arithmetic in degrees).
|
|
435
|
+
presentation={(() => {
|
|
436
|
+
const saved = view.savedView();
|
|
437
|
+
return saved ? { camera: { fov: blenderViewFieldOfView(saved.lens) } } : null;
|
|
438
|
+
})()}
|
|
439
|
+
// Every entry this document opens is a `model` stage, the standing `blender:runtime`
|
|
440
|
+
// address included (its id carries no `model:` prefix).
|
|
441
|
+
stageKind={documentKind}
|
|
239
442
|
// SOLID SHADING IS BLENDER'S, and Blender's Solid mode has NO world
|
|
240
443
|
// light: `light_ambient` is (0,0,0), there is no IBL, and the model is
|
|
241
444
|
// lit by four studio lights stated in VIEW space. So this document
|
|
@@ -249,22 +452,15 @@ function BlenderModelViewport({
|
|
|
249
452
|
// The BACKGROUND stays the dressing's, because the Blender palette
|
|
250
453
|
// already paints the viewport its own flat grey.
|
|
251
454
|
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
// so the viewport was the one surface in the chain running a different
|
|
256
|
-
// curve from the thing it frames. MEASURED on the factory cube, our own
|
|
257
|
-
// radiance through each operator against Blender's (141,143,145)/
|
|
258
|
-
// (129,131,131)/(111,112,113): ACES lands the three faces within 6
|
|
259
|
-
// levels with a spread of 40 where Blender's is 30 — the curve, not the
|
|
260
|
-
// lights, is what was left of row 1's spread — and AgX within 3 at a
|
|
261
|
-
// spread of 26.
|
|
455
|
+
// THE VIEW TRANSFORM IS EACH SHADING MODE'S OWN (`src/presentation.ts`: Standard for Solid,
|
|
456
|
+
// AgX for Material Preview and Rendered), so the dressing states none: a document mapper
|
|
457
|
+
// would outrank every mode's.
|
|
262
458
|
dressing={{
|
|
263
459
|
environment: false,
|
|
264
460
|
keyLight: false,
|
|
265
|
-
toneMapping: THREE.AgXToneMapping,
|
|
266
461
|
viewLocked: view.studioLights(),
|
|
267
462
|
}}
|
|
268
463
|
/>
|
|
464
|
+
</div>
|
|
269
465
|
);
|
|
270
466
|
}
|
|
@@ -60,7 +60,11 @@ export const commands: CommandContribution['commands'] = {
|
|
|
60
60
|
'blender-screenshot-view': verb('always', 60_000),
|
|
61
61
|
'blender-read-file': verb('none', 60_000),
|
|
62
62
|
'blender-write-file': verb('none', 60_000),
|
|
63
|
-
|
|
63
|
+
// Listing waits on the worker like a read: after an execute the MCP mirror lists
|
|
64
|
+
// while the page is still presenting the frame and refreshing its RNA and
|
|
65
|
+
// outliner reads, which on a large document outlasts the generic 5s default and
|
|
66
|
+
// turned a successful execute into a timeout.
|
|
67
|
+
'blender-list-files': verb('none', 60_000),
|
|
64
68
|
// THE RNA DOOR (WORK.md §Blender in the tab is Blender, "Inspection
|
|
65
69
|
// parity", I1). Two READS — a datablock's `bl_rna.properties` and the
|
|
66
70
|
// Properties context — and one WRITE. The reads present nothing, so they
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BLENDER'S WORLD STUDIO LIGHTS: the eight HDRIs Blender ships for Material Preview
|
|
3
|
+
* (`datafiles/studiolights/world`, copied from Blender 5.2), registered as environment images
|
|
4
|
+
* (`@volter/editor-sdk/kit/environment-images`) so a view can light by one and draw it behind
|
|
5
|
+
* the scene. Material Preview opens on Forest. All CC0, by Greg Zaal (Poly Haven); the terms
|
|
6
|
+
* and each original are in `studiolights/world/license.txt`.
|
|
7
|
+
*/
|
|
8
|
+
import type { EnvironmentImageSet } from '@volter/editor-sdk/kit/environment-images';
|
|
9
|
+
import city from './studiolights/world/city.exr?url';
|
|
10
|
+
import courtyard from './studiolights/world/courtyard.exr?url';
|
|
11
|
+
import forest from './studiolights/world/forest.exr?url';
|
|
12
|
+
import interior from './studiolights/world/interior.exr?url';
|
|
13
|
+
import night from './studiolights/world/night.exr?url';
|
|
14
|
+
import studio from './studiolights/world/studio.exr?url';
|
|
15
|
+
import sunrise from './studiolights/world/sunrise.exr?url';
|
|
16
|
+
import sunset from './studiolights/world/sunset.exr?url';
|
|
17
|
+
|
|
18
|
+
export const point = 'workspace.environment';
|
|
19
|
+
export const environment: EnvironmentImageSet = {
|
|
20
|
+
id: 'blender-studiolights-world',
|
|
21
|
+
source: "Blender's world studio lights, CC0 (Poly Haven); studiolights/world/license.txt",
|
|
22
|
+
images: [
|
|
23
|
+
{ id: 'blender:forest', title: 'Forest', url: forest, format: 'exr' },
|
|
24
|
+
{ id: 'blender:city', title: 'City', url: city, format: 'exr' },
|
|
25
|
+
{ id: 'blender:courtyard', title: 'Courtyard', url: courtyard, format: 'exr' },
|
|
26
|
+
{ id: 'blender:interior', title: 'Interior', url: interior, format: 'exr' },
|
|
27
|
+
{ id: 'blender:night', title: 'Night', url: night, format: 'exr' },
|
|
28
|
+
{ id: 'blender:studio', title: 'Studio', url: studio, format: 'exr' },
|
|
29
|
+
{ id: 'blender:sunrise', title: 'Sunrise', url: sunrise, format: 'exr' },
|
|
30
|
+
{ id: 'blender:sunset', title: 'Sunset', url: sunset, format: 'exr' },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
@@ -911,10 +911,10 @@
|
|
|
911
911
|
"path": "M1.5 6.9A5.4 5.4 0 1 0 12.3 6.9A5.4 5.4 0 1 0 1.5 6.9ZM3.28 6.9A3.63 3.63 0 1 1 10.53 6.9A3.63 3.63 0 1 1 3.28 6.9ZM9.64 10.99L13.67 15.02A0.96 0.96 0 0 0 15.02 13.67L10.99 9.64A0.96 0.96 0 0 0 9.64 10.99Z"
|
|
912
912
|
},
|
|
913
913
|
"magnifying-glass-minus": {
|
|
914
|
-
"path": "M1.72 6.9A5.19 5.19 0 1 0 12.09 6.9A5.19 5.19 0 1 0 1.72 6.9ZM4.
|
|
914
|
+
"path": "M1.72 6.9A5.19 5.19 0 1 0 12.09 6.9A5.19 5.19 0 1 0 1.72 6.9ZM4.1 7.34L4.1 6.46L9.71 6.46L9.71 7.34ZM8.62 10.55L13.62 15.55A1.37 1.37 0 0 0 15.55 13.62L10.55 8.62A1.37 1.37 0 0 0 8.62 10.55Z"
|
|
915
915
|
},
|
|
916
916
|
"magnifying-glass-plus": {
|
|
917
|
-
"path": "M1.72 6.9A5.19 5.19 0 1 0 12.09 6.9A5.19 5.19 0 1 0 1.72 6.9ZM6.
|
|
917
|
+
"path": "M1.72 6.9A5.19 5.19 0 1 0 12.09 6.9A5.19 5.19 0 1 0 1.72 6.9ZM6.46 4.1L7.34 4.1L7.34 6.46L9.71 6.46L9.71 7.34L7.34 7.34L7.34 9.71L6.46 9.71L6.46 7.34L4.1 7.34L4.1 6.46L6.46 6.46ZM8.62 10.55L13.62 15.55A1.37 1.37 0 0 0 15.55 13.62L10.55 8.62A1.37 1.37 0 0 0 8.62 10.55Z"
|
|
918
918
|
},
|
|
919
919
|
"maximize": {
|
|
920
920
|
"path": "M5.8 0.28L1.17 0.28A0.89 0.89 0 0 0 1.17 2.06L5.8 2.06A0.89 0.89 0 0 0 5.8 0.28ZM0.28 1.17L0.28 5.8A0.89 0.89 0 0 0 2.06 5.8L2.06 1.17A0.89 0.89 0 0 0 0.28 1.17ZM10.2 2.06L14.83 2.06A0.89 0.89 0 0 0 14.83 0.28L10.2 0.28A0.89 0.89 0 0 0 10.2 2.06ZM13.94 1.17L13.94 5.8A0.89 0.89 0 0 0 15.72 5.8L15.72 1.17A0.89 0.89 0 0 0 13.94 1.17ZM5.8 13.94L1.17 13.94A0.89 0.89 0 0 0 1.17 15.72L5.8 15.72A0.89 0.89 0 0 0 5.8 13.94ZM2.06 14.83L2.06 10.2A0.89 0.89 0 0 0 0.28 10.2L0.28 14.83A0.89 0.89 0 0 0 2.06 14.83ZM10.2 15.72L14.83 15.72A0.89 0.89 0 0 0 14.83 13.94L10.2 13.94A0.89 0.89 0 0 0 10.2 15.72ZM15.72 14.83L15.72 10.2A0.89 0.89 0 0 0 13.94 10.2L13.94 14.83A0.89 0.89 0 0 0 15.72 14.83Z"
|
|
@@ -1207,6 +1207,11 @@
|
|
|
1207
1207
|
"tool-scale": {
|
|
1208
1208
|
"path": "M1.17 1.1L14.83 1.1A0.55 0.55 0 0 0 14.83 0.01L1.17 0.01A0.55 0.55 0 0 0 1.17 1.1ZM14.29 0.56L14.29 14.22A0.55 0.55 0 0 0 15.38 14.22L15.38 0.56A0.55 0.55 0 0 0 14.29 0.56ZM14.83 13.68L1.17 13.68A0.55 0.55 0 0 0 1.17 14.77L14.83 14.77A0.55 0.55 0 0 0 14.83 13.68ZM1.71 14.22L1.71 0.56A0.55 0.55 0 0 0 0.62 0.56L0.62 14.22A0.55 0.55 0 0 0 1.71 14.22ZM0.31 7.51A0.24 0.24 0 0 0 0.07 7.76L0.07 14.95A0.24 0.24 0 0 0 0.31 15.2L7.51 15.2A0.24 0.24 0 0 0 7.76 14.95L7.76 7.76A0.24 0.24 0 0 0 7.51 7.51ZM9.19 7.12L11.75 4.55A0.65 0.65 0 0 0 10.84 3.64L8.27 6.2A0.65 0.65 0 0 0 9.19 7.12ZM9.59 3.12L12.27 5.8L13.37 2.02Z"
|
|
1209
1209
|
},
|
|
1210
|
+
"tool-select-box": {
|
|
1211
|
+
"path": "M5.8 12.27L8.24 9.71L11.9 9.71L5.68 3.73Z",
|
|
1212
|
+
"tone": "select",
|
|
1213
|
+
"tonedPath": "M-0.54 0.31L2.75 0.31L2.75 -0.54L-0.54 -0.54ZM-0.54 2.51L0.31 2.51L0.31 -0.54L-0.54 -0.54ZM13.25 0.31L16.54 0.31L16.54 -0.54L13.25 -0.54ZM15.69 2.39L16.54 2.39L16.54 -0.54L15.69 -0.54ZM-0.54 16.54L2.51 16.54L2.51 15.69L-0.54 15.69ZM-0.54 16.54L0.31 16.54L0.31 13.37L-0.54 13.37ZM13.25 16.54L16.54 16.54L16.54 15.69L13.25 15.69ZM15.69 16.54L16.54 16.54L16.54 13.37L15.69 13.37ZM4.34 0.31L7.02 0.31L7.02 -0.54L4.34 -0.54ZM8.49 0.31L11.29 0.31L11.29 -0.54L8.49 -0.54ZM4.95 16.54L7.88 16.54L7.88 15.69L4.95 15.69ZM9.22 16.54L12.15 16.54L12.15 15.69L9.22 15.69ZM-0.54 6.41L0.31 6.41L0.31 3.85L-0.54 3.85ZM-0.54 10.56L0.31 10.56L0.31 8L-0.54 8ZM15.69 6.41L16.54 6.41L16.54 3.85L15.69 3.85ZM15.69 10.56L16.54 10.56L16.54 8L15.69 8Z"
|
|
1214
|
+
},
|
|
1210
1215
|
"tool-transform": {
|
|
1211
1216
|
"path": "M4.83 4.52A0.31 0.31 0 0 0 4.52 4.83L4.52 11.17A0.31 0.31 0 0 0 4.83 11.48L11.17 11.48A0.31 0.31 0 0 0 11.48 11.17L11.48 4.83A0.31 0.31 0 0 0 11.17 4.52ZM8 -1.64L5.93 1.41L10.07 1.41ZM8 17.64L10.07 14.59L5.93 14.59ZM-1.64 8L1.41 10.07L1.41 5.93ZM17.64 8L14.59 5.93L14.59 10.07ZM15.55 10.75L15.09 11.77L14.5 12.72L13.78 13.58L12.95 14.33L12.02 14.96L11.01 15.45L11.48 16.61L12.65 16.05L13.72 15.32L14.68 14.45L15.52 13.46L16.2 12.36L16.73 11.18ZM15.51 10.96A0.63 0.63 0 1 0 16.77 10.96A0.63 0.63 0 1 0 15.51 10.96ZM10.62 16.03A0.63 0.63 0 1 0 11.87 16.03A0.63 0.63 0 1 0 10.62 16.03ZM5.25 15.55L4.23 15.09L3.28 14.5L2.42 13.78L1.67 12.95L1.04 12.02L0.55 11.01L-0.61 11.48L-0.05 12.65L0.68 13.72L1.55 14.68L2.54 15.52L3.64 16.2L4.82 16.73ZM4.41 16.14A0.63 0.63 0 1 0 5.67 16.14A0.63 0.63 0 1 0 4.41 16.14ZM-0.66 11.24A0.63 0.63 0 1 0 0.6 11.24A0.63 0.63 0 1 0 -0.66 11.24ZM0.45 5.25L0.91 4.23L1.5 3.28L2.22 2.42L3.05 1.67L3.98 1.04L4.99 0.55L4.52 -0.61L3.35 -0.05L2.28 0.68L1.32 1.55L0.48 2.54L-0.2 3.64L-0.73 4.82ZM-0.77 5.04A0.63 0.63 0 1 0 0.49 5.04A0.63 0.63 0 1 0 -0.77 5.04ZM4.13 -0.03A0.63 0.63 0 1 0 5.38 -0.03A0.63 0.63 0 1 0 4.13 -0.03ZM10.75 0.45L11.77 0.91L12.72 1.5L13.58 2.22L14.33 3.05L14.96 3.98L15.45 4.99L16.61 4.52L16.05 3.35L15.32 2.28L14.45 1.32L13.46 0.48L12.36 -0.2L11.18 -0.73ZM10.33 -0.14A0.63 0.63 0 1 0 11.59 -0.14A0.63 0.63 0 1 0 10.33 -0.14ZM15.4 4.76A0.63 0.63 0 1 0 16.66 4.76A0.63 0.63 0 1 0 15.4 4.76Z"
|
|
1212
1217
|
},
|
|
@@ -1225,6 +1230,21 @@
|
|
|
1225
1230
|
"users": {
|
|
1226
1231
|
"path": "M1.78 4.34A3.05 3.05 0 1 0 7.88 4.34A3.05 3.05 0 1 0 1.78 4.34ZM0.31 13.61L0.37 12.91L0.53 12.22L0.81 11.56L1.18 10.96L1.64 10.42L2.17 9.96L2.78 9.59L3.43 9.32L4.12 9.15L4.83 9.1L5.53 9.15L6.22 9.32L6.88 9.59L7.48 9.96L8.02 10.42L8.48 10.96L8.85 11.56L9.12 12.22L9.29 12.91L9.34 13.61L11.05 13.61L10.97 12.64L10.75 11.69L10.37 10.79L9.86 9.95L9.23 9.21L8.49 8.58L7.65 8.07L6.75 7.69L5.8 7.47L4.83 7.39L3.85 7.47L2.91 7.69L2 8.07L1.17 8.58L0.43 9.21L-0.21 9.95L-0.72 10.79L-1.09 11.69L-1.32 12.64L-1.39 13.61ZM-1.39 13.61A0.85 0.85 0 1 0 0.31 13.61A0.85 0.85 0 1 0 -1.39 13.61ZM9.34 13.61A0.85 0.85 0 1 0 11.05 13.61A0.85 0.85 0 1 0 9.34 13.61ZM11.54 3.16L11.8 3.09L12.07 3.05L12.35 3.06L12.61 3.11L12.87 3.21L13.11 3.33L13.33 3.5L13.52 3.7L13.67 3.92L13.79 4.16L13.88 4.42L13.92 4.69L13.92 4.96L13.88 5.23L13.79 5.49L13.67 5.74L13.52 5.96L13.33 6.16L13.11 6.32L12.87 6.45L12.61 6.54L12.35 6.59L12.07 6.6L11.8 6.57L11.54 6.5L11 7.97L11.5 8.11L12.01 8.17L12.52 8.15L13.03 8.06L13.51 7.89L13.96 7.64L14.37 7.33L14.73 6.96L15.02 6.54L15.25 6.08L15.41 5.59L15.49 5.08L15.49 4.57L15.41 4.06L15.25 3.57L15.02 3.11L14.73 2.69L14.37 2.32L13.96 2.01L13.51 1.77L13.03 1.6L12.52 1.5L12.01 1.48L11.5 1.54L11 1.68ZM10.49 2.42A0.79 0.79 0 1 0 12.06 2.42A0.79 0.79 0 1 0 10.49 2.42ZM10.49 7.24A0.79 0.79 0 1 0 12.06 7.24A0.79 0.79 0 1 0 10.49 7.24ZM9.04 11.66L9.26 11.16L9.56 10.71L9.92 10.3L10.34 9.95L10.81 9.68L11.32 9.47L11.85 9.35L12.39 9.31L12.94 9.35L13.47 9.47L13.97 9.68L14.44 9.95L14.86 10.3L15.23 10.71L15.52 11.16L15.75 11.66L17.29 11.1L16.96 10.37L16.53 9.71L16 9.12L15.38 8.61L14.7 8.21L13.96 7.91L13.18 7.73L12.39 7.67L11.6 7.73L10.82 7.91L10.09 8.21L9.4 8.61L8.79 9.12L8.26 9.71L7.82 10.37L7.49 11.1ZM7.45 11.38A0.82 0.82 0 1 0 9.08 11.38A0.82 0.82 0 1 0 7.45 11.38ZM15.7 11.38A0.82 0.82 0 1 0 17.34 11.38A0.82 0.82 0 1 0 15.7 11.38Z"
|
|
1227
1232
|
},
|
|
1233
|
+
"viewport-camera": {
|
|
1234
|
+
"path": "M4 12L4 10.5C4 10.22 4.22 10 4.5 10L10.5 10C10.78 10 11 10.22 11 10.5L11 15.5C11 15.78 10.78 16 10.5 16L5.71 16C5.57 16 5.45 15.95 5.35 15.85L4.15 14.65C4.05 14.55 4 14.43 4 14.29L4 13L3 13L3 13.5C3 13.78 2.78 14 2.5 14C2.22 14 2 13.78 2 13.5L2 11.5C2 11.22 2.22 11 2.5 11C2.78 11 3 11.22 3 11.5L3 12ZM5.91 15L5 14.09L5 11L10 11L10 15ZM13.22 13.5L15 14.59L15 12.41ZM15.15 15.85C15.24 15.94 15.36 16 15.5 16C15.78 16 16 15.78 16 15.5L16 11.5C16 11.22 15.78 11 15.5 11C15.36 11 15.24 11.06 15.15 11.15L12.04 13.04C11.87 13.12 11.75 13.3 11.75 13.5C11.75 13.7 11.87 13.88 12.04 13.96ZM6.38 4.22C6.02 4.3 5.65 4.17 5.41 3.89C4.95 3.34 4.27 3 3.5 3C2.12 3 1 4.12 1 5.5C1 6.88 2.12 8 3.5 8L10.5 8C12.43 8 14 6.43 14 4.5C14 2.57 12.43 1 10.5 1C8.91 1 7.56 2.06 7.14 3.52C7.04 3.88 6.75 4.15 6.38 4.22ZM6.61 2.24C6.42 2.56 6.28 2.89 6.18 3.25C5.94 2.97 5.67 2.73 5.36 2.54C4.82 2.2 4.18 2 3.5 2C1.57 2 0 3.57 0 5.5C0 7.43 1.57 9 3.5 9L10.5 9C12.99 9 15 6.99 15 4.5C15 2.01 12.99 0 10.5 0C8.84 0 7.38 0.9 6.61 2.24ZM10.5 5C10.78 5 11 4.78 11 4.5C11 4.22 10.78 4 10.5 4C10.22 4 10 4.22 10 4.5C10 4.78 10.22 5 10.5 5ZM10.5 6C11.33 6 12 5.33 12 4.5C12 3.67 11.33 3 10.5 3C9.67 3 9 3.67 9 4.5C9 5.33 9.67 6 10.5 6ZM4.5 5.5C4.5 6.05 4.05 6.5 3.5 6.5C2.95 6.5 2.5 6.05 2.5 5.5C2.5 4.95 2.95 4.5 3.5 4.5C4.05 4.5 4.5 4.95 4.5 5.5Z"
|
|
1235
|
+
},
|
|
1236
|
+
"viewport-camera-locked": {
|
|
1237
|
+
"path": "M3 7L3 5C3 2.24 5.24 0 8 0C10.76 0 13 2.24 13 5L13 7L13.5 7C13.78 7 14 7.22 14 7.5L14 15.5C14 15.78 13.78 16 13.5 16L2.5 16C2.22 16 2 15.78 2 15.5L2 7.5C2 7.22 2.22 7 2.5 7ZM4 5C4 2.79 5.79 1 8 1C10.21 1 12 2.79 12 5L12 7L4 7ZM8 10C7.45 10 7 10.45 7 11C7 11.37 7.2 11.69 7.5 11.87L7.5 13C7.5 13.28 7.72 13.5 8 13.5C8.28 13.5 8.5 13.28 8.5 13L8.5 11.87C8.8 11.69 9 11.37 9 11C9 10.45 8.55 10 8 10Z"
|
|
1238
|
+
},
|
|
1239
|
+
"viewport-camera-unlocked": {
|
|
1240
|
+
"path": "M8 10C7.45 10 7 10.45 7 11C7 11.37 7.2 11.69 7.5 11.87L7.5 13C7.5 13.28 7.72 13.5 8 13.5C8.28 13.5 8.5 13.28 8.5 13L8.5 11.87C8.8 11.69 9 11.37 9 11C9 10.45 8.55 10 8 10ZM8 1C5.79 1 4 2.79 4 5L4 7L13.5 7C13.78 7 14 7.22 14 7.5L14 15.5C14 15.78 13.78 16 13.5 16L2.5 16C2.22 16 2 15.78 2 15.5L2 7.5C2 7.22 2.22 7 2.5 7L3 7L3 5C3 2.24 5.24 0 8 0C10.22 0 12.1 1.44 12.75 3.44C12.83 3.69 12.69 3.94 12.45 4.02C12.17 4.11 11.87 3.94 11.77 3.66C11.22 2.11 9.74 1 8 1ZM3 8L3 15L13 15L13 8Z"
|
|
1241
|
+
},
|
|
1242
|
+
"viewport-camera-view": {
|
|
1243
|
+
"path": "M10.5 0C8.36 0 6.69 1.47 6.17 3.24C5.52 2.5 4.66 2 3.5 2C1.57 2 0 3.57 0 5.5C0 7.43 1.57 9 3.5 9L10.5 9C12.98 9 15 6.98 15 4.5C15 2.02 12.98 0 10.5 0ZM10.5 3C11.32 3 12 3.68 12 4.5C12 5.32 11.32 6 10.5 6C9.68 6 9 5.32 9 4.5C9 3.68 9.68 3 10.5 3ZM3.5 4C4.32 4 5 4.68 5 5.5C5 6.32 4.32 7 3.5 7C2.68 7 2 6.32 2 5.5C2 4.68 2.68 4 3.5 4ZM10.5 4C10.22 4 10 4.22 10 4.5C10 4.78 10.22 5 10.5 5C10.78 5 11 4.78 11 4.5C11 4.22 10.78 4 10.5 4ZM3.5 5C3.22 5 3 5.22 3 5.5C3 5.78 3.22 6 3.5 6C3.78 6 4 5.78 4 5.5C4 5.22 3.78 5 3.5 5ZM4.5 10C4.22 10 4 10.22 4 10.5L4 11.5L4 12L3 12L3 11.5C3 11.22 2.77 10.99 2.49 10.99C2.22 10.99 2 11.22 2 11.5L2 12.5L2 13.5C2.01 14.16 2.99 14.16 3 13.5L3 13L4 13L4 13.5L4 14.5C4 14.63 4.05 14.76 4.15 14.85L5.15 15.85C5.24 15.95 5.37 16 5.5 16L10.5 16C10.78 16 11 15.78 11 15.5L11 10.5C11 10.22 10.78 10 10.5 10ZM15.48 11C15.4 11 15.31 11.03 15.24 11.07L11.99 13.07C11.67 13.27 11.67 13.73 11.99 13.93L15.24 15.93C15.57 16.13 16 15.89 16 15.5L16 11.5C16 11.22 15.77 10.99 15.48 11Z"
|
|
1244
|
+
},
|
|
1245
|
+
"viewport-orthographic": {
|
|
1246
|
+
"path": "M1.42 1.93L14.6 1.93A0.51 0.51 0 0 0 14.6 0.92L1.42 0.92A0.51 0.51 0 0 0 1.42 1.93ZM0.92 1.42L0.92 14.6A0.51 0.51 0 0 0 1.93 14.6L1.93 1.42A0.51 0.51 0 0 0 0.92 1.42ZM1.42 6.32L14.6 6.32A0.51 0.51 0 0 0 14.6 5.31L1.42 5.31A0.51 0.51 0 0 0 1.42 6.32ZM5.31 1.42L5.31 14.6A0.51 0.51 0 0 0 6.32 14.6L6.32 1.42A0.51 0.51 0 0 0 5.31 1.42ZM1.42 10.71L14.6 10.71A0.51 0.51 0 0 0 14.6 9.7L1.42 9.7A0.51 0.51 0 0 0 1.42 10.71ZM9.7 1.42L9.7 14.6A0.51 0.51 0 0 0 10.71 14.6L10.71 1.42A0.51 0.51 0 0 0 9.7 1.42ZM1.42 15.11L14.6 15.11A0.51 0.51 0 0 0 14.6 14.09L1.42 14.09A0.51 0.51 0 0 0 1.42 15.11ZM14.09 1.42L14.09 14.6A0.51 0.51 0 0 0 15.11 14.6L15.11 1.42A0.51 0.51 0 0 0 14.09 1.42Z"
|
|
1247
|
+
},
|
|
1228
1248
|
"viewport-projection": {
|
|
1229
1249
|
"path": "M4.14 3.27L11.88 3.27A0.51 0.51 0 0 0 11.88 2.26L4.14 2.26A0.51 0.51 0 0 0 4.14 3.27ZM3.5 5.8L12.53 5.8A0.51 0.51 0 0 0 12.53 4.79L3.5 4.79A0.51 0.51 0 0 0 3.5 5.8ZM2.57 9.4L13.45 9.4A0.51 0.51 0 0 0 13.45 8.39L2.57 8.39A0.51 0.51 0 0 0 2.57 9.4ZM1.42 13.85L14.6 13.85A0.51 0.51 0 0 0 14.6 12.84L1.42 12.84A0.51 0.51 0 0 0 1.42 13.85ZM3.66 2.64L0.93 13.22A0.51 0.51 0 0 0 1.91 13.47L4.63 2.89A0.51 0.51 0 0 0 3.66 2.64ZM11.39 2.89L14.11 13.47A0.51 0.51 0 0 0 15.09 13.22L12.37 2.64A0.51 0.51 0 0 0 11.39 2.89ZM6.42 2.71L5.24 13.29A0.51 0.51 0 0 0 6.25 13.4L7.43 2.82A0.51 0.51 0 0 0 6.42 2.71ZM8.6 2.82L9.78 13.4A0.51 0.51 0 0 0 10.78 13.29L9.6 2.71A0.51 0.51 0 0 0 8.6 2.82Z"
|
|
1230
1250
|
},
|
|
@@ -1557,5 +1557,33 @@
|
|
|
1557
1557
|
"sha256": "5fcacba19714529d2283550eeff5c052cf59b190aa32403d2e692dc1ca424b07",
|
|
1558
1558
|
"box": [3, 3, 13, 13],
|
|
1559
1559
|
"path": "M4.03 3C3.47 3 3 3.47 3 4.03L3 11.97C3 12.53 3.47 13 4.03 13L11.97 13C12.53 13 13 12.53 13 11.97L13 4.03C13 3.47 12.53 3 11.97 3ZM4.03 4L11.97 4C11.99 4 12 4 12 4.03L12 11.97C12 11.99 11.99 12 11.97 12L4.03 12C4.01 12 4 11.99 4 11.97L4 4.03C4 4.01 4.01 4 4.03 4Z"
|
|
1560
|
+
},
|
|
1561
|
+
"viewport-camera": {
|
|
1562
|
+
"icon": "ICON_VIEW_CAMERA_UNSELECTED",
|
|
1563
|
+
"source": "release/datafiles/icons_svg/view_camera_unselected.svg",
|
|
1564
|
+
"sha256": "a286ff50999057585a5b45714bf98d2a6706f60be114b7b1386fb33d63a2cef3",
|
|
1565
|
+
"box": [0, 0, 16, 16],
|
|
1566
|
+
"path": "M4 12L4 10.5C4 10.22 4.22 10 4.5 10L10.5 10C10.78 10 11 10.22 11 10.5L11 15.5C11 15.78 10.78 16 10.5 16L5.71 16C5.57 16 5.45 15.95 5.35 15.85L4.15 14.65C4.05 14.55 4 14.43 4 14.29L4 13L3 13L3 13.5C3 13.78 2.78 14 2.5 14C2.22 14 2 13.78 2 13.5L2 11.5C2 11.22 2.22 11 2.5 11C2.78 11 3 11.22 3 11.5L3 12ZM5.91 15L5 14.09L5 11L10 11L10 15ZM13.22 13.5L15 14.59L15 12.41ZM15.15 15.85C15.24 15.94 15.36 16 15.5 16C15.78 16 16 15.78 16 15.5L16 11.5C16 11.22 15.78 11 15.5 11C15.36 11 15.24 11.06 15.15 11.15L12.04 13.04C11.87 13.12 11.75 13.3 11.75 13.5C11.75 13.7 11.87 13.88 12.04 13.96ZM6.38 4.22C6.02 4.3 5.65 4.17 5.41 3.89C4.95 3.34 4.27 3 3.5 3C2.12 3 1 4.12 1 5.5C1 6.88 2.12 8 3.5 8L10.5 8C12.43 8 14 6.43 14 4.5C14 2.57 12.43 1 10.5 1C8.91 1 7.56 2.06 7.14 3.52C7.04 3.88 6.75 4.15 6.38 4.22ZM6.61 2.24C6.42 2.56 6.28 2.89 6.18 3.25C5.94 2.97 5.67 2.73 5.36 2.54C4.82 2.2 4.18 2 3.5 2C1.57 2 0 3.57 0 5.5C0 7.43 1.57 9 3.5 9L10.5 9C12.99 9 15 6.99 15 4.5C15 2.01 12.99 0 10.5 0C8.84 0 7.38 0.9 6.61 2.24ZM10.5 5C10.78 5 11 4.78 11 4.5C11 4.22 10.78 4 10.5 4C10.22 4 10 4.22 10 4.5C10 4.78 10.22 5 10.5 5ZM10.5 6C11.33 6 12 5.33 12 4.5C12 3.67 11.33 3 10.5 3C9.67 3 9 3.67 9 4.5C9 5.33 9.67 6 10.5 6ZM4.5 5.5C4.5 6.05 4.05 6.5 3.5 6.5C2.95 6.5 2.5 6.05 2.5 5.5C2.5 4.95 2.95 4.5 3.5 4.5C4.05 4.5 4.5 4.95 4.5 5.5Z"
|
|
1567
|
+
},
|
|
1568
|
+
"viewport-camera-view": {
|
|
1569
|
+
"icon": "ICON_VIEW_CAMERA",
|
|
1570
|
+
"source": "release/datafiles/icons_svg/view_camera.svg",
|
|
1571
|
+
"sha256": "17fd5360f46a00bcf2c3d72f115f017ee693775dd3af44fab89e1a939a879e07",
|
|
1572
|
+
"box": [0, 0, 16, 16.13],
|
|
1573
|
+
"path": "M10.5 0C8.36 0 6.69 1.47 6.17 3.24C5.52 2.5 4.66 2 3.5 2C1.57 2 0 3.57 0 5.5C0 7.43 1.57 9 3.5 9L10.5 9C12.98 9 15 6.98 15 4.5C15 2.02 12.98 0 10.5 0ZM10.5 3C11.32 3 12 3.68 12 4.5C12 5.32 11.32 6 10.5 6C9.68 6 9 5.32 9 4.5C9 3.68 9.68 3 10.5 3ZM3.5 4C4.32 4 5 4.68 5 5.5C5 6.32 4.32 7 3.5 7C2.68 7 2 6.32 2 5.5C2 4.68 2.68 4 3.5 4ZM10.5 4C10.22 4 10 4.22 10 4.5C10 4.78 10.22 5 10.5 5C10.78 5 11 4.78 11 4.5C11 4.22 10.78 4 10.5 4ZM3.5 5C3.22 5 3 5.22 3 5.5C3 5.78 3.22 6 3.5 6C3.78 6 4 5.78 4 5.5C4 5.22 3.78 5 3.5 5ZM4.5 10C4.22 10 4 10.22 4 10.5L4 11.5L4 12L3 12L3 11.5C3 11.22 2.77 10.99 2.49 10.99C2.22 10.99 2 11.22 2 11.5L2 12.5L2 13.5C2.01 14.16 2.99 14.16 3 13.5L3 13L4 13L4 13.5L4 14.5C4 14.63 4.05 14.76 4.15 14.85L5.15 15.85C5.24 15.95 5.37 16 5.5 16L10.5 16C10.78 16 11 15.78 11 15.5L11 10.5C11 10.22 10.78 10 10.5 10ZM15.48 11C15.4 11 15.31 11.03 15.24 11.07L11.99 13.07C11.67 13.27 11.67 13.73 11.99 13.93L15.24 15.93C15.57 16.13 16 15.89 16 15.5L16 11.5C16 11.22 15.77 10.99 15.48 11Z"
|
|
1574
|
+
},
|
|
1575
|
+
"viewport-camera-locked": {
|
|
1576
|
+
"icon": "ICON_VIEW_LOCKED",
|
|
1577
|
+
"source": "release/datafiles/icons_svg/view_locked.svg",
|
|
1578
|
+
"sha256": "8b84ee3bb6920f48eebc0e43116321977e18a09931879081550c9c4564fe4ec1",
|
|
1579
|
+
"box": [2, 0, 14, 16],
|
|
1580
|
+
"path": "M3 7L3 5C3 2.24 5.24 0 8 0C10.76 0 13 2.24 13 5L13 7L13.5 7C13.78 7 14 7.22 14 7.5L14 15.5C14 15.78 13.78 16 13.5 16L2.5 16C2.22 16 2 15.78 2 15.5L2 7.5C2 7.22 2.22 7 2.5 7ZM4 5C4 2.79 5.79 1 8 1C10.21 1 12 2.79 12 5L12 7L4 7ZM8 10C7.45 10 7 10.45 7 11C7 11.37 7.2 11.69 7.5 11.87L7.5 13C7.5 13.28 7.72 13.5 8 13.5C8.28 13.5 8.5 13.28 8.5 13L8.5 11.87C8.8 11.69 9 11.37 9 11C9 10.45 8.55 10 8 10Z"
|
|
1581
|
+
},
|
|
1582
|
+
"viewport-camera-unlocked": {
|
|
1583
|
+
"icon": "ICON_VIEW_UNLOCKED",
|
|
1584
|
+
"source": "release/datafiles/icons_svg/view_unlocked.svg",
|
|
1585
|
+
"sha256": "1a70d7dfe4a77e6a628426f6400b0e273e2b543bda53d967f7d4c0da3268bd52",
|
|
1586
|
+
"box": [2, 0, 14, 16],
|
|
1587
|
+
"path": "M8 10C7.45 10 7 10.45 7 11C7 11.37 7.2 11.69 7.5 11.87L7.5 13C7.5 13.28 7.72 13.5 8 13.5C8.28 13.5 8.5 13.28 8.5 13L8.5 11.87C8.8 11.69 9 11.37 9 11C9 10.45 8.55 10 8 10ZM8 1C5.79 1 4 2.79 4 5L4 7L13.5 7C13.78 7 14 7.22 14 7.5L14 15.5C14 15.78 13.78 16 13.5 16L2.5 16C2.22 16 2 15.78 2 15.5L2 7.5C2 7.22 2.22 7 2.5 7L3 7L3 5C3 2.24 5.24 0 8 0C10.22 0 12.1 1.44 12.75 3.44C12.83 3.69 12.69 3.94 12.45 4.02C12.17 4.11 11.87 3.94 11.77 3.66C11.22 2.11 9.74 1 8 1ZM3 8L3 15L13 15L13 8Z"
|
|
1560
1588
|
}
|
|
1561
1589
|
}
|
|
@@ -35,5 +35,38 @@ export const keymap: KeymapContribution = {
|
|
|
35
35
|
'edit.deselectAll': [{ key: 'a', alt: true }],
|
|
36
36
|
'viewport.frameSelection': [{ key: '.' }, { key: '', code: 'NumpadDecimal' }],
|
|
37
37
|
'viewport.cyclePivot': [{ key: ',' }],
|
|
38
|
+
// `km_view3d` — `view3d.view_persportho` on NUMPAD_5; Blender has no isometric preset key.
|
|
39
|
+
'view.perspective': [],
|
|
40
|
+
'view.projection': [{ key: '', code: 'Numpad5' }],
|
|
41
|
+
// `km_view3d` — `view3d.view_axis` with Ctrl for the opposite side, `view3d.view_orbit` on
|
|
42
|
+
// NUMPAD_2/4/6/8 (15°, `pad_rot_angle`) and NUMPAD_9 (π), `view3d.view_roll` on Shift+4/6.
|
|
43
|
+
// Blender's Ctrl is the Control key on every platform, macOS included (`ctrl`, not `mod`).
|
|
44
|
+
'view.back': [{ key: '', code: 'Numpad1', ctrl: true }],
|
|
45
|
+
'view.left': [{ key: '', code: 'Numpad3', ctrl: true }],
|
|
46
|
+
'view.bottom': [{ key: '', code: 'Numpad7', ctrl: true }],
|
|
47
|
+
'view.orbitLeft': [{ key: '', code: 'Numpad4' }],
|
|
48
|
+
'view.orbitRight': [{ key: '', code: 'Numpad6' }],
|
|
49
|
+
'view.orbitUp': [{ key: '', code: 'Numpad8' }],
|
|
50
|
+
'view.orbitDown': [{ key: '', code: 'Numpad2' }],
|
|
51
|
+
'view.opposite': [{ key: '', code: 'Numpad9' }],
|
|
52
|
+
'view.rollLeft': [{ key: '', code: 'Numpad4', shift: true }],
|
|
53
|
+
'view.rollRight': [{ key: '', code: 'Numpad6', shift: true }],
|
|
54
|
+
// `view3d.view_all` on HOME; `view3d.zoom` on NUMPAD_PLUS/NUMPAD_MINUS and Ctrl+=/Ctrl+-.
|
|
55
|
+
'view.all': [{ key: '', code: 'Home' }],
|
|
56
|
+
'view.zoomIn': [{ key: '', code: 'NumpadAdd' }, { key: '', code: 'Equal', ctrl: true }],
|
|
57
|
+
'view.zoomOut': [{ key: '', code: 'NumpadSubtract' }, { key: '', code: 'Minus', ctrl: true }],
|
|
58
|
+
},
|
|
59
|
+
// `km_view3d` — `view3d.rotate` on MIDDLEMOUSE and `view3d.move` on Shift+MIDDLEMOUSE; the
|
|
60
|
+
// right button is the context menu's, and Shift+Right places the 3D cursor. The factory
|
|
61
|
+
// rotation is Turntable at 0.4° a pixel (`view_rotate_method`,
|
|
62
|
+
// `view_rotate_sensitivity_turntable`, read back from Blender 5.2; a pixel over `UI_SCALE_FAC`,
|
|
63
|
+
// so one CSS pixel on any display), with Auto Perspective on (`use_auto_perspective`).
|
|
64
|
+
// Zoom: a key steps the distance by 1.2 (`view_zoom_apply_step`), and Ctrl+middle drags in the
|
|
65
|
+
// factory Dolly style (`U.viewzoom`, `viewzoom_scale_value`).
|
|
66
|
+
navigation: {
|
|
67
|
+
orbit: 'middle',
|
|
68
|
+
turntable: { degreesPerPixel: 0.4 },
|
|
69
|
+
autoPerspective: true,
|
|
70
|
+
zoom: { step: 1.2, drag: 'dolly' },
|
|
38
71
|
},
|
|
39
72
|
};
|
|
@@ -53,6 +53,11 @@
|
|
|
53
53
|
"selection": "#ed5700",
|
|
54
54
|
"active": "#ffa028"
|
|
55
55
|
},
|
|
56
|
+
"gizmo": {
|
|
57
|
+
"x": "#ff3352",
|
|
58
|
+
"y": "#8bdc00",
|
|
59
|
+
"z": "#2890ff"
|
|
60
|
+
},
|
|
56
61
|
"region": {
|
|
57
62
|
"outliner": "#282828",
|
|
58
63
|
"properties": "#303030"
|
|
@@ -65,6 +70,7 @@
|
|
|
65
70
|
"scene": "#cccccc",
|
|
66
71
|
"collection": "#ffffff",
|
|
67
72
|
"tool": "#cbcbcb",
|
|
73
|
+
"select": "#ffaf2a",
|
|
68
74
|
"operator": "#95dab2"
|
|
69
75
|
},
|
|
70
76
|
"semantic": {
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
* Blender's bottom row opens with three tiny mouse glyphs, each with one
|
|
5
5
|
* button lit and the action it performs beside it — `Select`, `Rotate View`,
|
|
6
6
|
* `Options` in the reference (`modeling.png`). It is the one piece of chrome
|
|
7
|
-
* that tells a newcomer what the three buttons DO without a menu
|
|
8
|
-
* gap a human build session measured here was exactly that question ("how do
|
|
9
|
-
* I move the camera", six times — `viewport-controls-hint.ts`).
|
|
7
|
+
* that tells a newcomer what the three buttons DO without a menu.
|
|
10
8
|
*
|
|
11
9
|
* THEY ARE HOVER-DRIVEN THERE, AND ALWAYS-ON HERE. Measured across the whole
|
|
12
10
|
* reference set: `modeling.png` is the ONE frame with any ink left of centre
|
|
@@ -33,9 +31,7 @@
|
|
|
33
31
|
* `editor-viewport.ts:682-686` sets `orbitControls.mouseButtons` to
|
|
34
32
|
* `{ LEFT: -1, MIDDLE: PAN, RIGHT: ROTATE }`, and the left button is left
|
|
35
33
|
* free for the selection raycast (`editor-viewport.ts:3782`,
|
|
36
|
-
* `if (e.button === 0 && !e.altKey)`).
|
|
37
|
-
* same thing (`ViewportControlsHint.tsx:22`: "Orbit: right-drag · Pan:
|
|
38
|
-
* middle-drag").
|
|
34
|
+
* `if (e.button === 0 && !e.altKey)`).
|
|
39
35
|
*
|
|
40
36
|
* That contract is a constant of the host, not of any document, so the table
|
|
41
37
|
* below is a constant too — with the citations above as its provenance. When
|