CommunityImagengithub.com

michaelweber/page-to-poster

Convierte la descripción de una aventura (PDF o texto) junto con pautas de mapa en un modelo 3D de Blender y un póster isométrico recortable listo para imprimir, con versiones para el director de juego y los jugadores.

¿Qué es page-to-poster?

Esta habilidad permite a un agente de IA transformar descripciones escritas de localizaciones de aventuras (mazmorras, búnkeres, naves, estaciones o edificios) en un modelo 3D de Blender y un póster isométrico recortable con estilo de serigrafía. El agente lee el texto de la aventura (PDF o archivo de texto plano), extrae la información clave y genera cinco archivos de instrucciones: un resumen (`brief.md`), un diseño de habitaciones en metros (`layout.json`), los objetos de cada habitación (`props.py`), y la composición del póster (`poster.json`). Luego, mediante un script Python (`p2p.py`), verifica el diseño, construye el modelo 3D en Blender sin interfaz gráfica, renderiza la vista isométrica y compone el póster final en versiones para el director de juego y los jugadores. El flujo de trabajo está diseñado para ser iterativo: cada paso genera una imagen de previsualización que el agente debe revisar antes de continuar, identificando errores visuales imposibles de detectar solo con números. La habilidad es compatible con Claude Code, Cursor y otros agentes de IA, y requiere Blender, Python y, opcionalmente, ComfyUI para texturizado avanzado.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/michaelweber/page-to-poster/tree/HEAD

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

De página a póster

Lees la aventura y escribes cinco pequeños archivos que describen el lugar. El código de esta carpeta los verifica, construye un modelo 3D en Blender sin interfaz gráfica, lo renderiza y compone el póster.

aventura (PDF/texto) + pautas
   -> brief.md        lo que el texto dice sobre el lugar          (tú escribes)
   -> layout.json     habitaciones, pasajes, entradas, en metros  (tú escribes; `check` valida y lo dibuja)
   -> props.py        qué hay en cada habitación                  (tú escribes; `build` lo modela)
   -> poster.json     título, etiquetas, llamadas, cuadro de texto (tú escribes; `poster` renderiza y compone)
   -> out/poster_gm.png, poster_player.png, poster_bare.png  (+ .jpg)

p2p.py está en esta carpeta. Llámalo con su ruta completa desde el directorio de trabajo del usuario, para que las carpetas de proyecto se creen allí y no aquí; los comandos a continuación escriben python p2p.py para abreviar. Usa python3 donde python no sea Python 3.

Reglas que se aplican en todo momento

  1. Mira cada imagen que hagas. Cada paso escribe una imagen; ábrela con tu herramienta de lectura de imágenes antes de continuar. Los pasos nombran el archivo. La mayoría de los errores son obvios en la imagen e invisibles en los números.
  2. Metros, +x este, +y norte, suelo en z = 0. Los ángulos en props.py son en radianes.
  3. Di lo que estimaste. Las aventuras rara vez dan tamaños o posiciones exactas. Estima, escribe "est." en brief.md, y enumera las estimaciones cuando informes. Nunca presentes un hecho inventado como si fuera del libro.
  4. Blender se ejecuta sin interfaz gráfica a través de p2p.py. No abras su ventana y no uses un servidor MCP de Blender.
  5. Los borradores son económicos. Haz pósters borrador con --dpi 100; renderiza el final con --dpi 200 una vez que el borrador esté correcto.
  6. La carpeta del proyecto contiene el texto y los mapas de la aventura. Mantenla fuera de repositorios públicos a menos que la aventura sea tuya para publicar.

Paso 0: verifica la máquina

python p2p.py doctor

Debe terminar con Core pipeline: ready. Si faltan paquetes de Python: pip install -r requirements.txt. Si falta Blender, sigue docs/setup.md. No se necesita GPU (el renderizado usa la CPU como alternativa), y ComfyUI es opcional: solo pasa --comfy URL si el usuario te proporcionó una (docs/comfyui.md).

Paso 1: inicia el proyecto y lee la fuente

python p2p.py new PROYECTO --source aventura.pdf --pages 53-56

PROYECTO es una carpeta nueva para este mapa, por ejemplo maps/el-nombre-del-sitio en el directorio de trabajo del usuario. --pages son los números de página del PDF (la primera página es 1) para la ubicación a mapear; omítelo para un PDF corto. Una fuente de texto o Markdown se copia tal cual. El comando escribe plantillas para los cinco archivos y, para un PDF, source/page_NNN.txt y source/page_NNN.png.

