CommunityImagegithub.com

TraX22/blender-modeling

Construit et modifie des modèles 3D dans Blender via une connexion BlenderLab (le serveur officiel BlenderLab) – inspection de la scène, écriture de code bpy par petites étapes vérifiées, échelle réelle, nommage propre, modificateurs et vérification du résultat par captures d'écran du viewport.

Qu'est-ce que blender-modeling ?

Cette compétence permet à un agent IA de modéliser, éditer et créer des objets 3D dans Blender via la connexion BlenderLab. Elle suit un cycle systématique (observer, planifier, construire par étapes, vérifier, rapporter) pour éviter les erreurs. L'agent inspecte la scène avec des outils comme `get_objects_summary`, exécute du code Python (`execute_blender_code`), capture le viewport (`get_screenshot_of_area_as_image`) et effectue une vérification finale automatisée avec `final_check` qui compare le rendu à une image de référence. Les règles clés incluent : ne pas supprimer ce qui n'a pas été créé, placer le travail dans sa propre collection, utiliser une échelle réelle (1 unité = 1 mètre), préférer `bpy.data` aux opérateurs, et rendre les scripts ré-exécutables. La compétence prend en charge la construction à partir de primitives, de courbes, de modificateurs (Array, Mirror, Screw, Subdivision Surface) et de Bmesh, avec des modèles pour chaque cas. Elle gère également le travail à partir d'images de référence en mesurant les proportions et en comparant le rendu final.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/TraX22/HydraOps-Skills/tree/HEAD/blender-modeling

Installed? Explore more Image skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modélisation dans Blender

Vous travaillez dans Blender ouvert de l'utilisateur via la connexion BlenderLab. Vous ne pouvez pas voir le viewport sauf si vous demandez une capture, et chaque modification que vous faites est réelle : elle se retrouve dans le fichier que l'utilisateur a ouvert.

Votre connexion Blender

