github m3trik/pythontk v0.9.24
pythontk v0.9.24

4 hours ago
  • 2026-08-17 — MeshConvert.prune_glb_unreferenced_textures (file_utils/mesh_convert/_mesh_convert.py): the applier-tail sweep that drops textures no material samples, the images only they read, and the bufferViews only those images owned. Every channel writer rebinds a slot to the image it embeds and leaves the displaced one in place; for the ORM repack that is FBX2glTF's own ao_met_rough_<mat> packing, a full-size PNG in the BIN. Measured on HOOKS_PINS.glb: 4 images / 1 unreferenced / 2.05 MB dead payload, flagged by the reviewer as an orphaned texture, one per repacked material every export. Referenced = any textureInfo in the material tree (key ending Texture with an int index, which covers the core slots and every KHR_materials_* extension); images survive through source or an extension source; a view is dropped only when the pruned images were its ONLY readers (FBX2glTF does share views between images). Survivors are renumbered everywhere — material refs, the session embed cache, textures[].source, and every bufferView key in the document via a generic walk — and the BIN is rebuilt from the surviving views. Runs at the tail of apply_scene_sidecar BEFORE the embedded texture map records image indices; a clean file is not rewritten. Verified on the production GLB: 12.96 → 10.90 MB, geometry decodes identically, ORM channels intact. 6 failing-first tests (incl. the EXT_lights_image_based bail, since that extension names images from the root and the walk cannot see it, and a bail when any bufferView lives outside the embedded buffer 0 — the BIN rebuild re-slices every kept view out of that one buffer, so an external-URI view would keep its buffer but get a byteOffset into the rebuilt BIN); test_mesh_convert 117/117.

  • 2026-08-17 — MapFactory.pack_orm_texture recognises the SAME packed map named in several slots when the paths are equal but distinct string objects (core_utils/engines/textures/map_factory/_map_factory.py::_resolve_orm_sources). A StingrayPBS material that wires one ORM file node into its AO, roughness and metallic inputs reaches MeshConvert.set_glb_metallic_roughness as three equal paths that are three distinct objects (the export sidecar is a JSON round trip). The resolver matched slots to the packed source by identity, so only the first slot was recognised; the other two stayed a packed path, were cleared as "carries no channel", and took their black fills. Measured on a production delivery (HOOKS_PINS.glb): the referenced ORM had AO intact and roughness 0 / metallic 0 at every one of 20,279 sampled texels, while the on-disk ORM was fully authored. Equality now, one failing-first test; test_img + test_map_factory 292/292.

  • 2026-08-17 — MapCompositor._seed_masks vets each mask source before OR-ing it into the union (core_utils/engines/textures/map_compositor.py). The union masked from every detectable-background type trusted each source equally, so one type with dilated islands (Painter edge padding bleeding into a neighbour's UV region) or a mis-detected background inflated every layer mask and dragged an otherwise clean batch into the >5% overlap WARNING — with no attempt to prefer the reliable source. Each type's own inter-layer overlap is now measured (_max_pairwise_overlap, threshold _MASK_OVERLAP_MAX); a source past it is dropped, with an INFO naming it and its overlap, whenever at least one clean type exists. Only when every source agrees do the layers overlap is the WARNING emitted, and it now says so — that is a genuine object-in-both-sets / shared-UV issue the user must act on, not a masking artefact. Two failing-first tests (noisy source dropped when a clean one exists / all-source overlap still warns); test_map_compositor 72/72.

Don't miss a new pythontk release

NewReleases is sending notifications on new releases.