CommunityImagegithub.com

michaelweber/page-to-poster

Transformez un lieu d'aventure écrit (pages PDF ou texte) accompagné de directives de cartographie approximatives en un modèle 3D Blender et une affiche imprimable d'une carte isométrique en coupe, dans un style sérigraphique, avec versions pour le MJ et les joueurs.

Qu'est-ce que page-to-poster ?

Ce compétence permet de transformer une description écrite d'un lieu d'aventure (provenant d'un livre de jeu de rôle, d'une aventure PDF ou d'un texte quelconque) en une affiche de carte isométrique prête à imprimer, accompagnée d'un modèle 3D. Elle analyse le texte, génère un plan d'étage en 3D via Blender en mode sans tête, le texture et compose une affiche au format poster avec légendes, notes et boîtes de texte, le tout dans un style sérigraphique vintage. L'outil prend en charge les projets sur plusieurs niveaux, les pièces de forme irrégulière, les passages secrets et la différenciation entre la version du maître de jeu (avec toutes les informations) et celle des joueurs (sans les secrets). Elle est idéale pour les donjons, les bunkers, les vaisseaux spatiaux, les stations ou tout bâtiment décrit textuellement, et fonctionne sur n'importe quelle machine sans GPU dédié. Chaque étape produit un fichier image intermédiaire que l'utilisateur est invité à vérifier, minimisant ainsi les erreurs de modélisation.

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

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

Page to poster

Vous lisez l'aventure et écrivez cinq petits fichiers qui décrivent le lieu. Le code de ce dossier les vérifie, construit un modèle 3D dans Blender en mode sans tête, le rend et compose l'affiche.

aventure (PDF/texte) + directives
   -> brief.md        ce que le texte dit du lieu          (vous écrivez)
   -> layout.json     salles, passages, entrées, en mètres (vous écrivez ; `check` valide et le dessine)
   -> props.py        ce qui se trouve dans chaque salle   (vous écrivez ; `build` le modélise)
   -> poster.json     titre, étiquettes, légendes, boîte de texte (vous écrivez ; `poster` rend et compose)
   -> out/poster_gm.png, poster_player.png, poster_bare.png  (+ .jpg)

p2p.py se trouve dans ce dossier. Appelez-le par son chemin complet depuis le répertoire de travail de l'utilisateur, afin que les dossiers de projet atterrissent là et pas ici ; les commandes ci-dessous écrivent python p2p.py par souci de brièveté. Utilisez python3 là où python n'est pas Python 3.

Règles qui s'appliquent tout au long du processus

  1. Regardez chaque image que vous produisez. Chaque étape écrit une image ; ouvrez-la avec votre outil de lecture d'images avant de continuer. Les étapes nomment le fichier. La plupart des erreurs sont évidentes sur l'image et invisibles dans les nombres.
  2. Mètres, +x est, +y nord, sol à z = 0. Les angles dans props.py sont en radians.
  3. Dites ce que vous avez estimé. Les aventures donnent rarement des tailles ou des positions exactes. Estimez, écrivez « est. » dans brief.md, et listez les estimations lorsque vous faites votre rapport. Ne présentez jamais un fait inventé comme provenant du livre.
  4. Blender s'exécute en mode sans tête via p2p.py. N'ouvrez pas sa fenêtre et n'utilisez pas de serveur MCP Blender.
  5. Les brouillons sont peu coûteux. Affiches en brouillon à --dpi 100 ; rendez la version finale à --dpi 200 une fois le brouillon correct.
  6. Le dossier du projet contient le texte et les cartes de l'aventure. Gardez-le en dehors des dépôts publics, sauf si l'aventure vous appartient.

Étape 0 : vérifier la machine

python p2p.py doctor

Il doit se terminer par Core pipeline: ready. Si des paquets Python sont manquants : pip install -r requirements.txt. Si Blender est manquant, suivez docs/setup.md. Aucun GPU n'est nécessaire (le rendu se fait sur le CPU), et ComfyUI est optionnel : ne passez --comfy URL que si l'utilisateur vous en a fourni une (docs/comfyui.md).

Étape 1 : démarrer le projet et lire la source

python p2p.py new PROJET --source aventure.pdf --pages 53-56

PROJET est un nouveau dossier pour cette carte, par exemple cartes/le-nom-du-site dans le répertoire de travail de l'utilisateur. --pages sont les numéros de page du PDF (la première page est 1) pour le lieu à cartographier ; omettez-le pour un PDF court. Une source texte ou Markdown est copiée telle quelle. La commande écrit les modèles pour les cinq fichiers et, pour un PDF, source/page_NNN.txt et source/page_NNN.png.

Lisez tout le texte. Ensuite, regardez chaque image de page : les cartes, diagrammes et encadrés ne se trouvent que dans les images. Si le livre a une carte, cette carte détermine où se trouvent les salles ; le texte détermine ce qu'elles contiennent.

Remplissez guidelines.md à partir de ce que l'utilisateur a demandé ; tout ce qu'il n'a pas mentionné conserve sa valeur par défaut. Ne demandez à l'utilisateur que si vous ne pouvez pas déterminer quel lieu cartographier.

Remplissez brief.md : une ligne par salle, une par connexion, une par entrée. Citez les numéros de page. C'est l'étape où vous décidez ce qui est donné et ce qui est estimé.

Tailles lorsque le texte n'en donne aucune : une porte fait 1 m, un couloir 2,5 à 3,5 m, une cellule ou un bureau 4 x 5 m, une salle ordinaire 6 x 8 m, une grande salle 15 x 25 m, une caverne ou un hangar 30 x 50 m. Plafonds : 3 m pour l'ordinaire, 5 à 8 m pour les grandes salles. Un carré de carte mesure généralement 5 pieds (1,5 m) dans les jeux fantastiques ; un « pas » mesure environ un mètre.

Étape 2 : layout.json

Remplacez le modèle par les salles, passages et entrées du site. docs/layout.md contient toutes les clés. Deux projets complets pour apprendre : examples/brine-chapel/ (un donjon de pierre, à partir d'un texte sans carte) et examples/kestrel-9/ (une station préfabriquée, à partir d'un PDF avec une carte schématique). Les essentiels :

  • Une salle est un rectangle aligné sur les axes de sol dégagé : « min » est son coin sud-ouest, « max » son coin nord-est. Laissez au moins 0,6 m (un mur) entre deux salles.
  • Un lien relie deux salles le long d'un « path » de points. Le premier point se trouve sur le bord de la salle a, le dernier sur le bord de la salle b ; le chemin quitte chaque salle à angle droit par rapport au mur. « kind » est open, blocked (un effondrement le bouche) ou vent (un conduit ou un passage rampant, de salle à salle ; « z » définit sa hauteur). « doors » : [« a », « b »] place des portes à ces extrémités. « secret » : true marque un lien que les joueurs ne doivent pas voir : ses portes deviennent des portes secrètes et l'affiche joueur omet son tracé.
  • Là où trois passages ou plus se rencontrent, nommez l'endroit sous « junctions » et terminez les chemins là.
  • Une entrée est un moyen d'entrer depuis l'extérieur de la carte : stairs, ramp, tunnel ou door sur un mur (« side » : « N|S|E|W », « at » : position le long de ce mur), ou un shaft à un point à l'intérieur d'une salle. Un passage qui quitte la carte et est effondré est un tunnel avec « blocked » : true.
  • Une salle qui n'est pas un rectangle : donnez-lui son rectangle englobant, puis façonnez-la dans props.py (cave_fill pour les cavernes et effondrements, partition pour les murs intérieurs), ou divisez-la en deux salles reliées par un lien court et large.
  • Une affiche montre un seul niveau. Pour un site à plusieurs niveaux, faites un projet par niveau et reliez-les par des entrées étiquetées « Escaliers vers le niveau 2 ».

Disposez le site de manière à ce qu'il soit compact et à peu près aussi profond que large ; cela remplit le mieux une feuille 3:2 dans la vue isométrique. Placez la première salle près de (0, 0) et travaillez vers l'extérieur sur une grille de 1 m.

python p2p.py check PROJET

Corrigez chaque erreur (chaque message indique quoi modifier). Ouvrez ensuite PROJET/build/layout_check.png et comparez-le avec la carte du livre ou avec votre brief : chaque salle présente, les voisins du bon côté, chaque connexion dessinée, les portes et entrées sur les bons murs. Répétez jusqu'à ce que ce soit correct. Ne continuez pas avec une disposition que vous n'avez pas regardée.

Étape 3 : textures

python p2p.py textures PROJET

Cela dessine les textures de surface et les décalcomanies par code, en quelques secondes. Avec un ComfyUI qui a passé doctor --comfy URL, ajoutez --comfy URL pour les faire peindre à la place (plus lent, plus riche). Exécutez ceci avant la première construction ; si vous refaites les textures plus tard, reconstruisez.

Ouvrez deux ou trois des PROJET/textures/_preview/<texture>_tiled.png (concrete_floor, stone_wall, rust_metal, planks, tile, ...) et choisissez des matériaux de sol et de mur qui conviennent au lieu ; définissez-les comme « floor » et « wall » dans layout.json, pour le site ou par salle. Le tableau des matériaux dans docs/kit-reference.md liste chaque matériau et la texture qu'il utilise.

Étape 4 : props.py, puis build

props.py contient une fonction par salle, nommée room_<id>, qui place ce que le brief liste pour cette salle. docs/props.md explique le kit et montre chaque accessoire prêt à l'emploi ; docs/kit-reference.md liste chaque appel ; examples/brine-chapel/props.py est un module complet. Une salle minimale :

def room_3(r, rng, P):
    mb = MB("r3_props")                                  # un constructeur de maillage : ajoutez des solides, puis créez un objet
    pillar(mb, r.minx + 2, r.cy, h=r.h)                  # les accessoires prêts à l'emploi prennent x, y en mètres ...
    altar(mb, T(r.cx, r.maxy - 2, 0, rz=0))              # ... ou une transformée T(x, y, z, rz=angle)
    box(mb, "wood", (r.cx, r.cy, 0.4), (2.0, 1.0, 0.8))  # tout le reste : boîtes, cylindres, balayages
    mb.obj(P)                                            # P est la collection de cette salle
    feat("3.altar", r.cx, r.maxy - 2, 1.1)               # un point nommé auquel une légende d'affiche peut s'accrocher

Ce qui rend une salle lisible sur l'affiche :

  • Modélisez ce que le texte nomme en premier, et donnez à chacun un feat(). Ajoutez ensuite 3 à 10 choses ordinaires qui disent à quoi sert la salle. Une salle vide ressemble à une erreur.
  • Les formes se lisent par silhouette et tonalité. Les matériaux brillants (tile, bone, ice, mushroom, glass) s'impriment en blanc, les rouges (hazard, liquid_acid, emissive_red) s'impriment dans l'encre d'accent, les foncés s'impriment en noir. Les choses plus petites qu'environ 0,3 m disparaissent.
  • Gardez 1,5 m de dégagement devant chaque porte, et gardez les accessoires à l'intérieur du rectangle de la salle.
  • Utilisez rng pour chaque choix aléatoire afin que chaque construction soit identique.
  • Les fosses et les bassins ont besoin du quatrième paramètre : def room_4(r, rng, P, cut).
python p2p.py build PROJET
python p2p.py render PROJET closeup --rooms 3,4

build prend quelques secondes et liste les noms des caractéristiques que vous avez enregistrées. Ouvrez PROJET/build/iso_preview.png (le modèle entier, non texturé) après chaque construction, et PROJET/renders/closeup_<id>.png pour chaque salle sur laquelle vous avez travaillé. Vérifiez : rien ne flotte, rien ne traverse un mur, les portes sont dégagées, la salle est reconnaissable d'après le texte. Une erreur Python dans props.py est imprimée avec son numéro de ligne ; le journal complet est build/build.log.

Les gros plans utilisent la vue et la coupe de poster.json, comme le fera l'affiche. Les murs sont ouverts à « cutaway » : {« wall » : 3.2} mètres, et chacun des deux murs les plus proches du spectateur cache une bande de sol à peu près aussi large que cette hauteur. Cela convient aux grandes salles. Si les salles sont petites (moins d'environ 8 m), abaissez-le maintenant, à 1,5 ou 2, sinon tout ce qui se trouve le long des murs proches disparaît.