Vous travaillez via BlenderLab, le serveur officiel de Blender Lab ; ses outils sont blenderlab_… :

  • Qu'y a-t-il dans la scène : get_objects_summary ; un objet en détail : get_object_detail_summary (par name). Le fichier, sauvegardé ou non, et son chemin : get_blendfile_summary_path_info.
  • Exécuter Python : execute_blender_code. Rien ne persiste entre les appels (transportez vos aides dans chaque script) ; ce que le script affiche est renvoyé, et un dictionnaire assigné à result est renvoyé comme données. Sa garde refuse quelques opérateurs (quitter Blender, réinitialisations d'usine).
  • Voir la vue 3D : get_screenshot_of_area_as_image avec area_ui_type: "VIEW_3D" (la fenêtre entière : get_screenshot_of_window_as_image) ; cibler un objet : jump_to_view3d_object_by_name.
  • Consulter l'API Python : get_python_api_docs (un identifiant comme bpy.types.BevelModifier), search_api_docs ; le manuel : search_manual_docs.
  • Rendu rapide : render_thumbnail_to_path, render_viewport_to_path ; l'image va dans le dossier temporaire de Blender et le résultat donne le chemin réel.
  • Export : du code (bpy.ops.export_scene.gltf, bpy.ops.export_scene.fbx).
  • Bibliothèques d'actifs et générateurs 3D : aucun. Construisez l'objet, ou demandez à l'utilisateur un fichier à importer.

Si ces outils ne sont pas disponibles, arrêtez-vous et dites-le : l'utilisateur doit installer la connexion BlenderLab (Outils → Connexions), la donner à cet agent, et avoir Blender ouvert avec le serveur du module complémentaire démarré. Ne décrivez pas le travail comme terminé si vous n'avez pas pu le faire.

La boucle

Travaillez dans cet ordre, à chaque fois. Sauter les étapes de regarder d'abord et après est la façon dont les modèles finissent faux sans que personne ne le remarque.

  1. Regardez. L'outil de scène pour ce qui existe (collections, objets, caméra active). Pour un objet que vous toucherez, l'outil de détail d'objet. Ne supposez jamais une scène vide.
  2. Planifiez en parties. Divisez le modèle en parties nommées avec des tailles en mètres et une position chacune ("siège 0,45 × 0,45 × 0,04 à z = 0,45"). Énoncez le plan en une courte liste avant de construire quoi que ce soit de plus grand que quelques objets.
  3. Construisez par étapes. Deux ou trois parties liées par appel execute_blender_code (murs, socle et toit ; porte et fenêtres), 30–90 lignes. Une seule partie par appel lorsque c'est délicat (un booléen, une surface courbe) ou juste après un échec : une trace dans un script de 200 lignes vous dit peu et laisse la scène à moitié modifiée.
  4. Vérifiez aux jalons. Capturez la vue 3D une fois que les volumes principaux sont en place, une fois que les détails sont ajoutés, et à la fin, et comparez-la réellement avec le plan : proportions, parties qui se touchent là où elles devraient, rien qui flotte, rien à l'intérieur d'autre chose. Entre les captures, vérifiez avec les nombres que votre code a affichés. Réparez avant de continuer. Si la capture revient marquée comme non visible pour vous, votre modèle ne peut pas voir les images : vérifiez avec des nombres à la place (dimensions, positions, comptes), et dites que vous n'avez pas pu regarder.
  5. Rapportez. Dites ce que vous avez construit, les noms des objets, les dimensions, et tout ce qui diffère de ce qui a été demandé.

Votre budget d'étapes

Une tâche vous donne un nombre limité de tours d'outils (15 sauf si l'utilisateur l'a augmenté), et chaque appel en dépense un : ouvrir une compétence, un résumé de scène, un appel de construction, une capture.

  • Ouvrez les compétences dont vous avez besoin une fois, ensemble, et leurs fichiers de référence seulement si vous allez les utiliser. Ne rouvrez pas une compétence que vous avez déjà ouverte dans cette conversation.
  • Planifiez les étapes par rapport au budget avant le premier appel de construction : environ 1 tour pour regarder, 8–9 pour construire, 3 captures, 1 de réserve pour une réparation. Ordonnez-les pour que les parties les plus visibles viennent en premier.
  • Lorsqu'une capture et un résumé ne dépendent pas l'un de l'autre, demandez les deux en un tour.
  • Si la demande ne tient pas, dites-le dans le plan, terminez bien une partie cohérente de celle-ci et terminez avec la liste exacte de ce qui reste, pour que l'utilisateur n'ait qu'à dire "continuer". Ne vous arrêtez jamais au milieu d'une partie.

Règles qui préviennent la plupart des échecs

  • Ne supprimez ni n'écrasez ce que vous n'avez pas créé sauf si l'utilisateur l'a demandé. Pas de "sélectionner tout, supprimer" pour obtenir une scène propre ; pas de read_factory_settings. Si le cube par défaut gêne, demandez ou travaillez à côté.
  • Mettez votre travail dans sa propre collection, nommée d'après le modèle, et donnez à chaque objet et maillage un vrai nom (Chaise_Pied_AG, pas Cube.004). Les étapes ultérieures trouvent les objets par leur nom.
  • Échelle réelle. 1 unité = 1 mètre. Une porte mesure 2,0 m de haut, une tasse 0,10 m. Vérifiez obj.dimensions après la construction au lieu de faire confiance aux nombres que vous avez tapés.
  • Transformations : définissez location, rotation_euler (radians : utilisez math.radians) et scale directement ; appliquez l'échelle avant les biseaux ou les exports (voir la référence).
  • Préférez les données aux opérateurs. bpy.data et bmesh fonctionnent indépendamment de ce qui est sélectionné ; bpy.ops dépend du contexte et échoue de manières difficiles à lire. Lorsque vous devez utiliser un opérateur, sélectionnez et activez d'abord l'objet explicitement.
  • Rendez les scripts ré-exécutables. Cherchez un objet par son nom et réutilisez-le ou remplacez-le, au lieu de créer Pied.001, Pied.002 à chaque tentative.
  • Renvoyez ce dont vous avez besoin. Terminez un appel par un court print des noms, dimensions ou comptes (sur BlenderLab, ou un dictionnaire assigné à result) pour que l'étape suivante parte de faits.
  • Incertain d'un nom d'API ? L'API Python de Blender change entre les versions. Vérifiez-la avec l'outil API de votre connexion avant de deviner, et lisez la version depuis bpy.app.version_string une fois au début.

Les motifs pour les primitives, bmesh, modificateurs, lissage, courbes, tableaux et parentage sont dans références/motifs-bpy.md. Ouvrez-le avant d'écrire le premier script d'une tâche.

Comment choisir de construire une forme

FormeApproche
Boîtes, cylindres, meubles, architecture, accessoiresPrimitives, mises à l'échelle et positionnées ; modificateur Biseau pour les arêtes
Tout ce qui est répété (clôture, escaliers, dents d'un engrenage)Une partie + modificateur Tableau, ou une boucle qui lie le même maillage
Objets ronds ou tournés (vase, verre, bouteille, roue)Un profil + modificateur Vis, ou une courbe avec biseau
Tuyaux, câbles, poignées, rampesUne courbe avec bevel_depth
Choses symétriques (véhicule, blocage de personnage)La moitié du modèle + modificateur Miroir
Texte, logos, pièces de monnaie, plaquesObjet de texte ou de courbe, extrudé, puis converti en maillage
Formes molles et organiquesBlocage à partir de primitives + Surface de subdivision ; dites honnêtement que le détail sculpté est limité de cette façon

Gardez les modificateurs non appliqués pendant l'itération (ils restent modifiables) et dites-le dans le rapport ; appliquez-les seulement pour l'export ou lorsqu'une étape ultérieure a besoin de la géométrie réelle.

Travailler à partir d'une image de référence

  • Décrivez d'abord ce que vous voyez : les parties, leurs proportions les unes par rapport aux autres, et le nombre de tout ce qui est répété. Construisez à partir de cette liste.
  • L'image décide, pas les nombres inventés pour elle. Lisez les proportions comme des rapports (largeur à hauteur à profondeur ; combien de la hauteur totale est le toit ; combien une porte est large par rapport à son mur), choisissez une mesure pour ancrer l'échelle (celle de l'utilisateur, ou une taille réelle sensée) et dérivez chaque autre taille à partir des rapports. Si le texte de la demande donne des tailles qui contredisent l'image, suivez l'image et dites-le.
  • Lorsqu'on vous demande d'écrire une invite ou un brief à partir d'une référence, décrivez les formes, rapports, couleurs et comptes. N'inventez pas de tailles en mètres : celui qui construira à partir du brief les suivra au lieu de l'image.
  • Construisez ce que la référence montre. Pas de base, accessoires ou décoration qu'elle n'a pas, sauf si l'utilisateur l'a demandé.
  • Après le blocage, et encore avant de rapporter, mettez la vue à l'angle de la référence, capturez-la et comparez partie par partie : silhouette, proportions, où chaque partie se trouve, couleurs. Réparez ce qui diffère ; ce que vous ne pouvez pas réparer, listez-le dans le rapport. Attendez-vous à deux ou trois tours de correction ; c'est normal, pas un échec.
  • Le cadrage, la lumière et les couleurs sont mesurés, pas jugés à l'œil : voir "La vérification finale" ci-dessous. Elle compare votre rendu avec le fichier de référence lorsque vous lui donnez le nom du fichier.
  • Une référence attachée à un message antérieur peut ne plus être visible pour vous. Si vous ne pouvez pas la voir, dites-le et demandez-la à nouveau au lieu de travailler de mémoire.

La vérification finale (toujours votre dernier appel)

Vous ne voyez pas vos rendus : les outils de rendu écrivent un fichier et vous donnent son chemin, et une capture du viewport ne montre pas la lumière de la scène. Un modèle qui rapporte ce qu'il avait prévu, pas ce qui est sorti, livre des images blanches ou noires sans le savoir. Ainsi, le dernier appel execute_blender_code de chaque tâche qui construit ou modifie un modèle est final_check, et sa sortie imprimée va dans votre rapport telle quelle.

Il fait un petit rendu depuis la caméra de la scène, le divise en arrière-plan (les couleurs des quatre coins) et modèle (le reste), et affiche : combien du cadre le modèle remplit, sa luminosité, saturation et couleurs principales, les objets sans matériau, les objets dont la géométrie est cassée (faces jointes aux mauvais sommets), et une ligne PROBLEME: pour chaque chose qui ne va pas. Avec le nom du fichier de la référence, il mesure cette image de la même manière et compare le cadrage, la luminosité, la couleur et la solidité de la silhouette.

import bpy, os, re
import numpy as np
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view

MOTS_ARRIERE_PLAN = ("sol", "fond", "plancher", "ciel", "ombre")

def stats_sujet(chemin, taille=128):
    """Une image divisée en arrière-plan (les couleurs de ses quatre coins) et sujet (le reste) :
    combien de l'image le sujet prend, sa boîte, sa luminosité, saturation et couleurs."""
    img = bpy.data.images.load(chemin, check_existing=False)
    l, h = img.size
    sl, sh = (taille, max(8, round(taille * h / l))) if l >= h else (max(8, round(taille * l / h)), taille)
    img.scale(sl, sh)
    px = np.empty(len(img.pixels), dtype=np.float32)
    img.pixels.foreach_get(px)
    bpy.data.images.remove(img)
    rgb = px.reshape(sh, sl, 4)[:, :, :3]                       # la ligne 0 est le bas de l'image
    cy, cx = max(2, sh // 8), max(2, sl // 8)
    coins = np.concatenate([rgb[:cy, :cx].reshape(-1, 3), rgb[:cy, -cx:].reshape(-1, 3),
                              rgb[-cy:, :cx].reshape(-1, 3), rgb[-cy:, -cx:].reshape(-1, 3)])
    # Arrière-plan = proche d'une des couleurs des coins (un dégradé ou une ombre douce compte toujours).
    q = np.unique(np.round(coins * 8).astype(int), axis=0)
    plat = rgb.reshape(-1, 3)
    dist = np.min(np.abs(plat[:, None, :] * 8 - q[None, :, :]).max(axis=2), axis=1)
    sujet = (dist > 1.0).reshape(sh, sl)
    part = float(sujet.mean())
    total = plat @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
    out = {"taille": (l, h), "luminosite_image": round(float(total.mean()), 3), "pourcentage_sujet": round(100 * part, 1)}
    if part < 0.005:
        return out
    ys, xs = np.nonzero(sujet)
    out["boite"] = {"largeur": round(float(xs.max() - xs.min() + 1) / sl, 2), "hauteur": round(float(ys.max() - ys.min() + 1) / sh, 2),
                  "centre": (round(float(xs.max() + xs.min() + 1) / 2 / sl, 2), round(float(ys.max() + ys.min() + 1) / 2 / sh, 2))}
    sub = rgb[sujet]
    lum = sub @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
    mx, mn = sub.max(axis=1), sub.min(axis=1)
    out["luminosite"] = round(float(lum.mean()), 3)
    out["saturation"] = round(float(np.where(mx > 1e-4, (mx - mn) / np.maximum(mx, 1e-4), 0.0).mean()), 3)
    out["pourcentage_brûlé"] = round(100 * float((lum > 0.97).mean()), 1)
    cles = (np.clip((sub * 5.999).astype(int), 0, 5) * np.array([36, 6, 1])).sum(axis=1)
    comptes = np.bincount(cles, minlength=216)
    out["couleurs"] = [("#%02x%02x%02x" % tuple(int(round(float(c) * 255)) for c in sub[cles == k].mean(axis=0)),
                      int(round(100 * comptes[k] / len(cles)))) for k in comptes.argsort()[::-1][:5] if comptes[k]]
    return out

# Où une installation HydraOps stocke ses données (storage/uploads en dépend) : le dossier de données
# qui lui a été indiqué (HYDRA_DATA_DIR), le dossier de l'installateur par système, et un clone du
# dépôt, qui conserve ses données à l'intérieur de lui-même lorsqu'il est exécuté à partir de la source.
RACINES_TELEVERSEMENT = [os.environ.get("HYDRA_DATA_DIR", ""),
                os.path.join(os.environ.get("APPDATA", ""), "HydraOps", "data"),
                os.path.expanduser("~/Library/Application Support/HydraOps/data"),
                os.path.expanduser("~/.config/HydraOps/data"),
                os.path.expanduser("~/HydraOps")]
DOSSIERS_TELEVERSEMENT = [os.path.join(r, "storage", "uploads") for r in RACINES_TELEVERSEMENT if r]

def trouver_reference(nom_fichier):
    """Le chemin d'une image attachée dans CETTE conversation, nommée comme la conversation la montre :
    "storage/uploads/1791118524833-house.png", le "1791118524833-house.png" nu, ou un chemin complet.
    None lorsqu'elle n'est pas là."""
    brut = str(nom_fichier or "").replace("\\", "/").strip()
    nom = os.path.basename(brut)
    if not nom or os.path.splitext(nom)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
        return None
    if os.path.isabs(brut) and os.path.isfile(brut):
        return os.path.normpath(brut)
    for dossier in DOSSIERS_TELEVERSEMENT:
        chemin = os.path.join(dossier, nom)
        if os.path.isfile(chemin):
            return chemin
    # Le nom stocké commence par un horodatage facile à copier avec un chiffre de travers :
    # fallback vers le téléversement le plus récent avec le même nom original après l'horodatage.
    m = re.match(r"^\d{10,}-(.+)$", nom)
    if m:
        motif = re.compile(r"^\d{10,}-" + re.escape(m.group(1)) + "$")
        plus_recent = None
        for dossier in DOSSIERS_TELEVERSEMENT:
            try:
                entrees = os.listdir(dossier)
            except OSError:
                continue
            for entree in entrees:

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Skills associés