From 8e28cf7a0a17d443aae755afa41c7eb293a3e667 Mon Sep 17 00:00:00 2001 From: Moshu <1709219+DatMoshu@users.noreply.github.com> Date: Sat, 10 Oct 2026 14:11:17 -0500 Subject: [PATCH 1/3] Exclude skeleton display shapes from imported item geometry --- .../integration/blender_import_asset_check.py | 36 +++++++++++++++++++ tools/uo-content/blender_build.py | 6 +++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/integration/blender_import_asset_check.py diff --git a/tests/integration/blender_import_asset_check.py b/tests/integration/blender_import_asset_check.py new file mode 100644 index 0000000..63a7db4 --- /dev/null +++ b/tests/integration/blender_import_asset_check.py @@ -0,0 +1,36 @@ +"""A skinned GLB keeps its real mesh but excludes imported bone-display geometry.""" +import ast +from pathlib import Path +import tempfile + +import bpy +from mathutils import Vector +import numpy as np + + +root = Path(__file__).resolve().parents[2] +tree = ast.parse((root / 'tools/uo-content/blender_build.py').read_text(encoding='utf-8')) +function = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == 'import_asset') +exec(compile(ast.Module(body=[function], type_ignores=[]), 'import_asset', 'exec')) + +with tempfile.TemporaryDirectory() as temp: + bpy.ops.wm.read_factory_settings(use_empty=True) + bpy.ops.object.armature_add() + rig = bpy.context.object + bpy.ops.mesh.primitive_cube_add(size=0.25, location=(0, 0, 2)) + mesh = bpy.context.object + # A legitimate mesh may itself be named Icosphere; filtering by name is wrong. + mesh.name = 'Icosphere' + mesh.vertex_groups.new(name=rig.data.bones[0].name).add(list(range(8)), 1, 'REPLACE') + mesh.modifiers.new('Skin', 'ARMATURE').object = rig + mesh.parent = rig + path = Path(temp) / 'skinned.glb' + bpy.ops.export_scene.gltf(filepath=str(path), export_format='GLB') + bpy.ops.wm.read_factory_settings(use_empty=True) + objects = import_asset(path) + assert len(objects) == 1, [obj.name for obj in objects] + points = np.array([tuple(objects[0].matrix_world @ vertex.co) for vertex in objects[0].data.vertices]) + np.testing.assert_allclose(points.min(axis=0), [-0.125, -0.125, 1.875], atol=1e-5) + np.testing.assert_allclose(points.max(axis=0), [0.125, 0.125, 2.125], atol=1e-5) + assert set(bpy.data.objects) == set(objects) +print('Skinned GLB imports only supplied geometry; fit bounds preserved.') diff --git a/tools/uo-content/blender_build.py b/tools/uo-content/blender_build.py index 067b64e..ec3f04a 100644 --- a/tools/uo-content/blender_build.py +++ b/tools/uo-content/blender_build.py @@ -210,7 +210,11 @@ def import_asset(path): for o in dst.objects: if o: bpy.context.scene.collection.objects.link(o) imported=set(bpy.data.objects)-before - meshes=[o for o in imported if o.type=='MESH'] + # glTF import creates mesh custom shapes for its skeleton display. They are + # helpers, not supplied geometry, and would corrupt both fit bounds and sprites. + bone_shapes={bone.custom_shape for o in imported if o.type=='ARMATURE' + for bone in o.pose.bones if bone.custom_shape is not None} + meshes=[o for o in imported if o.type=='MESH' and o not in bone_shapes] if not meshes: raise ValueError('No mesh found in supplied asset.') dg=bpy.context.evaluated_depsgraph_get() # Flatten an imported asset's current shape before binding it to the canonical rig. From d1822fb158ba97a754dc1d50333c8b02ebf77425 Mon Sep 17 00:00:00 2001 From: Moshu <1709219+DatMoshu@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:55:08 -0500 Subject: [PATCH 2/3] Changelog: issue #12 is fixed (#18, #30) after the portability merge Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28734b3..0251051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,8 +26,8 @@ - Robustness fixes for issues #5-#11 (#18): VD offsets are bounded in `vdtool`/`uo_vd_writer`, outfit-lab `vd.py` clips runs, `vdtool` prints usage on bad arguments, `jsonio` writes are atomic, `restore()` compares sha256, annotation errors are narrowed and warn, and the studio skips an unreadable job status. -- Issue #12 (portability) is partly done in #18: the `region_fonts` font fallback and `find_blender` discovery. The - rest is still open (SM-12). +- Issue #12 (portability) fixed (#18, #30): the `region_fonts` font fallback, one `find_blender` discovery shared by + the tools, and no hardcoded Windows paths (the hardcoded-path test matches backslash and slash). ### Local servers (security) - Content Studio, Fit Lab and the live pose editor bind to the loopback interface only and check `Host` and `Origin` From 1ffc72e11c81b1ddd95e21e0b909bda001ccd01c Mon Sep 17 00:00:00 2001 From: Moshu <1709219+DatMoshu@users.noreply.github.com> Date: Sat, 24 Oct 2026 23:33:24 -0500 Subject: [PATCH 3/3] Keep generated payloads ignored and document shared legacy paths --- .gitignore | 37 +++++++++++++++++++++++++++++++++++++ workspace/README.md | 6 ++++++ 2 files changed, 43 insertions(+) diff --git a/.gitignore b/.gitignore index 06a5a23..014f6ef 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,40 @@ launchers/_shared/config.local.sh # Browser automation scratch /.playwright-mcp/ + +# Fleet size floor: generated payloads stay outside checkouts. +build/ +builds/ +out/ +output/ +outputs/ +exports/ +dist/ +tmp/ +temp/ +.venv/ +.venv-*/ +node_modules/ +.worktrees/ +worktrees/ +.claude/worktrees/ +runs/ +evidence/ +*.log +*.zip +*.7z +*.rar +*.iso +*.chd +*.bin +*.mov +*.wav +*.psd +!data/** + +# Local compatibility symlinks are files to Git (directory patterns do not match). +/.playwright-mcp +/.venv +/.venvs +/build +/outputs diff --git a/workspace/README.md b/workspace/README.md index fa33c41..d0aec25 100644 --- a/workspace/README.md +++ b/workspace/README.md @@ -1,5 +1,11 @@ # workspace/ +Task 553 moved the existing local payloads into the shared work area's `workspace/` +directory. This checkout keeps local symbolic links for legacy paths used by the +editor, pipeline launchers and authoring scripts. The data is stored outside the +checkout; preserve the links when using those legacy paths. No authoring data was +deleted. New jobs should use `SPRITEMOTION_WORK_ROOT` directly. + Generated payloads now belong under `SPRITEMOTION_WORK_ROOT` (Windows default: `D:/_agentShared/work/spritemotion`); review screenshots, logs and clips belong in central evidence runs. New content-studio jobs and checkout-specific Python