CommunityImagemgithub.com

TraX22/modelagem-blender

Constrói e edita modelos 3D no Blender do usuário através de uma conexão Blender (servidor oficial BlenderLab) - inspecionando a cena, escrevendo código bpy em pequenas etapas verificadas, escala do mundo real, nomes limpos, modificadores e verificando o resultado com capturas de tela da viewport. Use quando o usuário pedir para modelar, construir, criar, corrigir ou alterar um objeto ou cena no Blender, ou para fazer um modelo 3D a partir de uma descrição ou imagem de referência.

O que é modelagem-blender?

Esta skill permite que um agente de IA construa e edite modelos 3D diretamente no Blender em execução do usuário, usando uma conexão BlenderLab. Ela é útil para desenvolvedores e artistas 3D que desejam automatizar tarefas de modelagem, criar protótipos rápidos ou gerar objetos 3D a partir de descrições textuais ou imagens de referência. A skill suporta a plataforma Blender, utilizando a API Python (bpy) para executar código, inspecionar cenas e aplicar modificadores. O agente trabalha em um loop estruturado: primeiro examina a cena, planeja o modelo em partes nomeadas com tamanhos reais, constrói em estágios pequenos e verificáveis, captura a viewport para verificar visualmente e relata o que foi feito. As regras incluem não deletar objetos que não criou, nomear objetos adequadamente, usar escala real (1 unidade = 1 metro), preferir operações de dados a operadores de contexto, e verificar o resultado final com uma função de verificação que analisa renderizações. A skill também fornece padrões para construir diferentes formas geométricas, como primitivas, modificadores de matriz, parafuso, espelho e superfícies de subdivisão, além de orientações para trabalhar com imagens de referência.

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

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Modelagem no Blender

Você trabalha dentro do Blender em execução do usuário através da conexão BlenderLab. Você não pode ver a viewport a menos que peça uma captura, e cada alteração que faz é real: ela é aplicada no arquivo que o usuário tem aberto.

Sua conexão Blender

Você trabalha através do BlenderLab, o servidor oficial do Blender Lab; suas ferramentas são blenderlab_…:

  • O que está na cena: get_objects_summary; um objeto em detalhe: get_object_detail_summary (por name). O arquivo, salvo ou não, e seu caminho: get_blendfile_summary_path_info.
  • Executar Python: execute_blender_code. Nada persiste entre chamadas (carregue seus auxiliares em cada script); o que o script imprime retorna, e um dicionário atribuído a result retorna como dados. Sua proteção recusa alguns operadores (sair do Blender, redefinições de fábrica).
  • Ver a viewport 3D: get_screenshot_of_area_as_image com area_ui_type: "VIEW_3D" (a janela inteira: get_screenshot_of_window_as_image); aponte para um objeto: jump_to_view3d_object_by_name.
  • Verificar a API Python: get_python_api_docs (um identificador como bpy.types.BevelModifier), search_api_docs; o manual: search_manual_docs.
  • Renderização rápida: render_thumbnail_to_path, render_viewport_to_path; a imagem vai para a pasta temporária do Blender e o resultado fornece o caminho real.
  • Exportar: código (bpy.ops.export_scene.gltf, bpy.ops.export_scene.fbx).
  • Bibliotecas de ativos e geradores 3D: nenhuma. Construa o objeto ou peça ao usuário um arquivo para importar.

Se essas ferramentas não estiverem disponíveis, pare e avise: o usuário precisa instalar a conexão BlenderLab (Ferramentas → Conexões), fornecê-la a este agente e ter o Blender aberto com o servidor do add-on iniciado. Não descreva o trabalho como concluído quando você não pôde fazê-lo.

O ciclo

