Skip to content
DouglasGDevPublic

About

Agente de modelagem 3D ao vivo no Blender com LLM local, tools reais, espelho de viewport e busca autônoma por texturas e modelos CC0

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Meshloom

Workspace de agentes de IA para modelagem e animação 3D no Blender — ao vivo.

O usuário descreve o objeto (texto, foto, link) e vê um agente sênior modelar, aplicar materiais e animar no Blender aberto na própria máquina, narrando cada decisão. Não é um gerador que devolve um .glb no fim: é um modelador trabalhando na frente do usuário, com o processo inteiro observável, interrompível e reversível.

A tese

  1. O Blender é o palco. O viewport do usuário é a saída principal do produto; a UI do Meshloom é a mesa de trabalho ao redor dele.
  2. Vivacidade é uma propriedade de sistemas, não um efeito de UI. Exige narração, despacho imediato de tools, eventos de cena, checkpoints e transparência de espera — cinco camadas que se cobrem.
  3. O LLM é o componente mais lento. Nenhum feedback visual pode depender de um turno do modelo. O Blender fala primeiro; o modelo narra e decide.
  4. Model-agnostic de verdade. O mesmo contrato funciona com OpenAI, Anthropic, Gemini, Grok, DeepSeek, Ollama, LM Studio, llama.cpp e vLLM; modelos locais recebem toolsets menores e decodificação restrita.

Não-negociável

  • Silêncio é bug: toda tool call tem narração, card e efeito visível.
  • Nada de "gera tudo e mostra no final": construção incremental em beats de 2–8 s.
  • Interromper, corrigir e reverter a qualquer momento, sem perder trabalho.
  • O agente se vê: captura o viewport e corrige proporções contra a referência.
  • O usuário vê o que o modelo vê: inspetor de contexto por turno.
  • Escopo fechado: só a coleção Meshloom, datablocks SN_*, snapshots por beat.

Arquitetura em uma tela

Electron (React/TS)  ──  Agent Runtime  ──  LLM Gateway (SSE)
        │                     │
        │                     ├── Tool Registry ── MCP Server (externo)
        │                     └── Blender Bridge ── TCP controle ──┐
        │                                                          │
        └── MessagePort (frames) ◄── worker WebP ◄── TCP visual ───┤
                                                                   ▼
                                                    Blender Addon (Python)
                                                    fila → timer main thread
                                                    bpy · diffs · GPUOffScreen
  • Dois sockets: controle (JSON, ordenado) e visual (RGBA, descartável).
  • MCP na camada de tools; o fio do Blender é um protocolo binário próprio, porque MCP não tem canal de vídeo.
  • Event sourcing em SQLite: replay, retomada, auditoria e branching.
  • Snapshot por beat + undo nativo por tool.
  • Espelho 640×360 a 24 fps por padrão (MESHLOOM_MIRROR_FPS, 1–60); o usuário pode trabalhar no Blender em outro monitor.

Documentos

# Documento Conteúdo
1 Arquitetura Componentes, processos, protocolo, event sourcing, runtime, LLM, MCP, segurança, recuperação, ADRs
2 Vivacidade As cinco camadas, anatomia do beat, UI, espelho, pacing, interrupção, checkpoints, métricas
3 System prompt Racional, variáveis, enforcement, variante local, testes — prompt utilizável em prompts/meshloom-agent.md
4 Tools Envelope, classes de latência, catálogo, JSON Schemas, erros, toolsets
5 Walkthrough Cadeira com gavetas, cronometrada, com interrupção do usuário e correção visual
6 Roadmap P0–P4, demos, critérios de saída, riscos, testes
7 Latência Orçamento por salto, decisões, anti-padrões, instrumentação, degradação

Ver o loop real (P0)

Requer Blender 5.x (testado em 5.2.2 LTS), Ollama com gemma4:latest e Node 20+.

cd app
npm install
npm start

O app compila o renderer e abre o Electron. Aponte o blender.exe em Configurações ou clique em Abrir Blender na tela do espelho; o addon sobe via blender/bootstrap.py e abre a coleção Meshloom vazia. O provedor padrão é o Ollama em http://127.0.0.1:11434; OpenAI também é aceito com chave em Configurações.