Étape 5 : poster.json, puis l'affiche

Modifiez le modèle : titre, sous-titre, pied de page, une légende par caractéristique notable, et les boîtes de texte. docs/poster.md contient toutes les clés.

  • Une légende est {« feature » : « 3.altar », « text » : « Autel de sel »} ; le nom de la caractéristique est celui que vous avez enregistré avec feat(). Ajoutez « gm_only » : true pour le garder hors de la version joueur. Gardez le texte sous environ huit mots.
  • « box » contient les notes du MJ (« gm » : une table de rencontre, des minuteries, la vérité en une ligne) et ce qui est dit aux joueurs (« player »). Six à huit lignes courtes chacun.
  • La version joueur supprime les légendes gm_only et les liens marqués « secret ». L'illustration est la même pour les deux, donc une salle secrète est toujours dessinée ; cachez son nom avec « room_labels » : {« 5 » : {« gm » : « Ossuaire », « player » : « »}} et dites à l'utilisateur que la salle elle-même apparaît.
  • Choisissez une palette qui convient au lieu (papier, encre, blanc, accent), ou conservez le défaut jaune-vert acide.
  • « size_in » est la feuille en pouces, [36, 24] sauf si l'utilisateur a demandé une autre taille. Les étiquettes rétrécissent avec la feuille ; sur une petite, augmentez « label_scale » jusqu'à ce que les légendes soient confortables à lire.
