141 lines
12 KiB
Markdown
141 lines
12 KiB
Markdown
# Slice Wrap — add-on Blender : surface propre à partir d'un maillage troué, wrap, nettoyage et analyse
|
||
|
||
L'utilisateur parle **français** : répondre et documenter en français. Libellés d'interface Blender en anglais
|
||
(convention Blender), descriptions / docstrings en français. Documentation en deux langues, à tenir à jour
|
||
ensemble : `README.fr.md` en français, `README.md` en anglais (sa traduction) ; de même `CHANGELOG.fr.md` /
|
||
`CHANGELOG.md` (les fiches du site et les notes de version les lisent dans les deux langues).
|
||
|
||
## But
|
||
Transformer un maillage **troué, à doubles parois ou bruité** (scan, génération IA type Pixal3D / Trellis — ex.
|
||
tête brute de 11,8 M faces) en une **surface fermée, propre et détaillée**, prête à sculpter ou à retopologiser,
|
||
puis fournir une **analyse de forme** (densité de détail, tags, direction des plis).
|
||
Né pendant le projet personnage « Chiara » (`C:\Users\a-mma\Desktop\CharacterDesign\Work3D`) : le Voxel Remesh de
|
||
Blender donnait du moiré et des fuites sur la tête générée ; un script de remplissage voxel tranche par tranche a
|
||
résolu le problème, d'où l'add-on.
|
||
|
||
**Concept à préserver** : Slice Wrap = « maillage troué → surface propre + analyse ». Aucune logique de topologie /
|
||
flow ici : la retopologie guidée est un projet séparé, **Slice Retopo**
|
||
(`E:\0000WorkTechnique\0002dev\slice_retopo_addon`), qui lit les attributs produits par Slice Wrap.
|
||
|
||
## Pipeline (bouton « Slice Wrap » = étapes 1 → 3, + 4 si « Despike After »)
|
||
1. **Envelope** (`core.build_envelope`) — sur la source (jamais modifiée) :
|
||
échantillonnage de surface (sommets + points sur les triangles > ½ voxel) → voxels occupés → fermeture
|
||
morphologique (`close_iter`) → **remplissage 3D global ∪ remplissage 2D tranche par tranche selon X, Y, Z**
|
||
(un trou ne fait fuir que les tranches qu'il traverse) → ouverture (`open_iter`, filaments) → surface en cubes
|
||
→ **Voxel Remesh de Blender** sur ce volume déjà plein (aucune fuite possible) → plus grande pièce → lissage
|
||
de Taubin (`smooth_iter`). Numpy pur (pas de scipy / skimage dans le Python de Blender).
|
||
2. **Wrap Setup** — gonflement le long des normales (défaut 2 voxels) pour que l'enveloppe soit entièrement
|
||
**au-dessus** de la peau, **Multires** (niveaux, Catmull-Clark) puis **Shrinkwrap** : *Project*, aucun axe
|
||
(= le long des normales), **Negative seul**, Limit (défaut 4 × gonflement), Cull Off, Offset 0.
|
||
3. **Freeze** — copie évaluée → `multires_reshape` (la projection est figée DANS les niveaux du Multires : base
|
||
légère + détails) ; **Repair Missed Rays** : chaque sommet est comparé avant / après projection ; resté sur
|
||
place (< 0,3 × gonflement → aiguille) ou parti trop loin (> 2 × la moyenne de ses voisins → creux sur une paroi
|
||
interne) = raté, reconstruit par diffusion depuis ses voisins réussis.
|
||
4. **Despike** (manuel ou en fin de chaîne) — ne corrige qu'un sommet **isolé** : écart à la moyenne des voisins
|
||
> Distance (0 = longueur moyenne d'arête) ET > Isolation × l'écart moyen des voisins ; Strength, Iterations ;
|
||
filtres par **groupes de sommets** (poids = intensité ; liste vide = toute la surface ; liste non vide mais
|
||
aucun groupe trouvé = refus) ; **Protect Detail** (× (1 − détail)).
|
||
5. **Analyze** (optionnel, ne modifie pas la géométrie) — tenseur de covariance des normales sur `Neighborhood`
|
||
anneaux (valeurs propres λ1 ≥ λ2 ≥ λ3) + courbure moyenne signée :
|
||
`sw_detail` (λ2+λ3 normalisé), `sw_anisotropy` ((λ2−λ3)/(λ2+λ3)), `sw_curvature`, `sw_shape`
|
||
(0 Flat, 1 Bulge, 2 Hollow, 3 Ridge, 4 Fold, 5 Saddle), `sw_flow` (vecteur propre de λ3 = direction du pli),
|
||
couleurs `SW_Detail_Color` / `SW_Shape_Color`, groupes `SW_Detail`, `SW_Bulge`, `SW_Hollow`, `SW_Ridge`,
|
||
`SW_Fold`, `SW_Saddle`. Écrit sur les N premiers sommets du maillage évalué = sommets de base.
|
||
|
||
## Décisions et pourquoi
|
||
- **Pas de vrai modificateur** (impossible en Python) → opérateurs + paramètres mémorisés sur le résultat
|
||
(`slice_wrap_source`, `slice_wrap_pitch`, `slice_wrap_inflate`) + **Regenerate** (recalcul sur place).
|
||
- **Nom du résultat** = source + **Suffix** (défaut `_slicewrapped`), **numéroté .001, .002…** : aucun objet
|
||
existant n'est jamais écrasé (avant la v1.3, relancer remplaçait le maillage existant — corrigé). Seul
|
||
Regenerate recalcule l'objet actif. Refusé si l'enveloppe est déjà wrappée (Multires / Shrinkwrap).
|
||
- **Collection** optionnelle (défaut cochée, `SliceWrapCollection`) : réutilisée si elle existe (contenu
|
||
intact), sinon créée et liée à la scène ; décochée → collections de la source.
|
||
- **Voxel Relative / Absolute — conservé (décision utilisateur, 2026-10-17)** : Relative (défaut 0,6 % de la plus
|
||
grande dimension, ≈ 170 voxels) rend le réglage indépendant de l'échelle (têtes IA normalisées à ~1 m, tête
|
||
réelle 23 cm, main C4D importée à 3,7 m) ; Absolute donne la **même densité physique à plusieurs parties d'un
|
||
même personnage** (tête, mains, pieds à raccorder). L'équivalent absolu du mode Relative est affiché.
|
||
Possible plus tard : ranger Absolute dans une option avancée repliée.
|
||
- Défaut 0,6 % (et non 0,4 %) : une base trop dense fait « voir » le grain de la source à la projection.
|
||
- Despike par **isolation** et non par seuil seul : le seuil seul attrapait 8 % des sommets (oreilles, paupières).
|
||
- Réparation des ratés **au Freeze** (on connaît exactement les sommets ratés) plutôt que deviner après coup.
|
||
- Couleurs suffixées `_Color` : groupes de sommets et attributs partagent le même espace de noms.
|
||
- **Plus de réglage Target (décision utilisateur, 2026-10-17)** : il compliquait l'interface pour rien. Tout
|
||
s'applique à l'objet actif **et sélectionné** (`_obj_mode_mesh`) ; Wrap Setup projette sur `slice_wrap_source`.
|
||
- **Calculs longs = générateurs + opérateur modal** (`_LongOperator`) : l'API bpy n'est pas thread-safe, donc pas
|
||
de thread. Les étapes (`step_*`, `core.build_envelope`, `despike`, `fix_missed_rays`, `analyze_shape`…) cèdent
|
||
la main en produisant (fraction, texte) ; `core.sub` imbrique les progressions, `core.run` exécute d'une traite
|
||
(scripts / tests, `bpy.ops` sans INVOKE). Depuis l'UI : confirmation (`invoke_props_dialog`, préférence
|
||
`confirm_long`), puis timer 0,02 s, tranches de 0,15 s, saisie absorbée, Échap → `gen.close()` : chaque étape
|
||
nettoie dans `except BaseException` / `finally` (aucun demi-résultat ; la scène n'est modifiée qu'à la fin).
|
||
Progression : `workspace.status_text_set(fonction)` + boîte en haut du panneau. Les étapes lisent `bpy.context`
|
||
à chaque usage (le contexte d'un opérateur n'est plus valide aux ticks suivants). Pas de `REGISTER` sur ces
|
||
opérateurs (un redo relancerait un modal). Surcoût mesuré ~10 %. Appels Blender d'un seul tenant (Multires
|
||
subdivide / Reshape, Voxel Remesh) : l'écran reste figé sur leur libellé.
|
||
|
||
## Fichiers
|
||
- `__init__.py` : réglages (`SliceWrapSettings`), étapes (`step_envelope`, `step_wrap`, `step_freeze`,
|
||
`step_despike`, `step_analyze`, `_reshape_from_evaluated`, `_result_collection`), opérateurs (`run`,
|
||
`envelope`, `regenerate`, `wrap_setup`, `freeze`, `despike`, `analyze`, `show`, `group_add/remove/clear`),
|
||
panneaux (principal, Steps replié, Despike, Analyze replié), liste `SLICEWRAP_UL_groups`.
|
||
- `core.py` : voxels (`dilate`, `erode`, `_outside`, `fill`), maillage (`mesh_world_arrays`, `sample_surface`,
|
||
`voxelize`, `blocky_mesh`), nettoyage (`taubin`, `keep_largest_island`, `despike`, `group_weights`,
|
||
`fix_missed_rays`), analyse (`analyze_shape`, `write_analysis`), `unique_object_name`, `build_envelope`.
|
||
- `tests/run_tests.py` : suite (sphère trouée bruitée ; `--head` = tête réelle + rendus). Exclu du paquet.
|
||
- `blender_manifest.toml` : extension **5.1+** (décidé le 2026-10-20) ; `[build]` exclut `__pycache__/`, zips,
|
||
`.a2m/`, `.claude/`, `.git/`, `.gitignore`, `CLAUDE.md`, `tests/`, `dist/`.
|
||
- `.a2m/project.toml` : données du projet pour la chaîne d'outils a2m et le site (écrit par `a2m adopt`).
|
||
- `CHANGELOG.md` (en) / `CHANGELOG.fr.md` (fr) : historique ; la section « Unreleased » / « Non publié » donne les
|
||
notes de la prochaine release.
|
||
|
||
## Commandes
|
||
- Tests : `"C:\Program Files\Blender Foundation\Blender 5.2\blender.exe" -b -P tests/run_tests.py -- <dossier> [--head]`
|
||
→ `RESULTAT : TOUT OK` (~1 min, 29 contrôles dont annulations) ; avec `--head`, regarder `slice_wrap_head_face.png` / `_cote.png`.
|
||
**À lancer avant chaque release** (la chaîne a2m ne lance pas encore les tests de l'addon).
|
||
- Construction et publication : chaîne d'outils **a2m** (repo `ange/apc-toolchain`, remplace les anciens
|
||
`tools/Build-Package.ps1` et `tools/New-GiteaRelease.ps1`, retirés le 2026-10-20) :
|
||
`a2m validate --load` (manifest validé et addon chargé dans Blender 5.1 et 5.2, profil temporaire),
|
||
`a2m build` → `dist\slice_wrap-<version>.zip`, `a2m release [patch|minor|major]` (version du manifest, CHANGELOG,
|
||
commit, tag `v<version>`, release Gitea avec le zip et `publication.json`), `a2m release --dev` (pré-release de
|
||
test, réservée aux membres). Publication = action externe : ne la lancer qu'à la demande de l'utilisateur.
|
||
- `.gitignore` : `__pycache__/`, `dist/`, `*.zip`, rendus de test.
|
||
|
||
## Historique
|
||
Voir [CHANGELOG.fr.md](CHANGELOG.fr.md) (une entrée par version ; les changements à venir dans « Non publié »).
|
||
|
||
## Mesures de référence (Blender 5.2.1, tête Pixal3D brute 11,8 M faces)
|
||
Enveloppe 70 k faces en 11 s ; chaîne complète ~1,5–2 min ; ~93 k ratés réparés au Freeze ; Despike filtré
|
||
« Oreilles » 683 corrections vs 4 749 global ; Analyze 9 s (Flat 86 %, Ridge 5 %, Bulge 3 %, Fold 3 %, Hollow 2 %,
|
||
Saddle 1 % — hélice des oreilles et lèvres en Ridge, paupières et commissures en Fold).
|
||
|
||
## Dépôt d'extensions Blender sur a2msystemes.org (idée du 2026-10-17, prise en charge par APC)
|
||
But : les addons de l'utilisateur (Slice Wrap, Slice Retopo, suivants) installables et mis à jour via
|
||
*Preferences > Get Extensions > Repositories > Add Remote Repository*, au lieu d'*Install from Disk*.
|
||
Cette note est à l'origine du projet **APC** (*A2M Publication Contract*) : le dépôt n'est plus à écrire ici.
|
||
- **Publication** : `a2m release` (chaîne d'outils `ange/apc-toolchain`) joint à chaque release Gitea le zip et un
|
||
`publication.json` (contrat `ange/apc-spec`) ; le script de déploiement du site (`ange/site-a2msystemes-org`)
|
||
en fabrique `blender-addons/index.json` et les pages du projet. Adresse du dépôt et pages : voir le site.
|
||
- **À rappeler à l'utilisateur** : les addons déjà installés par *Install from Disk* (dépôt `user_default`) ne
|
||
reçoivent pas les mises à jour → les désinstaller puis réinstaller une fois depuis le nouveau dépôt.
|
||
|
||
## Pistes
|
||
- Option avancée repliée pour Absolute ; préréglages par type d'objet (tête, main, accessoire).
|
||
- Accélérer le remplissage (propagation par lignes / scanline au lieu d'une dilatation voxel par voxel).
|
||
- Surplombs (pavillon de l'oreille) : seconde projection *Nearest Surface Point* limitée à un groupe de sommets.
|
||
- Remaillage adaptatif (octree + Dual Contouring) pour props / LOD / cible de bake (pas pour l'animation).
|
||
- Retopologie guidée → projet **Slice Retopo** (marqueurs + retopo_tags + motifs ; voir son CLAUDE.md).
|
||
|
||
## Préférences de travail de l'utilisateur
|
||
- « Ne fais rien, explique » = aucune modification ; valider le plan avant les gros lancements ; expliquer les
|
||
paramètres (à quoi ils servent) avant de décider.
|
||
- Ne pas casser le concept de base d'un add-on en ajoutant des fonctions : sous-panneaux optionnels.
|
||
- Dans son Blender ouvert (MCP) : lecture seule d'abord, copie « au cas où », sauvegarder / restaurer la vue.
|
||
- Calculs longs dans des fenêtres PowerShell externes ; vider la VRAM ComfyUI avant chaque génération
|
||
(`~/.claude/CLAUDE.md`).
|
||
|
||
## Skills du projet (`.claude/skills/`)
|
||
- `slice-wrap-dev` — faire évoluer, tester et livrer cet add-on.
|
||
- `mesh-wrap-cleanup` — la méthode wrap et le nettoyage manifold (à la main ou avec l'add-on).
|
||
- `blender-addon-testing` — tests en arrière-plan, rendus de contrôle, paquet d'extension, pièges de l'API.
|
||
- `blender-live-mcp` — travailler dans le Blender ouvert de l'utilisateur sans rien casser.
|
||
- `ai-3d-asset-pipeline` — génération 3D par IA (ComfyUI) qui produit les maillages sources.
|