Página para pôster
Você lê a aventura e escreve cinco pequenos arquivos que descrevem o local. O código nesta pasta os verifica, constrói um modelo 3D no Blender headless, renderiza e compõe o pôster.
adventure (PDF/texto) + diretrizes
-> brief.md o que o texto diz sobre o local (você escreve)
-> layout.json salas, passagens, entradas, em metros (você escreve; `check` valida e desenha)
-> props.py o que está em cada sala (você escreve; `build` modela)
-> poster.json título, rótulos, chamadas, caixa de texto (você escreve; `poster` renderiza e compõe)
-> out/poster_gm.png, poster_player.png, poster_bare.png (+ .jpg)
p2p.py está nesta pasta. Chame-o pelo caminho completo a partir do diretório de trabalho do usuário, para que as pastas do projeto sejam criadas lá e não aqui; os comandos abaixo escrevem python p2p.py por simplicidade. Use python3 onde python não for Python 3.
Regras que valem durante todo o processo
- Olhe para cada imagem que você fizer. Cada etapa escreve uma imagem; abra-a com sua ferramenta de leitura de imagens antes de prosseguir. As etapas nomeiam o arquivo. A maioria dos erros é óbvia na imagem e invisível nos números.
- Metros, +x leste, +y norte, piso em z = 0. Ângulos em
props.pysão em radianos. - Diga o que você estimou. Aventuras raramente dão tamanhos ou posições exatas. Estime, escreva "est." em
brief.mde liste as estimativas ao relatar. Nunca apresente um fato inventado como sendo do livro. - Blender roda em headless através de
p2p.py. Não abra sua janela e não use um servidor MCP do Blender. - Rascunhos são baratos. Faça pôsteres de rascunho com
--dpi 100; renderize o final com--dpi 200quando o rascunho estiver correto. - A pasta do projeto contém o texto e os mapas da aventura. Mantenha-a fora de repositórios públicos, a menos que a aventura seja sua para publicar.
Etapa 0: verificar a máquina
python p2p.py doctor
Deve terminar com Core pipeline: ready. Se faltarem pacotes Python: pip install -r requirements.txt. Se o Blender estiver faltando, siga docs/setup.md. Não é necessária GPU (a renderização cai para a CPU) e ComfyUI é opcional: só passe --comfy URL se o usuário tiver lhe fornecido um (docs/comfyui.md).
Etapa 1: iniciar o projeto e ler a fonte
python p2p.py new PROJETO --source aventura.pdf --pages 53-56
PROJETO é uma nova pasta para este mapa, por exemplo maps/o-nome-do-local no diretório de trabalho do usuário. --pages são os números de página do próprio PDF (a primeira página é 1) para o local a ser mapeado; omita para um PDF curto. Uma fonte de texto ou Markdown é copiada como está. O comando escreve modelos para os cinco arquivos e, para um PDF, source/page_NNN.txt e source/page_NNN.png.
Leia todo o texto. Em seguida, olhe cada imagem de página: mapas, diagramas e barras laterais estão apenas nas imagens. Se o livro tiver um mapa, esse mapa decide onde as salas ficam; o texto decide o que há nelas.
Preencha guidelines.md com o que o usuário pediu; qualquer coisa que ele não mencionou mantém seu padrão. Pergunte ao usuário apenas se você não conseguir identificar qual local mapear.
Preencha brief.md: uma linha por sala, uma por conexão, uma por entrada. Cite números de página. Esta é a etapa em que você decide o que é dado e o que é estimado.
Tamanhos quando o texto não fornece nenhum: uma porta tem 1 m, um corredor 2,5 a 3,5 m, uma cela ou escritório 4 x 5 m, uma sala comum 6 x 8 m, um salão 15 x 25 m, uma caverna ou hangar 30 x 50 m. Tetos: 3 m comum, 5 a 8 m salões. Um quadrado de mapa geralmente tem 5 pés (1,5 m) em jogos de fantasia; um "passo" tem cerca de um metro.
Etapa 2: layout.json
Substitua o modelo pelas salas, passagens e entradas do local. docs/layout.md tem todas as chaves. Dois projetos completos para aprender: examples/brine-chapel/ (uma masmorra de pedra, a partir de texto sem mapa) e examples/kestrel-9/ (uma estação pré-fabricada, a partir de um PDF com um mapa esboçado). Os essenciais:
- Uma sala é um retângulo alinhado aos eixos de piso claro:
"min"é seu canto sudoeste,"max"seu nordeste. Deixe pelo menos 0,6 m (uma parede) entre quaisquer duas salas. - Um link une duas salas ao longo de um
"path"de pontos. O primeiro ponto fica na borda da salaa, o último na borda da salab; o caminho sai de cada sala em ângulo reto com a parede."kind"éopen,blocked(um desabamento obstrui) ouvent(um duto ou passagem estreita, de sala a sala;"z"define sua altura)."doors": ["a", "b"]coloca portas nessas extremidades."secret": truemarca um link que os jogadores não devem ver: suas portas se tornam portas secretas e o pôster do jogador omite sua marca de rota. - Onde três ou mais passagens se encontram, nomeie o ponto sob
"junctions"e termine os caminhos lá. - Uma entrada é um caminho de fora para dentro do mapa:
stairs,ramp,tunneloudoorem uma parede ("side": "N|S|E|W","at": posição ao longo dessa parede), ou umshaftem um ponto dentro de uma sala. Uma passagem que sai do mapa e está desabada é umtunnelcom"blocked": true. - Uma sala que não é um retângulo: dê a ela seu retângulo delimitador, depois modele-a em
props.py(cave_fillpara cavernas e desabamentos,partitionpara paredes internas), ou divida-a em duas salas unidas por um link curto e largo. - Um pôster mostra um nível. Para um local com vários níveis, faça um projeto por nível e una-os com entradas rotuladas como "Escadas para o nível 2".
Disponha o local de modo que seja compacto e aproximadamente tão profundo quanto largo; isso preenche melhor uma folha 3:2 na vista isométrica. Coloque a primeira sala perto de (0, 0) e trabalhe para fora em uma grade de 1 m.
python p2p.py check PROJETO
Corrija cada erro (cada mensagem diz o que mudar). Em seguida, abra PROJETO/build/layout_check.png e compare-o com o mapa do livro ou com seu brief: todas as salas presentes, vizinhos nos lados corretos, todas as conexões desenhadas, portas e entradas nas paredes corretas. Repita até que esteja correto. Não prossiga com um layout que você não tenha olhado.
Etapa 3: texturas
python p2p.py textures PROJETO
Isso desenha as texturas de superfície e decalques por código, em alguns segundos. Com um ComfyUI que passou em doctor --comfy URL, adicione --comfy URL para tê-las pintadas em vez disso (mais lento, mais rico). Execute isso antes da primeira construção; se você refizer texturas depois, construa novamente.
Abra dois ou três de PROJETO/textures/_preview/<texture>_tiled.png (concrete_floor, stone_wall, rust_metal, planks, tile, ...) e escolha materiais de piso e parede que combinem com o local; defina-os como "floor" e "wall" em layout.json, para o local ou por sala. A tabela de materiais em docs/kit-reference.md lista cada material e a textura que ele usa.
Etapa 4: props.py, depois build
props.py contém uma função por sala, chamada room_<id>, que coloca o que o brief lista para aquela sala. docs/props.md explica o kit e mostra cada adereço pronto; docs/kit-reference.md lista cada chamada; examples/brine-chapel/props.py é um módulo completo. Uma sala mínima:
def room_3(r, rng, P):
mb = MB("r3_props") # um construtor de malha: adicione sólidos, depois faça um objeto
pillar(mb, r.minx + 2, r.cy, h=r.h) # adereços prontos usam x, y em metros ...
altar(mb, T(r.cx, r.maxy - 2, 0, rz=0)) # ... ou uma transformação T(x, y, z, rz=ângulo)
box(mb, "wood", (r.cx, r.cy, 0.4), (2.0, 1.0, 0.8)) # qualquer outra coisa: caixas, cilindros, varreduras
mb.obj(P) # P é a coleção desta sala
feat("3.altar", r.cx, r.maxy - 2, 1.1) # um ponto nomeado ao qual uma chamada do pôster pode se prender
O que faz uma sala ser legível no pôster:
- Modele o que o texto nomeia primeiro e dê a cada um um
feat(). Depois, adicione de 3 a 10 coisas comuns que digam para que serve a sala. Uma sala vazia parece um erro. - As formas são lidas pela silhueta e pelo tom. Materiais brilhantes (
tile,bone,ice,mushroom,glass) imprimem em branco, vermelhos (hazard,liquid_acid,emissive_red) imprimem na tinta de destaque, escuros imprimem em preto. Coisas menores que cerca de 0,3 m desaparecem. - Mantenha 1,5 m livre na frente de cada porta e mantenha os adereços dentro do retângulo da sala.
- Use
rngpara cada escolha aleatória para que cada construção seja idêntica. - Fossos e piscinas precisam do quarto parâmetro:
def room_4(r, rng, P, cut).
python p2p.py build PROJETO
python p2p.py render PROJETO closeup --rooms 3,4
build leva segundos e lista os nomes das features que você registrou. Abra PROJETO/build/iso_preview.png (o modelo inteiro, sem textura) após cada construção, e PROJETO/renders/closeup_<id>.png para cada sala em que você trabalhou. Verifique: nada flutuando, nada atravessando uma parede, portas desobstruídas, a sala reconhecível a partir do texto. Um erro Python em props.py é impresso com seu número de linha; o log completo é build/build.log.
Closeups usam a vista e o recorte de poster.json, o mesmo que o pôster usará. Paredes são cortadas abertas em "cutaway": {"wall": 3.2} metros, e cada uma das duas paredes mais próximas do visualizador esconde uma faixa de piso com cerca da mesma largura dessa altura. Isso é adequado para salões. Se as salas são pequenas (menos de cerca de 8 m), reduza agora, para 1,5 ou 2, ou tudo ao longo das paredes próximas desaparecerá.
Etapa 5: poster.json, depois o pôster
Edite o modelo: título, subtítulo, rodapé, uma chamada por feature notável e as caixas de texto. docs/poster.md tem todas as chaves.
- Uma chamada é
{"feature": "3.altar", "text": "Altar de sal"}; o nome da feature é um que você registrou comfeat(). Adicione"gm_only": truepara mantê-la fora da versão do jogador. Mantenha o texto com cerca de oito palavras ou menos. boxcontém as notas do Mestre ("gm": uma tabela de encontros, cronômetros, a verdade em uma linha) e o que os jogadores sabem ("player"). Seis a oito linhas curtas cada.- A versão do jogador remove chamadas
gm_onlye links marcados como"secret". A arte é a mesma para ambos, então uma sala secreta ainda é desenhada; esconda seu nome com"room_labels": {"5": {"gm": "Ossuário", "player": ""}}e informe ao usuário que a sala em si aparece. - Escolha uma paleta que combine com o local (papel, tinta, branco, destaque) ou mantenha o padrão amarelo-esverdeado ácido.
size_iné a folha em polegadas,[36, 24]a menos que o usuário tenha pedido outro tamanho. Os rótulos encolhem com a folha; em uma pequena, aumente"label_scale"até que as chamadas fiquem confortáveis de ler.
python p2p.py poster PROJETO --dpi 100
Abra PROJETO/out/poster_gm.png e poster_player.png. Verifique e corrija em poster.json:
| Problema | Correção |
|---|---|
| Um rótulo de sala cobre o que importa na sala | "room_label_offsets": {"3": [dx, dy]}, em metros leste e norte. Na vista padrão, leste é direita-para-cima na folha e norte é esquerda-para-cima: [d, -d] move um rótulo para a direita, [d, d] para cima |
| Uma etiqueta de chamada fica em cima de algo | "side": "nw" nessa chamada coloca sua etiqueta nesse lado do ponto (n é para cima na folha); ou encurte, mescle ou remova chamadas em uma sala lotada |
| Coisas ao longo das paredes próximas estão ocultas | abaixe "cutaway": {"wall": 1.6}, depois renderize novamente |
| Rótulos muito pequenos ou muito grandes para o local | "label_scale": 1.3 |
| Título, caixa ou legenda fica em cima da arte | "corners": {"title": "tl", "box": "br", "legend": "bl"}, ou encolha a arte com "art_box" |
| O lado interessante do local está virado para longe | "view": {"azimuth": 135} (tente -45, 45, 135, -135), depois renderize novamente |
| Adereços altos escondem o que está atrás deles | "cutaway": {"props": {"3": 4.0}} corta os adereços da sala 3 a 4 m |
| Impressão inteira muito escura ou muito clara | "exposure": 1.15 (mais claro) ou 0.9 (mais escuro) |
| Uma chamada aponta para nada | as coordenadas de feat() em props.py estão erradas; corrija, construa, renderize |
Após alterar apenas texto ou rótulos, adicione --reuse-render para pular a renderização. Após alterar layout.json, props.py, a vista ou o recorte, construa e renderize novamente. Quando o rascunho estiver correto:
python p2p.py poster PROJETO --dpi 200
Isso escreve out/poster_gm, poster_player e poster_bare como PNG e JPEG no tamanho da folha (36 x 24 polegadas são 7200 x 4800 pixels). Abra o pôster final do Mestre mais uma vez antes de relatar.
Extras opcionais
- Mapas de batalha.
python p2p.py render PROJETO plandá uma planta baixa do local (renders/plan.png);render PROJETO rooms --rooms 3dá uma sala a 70 pixels por metro (renders/room_3.png). - Pinturas de sala (precisa de ComfyUI com a LoRA de controle de profundidade; veja docs/comfyui.md). Escreva
paint.jsoncom uma frase por sala, então:python p2p.py render PROJETO eyeepython p2p.py paint PROJETO --comfy URL. As pinturas emout/paintings/mantêm o layout de cada sala porque o mapa de profundidade do modelo as orienta.--strength(padrão 1,2, faixa útil 1,0 a 1,4) define o quão firmemente.
Relatar
Forneça ao usuário: os caminhos dos pôsteres; o que você estimou ou inventou (tamanhos, posições, qualquer coisa que o texto deixou em aberto); qualquer coisa nas diretrizes que você não conseguiu fazer; e como mudar as coisas (editar os arquivos, executar o comando novamente).
Quando algo dá errado
| Mensagem ou sintoma | Causa e correção |
|---|---|
Blender: MISSING | Instale-o ou defina a variável de ambiente BLENDER para o executável: docs/setup.md |
layout.json has errors na construção | Execute check; cada linha diz o que mudar |
callout ... names feature ..., which props.py never registered | A grafia difere do nome de feat(); build imprime os nomes válidos |
| Superfícies são cinza plano | textures não foi executado antes de build; execute-o, depois construa novamente |
Failed to retain CUDA context, ou a GPU fica sem memória | Outro programa está usando a GPU: adicione --device cpu, ou --gpu NOME para escolher uma placa |
no GPU device matching ... | O nome dado a --gpu não corresponde a nenhuma placa; doctor as lista |
| A renderização está lenta | Está na CPU. Rascunhe com --dpi 100; o final ainda termina, apenas mais tarde |
| Um adereço é cortado plano no topo | É mais alto que o recorte; isso é intencional. R |