Files
llamamod/notes/debugviz.md
T

18 KiB
Raw Blame History

Debug-Ansicht: Kollision & unsichtbare Objekte im Spiel (tools/debugviz.py)

Stand: 2026-11-06. Zeigt in allen 34 Level-WADs die Kollisionsgeometrie (auch unsichtbare Wände/Böden, Todesebenen) und unsichtbare Logik-Objekte (Kameras, Trigger, Ausgänge, Checkpoints, Kraft-/Box-Zonen, Umgebungszonen …) direkt im Spiel – als farbiges Drahtgitter, unsichtbare Flächen zusätzlich halbtransparent gefüllt. Spielmechanik bleibt unverändert.

Benutzung

lama-mod debug an      # Debug-WADs zuschalten (baut das Overlay neu, aktiviert damit auch andere Mods)
lama-mod debug aus     # wieder Original-Level (testlevel-Mod bleibt, wie sie war)
lama-mod debug         # Status

Wirkt beim nächsten Laden eines Levels (am besten Spiel neu starten). Nicht umschalten, während ein Level lädt. Mod-Ordner: ~/.wine-lama/mods/debug/wads/*.wad (gleiche Namen wie auf der CD, z. B. t1l1m001.wad); die Datei ~/.wine-lama/mods/debug/.aus = abgeschaltet. Reihenfolge im Overlay: alphabetisch, spätere gewinnen → testlevel (T1L9*) bleibt immer, Debug-WADs ersetzen nur die Original-Level. lama-mod an|aus|status funktionieren wie vorher (an überspringt Mod-Ordner mit .aus). Sicherung des alten Skripts: deb-build/lama-mod.bak-vor-debug.

Neu bauen (z. B. nach Änderungen an Farben):

python3 tools/debugviz.py --all ~/.wine-lama/mods/debug/wads --report /tmp/dv.json
python3 tools/debugviz.py IN.wad OUT.wad [--no-fill] [--no-markers] [--no-coll] [--opaque]
                          [--width 0.007] [--offset 0.012]

Laufzeit ≈ 10–40 s je Level.

Farblegende

Alle Farben sind RGB (0–255). Linien = Drahtgitter (deckend), Füllung = halbtransparent (Alpha 96/255, Blendmodus 1 der Engine).

Kollisionsflächen (Rahmen um jedes Kollisionspolygon der Welt-Kacheln)

Kantenfarbe nach Lage, solange das Material 0 (normal) ist:

Farbe RGB Bedeutung
Grün (Linien) 40,255,40 begehbarer Boden (Normale steil nach oben, ny ≥ 0,7)
Gelbgrün (Linien) 200,255,0 Schräge (0,2 ≤ ny < 0,7)
Blau (Linien) 0,150,255 Wand (−0,2 < ny < 0,2)
Orange (Linien) 255,130,0 Decke / Überhang (ny ≤ −0,2)
Rot, halbtransparent gefüllt 255,0,0 unsichtbare Kollision: keine Render-Fläche innerhalb ±0,5 Einheiten entlang der Normale (= unsichtbare Wand/Boden/Decke). Die Kantenfarbe zeigt weiterhin Lage bzw. Material.

Kollision mit Sondermaterial (Byte 1 des Kollisionspolygons ≠ 0): Kanten in Materialfarbe, bei unsichtbaren Polygonen auch die Füllung in Materialfarbe (statt Rot):

Material Farbe RGB Bedeutung
30 (0x1e) Magenta, nur Linien (keine Füllung) 255,0,255 kein Boden / Fall- und Todesebene: Spieler fällt hindurch (FUN_004036d0 setzt Boden = keiner). Meist Modell 0 bei y ≈ −2,75 in jeder Rasterzelle unter den Inseln → magentafarbenes Gitter = Absturz-/Todeszone.
16 Violett, immer gefüllt 190,60,255 Ausgangs-Boden: LevelDoor löst nur aus, wenn der Spieler auf Material 16 in der Türzelle steht
17 (0x11) Gelb 255,255,0 selektive Kollision: wirkt nur für Objekte mit Laufzeit-Flag 0x8000000 (Strat+0xe8)
18 (0x12) Hellorange/Pfirsich 255,200,120 selektive Kollision: wirkt NICHT für Objekte mit Flag 0x8000000
1 Türkis 0,200,200 Sondermaterial, Bedeutung unbekannt (Strat-Skripte werten es aus)
2 Weiß 255,255,255 dto.
3 Rosa 255,120,170 dto. (häufig in Welt 5 Berg)
5 Mint 120,255,200 dto. (Fluss, Achterbahn)
6 Lavendel 160,120,255 dto.
7 Hellorange 255,170,80 dto.
8 Dunkelgrün 0,130,60 dto.
9 Hellblau 150,200,255 dto.
10 Oliv 150,150,0 dto.
11 Braun 150,90,40 dto.
13 Lachs 255,130,110 dto.
15 Hellgelb 255,255,150 dto.
20 Petrol 0,120,140 dto.
26 Dunkelviolett 110,40,160 dto.
27 Silber 190,190,190 dto. (Verfolgungsjagd t1l2m003)
28 Pink 255,0,140 dto.
33 Dunkelrot 150,0,0 dto.
255 Fast schwarz 20,20,20 dto.
sonstige Hellgrau 230,230,230 (kommt in den Originalen nicht vor)

Objekt-Marker (unsichtbare Strats, MAP-Strat-Einträge)

Markerwürfel (10 cm) auf einem dünnen Pfahl 0,35 über dem Objekt-Ursprung; kurzer waagrechter Strich = Blickrichtung (Kameras, Checkpoints).

Farbe RGB Kategorie Typen
Weiß, Pfeil flach am Boden 255,255,255 Spielerstart (Richtung) Llama, KuscoAndPachaTogetherOnATree (Fluss), RollerCoaster, LlamaChase
Violett + Drahtwürfel 1×1×1 um die Rasterzelle 190,60,255 Ausgang (Auslösezelle) LevelDoor, VillageEndDoor, EndGameDoor, TeleportDoor
Grüntürkis 0,255,140 Checkpoint CheckPointPost, CheckPointBox
Cyan 0,255,255 Kamera Camera* (CameraSide, CameraLift, CameraForceInsideBox …), CutSceneCamera*, RiverWaterfallCamera
Gelb 255,255,0 Trigger CutSceneTrigger*, Trigger, Say* (Sprüche), ChangeToBunny/Turtle, RiverStartSign
Orange 255,90,0 Kraft-/Sonderzonen Force*, FallOffNoHurt, ObjectDontDropInBox, RiverCurrentBox, RiverRebound, WaterReboundPoints*, SuckingWPs, ObjectSuckPoints, RiverTrackChange, TrackSwitch, RiverWhirlpool, BackgroundRotationChanger
Gold 255,200,0 Geheimnis-Zone Secret
Grau 170,170,170 sonstige unsichtbare Steuer-Objekte CheatTeleport, Wompy*, Spawner, Helper, Coin-Linien/-Kreise, Rain, DrippingWater, WaterfallMist, MusicBox, FlockOfBirds, Bats, Scorpions*, AppearingPlatforms* …
Dunkelgrau, kleiner Punkt (6 cm) 90,90,90 alle übrigen Objekte (Ursprung) Münzen, Gegner, Türen … – damit wirklich nichts fehlt
Rosa Drahtwürfel je Rasterzelle 255,100,180 Umgebungs-/Musik-/Nebelzone MAP-Block 4 (90-Byte-Einträge, Zelle x/y/z)

Zonen-Boxen: hat ein Marker-Objekt genau 2 Wegpunkte und liegt das Objekt im Rechteck dazwischen, ist das ein Box-Bereich (Secret, CameraSide/Lift, ObjectDontDropInBox, FallOffNoHurt, RiverCurrentBox …): Drahtquader in der Kategoriefarbe von min(y) bis max(y)+1,2 (die Höhe ist geschätzt – der Code prüft vermutlich nur x/z), bei Flächen ≤ 64 Einheiten² mit halbtransparentem Boden (Alpha 70). Wegpunkt-Ketten anderer Marker-Objekte: kleine Würfel (5 cm) in abgedunkelter Kategoriefarbe (60 %), bei Kraftzonen mit Verbindungslinien (z. B. WaterReboundPoints-Pfade, CutSceneTrigger-Kamerafahrt nur als Punkte).

Wie es gebaut ist (Entscheidungen)

  • Kollisions-Overlay an die TRAK-Modelle angehängt (nicht als eigene Kacheln): pro Kollisionspolygon ein Rahmen aus 2·n Vertices (äußere Ecken + um die Linienbreite nach innen versetzte Ecken, aus den Kantenebenen berechnet), 2·n Dreiecke; Füllung nutzt die inneren Ecken mit. Alles 0,012 entlang der Flächennormale versetzt (kein Z-Fighting). Dadurch stimmen Kachel-Transformation, Zonen-/Sichtbarkeit und Kachel-Wechsel (Alt-Modelle, WFPC 0x10000) automatisch. Kollision wird aus dem Spielraum zurückgerechnet (geomlib.coll_decode/ coll_polygon, z gespiegelt).
  • Face-Flags der Overlay-Dreiecke: 0x800 | 0x400 (doppelseitig) | 0x4000 (Vertexfarben ignorieren) | 0x8 bei jedem Texturwechsel und spätestens alle 250 Faces (Render-Batch).
  • Farben = angehängte Flachfarb-Texturen (TEXT-Tabelle, Seite −1): deckend Flags 0x105, halbtransparent 0x1105 (Bits 12–14 = 1 → Tabelle 0x573eb8: Blendmodus 1 = SRCALPHA/INVSRCALPHA, Alpha aus dem 4. Byte (v1) des Eintrags; im Spiel geprüft). Halbtransparente Dreiecke laufen über den Sortierpuffer (FUN_0041ed90, 512 KB/Frame ≈ 5000 Dreiecke; ist er voll, fallen sie einfach weg – kein Absturz). Deshalb gibt es Füllungen nur für unsichtbare Kollision, Material 16 und kleine Zonenböden.
  • Vertexfarben-Block in MAP: je Kachel (und je Farbsatz/Frame, auch für Alt-Modelle) um nV(Overlay) Einträge 0x00808080 verlängert (Byte 3 = 0, ändert die Frame-Anzahl nicht).
  • Unsichtbar-Erkennung: Render-Dreiecke aller Kacheln in Weltkoordinaten, nach Zellen gebuckelt; für Mittelpunkt + 4 Punkte (70 % Richtung Ecken) jedes Kollisionspolygons wird ein Strahl entlang der Normale (±0,5) gegen Dreiecke mit ähnlicher Ausrichtung (|cos| > 0,4) getestet; < 60 % Treffer = unsichtbar. Je Modell Mehrheitsentscheid über bis zu 6 Instanzen. Strat-Modelle (STPC: Türen, Plattformen, Figuren) zählen NICHT als Render-Fläche – Kollision hinter beweglichen Objekten kann daher rot erscheinen. (Erste Version mit 0,06/0,15 Abstand markierte ganze Hauswände und Felswände rot, weil die Kollisionshülle 0,2–0,4 vor der sichtbaren Wand liegt – zu viel Rauschen.)
  • Marker als neue Kacheln ohne Kollision, je Rasterzelle ein Modell (Linien in ≤ 1-Stücke zerteilt und in die Zelle ihres Mittelpunkts gelegt) → bekommen automatisch die Zone der Zelle. Neue Kacheln werden hinten angehängt (Indizes der Original-Kacheln bleiben).
  • Spielmechanik unverändert: keine Kollisionspolygone hinzugefügt/geändert, Strats, Wegpunkte, Parameterpool, Zonen und Rest von MAP byte-gleich (wird nach dem Schreiben geprüft: Strats/Knoten/Rest/Original-Kacheln identisch).
  • Größe: WADs wachsen um 0,1–4 MB (max. 8,9 MB, t1l6m003). Level-Speicher (Arena 16 MB, FUN_0041ef00) hält TRAK+STPC+MAP – reicht für alle (Ladetest).

Grenzen / bekannt

  • Kollision der Objekt-Modelle (STPC: bewegliche Plattformen, Türen, Kisten …) wird nicht gezeigt – die bewegen sich; nur ihr Ursprung als dunkelgrauer Punkt.
  • Zonen-Höhen der Box-Bereiche sind geschätzt; ob die Engine y prüft, ist offen.
  • Bedeutung der meisten Sondermaterialien unbekannt (nur Farbe + Anzahl, s. Tabelle unten); Material 30 = kein Boden, 16 = Ausgang, 17/18 = selektiv sicher aus dem Code.
  • Umgebungszonen (MAP-Block 4): Zelle sicher, Bedeutung (Nebel/Musik/Kamera) nur mittel sicher; Vorgabe-Eintrag (Position 1,1,1) wird nicht gezeichnet.
  • Liegt die Kamera dicht an einem Marker/einer Linie, zerschneidet der Near-Plane-Clip der Engine die Dreiecke manchmal zu großen Fetzen (nur kurz sichtbar).
  • Leistung: unter Wine/llvmpipe (Xvfb) ohne spürbaren Einbruch; auf echter GPU unkritisch (≤ 2000 zusätzliche Vertices je Kachelmodell, Ø ~1–2× so viele Dreiecke wie das Original).
  • Testlevel T1L9 (Testwelt) hat keine Debug-Version (testlevel-Mod hat Vorrang).

Statistik je Level (Stand Build 2026-11-06)

Level Welt WAD alt→neu (KB) Koll.-Polygone davon unsichtbar Sondermaterialien (Anzahl Polygone) Marker
t1l1m001 Dorf 6158→8235 3474 215 2:138, 6:4, 8:35, 9:13, 16:8, 17:5, 18:84, 28:19, 30:1 ausgang 1, checkpoint 6, geheimnis 2, kamera 6, kraft 6, objekt 95, sonst 4, start 1, trigger 1, umgebung 12
t1l1m002 Dorf 5471→7573 3909 130 6:4, 8:14, 16:9, 18:65, 28:23, 30:1 ausgang 1, checkpoint 5, geheimnis 1, kamera 8, kraft 5, objekt 89, sonst 9, start 1, trigger 1, umgebung 15
t1l1m003 Dorf 5891→8348 3893 79 2:15, 6:4, 8:8, 16:5, 17:3, 18:458, 20:6, 28:23, 30:1 ausgang 1, kamera 3, kraft 6, objekt 45, sonst 3, start 1, trigger 2, umgebung 4
t1l1m004 Dorf 5866→7006 2169 60 2:15, 8:8, 16:16, 17:3, 18:90, 30:1 ausgang 2, geheimnis 1, kamera 2, kraft 1, objekt 31, sonst 2, start 1, trigger 1, umgebung 5
t1l2m001 Dschungelnacht 5094→7491 3933 460 1:26, 2:111, 6:156, 9:34, 16:22, 17:24, 18:284, 20:80, 28:28, 30:138, 33:1 ausgang 1, checkpoint 2, geheimnis 1, kamera 3, kraft 6, objekt 39, sonst 8, start 1, trigger 1, umgebung 8
t1l2m002 Dschungelnacht 5329→7561 3715 389 1:216, 2:57, 6:154, 9:24, 16:34, 17:23, 18:319, 28:28, 30:50 ausgang 1, checkpoint 4, geheimnis 1, kamera 3, kraft 7, objekt 105, sonst 2, start 1, trigger 1, umgebung 18
t1l2m003 Dschungelnacht (Verfolgung) 2837→5142 4844 630 5:3, 9:6, 10:6, 11:37, 16:26, 27:206, 30:46 kamera 2, kraft 1, objekt 33, sonst 4, start 1, trigger 1
t1l3m001 Fluss 4099→8220 8371 1000 5:26, 16:8, 18:23 ausgang 2, geheimnis 1, kamera 13, kraft 4, objekt 140, sonst 13, start 1, trigger 5
t1l3m002 Fluss 3799→8249 8824 1097 5:40, 16:5, 18:23, 30:1 ausgang 2, geheimnis 1, kamera 19, kraft 44, objekt 115, sonst 39, start 1, trigger 6
t1l3m003 Fluss 4497→8537 8387 868 5:56, 16:5 ausgang 4, geheimnis 1, kamera 15, kraft 7, objekt 52, sonst 15, start 1, trigger 3
t1l3m004 Fluss 2674→3754 2201 228 5:18, 16:5 ausgang 2, kamera 5, kraft 4, objekt 31, sonst 7, start 1, trigger 2
t1l4m001 Dschungeltag 5469→7380 3214 230 1:148, 6:105, 10:33, 16:18, 17:33, 18:158, 20:25, 28:24, 30:10 ausgang 1, checkpoint 9, geheimnis 1, kamera 5, kraft 12, objekt 120, sonst 3, start 1, trigger 1, umgebung 10
t1l4m002 Dschungeltag 5807→8045 3820 157 1:186, 6:123, 10:80, 16:33, 17:55, 18:309, 20:6, 28:98, 30:22 ausgang 1, checkpoint 7, geheimnis 2, kamera 1, kraft 14, objekt 135, sonst 3, start 1, trigger 1, umgebung 14
t1l4m003 Dschungeltag 6320→8886 4805 135 1:174, 2:52, 6:10, 10:33, 16:36, 17:110, 18:890, 20:186, 28:27, 30:33, 255:8 ausgang 1, checkpoint 3, geheimnis 2, kamera 6, kraft 4, objekt 99, sonst 3, start 1, trigger 1, umgebung 16
t1l5m001 Berg 6090→8613 4419 188 3:356, 8:96, 16:24, 17:35, 18:539, 20:66, 28:6, 30:87 ausgang 1, checkpoint 3, geheimnis 2, kamera 5, kraft 2, objekt 83, sonst 7, start 1, trigger 1, umgebung 15
t1l5m002 Berg 5949→8558 5242 220 2:20, 3:396, 8:23, 9:15, 16:19, 17:33, 18:342, 20:57, 30:120 ausgang 1, checkpoint 3, geheimnis 4, kamera 10, kraft 3, objekt 67, sonst 6, start 1, trigger 1, umgebung 7
t1l5m003 Berg 4989→6796 3399 98 3:232, 8:223, 16:17, 18:20, 20:6, 30:78 ausgang 1, checkpoint 5, geheimnis 1, kamera 4, kraft 1, objekt 71, sonst 6, start 1, trigger 1, umgebung 2
t1l6m001 Stadt 6210→8227 2857 169 6:29, 16:18, 17:140, 18:110, 30:38 ausgang 1, checkpoint 4, geheimnis 1, kamera 5, kraft 3, objekt 83, sonst 3, start 1, trigger 5, umgebung 20
t1l6m002 Stadt 5155→7060 1771 106 6:56, 16:9, 17:20, 18:39, 30:17 ausgang 1, checkpoint 10, geheimnis 1, kamera 7, kraft 9, objekt 127, sonst 16, start 1, trigger 5, umgebung 46
t1l6m003 Stadt 6693→8924 2462 148 1:39, 16:13, 17:80, 18:154, 28:3, 30:10 ausgang 1, checkpoint 11, geheimnis 6, kamera 11, kraft 6, objekt 107, sonst 15, start 1, trigger 3, umgebung 59
t1l7m001 Katakomben (Achterbahn) 3054→6705 4043 20 5:54, 11:18, 13:18, 16:9, 30:18 kamera 1, objekt 2, sonst 36, start 1, trigger 1, umgebung 14
t1l7m002 Katakomben 5280→7722 1795 111 2:1, 11:3, 16:44, 30:93 ausgang 3, checkpoint 12, geheimnis 5, kamera 6, kraft 4, objekt 186, sonst 12, start 1, trigger 1, umgebung 23
t1l7m003 Katakomben (Achterbahn) 2496→5140 1999 11 5:54, 11:90, 13:36, 15:18, 16:9, 30:9 kamera 1, objekt 11, sonst 41, start 1, trigger 1
t1l7m004 Katakomben 5299→6978 1892 61 2:7, 16:32, 30:70 ausgang 1, checkpoint 3, geheimnis 3, kamera 4, kraft 7, objekt 93, sonst 10, start 1, trigger 2, umgebung 15
t1l7m005 Katakomben (Achterbahn) 2390→4782 1885 9 5:54, 11:36, 13:36, 16:9, 30:9 kamera 1, objekt 1, sonst 51, start 1, trigger 1, umgebung 9
t1l8m001 Labor 6632→8824 3396 185 1:40, 8:9, 16:16, 17:8, 18:181, 30:56 ausgang 1, checkpoint 2, geheimnis 2, kamera 8, kraft 2, objekt 99, sonst 3, start 1, trigger 2, umgebung 19
t1l8m002 Labor 5427→7112 2204 76 16:1, 17:44, 18:496, 30:42 ausgang 1, geheimnis 1, kamera 5, kraft 2, objekt 74, sonst 2, start 1, trigger 3, umgebung 1
t1l8m003 Labor 4656→5546 1398 34 16:9, 17:10, 18:196, 30:21 ausgang 1, checkpoint 2, geheimnis 1, kamera 2, kraft 2, objekt 36, sonst 2, start 1, trigger 3, umgebung 6
t1l8m004 Labor 5817→8407 2993 124 9:10, 16:66, 17:1, 30:57 ausgang 1, checkpoint 7, geheimnis 3, kamera 12, kraft 6, objekt 164, sonst 3, start 1, trigger 7, umgebung 46
t1l8m005 Labor 5375→8660 4041 179 7:10, 16:78, 17:1, 30:82 checkpoint 7, geheimnis 1, kamera 14, kraft 5, objekt 113, sonst 15, start 1, trigger 2, umgebung 47
t1l8m006 Labor 5423→5948 738 44 2:13, 8:24, 16:3, 17:5, 18:27, 26:4 ausgang 1, objekt 15, start 1, trigger 17
t6l6m001 Sonder (Optionen/Karte) 1331→1482 333 0 kamera 1, objekt 3
t6l6m002 Sonder (Optionen/Karte) 3605→3640 64 0 kamera 1, objekt 2

(Kollisionspolygone je TRAK-Modell gezählt, nicht je Kachel-Instanz.)

Tests (2026-11-06)

Offline (alle 33 Debug-WADs, $JOB/tmp/dv/offcheck.py): TRAK parst und baut byte-gleich zurück; ≤ 8000 Vertices je Modell (max. 2012, t1l4m003); Render-Batches ≤ 256 Faces; alle Texturindizes < Tabellengröße; Vertexfarben-Block je Kachel = nV(Modell) (alle Frames); MAP mit mapchunk vollständig geparst (Rest 0 Byte) und byte-gleich neu gebaut; Strats, Wegpunkte, Parameterpool, Umgebungszonen, Zonentabelle/-raster, Alt-Block und Original-Kacheln unverändert; Original-Modelle (Vertices, Faces, Kollision) byte-genau unverändert; neue Marker-Modelle ohne Kollision; alle anderen Chunks (STPC, Sound, LGPC …) identisch; Kacheln im Raster. TRAK+STPC+MAP+LGPC ≤ 7,6 MB (Level-Arena 16 MB).

Im Spiel geladen (lamatest, über Slot Welt 9 als T1L9M001.wad, ~40–90 s gespielt/gedreht, kein Absturz, kein Hänger, keine Fehler im Wine-Log): t1l1m001 (mehrfach, Sichtprüfung), t1l1m003, t1l1m004, t1l2m001, t1l2m002, t1l3m001–004 (Fluss läuft auch im Slot 9 normal: Boot fährt), t1l4m001–003, t1l5m001–003, t1l6m001–003 = 19 Level. Getestet wurde der Build direkt vor dem endgültigen: identische Geometrie, nur die Farbwerte der Sondermaterialien wurden danach auf feste Farben je Material umgestellt (+8–16 Byte Texturtabelle). Die Vorschaubilder stammen ebenfalls aus diesem Build – Sondermaterial-Farben darin (z. B. Türkis-Füllung im Berg-Bild) entsprechen noch nicht ganz der Legende oben. Nur offline geprüft: t1l1m002 und t1l2m003 (Ladeversuch scheiterte vorher an der Menü-Erkennung von lamatest, nicht am Level), t1l7m001–005 (Katakomben/Achterbahn), t1l8m001–006 (Labor), t6l6m001/002. Ein Test über den echten Spielstand-Slot wurde nicht gemacht.

lamatest-Änderung nebenbei: Spielstand-Auswahl mit mehr Versuchen und 0,3 s langen Tastendrücken (unter CPU-Last gingen kurze Drücke verloren).

Bilder: notes/img/debug/00_montage.jpg (6 Welten), 01_…06_*.jpg (1280×720).