Não há replay: cada tool call é executada no Blender aberto, o espelho de 640×360 chega por TCP e os eventos são gravados em %APPDATA%\Meshloom\meshloom.db. Fechar e reabrir o app restaura sessão, plano e cena; reiniciar o Blender reconecta em ~2,5 s. O addon não tem hot reload: para carregar tools novas, salve a cena e reinicie o Blender.

Testes headless das regras de apoio e movimento relativo (rest_on, delta_m, parent), do plano, do arrasto no espelho e das tools avançadas:

blender --background --factory-startup --python blender/tests/test_rest_on.py
blender --background --factory-startup --python blender/tests/test_plan.py
blender --background --factory-startup --python blender/tests/test_drag.py
blender --background --factory-startup --python blender/tests/test_advanced.py
blender --background --factory-startup --python blender/tests/test_materials.py
blender --background --factory-startup --python blender/tests/test_group.py
blender --background --factory-startup --python blender/tests/test_import_model.py
blender --background --factory-startup --python blender/tests/test_scene.py

Testes do runtime do agente e do plano (Node, sem Blender):

cd app
npm test

Ver o protótipo

A experiência do walkthrough está executável em prototype/. Requer Node 20+.

cd prototype
npm install
npm run dev

Abre em http://127.0.0.1:5173.

O protótipo é um replay do walkthrough de 38 s sobre uma cena Three.js real, não uma maquete estática:

  • Real: a cadeira (geometria, hierarquia, bevels, aprofundamento das gavetas, materiais PBR procedurais), a animação SN_Act_Drawers em 30 fps, checkpoints/reverter/ramificar a partir do event log, captura do viewport e exportação de um SN_Chair_prototype.glb com a animação (cerca de 1,1 MB).
  • Simulado e rotulado na UI: o Blender (não há addon nem processo Blender), as latências das tools, o LLM e o gateway. O replay não tem cache nem RTT reais; o painel de contexto mostra apenas a estimativa de tokens do turno.
  • Interação: dá para pausar, buscar qualquer ponto da timeline, mandar uma correção a qualquer momento (a fila aplica no próximo limite de beat) e reverter ou abrir um ramo em qualquer checkpoint.

Decisões estruturais

Decisão Escolha Por quê
Shell Electron + React/TS Um runtime para UI, agente, MCP e protocolo; o gargalo é LLM + bpy, não o frontend
Transporte Blender TCP loopback com framing próprio Zero dependências no Python do Blender, frames binários, backpressure separada
MCP Camada de tools, não o fio do viewport MCP não define vídeo nem revisão de cena
Unidade de checkpoint Beat Snapshot por tool custa caro e trava a UI
Narração Texto nativo do assistente Streama sem round-trip; guarda sintética cobre modelos preguiçosos
Modelos 1 por padrão, papéis opcionais Menos configuração; executor nunca é modelo de raciocínio
Contexto Digest + diff Cena inteira no prompt é lenta e confusa
Tools Pequenas, por estágio Aderência de modelos locais e menos tokens

Estado

P0 — Live Loop implementado e medido de ponta a ponta. O loop real está em app/ (Electron + React), blender/meshloom_addon/ (addon) e app/electron/ (bridge, agente, event log). Cada tool call é executada no Blender aberto; não há replay. O protótipo continua como demonstração da experiência em Three.js, mas o P0 o substitui por LLM real (Ollama nativo), tools reais e persistência SQLite.

Medições de 2026-10-29 (Windows 10.0.26200, Blender 5.2.2 LTS, gemma4:latest local, Electron 39):