Lee todo el texto. Luego mira cada imagen de página: los mapas, diagramas y barras laterales solo están en las imágenes. Si el libro tiene un mapa, ese mapa decide dónde están las habitaciones; el texto decide qué hay en ellas.

Completa guidelines.md según lo que pidió el usuario; cualquier cosa que no mencionen mantiene su valor predeterminado. Pregunta al usuario solo si no puedes determinar qué ubicación mapear.

Completa brief.md: una fila por habitación, una por conexión, una por entrada. Cita los números de página. Este es el paso en el que decides qué está dado y qué está estimado.

Tamaños cuando el texto no da ninguno: una puerta mide 1 m, un pasillo 2.5 a 3.5 m, una celda u oficina 4 x 5 m, una habitación ordinaria 6 x 8 m, un salón 15 x 25 m, una caverna o hangar 30 x 50 m. Techos: 3 m ordinario, 5 a 8 m en salones. Un cuadrado de mapa suele ser 5 pies (1.5 m) en juegos de fantasía; un "paso" es aproximadamente un metro.

Paso 2: layout.json

Reemplaza la plantilla con las habitaciones, pasajes y entradas del sitio. docs/layout.md tiene todas las claves. Dos proyectos completos para aprender: examples/brine-chapel/ (una mazmorra de piedra, a partir de texto sin mapa) y examples/kestrel-9/ (una estación prefabricada, a partir de un PDF con un mapa boceto). Lo esencial:

  • Una habitación es un rectángulo alineado a los ejes de suelo despejado: "min" es su esquina suroeste, "max" su noreste. Deja al menos 0.6 m (un muro) entre dos habitaciones cualesquiera.
  • Un enlace une dos habitaciones a lo largo de un "path" de puntos. El primer punto está en el borde de la habitación a, el último en el borde de la habitación b; el camino sale de cada habitación en ángulo recto con el muro. "kind" es open, blocked (un derrumbe lo obstruye) o vent (un conducto o pasaje de rodillas, de habitación a habitación; "z" establece su altura). "doors": ["a", "b"] coloca puertas en esos extremos. "secret": true marca un enlace que los jugadores no deben ver: sus puertas se convierten en puertas secretas y el póster del jugador omite su marca de ruta.
  • Donde se encuentren tres o más pasajes, nombra el punto bajo "junctions" y termina los caminos allí.
  • Una entrada es una forma de acceso desde fuera del mapa: stairs, ramp, tunnel o door en un muro ("side": "N|S|E|W", "at": posición a lo largo de ese muro), o un shaft en un punto dentro de una habitación. Un pasaje que sale del mapa y está derrumbado es un tunnel con "blocked": true.
  • Una habitación que no es un rectángulo: dale su rectángulo delimitador, luego dale forma en props.py (cave_fill para cuevas y derrumbes, partition para muros internos), o divídela en dos habitaciones unidas por un enlace corto y ancho.
  • Un póster muestra un nivel. Para un sitio con varios niveles, haz un proyecto por nivel y únelos con entradas etiquetadas "Escaleras al nivel 2".

Diseña el sitio para que sea compacto y aproximadamente tan profundo como ancho; eso llena mejor una hoja 3:2 en la vista isométrica. Coloca la primera habitación cerca de (0, 0) y trabaja hacia afuera en una cuadrícula de 1 m.

python p2p.py check PROYECTO

Corrige cada error (cada mensaje dice qué cambiar). Luego abre PROYECTO/build/layout_check.png y compáralo con el mapa del libro o con tu resumen: cada habitación presente, vecinos en los lados correctos, cada conexión dibujada, puertas y entradas en los muros correctos. Repite hasta que esté correcto. No continúes con un diseño que no hayas mirado.

Paso 3: texturas

python p2p.py textures PROYECTO

Esto dibuja las texturas de superficie y las calcomanías mediante código, en unos segundos. Con un ComfyUI que haya pasado doctor --comfy URL, añade --comfy URL para que se pinten en su lugar (más lento, más rico). Ejecuta esto antes de la primera construcción; si luego rehaces texturas, construye de nuevo.

Abre dos o tres de PROYECTO/textures/_preview/<texture>_tiled.png (concrete_floor, stone_wall, rust_metal, planks, tile, ...) y elige materiales de suelo y muro que se adapten al lugar; establécelos como "floor" y "wall" en layout.json, para todo el sitio o por habitación. La tabla de materiales en docs/kit-reference.md enumera cada material y la textura que utiliza.

