Kartengenerator

  • Hi, ich würde mich auch gerne in die Diskussion einklinken :)


    Da sich ja schon ein paar Leute fleißig in den Programmablauf und die Bundles hacken, dachte ich mir ich sehe mir die Karten ein wenig genauer an. Mir gefallen die Möglichkeiten der rasterfreien Kartengestaltung, ich glaube CiM 2 könnte das erste Spiel sein in dem man ein Verkehrsnetz für eine (Klein)Stadt maßstabsgetreu nachbauen kann.


    Mein Gedanke ist daher, ein Grundgerüst aus frei verfügbaren Kartendaten, z.B. OpenStreetMap, zu generieren, bestehend aus
    * Heightmap
    * bestimmten Bodentexturen, die den Verlauf von Straßen, Bahntrassen, Flüssen etc. nachbilden
    * Bodentexturen wären auch für Gebiete denkbar (besiedelt, Ackerland, Wiese, Wald...)
    * Wahrscheinlich nicht leicht machbar, aber denkbar wäre auch, Objekte zu generieren: Bäume, Gebäude, Straßen


    Mit dem letzten Punkt rechne ich nicht wirklich, da das Objektformat nur schwer aus den Maps zu lesen ist und z.B. gewisse reale Straßenverläufe im CiM 2 nicht nachgebildet werden können. Außerdem ist die Verknüpfung der Straßen alles andere als trivial. Um eine reale Gegend 1:1 nachzubauen dürfte aber eine korrekte Heightmap und Markierungen für sämtliche Straßen völlig ausreichen.


    Nun hab ich mich für ein paar Stunden in den Hex-Editor gehängt und das *.map-Format ein wenig analysiert...


    Grundaufbau:
    * Header: dynamisch, relativ kurz
    * HeightMap: statisch, 16.793.604 Byte (2049 * 2049 * 4)
    * TextureMap: statisch, 16.777.216 Byte (2048 * 2048 * 4)
    * GameObjects: dynamisch


    Header:
    Der Header beginnt mit dem String "GameState+SerializableMetaData" und enthält ein paar Metadaten wie den Namen der Map, sowie ein PNG-Bild der Minimap (binär codiert). Am Ende des Headers findet sich nach einigen Nullen der String "GameState+SerializableTerrainData" und darauf die Reihenfolge der verwendeten Texturen.
    Viel genauer hab ich da noch nicht reingeschaut, sind sicher noch ein paar wichtige Daten drin versteckt.


    HeightMap:
    Wie genau man den Einstiegpunkt findet, weiß ich leider nicht, nur so viel: Nach der "SerializableTerrainData" kommt eine Reihe von Nullen und wenn die erste Ecke nicht auf Höhe 0 ist, kann man den ersten Eintrag != 0 als Startpunkt wählen.
    Die Heightmap ist eine statische, unkomprimierte Matrix (deshalb kann auch keine Map <32MB werden, je 16MB entfallen nämlich immer auf die Heightmap und Texturmap).
    Jede Zeile besteht aus 2048 Feldern, aber die Heightmap wird über ihre Kanten definiert, daher stehen uns je Zeile 2049 Werte zur Verfügung. Mit 2049 * 2049 Werten zu je 32 bit kommen wir auf 16.793.604 Byte, was ca. 16MB entspricht.


    Die Höhe wird in Werten von - 1.048,575 bis + 1.048,576 angegeben, im Spiel werden dann Höhenmeter daraus. Wir können also Berge mit bis zu 1km Höhe, bzw. Ozeane mit 1km Tiefe anlegen (aber praktisch keine Flüsse ;(). Die Zahlen werden einfach als 32bit signed Integer (little Endian) gespeichert. Um jedoch auf die spielinternen Höhenmeter zu kommen, wird an der drittletzten Stelle ein Komma eingefügt.


    Zwei Beispiele:
    06 b5 01 00
    Als 32bit Integer interpretiert wird die Zahl 111877 daraus, mit den Komma vor der drittletzten Ziffer kommt man auf 111,877 m Höhe.
    34 cf fc ff
    Dieser Wert als signed Integer ergibt -209100 oder spielintern 209,1 m unter dem Meeresspiegel.


    TextureMap:
    Direkt auf die HeightMap folgt die TextureMap, diesmal nur mit 2048 x 2048 Einträgen, weil Flächen, nicht Kanten, definiert werden.
    Zunächst etwas Grundwissen: Vielleicht ist dem ein oder anderen schon aufgefallen, dass es im Mapeditor nur 4 Texturen gibt. Das hat seinen Grund: Das aktuelle Kartenformat unterstützt nämlich nur die Verwendung von 4 Texturen. Eine Karte besteht immer aus einer Basistextur mit 100% Deckkraft. Auf diese können die anderen 3 Texturen mit beliebiger Deckkraft hinzugemischt werden. Daneben gibt es noch zwei Spezialtexturen: Den Betonboden aus den Städten und ein schwarzes Nichts (wozu auch immer das gut ist...)


    Zurück zum Format; auch hier besteht jeder Eintrag aus einem 32-bit Wort mit folgender Belegung:
    Byte 1, 2 und 3 geben jeweils die Deckkraft der 3 Nicht-Basistexturen an (Werte von 0 bis 255 werden übersetzt in 0% bis 100%)
    Byte 4 bestimmt über die Basistextur mit einem recht eigenartigen Verhalten
    - Werte unter 0x50 erzeugen das schwarze Nichts, je kleiner der Wert desto größer ist der schwarze Fleck auf dem Quadranten
    - Werte über 0xa erzeugen den versiegelten Boden mit braunem Rand. Auch hier gilt: Je größer der Wert desto größer die ausgefüllte Fläche. Alle Werte dazwischen erzeugen die Basistextur mit 100% Deckkraft.


    Nach exakt 16.777.216 Byte ist auch der Spaß vorbei und es geht weiter mit den


    GameObjects:
    damit hab ich mich noch gar nicht auseinandergesetzt, sry ;D



    Also, was lässt sich damit anfangen?
    - Heightmaps lassen sich aus frei verfügbaren Kartendaten generieren
    - Bodentexturen können auch an beliebiger Stelle gezeichnet werden, die Auflösung (jedes Feld hat 4x4 Meter) ist auch hoch genug, um Straßen zu tracen.
    - Die Spielfläche ist mit 8x8 km (also 64km^2) groß genug, um kleinere Städte maßstabsgetreu wiederzugeben.


    => einer Simulation von real existierenden Städten steht damit nichts mehr im Weg 8)


    Ich hab schon Ideen für einen Karten-Generator der mit OpenStreetMap-Daten arbeitet, aber bis da produktive Ergebnisse rauskommen wird wohl noch ein Weilchen vergehen.

  • Wenn du ein Tool schreiben würdest, das aus 2048 * 2048 großen bmp-Dateien eine leere High-Map erstellt, die man ins Spiel laden kann, wäre das auch schon ein toller Anfang ;)

    Mal nebenbei, wird auf anderen Seiten auch über Modifizierbarkeit diskutiert? Oder was auf die Beine gestellt?


    Soweit ich das gesehen habe, verlinkt selbst CimX zu uns :whistling:
    Ohne eis_os, immi und Co wären wir auch noch nicht soweit :)

  • Sorry, das ich hier gerade nicht viel schreibe, Schwarz könnte zur Erstellung von Löchern genutzt werden. Schreibe gerade an einen BundleCodec in C um die Daten einzulesen und zu schreiben.


    Jedes serialisiertes Objekt scheint im Bundle Format eine Objektversion zu haben, das wird genutzt damit nicht ein Bundle mit der falschen Spielversion geladen wird. Ein Binary Diff zwischen CIM2 Versionen zeigt, das mit Steam auch die Bundle Dateien gepacht werden, da der Datentransfer aber viel kleiner und schneller ist, wird wohl nur ein Binary Diff angewendet.


    Daher, solltet ihr an den Dateien schrauben, dies könnte zu Problemen mit Steam Updates führen. Daher eine Kopie in Reserve Halten :)

  • Klamann
    nice work :) Die verschiedenen Bodentexturen bieten sich ja förmlich als Markierungen an, um die einzelnen Straßenverläufe (und Typen) aus Openstreetmap zu übernehmen. Das plus die Heightmap sollten für jedem Mapper mehr als genug Anhaltspunkte sein, um die Karte weiter zu gestalten.



    Die Höhe wird in Werten von - 1.048,575 bis + 1.048,576 angegeben, im Spiel werden dann Höhenmeter daraus. Wir können also Berge mit bis zu 1km Höhe, bzw. Ozeane mit 1km Tiefe anlegen (aber praktisch keine Flüsse ;(). Die Zahlen werden einfach als 32bit signed Integer (little Endian) gespeichert. Um jedoch auf die spielinternen Höhenmeter zu kommen, wird an der drittletzten Stelle ein Komma eingefügt.


    Man könnte auch sagen die Höhe wird in Millimetern gespeichert. ;)
    Woher kommt die Limitierung auf 25bit Werte bei 32bit Integerzahlen (wenn ich dass richtig sehe)? Akzeptiert die GameEngine einfach keine höheren Werte? Nicht dass das groß ne Rolle spielt, 1km hohe Berge sind für den Nahverkehr völlig ausreichend, bin nur neugierig.


    Jedes serialisiertes Objekt scheint im Bundle Format eine Objektversion zu haben, das wird genutzt damit nicht ein Bundle mit der falschen Spielversion geladen wird.

    wie kommst du darauf? Sowas ist mir bisher eigentlich nicht aufgefallen, meine ich ...


  • Woher kommt die Limitierung auf 25bit Werte bei 32bit Integerzahlen (wenn ich dass richtig sehe)? Akzeptiert die GameEngine einfach keine höheren Werte? Nicht dass das groß ne Rolle spielt, 1km hohe Berge sind für den Nahverkehr völlig ausreichend, bin nur neugierig.


    Ich nehme mal an die Entwickler haben die Höhenwerte einfach als 32bit Integer gespeichert und sich nicht weiter um die Effizienz des Formats gekümmert. Insgesamt ist das Dateiformat sowieso nicht als besonders sparsam zu betrachten, allein dass die Erhöhungen und Texturen unkomprimiert gespeichert werden ist völlig unnötig. Die von mir hochgeladene Map bekomme ich mit 7zip auf gute 100kb, da ist also einiges Einsparpotential vorhanden.


    Es würden sogar 20bit-Zahlen (bzw. 21bit für positiv/negativ) ausreichen, geringfügig größere und kleinere Werte werden einfach ignoriert (also auf die maximal erlaubte Höhe bzw. Tiefe gekürzt). Ich habe aber auch einmal eine Map erstellt, die komplett unter Wasser war, weil ich versehentlich einen Wert eingetragen hatte der weit außerhalb des darstellbaren Bereichs liegt, also damit sollte man nicht zu viel Blödsinn treiben ;)


    Man könnte auch sagen die Höhe wird in Millimetern gespeichert. ;)


    Stimmt daran hatte ich gar nicht gedacht ^^
    Im Editor wird die Höhe in Meter auf bis zu 3 Nachkommastellen angezeigt, also dachte ich mir ich halte mich einfach daran. Ist auch intuitiver, finde ich.

  • Jetzt konnte ich es mir doch nicht nehmen lassen, mit dem Bau eines Kartengenerators anzufangen... Ein erstes Ergebnis (siehe Anhang).

    Gibt es da schon Neuigkeiten, oder eine Möglichkeit an dieses Skript heranzukommen?
    Bin persönlich sehr daran interessiert echte Städte nachzubauen, beziehungsweise zumindest das Terrain zu übernehmen.


    Dazu noch eine Frage: Wie kommt man z.B. mit OpenStreetMap am besten an Heightmaps usw.?

  • Gerade hab ich wieder ein wenig daran weiter gearbeitet, hab aber im Moment einfach wenig Zeit. Ich habe eine Abstraktionsebene entwickelt, die aus 2049x2049er Matrizen mit Werten zwischen -1048,5 und +1048,5 die Reliefs generiert. Das gleiche Schema gilt für die Texturen (2048x2048er Matrix), wobei man die Werte für die Texturen jetzt einfach über ein enum abrufen und auch mischen kann, z.B. mit Textur.ROUGH_GRASS.draw(alpha).
    Das Tool soll definitiv entwicklerfreundlich sein, auch daher braucht es ein wenig Zeit.
    Der nächste Schritt wäre dann natürlich ein einfach zu bedienendes Frontend, damit man sich eine eigene Karte auswählen und generieren lassen kann.


    Zu den Datenquellen:
    Die topographischen Daten bekommt man nicht aus der OpenStreetMap, ich wollte dafür auf den SRTM-Datensatz zugreifen. Die Daten sich leider nicht so umfangreich wie ich mir das gewünscht hätte (ein Datenpunkt deckt bis zu 90x90m ab), aber dafür sind die Daten praktisch weltweit verfügbar. Wenn jemand einen genaueren, frei verfügbaren Datensatz kennt, ich würde mich sehr über einen Hinweis freuen :)
    Bathymetrie (Topographie des Meeresbodens) interessiert mich nicht primär, aber auch da wäre mindestens ein Datensatz verfügbar.


    Die Kartendaten aus der OpenStreetMap möchte ich über die XAPI herausholen. Vorteil gegenüber der normalen API: Nur Lesezugriff, Filterfunktion, Server akzeptieren auch große Gebiete, kein hartes Trafficlimit.


    Also, es geht voran, aber alles zu seiner Zeit. Noch kann ich keine CiM2-Maps aus den frei verfügbaren Kartendaten generieren.
    Sobald eine gewisse Grundstruktur steht, werde ich auch über ein Quellcode-Release nachdenken.

  • So, der erste Versuch eines Terrain-Modells, basierend auf SRTM-Daten (Anahng ist mittlerweile zu groß fürs Forum):
    N47E011.7z


    Die karte zeigt den Ausschnitt zwischen (47,11) und (48,12) Grad, das ist folgender Ausschnitt:
    OpenStreetMap


    Die Löcher in den Bergen sind Lücken im Datensatz, ich arbeite gerade an Möglichkeiten, die irgendwie "wegzuinterpolieren". Auch mit der Skalierung bin ich noch nicht wirklich zufrieden. Das fällt bei der großen Fläche vielleicht nicht so auf, aber eine reale Karte soll letztendlich 8x8km und nicht 100x70 haben ;)
    Wer sich schonmal ein wenig mit bikubischer Interpolation beschäftigt hat, ich würde mich über Hilfe freuen:
    java - Scale 2D-Array using Bicubic Interpolation - Stack Overflow

  • Guten Abend,
    ich habe in den letzten Wochen lange Abende an meinem Kartengenerator gebastelt und erste einigermaßen ansehnliche Ergebnisse bekommen.
    Aber seht selbst - hier findet ihr exemplarisch meine Heimatstadt Freising (siehe Kartenausschnitt):


    Freising-alpha1.zip (4,6mb)
    Freising-alpha1.7z (2,5mb)
    (wegen Verwendung von Daten aus der OpenStreetMap stehen die Dateien unter den Bedingungen der ODbL)


    Sämtliche Markierungen auf der Oberfläche kommen aus der OpenStreetMap, dazu gehören Straßen, Eisenbahnlinien, Flüsse und Gebiete (Feld, Wald, Weide). Das Relief basiert auf dem SRTM-Datensatz der NASA. Die Karte ist nicht im Maßstab verkleinert, sondern wurde 1:1 mit Daten aus der echten Welt gefüttert; die ingame-Kartengröße von 8x8 km wird damit voll ausgenutzt.


    Die Karten lassen sich mit meinem Generator aktuell in ca. 8 Sekunden generieren (abzüglich Downloadzeit), allerdings sind wichtige Programmteile wie der Download der Quelldaten nicht automatisiert und überhaupt braucht das Programm noch einiges an Pflege, ein Release wird also noch eine Weile dauern (und auch dann wird es nur eine Kommandozeilen-Version geben).


    Ich hoffe es gefällt :)

  • Sieht echt gut aus, so etwas wünsche ich mir.


    Aber was heißt hier Download der Quelldateien automatisiert? Die will mich mir schon selber runterladen können.

  • wow, das ist der absolute hammer mit dem map-tool.


    ich würde mich ja schon über einen höhendaten-import freuen,
    aber mit der kartenzeichnung drauf ist das unglaublich! :thumbsup:



    wird es auch die möglichkeit geben die größe des kartenausschnitts
    zu verändern? um größere städte umzusetzen wäre es toll wenn man z.b.
    auch eine karte im faktor 2:1 generieren könnte.


    gruß

  • wird es auch die möglichkeit geben die größe des kartenausschnitts zu verändern?


    Ich kann jetzt schon Karten mit beliebigen Abmessungen erstellen, das Limit dürfte da eher die Kapazität der Server sein, von denen die eigentlichen Daten kommen, und natürlich der Sinn des Unterfangens, weil es irgendwann dann doch zu klein auf der Map wird ;D
    Ich würde ein soft Limit mal bei ca. 50x50km ansetzen, mit abgespecktem OpenStreetMap-Download (nur Hauptverkehrsverbindungen) sollten sogar bis zu 200x200km in akzeptabler Zeit möglich sein - wer das denn möchte. Aber wie gesagt, das braucht noch ein wenig...

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!