python p2p.py poster PROJET --dpi 100

Ouvrez PROJET/out/poster_gm.png et poster_player.png. Vérifiez et corrigez dans poster.json :

ProblèmeCorrectif
Une étiquette de salle couvre ce qui est important dans la salle« room_label_offsets » : {« 3 » : [dx, dy]}, en mètres vers l'est et le nord. Dans la vue par défaut, l'est est à droite et en haut sur la feuille, le nord est à gauche et en haut : [d, -d] déplace une étiquette vers la droite, [d, d] vers le haut
Une étiquette de légende repose sur quelque chose« side » : « nw » sur cette légende place son étiquette de ce côté du point (n est le haut de la feuille) ; ou raccourcissez, fusionnez ou supprimez les légendes dans une salle bondée
Les éléments le long des murs proches sont cachésabaissez « cutaway » : {« wall » : 1.6}, puis rendez à nouveau
Étiquettes trop petites ou trop grandes pour le site« label_scale » : 1.3
Le titre, la boîte ou la légende repose sur l'illustration« corners » : {« title » : « tl », « box » : « br », « legend » : « bl »}, ou réduisez l'illustration avec « art_box »
Le côté intéressant du site fait face à l'opposé« view » : {« azimuth » : 135} (essayez -45, 45, 135, -135), puis rendez à nouveau
Les accessoires hauts cachent ce qui est derrière« cutaway » : {« props » : {« 3 » : 4.0}} coupe les accessoires de la salle 3 à 4 m
L'impression entière est trop sombre ou trop pâle« exposure » : 1.15 (plus clair) ou 0.9 (plus foncé)
Une légende ne pointe sur rienses coordonnées feat() dans props.py sont erronées ; corrigez, construisez, rendez

