CommunityBildgithub.com

michaelweber/page-to-poster

Verwandle eine schriftlich beschriebene Abenteuerlocation (PDF oder Text) mit groben Kartierungsvorgaben in ein 3D-Blender-Modell und ein druckfertiges isometrisches Schnittansicht-Poster im Siebdruck-Stil, inklusive Spielleiter- und Spielerversion.

Was ist page-to-poster?

Dieses Skill verwandelt eine schriftlich beschriebene Abenteuerlocation (z.B. aus einem PDF oder Text eines Rollenspielabenteuers) in ein 3D-Modell mit Blender und erstellt daraus ein druckfertiges Poster im Siebdruck-Stil. Es richtet sich an Entwickler und KI-Agenten-Nutzer, die Dungeons, Bunker, Schiffe, Stationen oder Gebäude für Rollenspiele modellieren möchten. Das Skill unterstützt die Plattformen Claude Code, Cursor und Codex und arbeitet auf Basis eines Headless-Blender-Setups. Es führt den Benutzer durch einen strukturierten Workflow: Zuerst werden fünf Konfigurationsdateien (brief.md, layout.json, props.py, poster.json) erstellt, die die Location beschreiben. Dann werden mit `p2p.py` ein Schritt zur Überprüfung (`check`), eine Texturgenerierung (`textures`), der 3D-Bau (`build`) und schließlich die Poster-Renderung (`poster`) ausgeführt. Das Ergebnis sind zwei Posterversionen (für Spielleiter und Spieler) und eine nackte Version als PNG und JPEG, die in hoher Auflösung (z.B. 7200x4800 Pixel bei 200 DPI) ausgegeben werden.

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

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Seite zu Poster

Lesen Sie das Abenteuer und schreiben Sie fünf kleine Dateien, die den Ort beschreiben. Der Code in diesem Ordner prüft sie, baut ein 3D-Modell im Headless-Blender, rendert es und setzt das Poster zusammen.

Abenteuer (PDF/Text) + Richtlinien
   -> brief.md        Was der Text über den Ort sagt (Sie schreiben)
   -> layout.json     Räume, Gänge, Eingänge, in Metern       (Sie schreiben; `check` validiert und zeichnet es)
   -> props.py        Was in jedem Raum steht                 (Sie schreiben; `build` modelliert es)
   -> poster.json     Titel, Beschriftungen, Callouts, Textbox (Sie schreiben; `poster` rendert und setzt zusammen)
   -> out/poster_gm.png, poster_player.png, poster_bare.png  (+ .jpg)

p2p.py befindet sich in diesem Ordner. Rufen Sie es mit dem vollständigen Pfad aus dem Arbeitsverzeichnis des Benutzers auf, damit Projektordner dort und nicht hier landen; die folgenden Befehle schreiben der Einfachheit halber python p2p.py. Verwenden Sie python3, wo python nicht Python 3 ist.

Durchgängig geltende Regeln

  1. Betrachten Sie jedes von Ihnen erstellte Bild. Jeder Schritt schreibt ein Bild; öffnen Sie es mit Ihrem Bildlese-Werkzeug, bevor Sie fortfahren. Die Schritte benennen die Datei. Die meisten Fehler sind im Bild offensichtlich und in den Zahlen unsichtbar.
  2. Meter, +x Osten, +y Norden, Boden bei z = 0. Winkel in props.py sind Bogenmaß.
  3. Sagen Sie, was Sie geschätzt haben. Abenteuer geben selten Größen oder genaue Positionen an. Schätzen Sie, schreiben Sie „geschätzt" in brief.md, und listen Sie die Schätzungen auf, wenn Sie Bericht erstatten. Präsentieren Sie niemals eine erfundene Tatsache als die des Buches.
  4. Blender läuft headless über p2p.py. Öffnen Sie sein Fenster nicht und verwenden Sie keinen Blender-MCP-Server.
  5. Entwürfe sind günstig. Entwurfsposter mit --dpi 100; rendern Sie das finale mit --dpi 200, sobald der Entwurf richtig ist.
  6. Der Projektordner enthält den Text und die Karten des Abenteuers. Halten Sie ihn aus öffentlichen Repositories heraus, es sei denn, das Abenteuer gehört Ihnen zur Veröffentlichung.