Critério Medido Meta Situação
Primeiro pixel — turnos de ajuste (sessão curta) 1,88–2,04 s (p50 1,95 s, n=3) p50 ≤1,5 s / p95 ≤3 s p95 atendido; p50 ~0,45 s acima
Primeiro pixel — turno inicial de modelagem 5,5 s e 9,9 s nas sessões de material; 11,3 s no turno da cadeira; 13,3–35,5 s em turnos de modelagem maiores; 16,25 s na validação de física de 2026-10-30 p95 ≤3 s não atendido com gemma4 local
Espelho 11,4–12 fps a 640×360 com o intervalo antigo de 12 fps; captura 3–6 ms ≥8 fps atendido a 12 fps; padrão agora pede 24 fps, medição ao vivo pendente
Sessão contínua 30,03 min de soak, 86 tools na janela (139 na sessão), 0 crashes, 0 desconexões 30 min / 100 tools sem crash do bridge bridge estável; 100 tools na janela não atingido com gemma4 local
Cobertura de cards 100% das tool calls, inclusive erro e cancelamento 100% atendido
bpy fora da main thread 0 — fila em bpy.app.timers com guard em server.py:421 0 atendido
Interrupção 4–205 ms até parar; trabalho já aplicado preservado ≤1 s atendido
Reconexão 2,45 s após reiniciar o Blender; app reabre com sessão, plano e cena ≤3 s atendido

A lacuna do primeiro pixel é o LLM, não o Blender: nos turnos de material as tools responderam em 0,7–2,3 ms e a captura em 464 ms (incluindo a compilação de shader do modo material; 35–48 ms em sólido), mas o gemma4 local leva 5–36 s para emitir a primeira tool call de um turno de modelagem. Para bater a meta é preciso um modelo mais rápido (nuvem) ou reduzir o prompt/toolset do turno inicial. O valor fica registrado como latency.first_pixel no event log.

Em 2026-10-30 o agente passou a buscar texturas sozinho: find_texture consulta o catálogo CC0 do Poly Haven (869 texturas), entende termos em português (madeira escura → wood dark) e devolve ids com miniatura; download_texture resolve o mapa difuso 1k/2k, baixa, valida bytes mágicos JPEG/PNG, limite de 20 MB e md5 do catálogo, e grava em <state>/textures/ devolvendo o caminho absoluto que create_material texture "image" consome. As duas tools rodam no app (Node), fora da fila main-thread do Blender, e só são anunciadas quando o addon tem create_material; um download leva 0,7–1,5 s e não congela o espelho. Se a rede falhar, o erro traz o caminho de saída: textura procedural (wave/noise/voronoi). Os testes em app/tests/textures.test.cjs cobrem ranqueamento PT/EN, escolha do JPG, md5 errado, HTML disfarçado de imagem, 404, limites e a exposição condicional das tools. No par isolado, dois turnos fizeram a cadeia completa sem instrução de passo: find_texture {"query": "madeira escura"} → download_texture {"id": "dark_wood"} (737 KB, md5 ok) → create_material texture "image" com o path → uv_unwrap cube → assign_material → set_shading material → capture_viewport, e as capturas mostram o veio real da madeira no cubo. O mesmo turno expôs dois falsos positivos corrigidos: "capture o viewport para eu ver" virava exigência de control_playback (agora "quero ver/para eu ver" só pede playback quando o pedido tem animação ou física), e a revisão visual mandou mover uma caixa que o usuário já tinha posicionado em coordenadas explícitas (agora coordenada do pedido vence a revisão e a correção vira advisory). Fica um limite do modelo local: no segundo teste o gemma4 reaplicou a textura numa caixa existente em vez de criar a caixa nova pedida em (0.8, 0, 0.25) e encerrou como done — a cadeia de textura estava correta, mas a criação não aconteceu.