Paso 4: props.py, luego construye

props.py contiene una función por habitación, llamada room_<id>, que coloca lo que el resumen enumera para esa habitación. docs/props.md explica el kit y muestra cada accesorio prefabricado; docs/kit-reference.md enumera cada llamada; examples/brine-chapel/props.py es un módulo completo. Una habitación mínima:

def room_3(r, rng, P):
    mb = MB("r3_props")                                  # un constructor de malla: añade sólidos, luego crea un objeto
    pillar(mb, r.minx + 2, r.cy, h=r.h)                  # los accesorios prefabricados toman x, y en metros ...
    altar(mb, T(r.cx, r.maxy - 2, 0, rz=0))              # ... o una transformación T(x, y, z, rz=ángulo)
    box(mb, "wood", (r.cx, r.cy, 0.4), (2.0, 1.0, 0.8))  # cualquier otra cosa: cajas, cilindros, barridos
    mb.obj(P)                                            # P es la colección de esta habitación
    feat("3.altar", r.cx, r.maxy - 2, 1.1)               # un punto nombrado del que puede colgar una llamada del póster

Lo que hace que una habitación se lea en el póster:

  • Modela lo que el texto nombra primero, y dale a cada uno un feat(). Luego añade de 3 a 10 cosas ordinarias que digan para qué sirve la habitación. Una habitación vacía parece un error.
  • Las formas se leen por silueta y tono. Los materiales brillantes (tile, bone, ice, mushroom, glass) imprimen en blanco, los rojos (hazard, liquid_acid, emissive_red) imprimen en la tinta de acento, los oscuros imprimen en negro. Las cosas más pequeñas de aproximadamente 0.3 m desaparecen.
  • Mantén 1.5 m despejados frente a cada entrada, y mantén los accesorios dentro del rectángulo de la habitación.
  • Usa rng para cada elección aleatoria para que cada construcción sea idéntica.
  • Los pozos y piscinas necesitan el cuarto parámetro: def room_4(r, rng, P, cut).
python p2p.py build PROYECTO
python p2p.py render PROYECTO closeup --rooms 3,4

build toma segundos y enumera los nombres de las características que registraste. Abre PROYECTO/build/iso_preview.png (todo el modelo, sin textura) después de cada construcción, y PROYECTO/renders/closeup_<id>.png para cada habitación en la que trabajaste. Verifica: nada flotando, nada atravesando un muro, entradas despejadas, la habitación reconocible a partir del texto. Un error de Python en props.py se imprime con su número de línea; el registro completo está en build/build.log.

Los primeros planos usan la vista y el corte de poster.json, igual que el póster. Los muros se cortan a "cutaway": {"wall": 3.2} metros, y cada uno de los dos muros más cercanos al espectador oculta una tira de suelo aproximadamente tan ancha como esa altura. Eso es adecuado para salones. Si las habitaciones son pequeñas (menos de aproximadamente 8 m), bájalo ahora, a 1.5 o 2, o todo lo que esté a lo largo de los muros cercanos desaparecerá.

Paso 5: poster.json, luego el póster

Edita la plantilla: título, subtítulo, pie de página, una llamada por característica notable y los cuadros de texto. docs/poster.md tiene todas las claves.

  • Una llamada es {"feature": "3.altar", "text": "Altar de sal"}; el nombre de la característica es uno que registraste con feat(). Añade "gm_only": true para mantenerlo fuera de la versión del jugador. Mantén el texto en menos de unas ocho palabras.
  • box contiene las notas del DJ ("gm": una tabla de encuentros, temporizadores, la verdad en una línea) y lo que se les dice a los jugadores ("player"). Seis a ocho líneas cortas cada uno.
  • La versión del jugador elimina las llamadas gm_only y los enlaces marcados como "secret". La obra de arte es la misma para ambos, por lo que una habitación secreta aún se dibuja; oculta su nombre con "room_labels": {"5": {"gm": "Osario", "player": ""}} e informa al usuario que la habitación en sí se muestra.
  • Elige una paleta que se adapte al lugar (papel, tinta, blanco, acento), o mantén el valor predeterminado amarillo-verdoso ácido.
  • size_in es la hoja en pulgadas, [36, 24] a menos que el usuario haya solicitado otro tamaño. Las etiquetas se reducen con la hoja; en una pequeña, aumenta "label_scale" hasta que las llamadas sean cómodas de leer.
