@volter/editor-blender 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1409 -0
- package/README.md +17 -0
- package/contributions/blender-header-menus.tsx +483 -0
- package/contributions/blender-icon-trace.mjs +403 -0
- package/contributions/blender-icons.source.mjs +2925 -0
- package/contributions/blender-node-editor.document.tsx +1402 -0
- package/contributions/blender-node-geometry.ts +1138 -0
- package/contributions/blender-node-panels.source.mjs +485 -0
- package/contributions/blender-outliner-authoring.ts +1729 -0
- package/contributions/blender-outliner-model.ts +389 -0
- package/contributions/blender-palette.source.mjs +319 -0
- package/contributions/blender-properties-model.ts +351 -0
- package/contributions/blender-properties-tab.tsx +100 -0
- package/contributions/blender-properties-view.tsx +1191 -0
- package/contributions/blender-runtime-skin.ts +619 -0
- package/contributions/blender-runtime.document.tsx +232 -0
- package/contributions/blender-timeline-geometry.ts +323 -0
- package/contributions/blender-timeline.document.tsx +1056 -0
- package/contributions/blender-uv-editor.document.tsx +483 -0
- package/contributions/blender-uv-geometry.ts +305 -0
- package/contributions/blender-version.status.tsx +93 -0
- package/contributions/blender.command.ts +102 -0
- package/contributions/blender.icons.json +1247 -0
- package/contributions/blender.icons.traced.json +1561 -0
- package/contributions/blender.keymap.ts +39 -0
- package/contributions/blender.node-panels.json +2436 -0
- package/contributions/blender.palette.json +93 -0
- package/contributions/blender.status.tsx +263 -0
- package/contributions/blender.style.ts +271 -0
- package/contributions/model.layout.ts +53 -0
- package/contributions/models.finder.ts +59 -0
- package/contributions/properties-bone-constraints.inspector.tsx +50 -0
- package/contributions/properties-bone.inspector.tsx +184 -0
- package/contributions/properties-collection.inspector.tsx +96 -0
- package/contributions/properties-constraints.inspector.tsx +69 -0
- package/contributions/properties-data.inspector.tsx +229 -0
- package/contributions/properties-material.inspector.tsx +121 -0
- package/contributions/properties-modifiers.inspector.tsx +74 -0
- package/contributions/properties-object.inspector.tsx +215 -0
- package/contributions/properties-output.inspector.tsx +210 -0
- package/contributions/properties-particles.inspector.tsx +494 -0
- package/contributions/properties-physics.inspector.tsx +614 -0
- package/contributions/properties-render.inspector.tsx +446 -0
- package/contributions/properties-scene.inspector.tsx +174 -0
- package/contributions/properties-texture.inspector.tsx +300 -0
- package/contributions/properties-view-layer.inspector.tsx +145 -0
- package/contributions/properties-world.inspector.tsx +130 -0
- package/contributions/sculpt.layout.ts +25 -0
- package/contributions/shading.layout.ts +99 -0
- package/contributions/texture.layout.ts +16 -0
- package/contributions/uv-editing.layout.ts +93 -0
- package/host/blender-runtime-host.ts +1256 -0
- package/package.json +77 -0
- package/src/layouts.tsx +48 -0
- package/src/looks.ts +14 -0
- package/src/node-view-state.ts +125 -0
- package/src/timeline-view-state.ts +154 -0
- package/src/uv-view-state.ts +125 -0
|
@@ -0,0 +1,229 @@
|
|
|
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
|
+
*/
|
|
34
|
+
import type { ToolContributionNode } from '@volter/editor-sdk/contributions';
|
|
35
|
+
import { blenderPropertiesState, resolveBlenderSubject } from './blender-properties-model';
|
|
36
|
+
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
37
|
+
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
38
|
+
|
|
39
|
+
/** `properties_data_mesh.py`, panel by panel. Line numbers are that file's. */
|
|
40
|
+
const CURATED: readonly BlenderCuratedPanel[] = [
|
|
41
|
+
{
|
|
42
|
+
// DATA_PT_vertex_groups, :198 — `template_list` over `ob.vertex_groups`,
|
|
43
|
+
// then the active group's name and lock (`MESH_UL_vgroups.draw_item`,
|
|
44
|
+
// :127). The door hands this tab the object's collection as its own path
|
|
45
|
+
// because a vertex group lives on the OBJECT, not on the mesh.
|
|
46
|
+
title: 'Vertex Groups',
|
|
47
|
+
from: 'Vertex Groups',
|
|
48
|
+
collection: true,
|
|
49
|
+
active: 'activeVertexGroup',
|
|
50
|
+
properties: ['name', 'index', 'lock_weight'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
// DATA_PT_shape_keys, :300 — `template_list` over `key.key_blocks`, with
|
|
54
|
+
// each key's value and slider range (:282-294). The door names the Key
|
|
55
|
+
// datablock; its `key_blocks` is the list.
|
|
56
|
+
title: 'Shape Keys',
|
|
57
|
+
from: 'Shape Keys',
|
|
58
|
+
properties: ['key_blocks', 'use_relative', 'eval_time', 'reference_key'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
// DATA_PT_uv_texture, :377 — `template_list` over `mesh.uv_layers`
|
|
62
|
+
// (`MESH_UL_uvmaps.draw_item`, :135: the name and `active_render`).
|
|
63
|
+
title: 'UV Maps',
|
|
64
|
+
properties: ['uv_layers'],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
// DATA_PT_vertex_colors, :683 — `mesh.color_attributes`.
|
|
68
|
+
title: 'Color Attributes',
|
|
69
|
+
properties: ['color_attributes'],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
// DATA_PT_mesh_attributes, :545 — `mesh.attributes`.
|
|
73
|
+
title: 'Attributes',
|
|
74
|
+
properties: ['attributes'],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
// DATA_PT_texture_space, :173.
|
|
78
|
+
title: 'Texture Space',
|
|
79
|
+
closed: true,
|
|
80
|
+
properties: ['texture_mesh', 'use_auto_texspace', 'texspace_location', 'texspace_size'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
// DATA_PT_remesh, :403.
|
|
84
|
+
title: 'Remesh',
|
|
85
|
+
closed: true,
|
|
86
|
+
properties: [
|
|
87
|
+
'remesh_mode',
|
|
88
|
+
'remesh_voxel_size',
|
|
89
|
+
'remesh_voxel_adaptivity',
|
|
90
|
+
'use_remesh_fix_poles',
|
|
91
|
+
'use_remesh_preserve_volume',
|
|
92
|
+
'use_remesh_preserve_attributes',
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
// DATA_PT_customdata, :435, titled "Geometry Data". Its body is FIVE
|
|
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.
|
|
102
|
+
title: 'Geometry Data',
|
|
103
|
+
closed: true,
|
|
104
|
+
properties: ['has_custom_normals', 'total_vert_sel', 'total_edge_sel', 'total_face_sel'],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
// DATA_PT_mesh_animation, :461 — and DATA_PT_armature_animation, the same
|
|
108
|
+
// panel on an armature.
|
|
109
|
+
title: 'Animation',
|
|
110
|
+
closed: true,
|
|
111
|
+
properties: ['animation_data'],
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// ---- `properties_data_armature.py`, for an ARMATURE's data. Same tab,
|
|
115
|
+
// same path, and each of these finds nothing on a mesh.
|
|
116
|
+
{
|
|
117
|
+
// DATA_PT_pose, :44 — Rest Position / Pose Position.
|
|
118
|
+
title: 'Pose',
|
|
119
|
+
properties: ['pose_position'],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
// DATA_PT_bone_collections, :101 — `template_list` over
|
|
123
|
+
// `arm.collections_all`. The door also hands this tab the armature's BONES
|
|
124
|
+
// as a path of its own, which the next panel lists.
|
|
125
|
+
title: 'Bone Collections',
|
|
126
|
+
closed: true,
|
|
127
|
+
properties: ['collections_all', 'collections'],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
// The armature's bones, which `buttons_context_path_data` puts on this
|
|
131
|
+
// tab's path list (`session.py::rna_context`) because Blender's Object
|
|
132
|
+
// Data tab is where an armature's skeleton is read. Blender itself draws
|
|
133
|
+
// them in the Outliner and the viewport rather than as a Properties list;
|
|
134
|
+
// this is the one panel here that is OURS, and it is named so.
|
|
135
|
+
title: 'Bones',
|
|
136
|
+
from: 'Bones',
|
|
137
|
+
collection: true,
|
|
138
|
+
active: 'activeBone',
|
|
139
|
+
properties: ['name', 'parent', 'use_connect', 'use_deform', 'head', 'tail', 'length'],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
// DATA_PT_display, :55 (armature).
|
|
143
|
+
title: 'Viewport Display',
|
|
144
|
+
closed: true,
|
|
145
|
+
properties: [
|
|
146
|
+
'display_type',
|
|
147
|
+
'show_names',
|
|
148
|
+
'show_bone_custom_shapes',
|
|
149
|
+
'show_bone_colors',
|
|
150
|
+
'show_axes',
|
|
151
|
+
'axes_position',
|
|
152
|
+
'relation_line_position',
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
// DATA_PT_iksolver_itasc, :192.
|
|
157
|
+
title: 'Inverse Kinematics',
|
|
158
|
+
closed: true,
|
|
159
|
+
properties: ['pose'],
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
const TAB = { id: 'data', curated: CURATED } as const;
|
|
164
|
+
|
|
165
|
+
export const point = 'selection.inspector';
|
|
166
|
+
export const title = 'Object Data';
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* THE DATA TAB'S GLYPH IS PER OBJECT TYPE, and it is the one tab in the rail
|
|
170
|
+
* that is. `buttons_context_items` gives it `ICON_NONE`
|
|
171
|
+
* (`makesrna/intern/rna_space.cc:595`) because `buttons_context_compute`
|
|
172
|
+
* fills it in at read time: `sbuts->dataicon = RNA_struct_ui_icon(ptr->type)`
|
|
173
|
+
* over the data on the context path, with Light special-cased to
|
|
174
|
+
* `ICON_OUTLINER_DATA_LIGHT` (`space_buttons/buttons_context.cc:795-810`).
|
|
175
|
+
* So a mesh's tab is the MESH_DATA triangle, an armature's the ARMATURE_DATA
|
|
176
|
+
* figure, a camera's the camera body.
|
|
177
|
+
*
|
|
178
|
+
* Each key below is the RNA STRUCT's own `bl_rna.identifier` — which is what
|
|
179
|
+
* `rna_context` reports as `active.dataType` — and each value is the glyph
|
|
180
|
+
* traced from the icon that struct's `RNA_def_struct_ui_icon` call names.
|
|
181
|
+
*/
|
|
182
|
+
const DATA_GLYPHS: Readonly<Record<string, string>> = {
|
|
183
|
+
Mesh: 'properties-data-mesh', // rna_mesh.cc:2916 ICON_MESH_DATA
|
|
184
|
+
Armature: 'properties-data-armature', // rna_armature.cc:2191 ICON_ARMATURE_DATA
|
|
185
|
+
Curve: 'properties-data-curve', // rna_curve.cc:1685 ICON_CURVE_DATA
|
|
186
|
+
SurfaceCurve: 'properties-data-surface', // ICON_SURFACE_DATA
|
|
187
|
+
MetaBall: 'properties-data-meta', // rna_meta.cc:331 ICON_META_DATA
|
|
188
|
+
TextCurve: 'properties-data-font', // ICON_FONT_DATA
|
|
189
|
+
Lattice: 'properties-data-lattice', // rna_lattice.cc:337 ICON_LATTICE_DATA
|
|
190
|
+
Camera: 'properties-data-camera', // rna_camera.cc:741 ICON_CAMERA_DATA
|
|
191
|
+
Light: 'properties-data-light', // special-cased above ICON_OUTLINER_DATA_LIGHT
|
|
192
|
+
Speaker: 'properties-data-speaker', // rna_speaker.cc:40 ICON_SPEAKER
|
|
193
|
+
PointCloud: 'properties-data-pointcloud', // rna_pointcloud.cc:174 ICON_POINTCLOUD_DATA
|
|
194
|
+
Volume: 'properties-data-volume', // rna_volume.cc:278 ICON_VOLUME_DATA
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The host reads either a NAME or a function of the subject
|
|
199
|
+
* (`tool-loader.ts`'s `LoadedToolContributionBase.icon`). The type comes from
|
|
200
|
+
* the context the door last answered for this subject — the same read every
|
|
201
|
+
* tab's `match()` drives — so no extra engine call happens to paint a glyph,
|
|
202
|
+
* and before the first answer lands the tab draws the mesh mark, which is what
|
|
203
|
+
* Blender shows for the starter cube.
|
|
204
|
+
*/
|
|
205
|
+
export const icon = (node: ToolContributionNode | null, adapter: unknown): string => {
|
|
206
|
+
const subject = resolveBlenderSubject(node, adapter);
|
|
207
|
+
if (subject === null || subject.kind !== 'object') return 'properties-data';
|
|
208
|
+
const state = blenderPropertiesState();
|
|
209
|
+
if (state.object !== subject.name) return 'properties-data';
|
|
210
|
+
const type = state.context?.active?.dataType;
|
|
211
|
+
// An object with no data at all draws Blender's EMPTY_DATA — though the tab
|
|
212
|
+
// itself does not stand for one (`buttons_context_path_data` fails), so this
|
|
213
|
+
// arm is what a type this table has no row for falls back to rather than a
|
|
214
|
+
// state the rail normally shows.
|
|
215
|
+
if (type === null || type === undefined) return 'properties-data-empty';
|
|
216
|
+
return DATA_GLYPHS[type] ?? 'properties-data';
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/** `ED_buttons_tabs_list` (`space_buttons.cc:218-252`) is the rail's order;
|
|
220
|
+
* these are that order, spaced so a tab can be inserted between two. */
|
|
221
|
+
export const order = 120;
|
|
222
|
+
/** THE RAIL'S GROUPS, from `ED_buttons_tabs_list`'s own `add_spacer()` calls
|
|
223
|
+
* (`space_buttons/space_buttons.cc:201-255`): the tool tab, then the scene
|
|
224
|
+
* group (Render, Output, View Layer, Scene, World), then Collection, then the
|
|
225
|
+
* object group (Object … Material), then Texture. The presentation draws a
|
|
226
|
+
* separator wherever this changes. */
|
|
227
|
+
export const railGroup = 'object';
|
|
228
|
+
export const match = blenderPropertiesTabMatch(TAB);
|
|
229
|
+
export default blenderPropertiesTabSection(TAB);
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
*/
|
|
32
|
+
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
33
|
+
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
34
|
+
|
|
35
|
+
/** `properties_material.py`, panel by panel. Line numbers are that file's. */
|
|
36
|
+
const CURATED: readonly BlenderCuratedPanel[] = [
|
|
37
|
+
{
|
|
38
|
+
// EEVEE_MATERIAL_PT_context_material, :76 — the slot list. `link` is the
|
|
39
|
+
// slot's own Object/Data toggle, `material` the pointer it holds.
|
|
40
|
+
title: 'Material Slots',
|
|
41
|
+
from: 'Material Slots',
|
|
42
|
+
collection: true,
|
|
43
|
+
active: 'activeMaterial',
|
|
44
|
+
properties: ['name', 'material', 'link', 'slot_index'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
// EEVEE_MATERIAL_PT_surface, :164 — see the header for why this is the
|
|
48
|
+
// material's own two node properties rather than the shader's inputs.
|
|
49
|
+
title: 'Surface',
|
|
50
|
+
from: 'Material',
|
|
51
|
+
properties: ['use_nodes', 'node_tree', 'surface_render_method', 'use_backface_culling'],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
// EEVEE_MATERIAL_PT_volume (:178) and _displacement (:201) / _thickness
|
|
55
|
+
// (:223), all three of them node-tree panels like Surface; what the
|
|
56
|
+
// material holds for them is its own.
|
|
57
|
+
title: 'Volume',
|
|
58
|
+
closed: true,
|
|
59
|
+
from: 'Material',
|
|
60
|
+
properties: ['volume_intersection_method', 'displacement_method', 'max_vertex_displacement'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
// EEVEE_MATERIAL_PT_settings (:297) and its two children (:322, :338).
|
|
64
|
+
title: 'Settings',
|
|
65
|
+
closed: true,
|
|
66
|
+
from: 'Material',
|
|
67
|
+
properties: [
|
|
68
|
+
'pass_index',
|
|
69
|
+
'use_backface_culling_shadow',
|
|
70
|
+
'use_backface_culling_lightprobe_volume',
|
|
71
|
+
'use_transparent_shadow',
|
|
72
|
+
'use_transparency_overlap',
|
|
73
|
+
'use_raytrace_refraction',
|
|
74
|
+
'thickness_mode',
|
|
75
|
+
'use_thickness_from_shadow',
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
// MATERIAL_PT_lineart, :377 — reads `mat.lineart`, a sub-struct, so its
|
|
80
|
+
// pointer row stands and the values are one drill down.
|
|
81
|
+
title: 'Line Art',
|
|
82
|
+
closed: true,
|
|
83
|
+
from: 'Material',
|
|
84
|
+
properties: ['lineart'],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
// MATERIAL_PT_viewport, :354 — the three values the solid viewport
|
|
88
|
+
// shades with, which is what our own three.js presenter reads.
|
|
89
|
+
title: 'Viewport Display',
|
|
90
|
+
from: 'Material',
|
|
91
|
+
properties: ['diffuse_color', 'metallic', 'roughness'],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
// MATERIAL_PT_animation, :390-ish (after Line Art in the `classes` tuple).
|
|
95
|
+
title: 'Animation',
|
|
96
|
+
closed: true,
|
|
97
|
+
from: 'Material',
|
|
98
|
+
properties: ['animation_data'],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const TAB = { id: 'material', curated: CURATED } as const;
|
|
103
|
+
|
|
104
|
+
export const point = 'selection.inspector';
|
|
105
|
+
export const title = 'Material';
|
|
106
|
+
/** `blender.icons.json` — Blender's own mark, TRACED from
|
|
107
|
+
* `release/datafiles/icons_svg/` at the engine's pin (the icon this tab draws
|
|
108
|
+
* is named in `buttons_context_items`, `makesrna/intern/rna_space.cc:579`),
|
|
109
|
+
* and tinted by the group `UI_icons.hh` declares it in. */
|
|
110
|
+
export const icon = 'properties-material';
|
|
111
|
+
/** `ED_buttons_tabs_list` (`space_buttons.cc:218-252`) is the rail's order;
|
|
112
|
+
* these are that order, spaced so a tab can be inserted between two. */
|
|
113
|
+
export const order = 160;
|
|
114
|
+
/** THE RAIL'S GROUPS, from `ED_buttons_tabs_list`'s own `add_spacer()` calls
|
|
115
|
+
* (`space_buttons/space_buttons.cc:201-255`): the tool tab, then the scene
|
|
116
|
+
* group (Render, Output, View Layer, Scene, World), then Collection, then the
|
|
117
|
+
* object group (Object … Material), then Texture. The presentation draws a
|
|
118
|
+
* separator wherever this changes. */
|
|
119
|
+
export const railGroup = 'object';
|
|
120
|
+
export const match = blenderPropertiesTabMatch(TAB);
|
|
121
|
+
export default blenderPropertiesTabSection(TAB);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BLENDER'S MODIFIERS TAB, as a Properties section (WORK.md §Blender in the
|
|
3
|
+
* tab is Blender, "Inspection parity", I2): the STACK, each modifier its own
|
|
4
|
+
* panel with its own settings, with the generic view beneath under "All
|
|
5
|
+
* properties".
|
|
6
|
+
*
|
|
7
|
+
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_data_modifier.py` at
|
|
8
|
+
* the engine's pin, and reading it is what decided the shape here. That file
|
|
9
|
+
* is barely a panel list at all: `DATA_PT_modifiers.draw` is `Add Modifier`
|
|
10
|
+
* plus `layout.template_modifiers()`, and the per-modifier panels are built in
|
|
11
|
+
* C from each type's own RNA (`MOD_*.cc`'s `panel_draw`). There is no Python
|
|
12
|
+
* table of sixty modifier types to transcribe — and transcribing one would be
|
|
13
|
+
* a COPY of Blender's layout, which the ruling forbids, and would go stale the
|
|
14
|
+
* first time a modifier gained a property.
|
|
15
|
+
*
|
|
16
|
+
* So the stack is derived instead: each member of `ob.modifiers` is a panel
|
|
17
|
+
* headed by the four toggles every modifier has (`ModifierData`'s own
|
|
18
|
+
* `show_viewport` / `show_render` / `show_in_editmode` / `show_on_cage`,
|
|
19
|
+
* which is the header row `template_modifiers` draws), followed by every
|
|
20
|
+
* property THIS MODIFIER'S OWN struct declares — `andDeclared`, which reads
|
|
21
|
+
* `BlenderRnaRow.group`, RNA's own answer to "which struct declares this".
|
|
22
|
+
* For a Subdivision modifier that is exactly `SubsurfModifier`'s levels,
|
|
23
|
+
* render_levels, quality, uv_smooth and the rest, in RNA's order.
|
|
24
|
+
*
|
|
25
|
+
* The ACTIVE modifier (`buttons_context_path_modifier`, which the door reports
|
|
26
|
+
* as `activeModifier`) is marked, the way Blender outlines the active panel.
|
|
27
|
+
*
|
|
28
|
+
* It stands when: the object's type is in `buttons_context.cc`'s modifier set.
|
|
29
|
+
*/
|
|
30
|
+
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
31
|
+
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
32
|
+
|
|
33
|
+
const CURATED: readonly BlenderCuratedPanel[] = [
|
|
34
|
+
{
|
|
35
|
+
title: 'Modifiers',
|
|
36
|
+
from: 'Modifiers',
|
|
37
|
+
collection: true,
|
|
38
|
+
active: 'activeModifier',
|
|
39
|
+
// `ModifierData`'s own, in the order `template_modifiers` heads a panel
|
|
40
|
+
// with them; everything after these is the type's own (see the header).
|
|
41
|
+
properties: [
|
|
42
|
+
'name',
|
|
43
|
+
'type',
|
|
44
|
+
'show_viewport',
|
|
45
|
+
'show_render',
|
|
46
|
+
'show_in_editmode',
|
|
47
|
+
'show_on_cage',
|
|
48
|
+
'use_pin_to_last',
|
|
49
|
+
'is_active',
|
|
50
|
+
],
|
|
51
|
+
andDeclared: true,
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const TAB = { id: 'modifier', curated: CURATED } as const;
|
|
56
|
+
|
|
57
|
+
export const point = 'selection.inspector';
|
|
58
|
+
export const title = 'Modifiers';
|
|
59
|
+
/** `blender.icons.json` — Blender's own mark, TRACED from
|
|
60
|
+
* `release/datafiles/icons_svg/` at the engine's pin (the icon this tab draws
|
|
61
|
+
* is named in `buttons_context_items`, `makesrna/intern/rna_space.cc:579`),
|
|
62
|
+
* and tinted by the group `UI_icons.hh` declares it in. */
|
|
63
|
+
export const icon = 'properties-modifiers';
|
|
64
|
+
/** `ED_buttons_tabs_list` (`space_buttons.cc:218-252`) is the rail's order;
|
|
65
|
+
* these are that order, spaced so a tab can be inserted between two. */
|
|
66
|
+
export const order = 80;
|
|
67
|
+
/** THE RAIL'S GROUPS, from `ED_buttons_tabs_list`'s own `add_spacer()` calls
|
|
68
|
+
* (`space_buttons/space_buttons.cc:201-255`): the tool tab, then the scene
|
|
69
|
+
* group (Render, Output, View Layer, Scene, World), then Collection, then the
|
|
70
|
+
* object group (Object … Material), then Texture. The presentation draws a
|
|
71
|
+
* separator wherever this changes. */
|
|
72
|
+
export const railGroup = 'object';
|
|
73
|
+
export const match = blenderPropertiesTabMatch(TAB);
|
|
74
|
+
export default blenderPropertiesTabSection(TAB);
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BLENDER'S OBJECT TAB, as a Properties section (WORK.md §Blender in the tab
|
|
3
|
+
* is Blender, "Inspection parity", I2). The body is Blender's own panels, in
|
|
4
|
+
* Blender's own order, over the RNA door; the generic view of every property
|
|
5
|
+
* the object has stays beneath them under "All properties".
|
|
6
|
+
*
|
|
7
|
+
* THE SPECIFICATION IS `scripts/startup/bl_ui/properties_object.py`, read at
|
|
8
|
+
* the engine's pin. Its `classes` tuple at the foot of the file is the panel
|
|
9
|
+
* ORDER, each class's `bl_label` the title, each `layout.prop(ob, "x")` an
|
|
10
|
+
* entry below in the order the draw function lists it, and `bl_options =
|
|
11
|
+
* {'DEFAULT_CLOSED'}` a `closed: true`. Blender's UI layer is never run,
|
|
12
|
+
* ported or recorded — the Python is read as a statement of WHICH properties
|
|
13
|
+
* this tab shows, and our widgets draw them from the door's rows.
|
|
14
|
+
*
|
|
15
|
+
* WHAT THE TRANSCRIPTION DELIBERATELY DROPS:
|
|
16
|
+
* - the `poll`/`if` branches. `show_all_edges` is mesh-only in Python; here
|
|
17
|
+
* it is simply not on a Camera's RNA, so it does not draw. Same outcome,
|
|
18
|
+
* from the datablock rather than from a copy of Blender's conditions.
|
|
19
|
+
* - `OBJECT_PT_context_object` (`bl_label = ""`) — the breadcrumb row, which
|
|
20
|
+
* is the host's identity row here.
|
|
21
|
+
* - the four panels whose datablock is NOT the object: Motion Paths reads
|
|
22
|
+
* `ob.animation_visualization` and `ob.motion_path`, Light/Shadow Linking
|
|
23
|
+
* their collections. The context door names the object, so those are
|
|
24
|
+
* reached by drilling their POINTER row in the generic view beneath — a
|
|
25
|
+
* curated panel addresses one of the TAB's own datablocks, and inventing a
|
|
26
|
+
* second addressing scheme to reach a sub-struct is the kind of machinery
|
|
27
|
+
* the generic view exists to make unnecessary.
|
|
28
|
+
*
|
|
29
|
+
* It stands when: `buttons_context_path_object` — there is an active object.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
|
|
33
|
+
import type { BlenderCuratedPanel } from './blender-properties-view';
|
|
34
|
+
|
|
35
|
+
/** `properties_object.py`, panel by panel. Line numbers are that file's. */
|
|
36
|
+
const CURATED: readonly BlenderCuratedPanel[] = [
|
|
37
|
+
{
|
|
38
|
+
// OBJECT_PT_transform, :36. Blender draws the rotation channel that
|
|
39
|
+
// `rotation_mode` selects; all three are listed, and the two the object is
|
|
40
|
+
// not in are still real RNA, so all three draw. That is MORE than Blender
|
|
41
|
+
// shows and it is the honest reading: the door reports what the object
|
|
42
|
+
// holds, and a quaternion object's euler is not a fiction.
|
|
43
|
+
title: 'Transform',
|
|
44
|
+
properties: [
|
|
45
|
+
'location',
|
|
46
|
+
'lock_location',
|
|
47
|
+
'rotation_quaternion',
|
|
48
|
+
'rotation_axis_angle',
|
|
49
|
+
'rotation_euler',
|
|
50
|
+
'lock_rotation_w',
|
|
51
|
+
'lock_rotation',
|
|
52
|
+
'rotation_mode',
|
|
53
|
+
'scale',
|
|
54
|
+
'lock_scale',
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
// OBJECT_PT_delta_transform, :86.
|
|
59
|
+
title: 'Delta Transform',
|
|
60
|
+
closed: true,
|
|
61
|
+
properties: [
|
|
62
|
+
'delta_location',
|
|
63
|
+
'delta_rotation_quaternion',
|
|
64
|
+
'delta_rotation_euler',
|
|
65
|
+
'delta_scale',
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
// OBJECT_PT_relations, :132.
|
|
70
|
+
title: 'Relations',
|
|
71
|
+
closed: true,
|
|
72
|
+
properties: [
|
|
73
|
+
'parent',
|
|
74
|
+
'parent_type',
|
|
75
|
+
'parent_bone',
|
|
76
|
+
'parent_bone_head_tail_factor',
|
|
77
|
+
'parent_vertices',
|
|
78
|
+
'use_parent_final_indices',
|
|
79
|
+
'use_camera_lock_parent',
|
|
80
|
+
'track_axis',
|
|
81
|
+
'up_axis',
|
|
82
|
+
'pass_index',
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
// OBJECT_PT_collections, :185 — a UIList of the collections this object is
|
|
87
|
+
// in. `users_collection` is the RNA behind it.
|
|
88
|
+
title: 'Collections',
|
|
89
|
+
closed: true,
|
|
90
|
+
properties: ['users_collection'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
// OBJECT_PT_instancing, :284, with OBJECT_PT_instancing_size (:317) as its
|
|
94
|
+
// `bl_parent_id` child.
|
|
95
|
+
title: 'Instancing',
|
|
96
|
+
closed: true,
|
|
97
|
+
properties: [
|
|
98
|
+
'instance_type',
|
|
99
|
+
'use_instance_vertices_rotation',
|
|
100
|
+
'instance_collection',
|
|
101
|
+
'show_instancer_for_viewport',
|
|
102
|
+
'show_instancer_for_render',
|
|
103
|
+
],
|
|
104
|
+
sub: [
|
|
105
|
+
{
|
|
106
|
+
title: 'Scale by Face Size',
|
|
107
|
+
closed: true,
|
|
108
|
+
properties: ['use_instance_faces_scale', 'instance_faces_scale'],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
// OBJECT_PT_display, :218.
|
|
114
|
+
title: 'Viewport Display',
|
|
115
|
+
closed: true,
|
|
116
|
+
properties: [
|
|
117
|
+
'show_name',
|
|
118
|
+
'show_axis',
|
|
119
|
+
'show_wire',
|
|
120
|
+
'show_all_edges',
|
|
121
|
+
'show_texture_space',
|
|
122
|
+
'show_in_front',
|
|
123
|
+
'display_type',
|
|
124
|
+
'color',
|
|
125
|
+
'show_bounds',
|
|
126
|
+
'display_bounds_type',
|
|
127
|
+
'display',
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
// OBJECT_PT_shadow_terminator, :602.
|
|
132
|
+
title: 'Shadow Terminator',
|
|
133
|
+
closed: true,
|
|
134
|
+
properties: [
|
|
135
|
+
'shadow_terminator_normal_offset',
|
|
136
|
+
'shadow_terminator_geometry_offset',
|
|
137
|
+
'shadow_terminator_shading_offset',
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
// OBJECT_PT_visibility, :412. Blender draws four of these INVERTED
|
|
142
|
+
// (`invert_checkbox=True`, so "Selectable" is `not hide_select`); the
|
|
143
|
+
// checkbox here is the RNA's own value and its label is the RNA's own
|
|
144
|
+
// name, because inverting a value in the presentation is exactly the
|
|
145
|
+
// fabrication the anti-shim rule forbids.
|
|
146
|
+
title: 'Visibility',
|
|
147
|
+
closed: true,
|
|
148
|
+
properties: [
|
|
149
|
+
'hide_select',
|
|
150
|
+
'hide_surface_pick',
|
|
151
|
+
'hide_viewport',
|
|
152
|
+
'hide_render',
|
|
153
|
+
'visible_camera',
|
|
154
|
+
'visible_shadow',
|
|
155
|
+
'visible_raycast',
|
|
156
|
+
'visible_diffuse',
|
|
157
|
+
'visible_glossy',
|
|
158
|
+
'visible_transmission',
|
|
159
|
+
'visible_volume_scatter',
|
|
160
|
+
'hide_probe_volume',
|
|
161
|
+
'hide_probe_sphere',
|
|
162
|
+
'hide_probe_plane',
|
|
163
|
+
'use_grease_pencil_lights',
|
|
164
|
+
'is_holdout',
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
// OBJECT_PT_lineart, :340 — the panel reads `ob.lineart`, a sub-struct, so
|
|
169
|
+
// what stands here is its POINTER row; the values are one drill down.
|
|
170
|
+
title: 'Line Art',
|
|
171
|
+
closed: true,
|
|
172
|
+
properties: ['lineart'],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
// OBJECT_PT_animation (:631) and OBJECT_PT_custom_props (:635).
|
|
176
|
+
title: 'Animation',
|
|
177
|
+
closed: true,
|
|
178
|
+
properties: ['animation_data'],
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const TAB = { id: 'object', standing: 'object', curated: CURATED } as const;
|
|
183
|
+
|
|
184
|
+
export const point = 'selection.inspector';
|
|
185
|
+
export const title = 'Object';
|
|
186
|
+
/** `blender.icons.json` — Blender's own mark, TRACED from
|
|
187
|
+
* `release/datafiles/icons_svg/` at the engine's pin (the icon this tab draws
|
|
188
|
+
* is named in `buttons_context_items`, `makesrna/intern/rna_space.cc:579`),
|
|
189
|
+
* and tinted by the group `UI_icons.hh` declares it in. */
|
|
190
|
+
export const icon = 'properties-object';
|
|
191
|
+
/** `ED_buttons_tabs_list` (`space_buttons.cc:218-252`) is the rail's order;
|
|
192
|
+
* these are that order, spaced so a tab can be inserted between two. */
|
|
193
|
+
export const order = 70;
|
|
194
|
+
/** THE RAIL'S GROUPS, from `ED_buttons_tabs_list`'s own `add_spacer()` calls
|
|
195
|
+
* (`space_buttons/space_buttons.cc:201-255`): the tool tab, then the scene
|
|
196
|
+
* group (Render, Output, View Layer, Scene, World), then Collection, then the
|
|
197
|
+
* object group (Object … Material), then Texture. The presentation draws a
|
|
198
|
+
* separator wherever this changes. */
|
|
199
|
+
export const railGroup = 'object';
|
|
200
|
+
/** THE TAB THE RAIL OPENS ON. Blender's Properties editor stores its context
|
|
201
|
+
* per screen in the startup file, and at factory settings the Layout
|
|
202
|
+
* workspace's is OBJECT:
|
|
203
|
+
*
|
|
204
|
+
* bpy.data.screens['Layout'].areas[PROPERTIES].spaces[0].context -> 'OBJECT'
|
|
205
|
+
*
|
|
206
|
+
* (measured against Blender 5.2.0 LTS at `--factory-startup`, walk 5 parity
|
|
207
|
+
* row 3; the other screens read OBJECT, MODIFIER, WORLD, DATA, TOOL, OUTPUT
|
|
208
|
+
* and RENDER, so a per-workspace default is the next fidelity step and this
|
|
209
|
+
* one declaration is the Layout/Model answer). Without it the rail opened on
|
|
210
|
+
* Render — the first tab in `ED_buttons_tabs_list`'s order — and a person
|
|
211
|
+
* selecting the cube got Sampling and Light Paths where Blender shows
|
|
212
|
+
* Transform. */
|
|
213
|
+
export const railDefault = true;
|
|
214
|
+
export const match = blenderPropertiesTabMatch(TAB);
|
|
215
|
+
export default blenderPropertiesTabSection(TAB);
|