Schritt 0: Überprüfen Sie die Maschine

python p2p.py doctor

Er muss enden mit: Core pipeline: ready. Falls Python-Pakete fehlen: pip install -r requirements.txt. Falls Blender fehlt, folgen Sie docs/setup.md. Keine GPU nötig (Rendering fällt auf die CPU zurück), und ComfyUI ist optional: Übergeben Sie --comfy URL nur, wenn der Benutzer Ihnen eine gegeben hat (docs/comfyui.md).

Schritt 1: Starten Sie das Projekt und lesen Sie die Quelle

python p2p.py new PROJEKT --source abenteuer.pdf --pages 53-56

PROJEKT ist ein neuer Ordner für diese Karte, z.B. maps/der-ort-name im Arbeitsverzeichnis des Benutzers. --pages sind die eigenen Seitenzahlen des PDFs (erste Seite ist 1) für die zu kartierende Location; lassen Sie es bei einem kurzen PDF weg. Eine Text- oder Markdown-Quelle wird unverändert kopiert. Der Befehl schreibt Vorlagen für die fünf Dateien und, für ein PDF, source/page_NNN.txt und source/page_NNN.png.

Lesen Sie den gesamten Text. Dann betrachten Sie jedes Seitenbild: Karten, Diagramme und Randspalten sind nur in den Bildern. Wenn das Buch eine Karte hat, entscheidet diese Karte, wo Räume sitzen; der Text entscheidet, was darin ist.

Füllen Sie guidelines.md basierend auf dem aus, worum der Benutzer gebeten hat; alles, was er nicht erwähnt hat, behält seinen Standard. Fragen Sie den Benutzer nur, wenn Sie nicht erkennen können, welche Location kartiert werden soll.

Füllen Sie brief.md: eine Zeile pro Raum, eine pro Verbindung, eine pro Eingang. Zitieren Sie Seitennummern. Dies ist der Schritt, in dem Sie entscheiden, was gegeben und was geschätzt ist.

Größen, wenn der Text keine angibt: eine Tür ist 1 m, ein Korridor 2,5 bis 3,5 m, eine Zelle oder ein Büro 4 x 5 m, ein gewöhnlicher Raum 6 x 8 m, eine Halle 15 x 25 m, eine Höhle oder ein Hangar 30 x 50 m. Decken: 3 m gewöhnlich, 5 bis 8 m Hallen. Ein Kartenquadrat ist in Fantasy-Spielen normalerweise 5 Fuß (1,5 m); ein „Schritt" ist etwa ein Meter.

Schritt 2: layout.json

Ersetzen Sie die Vorlage mit den Räumen, Gängen und Eingängen des Ortes. docs/layout.md hat jeden Schlüssel. Zwei vollständige Projekte zum Lernen: examples/brine-chapel/ (ein Steindungeon, aus Text ohne Karte) und examples/kestrel-9/ (eine vorgefertigte Station, aus einem PDF mit einer Skizzenkarte). Das Wesentliche:

  • Ein Raum ist ein achsenausgerichtetes Rechteck aus klarem Boden: min ist seine Südwestecke, max seine Nordostecke. Lassen Sie mindestens 0,6 m (eine Wand) zwischen zwei beliebigen Räumen.
  • Eine Verbindung verbindet zwei Räume entlang eines Pfades von Punkten. Der erste Punkt liegt auf der Kante von Raum a, der letzte auf der Kante von Raum b; der Pfad verlässt jeden Raum im rechten Winkel zur Wand. kind ist open, blocked (ein Einsturz versperrt ihn) oder vent (ein Kanal oder Kriechgang, Raum zu Raum; z setzt seine Höhe). doors: ["a", "b"] setzt Türen an diesen Enden. secret: true markiert eine Verbindung, die die Spieler nicht sehen dürfen: Ihre Türen werden zu Geheimtüren, und das Spielerposter lässt ihre Routenmarkierung weg.
  • Wo drei oder mehr Gänge aufeinandertreffen, benennen Sie den Ort unter junctions und lassen Sie die Pfade dort enden.
  • Ein Eingang ist ein Weg von außerhalb der Karte: stairs, ramp, tunnel oder door an einer Wand (side: "N|S|E|W", at: Position entlang dieser Wand), oder ein shaft an einem Punkt in einem Raum. Ein Gang, der die Karte verlässt und eingestürzt ist, ist ein tunnel mit blocked: true.
  • Ein Raum, der kein Rechteck ist: Geben Sie ihm sein Begrenzungsrechteck, formen Sie es dann in props.py (cave_fill für Höhlen und Einstürze, partition für Innenwände), oder teilen Sie ihn in zwei Räume auf, die durch eine kurze breite Verbindung verbunden sind.
  • Ein Poster zeigt eine Ebene. Für einen Ort mit mehreren Ebenen erstellen Sie ein Projekt pro Ebene und verbinden Sie sie mit Eingängen, die als „Treppe zu Ebene 2" beschriftet sind.