Trabalhe nesta ordem, sempre. Pular as etapas de olhar primeiro e verificar depois é como os modelos ficam errados sem que ninguém perceba.

  1. Olhe. A ferramenta de cena para o que existe (coleções, objetos, câmera ativa). Para um objeto que você tocará, a ferramenta de detalhe do objeto. Nunca presuma uma cena vazia.
  2. Planeje em partes. Divida o modelo em partes nomeadas com tamanhos em metros e uma posição cada ("assento 0,45 × 0,45 × 0,04 em z = 0,45"). Declare o plano em uma lista curta antes de construir algo maior do que alguns objetos.
  3. Construa em estágios. Duas ou três partes relacionadas por chamada execute_blender_code (paredes, plinto e telhado; porta e janelas), 30-90 linhas. Uma parte única por chamada quando for complicada (um booleano, uma superfície curvada) ou logo após uma falha: um traceback em um script de 200 linhas diz pouco e deixa a cena meio alterada.
  4. Verifique nos marcos. Capture a viewport 3D assim que os volumes principais estiverem no lugar, assim que os detalhes estiverem prontos e no final, e compare com o plano: proporções, partes tocando onde deveriam, nada flutuando, nada dentro de outra coisa. Entre capturas, verifique com os números que seu código imprimiu. Corrija antes de prosseguir. Se a captura retornar marcada como não visível para você, seu modelo não pode ver imagens: verifique com números (dimensões, posições, contagens) e diga que você não pôde olhar.
  5. Relate. Diga o que você construiu, os nomes dos objetos, as dimensões e qualquer coisa que difira do que foi solicitado.

Seu orçamento de etapas

Uma tarefa fornece um número limitado de rodadas de ferramentas (15 a menos que o usuário aumente), e cada chamada gasta uma: abrir uma skill, um resumo da cena, uma chamada de construção, uma captura.

  • Abra as skills que você precisa de uma vez, juntas, e seus arquivos de referência apenas se você for usá-los. Não reabra uma skill que você já abriu nesta conversa.
  • Planeje os estágios contra o orçamento antes da primeira chamada de construção: cerca de 1 rodada para olhar, 8-9 para construir, 3 capturas, 1 de sobra para uma correção. Ordene-os para que as partes mais visíveis venham primeiro.
  • Quando uma captura e um resumo não dependem um do outro, peça ambos em uma rodada.
  • Se a solicitação não couber, diga no plano, termine uma parte coerente dela bem e termine com a lista exata do que falta, para que o usuário precise apenas dizer "continue". Nunca pare no meio de uma parte.

Regras que evitam a maioria das falhas

  • Não delete ou sobrescreva o que você não criou a menos que o usuário tenha pedido. Sem "selecionar tudo, deletar" para obter uma cena limpa; sem read_factory_settings. Se o cubo padrão estiver no caminho, pergunte ou trabalhe ao lado dele.
  • Coloque seu trabalho em sua própria coleção, nomeada após o modelo, e dê a cada objeto e malha um nome real (Cadeira_Perna_DT, não Cube.004). Etapas posteriores encontram objetos por nome.
  • Escala real. 1 unidade = 1 metro. Uma porta tem 2,0 m de altura, uma caneca 0,10 m. Verifique obj.dimensions após a construção, em vez de confiar nos números que você digitou.
  • Transformações: defina location, rotation_euler (radianos: use math.radians) e scale diretamente; aplique escala antes de chanfros ou exportações (consulte a referência).
  • Prefira dados a operadores. bpy.data e bmesh funcionam independentemente do que está selecionado; bpy.ops depende do contexto e falha de maneiras difíceis de ler. Quando você precisar usar um operador, selecione e ative o objeto explicitamente primeiro.
  • Torne scripts reexecutáveis. Procure um objeto pelo nome e reutilize ou substitua-o, em vez de criar Perna.001, Perna.002 a cada tentativa.
  • Envie o que você precisa. Termine uma chamada com um print curto de nomes, dimensões ou contagens (no BlenderLab, ou um dicionário atribuído a result) para que a próxima etapa comece a partir de fatos.
  • Não tem certeza de um nome de API? A API Python do Blender muda entre versões. Verifique-a com a ferramenta de API da sua conexão antes de adivinhar e leia a versão de bpy.app.version_string uma vez no início.

Padrões para primitivas, bmesh, modificadores, suavização, curvas, arrays e parenting estão em references/bpy-patterns.md. Abra-o antes de escrever o primeiro script de uma tarefa.