Après avoir modifié uniquement le texte ou les étiquettes, ajoutez --reuse-render pour ignorer le rendu. Après avoir modifié layout.json, props.py, la vue ou la coupe, construisez et rendez à nouveau. Lorsque le brouillon est correct :

python p2p.py poster PROJET --dpi 200

Cela écrit out/poster_gm, poster_player et poster_bare en PNG et JPEG à la taille de la feuille (36 x 24 pouces correspond à 7200 x 4800 pixels). Ouvrez l'affiche MJ finale une fois de plus avant de faire votre rapport.

Extras optionnels

  • Cartes de combat. python p2p.py render PROJET plan donne un plan vu de dessus du site (renders/plan.png) ; render PROJET rooms --rooms 3 donne une salle à 70 pixels par mètre (renders/room_3.png).
  • Peintures de salle (nécessite ComfyUI avec le LoRA de contrôle de profondeur ; voir docs/comfyui.md). Écrivez paint.json avec une phrase par salle, puis : python p2p.py render PROJET eye et python p2p.py paint PROJET --comfy URL. Les peintures dans out/paintings/ conservent la disposition de chaque salle car la carte de profondeur du modèle les guide. --strength (par défaut 1,2, plage utile 1,0 à 1,4) définit la fermeté.

Rapport

Donnez à l'utilisateur : les chemins des affiches ; ce que vous avez estimé ou inventé (tailles, positions, tout ce que le texte laissait ouvert) ; tout ce que vous n'avez pas pu faire dans les directives ; et comment modifier les choses (modifiez les fichiers, relancez la commande).

Quand quelque chose tourne mal

Message ou symptômeCause et correctif
Blender: MISSINGInstallez-le ou définissez la variable d'environnement BLENDER vers l'exécutable : docs/setup.md
layout.json has errors lors de la constructionExécutez check ; chaque ligne indique quoi modifier
callout ... names feature ..., which props.py never registeredL'orthographe diffère du nom feat() ; build imprime les noms valides
Les surfaces sont gris plattextures n'a pas été exécuté avant build ; exécutez-le, puis reconstruisez
Failed to retain CUDA context, ou le GPU manque de mémoireUn autre programme utilise le GPU : ajoutez --device cpu, ou --gpu NOM pour choisir une carte
no GPU device matching ...Le nom donné à --gpu ne correspond à aucune carte ; doctor les liste
Le rendu est lentIl est sur le CPU. Brouillon à --dpi 100 ; la version finale finit quand même, juste plus tard
Un accessoire est coupé net en hautIl est plus haut que la coupe ; c'est intentionnel. Relevez la coupe du mur, ou ajoutez une coupe spécifique pour accessoires
Les textures ne collent pas à la géométrieReconstruisez après avoir changé la disposition ou les accessoires : le build refait les UV
Le script s'arrête sans messageRegardez build/build.log et out/out.log

Skills associés