Em 2026-10-30 o escopo de autonomia cresceu para a internet e para modelos 3D reais. web_search consulta o DuckDuckGo sem chave (endpoints lite/html), decodifica o redirecionamento uddg e devolve título, URL e trecho; web_fetch lê a página e devolve texto limpo marcado como conteúdo externo (dado, não instrução). O fetch é fechado contra SSRF: só http/https, portas 80/443, sem credenciais na URL, DNS resolvido para todos os endereços e cada um recusado se for privado/loopback/link-local/metadata, redirecionamentos manuais revalidados hop a hop, limite de 2 MB e recusa de conteúdo binário. find_model ranqueia o catálogo CC0 de modelos do Poly Haven traduzindo português, download_model baixa o glTF + bin + texturas com md5 por arquivo, allowlist de hosts, rejeição de caminho ../, teto de 80 MB e validação de que toda referência do glTF veio no pacote; import_model (addon) importa .gltf/.glb, funde as malhas num único objeto SN_*, aplica location/rotation/scale pedidos, apoia a base em z=0, renomeia materiais para SN_Mat_*, recusa nome duplicado e modelos acima de 300k tris, e registra tudo no digest como mode: "import". O agente anuncia web_*/find_model/download_model sempre, import_model quando o addon o expõe e texturas só com create_material; se o pedido menciona pesquisa/internet e nenhuma tool online foi chamada, um nudge único cobra web_search/find_model, leitura com web_fetch e proíbe responder de memória ou inventar fonte. Validação real: find_model {"query": "cadeira de jantar"} devolveu 5 cadeiras do catálogo; download_model {"id": "GreenChair_01", "size": "1k"} baixou 608 KB em 5 arquivos com md5 ok; o import headless gerou SN_CadeiraVerde com 4.213 tris, 0,673 × 0,664 × 1,059 m, base em z=0 e material SN_Mat_GreenChair_01; web_search/web_fetch ao vivo leram uma página sobre altura de cadeira. O teste do addon pegou dois defeitos reais que só aparecem em modelos de várias malhas: object.join() invalida as referências Python das peças fundidas (o loop de limpeza estourava ReferenceError em qualquer glTF com mais de uma malha) e o importador glTF deixa o objeto em rotation_mode quaternion, que ignorava a rotação pedida via rotation_euler. Os testes em app/tests/web.test.cjs, app/tests/models.test.cjs e blender/tests/test_import_model.py cobrem parsing, SSRF, redirects, md5, pacote incompleto, getTools e a importação real com união, materiais, posição, rotação, escala e erros.

Materiais, texturas e shading são reais, não scriptados: create_material monta Principled BSDF com preset e textura procedural (noise/wave/voronoi) ou imagem local, assign_material pinta o objeto inteiro ou faces filtradas por normal/bbox, uv_unwrap gera UVs e set_shading troca o viewport. O scene_digest devolve material_details (cor base, preset, roughness, metallic, textura, usuários) e os materiais/UVs de cada objeto, então o agente parte do estado real da cena. Os testes headless em blender/tests/test_materials.py cobrem criação, nodes de bump/mix, atribuição por face, digest, UV, textura de imagem e erros (MATERIAL_EXISTS, MATERIAL_NOT_FOUND, EMPTY_SELECTION, INVALID_PARAM). No app real, um turno criou SN_Caixa_Material com madeira #8B5A2B (wave), aplicou nas 6 faces e pôs o viewport em material em 29,2 s; outro criou SN_Lata_Metal (cilindro 0,5 m) com prata metálica #C0C0C0 (metallic 1, roughness 0,25), aplicou nas 34 faces, trocou o shading e capturou em iso (768×447, 464 ms) em 14,2 s, com a captura realimentando o modelo. Trocar para material faz o Blender compilar shaders na main thread; o app repete o digest com janela maior antes de declarar falha e trata a troca como alteração visual que pede captura.

Em 2026-10-29, um turno real de cadeira com gemma4 completou 6 peças (quatro pernas em ±0,19 m, assento em (0, 0, 0,465), encosto em (0, 0,20, 0,74)), SN_Mat_Madeira nas 6 peças e captura final, em 31 iterações, 118,8 s (primeiro pixel 11,3 s, captura material 92,7 ms), terminando done sem erro de limite. Para chegar lá, o add_primitive passou a aceitar campos omitidos com defaults reais (type cube, nome SN_<Tipo>, dimensões 1 m, centro no chão) e reporta "Padrões aplicados" no resultado; o prompt lista todos os parâmetros e as combinações válidas; o addon ganhou delete_object; e o agente confere no digest, antes de encerrar, se alguma peça ficou sem material quando o pedido menciona material.

