blendertk v0.5.42
-
2026-07-30 — no Blender analogue of mayatk's intermediate-shape freeze bug (documented, verified). Maya's
cmds.instanceshares every shape including the deformer's intermediate (orig) shape, which blocksmakeIdentityforever (see mayatk's CHANGELOG). Blender has no intermediate datablock — modifiers evaluate off the singleobj.data— sopreserved_instances/uninstancehave nothing equivalent to fork, and thedelete_historyopt-in the Maya twin needed is deliberately absent (delete_historywas already a documented no-op here). Suites re-verified unchanged:test_node_utils.py/test_xform_utils.pyPASS headless. -
2026-07-30 —
NodeUtils.preserved_instances+freeze_transforms(instance_strategy=...)(mayatk parity — see its CHANGELOG for the mechanism and the failure it fixes). Linked-data twin of the Maya context manager: a group is the objects sharing one datablock, the master's "fork" isdata.copy(), and restore re-points every sibling at the master's post-op data with its world matrix compensated (B = post_world⁻¹ @ pre_world, siblingW → W @ B⁻¹) — writingmatrix_worldabsorbs parents / parent-inverse / deltas in one assignment, so the Maya twin's pivot re-pinning and instance-edge surgery have no analogue here. The orphaned original datablock is removed and the fork takes over its name. Triage (skipped up front, reported, untouched): library-linked objects/data, and siblings whose transforms are driven (drivers, action fcurves, unmuted constraints — compact yes/no twin oftransform_diag._driving_connections).freeze_transforms— wheretransform_applyrefuses multi-user data outright, so "skip" was the only behavior — gains the sameinstance_strategy="skip"|"preserve"|"uninstance"(default unchanged).fix_non_orthogonal_axesdeliberately does not take the option (divergence documented in its docstring): the Blender fix writes only transform channels, never shared geometry, so linked duplicates were already fixed in place with their instancing intact.test_node_utils.py+11 checks,test_xform_utils.py+7; both suites PASS headless. -
2026-07-30 —
libpyside: Failed to disconnect … from signalconsole spam from the Reference Manager header and the Channels table (mayatk twins fixed identically). User-reported from a live session (reference_manager.py:246,refresh_requested()). Two defects, both in idempotent signal re-wiring — mandatory here because the QWidget outlives the slots instance across a reload. (1) Blanketsignal.disconnect()on a signal with nothing attached: PySide reports that throughwarnings.warn, not an exception, so the surroundingexcept (RuntimeError, TypeError)never suppressed it and every first call printed the line — the header'srefresh_requested, and channels' fourcellScrub*/cellWheelScrolledbindings (which, unlike thecellChanged/customContextMenuRequestedblankets beside them, have no owner but us). Both now drop only their own prior connection, tracked per (widget, key). (2) The tracked-disconnect primitive had the same hole:_rewire_signaldropped its storedQMetaObject.Connectionthrough the signal-instancedisconnect(), which expects a slot — handed a Connection it can't match (the reload case: PySide breaks the binding when the old slots instance is collected) it warns and returns rather than raising, so theexceptwas dead code and the warning would simply have come back wearing a Connection repr. Verified against live PySide6, then switched to the staticQObject.disconnect()— the API that actually takes a Connection — with a falsy (already-broken) Connection skipped outright.test_blender_ui_handler.py226/226 (its single-connection regression unchanged) with a warning-free run,test_channels.py56/56,test_reference_manager.py64/64. -
2026-07-30 — Maya-scene import robustness: tolerant scene open,
.mbdriver probe, namespace-safe translated shaders, and collision-safe visibility replay. Four hardenings of the pull-direction conversion, each closing a real production edge. (1) Tolerant open (both conversion templates):cmds.file(open=...)raisesRuntimeErrorover missing renderer plugins / unknown nodes even when the scene content loaded — routine for production scenes on a vanilla mayapy — and that killed the whole conversion;_open_scenenow verifies viacmds.file(q, sceneName)that the scene actually loaded and tolerates the noise (printed), while a genuinely failed open still raises. (2)scene_has_complex_animationnow answers for.mbtoo (was a blindFalse): node type names are stored as plain byte strings in the binary, so a chunked, overlap-safe token scan over the same_DRIVER_NODE_TYPES(+ the<node>_visibilitycurve name) gives binary scenes the same bake-vs-raw browser signal — heuristic by contract, since a false positive only costs an unneeded bake attempt (the Maya-side probe re-decides under"auto"). (3) Namespace-safe created nodes (both templates): a referenced scene's materials are namespaced (loadReferenceDepth="all"), andf"{mat}_fbxsafe"put the translated phong INSIDE that namespace — its exported name's colon then at the mercy of FBX name mangling, silently missing the Blender-side slot match;_ns_safeflattens colons so created phong/standardSurface/bump nodes live at root under names that round-trip verbatim. (4) Visibility collision handling:_collect_baked_visibilitykeyed the manifest by short name and silently last-writer-won on duplicates — and the Blender-side replay matches by short name, so ONE object's curve would land on BOTH; differing duplicates are now dropped loudly (identical ones merge), no keys beating wrong keys. Also: the four engine entry points expand~beside$VAR.test_scene_import.py+10 checks (incl. a behavioral run of the extracted collector against a stubcmds, and.mbscans with a chunk-boundary-straddling token), verified failing pre-fix via stashed-source run; suite PASS under both the venv and headless Blender 5.1. -
2026-07-30 —
MatUtils.find_unassigned(mayatk parity — see its CHANGELOG for the Maya-side subtlety). Objects carrying no material: no material slots at all, or every slot empty. Blender has the real state Maya has to synthesize — nothing here joins a default shading group — so there's noinclude_defaultaxis; objects that can't hold materials (empties, lights, cameras) are not reported, since they aren't unshaded geometry. Sameobjects=Nonepool convention and object-level scope asfind_by_mat_id, whose complement it is. Drives tentacle's materials "No Material" select mode.test_mat_anim_utils.py+6. -
2026-07-29 — RizomUV temp payloads move onto
ptk.TempArtifacts, and the no-save error diagnoses itself (mirrors mayatk — see its CHANGELOG for the diagnosis). This twin used the same two fixed names as the Maya bridge (rizomuv_exported.fbx/riz_uv_script.luain the system temp dir), so the two panels were racing each other for one script file — and RizomUV re-reads the-cfiscript after launch, so whichever run got overwritten exited 0 without ever reachingZomSave. The round-trip now allocates both payloads from a"scoped"store (clean run deletes them, a failure keeps them so the script can be opened in RizomUV's Script Editor) and the one-waysendfrom a"detached"one (its session may outlive us, so allocation age-sweeps instead of deleting) — replacing a hand-rolledtime.time_ns()tag thatTempArtifactsdocuments as not unique enough alone on Windows._no_save_diagnosisreplaces the old message's promise of a Lua traceback (RizomUV prints nothing in-cfimode) with the script/FBX paths — both rendered OS-native, they used to mix separators — a comparison of the on-disk script against what was written, and a pointer at RizomUV's Script Editor._release_temp_payloadsre-allocates on the next run, sincecleanupuntracks what it removed and a cached path would leak.test_rizom_construction.py+10 → 34/34. -
2026-07-29 —
m_framestep cycle + clip fitting (mayatk parity — see its CHANGELOG for the detail);CamUtilsgainsget_view_state/set_view_state/fit_camera_clipping. Same contract:m_frame(steps=2, adjust_clipping=True), first press frames, further presses withinFRAME_STEP_TIMEOUTstep in, the last returns the view exactly where it started, and a pause collapses the cycle to a plain there-and-back toggle (sharedptk.StepToggle). Blender-side differences: the ideal distance comes fromview3d.view_selecteditself (no per-element fit factors) with the step ramp applied as a dolly onview_distance, and the state snapshotted/restored is the RegionView3D (location / rotation / distance / perspective) plus the viewport's clip planes —SpaceView3D.clip_start/clip_endis what a free view actually clips against, not the camera dataadjust_camera_clippingwrites — so the fit widens whichever surface is doing the clipping (the lens while locked to a camera, else the viewport's planes) against that surface's own current values, and the snapshot carries the lens planes too or the widening would outlive the view it was made for. Smooth view is suppressed for the framing op, which otherwise animates the view over the next ~200 ms and would fight the step dolly.fit_camera_clippingmeasures depth fromview_location/view_rotation/view_distancerather thanrv3d.view_matrix, which is only refreshed at draw time and is still the previous view right after a framing op.test_macros.py+8,test_cam_utils.py+9. -
2026-07-29 — Shell Xform tri-state snap (off / grid / shell) +
UvUtils.get_neighbor_shell_bounds(mayatk parity — see its CHANGELOG for the detail). Same 3-stateActionOptioncycle and the sameptk.MathUtils.next_clear_offsetgeometry, so the two panels behave identically. The pool is every mesh whose active UV layer name matches the input's, minus the input's own meshes — and unlike the Maya twin that excludes every island of the input, not just the selected ones, becausemove_uvsshifts an object's whole UV map, so all of its islands travel together and none can be parked against. Linked duplicates are excluded with their shared datablock. Bounds accumulate as a running min/max per island rather than collecting every loop UV.shell_xform_slot_check.py+22 → 43 (park, walk-past, scope-independence, grid fallback, out-of-lane, UV-layer scoping, tri-state shape);test_uv_utils.py+6.