Legen Sie den Ort kompakt und annähernd so tief wie breit an; das füllt ein 3:2-Blatt in der isometrischen Ansicht am besten. Setzen Sie den ersten Raum nahe (0, 0) und arbeiten Sie sich auf einem 1-m-Raster nach außen.

python p2p.py check PROJEKT

Beheben Sie jeden Fehler (jede Nachricht sagt, was zu ändern ist). Dann öffnen Sie PROJEKT/build/layout_check.png und vergleichen Sie es mit der Buchkarte oder Ihrem Briefing: jeder Raum vorhanden, Nachbarn auf den richtigen Seiten, jede Verbindung gezeichnet, Türen und Eingänge auf den richtigen Wänden. Wiederholen Sie dies, bis es richtig ist. Fahren Sie nicht mit einem Layout fort, das Sie nicht angesehen haben.

Schritt 3: Texturen

python p2p.py textures PROJEKT

Dies zeichnet die Oberflächentexturen und Dekorationen per Code, in wenigen Sekunden. Mit einem ComfyUI, das doctor --comfy URL bestanden hat, fügen Sie --comfy URL hinzu, um sie stattdessen malen zu lassen (langsamer, reichhaltiger). Führen Sie dies vor dem ersten Bau aus; wenn Sie später Texturen neu erstellen, bauen Sie erneut.

Öffnen Sie zwei oder drei von PROJEKT/textures/_preview/<textur>_tiled.png (concrete_floor, stone_wall, rust_metal, planks, tile, ...) und wählen Sie Boden- und Wandmaterialien, die zum Ort passen; setzen Sie sie als floor und wall in layout.json, für den Ort oder pro Raum. Die Materialtabelle in docs/kit-reference.md listet jedes Material und die verwendete Textur auf.

Schritt 4: props.py, dann bauen

props.py enthält eine Funktion pro Raum, benannt room_<id>, die platziert, was das Briefing für diesen Raum auflistet. docs/props.md erklärt das Kit und zeigt jedes fertige Prop; docs/kit-reference.md listet jeden Aufruf auf; examples/brine-chapel/props.py ist ein vollständiges Modul. Ein minimaler Raum:

def room_3(r, rng, P):
    mb = MB("r3_props")                                  # ein Mesh-Builder: Festkörper hinzufügen, dann ein Objekt erstellen
    pillar(mb, r.minx + 2, r.cy, h=r.h)                  # fertige Props nehmen x, y in Metern ...
    altar(mb, T(r.cx, r.maxy - 2, 0, rz=0))              # ... oder eine Transform T(x, y, z, rz=Winkel)
    box(mb, "wood", (r.cx, r.cy, 0.4), (2.0, 1.0, 0.8))  # alles andere: Boxen, Zylinder, Sweeps
    mb.obj(P)                                            # P ist die Sammlung dieses Raums
    feat("3.altar", r.cx, r.maxy - 2, 1.1)               # ein benannter Punkt, an den ein Poster-Callout angehängt werden kann

