@volter/editor-blender 0.1.150 → 0.1.152
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/LICENSE +18 -9
- package/README.md +5 -5
- package/contributions/blender-animation-editors.tsx +17 -69
- package/contributions/blender-display-settings.ts +36 -0
- package/contributions/blender-export-gltf.ts +228 -0
- package/contributions/blender-export.verbs.ts +39 -0
- package/contributions/blender-gltf-export.verbs.ts +312 -0
- package/contributions/blender-header-menus.tsx +37 -133
- package/contributions/blender-icon-trace.mjs +8 -98
- package/contributions/blender-icons.source.mjs +96 -860
- package/contributions/blender-mixer-pose.ts +369 -0
- package/contributions/blender-model-opening.tsx +26 -56
- package/contributions/blender-node-editor.document.tsx +30 -181
- package/contributions/blender-node-geometry.ts +44 -230
- package/contributions/blender-node-panels.source.mjs +7 -121
- package/contributions/blender-outliner-authoring.ts +61 -415
- package/contributions/blender-outliner-model.ts +15 -112
- package/contributions/blender-play-area.tsx +99 -0
- package/contributions/blender-playback-movie.ts +409 -0
- package/contributions/blender-pose.ts +18 -368
- package/contributions/blender-properties-context.ts +1 -1
- package/contributions/blender-properties-model.ts +59 -91
- package/contributions/blender-properties-tab.tsx +3 -50
- package/contributions/blender-properties-view.tsx +39 -181
- package/contributions/blender-remedy.ts +14 -0
- package/contributions/blender-runtime-skin.ts +103 -99
- package/contributions/blender-runtime.document.tsx +170 -618
- package/contributions/blender-scene-mixer.ts +51 -0
- package/contributions/blender-screenshot.service.ts +28 -0
- package/contributions/blender-timeline-geometry.ts +16 -136
- package/contributions/blender-timeline.document.tsx +145 -333
- package/contributions/blender-uv-editor.document.tsx +48 -112
- package/contributions/blender-uv-geometry.ts +14 -179
- package/contributions/blender-version.status.tsx +45 -31
- package/contributions/blender.environment.ts +2 -2
- package/contributions/blender.icons.json +0 -9
- package/contributions/blender.keymap.ts +9 -51
- package/contributions/blender.node-panels.json +4 -13
- package/contributions/blender.page.ts +64 -0
- package/contributions/blender.status.tsx +8 -150
- package/contributions/blender.style.ts +25 -152
- package/contributions/blender.verbs.ts +52 -0
- package/contributions/model.layout.ts +2 -34
- package/contributions/models.finder.ts +3 -30
- package/contributions/properties-bone-constraints.inspector.tsx +1 -16
- package/contributions/properties-bone.inspector.tsx +2 -31
- package/contributions/properties-collection.inspector.tsx +1 -34
- package/contributions/properties-constraints.inspector.tsx +1 -21
- package/contributions/properties-data.inspector.tsx +2 -39
- package/contributions/properties-material.inspector.tsx +2 -33
- package/contributions/properties-modifiers.inspector.tsx +2 -30
- package/contributions/properties-object.inspector.tsx +2 -44
- package/contributions/properties-output.inspector.tsx +1 -27
- package/contributions/properties-particles.inspector.tsx +2 -46
- package/contributions/properties-physics.inspector.tsx +2 -41
- package/contributions/properties-render.inspector.tsx +1 -45
- package/contributions/properties-scene.inspector.tsx +1 -35
- package/contributions/properties-texture.inspector.tsx +1 -49
- package/contributions/properties-view-layer.inspector.tsx +1 -29
- package/contributions/properties-world.inspector.tsx +2 -39
- package/contributions/shading.layout.ts +3 -78
- package/contributions/uv-editing.layout.ts +3 -70
- package/dist-node/serving.mjs +13 -32
- package/host/blender-runtime-host.ts +795 -412
- package/host/scene-sound.ts +143 -0
- package/node/blender-mcp.ts +52 -95
- package/package.json +1221 -46
- package/producticons/blender-icons.otf +0 -0
- package/producticons/blender-product-icon-theme.json +129 -0
- package/serving/blender-routes.ts +9 -48
- package/serving/blender-wasm-artifact.ts +29 -166
- package/serving/index.ts +1 -1
- package/src/api.ts +9 -0
- package/src/layouts.tsx +1 -17
- package/src/looks.ts +1 -12
- package/src/model-document-saving.ts +25 -0
- package/src/node-view-state.ts +3 -25
- package/src/play-mode.ts +13 -42
- package/src/play-tool.ts +44 -0
- package/src/presentation.ts +6 -18
- package/src/regions.ts +3 -43
- package/src/start-document.ts +15 -0
- package/src/timeline-view-state.ts +4 -39
- package/src/uv-view-state.ts +4 -20
- package/themes/blender.json +299 -0
- package/view/blender-view-routes.ts +1 -1
- package/contributions/blender-game-panel.tsx +0 -809
- package/contributions/blender-palette.source.mjs +0 -350
- package/contributions/blender-play-skin.ts +0 -508
- package/contributions/blender-screenshot.menu.ts +0 -32
- package/contributions/blender.command.ts +0 -109
- package/contributions/blender.palette.json +0 -102
- package/src/play-live.ts +0 -27
|
@@ -1,45 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The BLENDER style bundle — Blender's greys and widget blue as a palette
|
|
3
|
-
* document (`blender.palette.json`, the same v3 shape a person imports), its
|
|
4
|
-
* own opaque material, and Blender's chrome regions
|
|
5
|
-
* (`@volter/sdk/looks`, a `workspace.style` contribution).
|
|
6
|
-
*
|
|
7
|
-
* The material transcribes Blender's default theme: widgets round at 0.2 of
|
|
8
|
-
* their height (4px on a 20px widget), areas and panels are flat with no
|
|
9
|
-
* shadow, and only menus and popovers cast one (`menu_shadow_fac` 0.5 over
|
|
10
|
-
* `menu_shadow_width` 4). The `large` radius is our reading — Blender rounds
|
|
11
|
-
* its popovers the same as its menus. The density is measured from Blender
|
|
12
|
-
* 5.2's own frames at 1x:
|
|
13
|
-
* 26px top bar and area headers, 24px status bar, and — re-measured against
|
|
14
|
-
* the frames widget by widget — EVERY widget 20px tall, button/field/toggle
|
|
15
|
-
* alike, so all three control sizes are 20 rather than a 18/20/24 ramp
|
|
16
|
-
* Blender does not have. Its UI face measures 11px (an 8px cap+ascender on
|
|
17
|
-
* "Add Modifier"); the rest of the type scale is our reading.
|
|
18
|
-
*
|
|
19
|
-
* THE ICON SET (`blender.icons.json`) IS PART BLENDER'S, AND THAT IS WHY THIS
|
|
20
|
-
* PACKAGE CARRIES TWO LICENCES. Of its 346 glyphs, 197 are machine-TRACED —
|
|
21
|
-
* byte for byte — from Blender's own vector sources
|
|
22
|
-
* (`release/datafiles/icons_svg/*.svg`, 188 distinct files at the engine's
|
|
23
|
-
* pin): an affine transform of Blender's outline onto a 16-unit grid, nothing
|
|
24
|
-
* re-proportioned or re-centred, so a traced glyph IS Blender's drawing and a
|
|
25
|
-
* derivative of GPL-2.0-or-later artwork. Their provenance — `ICON_*` name,
|
|
26
|
-
* source file and sha256 per glyph — is `blender.icons.traced.json`, written
|
|
27
|
-
* by `blender-icon-trace.mjs`. The other 149 are our own drawings in Blender's
|
|
28
|
-
* idiom: a 16-unit grid, monochrome filled silhouettes, ~1.3-unit round-ended
|
|
29
|
-
* strokes, no outline around a fill.
|
|
30
|
-
*
|
|
31
|
-
* So `@volter/editor-blender` is `AGPL-3.0-only AND GPL-3.0-or-later` — our code AGPL,
|
|
32
|
-
* Blender's artwork GPL, combined under each licence's §13
|
|
33
|
-
* (`packages/blender/LICENSE` carries the notice and both texts). NOTHING
|
|
34
|
-
* TRACED MAY BE COPIED INTO AN APACHE-2.0 OR MIT PART OF THIS REPO;
|
|
35
|
-
* `packages/editor`'s own icon set in particular stays free of it.
|
|
36
|
-
*
|
|
37
|
-
* A glyph is EDITED in `blender-icons.source.mjs` and the JSON re-emitted by
|
|
38
|
-
* running it; a path `d` is not a thing a person edits by hand.
|
|
39
|
-
*/
|
|
1
|
+
/** Blender’s workspace style supplies theme colours, opaque material, typography and region geometry. Widgets round at 0.2 of their height; menus and popovers carry the theme shadow. blender-icons.source.mjs generates the icon set. Traced Blender artwork is GPL-3.0-or-later, combined with this package’s AGPL-3.0-only code under both licences’ section 13; preserve LICENSE and per-glyph provenance in blender.icons.traced.json. Do not copy traced artwork into Apache-2.0 or MIT packages. */
|
|
40
2
|
import type { IconSetContribution, StyleContribution } from '@volter/sdk/looks';
|
|
41
3
|
import iconsJson from './blender.icons.json';
|
|
42
|
-
import palette from './blender.palette.json';
|
|
43
4
|
|
|
44
5
|
/**
|
|
45
6
|
* `resolveJsonModule` widens every string in a JSON import, so each glyph's
|
|
@@ -54,7 +15,8 @@ export const point = 'workspace.style';
|
|
|
54
15
|
export const style: StyleContribution = {
|
|
55
16
|
id: 'blender',
|
|
56
17
|
title: 'Blender',
|
|
57
|
-
|
|
18
|
+
// Theme role values come from userdef_default_theme.c through scripts/workbench/generate-blender-theme.mjs. JSON carries the palette; constants below define this style’s chrome geometry.
|
|
19
|
+
colorTheme: 'blender',
|
|
58
20
|
materialId: 'blender',
|
|
59
21
|
material: {
|
|
60
22
|
id: 'blender',
|
|
@@ -69,139 +31,47 @@ export const style: StyleContribution = {
|
|
|
69
31
|
density: {
|
|
70
32
|
control: { compact: 20, default: 20, comfortable: 20 },
|
|
71
33
|
font: { xs: 9, sm: 10, base: 11, md: 11, lg: 12 },
|
|
72
|
-
//
|
|
73
|
-
// chrome (the Properties tab rail in `properties-data-edit.png`, the
|
|
74
|
-
// Outliner's row and toggle marks in `outliner.png`).
|
|
75
|
-
//
|
|
76
|
-
// `2xl` is now MEASURED too, and it came down from the extrapolated 20.
|
|
77
|
-
// Its only consumer is the viewport navigation cluster
|
|
78
|
-
// (`ViewportFurniture.tsx`), and Blender's own cluster in
|
|
79
|
-
// `modeling-object-none.png` draws 15x15 px of INK at 1x. These glyphs
|
|
80
|
-
// ink at ~0.85 of their box (`blender-icons.source.mjs`'s `S` dial), so
|
|
81
|
-
// 15 px of ink is an 18 px box — at 20 the cluster measured 17x17,
|
|
82
|
-
// two pixels heavier than the frame it is copying.
|
|
83
|
-
//
|
|
84
|
-
// `xs` IS MEASURED NOW, and the note it replaces was wrong about the
|
|
85
|
-
// frames: they do show glyphs below the 14 px chrome rung — the claim
|
|
86
|
-
// had only ever looked at the status bar. The whole sub-14 inventory,
|
|
87
|
-
// ink bounding boxes in CSS px (device halved), all at native 2x:
|
|
88
|
-
//
|
|
89
|
-
// `+` Add Modifier `properties-modifier.png` 9.0 x 9.0
|
|
90
|
-
// `+` list add `properties-data-edit.png` 9.0 x 9.0
|
|
91
|
-
// chevron, list button `properties-data-edit.png` 9.0 x 5.0
|
|
92
|
-
// chevron, panel band `properties-data-edit.png` 9.0 x 5.0
|
|
93
|
-
// caret, datablock well `properties-data-edit.png` 6.5 x 4.0
|
|
94
|
-
// caret, editor-type `properties-modifier.png` 6.5 x 4.0
|
|
95
|
-
// grip `::::` `properties-data-edit.png` 10.0 x 4.0
|
|
96
|
-
// triangle, list row `properties-data-edit.png` 6.0 x 5.0
|
|
97
|
-
//
|
|
98
|
-
// FOUR of those agree at 9.0 wide and that is the rung. Solved against
|
|
99
|
-
// the one glyph both sides draw on the same fill (`#535353`, so the
|
|
100
|
-
// threshold is identical): Blender's `+` arms measure 17.43 and 17.08
|
|
101
|
-
// device px by sub-pixel coverage, mean 8.63 CSS; ours at `xs` 12
|
|
102
|
-
// measure 20.75 device (10.38 CSS, against the path's own geometric
|
|
103
|
-
// bbox of 10.48 — they agree to a tenth). Our glyphs ink 0.865 of the
|
|
104
|
-
// box, so the box that inks 8.63 is 9.98. Two other estimators on the
|
|
105
|
-
// same pair — ink MASS (mass scales as the square, ratio 0.8171) and
|
|
106
|
-
// the pixel bbox (18/22 = 0.818) — give 9.81 and 9.82. The rung is 10.
|
|
107
|
-
//
|
|
108
|
-
// THE RUNG WAS CHECKED AGAINST A SECOND GLYPH FAMILY AFTER LANDING,
|
|
109
|
-
// and it holds: at 10 the dock well's caret inks 5.84 x 3.71 CSS
|
|
110
|
-
// against Blender's editor-type caret at 6.0 x 4.0 (12 x 8 device,
|
|
111
|
-
// `properties-modifier.png` x 62..73, y 23..30). Our chevron inks 0.58
|
|
112
|
-
// of its box where the `+` inks 0.87, so the two agree on the rung
|
|
113
|
-
// only because each was solved against its own counterpart — which is
|
|
114
|
-
// why both were measured rather than one inferred from the other.
|
|
115
|
-
//
|
|
116
|
-
// RESIDUE, measured not closed: Blender has TWO chevron sizes, the
|
|
117
|
-
// well caret at 6.0 wide and the panel-band / list-button chevron at
|
|
118
|
-
// 9.0, and they are not a uniform scale of each other (0.67 wide
|
|
119
|
-
// against 0.80 tall). One rung cannot serve both; our chevron lands on
|
|
120
|
-
// the well caret, so a band chevron drawn at `xs` would ink 5.84 where
|
|
121
|
-
// Blender's inks 9.0. Closing that needs a second glyph, not a rung —
|
|
122
|
-
// and no expanded band is on screen in the Model document to confirm
|
|
123
|
-
// it against.
|
|
34
|
+
// Icon boxes account for each glyph’s optical extent; small controls, ordinary chrome and viewport navigation use distinct rungs.
|
|
124
35
|
icon: { xs: 10, sm: 14, md: 14, lg: 16, xl: 16, '2xl': 18 },
|
|
125
36
|
chrome: {
|
|
37
|
+
// The top bar's height, traced from Blender 5.2's own frames at 1x.
|
|
126
38
|
commandBar: 26,
|
|
127
39
|
panelHeader: 26,
|
|
128
40
|
localToolbar: 26,
|
|
41
|
+
// Blender’s Outliner row pitch is 20 CSS pixels.
|
|
129
42
|
treeRow: 20,
|
|
130
|
-
//
|
|
131
|
-
// own height. Measured on the native 2x `outliner.png`: the type
|
|
132
|
-
// glyphs of Scene Collection, Collection and Camera centre at x 60.5,
|
|
133
|
-
// 100.5 and 140.5 device px (30.25 / 50.25 / 70.25 CSS) and each
|
|
134
|
-
// row's chevron sits one cell to the left of its glyph (Collection's
|
|
135
|
-
// at 30.25, Camera's at 50.25), so the step is 20 and the chevron and
|
|
136
|
-
// the glyph each own one cell of it.
|
|
43
|
+
// Outliner depth steps by one row-height cell; each row’s chevron occupies the cell before its type glyph.
|
|
137
44
|
treeIndent: 20,
|
|
138
45
|
statusBar: 24,
|
|
139
|
-
//
|
|
140
|
-
// (`modeling-edit-none.png`, column x=25 and row y=400): a tool is a
|
|
141
|
-
// 40x35 BORDER BOX — 38x34 of `widget.menu` fill inside a one-pixel
|
|
142
|
-
// boundary — and the boxes are CONTIGUOUS within a group, sharing that
|
|
143
|
-
// one pixel, so the pitch down a strip is 35. Plain viewport shows
|
|
144
|
-
// only BETWEEN groups (four times in Blender's twenty-one-tool rail).
|
|
145
|
-
// These three numbers are the border box, because that is what the
|
|
146
|
-
// rail's CSS sets and what the collapse (`margin-block-start: -1px`
|
|
147
|
-
// in `workspace-dock.css`) measures against: 36 - 1 = the 35 pitch.
|
|
148
|
-
//
|
|
149
|
-
// `toolGap` RE-RE-MEASURED 2026-09-19 on the same frame, BOX EDGE TO
|
|
150
|
-
// BOX EDGE rather than fill to fill: group 1's border box ends at
|
|
151
|
-
// device y 312 and group 2's begins at 323, so the separation is 10
|
|
152
|
-
// device px — 5 CSS, not the 7 or 8 the two earlier readings gave.
|
|
153
|
-
// Both were counting the group's own 1 CSS px emboss (device rows
|
|
154
|
-
// 313-314, luminance 53 over the viewport's 63) and the two borders
|
|
155
|
-
// as part of the bare viewport between the groups.
|
|
46
|
+
// Toolbar dimensions are border boxes. Adjacent tools share a one-pixel boundary; toolGap separates groups.
|
|
156
47
|
toolSize: 36,
|
|
157
48
|
toolWidth: 40,
|
|
158
49
|
toolGap: 5,
|
|
159
|
-
//
|
|
160
|
-
// Outliner-to-Properties groove (median over x 2900..3400) runs
|
|
161
|
-
// y 419..425 as (21,21,21) and the viewport-to-Properties groove
|
|
162
|
-
// runs x 2836..2842 as the same value — 7 device px, 3.5 CSS px,
|
|
163
|
-
// both times with a one-pixel lighter emboss on each side. The
|
|
164
|
-
// colour is the palette's `color.boundary.area`.
|
|
50
|
+
// The area seam is a 3.5-CSS-px dark groove between docked areas.
|
|
165
51
|
areaSeam: 3.5,
|
|
166
|
-
//
|
|
167
|
-
// into the area's OWN fill. SOLVED on the native 2x `modeling.png`
|
|
168
|
-
// from four fills and their light bands — Properties 47 -> 64
|
|
169
|
-
// (x=3200, y 426..427), Outliner 39 -> 57/56 (y=300, x 2843..2844),
|
|
170
|
-
// the Properties tab rail 23 -> 43/42 (y=1500, same columns), the
|
|
171
|
-
// Outliner's alternate row 42 -> 59/60 (x=3200, y 417..418) — whose
|
|
172
|
-
// individual alphas are 8.17/8.10/8.62/8.45%, mean 8.14; 8.2 is the
|
|
173
|
-
// one value that renders all four measured integers. The viewport's
|
|
174
|
-
// 63 -> 93/94 and the 3D View header's 52 -> 86 are this same lift
|
|
175
|
-
// applied TWICE; see `theme.ts`'s `areaEmbossValue`.
|
|
52
|
+
// Emboss mixes white into each area’s own fill; overlapping viewport passes apply the lift twice.
|
|
176
53
|
areaEmboss: 8.2,
|
|
54
|
+
// Viewport navigation uses its own 28-px button size rather than the 20-px menu/header control height.
|
|
55
|
+
navigationControl: 28,
|
|
56
|
+
// Center the collapsed 4.5×9 and expanded 8.5×5 CSS-pixel carets in an 18-pixel cell.
|
|
57
|
+
treeCaret: 18,
|
|
58
|
+
// Outliner glyph plates use a 5-CSS-px corner radius.
|
|
59
|
+
treeGlyphPlateRadius: 5,
|
|
60
|
+
// Composite Outliner glyphs at 0.80 over the row fill.
|
|
61
|
+
treeGlyphOpacity: 0.8,
|
|
177
62
|
},
|
|
178
63
|
},
|
|
179
64
|
stage: {
|
|
180
|
-
//
|
|
181
|
-
// gizmo perhaps the wrong size?"). `U.gizmo_size` is 75
|
|
182
|
-
// (`DNA_userdef_types.h:1095`; Preferences > Viewport > Gizmos > Size,
|
|
183
|
-
// `rna_userdef.cc:5415-5419`, range 10..200), and it is px PER GIZMO
|
|
184
|
-
// UNIT: `wm_gizmo.cc:450-474` sets `scale_final = scale_basis *
|
|
185
|
-
// UI_SCALE_FAC * U.gizmo_size * ED_view3d_pixel_size_no_ui_scale(...)`,
|
|
186
|
-
// whose last term is world units per DEVICE px against a
|
|
187
|
-
// `UI_SCALE_FAC` of device px per UI px — so one gizmo unit is 75 CSS
|
|
188
|
-
// px whatever the display scale (read back from the engine's own pin
|
|
189
|
-
// on this box: `ui_scale=2.0 pixel_size=2.0 gizmo_size=75`).
|
|
65
|
+
// Blender gizmo units stay 75 CSS px at every display scale, using world units per device pixel and UI_SCALE_FAC.
|
|
190
66
|
gizmoSize: 75,
|
|
191
67
|
// A resting handle at 0.6 opacity, a highlighted one opaque in its own colour
|
|
192
68
|
// (`transform_gizmo_3d.cc`, `gizmo_get_axis_color`).
|
|
193
69
|
gizmoOpacity: 0.6,
|
|
194
|
-
//
|
|
195
|
-
// of orange around the cube in `modeling-object-selected.png` (at 2x), no halo.
|
|
70
|
+
// Blender’s selection outline is a hard 2-CSS-pixel line without a halo; hidden parts are not drawn.
|
|
196
71
|
outlineStyle: 'crisp',
|
|
197
72
|
outlineWidth: 4,
|
|
198
73
|
outlineHidden: false,
|
|
199
|
-
//
|
|
200
|
-
// `modeling-object-none.png` at device resolution: the 1 m line is 4 px at half rise and
|
|
201
|
-
// plateaus at 83, the 10 m line is 6 px and plateaus at 101 — so the major line is wider
|
|
202
|
-
// (2.25 against 1.5) and carried (102 − 63) / (84 − 63) past the minor's colour from the
|
|
203
|
-
// background they share. Stated here, the look's, since the editor's own floor is a
|
|
204
|
-
// single hairline level.
|
|
74
|
+
// The Blender floor uses distinct major/minor line widths and colour strengths over the shared background.
|
|
205
75
|
gridLineWidth: 1.5,
|
|
206
76
|
gridMajorWidth: 2.25,
|
|
207
77
|
gridMajorContrast: 39 / 21,
|
|
@@ -215,5 +85,8 @@ export const style: StyleContribution = {
|
|
|
215
85
|
},
|
|
216
86
|
},
|
|
217
87
|
icons,
|
|
218
|
-
|
|
88
|
+
typography: {
|
|
89
|
+
sans: "'Inter', 'DejaVu Sans', system-ui, sans-serif",
|
|
90
|
+
mono: "'DejaVu Sans Mono', Menlo, monospace",
|
|
91
|
+
},
|
|
219
92
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Session Blender verbs relay to volter.<verb> commands registered by the page half. Each manifest entry owns its timeout and derived-refresh policy. Mutating commands present a frame and refresh the document; handlers use only SDK host doors. */
|
|
2
|
+
import type { VerbHandler } from '@volter/sdk/commands';
|
|
3
|
+
import { handleBlenderCommand } from '../host/blender-runtime-host';
|
|
4
|
+
|
|
5
|
+
/** The lane's one handler, for every verb. */
|
|
6
|
+
const handle: VerbHandler = (cmd) => handleBlenderCommand(cmd as { type: string; [key: string]: unknown });
|
|
7
|
+
|
|
8
|
+
export const blenderVerbs: Readonly<Record<string, VerbHandler>> = {
|
|
9
|
+
// Presentation-only comparison, no engine mutation or derived frame refresh.
|
|
10
|
+
'blender-draw-batching': handle,
|
|
11
|
+
'blender-start': handle,
|
|
12
|
+
// Stop drains accepted modeling work and persists it before teardown.
|
|
13
|
+
'blender-stop': handle,
|
|
14
|
+
'blender-execute': handle,
|
|
15
|
+
'blender-native-preview': handle,
|
|
16
|
+
'blender-scene-info': handle,
|
|
17
|
+
'blender-object-info': handle,
|
|
18
|
+
'blender-screenshot-view': handle,
|
|
19
|
+
'blender-read-file': handle,
|
|
20
|
+
'blender-write-file': handle,
|
|
21
|
+
// File listing uses the worker-read timeout while presentation and derived reads finish.
|
|
22
|
+
'blender-list-files': handle,
|
|
23
|
+
// RNA reads expose properties and context without presenting; RNA writes mutate, present and derive.
|
|
24
|
+
'blender-rna': handle,
|
|
25
|
+
'blender-rna-context': handle,
|
|
26
|
+
'blender-rna-set': handle,
|
|
27
|
+
// Outliner reads expose the View Layer tree; restriction writes mutate, present and derive.
|
|
28
|
+
'blender-outliner': handle,
|
|
29
|
+
// The node-tree door is read-only and requires no derivation.
|
|
30
|
+
'blender-node-tree': handle,
|
|
31
|
+
// The UV door reads one mesh layout; no UV writer is registered here.
|
|
32
|
+
'blender-uv-layout': handle,
|
|
33
|
+
// THE RIG AND CLIP DOORS (the Timeline). Both READS — the skin binding a
|
|
34
|
+
// `THREE.SkinnedMesh` is built from and the action as three.js tracks — so
|
|
35
|
+
// neither presents and neither owes a derivation. There is deliberately no
|
|
36
|
+
// writer beside them: keying, moving a key and setting a range are edits,
|
|
37
|
+
// and the ONE write the Timeline makes (`scene.frame_current`, once on
|
|
38
|
+
// pause and at scrub-end) goes through `blender-rna-set`, which already
|
|
39
|
+
// exists and already presents.
|
|
40
|
+
'blender-action-clip': handle,
|
|
41
|
+
// Node-view commands inspect and navigate view state. Editing gestures return the same named refusal as pointer handlers.
|
|
42
|
+
'blender-node-view': handle,
|
|
43
|
+
'blender-outliner-set': handle,
|
|
44
|
+
// A FILM, RENDERED (`cyclotron render-movie`, a film's Blender shots): started, answering at once, then followed
|
|
45
|
+
// until it ends. The frames are Blender's own renders; each chunk puts the scene's render settings and frame back, so
|
|
46
|
+
// none presents a change to the model.
|
|
47
|
+
'blender-render-movie': handle,
|
|
48
|
+
'blender-render-status': handle,
|
|
49
|
+
// A read of whether this tab HAS a session; it never starts one — and the
|
|
50
|
+
// one verb that answers before a project session exists.
|
|
51
|
+
'blender-status': handle,
|
|
52
|
+
};
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The MODEL workspace — Blender-shaped modeling: a dominant document, outliner
|
|
3
|
-
* above properties (`@volter/sdk/looks`, a `workspace.layout`
|
|
4
|
-
* contribution). Its arrangement carries Blender 5.2's own MODELING-workspace
|
|
5
|
-
* proportions; `../src/layouts.tsx` states the measurement and the host's
|
|
6
|
-
* fixed-pane clamp that bounds it.
|
|
7
|
-
*/
|
|
1
|
+
/** Model workspace: a dominant document, outliner above Properties and a lower Timeline area. The workbench owns pane geometry. */
|
|
8
2
|
|
|
9
3
|
import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
|
|
10
4
|
import { BLENDER_REGIONS } from '../src/regions';
|
|
@@ -26,32 +20,6 @@ export const layout: WorkspaceLayoutContribution = {
|
|
|
26
20
|
inspector: 'properties',
|
|
27
21
|
drawer: 'hidden',
|
|
28
22
|
},
|
|
29
|
-
//
|
|
30
|
-
// a timeline at the bottom?"). It does — and the workspace this Model
|
|
31
|
-
// document stands in is Blender's LAYOUT screen for us, not its Modeling
|
|
32
|
-
// one: Layout is the four-area screen with the Timeline, and Modeling is the
|
|
33
|
-
// same screen WITHOUT it (measured below).
|
|
34
|
-
//
|
|
35
|
-
// THE PROPORTION IS BLENDER'S OWN, measured through bpy on the installed
|
|
36
|
-
// Blender 5.2.1 at factory settings in a 1920-wide window
|
|
37
|
-
// (`bpy.data.workspaces['Layout'].screens[0].areas`):
|
|
38
|
-
//
|
|
39
|
-
// PROPERTIES x1579 y23 w339 h839
|
|
40
|
-
// OUTLINER x1579 y865 w339 h189
|
|
41
|
-
// DOPESHEET_EDITOR x2 y23 w1574 h74 ui_type TIMELINE ← this area
|
|
42
|
-
// VIEW_3D x2 y100 w1574 h954
|
|
43
|
-
//
|
|
44
|
-
// and the same screen at a 1600×929 window gives the Timeline 63 of an
|
|
45
|
-
// 880-px centre column. Both are the same ratio to three places: 74/1029 =
|
|
46
|
-
// 0.0719 and 63/880 = 0.0716. The Modeling screen's own reading, for the
|
|
47
|
-
// contrast: VIEW_3D `x2 y23 w1574 h1031` and no DOPESHEET area at all.
|
|
48
|
-
//
|
|
49
|
-
// It is stated to four places because it is a measurement, not a preference;
|
|
50
|
-
// the dock uses it once, to size the group the first time this workspace
|
|
51
|
-
// stands up, and a capture of the settled grid wins afterwards.
|
|
52
|
-
//
|
|
53
|
-
// IN GAME MODE THE SAME AREA HOLDS THE GAME PANEL (`../src/play-mode.ts`):
|
|
54
|
-
// the document below draws the Timeline or the panel by the model's mode,
|
|
55
|
-
// so the area, its proportion and a person's resize all carry over.
|
|
23
|
+
// The Model workspace’s bottom editor area hosts the Timeline or Game panel. Initial Timeline ratio is Blender Layout’s 74/1029 ≈ 0.0719; a person’s later resize takes precedence.
|
|
56
24
|
areas: [{ id: 'timeline', document: 'blender-timeline.document', place: 'below', ratio: 0.0719 }],
|
|
57
25
|
};
|
|
@@ -1,33 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* The `modelsFromBlendFiles` FINDER (ARCHITECTURE-CORE §The project model,
|
|
3
|
-
* "A model is Blender data, and a prefab is not a model"): every `.blend`
|
|
4
|
-
* under the selection's `include` globs is a `model` document — a Blender
|
|
5
|
-
* file the engine in the tab opens, edits and saves. The bpy scripts that
|
|
6
|
-
* authored it are ordinary project files beside it (`src/models/<name>.py`);
|
|
7
|
-
* this document does not run them, a person or an agent does, through the
|
|
8
|
-
* session's Blender (`volter blender-mcp`).
|
|
9
|
-
*
|
|
10
|
-
* It replaced `modelsFromMeshModules`, which listed every module exporting
|
|
11
|
-
* `build(): THREE.BufferGeometry` (owner ruling 2026-09-19, "do the delete":
|
|
12
|
-
* the TypeScript mesh kit was the modeling engine while Blender could not run
|
|
13
|
-
* in the tab; it now does, and a model is Blender data).
|
|
14
|
-
*
|
|
15
|
-
* Select it in `editor/volter.adapter.ts`:
|
|
16
|
-
*
|
|
17
|
-
* documents: { find: [{ finder: 'modelsFromBlendFiles', include: ['src/models/**\/*.blend'] }] }
|
|
18
|
-
*
|
|
19
|
-
* THE SESSION'S OWN MODEL. A project that holds no `.blend` of its own still
|
|
20
|
-
* needs ONE model — it is what `blender-start` presents into and what a first
|
|
21
|
-
* bpy call models in, saved to the session's default `models/model.blend` — so
|
|
22
|
-
* when the selection finds no file, the finder lists the standing
|
|
23
|
-
* `blender:runtime` entry instead. Never beside a real model.
|
|
24
|
-
*
|
|
25
|
-
* This module is a plain object the HOST registers; it mounts no UI. It reads
|
|
26
|
-
* the project only through `input.files` — the PATHS matching those globs.
|
|
27
|
-
* Deliberately not `input.sources`: a `.blend` is binary, there is nothing in
|
|
28
|
-
* it a text walk could honestly read, and the entry is the file itself. It says
|
|
29
|
-
* so (`reads: 'files'`), and the host then lists the files without fetching them.
|
|
30
|
-
*/
|
|
1
|
+
|
|
31
2
|
|
|
32
3
|
import { z } from 'zod';
|
|
33
4
|
|
|
@@ -62,6 +33,8 @@ export const finder = {
|
|
|
62
33
|
authorable: true,
|
|
63
34
|
reach: { kind: 'root-mount' as const },
|
|
64
35
|
source: { path },
|
|
36
|
+
// (the Model document opens a `.blend` not written yet as Blender's empty file, and its first save writes it)
|
|
37
|
+
opensUnwritten: true,
|
|
65
38
|
});
|
|
66
39
|
}
|
|
67
40
|
if (entries.length === 0) {
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S BONE CONSTRAINTS TAB, as a Properties section (WORK.md §Blender in
|
|
3
|
-
* the tab is Blender, "Inspection parity", I2): the active POSE BONE's
|
|
4
|
-
* constraint stack, with the generic view beneath under "All properties".
|
|
5
|
-
*
|
|
6
|
-
* It is the Object Constraints tab over a different datablock, and Blender
|
|
7
|
-
* says so itself: `properties_constraint.py` registers
|
|
8
|
-
* `BONE_PT_constraints` beside `OBJECT_PT_constraints` with the same
|
|
9
|
-
* `template_constraints()` body, differing only in reading
|
|
10
|
-
* `context.pose_bone.constraints`. So it shares the header list that module
|
|
11
|
-
* exports rather than restating it — a second copy is a second thing to keep
|
|
12
|
-
* in step.
|
|
13
|
-
*
|
|
14
|
-
* It stands when: `buttons_context_path_pose_bone` — a pose channel for the
|
|
15
|
-
* active bone, and never in Edit mode.
|
|
16
|
-
*/
|
|
1
|
+
/** Blender’s Bone Constraints Properties tab follows scripts/startup/bl_ui/properties_constraint.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
17
2
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
18
3
|
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
19
4
|
import { CONSTRAINT_HEADER } from './properties-constraints.inspector';
|
|
@@ -1,26 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S BONE TAB, as a Properties section (WORK.md §Blender in the tab is
|
|
3
|
-
* Blender, "Inspection parity", I2). Blender's own panels over the RNA door,
|
|
4
|
-
* with the generic view beneath under "All properties".
|
|
5
|
-
*
|
|
6
|
-
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_data_bone.py` at the
|
|
7
|
-
* engine's pin: its `classes` tuple is the panel order, `bl_label` the title,
|
|
8
|
-
* each `layout.prop(bone, "x")` an entry. Blender's UI layer is never run,
|
|
9
|
-
* ported or recorded.
|
|
10
|
-
*
|
|
11
|
-
* TWO DATABLOCKS, AND THAT IS THE WHOLE STRUCTURE OF THIS TAB. Blender's panels
|
|
12
|
-
* read `bone` (the armature's `Bone`, or the `EditBone` in Edit mode) for the
|
|
13
|
-
* rest pose — head, tail, roll, envelope, collections, display — and `pchan`
|
|
14
|
-
* (the `PoseBone`) for the POSE: its location/rotation/scale and its inverse
|
|
15
|
-
* kinematics. `buttons_context_path_bone` and `_pose_bone` build both, and the
|
|
16
|
-
* door hands this tab both as "Bone" and "Pose Bone", so each panel names the
|
|
17
|
-
* one Blender's draw function reads. That is why Transform below is split in
|
|
18
|
-
* two the way `BONE_PT_transform` (:39) is: its first half is `pchan`, its
|
|
19
|
-
* second `bone`.
|
|
20
|
-
*
|
|
21
|
-
* It stands when: `buttons_context_path_bone` — an armature with an ACTIVE bone
|
|
22
|
-
* (the edit bone in Edit mode).
|
|
23
|
-
*/
|
|
1
|
+
/** Blender’s Bone Properties tab follows scripts/startup/bl_ui/properties_data_bone.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
24
2
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
25
3
|
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
26
4
|
|
|
@@ -44,14 +22,7 @@ const CURATED: readonly BlenderCuratedPanel[] = [
|
|
|
44
22
|
],
|
|
45
23
|
},
|
|
46
24
|
{
|
|
47
|
-
// BONE_PT_transform
|
|
48
|
-
// NO MATRICES. The first cut named `matrix` and `matrix_local` beside them
|
|
49
|
-
// and the live read showed why Blender does not: a 3x3 and a 4x4 draw as
|
|
50
|
-
// twenty-five numbered rows, which buries the five values the panel is
|
|
51
|
-
// about. They are still in "All properties" beneath, where a matrix is
|
|
52
|
-
// something you go looking for. (`roll` is an EditBone property, so it
|
|
53
|
-
// draws in Edit mode and not in Object mode — the datablock's answer, not
|
|
54
|
-
// a condition of ours.)
|
|
25
|
+
// BONE_PT_transform rest geometry excludes matrices; those remain under All properties. roll exists only on EditBone and draws only when that datablock exposes it.
|
|
55
26
|
title: 'Transform',
|
|
56
27
|
from: 'Bone',
|
|
57
28
|
properties: ['head', 'tail', 'roll', 'length', 'lock'],
|
|
@@ -1,37 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S COLLECTION TAB, as a Properties section (WORK.md §Blender in the
|
|
3
|
-
* tab is Blender, "Inspection parity", I2). Blender's own panels over the RNA
|
|
4
|
-
* door, with the generic view beneath under "All properties".
|
|
5
|
-
*
|
|
6
|
-
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_collection.py` at the
|
|
7
|
-
* engine's pin — `classes` (`:146-155`) is the panel order and `bl_label` the
|
|
8
|
-
* title. Blender's UI layer is never run, ported or recorded.
|
|
9
|
-
*
|
|
10
|
-
* THE FIRST PANEL IS CALLED "VISIBILITY" AT THIS PIN, not "Restrictions":
|
|
11
|
-
* `COLLECTION_PT_collection_flags.bl_label` is `"Visibility"` (`:32`). Its
|
|
12
|
-
* child View Layer (`:47`) does not read the collection at all — it reads
|
|
13
|
-
* `view_layer.active_layer_collection` (`:56`), a different datablock, which
|
|
14
|
-
* the context door now names beside the collection as this tab's second path.
|
|
15
|
-
*
|
|
16
|
-
* TWO CHECKBOXES BLENDER DRAWS INVERTED. `hide_select` is labelled
|
|
17
|
-
* "Selectable" with `invert_checkbox=True` (`:41`) and `exclude` is labelled
|
|
18
|
-
* "Include" the same way (`:60`). What stands here is the RNA's own value
|
|
19
|
-
* under the RNA's own name, because inverting a value in the presentation is
|
|
20
|
-
* exactly the fabrication the anti-shim rule forbids — the same reading the
|
|
21
|
-
* Object tab's Visibility panel already made.
|
|
22
|
-
*
|
|
23
|
-
* WHAT THE TRANSCRIPTION DELIBERATELY DROPS: `COLLECTION_PT_importer`
|
|
24
|
-
* (`:65`), whose poll is a PREFERENCE (`prefs.experimental.use_collection_
|
|
25
|
-
* importer`) and whose body is `template_collection_importer()` — an operator
|
|
26
|
-
* template. Exporters (`:80`) is the same widget over `collection.exporters`,
|
|
27
|
-
* and the collection itself is real RNA (`rna_collection.cc:954`), so its row
|
|
28
|
-
* stands. `COLLECTION_PT_collection_custom_props` (`:143`) is IDProperties
|
|
29
|
-
* rather than RNA.
|
|
30
|
-
*
|
|
31
|
-
* It stands when: `buttons_context_path_collection` — the view layer's ACTIVE
|
|
32
|
-
* collection, and never the scene's master collection, which only the engine
|
|
33
|
-
* knows.
|
|
34
|
-
*/
|
|
1
|
+
/** Blender’s Collection Properties tab follows scripts/startup/bl_ui/properties_collection.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
35
2
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
36
3
|
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
37
4
|
|
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S OBJECT CONSTRAINTS TAB, as a Properties section (WORK.md §Blender
|
|
3
|
-
* in the tab is Blender, "Inspection parity", I2): the STACK, each constraint
|
|
4
|
-
* its own panel with its own settings, the generic view beneath under "All
|
|
5
|
-
* properties".
|
|
6
|
-
*
|
|
7
|
-
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_constraint.py`, and
|
|
8
|
-
* it has the same shape `properties_data_modifier.py` does: `draw` is
|
|
9
|
-
* `layout.template_constraints()` and the per-type panels are built in C from
|
|
10
|
-
* each constraint's RNA. So the stack is derived the same way — the shared
|
|
11
|
-
* `Constraint` header properties named, then everything THIS constraint's own
|
|
12
|
-
* struct declares (`andDeclared`, which reads `BlenderRnaRow.group`). A Copy
|
|
13
|
-
* Location constraint gets `CopyLocationConstraint`'s target, subtarget, use_x
|
|
14
|
-
* / use_y / use_z, invert flags and offset, in RNA's order.
|
|
15
|
-
*
|
|
16
|
-
* The ACTIVE constraint (`buttons_context_path_constraint`, reported as
|
|
17
|
-
* `activeConstraint`) is marked.
|
|
18
|
-
*
|
|
19
|
-
* It stands when: `buttons_context_path_object` — Constraints shares Object's
|
|
20
|
-
* path, so the engine grants it whenever it grants Object.
|
|
21
|
-
*/
|
|
1
|
+
/** Blender’s Object Constraints Properties tab follows scripts/startup/bl_ui/properties_constraint.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
22
2
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
23
3
|
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
24
4
|
|
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S OBJECT DATA TAB, as a Properties section (WORK.md §Blender in the
|
|
3
|
-
* tab is Blender, "Inspection parity", I2). Blender's own panels over the RNA
|
|
4
|
-
* door, with the generic view of the whole datablock beneath under "All
|
|
5
|
-
* properties".
|
|
6
|
-
*
|
|
7
|
-
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_data_mesh.py`, read
|
|
8
|
-
* at the engine's pin: its `classes` tuple is the panel ORDER, each class's
|
|
9
|
-
* `bl_label` the title, each `layout.prop(me, "x")` an entry in the order the
|
|
10
|
-
* draw function lists it, `bl_options = {'DEFAULT_CLOSED'}` a `closed`, and a
|
|
11
|
-
* `template_list` a `collection` panel. Blender's UI layer is never run,
|
|
12
|
-
* ported or recorded.
|
|
13
|
-
*
|
|
14
|
-
* ONE TAB, MANY DATA TYPES, ONE LIST. `buttons_context_path_data` builds this
|
|
15
|
-
* tab for whatever the object's data IS, so the same section draws a Mesh, an
|
|
16
|
-
* Armature, a Camera or a Light — and the panels for all of them live in one
|
|
17
|
-
* list here, because a panel draws only where its properties exist. A Camera's
|
|
18
|
-
* data carries no `remesh_mode`, so the Remesh panel finds nothing and renders
|
|
19
|
-
* nothing; an Armature's carries no `uv_layers` and every mesh panel stands
|
|
20
|
-
* down for it. That is the same answer `MeshButtonsPanel.poll` /
|
|
21
|
-
* `ArmatureButtonsPanel.poll` give, from the datablock rather than from a copy
|
|
22
|
-
* of their conditions — which is why there is no per-type branch in this file
|
|
23
|
-
* and no second module per data type.
|
|
24
|
-
*
|
|
25
|
-
* NO "NORMALS" PANEL: the brief expected one and 5.2 does not have it. The
|
|
26
|
-
* mesh's `DATA_PT_normals` was folded away before this pin — `has_custom_normals`
|
|
27
|
-
* survives as the Geometry Data panel's operator condition (`:456`), and the
|
|
28
|
-
* shading-per-face data now lives on the Attributes list. Stated rather than
|
|
29
|
-
* invented.
|
|
30
|
-
*
|
|
31
|
-
* It stands when: `buttons_context_path_data` — the object HAS data (an Empty
|
|
32
|
-
* usually has none).
|
|
33
|
-
*/
|
|
1
|
+
/** Blender’s Object Data Properties tab follows scripts/startup/bl_ui/properties_data_mesh.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
34
2
|
import type { ToolContributionNode } from '@volter/sdk/contributions';
|
|
35
3
|
import { blenderPropertiesState, resolveBlenderSubject } from './blender-properties-model';
|
|
36
4
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
@@ -93,12 +61,7 @@ const CURATED: readonly BlenderCuratedPanel[] = [
|
|
|
93
61
|
],
|
|
94
62
|
},
|
|
95
63
|
{
|
|
96
|
-
//
|
|
97
|
-
// OPERATORS and one condition (`has_custom_normals`, :456) — no
|
|
98
|
-
// properties at all. Inspection parity is not editing parity
|
|
99
|
-
// (ARCHITECTURE-CORE §Blender north star), so what stands here is the
|
|
100
|
-
// condition the operators branch on, which is the only thing in the panel
|
|
101
|
-
// a reader can learn something from.
|
|
64
|
+
// Geometry Data exposes the condition and selection counts; its editing operators are not drawn here.
|
|
102
65
|
title: 'Geometry Data',
|
|
103
66
|
closed: true,
|
|
104
67
|
properties: ['has_custom_normals', 'total_vert_sel', 'total_edge_sel', 'total_face_sel'],
|
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BLENDER'S MATERIAL TAB, as a Properties section (WORK.md §Blender in the tab
|
|
3
|
-
* is Blender, "Inspection parity", I2). Blender's own panels over the RNA
|
|
4
|
-
* door, with the generic view beneath under "All properties".
|
|
5
|
-
*
|
|
6
|
-
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_material.py` at the
|
|
7
|
-
* engine's pin — its `classes` tuple is the panel order, `bl_label` the
|
|
8
|
-
* title, each `layout.prop(mat, "x")` an entry. Blender's UI layer is never
|
|
9
|
-
* run, ported or recorded.
|
|
10
|
-
*
|
|
11
|
-
* THE SLOTS LIST IS FIRST, and it is `MATERIAL_UL_matslots` over
|
|
12
|
-
* `ob.material_slots` (`:76`, `EEVEE_MATERIAL_PT_context_material`, whose
|
|
13
|
-
* `bl_label` is empty because in Blender it is the tab's header block rather
|
|
14
|
-
* than a panel). The context door hands it to this tab as its own path, with
|
|
15
|
-
* the ACTIVE slot named separately (`buttons_context_path_material`:
|
|
16
|
-
* `BKE_object_material_get(ob, ob->actcol)`), so the list marks it.
|
|
17
|
-
*
|
|
18
|
-
* SURFACE IS A NODE TREE, AND THIS IS NOT THE NODE VIEW. Blender's Surface
|
|
19
|
-
* panel (`:164`) calls `panel_node_draw`, which walks the material output
|
|
20
|
-
* node's `Surface` input back to the shader that feeds it and draws THAT
|
|
21
|
-
* node's inputs by name. Showing those values as a flat list needs the node
|
|
22
|
-
* tree walked, which is I5's read-only node view (§Inspection parity, I5) —
|
|
23
|
-
* and a half version of it here would be a second, worse implementation of
|
|
24
|
-
* the same walk. What stands in this panel instead is what the MATERIAL
|
|
25
|
-
* itself holds: `use_nodes` and the `node_tree` pointer, which drills into
|
|
26
|
-
* the generic view, plus the settings Blender's own EEVEE panels read off the
|
|
27
|
-
* material datablock directly.
|
|
28
|
-
*
|
|
29
|
-
* It stands when: `buttons_context_path_material` — the object's data holds a
|
|
30
|
-
* `materials` collection (the tab stands even when the active slot is empty).
|
|
31
|
-
*/
|
|
1
|
+
/** Blender’s Material Properties tab follows scripts/startup/bl_ui/properties_material.py at the engine pin. Curated panels read the RNA context’s datablocks; the generic view remains under All properties. Match tab availability and engine-specific panels against the live context. */
|
|
32
2
|
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
33
3
|
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
34
4
|
|
|
@@ -44,8 +14,7 @@ const CURATED: readonly BlenderCuratedPanel[] = [
|
|
|
44
14
|
properties: ['name', 'material', 'link', 'slot_index'],
|
|
45
15
|
},
|
|
46
16
|
{
|
|
47
|
-
// EEVEE_MATERIAL_PT_surface, :164
|
|
48
|
-
// material's own two node properties rather than the shader's inputs.
|
|
17
|
+
// EEVEE_MATERIAL_PT_surface, :164. Read the material’s node properties; shader inputs belong to its node tree.
|
|
49
18
|
title: 'Surface',
|
|
50
19
|
from: 'Material',
|
|
51
20
|
properties: ['use_nodes', 'node_tree', 'surface_render_method', 'use_backface_culling'],
|