Em 2026-10-30, um segundo turno de cadeira no par isolado fechou em 9 tools, 15 iterações e 78,6 s (primeiro pixel 12,2 s), done, plano fechado e gaps vazio. O gemma4 montou três pernas, assento e encosto; o gate de montagem pediu a quarta perna com a posição exata e o modelo a criou, sem mexer no resto. O digest final mediu as 6 peças e a captura iso confirmou a cadeira montada. O assento ficou em (0, 0, 0,47), 5 mm acima do topo das pernas — dentro da tolerância de 2 cm do gate e sem vão visível na captura. O gate valida pernas sob cantos distintos, assento assentado e encosto em pé na borda, e devolve add_primitive/set_transform com números exatos em vez de deixar o modelo improvisar. Numa tentativa anterior o gate usava o centroide das pernas como centro e cada perna corrigida movia o alvo, fazendo o modelo perseguir posições que mudavam; agora o assento, quando está sobre as pernas, ancora o centro e o alvo fica fixo.

A revisão visual automática roda ao fim do turno, quando o gate não achou nada e a captura é atual: uma chamada sem tools (o gemma4 ignora a imagem quando o payload leva tools), com o pedido, as peças reais, as medidas e os contatos verificados. Nas execuções de 2026-10-30 ela respondeu OK em todas as chamadas (duas numa execução intermediária e uma na validação final), com o evento vision.review gravado no event log. Se a revisão citar um defeito, o app só manda corrigir quando as medidas não o contradizem: uma alegação de peça solta cai quando o contato medido prova o apoio, e alegações sobre a cadeira caem quando o gate está limpo. Caso contrário ela vira observação advisory na thread e nada é alterado. Isso foi necessário porque, antes, uma revisão disse que o encosto de uma cadeira correta estava "muito recuado" e o modelo moveu as quatro pernas para o centro; o gate restaurou a montagem, mas o turno ficou sujo. O prompt da revisão agora lista as relações medidas como corretas (pernas nos cantos, assento centrado, encosto na linha da borda) e pede só defeito grave. Também em 2026-10-30, update_plan passou a truncar títulos novos em 80 caracteres (e a manter o título atual quando a atualização traz um longo demais): um título de 100 caracteres gerava INVALID_PARAM, o modelo repetia a chamada e o plano ficava aberto. O add_primitive agora avisa que rest_on sem location_m centraliza em x=0, y=0, o que antes empilhava as pernas no centro da cadeira.

Em 2026-10-30, a validação de física (piso 4×4×0,1 m, torre de 3 caixas de 0,4 m, esfera de 0,5 m caindo de 2 m, bake 1–72, antes/depois) rodou no par isolado em 21 tools, 30 iterações e 202 s (primeiro pixel 16,25 s), terminando done com o plano fechado (4/4 itens done, nenhum aberto). O bake mediu deslocamentos de 0,017 m, 0,768 m e 1,915 m nas caixas — duas das três tombaram — e contato da esfera com a caixa do topo no frame 11; as capturas "Antes" (frame 1) e "Depois" (frame 72) mostram a torre em pé e as caixas espalhadas com a esfera assentada. Numa tentativa anterior o modelo passou location em vez de location_m; o addon aceitava a chave desconhecida em silêncio, criava um cubo em cima da torre e aplicava colisão SPHERE nele, e o bake explodia com caixas a 39–43 m. Agora add_primitive, set_transform e add_rigid_body rejeitam argumentos desconhecidos citando o campo e o uso correto, o digest expõe collision_shape, e o agente cobra a esfera real (add_primitive type sphere) quando existe malha de cubo com colisão esférica ou quando o pedido cita esfera e ela não existe, diagnostica esfera sobreposta à torre no frame 1 em vez de mandar levantar as caixas, e só aceita a captura "Depois" no frame final da simulação.