python p2p.py poster PROYECTO --dpi 100

Abre PROYECTO/out/poster_gm.png y poster_player.png. Verifica y corrige en poster.json:

ProblemaSolución
Una etiqueta de habitación cubre lo que importa en la habitación"room_label_offsets": {"3": [dx, dy]}, en metros este y norte. En la vista predeterminada, el este es derecha-y-arriba en la hoja y el norte es izquierda-y-arriba: [d, -d] mueve una etiqueta a la derecha, [d, d] hacia arriba
Una etiqueta de llamada se superpone a algo"side": "nw" en esa llamada coloca su etiqueta en ese lado del punto (n es arriba en la hoja); o acorta, fusiona o elimina llamadas en una habitación abarrotada
Las cosas a lo largo de los muros cercanos están ocultasbaja "cutaway": {"wall": 1.6}, luego renderiza de nuevo
Etiquetas demasiado pequeñas o demasiado grandes para el sitio"label_scale": 1.3
El título, el cuadro o la leyenda se superpone a la obra de arte"corners": {"title": "tl", "box": "br", "legend": "bl"}, o reduce el arte con "art_box"
El lado interesante del sitio está de espaldas"view": {"azimuth": 135} (prueba -45, 45, 135, -135), luego renderiza de nuevo
Los accesorios altos ocultan lo que está detrás de ellos"cutaway": {"props": {"3": 4.0}} corta los accesorios de la habitación 3 a 4 m
Toda la impresión demasiado oscura o demasiado pálida"exposure": 1.15 (más claro) o 0.9 (más oscuro)
Una llamada no apunta a nadasus coordenadas feat() en props.py están mal; corrige, construye, renderiza

Después de cambiar solo texto o etiquetas, añade --reuse-render para omitir el renderizado. Después de cambiar layout.json, props.py, la vista o el corte, construye y renderiza de nuevo. Cuando el borrador esté correcto:

python p2p.py poster PROYECTO --dpi 200

Eso escribe out/poster_gm, poster_player y poster_bare como PNG y JPEG al tamaño de la hoja (36 x 24 pulgadas son 7200 x 4800 píxeles). Abre el póster final del DJ una vez más antes de informar.

Extras opcionales

  • Mapas de batalla. python p2p.py render PROYECTO plan da un plano cenital del sitio (renders/plan.png); render PROYECTO rooms --rooms 3 da una habitación a 70 píxeles por metro (renders/room_3.png).
  • Pinturas de habitaciones (necesita ComfyUI con el LoRA de control de profundidad; consulta docs/comfyui.md). Escribe paint.json con una oración por habitación, luego: python p2p.py render PROYECTO eye y python p2p.py paint PROYECTO --comfy URL. Las pinturas en out/paintings/ mantienen el diseño de cada habitación porque el mapa de profundidad del modelo las guía. --strength (valor predeterminado 1.2, rango útil 1.0 a 1.4) establece la firmeza.

Informar

Dale al usuario: las rutas de los pósters; qué estimaste o inventaste (tamaños, posiciones, cualquier cosa que el texto dejara abierta); cualquier cosa en las pautas que no pudiste hacer; y cómo cambiar las cosas (editar los archivos, volver a ejecutar el comando).

Cuando algo sale mal

Mensaje o síntomaCausa y solución
Blender: MISSINGInstálalo o establece la variable de entorno BLENDER al ejecutable: docs/setup.md
layout.json has errors al construirEjecuta check; cada línea dice qué cambiar
callout ... names feature ..., which props.py never registeredLa ortografía difiere del nombre feat(); build imprime los nombres válidos
Las superficies son gris planotextures no se ejecutó antes de build; ejecútalo, luego construye de nuevo
Failed to retain CUDA context, o la GPU se queda sin memoriaOtro programa está usando la GPU: añade --device cpu, o --gpu NOMBRE para elegir una tarjeta
no GPU device matching ...El nombre dado a --gpu no coincide con ninguna tarjeta; doctor las enumera
El renderizado es lentoEstá en la CPU. Borrador con --dpi 100; el final aún termina, solo más tarde
Un accesorio está cortado plano en la parte superiorEs más alto que el corte; eso es intencionado. Si quieres que se vea todo, sube el corte o baja el accesorio

Skills relacionados