Was einen Raum auf dem Poster lesbar macht:

  • Modellieren Sie, was der Text zuerst nennt, und geben Sie jedem ein feat(). Fügen Sie dann 3 bis 10 gewöhnliche Dinge hinzu, die sagen, wofür der Raum ist. Ein leerer Raum sieht nach einem Fehler aus.
  • Formen lesen sich durch Silhouette und Ton. Helle Materialien (tile, bone, ice, mushroom, glass) drucken weiß, rote (hazard, liquid_acid, emissive_red) drucken in der Akzentfarbe, dunkle drucken schwarz. Dinge, die kleiner als etwa 0,3 m sind, verschwinden.
  • Halten Sie 1,5 m vor jeder Türöffnung frei und halten Sie Props innerhalb des Raumrechtecks.
  • Verwenden Sie rng für jede zufällige Wahl, damit jeder Bau identisch ist.
  • Gruben und Becken benötigen den vierten Parameter: def room_4(r, rng, P, cut).
python p2p.py build PROJEKT
python p2p.py render PROJEKT closeup --rooms 3,4

build dauert Sekunden und listet die von Ihnen registrierten Feature-Namen auf. Öffnen Sie PROJEKT/build/iso_preview.png (das gesamte Modell, untexturiert) nach jedem Bau und PROJEKT/renders/closeup_<id>.png für jeden Raum, an dem Sie gearbeitet haben. Überprüfen Sie: nichts schwebt, nichts geht durch eine Wand, Türöffnungen sind frei, der Raum ist aus dem Text erkennbar. Ein Python-Fehler in props.py wird mit seiner Zeilennummer ausgegeben; das vollständige Protokoll ist build/build.log.

Nahaufnahmen verwenden die Ansicht und den Schnitt aus poster.json, genau wie das Poster. Wände werden bei cutaway: {wall: 3.2} Metern aufgeschnitten, und jede der beiden dem Betrachter nächsten Wände verbirgt einen Streifen Boden, etwa so breit wie diese Höhe. Das passt zu Hallen. Wenn die Räume klein sind (unter etwa 8 m), senken Sie ihn jetzt auf 1,5 oder 2, oder alles entlang der nahen Wände verschwindet.

Schritt 5: poster.json, dann das Poster

Bearbeiten Sie die Vorlage: Titel, Untertitel, Fußzeile, ein Callout pro bemerkenswertem Feature und die Textboxen. docs/poster.md hat jeden Schlüssel.

  • Ein Callout ist {"feature": "3.altar", "text": "Salzaltar"}; der Feature-Name ist einer, den Sie mit feat() registriert haben. Fügen Sie "gm_only": true hinzu, um es von der Spielerversion fernzuhalten. Halten Sie den Text unter etwa acht Wörtern.
  • box enthält die Notizen des Spielleiters ("gm": eine Begegnungstabelle, Timer, die einzeilige Wahrheit) und was den Spielern gesagt wird ("player"). Jeweils sechs bis acht kurze Zeilen.
  • Die Spieler-Version entfernt gm_only-Callouts und als "secret" markierte Verbindungen. Die Kunst ist für beide gleich, daher wird ein geheimer Raum trotzdem gezeichnet; verstecken Sie seinen Namen mit "room_labels": {"5": {"gm": "Ossarium", "player": ""}} und sagen Sie dem Benutzer, dass der Raum selbst sichtbar ist.
  • Wählen Sie eine Palette, die zum Ort passt (Papier, Tinte, Weiß, Akzent), oder behalten Sie die Standard-Acid-Gelb-Grün-Farbe.
  • size_in ist das Blatt in Zoll, [36, 24] sofern der Benutzer keine andere Größe verlangt hat. Beschriftungen schrumpfen mit dem Blatt; bei einem kleinen, erhöhen Sie "label_scale", bis die Callouts angenehm zu lesen sind.
python p2p.py poster PROJEKT --dpi 100

Öffnen Sie PROJEKT/out/poster_gm.png und poster_player.png. Überprüfen und beheben Sie in poster.json:

