diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 44a411b..98f8d7f 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -909,3 +909,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-062 | 2026-10-02 | O9 | nouvelle commandlet `LPSetComponentCollision` ; `Content/BPs/MapMarkers.uasset` : composants `Tree` et `Car` en `NoCollision` (diff avant/après : 2 lignes) | chargement de partie de 71 s (identique en 4.27) dû aux ≈ 160 000 corps physiques des arbres et voitures décoratifs de la carte (décision utilisateur : option 1) ; build `LifePlay_Build_5.5_O9` | `Tools\Restore.ps1 -Lot O9` | | CHG-063 | 2026-10-02 | O9b | `LPSetComponentCollision` (modèles des nœuds « Add Component ») ; `Content/BPs/MapMarkers.uasset` : modèles `NODE_AddHISM-0` et `-1` (`HISM_C`, marqueurs de bâtiments) en `NoCollision` (diff : 2 lignes) | les clics se font sur les icônes, pas sur les bâtiments (utilisateur) ; ≈ 49 500 corps physiques restants au chargement ; build `LifePlay_Build_5.5_O9b` | `Tools\Restore.ps1 -Lot O9b` | | CHG-064 | 2026-10-02 | O2 | `Config/DefaultGame.ini` : `UsePakFile=True`, `bUseIoStore=True`, `bCompressed=True` (réglages par défaut du moteur : Oodle Kraken) ; `Modules` reste en fichiers nus | build Development 3 367 → 2 002 Mo (−40 %), 23 928 → 2 104 fichiers ; build `LifePlay_Build_5.5_O2` ; validé en jeu le 2026-10-02 (7 contrôles) | `Tools\Restore.ps1 -Lot O2` ou `git checkout O2-avant -- Config` | +| CHG-065 | 2026-10-02 | O4 | nouveau `Tools/FixTextureMips.py` ; 29 textures (`Clothes4` 27, route, trottoir) : `MipGenSettings` = groupe de texture, `NeverStream` = faux (diff : ces 2 propriétés + `LightingGuid` régénéré) | textures de couleur 3D sans mipmaps : scintillement à distance, pleine résolution toujours en mémoire ; exclus : cheveux et masques de transparence (29), interface et textures sans matériau (44), dimensions non puissance de 2 (733) ; build `LifePlay_Build_5.5_O4` | `Tools\Restore.ps1 -Lot O4` | diff --git a/Tools/FixTextureMips.py b/Tools/FixTextureMips.py new file mode 100644 index 0000000..592eff0 --- /dev/null +++ b/Tools/FixTextureMips.py @@ -0,0 +1,78 @@ +# Lot O4 : mipmaps et streaming des textures 3D qui en sont privees. +# +# Candidates : Texture2D de /Game dont MipGenSettings = NoMipmaps ou NeverStream = True, de dimensions en puissance +# de 2, referencees par au moins un materiau (Material, MaterialInstanceConstant) et par aucun widget ni Blueprint. +# Les textures d'interface ou chargees par nom (sans reference de materiau) ne sont jamais touchees. +# Modification : MipGenSettings = FromTextureGroup, NeverStream = False. Rien d'autre. +# +# Usage : +# UnrealEditor-Cmd.exe -run=pythonscript -script="/Tools/FixTextureMips.py [-apply]" +# Sans -apply : simulation (liste ecrite dans Saved/Audit/o4_candidats.txt). + +import os +import sys +import unreal + +apply = "-apply" in sys.argv +registry = unreal.AssetRegistryHelpers.get_asset_registry() +registry.search_all_assets(True) +opts = unreal.AssetRegistryDependencyOptions(include_soft_package_references=True, include_hard_package_references=True, + include_searchable_names=False, include_soft_management_references=False, + include_hard_management_references=False) +MATERIAL_CLASSES = {"Material", "MaterialInstanceConstant"} + +def pow2(n): + return n > 0 and (n & (n - 1)) == 0 + +candidates, skipped = [], {} +for a in registry.get_assets_by_class(unreal.TopLevelAssetPath("/Script/Engine", "Texture2D"), False): + pkg = str(a.package_name) + if not pkg.startswith("/Game/"): + continue + mips = str(a.get_tag_value("MipGenSettings") or "") + never = str(a.get_tag_value("NeverStream") or "") + if mips != "TMGS_NoMipmaps" and never != "True": + continue + dims = str(a.get_tag_value("Dimensions") or "0x0").split("x") + if len(dims) != 2 or not (pow2(int(dims[0])) and pow2(int(dims[1]))): + skipped["dimensions non puissance de 2"] = skipped.get("dimensions non puissance de 2", 0) + 1 + continue + classes = set() + for ref in registry.get_referencers(a.package_name, opts) or []: + for ra in registry.get_assets_by_package_name(ref): + classes.add(str(ra.asset_class_path.asset_name)) + if not (classes & MATERIAL_CLASSES): + skipped["aucun materiau"] = skipped.get("aucun materiau", 0) + 1 + continue + if classes - MATERIAL_CLASSES: + key = "reference aussi par " + ",".join(sorted(classes - MATERIAL_CLASSES)) + skipped[key] = skipped.get(key, 0) + 1 + continue + # Cheveux et masques de transparence exclus : les mipmaps y amincissent les meches et les decoupes a distance. + name = pkg.rsplit("/", 1)[1] + if "/Hairs/" in pkg or any(t in name for t in ("TR", "Trs", "Trans", "CUT", "_DO", "Cap")): + skipped["cheveux ou masque de transparence"] = skipped.get("cheveux ou masque de transparence", 0) + 1 + continue + candidates.append((pkg, mips, never, "x".join(dims))) + +out = os.path.join(unreal.Paths.project_saved_dir(), "Audit", "o4_candidats.txt") +with open(out, "w", encoding="utf-8") as f: + for c in candidates: + f.write(";".join(c) + "\n") +unreal.log_warning("FixTextureMips : {} candidate(s), ecartees {} -> {}".format(len(candidates), skipped, out)) + +if apply: + errors = 0 + for pkg, _, _, _ in candidates: + tex = unreal.EditorAssetLibrary.load_asset(pkg) + tex.set_editor_property("mip_gen_settings", unreal.TextureMipGenSettings.TMGS_FROM_TEXTURE_GROUP) + tex.set_editor_property("never_stream", False) + if tex.get_editor_property("mip_gen_settings") != unreal.TextureMipGenSettings.TMGS_FROM_TEXTURE_GROUP \ + or tex.get_editor_property("never_stream"): + errors += 1 + unreal.log_error("FixTextureMips : echec sur " + pkg) + continue + if not unreal.EditorAssetLibrary.save_loaded_asset(tex, only_if_is_dirty=False): + errors += 1 + unreal.log_error("FixTextureMips : sauvegarde impossible " + pkg) + unreal.log_warning("FixTextureMips : {} texture(s) modifiee(s), {} erreur(s)".format(len(candidates) - errors, errors))