227 lines
6.7 KiB
Go
227 lines
6.7 KiB
Go
package main
|
||
|
||
import (
|
||
"bspviz/internal/bsp"
|
||
"bspviz/internal/geom"
|
||
"bspviz/internal/mapfmt"
|
||
"bspviz/internal/viz"
|
||
"bspviz/internal/wad"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
)
|
||
|
||
func usageAndExit(msg string, code int) {
|
||
fmt.Fprintf(os.Stderr, "Fehler: %s\n\n", msg)
|
||
fmt.Fprintf(os.Stderr, "Beispiel:\n go run ./main.go -wad MYMAP.wad -map MAP01 -info\n\n")
|
||
fmt.Fprintf(os.Stderr, "Verfügbare Flags:\n")
|
||
flag.PrintDefaults()
|
||
os.Exit(code)
|
||
}
|
||
|
||
func main() {
|
||
// Flags
|
||
wadPath := flag.String("wad", "", "Pfad zur WAD (required)")
|
||
mapMarker := flag.String("map", "MAP01", "Map-Marker (z.B. MAP01, E1M1, MYMAP)")
|
||
listOnly := flag.Bool("list", false, "Nur Directory auflisten und beenden")
|
||
info := flag.Bool("info", false, "Roh-Infos zur Map (Counts von VERTEXES/LINEDEFS)")
|
||
extract := flag.String("extract", "", "Kommagetrennte Lump-Namen aus der Map extrahieren (z.B. VERTEXES,LINEDEFS)")
|
||
outdir := flag.String("out", ".", "Zielordner für -extract")
|
||
geomtest := flag.Bool("geomtest", false, "Geometrie-Check: Segmente/AABB/Probe-Split ausgeben")
|
||
buildbsp := flag.Bool("buildbsp", false, "BSP bauen und Metriken ausgeben")
|
||
alpha := flag.Float64("alpha", 10, "Kosten: Gewicht für Splits")
|
||
beta := flag.Float64("beta", 1, "Kosten: Gewicht für Balance")
|
||
eps := flag.Float64("eps", geom.EPS, "Epsilon für Geometrie")
|
||
leaf := flag.Int("leafmax", 12, "max. Segmente pro Leaf")
|
||
depth := flag.Int("maxdepth", 32, "max. Rekursionstiefe")
|
||
cands := flag.Int("cands", 16, "Anzahl Kandidaten (Subsample)")
|
||
seed := flag.Int64("seed", 0, "RNG-Seed (0 = default)")
|
||
dotOut := flag.String("dot", "", "DOT-Export-Datei (optional)")
|
||
treePNG := flag.String("treepng", "", "Graphviz-Baum als PNG (optional, benötigt -dot)")
|
||
overlay := flag.String("overlay", "", "Map-Overlay als PNG (optional)")
|
||
|
||
flag.Parse()
|
||
|
||
if strings.TrimSpace(*wadPath) == "" {
|
||
usageAndExit("Flag -wad fehlt. Bitte Pfad zu einer Doom-kompatiblen WAD-Datei angeben.", 2)
|
||
}
|
||
|
||
w, err := wad.Open(*wadPath)
|
||
if err != nil {
|
||
log.Fatalf("open wad: %v", err)
|
||
}
|
||
defer w.Close()
|
||
|
||
if *listOnly {
|
||
fmt.Printf("WAD: %s\n", *wadPath)
|
||
for i, d := range w.Dir() {
|
||
fmt.Printf("%3d: %-8s size=%-7d pos=%-8d\n", i, d.Name(), d.Size, d.FilePos)
|
||
}
|
||
return
|
||
}
|
||
|
||
start, end, err := w.FindMap(*mapMarker)
|
||
if err != nil {
|
||
log.Fatalf("find map: %v", err)
|
||
}
|
||
fmt.Printf("Map %s: Directory [%d, %d)\n", strings.ToUpper(*mapMarker), start, end)
|
||
|
||
//info über die daten in WAD
|
||
if *info {
|
||
lumps, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
||
if err != nil {
|
||
log.Fatalf("load map lumps: %v", err)
|
||
}
|
||
vb := lumps["VERTEXES"]
|
||
lb := lumps["LINEDEFS"]
|
||
m, err := mapfmt.LoadMap(lumps)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
verts := len(vb) / 4
|
||
lines := len(lb) / 14
|
||
fmt.Printf("VERTEXES: bytes=%d count=%d\n", len(vb), verts)
|
||
fmt.Printf("LINEDEFS: bytes=%d count=%d\n", len(lb), lines)
|
||
|
||
fmt.Printf("Map has %d vertices and %d linedefs\n", len(m.Vertices), len(m.Linedefs))
|
||
fmt.Printf("First vertex: %+v\n", m.Vertices[0])
|
||
fmt.Printf("First linedef: %+v\n", m.Linedefs[0])
|
||
|
||
if len(vb)%4 != 0 {
|
||
fmt.Println("WARN: VERTEXES size ist kein Vielfaches von 4 → Format prüfen")
|
||
}
|
||
if len(lb)%14 != 0 {
|
||
fmt.Println("WARN: LINEDEFS size ist kein Vielfaches von 14 → Format prüfen")
|
||
}
|
||
}
|
||
|
||
// Generiert einzelne Lump Dateien zum Debugen
|
||
if *extract != "" {
|
||
want := strings.Split(*extract, ",")
|
||
for i := range want {
|
||
want[i] = strings.ToUpper(strings.TrimSpace(want[i]))
|
||
}
|
||
lumps, err := w.LoadMapLumps(*mapMarker, want...)
|
||
if err != nil {
|
||
log.Fatalf("extract: %v", err)
|
||
}
|
||
if err := os.MkdirAll(*outdir, 0o755); err != nil {
|
||
log.Fatalf("mkdir %s: %v", *outdir, err)
|
||
}
|
||
for name, data := range lumps {
|
||
dst := filepath.Join(*outdir, fmt.Sprintf("%s.lmp", name))
|
||
if err := os.WriteFile(dst, data, 0o644); err != nil {
|
||
log.Fatalf("write %s: %v", dst, err)
|
||
}
|
||
fmt.Printf("wrote %s (%d bytes)\n", dst, len(data))
|
||
}
|
||
}
|
||
|
||
// Zum debug test der geo functions
|
||
if *geomtest {
|
||
raw, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
||
if err != nil {
|
||
log.Fatalf("load map lumps: %v", err)
|
||
}
|
||
m, err := mapfmt.LoadMap(raw)
|
||
if err != nil {
|
||
log.Fatalf("parse map: %v", err)
|
||
}
|
||
|
||
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
||
fmt.Printf("GEOM: vertices=%d linedefs=%d segs=%d\n", len(m.Vertices), len(m.Linedefs), len(segs))
|
||
if len(segs) == 0 {
|
||
fmt.Println("GEOM: keine Segmente gefunden – prüfe LINEDEFS/VERTEXES")
|
||
return
|
||
}
|
||
|
||
pts := make([]geom.Vec, 0, len(m.Vertices))
|
||
for _, v := range m.Vertices {
|
||
pts = append(pts, geom.V(float64(v.X), float64(v.Y)))
|
||
}
|
||
bb := geom.Bounds(pts)
|
||
w := bb.Max.X - bb.Min.X
|
||
h := bb.Max.Y - bb.Min.Y
|
||
fmt.Printf("AABB: min=(%.1f,%.1f) max=(%.1f,%.1f) size=(%.1f×%.1f)\n",
|
||
bb.Min.X, bb.Min.Y, bb.Max.X, bb.Max.Y, w, h)
|
||
|
||
O := segs[0].A
|
||
D := geom.Sub(segs[0].B, segs[0].A)
|
||
if geom.Len(D) < 1e-9 && len(segs) > 1 {
|
||
O = segs[1].A
|
||
D = geom.Sub(segs[1].B, segs[1].A)
|
||
}
|
||
var left, right, splits, degens int
|
||
for _, s := range segs {
|
||
f, b := geom.SplitSeg(s, O, D)
|
||
if len(f) == 0 && len(b) == 0 {
|
||
degens++
|
||
continue
|
||
}
|
||
left += len(f)
|
||
right += len(b)
|
||
if len(f) > 0 && len(b) > 0 {
|
||
splits++
|
||
}
|
||
}
|
||
fmt.Printf("PROBE-SPLIT: O=(%.1f,%.1f) D=(%.1f,%.1f)\n", O.X, O.Y, D.X, D.Y)
|
||
fmt.Printf("PROBE-SPLIT: left=%d right=%d splits=%d degens=%d (EPS=%.1e)\n",
|
||
left, right, splits, degens, geom.EPS)
|
||
|
||
return
|
||
}
|
||
|
||
if *buildbsp {
|
||
raw, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
||
if err != nil {
|
||
log.Fatalf("load map lumps: %v", err)
|
||
}
|
||
m, err := mapfmt.LoadMap(raw)
|
||
if err != nil {
|
||
log.Fatalf("parse map: %v", err)
|
||
}
|
||
|
||
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
||
p := bsp.Params{
|
||
Alpha: *alpha, Beta: *beta, Eps: *eps,
|
||
MaxDepth: *depth, LeafMax: *leaf, Cands: *cands, Seed: *seed,
|
||
}
|
||
root := bsp.Build(segs, p)
|
||
st := bsp.Measure(root)
|
||
|
||
fmt.Printf("BSP built.\n")
|
||
fmt.Printf(" nodes=%d leaves=%d maxDepth=%d totalLeafSegs=%d\n",
|
||
st.Nodes, st.Leaves, st.MaxDepth, st.TotalSegs)
|
||
fmt.Printf(" params: alpha=%.2f beta=%.2f eps=%.1e leafMax=%d maxDepth=%d cands=%d seed=%d\n",
|
||
p.Alpha, p.Beta, p.Eps, p.LeafMax, p.MaxDepth, p.Cands, p.Seed)
|
||
|
||
if *dotOut != "" {
|
||
if err := viz.EmitDOT(root, *dotOut); err != nil {
|
||
log.Fatalf("write DOT: %v", err)
|
||
}
|
||
fmt.Printf("DOT export geschrieben: %s\n", *dotOut)
|
||
|
||
if *treePNG != "" {
|
||
if err := viz.RunGraphviz(*dotOut, *treePNG); err != nil {
|
||
log.Printf("Graphviz fehlgeschlagen: %v", err)
|
||
} else {
|
||
fmt.Printf("Graphviz PNG gebaut: %s\n", *treePNG)
|
||
}
|
||
}
|
||
}
|
||
|
||
if *overlay != "" {
|
||
if err := viz.RenderPNG(m, root, *overlay); err != nil {
|
||
log.Fatalf("write overlay PNG: %v", err)
|
||
}
|
||
fmt.Printf("Overlay PNG geschrieben: %s\n", *overlay)
|
||
}
|
||
return
|
||
}
|
||
|
||
}
|