O espelho deixou de ser só leitura: arrastar um objeto no viewport do app move o objeto no Blender ao vivo (raycast, delta no plano XY pelos eixos da câmera, um único undo por arrasto); arrastar o fundo orbita, Shift ou botão do meio desloca a vista, e o inspetor atualiza durante o arrasto. O teste headless em blender/tests/test_drag.py cobre eixos, escala por pixel, z preservado e estado do arrasto; ao vivo, o objeto seguiu o ponteiro a 11 fps no par isolado.

O plano agora tolera o que o modelo local erra: update_plan aceita sinônimos de status (completed→done), id numérico e stage ausente, e o app funde atualizações parciais no plano atual (título/stage herdados por id ou posição e promoção da próxima etapa pendente quando nenhuma está ativa). Antes dessa fusão, o gemma4 entregava a cena e ainda assim encerrava com o plano aberto; na reexecução o plano fechou em 4/4. Uma sessão real do usuário expôs o caso pior: o modelo mandava três itens sem title e com status completed/concluído, o addon respondia INVALID_PARAM, o app repetia REPEATED_FAILURE e o modelo anunciava "plano atualizado com sucesso" sem nunca ter atualizado nada. O app agora sanitiza cada item antes de enviar (descarta item novo sem title, clampa título em 80 e nota em 120), resolve status por sinônimo e, quando uma atualização reduzida chega toda done com pelo menos dois itens, fecha também os itens restantes do plano atual em vez de deixá-lo aberto. Em 2026-10-30, no par isolado, o turno "Crie uma esfera… use update_plan…" fez dois update_plan ok (abertura e conclusão) e terminou done com o plano fechado e nenhum erro.

A trava de montagem da cadeira também deixou de disparar em pedidos que só citam a cadeira de passagem: "Crie um cubo de 0,5 m ao lado da cadeira" acionava o gate e o modelo começava a mover as pernas de uma cadeira já correta. Agora o gate só liga quando o verbo de construção está a até 48 caracteres da primeira menção e não há outro objeto (cubo, esfera, piso, mesa…) entre os dois; no par isolado o mesmo pedido terminou done, gaps vazio e sem nenhum nudge de perna.

Modelos de várias peças agora terminam FIXOS: o addon ganhou group_objects (mode: "join" funde as malhas num objeto com name, preservando materiais, UVs e posições no mundo; mode: "parent" mantém as peças e as fixa sob um root), e o gate da cadeira exige a união depois que a geometria está correta — a lacuna devolve o group_objects com os seis nomes exatos e "name": "SN_Cadeira". Uma cadeira já unida (um único objeto com o bbox da cadeira e base no chão) passa pelo gate mesmo que o nome seja o da peça principal: o scene_digest passou a expor bounds_m (AABB real no mundo), então uma origem herdada do encosto (z 0,74) não faz mais o gate achar que a cadeira flutua. Pedidos de montagem articulada (gaveta, porta, "anime a gaveta") continuam separados com parent, e a física entra depois da união: join remove corpos rígidos das peças e avisa para reaplicar no objeto unido, e o app nega add_rigid_body/bake_physics com POLICY_DENIED quando o pedido não menciona física, queda, colisão ou simulação. Antes, um turno de textura aplicava corpo rígido em todas as peças sem ter sido pedido. assign_material também parou de devolver o erro genérico: com um único SN_Mat_* na cena, um nome inválido (ex.: material_default) usa esse material e avisa no summary; com vários, o hint lista os nomes reais; e object/material trocados são corrigidos sozinhos quando há um único material (senão o erro mostra o exemplo com o nome do objeto). Como o gemma4 pode entrar em laço de argumentos errados, o agente encerra o turno após 12 chamadas com erro seguidas sem nenhum sucesso (reason: loop) em vez de girar até o limite de iterações. Os testes em blender/tests/test_group.py cobrem fallback de material, troca de argumentos, join com materiais/UV/bbox, parent com posição preservada, ciclo, peça animada, remoção de física e chaves desconhecidas; no app, app/tests/agent-autonomy.test.cjs cobre a lacuna de união, a cadeira unida com origem fora do centro, a guarda de física e o freio de laço.

