Skip to content

pythontk v0.9.10

Latest

Choose a tag to compare

@m3trik m3trik released this 08 Aug 01:18
  • 2026-08-07 — WebXR viewer: binds baked lightmaps, and stops double-lighting a pre-lit model. glTF 2.0 has no lightmap slot, so a baked map arrives disguised as a real texture slot on TEXCOORD_1 (blendertk's LightmapWebExport writes it into occlusionTexture, leaving the emissive slot free for the scene's authored emissive). preview_viewer.html now reads the lightmap_web manifest from the scene extras and puts it back: material.lightMap, channel from the manifest, lightMapIntensity from the recorded encode divisor, and the carrier slot cleared so the same texture is not applied twice under two interpretations. Two details decide whether it looks right or merely plausible — GLTFLoader treats occlusion as data and loads it linear, but the map is sRGB-encoded to survive 8 bits (left alone it renders far too dark), and the map is packed against UV2 rather than UV0. A carrier: "fused" manifest is correctly a no-op: commit_unlit already made the bake the material's own appearance.

    A lightmapped model also already contains its lighting, so the viewer's own RoomEnvironment IBL and key light are switched off while one is loaded (and restored for the next un-lightmapped model). Left on, the baked shadows stay put while everything around them lifts — which reads as a washed-out model rather than as double lighting, and is easy to misdiagnose as a bad bake.

  • 2026-08-07 - RPC plugin core: two bugs that made every host-side op silently not happen. Found by probing the live Substance Painter after a Maya Substance-bridge send delivered no mesh maps; both are in net_utils/rpc/plugin_core.py, so both were vendored into all four _rpc_core.py payloads (Maya/Blender x marmoset/substance) and into extapps' in-place Painter plugin.

    MainThreadMarshaller.run never returned inside a host. The hop used QTimer.singleShot(0, functor) on the comment that its 0-delay overload "schedules onto the application's own thread". It does not: with no context object Qt builds the helper QObject in the calling thread, which for an RPC op is the HTTP server's daemon thread, and that thread runs no event loop - so the functor was queued somewhere nothing pumps and every marshalled op raised TimeoutError after 60s. Invisible to the existing tests, which have no QCoreApplication and so all took the direct-call branch. It now hands the call to a relay QObject whose affinity is the main thread and whose queued signal crosses the boundary (built lazily, per binding, rebuilt if the host swaps applications); measured against PySide6 6.5.3 and 6.10.1 - the shorter singleShot(0, context, functor) spelling does not exist in PySide6 at all. Five new tests drive the real path (worker thread + a pumped main loop): the hop lands on the main thread, exceptions propagate verbatim, two hops in a row both land, is_active() is True off-thread, and a genuinely blocked main thread still times out.

    A host reload left the plugin serving an empty op table. RpcPlugin.import_ops(package) replaces the plugins' from . import ops: the op modules register at import time, so once a host re-executes the plugin package (Painter's Python > Reload Plugins Folder, a disable/re-enable, importlib.reload) the fresh RpcPlugin has an empty registry while the ops submodules are still cached - the import is a no-op, no decorator fires, and the server comes back answering Unknown op for every feature it exists to provide while system.ping still says it is alive. import_ops drops the subtree from sys.modules first, so registration is always tied to the registry that is live now. Three tests, one of which pins the plain-import baseline so the helper cannot be "simplified" away.

    TestWireContract now takes the marshaller's documented opt-out - it blocks the calling thread on the client call, so once anything in the session stands up a QCoreApplication (the new Qt cases do) the server's hop would wait on a loop that is waiting on the server. pythontk 2741 green.

  • 2026-08-07 — Scene-data grid: MeshConvert owns the sidecar's converter column, and every GLB conversion can carry it. Production GLBs never got the translation repairs the WebXR preview showed — fbx_to_glb has five callers (preview, both DCC Scene Exporters' GLB paths, tentacle's quick-export, the converter panel) and only the preview applied the sidecar, because the applier orchestration lived on PreviewDeliverer. The registry (SIDECAR_APPLIERS), schema version and the three ops — build_scene_sidecar (envelope), apply_scene_sidecar (dispatch sections + embed the envelope and its per-section outcome summary into the glTF root extras, path or open-session), read_scene_sidecar (the consumer half) — now live on MeshConvert beside the writers they dispatch to, and fbx_to_glb(sidecar=...) folds the whole apply+embed into the same post-conversion edit session as the alpha repair: one parameter, one file pass, and the artifact leaves self-describing with no side files. PreviewDeliverer slims to build → convert → apply-for-the-summary → publish; PreviewBridge._attach_sidecar delegates envelope construction to the new schema owner. --user-properties is now a default FBX2glTF flag — measured against v0.13.1 + Maya 2025: the DataNodes data_export channels deliberately embedded in the FBX arrive in per-node glTF extras with it and are silently dropped without, so a GLB-only deliverable now carries the full channel set inside. Seven new TestSceneSidecar tests (frozen envelope top level, apply+embed round-trip, session composition, None no-op, empty-sections embed, unknown-section skip, the fbx_to_glb parameter) plus an embed assertion on the published preview artifact; pythontk 2745 green. End-to-end verified live in both DCCs (standalone Maya 2025: lambert incandescence → emissiveFactor [1, .5, 0] + embedded envelope; headless Blender 5.1.2: emission×2 → factor + KHR_materials_emissive_strength: 2.0).

  • 2026-08-07 — WebXR preview: the scene sidecar travels in a versioned envelope, owned in one place. The .scene.json beside the exported FBX (and the same dict on Payload.extras) used to be the bare section map — no version, no source DCC, no tie to the FBX it belongs to. Fine while the only reader was PreviewDeliverer._apply_sidecar in this repo; frozen the moment an external tool parses it, which is the file's stated future (artist previews it, a dev pulls the same setup from FBX + sidecar alone). The top level is now {version, source, asset, color_space, sections}, built and written by PreviewBridge._attach_sidecar — both DCC producers call it rather than shaping the dict themselves, so the schema cannot fork between mayatk and blendertk (whose _produce tails had duplicated the extras/json/log block verbatim, the drift the shared owner also removes). The deliverer consumes only sections; SIDECAR_VERSION bumps on top-level reshapes, never for a new section (sections are the extension point, and a reader skips unknown ones). Fixed with it, a pre-existing misreport: the panel's three-way outcome (off / on-but-nothing-to-carry / on-but-nothing-matched) was unreachable — producers attached the extras key only when non-empty, so sidecar_requested (key presence) read False for an unlit scene and the summary told a user whose checkbox was ON "Scene sidecar off - showing what the FBX carried"; the deliver() log ternary was also inverted (the off case logged "produced for this export"). _attach_sidecar now attaches (and writes) the envelope whenever the producer runs — empty sections included, an explicit "sections": {} being itself information — restoring key-presence ⇔ requested; both pinned by a test that fails on the old code. Explicitly not a second channel registry beside the DCCs' DataNodes: tool-authored semantics already ride inside the FBX on data_export, so the sidecar carries only FBX-translation repairs, derived scene-read-only at push time — the boundary is documented in _attach_sidecar and in mayatk's docs/data_nodes.md. Deliverer fixtures now build through the envelope helper, plus four new tests: the frozen top-level key set (round-tripped through the written file), the empty-sections envelope still attaching, the requested-but-empty push reporting "nothing to carry" rather than "off" (fails on the old code), and the producer→deliverer pair end to end through the real _attach_sidecar. pythontk 2737 green; producers verified live in standalone Maya 2025 and headless Blender 5.1.2 (identical envelopes).