ProblemBehebung
Eine Raumbeschriftung überdeckt das Wichtige im Raum"room_label_offsets": {"3": [dx, dy]}, in Metern Osten und Norden. In der Standardansicht ist Osten rechts-oben auf dem Blatt und Norden links-oben: [d, -d] bewegt eine Beschriftung nach rechts, [d, d] nach oben
Ein Callout-Tag sitzt auf etwas"side": "nw" bei diesem Callout setzt sein Tag auf diese Seite des Punktes (n ist oben auf dem Blatt); oder kürzen, zusammenführen oder Callouts in einem überfüllten Raum entfernen
Dinge entlang der nahen Wände sind verborgensenken Sie "cutaway": {"wall": 1.6}, dann rendern Sie erneut
Beschriftungen zu klein oder zu groß für den Ort"label_scale": 1.3
Titel, Box oder Legende sitzt auf der Kunst"corners": {"title": "tl", "box": "br", "legend": "bl"}, oder verkleinern Sie die Kunst mit "art_box"
Die interessante Seite des Ortes zeigt weg"view": {"azimuth": 135} (versuchen Sie -45, 45, 135, -135), dann rendern Sie erneut
Hohe Props verbergen, was dahinter ist"cutaway": {"props": {"3": 4.0}} schneidet die Props von Raum 3 bei 4 m ab
Gesamtdruck zu dunkel oder zu blass"exposure": 1.15 (heller) oder 0.9 (dunkler)
Ein Callout zeigt ins Nichtsseine feat()-Koordinaten in props.py sind falsch; beheben, bauen, rendern

Nachdem Sie nur Text oder Beschriftungen geändert haben, fügen Sie --reuse-render hinzu, um das Rendern zu überspringen. Nachdem Sie layout.json, props.py, die Ansicht oder den Schnitt geändert haben, bauen und rendern Sie erneut. Wenn der Entwurf richtig ist:

python p2p.py poster PROJEKT --dpi 200

Dies schreibt out/poster_gm, poster_player und poster_bare als PNG und JPEG in Blattgröße (36 x 24 Zoll sind 7200 x 4800 Pixel). Öffnen Sie das finale GM-Poster noch einmal, bevor Sie Bericht erstatten.

Optionale Extras

  • Schlachtkarten. python p2p.py render PROJEKT plan ergibt eine Draufsicht des Ortes (renders/plan.png); render PROJEKT rooms --rooms 3 ergibt einen Raum bei 70 Pixeln pro Meter (renders/room_3.png).
  • Raumgemälde (benötigt ComfyUI mit dem Tiefensteuerungs-LoRA; siehe docs/comfyui.md). Schreiben Sie paint.json mit einem Satz pro Raum, dann: python p2p.py render PROJEKT eye und python p2p.py paint PROJEKT --comfy URL. Die Gemälde in out/paintings/ behalten das Layout jedes Raums bei, weil die Tiefenkarte des Modells sie steuert. --strength (Standard 1,2, nutzbarer Bereich 1,0 bis 1,4) legt fest, wie fest.

Bericht erstatten

Geben Sie dem Benutzer: die Pfade der Poster; was Sie geschätzt oder erfunden haben (Größen, Positionen, alles, was der Text offen ließ); alles in den Richtlinien, was Sie nicht tun konnten; und wie man Dinge ändert (die Dateien bearbeiten, den Befehl erneut ausführen).

Wenn etwas schief geht

Nachricht oder SymptomUrsache und Behebung
Blender: MISSINGInstallieren Sie es oder setzen Sie die Umgebungsvariable BLENDER auf die ausführbare Datei: docs/setup.md
layout.json has errors beim BauFühren Sie check aus; jede Zeile sagt, was zu ändern ist
callout ... names feature ..., which props.py never registeredSchreibweise weicht vom feat()-Namen ab; build gibt die gültigen Namen aus
Oberflächen sind flach grautextures wurde nicht vor build ausgeführt; führen Sie es aus, dann bauen Sie erneut
Failed to retain CUDA context, oder die GPU hat keinen Speicher mehrEin anderes Programm verwendet die GPU: fügen Sie --device cpu hinzu, oder --gpu NAME, um eine Karte auszuwählen
no GPU device matching ...Der für --gpu angegebene Name passt zu keiner Karte; doctor listet sie auf
Das Rendern ist langsamEs ist auf der CPU. Entwurf mit --dpi 100; das Finale wird trotzdem fertig, nur später
Ein Prop ist oben flach abgeschnittenEs ist höher als der Schnitt; das ist beabsichtigt. Erhöhen Sie "cutaway": {"wall": ...} für diesen Raum, wenn Sie die Spitze sehen wollen, oder lassen Sie es für einen abgeschnittenen Effekt.

Verwandte Skills