O soak de 30 min não completou 100 tools dentro da janela: a sessão somou 139 tools (51 antes + 86 no soak), sem crash de bridge e sem desconexão do espelho. A partir de ~80 tools na mesma sessão, o gemma4 local passou a encurtar pedidos e a inventar chaves (object_name em vez de object, translation em vez de delta_m); cada erro apareceu no card com correção do agente, mas o turno p50 subiu para 77,7 s (máx 220 s) e o primeiro pixel foi de 9 s a 77,6 s. Compactação de histórico/contexto é trabalho de P1.

Em 2026-10-30 o addon ganhou a camada de cena e áudio. add_camera cria câmera com lens_mm (retrato 85–135 mm, cena 24–35 mm) ou type: "ortho" com ortho_scale_m, mira por look_at (nome SN_* → centro do bbox, ou ponto) e pode virar a câmera ativa e entrar no modo câmera do viewport; add_light cobre SUN/AREA/POINT/SPOT com energia padrão por tipo, cor hex, tamanho, ângulo e blend; configure_scene ajusta mundo (cor/força), engine (eevee/cycles/workbench com fallback de nome), resolução, samples, film transparente, fps e faixa de frames; render_camera renderiza pela câmera pedida e devolve PNG como artifact (restaura resolução/formato/câmera e os samples do Cycles ao final), então o render entra na revisão visual como a captura do viewport. add_audio põe um WAV/MP3/OGG/FLAC na sequência de vídeo (allowlist de extensão, existência e teto de 50 MB) e estende o fim da linha do tempo; generate_voice roda no app e grava um WAV de voz pt-BR pelo SAPI do Windows, devolvendo o path que add_audio consome — o texto vai num arquivo temporário UTF-8 e os argumentos são passados por argv, nunca por interpolação de shell. O digest agora traz render, world, camera e audio, e o prompt ensina enquadramento, luz em 3 pontos e o encadeamento voz → áudio. Validação: blender/tests/test_scene.py cobre câmera (look_at, lente, ortho), luzes por tipo, configure_scene (cycles, mundo #202830, 320×180, 16 samples, fps 24, frames 1–48), um render Cycles real de 128×72 com restauração das configurações, WAV de verdade no add_audio (frame 5, volume 0,8, fim da cena estendido) e erros de nome duplicado/arquivo ausente/extensão inválida; app/tests/voice.test.cjs cobre parse do WAV, validação de texto/rate, uma síntese SAPI real (WAV > 2 KB, duração > 0,2 s), o resumo que aponta add_audio e o anúncio condicional das tools. A validação ao vivo no par isolado (render no card da UI, reprodução do WAV e um turno completo de câmera/luz) ficou pendente para não rodar probes com o jogo aberto.

python_exec passou a ser acesso total ao bpy por padrão: as tools específicas continuam sendo o caminho normal, mas compositor, sequencer, geometria procedural e modificadores exóticos ficam alcançáveis. O guard de AST continua ativo (sem eval/exec/compile/open/__import__, imports limitados a math, mathutils, bpy e bmesh, sem dunder, timeout e stdout truncado); MESHLOOM_DISABLE_PYTHON=1 desliga a tool e devolve POLICY_DENIED. O espelho subiu o pedido padrão de 12 para 24 fps via MESHLOOM_MIRROR_FPS (1–60, lido na carga do addon e anunciado no handshake); a captura de 3–6 ms cabe no intervalo, mas a taxa real a 24 fps ainda não foi medida no par ao vivo.

O que falta do produto: gateway multi-provedor completo (hoje Ollama nativo + OpenAI), MCP externo, edição de malha dedicada, rigging e lip sync de personagem, animação de câmera em tool própria (hoje por keyframe_object no SN_Camera), checkpoints por beat e empacotamento. P1–P4 no Roadmap.

Licença

MIT — veja LICENSE. Os modelos e texturas baixados em tempo de execução são CC0 do Poly Haven e mantêm a licença da fonte.

About

Agente de modelagem 3D ao vivo no Blender com LLM local, tools reais, espelho de viewport e busca autônoma por texturas e modelos CC0

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages