Skip to content

Commit 5c486b4

Browse files
committed
Add new operators and documentation for HUD and selection features
- Introduced `iops.object_select_similar_name` operator to select objects with similar names, ignoring numeric suffixes. - Added `iops.open_asset_in_new_blender` operator to open a `.blend` file in a new Blender process, preserving the current session. - Created `iops.ui_help_toggle` and `iops.ui_hud_params_toggle` marker operators for customizable keymap bindings in modal operators. - Implemented a comprehensive HUD system in `ui_hud.md`, detailing overlays for dynamic information, help, and statistics. - Added CSS styles in `extra.css` for consistent theming across documentation, reflecting Blender's UI aesthetics.
1 parent ec8cba0 commit 5c486b4

93 files changed

Lines changed: 5474 additions & 1010 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,46 @@
11
# Align Between Two
22

3-
Align active object between two selected objects.
3+
Duplicates the active object and distributes the copies along the straight line connecting two reference objects (or between three selected objects, pairwise). Optionally orients each duplicate so a chosen local axis tracks along the line. Useful for evenly spacing instances between two anchors without having to compute positions by hand.
44

5-
**Usage:**
6-
- Select two reference objects
7-
- Select target object (active)
8-
- Choose track axis and alignment method
9-
- Object positions between references
5+
<div class="iops-meta" markdown="1">
6+
<span class="key">bl_idname: iops.object_align_between_two</span>
7+
<span class="mode">Mode: Object</span>
8+
<span>Context: VIEW_3D</span>
9+
<span class="modal">Modal: no</span>
10+
<span class="hud">HUD: no</span>
11+
</div>
1012

11-
Useful for spacing objects evenly or creating alignments.
13+
## Overview
14+
The operator reads the current selection and the active object, then builds a sequence of interpolated positions between anchor locations. With two objects selected, copies are placed between the active object and the other selected object. With three objects selected, copies are placed in two segments along the chain `objects[0] -> objects[1] -> objects[2]` (pairwise interpolation between consecutive items in the selected-but-not-active list).
15+
16+
All duplicates are linked into a fresh collection named `Objects Between` at the scene root. Each duplicate is a shallow copy of the active object with its own `data` block (`active.data.copy()`), so meshes are not linked.
17+
18+
## Usage
19+
- Object Mode. Select either 2 or 3 objects; the active object is treated as one of the anchors when only 2 are selected.
20+
- No default keymap binding. Invoke via F3 search ("Align Between Two") or through the menus where the operator is wired.
21+
- Adjust `Count`, `Align`, `Track`, `Up`, and `Select Duplicated` in the redo (F6 / Adjust Last Operation) panel.
22+
- If `Track` equals `Up`, the operator reports `SAME AXIS` and does nothing (no duplicates are created).
23+
- If selection count is not 2 or 3, a message box `Must be 2 or 3 Objects Selected.` is shown and the operator aborts before creating the collection.
24+
25+
## Properties
26+
27+
| Name | Type | Default | Description |
28+
| --- | --- | --- | --- |
29+
| `track_axis` | Enum: `X`, `Y`, `Z`, `-X`, `-Y`, `-Z` | `Y` | Local axis of each duplicate that tracks along the line between the anchors. Only used when `Align` is on. |
30+
| `up_axis` | Enum: `X`, `Y`, `Z` | `Z` | Up axis passed to `Vector.to_track_quat`. Must differ from `track_axis`. |
31+
| `align` | Bool | `False` | When enabled, rotate each duplicate so `track_axis` points along the anchor-to-anchor vector, using `up_axis` as the up reference. Rotation is written via quaternion and then the rotation mode is switched back to `XYZ`. |
32+
| `count` | Int | `1` (soft range `0..100000000`) | Number of duplicates created per segment. Positions are evenly spaced at `p = i / (count + 1)` for `i` in `1..count`, so endpoints are never occupied. |
33+
| `select_duplicated` | Bool | `True` | When enabled, deselect the original active and anchor objects and select all newly created duplicates, making the last duplicate active. When disabled, the original selection is preserved. |
34+
35+
## Notes
36+
- A new collection `Objects Between` is created on every run and linked into the scene's root collection. Repeated runs produce `Objects Between`, `Objects Between.001`, etc.
37+
- Each duplicate copies the active object's `data`, so this is not memory-cheap for heavy meshes. There is no instancing option.
38+
- The interpolation uses `p = 1 / (count + 1) * (i + 1)`, which excludes both endpoints. With `count = 1` you get a single midpoint duplicate per segment.
39+
- With 3 selected objects, the direction used for `track_axis` alignment in every duplicate is `posA - posB` (the first segment's direction); the second segment uses the same axis vector, not its own.
40+
- The operator's `draw` method references a `mode` property that is not defined on the class, which will raise an error if Blender invokes the custom redo panel draw. The Adjust Last Operation panel may therefore fall back or error; properties remain editable via the operator's auto-generated UI in practice.
41+
- `REGISTER` and `UNDO` are set, so the operator participates in normal undo.
42+
43+
## Related
44+
- Align Origin To Selection
45+
- Align Origin To Bottom
46+
- [Object Aligner](op_object_aligner.md)

‎docs/operators/op_align_origin_to_face_normal.md‎

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
# Align Origin to Normal
22

3-
Align object origin to face normal direction.
3+
Aligns the active mesh object's origin and rotation to the active face: the origin is moved to the face centroid and the object is rotated so its local Z axis matches the face normal and its local X axis follows the face's longest-edge tangent. Useful when you need an object's transform to match a surface so subsequent moves, scales, and child placements respect that surface.
44

5-
**Features:**
6-
- Select face to align to
7-
- Object rotates to match normal
8-
- Origin position options
9-
- Maintains scale and other properties
5+
<div class="iops-meta" markdown="1">
6+
<span class="key">bl_idname: iops.mesh_align_origin_to_normal</span>
7+
<span class="mode">Mode: Edit Mesh</span>
8+
<span>Context: VIEW_3D</span>
9+
<span class="modal">Modal: no</span>
10+
<span class="hud">HUD: no</span>
11+
</div>
1012

11-
Perfect for aligning objects to surface angles.
13+
## Overview
14+
15+
Blender's "Set Origin" lets you place the origin at the 3D cursor or at the selected geometry, but it does not reorient the object to that geometry's normal. This operator combines both steps: it snaps the cursor to the current selection, sets the origin there, then rebuilds the object's world matrix from the active face's normal and longest-edge tangent so the resulting local axes are predictable (Z = normal, X follows the longest edge).
16+
17+
Before computing the new matrix, the operator applies any pending rotation so it can be invoked repeatedly without compounding error. Location and scale are preserved across the realignment.
18+
19+
## Usage
20+
21+
- Enter Edit Mode on a mesh object.
22+
- Make a face the active element (the active face is what `bm.faces.active` returns; click a face last to make it active).
23+
- Run the operator.
24+
25+
Default keymap: <kbd>Alt</kbd>+<kbd>F5</kbd> in Edit Mesh (from `prefs/hotkeys_default.py`).
26+
27+
The poll requires VIEW_3D, EDIT_MESH mode, at least one selected object, and an active object of type MESH.
28+
29+
## Notes
30+
31+
- The new local axes are built as `Z = face.normal`, `X = -face.calc_tangent_edge()` (longest-edge tangent, negated), `Y = (normal x tangent) * -1`.
32+
- The operator calls `view3d.snap_cursor_to_selected` and `object.origin_set(type="ORIGIN_CURSOR")`, so the 3D cursor is moved as a side effect.
33+
- It also calls `object.transform_apply(rotation=True)` twice (once at the start of `execute`, once inside the alignment routine) so repeated invocations produce stable results; this bakes any prior rotation into the mesh data.
34+
- If there is no active face the call to `face.calc_tangent_edge()` will fail - select a face explicitly before running.
35+
- Registered as a single `REGISTER`/`UNDO` operator; no panel, menu, or PropertyGroup is registered alongside it.
36+
37+
## Related
38+
39+
- Align View to Active
40+
- [Object Aligner](op_object_aligner.md)
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Asset Management
2+
3+
A bundle of operators that streamline the Blender Asset Browser workflow from the 3D Viewport: marking and clearing assets, navigating catalogs through cascading menus, creating and deleting catalogs, searching by name, switching libraries, and exposing the whole set through a pie menu. All catalog-mutating operators write directly to the active library's `blender_assets.cats.txt` and refresh open Asset Browsers.
4+
5+
## Overview
6+
7+
This module replaces the click-heavy round-trip between the 3D Viewport and the Asset Browser. Selection is resolved into mark-able / move-able data-blocks (objects, parent collections, active material, active image) by `utils.assets.resolve_assets_from_selection`. Catalog targets are picked from a dynamically built tree of cascading menus or via a search popup. Library switching is done in-pie without leaving the viewport.
8+
9+
A pool of 32 pre-registered generic `Menu` classes (`IOPS_MT_CatPool_0` ... `IOPS_MT_CatPool_31`) is used to build the nested catalog hierarchy because `layout.menu()` requires a fixed `bl_idname`. Each node carries an `_action` field ("move" or "delete") that decides which operator the menu draws.
10+
11+
## Operators
12+
13+
### Move Asset to Catalog (bl_idname: iops.asset_move_to_catalog)
14+
15+
<div class="iops-meta" markdown="1">
16+
<span class="key">bl_idname: iops.asset_move_to_catalog</span>
17+
<span class="mode">Mode: Object</span>
18+
<span>Context: VIEW_3D / Asset Browser</span>
19+
<span class="modal">Modal: no</span>
20+
<span class="hud">HUD: no</span>
21+
</div>
22+
23+
Assigns the catalog UUID stored on the operator to every asset resolved from the current selection (VIEW_3D) or to `context.asset.local_id` when invoked from the Asset Browser. Only local assets can be reassigned from the browser context.
24+
25+
#### Properties
26+
27+
| Name | Type | Default | Description |
28+
| --- | --- | --- | --- |
29+
| catalog_uuid | String | "" | Target catalog UUID. |
30+
| catalog_name | String | "" | Display name used in the report. |
31+
32+
### Mark as Asset (bl_idname: iops.asset_mark)
33+
34+
<div class="iops-meta" markdown="1">
35+
<span class="key">bl_idname: iops.asset_mark</span>
36+
<span class="mode">Mode: Object</span>
37+
<span>Context: VIEW_3D</span>
38+
<span class="modal">Modal: no</span>
39+
<span class="hud">HUD: no</span>
40+
</div>
41+
42+
Marks selected data-blocks as assets. The `mark_type` enum picks what to mark: selected objects, their non-scene parent collections, the active material on the active object, or the active image (resolved via `get_active_image`). Already-marked data-blocks are silently skipped.
43+
44+
#### Properties
45+
46+
| Name | Type | Default | Description |
47+
| --- | --- | --- | --- |
48+
| mark_type | Enum | `OBJECT` | `OBJECT` (selected objects), `COLLECTION` (parent collections of selected objects, scene root excluded), `MATERIAL` (active material on active object), `IMAGE` (active image). |
49+
50+
### Clear Asset (bl_idname: iops.asset_clear)
51+
52+
<div class="iops-meta" markdown="1">
53+
<span class="key">bl_idname: iops.asset_clear</span>
54+
<span class="mode">Mode: Object</span>
55+
<span>Context: VIEW_3D</span>
56+
<span class="modal">Modal: no</span>
57+
<span class="hud">HUD: no</span>
58+
</div>
59+
60+
Calls `asset_clear()` on every asset resolved from the current selection (objects, collections, materials).
61+
62+
### New Asset Catalog (bl_idname: iops.asset_create_catalog)
63+
64+
<div class="iops-meta" markdown="1">
65+
<span class="key">bl_idname: iops.asset_create_catalog</span>
66+
<span class="mode">Mode: any</span>
67+
<span>Context: VIEW_3D / Asset Browser</span>
68+
<span class="modal">Modal: no</span>
69+
<span class="hud">HUD: no</span>
70+
</div>
71+
72+
Creates a new catalog inside the catalog file passed in `catalog_file`. If empty, falls back to the current `.blend` file's catalog info via `get_current_file_catalog_info()`; if the file is unsaved the operator cancels with a warning. Opens a props dialog asking for `catalog_name`.
73+
74+
#### Properties
75+
76+
| Name | Type | Default | Description |
77+
| --- | --- | --- | --- |
78+
| catalog_name | String | "New Catalog" | Path of the new catalog. Use `/` for nesting (e.g. `Props/Furniture`). |
79+
| catalog_file | String | "" | Path of the target `blender_assets.cats.txt`. Empty = autodetect from saved file. |
80+
81+
### Delete Asset Catalog (bl_idname: iops.asset_delete_catalog)
82+
83+
<div class="iops-meta" markdown="1">
84+
<span class="key">bl_idname: iops.asset_delete_catalog</span>
85+
<span class="mode">Mode: any</span>
86+
<span>Context: VIEW_3D / Asset Browser</span>
87+
<span class="modal">Modal: no</span>
88+
<span class="hud">HUD: no</span>
89+
</div>
90+
91+
Removes a single catalog from a catalog file. Invokes a confirmation popup before executing.
92+
93+
#### Properties
94+
95+
| Name | Type | Default | Description |
96+
| --- | --- | --- | --- |
97+
| catalog_uuid | String | "" | UUID of the catalog to delete. |
98+
| catalog_file | String | "" | Catalog file path. |
99+
100+
### Delete Empty Catalogs (bl_idname: iops.asset_delete_empty_catalogs)
101+
102+
<div class="iops-meta" markdown="1">
103+
<span class="key">bl_idname: iops.asset_delete_empty_catalogs</span>
104+
<span class="mode">Mode: any</span>
105+
<span>Context: VIEW_3D / Asset Browser</span>
106+
<span class="modal">Modal: no</span>
107+
<span class="hud">HUD: no</span>
108+
</div>
109+
110+
Walks every asset data-block (`iter_all_asset_datablocks`), collects used catalog UUIDs, and deletes every catalog in `catalog_file` whose UUID is not in that set. Confirmation popup before running.
111+
112+
#### Properties
113+
114+
| Name | Type | Default | Description |
115+
| --- | --- | --- | --- |
116+
| catalog_file | String | "" | Catalog file path. Required (cancels if empty). |
117+
118+
### Search Catalog (Move) (bl_idname: iops.asset_search_move_to_catalog)
119+
120+
<div class="iops-meta" markdown="1">
121+
<span class="key">bl_idname: iops.asset_search_move_to_catalog</span>
122+
<span class="mode">Mode: Object</span>
123+
<span>Context: VIEW_3D / Asset Browser</span>
124+
<span class="modal">Modal: no</span>
125+
<span class="hud">HUD: no</span>
126+
</div>
127+
128+
Opens an `invoke_search_popup` listing every catalog in the active library (`wm.IOPS_AddonProperties.iops_active_asset_library`). Catalog items show a shortened name (last two path segments) with the full path in the tooltip. Picking a catalog assigns it to the resolved selection.
129+
130+
#### Properties
131+
132+
| Name | Type | Default | Description |
133+
| --- | --- | --- | --- |
134+
| catalog_choice | Enum (dynamic) | first item | Catalogs from the active library, or `NONE` if the library has none. |
135+
136+
### Search Catalog (Delete) (bl_idname: iops.asset_search_delete_catalog)
137+
138+
<div class="iops-meta" markdown="1">
139+
<span class="key">bl_idname: iops.asset_search_delete_catalog</span>
140+
<span class="mode">Mode: any</span>
141+
<span>Context: VIEW_3D / Asset Browser</span>
142+
<span class="modal">Modal: no</span>
143+
<span class="hud">HUD: no</span>
144+
</div>
145+
146+
Same search popup as above, but deletes the chosen catalog from the active library's catalog file.
147+
148+
#### Properties
149+
150+
| Name | Type | Default | Description |
151+
| --- | --- | --- | --- |
152+
| catalog_choice | Enum (dynamic) | first item | Catalog list from the active library. |
153+
154+
### Set Asset Library (bl_idname: iops.set_asset_library)
155+
156+
<div class="iops-meta" markdown="1">
157+
<span class="key">bl_idname: iops.set_asset_library</span>
158+
<span class="mode">Mode: any</span>
159+
<span>Context: VIEW_3D</span>
160+
<span class="modal">Modal: no</span>
161+
<span class="hud">HUD: no</span>
162+
</div>
163+
164+
Stores `library_path` into `wm.IOPS_AddonProperties.iops_active_asset_library` and re-opens the assets pie (`IOPS_MT_Pie_Assets`).
165+
166+
#### Properties
167+
168+
| Name | Type | Default | Description |
169+
| --- | --- | --- | --- |
170+
| library_path | String | "" | Filesystem path of the library to activate. |
171+
172+
### Select in Asset Browser (bl_idname: iops.select_in_asset_browser)
173+
174+
<div class="iops-meta" markdown="1">
175+
<span class="key">bl_idname: iops.select_in_asset_browser</span>
176+
<span class="mode">Mode: Object</span>
177+
<span>Context: VIEW_3D</span>
178+
<span class="modal">Modal: no</span>
179+
<span class="hud">HUD: no</span>
180+
</div>
181+
182+
Finds an open Asset Browser (`find_asset_browser_space`) and sets its `params.filter_search` to the name of the first asset resolved from the selection. Reports every matching asset and its kind. No-op (warning) if no Asset Browser is open or its `params` are unavailable.
183+
184+
### Clear Asset Browser Filter (bl_idname: iops.clear_asset_browser_filter)
185+
186+
<div class="iops-meta" markdown="1">
187+
<span class="key">bl_idname: iops.clear_asset_browser_filter</span>
188+
<span class="mode">Mode: any</span>
189+
<span>Context: VIEW_3D / Asset Browser</span>
190+
<span class="modal">Modal: no</span>
191+
<span class="hud">HUD: no</span>
192+
</div>
193+
194+
Empties `params.filter_search` on the first found Asset Browser and tags it for redraw.
195+
196+
### Refresh Asset Browser (bl_idname: iops.refresh_asset_browser)
197+
198+
<div class="iops-meta" markdown="1">
199+
<span class="key">bl_idname: iops.refresh_asset_browser</span>
200+
<span class="mode">Mode: any</span>
201+
<span>Context: VIEW_3D / Asset Browser</span>
202+
<span class="modal">Modal: no</span>
203+
<span class="hud">HUD: no</span>
204+
</div>
205+
206+
Calls `refresh_asset_browser()` to force every open Asset Browser to re-read its library. Use after external catalog edits.
207+
208+
### Expand Collection to Scene (bl_idname: iops.expand_instance_collection)
209+
210+
<div class="iops-meta" markdown="1">
211+
<span class="key">bl_idname: iops.expand_instance_collection</span>
212+
<span class="mode">Mode: Object</span>
213+
<span>Context: VIEW_3D</span>
214+
<span class="modal">Modal: no</span>
215+
<span class="hud">HUD: no</span>
216+
</div>
217+
218+
Active object must be an `EMPTY` with `instance_type == "COLLECTION"` and a non-null `instance_collection`. Wraps `bpy.ops.object.duplicates_make_real(use_base_parent=True, use_hierarchy=True)` to turn the collection instance into real hierarchy parented to the instancer empty.
219+
220+
### IOPS Asset Management Pie (bl_idname: iops.call_pie_assets)
221+
222+
<div class="iops-meta" markdown="1">
223+
<span class="key">bl_idname: iops.call_pie_assets</span>
224+
<span class="mode">Mode: any</span>
225+
<span>Context: VIEW_3D</span>
226+
<span class="modal">Modal: no</span>
227+
<span class="hud">HUD: no</span>
228+
</div>
229+
230+
Wraps `wm.call_menu_pie(name="IOPS_MT_Pie_Assets")`. Default keymap: <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>A</kbd>.
231+
232+
## Usage
233+
234+
- The pie (`iops.call_pie_assets`) is the canonical entry point — every other operator here is reachable through it.
235+
- Default keymap binding: <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>A</kbd> in 3D View. All other operators in this module have no default keymap binding and are invoked via the pie, the Asset Browser context menu, or operator search.
236+
- For mark/move/clear, selection must resolve at least one valid asset; the pie's library switching depends on `IOPS_AddonProperties.iops_active_asset_library` being set.
237+
- Creating catalogs requires the current `.blend` to be saved unless `catalog_file` is supplied explicitly.
238+
239+
## Notes
240+
241+
- 32 pool `Menu` classes (`IOPS_MT_CatPool_0` ... `IOPS_MT_CatPool_31`) are registered alongside the operators via `register_pool_menus()`. Catalog trees deeper than 32 inner nodes will draw orphan leaves (no submenu) rather than crash.
242+
- Search popups (`iops.asset_search_move_to_catalog`, `iops.asset_search_delete_catalog`) cache the enum items in a module-level list (`_catalog_search_items_cache`) so Blender keeps strong references during the popup's lifetime.
243+
- The pie operator (`iops.set_asset_library`) re-opens the same pie after switching libraries, so library switches feel instantaneous.
244+
- `iops.asset_clear` does not unmark images (only objects, collections, materials) because `resolve_assets_from_selection` does not return images.
245+
- All catalog-mutating operators call `refresh_asset_browser()` on success.
246+
247+
## Related
248+
249+
- [Asset Pie Menu](../ui/ui_pies.md) — `IOPS_MT_Pie_Assets`, the pie this module feeds.

0 commit comments

Comments
 (0)