Escolhendo como construir uma forma

FormaAbordagem
Caixas, cilindros, móveis, arquitetura, adereçosPrimitivas, escaladas e posicionadas; modificador Bevel para bordas
Qualquer coisa repetida (cerca, escadas, dentes de uma engrenagem)Uma parte + modificador Array, ou um loop que vincula a mesma malha
Objetos redondos ou torneados (vaso, vidro, garrafa, roda)Um perfil + modificador Screw, ou uma curva com bisel
Tubos, cabos, alças, trilhosUma curva com bevel_depth
Coisas simétricas (veículo, blockout de personagem)Metade do modelo + modificador Mirror
Texto, logotipos, moedas, placasObjeto de texto ou curva, extrudado, depois convertido em malha
Formas suaves e orgânicasBlockout de primitivas + Subdivision Surface; diga honestamente que o detalhe esculpido é limitado desta forma

Mantenha os modificadores não aplicados durante a iteração (eles permanecem editáveis) e diga isso no relatório; aplique-os apenas para exportação ou quando uma etapa posterior precisar da geometria real.

Trabalhando a partir de uma imagem de referência

  • Descreva o que você vê primeiro: as partes, suas proporções relativas umas às outras e a contagem de qualquer coisa repetida. Construa a partir dessa lista.
  • A imagem decide, não números inventados para ela. Leia as proporções como razões (largura para altura para profundidade; quanto da altura total é telhado; quão larga é uma porta em relação à sua parede), escolha uma medida para ancorar a escala (a do usuário, ou um tamanho razoável do mundo real) e derive todos os outros tamanhos das razões. Se o texto da solicitação fornecer tamanhos que contradizem a imagem, siga a imagem e diga isso.
  • Quando for solicitado a escrever um prompt ou um briefing a partir de uma referência, descreva formas, proporções, cores e contagens. Não invente tamanhos em metros: quem construir a partir do briefing os seguirá em vez da imagem.
  • Construa o que a referência mostra. Sem base, adereços ou decoração que ela não tenha, a menos que o usuário tenha pedido.
  • Após o blockout, e novamente antes de relatar, coloque a vista no ângulo da referência, capture-a e compare parte por parte: silhueta, proporções, onde cada parte se senta, cores. Corrija o que diferir; o que você não puder corrigir, liste no relatório. Espere duas ou três rodadas de correção; isso é normal, não uma falha.
  • O enquadramento, a luz e as cores são medidos, não julgados a olho: veja "A verificação final" abaixo. Ela compara sua renderização com o arquivo de referência quando você fornece o nome do arquivo.
  • Uma referência anexada a uma mensagem anterior pode não estar mais visível para você. Se você não puder vê-la, diga e peça novamente, em vez de trabalhar de memória.

A verificação final (sempre sua última chamada)

Você não vê suas renderizações: as ferramentas de renderização escrevem um arquivo e fornecem seu caminho, e uma captura da viewport não mostra a luz da cena. Um modelo que relata a partir do que intencionou, não do que resultou, entrega imagens brancas ou pretas sem saber. Portanto, a última chamada execute_blender_code de cada tarefa que constrói ou altera um modelo é final_check, e sua saída impressa vai para seu relatório como está.

Ela faz uma pequena renderização a partir da câmera da cena, divide-a em fundo (as cores dos quatro cantos) e modelo (o resto), e imprime: quanto do quadro o modelo preenche, seu brilho, saturação e cores principais, os objetos sem material, os objetos cuja geometria está quebrada (faces unidas aos vértices errados) e uma linha PROBLEM: para cada coisa que está errada. Com o nome do arquivo da referência, elamede a mesma imagem da mesma forma e compara enquadramento, brilho, cor e quão sólida é a silhueta.

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

BACKDROP_WORDS = ("ground", "backdrop", "floor", "sky", "shadow")

def subject_stats(path, size=128):
    """Uma imagem dividida em fundo (as cores de seus quatro cantos) e assunto (o resto):
    quanto da imagem o assunto ocupa, sua caixa, seu brilho, saturação e cores."""
    img = bpy.data.images.load(path, check_existing=False)
    w, h = img.size
    sw, sh = (size, max(8, round(size * h / w))) if w >= h else (max(8, round(size * w / h)), size)
    img.scale(sw, sh)
    px = np.empty(len(img.pixels), dtype=np.float32)
    img.pixels.foreach_get(px)
    bpy.data.images.remove(img)
    rgb = px.reshape(sh, sw, 4)[:, :, :3]                       # linha 0 é a parte inferior da imagem
    cy, cx = max(2, sh // 8), max(2, sw // 8)
    corners = 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)])
    # Fundo = próximo de uma das cores dos cantos (um gradiente ou uma sombra suave ainda conta).
    q = np.unique(np.round(corners * 8).astype(int), axis=0)
    flat = rgb.reshape(-1, 3)
    dist = np.min(np.abs(flat[:, None, :] * 8 - q[None, :, :]).max(axis=2), axis=1)
    subject = (dist > 1.0).reshape(sh, sw)
    share = float(subject.mean())
    whole = flat @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
    out = {"size": (w, h), "image_brightness": round(float(whole.mean()), 3), "subject_pct": round(100 * share, 1)}
    if share < 0.005:
        return out
    ys, xs = np.nonzero(subject)
    out["box"] = {"width": round(float(xs.max() - xs.min() + 1) / sw, 2), "height": round(float(ys.max() - ys.min() + 1) / sh, 2),
                  "center": (round(float(xs.max() + xs.min() + 1) / 2 / sw, 2), round(float(ys.max() + ys.min() + 1) / 2 / sh, 2))}
    sub = rgb[subject]
    lum = sub @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
    mx, mn = sub.max(axis=1), sub.min(axis=1)
    out["brightness"] = 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["burnt_pct"] = round(100 * float((lum > 0.97).mean()), 1)
    keys = (np.clip((sub * 5.999).astype(int), 0, 5) * np.array([36, 6, 1])).sum(axis=1)
    counts = np.bincount(keys, minlength=216)
    out["colors"] = [("#%02x%02x%02x" % tuple(int(round(float(c) * 255)) for c in sub[keys == k].mean(axis=0)),
                      int(round(100 * counts[k] / len(keys)))) for k in counts.argsort()[::-1][:5] if counts[k]]
    return out

# Onde uma instalação HydraOps mantém seus dados (storage/uploads depende dela): a pasta
# de dados que foi informada (HYDRA_DATA_DIR), a pasta do instalador por sistema e um clone
# do repositório, que mantém seus dados dentro de si quando executado a partir do código-fonte.
UPLOAD_ROOTS = [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")]
UPLOAD_FOLDERS = [os.path.join(r, "storage", "uploads") for r in UPLOAD_ROOTS if r]

def find_reference(file_name):
    """O caminho de uma imagem anexada NESTA conversa, nomeada como a conversa a mostra:
    "storage/uploads/1791118524833-house.png", o simples "1791118524833-house.png" ou um caminho completo.
    None quando não está lá."""
    raw = str(file_name or "").replace("\\", "/").strip()
    name = os.path.basename(raw)
    if not name or os.path.splitext(name)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
        return None
    if os.path.isabs(raw) and os.path.isfile(raw):
        return os.path.normpath(raw)
    for folder in UPLOAD_FOLDERS:
        path = os.path.join(folder, name)
        if os.path.isfile(path):
            return path
    # O nome armazenado começa com um timestamp que é fácil de copiar com um dígito errado:
    # recorrer ao upload mais recente com o mesmo nome original após o timestamp.
    m = re.match(r"^\d{10,}-(.+)$", name)
    if m:
        pattern = re.compile(r"^\d{10,}-" + re.escape(m.group(1)) + "$")
        newest = None
        for folder in UPLOAD_FOLDERS:
            try:
                entries = os.listdir(folder)
            except OSError:
                continue
            for entry in entries:
             

…

[truncado para tradução]

Individual skills in this repo

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

Habilidades Relacionadas