Skip to content

Commit 5b4a41d

Browse files
committed
feat(paths): enhance shape and painted file resolution in batch and resume commands
Refactor the batch_cmd and resume_cmd functions to utilize _shape_existing and _painted_existing for improved file resolution. This change ensures compatibility with both legacy and new directory structures, allowing for more robust asset processing. Update related tests to reflect the new behavior of file path handling in the intermediate directory.
1 parent 2985890 commit 5b4a41d

6 files changed

Lines changed: 81 additions & 51 deletions

File tree

GameAssets/src/gameassets/batch_cmd.py

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@
6464
_animator3d_output_path,
6565
_classify_row_state,
6666
_install_file,
67+
_painted_existing,
6768
_painted_path,
6869
_path_for_log,
6970
_paths_for_row_manifest,
7071
_rigging3d_output_path,
72+
_shape_existing,
7173
_shape_path,
7274
)
7375
from .pipeline import (
@@ -1406,7 +1408,7 @@ def _finalize_mesh_ok_d(
14061408
for idx in pending_3d_d:
14071409
row = rows[idx]
14081410
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1409-
mesh_shape = _shape_path(mesh_f)
1411+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
14101412
if not force and mesh_shape.is_file():
14111413
shape_skipped_d.add(idx)
14121414
shape_idx_map_d[row.id] = idx
@@ -1535,8 +1537,8 @@ def _finalize_mesh_ok_d(
15351537
row = rows[idx]
15361538
rec_d = results_d[idx]
15371539
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1538-
mesh_shape = _shape_path(mesh_f)
1539-
mesh_painted = _painted_path(mesh_f)
1540+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
1541+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
15401542
try:
15411543
assert paint3d_bin is not None
15421544
tex_argv = _texture_subprocess_argv(
@@ -1596,8 +1598,8 @@ def _finalize_mesh_ok_d(
15961598
for idx in shape_ok_paint_d:
15971599
row = rows[idx]
15981600
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1599-
mesh_shape = _shape_path(mesh_f)
1600-
mesh_painted = _painted_path(mesh_f)
1601+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
1602+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
16011603
if not force and mesh_painted.is_file():
16021604
paint_skipped_d.add(idx)
16031605
paint_idx_map_d[row.id] = idx
@@ -1674,7 +1676,7 @@ def _finalize_mesh_ok_d(
16741676
rec_d = results_d[idx]
16751677
row = rows[idx]
16761678
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1677-
mesh_painted = _painted_path(mesh_f)
1679+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
16781680
_pst = item_result.get("status", "")
16791681

16801682
if _pst == "progress":
@@ -1708,7 +1710,7 @@ def _finalize_mesh_ok_d(
17081710
row = rows[idx]
17091711
rec_d = results_d[idx]
17101712
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1711-
mesh_painted = _painted_path(mesh_f)
1713+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
17121714
dash.feed_event(row.id, "paint3d", "skipped", phase="texture")
17131715
_finalize_mesh_ok_d(rec_d, mesh_painted, row)
17141716
finalized_d.add(idx)
@@ -1726,7 +1728,7 @@ def _finalize_mesh_ok_d(
17261728
if rec_d.get("status") == "error":
17271729
continue
17281730
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1729-
mesh_shape = _shape_path(mesh_f)
1731+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
17301732
if not mesh_shape.is_file():
17311733
continue
17321734
rec_d["image_path"] = _path_for_log(img_f, manifest_dir)
@@ -1742,8 +1744,8 @@ def _finalize_mesh_ok_d(
17421744
row = rows[idx]
17431745
rec_d = results_d[idx]
17441746
_img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1745-
mesh_painted = _painted_path(mesh_f)
1746-
mesh_shape = _shape_path(mesh_f)
1747+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
1748+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
17471749
simplify_mesh = (
17481750
mesh_painted
17491751
if mesh_painted.is_file()
@@ -1772,8 +1774,8 @@ def _finalize_mesh_ok_d(
17721774
row = rows[idx]
17731775
rec_d = results_d[idx]
17741776
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1775-
mesh_painted = _painted_path(mesh_f)
1776-
mesh_shape = _shape_path(mesh_f)
1777+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
1778+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
17771779
actual_mesh = (
17781780
mesh_painted
17791781
if mesh_painted.is_file()
@@ -1806,7 +1808,7 @@ def _finalize_mesh_ok_d(
18061808
row = rows[idx]
18071809
rec_d = results_d[idx]
18081810
img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
1809-
mesh_shape = _shape_path(mesh_f)
1811+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
18101812
seed = _seed_for_row(profile, row.id)
18111813
t3d_args = _text3d_argv(text3d_bin, profile, img_f, mesh_shape, row, gpu_ids=gpu_ids)
18121814
if seed is not None:
@@ -2414,8 +2416,8 @@ def _finalize_mesh_ok(
24142416
for idx in pending_3d_indices:
24152417
row = rows[idx]
24162418
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2417-
mesh_shape = _shape_path(mesh_final)
2418-
mesh_painted = _painted_path(mesh_final)
2419+
mesh_shape = _shape_existing(mesh_final) or _shape_path(mesh_final)
2420+
mesh_painted = _painted_existing(mesh_final) or _painted_path(mesh_final)
24192421

24202422
if (
24212423
not force
@@ -2571,8 +2573,8 @@ def _finalize_mesh_ok(
25712573
rec = results[idx]
25722574
progress.update(task_paint, description=f"[cyan]{row.id}[/cyan] · quick paint")
25732575
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2574-
mesh_shape = _shape_path(mesh_final)
2575-
mesh_painted = _painted_path(mesh_final)
2576+
mesh_shape = _shape_existing(mesh_final) or _shape_path(mesh_final)
2577+
mesh_painted = _painted_existing(mesh_final) or _painted_path(mesh_final)
25762578

25772579
if mesh_final.is_file() and not force:
25782580
_finalize_mesh_ok(rec, mesh_final, row)
@@ -2630,8 +2632,8 @@ def _finalize_mesh_ok(
26302632
for idx in shape_ok_paint:
26312633
row = rows[idx]
26322634
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2633-
mesh_shape = _shape_path(mesh_final)
2634-
mesh_painted = _painted_path(mesh_final)
2635+
mesh_shape = _shape_existing(mesh_final) or _shape_path(mesh_final)
2636+
mesh_painted = _painted_existing(mesh_final) or _painted_path(mesh_final)
26352637

26362638
if mesh_final.is_file() and not force:
26372639
rec = results[idx]
@@ -2719,7 +2721,7 @@ def _finalize_mesh_ok(
27192721
rec = results[idx]
27202722
row = rows[idx]
27212723
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2722-
mesh_painted = _painted_path(mesh_final)
2724+
mesh_painted = _painted_existing(mesh_final) or _painted_path(mesh_final)
27232725
_pst = item_result.get("status", "")
27242726

27252727
if _pst == "progress":
@@ -2766,8 +2768,8 @@ def _finalize_mesh_ok(
27662768
row = rows[idx]
27672769
rec = results[idx]
27682770
_img_f, mesh_f = _paths_for_row_manifest(profile, manifest_dir, row)
2769-
mesh_painted = _painted_path(mesh_f)
2770-
mesh_shape = _shape_path(mesh_f)
2771+
mesh_painted = _painted_existing(mesh_f) or _painted_path(mesh_f)
2772+
mesh_shape = _shape_existing(mesh_f) or _shape_path(mesh_f)
27712773
simplify_mesh = (
27722774
mesh_painted
27732775
if mesh_painted.is_file()
@@ -2799,8 +2801,8 @@ def _finalize_mesh_ok(
27992801
row = rows[idx]
28002802
rec = results[idx]
28012803
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2802-
mesh_painted = _painted_path(mesh_final)
2803-
mesh_shape = _shape_path(mesh_final)
2804+
mesh_painted = _painted_existing(mesh_final) or _painted_path(mesh_final)
2805+
mesh_shape = _shape_existing(mesh_final) or _shape_path(mesh_final)
28042806
actual_mesh = (
28052807
mesh_painted
28062808
if mesh_painted.is_file()
@@ -2839,7 +2841,7 @@ def _finalize_mesh_ok(
28392841
rec = results[idx]
28402842
progress.update(task2, description=f"[cyan]{row.id}[/cyan] · Text3D")
28412843
img_final, mesh_final = _paths_for_row_manifest(profile, manifest_dir, row)
2842-
mesh_shape = _shape_path(mesh_final)
2844+
mesh_shape = _shape_existing(mesh_final) or _shape_path(mesh_final)
28432845

28442846
if not force and mesh_shape.is_file():
28452847
actual_mesh = mesh_final if mesh_final.is_file() else mesh_shape

GameAssets/src/gameassets/paths.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,26 +97,30 @@ def _intermediate_dir(mesh_final: Path) -> Path:
9797

9898

9999
def _shape_path(mesh_final: Path) -> Path:
100-
"""``id_shape.glb`` ao lado da mesh canónica (compat: batch_cmd/resume_cmd).
100+
"""``id_shape.glb`` em ``_intermediate/`` — destino canónico desde Round 2.
101101
102-
O orquestrador move este ficheiro para ``_intermediate/`` ao fim da
103-
pipeline via ``move_to_intermediate``.
102+
Antes existia ao lado da mesh canónica e era movido no fim da pipeline;
103+
isso fazia o ``resume`` e o ``batch`` perderem o ficheiro depois do
104+
move (procuravam só em ``meshes/``). Agora escrevemos directamente
105+
em ``_intermediate/`` desde o Stage 1 (``text3d generate``).
104106
105-
Round 2 fix: normaliza o stem via ``_base_stem`` para que a função seja
106-
idempotente — passar ``goblin.glb`` ou ``goblin_painted.glb`` devolve
107-
sempre ``goblin_shape.glb``.
107+
``_shape_existing`` mantém compatibilidade com layouts antigos
108+
(procura em ``meshes/`` primeiro, depois em ``_intermediate/``).
109+
110+
Idempotente em relação a sufixos canónicos do stem.
108111
"""
109112
base = _base_stem(mesh_final.stem)
110-
return mesh_final.with_name(f"{base}_shape{mesh_final.suffix}")
113+
return _intermediate_dir(mesh_final) / f"{base}_shape{mesh_final.suffix}"
111114

112115

113116
def _painted_path(mesh_final: Path) -> Path:
114-
"""``id_painted.glb`` ao lado da mesh canónica (compat).
117+
"""``id_painted.glb`` em ``_intermediate/`` — destino canónico desde Round 2.
115118
116-
Idempotente em relação a sufixos canónicos (Round 2 fix).
119+
Mesma lógica de :func:`_shape_path`: escrever directamente em
120+
``_intermediate/`` evita a corrida resume↔move ao fim do pipeline.
117121
"""
118122
base = _base_stem(mesh_final.stem)
119-
return mesh_final.with_name(f"{base}_painted{mesh_final.suffix}")
123+
return _intermediate_dir(mesh_final) / f"{base}_painted{mesh_final.suffix}"
120124

121125

122126
def _clean_path(mesh_final: Path) -> Path:
@@ -195,15 +199,20 @@ def _valid_file(p: Path) -> bool:
195199

196200

197201
def _resolve_intermediate_or_main(canonical: Path, mesh_final: Path) -> Path | None:
198-
"""Round 2: aceita o ficheiro em ``meshes/`` ou em ``meshes/_intermediate/``.
202+
"""Aceita o ficheiro no caminho canónico ou no legacy ``meshes/`` (compat).
199203
200-
O master pipeline move ``shape``/``painted`` para ``_intermediate/`` no fim.
201-
Para retomar uma pipeline parcial precisamos detectar o ficheiro em
202-
qualquer das duas localizações. Devolve o path existente (preferindo a
203-
localização canónica) ou ``None`` se nenhum existe e é válido.
204+
Desde Round 2 o caminho canónico de ``shape``/``painted``/``clean``/
205+
``rigged_hi`` é ``meshes/_intermediate/``; para retro-compat também
206+
aceitamos a localização antiga (``meshes/<asset>_shape.glb`` etc.).
207+
Devolve o primeiro ficheiro válido encontrado ou ``None``.
204208
"""
205209
if _valid_file(canonical):
206210
return canonical
211+
# Fallback para layouts antigos: <meshes>/<asset>_shape.glb.
212+
legacy = mesh_final.with_name(canonical.name)
213+
if _valid_file(legacy):
214+
return legacy
215+
# Fallback inverso (caso ``canonical`` ainda aponte para legacy).
207216
intermediate = _intermediate_dir(mesh_final) / canonical.name
208217
if _valid_file(intermediate):
209218
return intermediate

GameAssets/src/gameassets/resume_cmd.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@
4343
_classify_row_state,
4444
_classify_row_state_master,
4545
_install_file,
46+
_painted_existing,
4647
_painted_path,
4748
_paths_for_row_manifest,
4849
_rigging3d_output_path,
50+
_shape_existing,
4951
_shape_path,
5052
)
5153
from .pipeline import (
@@ -623,7 +625,7 @@ def _resume_fn(dash: BatchDashboard) -> None: # type: ignore[name-defined]
623625
t_tex = _texture_subprocess_argv(
624626
paint3d_bin,
625627
profile,
626-
_shape_path(it["mesh_final"]),
628+
_shape_existing(it["mesh_final"]) or _shape_path(it["mesh_final"]),
627629
it["img_final"],
628630
painted_out,
629631
row_id=row.id,
@@ -663,7 +665,7 @@ def _resume_fn(dash: BatchDashboard) -> None: # type: ignore[name-defined]
663665
paint_manifest_items.append(
664666
{
665667
"id": row.id,
666-
"mesh": str(_shape_path(it["mesh_final"])),
668+
"mesh": str(_shape_existing(it["mesh_final"]) or _shape_path(it["mesh_final"])),
667669
"image": str(it["img_final"]),
668670
"output": str(_painted_path(it["mesh_final"])),
669671
}
@@ -1080,7 +1082,7 @@ def _resume_fn(dash: BatchDashboard) -> None: # type: ignore[name-defined]
10801082
t_tex = _texture_subprocess_argv(
10811083
paint3d_bin,
10821084
profile,
1083-
_shape_path(it["mesh_final"]),
1085+
_shape_existing(it["mesh_final"]) or _shape_path(it["mesh_final"]),
10841086
it["img_final"],
10851087
painted_out,
10861088
row_id=row.id,
@@ -1114,7 +1116,7 @@ def _resume_fn(dash: BatchDashboard) -> None: # type: ignore[name-defined]
11141116
paint_manifest_items.append(
11151117
{
11161118
"id": row.id,
1117-
"mesh": str(_shape_path(it["mesh_final"])),
1119+
"mesh": str(_shape_existing(it["mesh_final"]) or _shape_path(it["mesh_final"])),
11181120
"image": str(it["img_final"]),
11191121
"output": str(_painted_path(it["mesh_final"])),
11201122
}

GameAssets/tests/test_pipeline_redesign.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ def test_intermediate_dir_layout(tmp_path: Path) -> None:
2828
inter = _intermediate_dir(mesh_final)
2929
assert inter == mesh_final.parent / "_intermediate"
3030

31-
# _shape_path / _painted_path mantêm-se ao lado da mesh canónica (compat)
32-
assert _shape_path(mesh_final) == mesh_final.parent / "goblin_shape.glb"
33-
assert _painted_path(mesh_final) == mesh_final.parent / "goblin_painted.glb"
31+
# _shape_path / _painted_path nascem agora em _intermediate/ (Round 2 fix:
32+
# evita corrida resume↔move-to-intermediate ao fim do pipeline).
33+
assert _shape_path(mesh_final) == inter / "goblin_shape.glb"
34+
assert _painted_path(mesh_final) == inter / "goblin_painted.glb"
3435

3536
# _clean_path / _rigged_hi_path nascem em _intermediate/
3637
assert _clean_path(mesh_final) == inter / "goblin_clean.glb"

GameAssets/tests/test_resume_master.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,10 @@ def test_classify_master_detects_shape_in_intermediate(tmp_path: Path) -> None:
169169
img = tmp_path / "img.png"
170170
_touch(img)
171171
mesh = tmp_path / "mesh.glb"
172-
# shape vai direto para _intermediate/ (simula run anterior).
172+
# shape canónico já é em _intermediate/ desde Round 2.
173173
canonical_shape = _shape_path(mesh)
174-
intermediate_shape = _intermediate_dir(mesh) / canonical_shape.name
175-
_touch(intermediate_shape)
176-
assert not canonical_shape.exists()
174+
assert canonical_shape == _intermediate_dir(mesh) / canonical_shape.name
175+
_touch(canonical_shape)
177176

178177
state = _classify_row_state_master(
179178
img_final=img, mesh_final=mesh, want_texture=True, wants_rig=False, wants_animate=False

0 commit comments

Comments
 (0)