Rail Road Tycoon MFC in Action



  • @KahnSoft sagte in Rail Road Tycoon MFC in Action:

    Chunks ein fixes LOD
    Das Chunksystem ist Dynamisch und passt sich der Netzgröße an, es hat 5 Stufen, und nur durch genaue Verteilung aller Chunks
    (in dem Fall viele hunderte) basieren immer auf einem 8ter System also chunksize zb 256 / 512 usw, auch das passiert volldynamisch.
    Sowie krumme Werte einspielen bekommst Du sogenannte gefürchtete Cracks im Gitter --) Hier mal der Creator der Crackfrei arbeitet.

    Hab da nicht ganz die Muße, mir den Code anzusehen, aber ich tippe mal wenn der "crackfrei" ist, dass er die Kanten irgendwie "zusammennäht" (nennt man glaube ich auch stitching). Für beliebige Geometrie kann das recht aufwändig werden, aber z.B. bei diesem Landschaftsrenderer mit kontinuierlichem LOD den ich beschrieben hab, ergaben sich die Nähte "natürlich" durch die Morphing-Zwischenstufen von höherer zu niedrigerer Auflösung.

    Heightmap-basierten Landschaftsrenderer
    Jo die Engine kann sowohl über HighMaps arbeiten, das kann ich hier optional Schalten, sogar Highmap kombiniert mit einer Passenden FarbTextur. Das Verfahren existiert parallel und ist teil meines 3DViews der Kamera Bilder in Echtzeit erzeugt, aber für das Spiel hier wird ein Netzverwendet dessen Größe ich in LuaSkript an die Engine übertrage zb 12000 Seitenlänge * 1000 dann habe ich genügend Rasterzellen die mit dem PerlinGenerator Hügelig gerechnet wird. die Höhen ergeben dann im sog. JetColorraum die Farben für nur Color Anzeige , bzw über die Shader lader ich dann wiederum 5 Texturen für jedes Z So entsteht eine Wellige Landschaft mit nur 5 kleinen Texturen 512*512 Damit wird dann das gesammte Terrain von der GRafikkarte im ShaderMode gerendert, und die Chunks handhaben die Auflösung dann automatisch im Shaderprogramm dem TexelMixer das läuft mit 1Khz Frameraten.

    Das heißt die Berglandschaft in dem Video ist nicht unbedingt eine Heightmap-Geometrie sondern ein eventuell generischeres 3D-Modell? Ich könnte mir vorstellen, dass ne Heightmap vielleicht noch etwas mehr Performance bringt, da die Berechnungen auf darauf deutlich einfacher sind.

    Die Ränder der Scene im Shader haben tatsächlich kanten umbrüche, die befinden sich dann aber außerhalb der Skybox, man kann quasi entscheiden ob man Cracks in der Szene hat, oder eine Treppen form an den außenkannten die ja nicht stören da sie keiner sehen kann (Sphere SkyBox)

    Nur die Ränder der "Szene", oder? Die dürften ja weit hinten am Horizont liegen und eh nicht zu sehen sein. Solange die Chunk-Ränder keine Löcher haben ist ja alles gut 😉

    Hah! Und das hüpfen der Lok.
    Das Video ist ziemlich alt, inzwischen fahren 200 Loks mit 1Khz bildrate auf den dynamisch gerenderten Tracks die mit dem VBO gestreamt werden, Basis bleiben die Kontrollpunkte die der User Erzeugt hat und Du sagst es es ist der Catmul Raum der hier massiv für 3D Splines verwendet wird. Den Link habe ich repariert, man kann die aktuelle Demo laden, und spielen lassen. Die Rotationen der Loks auf der Spline erfolgen mit unterschiedlichen Translationen, das ist hoch komplex der Spline 1:1 zu folgen , während deine Hauptkamera das betrachten muss 🙂

    Auch auf die Gefahr hin, dass ich dir vermutlich nichts neues erzähle (einfach ignorieren in dem Fall 😉 ):

    Ich kann mir vorstellen, dass ein Problem dabei ist, dass solche Parametrischen Kurven (Splines) meist eine Variable "Geschwindigkeit" haben. D.h. wenn du für f(t)f(t) das tt konstant erhöhst, dann bewegt sich der Kurvenpunkt, den du erhältst, nicht immer mit der selben Geschwindigkeit. Die Lösung dafür ist Bogenlängenparametrisierung (arc length parameterization).

    Ein weiteres Problem mit diesen Splines ist, dass sie keine natürliche "Normale" haben und man nicht wirklich weiß wo "oben" ist, wenn man einen Zug draufsetzt. Wie handhabst du das? Ich kann mir vorstellen die simpelste Lösung ist einfach die Normale des Bodens zu "erben", auf dem die Schiene liegt. Für freie Kurven im Raum (z.B. für Kamerafahrten) kann man denke ich die Normale auch mit dazu "modellieren": Man gibt einfach für die Endpunkte jedes Spline-Segments an, wo die "oben"-Richtung ist (im lokalen Koordinatensystem der Kamera oder des Fahrzeugs auf der Kurve). Die Normale kann man dann für die Kurvenpunkte dazwischen interpolieren. So hab ichs zumindest mal für ein Projekt gemacht, wo wir Kamerafahrten mit Catmull-Rom-Splines definiert haben.

    lleicht kann man auch schauen ob die Schienen dabei die Unterseite der Lok berühren
    Nee das währe viel zu langsam, es folgt also den Kontrollpunkten mit entsprechenden Offsets, die spline zwischen den Kontrollpunkten wird bei der Fahrt tatsächlich ständig im Voraus berechnet, da ich auch Weichen folgen muss was die Hölle war, die Gleise speichern also das Netzwerk was mit was verbunden ist, und die Lok generiert asynchron über threads den neusten Fahrweg über das Kontrollpunkt Netzwerk.

    Man muss das ja nicht in jedem Frame machen. Die Geometrie der Lok und Waggons bedingt ja einen minimalen Krümmungsradius. Ich würde Behaupten, für nen einfachen 4-Räder-Wagen und für vertikale Krümmungen ist das der kleinste Kreis der durch Vorder- und Hinterräder läuft, und den Boden des Wagens nicht berührt. Man kann die Kurve dann stückweise prüfen, dass sie den minimalen Krümmungsradius nicht unterschreitet. Für Horizontale Krümmung will man das vielleicht sowieso haben. Ich denke nicht, dass eine Lok mit angezogener Handbremse eine nahezu 180°-Wende im Stand hinlegen kann. Da will man vielleicht prüfen, ob ein Stück überhaupt befahrbar ist, und kann das eben auch auf die vertikale Krümmung ausweiten (über "Hubbel" fahren), berechnet aus Radabstand und Bodenhöhe des Wagens. Für mehr Spielspaß kann das dann auch ne "Crash-Bedingung" sein 😉

    Kurve wirkt dann oft ein wenig "matschig".
    Ja also mit der Maus drop't der User automatisch beiom tracken diese Kontrollpunkte die einen gewissen pitch haben, der Mechanismus verhindert zb Hakenschläge oder 90° Richtungsänderungen, so entsteht durch das Kontrollpunkt System automatisch im Vorfeld das es keine Gleisbrüche durch unterschrittene Winkel gibt.

    Ah. Hatte ich eben überlesen. Ja, man kann die Kontrollpukte auch so "tunen" dass illegale Gleise gar nicht baubar sind.

    "Überschwingen"
    Genau man muss wissen das der Catmull Algorithmus einem 5wer System unterliegt man also damit keine Strecken abbilden kann die nicht im Moduls 5 sind, macht man es kleiner wird es garstiger mit der Streuung, also das ist am ende alles Feinabgestimmt. Ein Heiden Aufwand.

    Ich frage mich, ob es nicht einfacher sein könnte, dem User das Feintuning zu überlassen, indem man eben z.B. auch ermöglicht, die selbe Kurve auch als Bézier- oder B-Spline zu bearbeiten (kann man verlustfrei hin- und zurück-transformieren). Die mathematisch zu "glätten" stell ich mir schwieriger und letztendlich "volatiler" vor. Man kann sich dann z.b. darauf beschränken dass, gewisse Krümmungen nicht überschritten werden. Aber das weisst du sicher besser, wie es am besten in dein Gesamtonzept passt. Mache nur etwas Brainstorming hier.

    Aber vielleicht hast du ja ganz andere Prioritäten im Moment,
    Ja im Moment bin ich dabei die Codemenge zu reduzieren , unnütze Daten zu entfernen, und einen sauberen Editor herzustellen, das Projekt ist 10 RNA Stufen gesplittet RNA1 -10

    Das ist auf jeden Fall ein komplexes Projekt. Das konnte man schon direkt im Video erkennen 😉

    Du scheinst da schon recht Tief drinnen zu stecken 🙂 Also danke für deine Analysen / Interesse
    Ich mach da stupide Weiter und es wird Jahre brauchen bis es ein 50 Player MMPGO abbildet so wie Sid Mayer es vor 30 Jahren vorlegte, unglaublich was die da auf dem Amiga oder PC integriert haben mit den Rechnern früher unglaublich....

    Hehe. 50 Spieler "MMO" ist schon nen hartes Ziel. Da kommt mir gleich in den Sinn dass es auf jeden Fall gute Mechanismen braucht, um Trollen und Griefern Einhalt zu gebieten. So Späße wie den Startpunkt des Anfängers mit einer kreisrunden Bahnstrecke einzubauen. Vielleicht so was wie einen sicheren "Startbereich" um das Gebiet, in dem man anfängt, wo man gut was bauen kann für den Anfang und bevor man sich ins "offene PvP-Gebiet" wagt 😃

    Grüße aus Berlin, und danke für deine Hinweise Mega Antwort leider geworden sorry...

    Kein Problem. Fand ich alles sehr interessant zu lesen! Viel Erfolg damit! Ich glaub da gibts auch sicher einige Foren und Communities für derartige Spiele. Da kann man vielleicht auch mal für Interessenten werben. Und du weißt sicher, dass es da auch eine Reihe Spiele in die Richtung gibt. Da kann man sich später auch sicher etwas Inspiration abholen. Für Spielmechanik finde ich persönlich OpenTTD ganz interessant. "Railway Empire" hab ich auch mal ne Weile gespielt und fand es spaßig. Das wäre eins mit 3D-Streckenbau wenn ich mich recht entsinne. Und das hatte auch ein nettes Feature, wo man einfach mal relaxen und in Ego-Perspektive (Lokführer) mit einem seiner Züge durch die Landschaft fahren konnte. Das ist etwas, das am Ende nicht fehlen sollte - eine gemütliche Zugfahrt durch die eigene Kreation 😃



  • @Finnegan sagte in Rail Road Tycoon MFC in Action:

    way E

    Hey Finnegan,

    Das Projekt ist auch auf GitHub, https://github.com/KahnSoft/FlexxRail

    Hab da nicht ganz die Muße, mir den Code anzusehen
    Ja ist auch nur ein Fetzen, zeigt aber den Vorgang der Chunk -Generierung ohne Cracks untereinander, da spielt ein gewisses Micro Overlapping eine rolle, das am ende aber kein Brot frisst (+2 auf den sizes der Chunks) Es wird sonnst Abstrus Rechen lastig wenn man da keine Tricks anwendet, also alles ist immer ein Kompromiss zwischen best of und Perform. Wenn man da Hunderte Loks mit Anhängern anzeigt, braucht man jeden Rechenquant, solange kein Gameplay statt findet, muss ich immer 1Khz Framerate erreichen, sonnst ist es später vorbei, also minimalste Operationen mit bestmöglichen Ergebnis.

    die Berglandschaft in dem Video ist nicht unbedingt eine Heightmap-Geometrie sondern ein eventuell generischeres 3D-Modell
    Genau es ist keine Highmap es ist alles dynamisch mit der Perlin Noise erzeugtes Grid- dessen Höhenwerte vom Shader mit Texpixeln im shader Texturmischer belegt werden, ich erzeuge damit riesige Karten mit Kantenlängen von bis zu 1 Million Dreiecke Seitenlänge was riesig ist, die Loks fahren mit rund 145Kmh alles bereist authentisch proportional, und ich kann die Kamera auf eine Fahrende Lok aufschalten und diese verfolgen, die benötigt in diesem Affentempo rund 5 Minuten um eine Seitenlänge zu passieren. Eine Highmap dieser Größe sprengt den Speicherrahmen , es ist auch nicht gerade günstig eine Textur zu haben die 10000 Pixel Breit und Hoch ist. Man nennt das mit vielen Highmaps dann Content-Streaming DirectX SDK macht das vor, aber ich muss wie gesagt insgesamt die Performance erhalten , es können später tausende Objekte mit den Chunks und dem LodSystem so veraltet werden. Da bin ich auch froh drüber es ohne ContentStreaming und Ohne HighMaps gemacht zu haben, zumal nach der Highmap dann nochmal selbige ColorMap als Textur benötigt wird, alles wird also Dynamisch zur Laufzeit erzeugt, die Qualität ist natürlich nicht die einer ColorMap, aber sie ist apokalyptisch groß, es wird also keine 3D -Lok Simulation wo jedes Gauge im Kesselhaus zu sehen ist, das zerbricht die Welt der unendlichen Weiten , das wird der große Vorteil gegenüber statischer Körperwelten. Also Aktienhandel Resourcenabbau alles Basiert auf Primitives, in einer erschlagenden Dimension.

    Nur die Ränder der "Szene",
    Genau keine Cracks in der Szene (NoGo) lediglich die Ränder haben eine Treppensignatur, aber die könnte ich wegrechnen ist aber unnötig out of SkyBox

    Parametrischen Kurven (Splines) meist eine Variable "Geschwindigkeit" haben.
    Da die Lok die Spline kennt (also die errechnet die selbst , es existieren real nur Kontrollpunkte) erzeugt sie für die Kurvigkeit eine Speed-Drossel die wirkt für Aufstieg bremsend für Abstieg Beschleunigend und für rechts links Radien Bremsend.

    void CSplineObjFollow::CalcSpeed()
    {
      const int dirSz = (int)m_DirN.size();
      if (m_SplineIdx < 0 || m_SplineIdx >= dirSz)
      {
        m_ConsecUphill = 0;
        m_ConsecDownhill = 0;
        m_ConsecCurve = 0;
        m_UphillFlt *= 0.9f;
        m_DownhillFlt *= 0.9f;
        m_CurveFlt *= 0.9f;
        m_LastSpeedFactor = 1.0f;
        return;
      }
    
      // Calculate Slope directly
      const float s = m_DirN[m_SplineIdx].z;
    
      // Calculate Curvature directly
      float avgCurv = 0.0f;
      int curvSamples = 0;
      const int splineSz = (int)m_Spline.size();
    
      if (splineSz >= 3)
      {
          const int maxCenter = splineSz - 2;
          CVertex* splinePtr = m_Spline.data();
          int si, center;
          float c;
    
          for (si = 0; si < 3; ++si)
          {
              center = m_SplineIdx + si;
              if (center < 1) center = 1;
              else if (center > maxCenter) center = maxCenter;
    
              c = Spline::LocalCurvature(splinePtr[center - 1], splinePtr[center], splinePtr[center + 1]);
              if (c > 0.0f) { avgCurv += c; ++curvSamples; }
          }
    
          if (curvSamples) avgCurv /= (float)curvSamples;
      }
    
      const float absS = fabsf(s);
      float factor = 1.0f;
    
      if (absS > SFF_EFFECT_THR)
      {
        float eff = absS - SFF_EFFECT_THR;
        if (s > 0.0f)                                                          // Bergauf: langsamer
        {
          if (eff > SFF_MAX_UP_EFF) eff = SFF_MAX_UP_EFF;
          factor -= eff * m_UphillFlt;
        }
        else                                                                   // Bergab: schneller
        {
          if (eff > 0.10f) eff = 0.10f;
          factor += eff * m_DownhillFlt;
        }
      }
    
      if (avgCurv > 1e-5f) ++m_ConsecCurve; else if (m_ConsecCurve > 0) --m_ConsecCurve;
    
      const float invUp = 1.0f / (float)SFF_CONSEC_FULL_UP;
      const float uphillTarget = (m_ConsecUphill > 0) ? (m_ConsecUphill * invUp) : 0.0f;
      const float downhillTarget = (m_ConsecDownhill > 0) ? (m_ConsecDownhill * invUp) : 0.0f;
      const float curveTarget = (avgCurv > 0.0f) ? (avgCurv / (avgCurv + 0.003f) * 1.5f) : 0.0f;
    
      m_UphillFlt += (uphillTarget - m_UphillFlt) * ((uphillTarget > m_UphillFlt) ? SFF_RISE : SFF_DECAY);
      m_CurveFlt += (curveTarget - m_CurveFlt) * ((curveTarget > m_CurveFlt) ? SFF_RISE : SFF_DECAY);
      m_DownhillFlt += (downhillTarget - m_DownhillFlt) * ((downhillTarget > m_DownhillFlt) ? SFF_RISE : SFF_DECAY);
    
      if (m_CurveFlt > 0.0f)                                                   // Kurven: langsamer
      {
        float tmp = 1.0f - SFF_CURVE_MAX_RED * (m_CurveFlt * (1.0f + 0.65f * m_CurveFlt) * (1.0f + 0.75f * m_CurveFlt));
        if (tmp < 0.1f) tmp = 0.1f;
        factor *= tmp;
      }
    
      if (factor < 0.25f) factor = 0.25f;
      else if (factor > 1.5f) factor = 1.5f;
    
      const float smooth = (factor < m_LastSpeedFactor) ? SFF_SPEED_SMOOTH_DOWN : (SFF_SPEED_SMOOTH_UP / (1.0f + m_Speed * SFF_SPD_SMOOTH_SCL));
      m_LastSpeedFactor += (factor - m_LastSpeedFactor) * smooth;
    }
    
    void CSplineObjFollow::CalcSpeedA()
    {
      float factor = 1.0f;
      const int dirSz = (int)m_DirN.size();
    
      if (dirSz >= 2 && m_SplineIdx >= 0)
      {
        float maxProxy = 0.0f;
        const CVertex *dirPtr = m_DirN.data();
    
        for (int i = 0; i < 3; ++i)
        {
          int pidx = m_SplineIdx + i;
          if (pidx < 1) pidx = 1;
          else if (pidx >= dirSz) pidx = dirSz - 1;
    
          int p0 = (pidx > 0) ? pidx - 1 : 0;
          float d = dirPtr[p0].Dot(dirPtr[pidx]);
          if (d > 1.0f) d = 1.0f;
          else if (d < -1.0f) d = -1.0f;
    
          float proxy = 1.0f - d;
          if (proxy > maxProxy) maxProxy = proxy;
        }
    
        const float curveEffect = (maxProxy > 0.0f) ? (maxProxy / (maxProxy + 0.001f)) : 0.0f;
        m_CurveFlt += (curveEffect - m_CurveFlt) * ((curveEffect > m_CurveFlt) ? SFF_RISE : SFF_DECAY);
        factor = 1.0f - SFF_CURVE_MAX_RED * (curveEffect * (1.0f + 0.75f * m_CurveFlt));
      }
    
      if (factor < 0.15f) factor = 0.15f;
      else if (factor > 1.5f) factor = 1.5f;
    
      float alpha = 0.20f + 0.55f * (1.0f - factor);
      if (alpha > 1.0f) alpha = 1.0f;
      if (factor < m_LastSpeedFactor) alpha = alpha * 0.9f + 0.5f * (1.0f - alpha);
    
      m_LastSpeedFactor += (factor - m_LastSpeedFactor) * alpha;
    }
    

    Diese Verfolgung von Tracks wird also in separaten Threads berechnet, und für jede Lok durchlaufen, da gibt es nicht die geringsten Probleme mit der Render Geschwindigkeit , es wird also kein Normal benötigt um die Geschwindigkeit auf den Dynamischen Tracks zu erhalten, die allerdings über normal vektoren verfügen, jedoch nie von der Mechanik abgefragt werden, die Tracks haben auch eine Beschreibung über den sog. TrackFragmentGenerator:

    /*
    ==========================================================
    TrackFragment Eigenschaften & Geometrie-Regelwerk
    ==========================================================
    
    1) Grundstruktur (RNA1):
       - Ein TrackFragment besteht aus mehreren Einzelteilen: Balast, Slicer (Schienen), Sleeper (Schwellen).
       - Die Geometrie wird entlang einer Eingangsspline (VertexLst) erzeugt und verteilt.
       - Jeder Einzelteiltyp hat eigene Parameter und Farben.
    
    2) Balast (RNA1):
       - Wird als U-Profil ohne Boden generiert.
       - Besteht aus Seitenwänden und Dach, keine Unterseite.
       - Die Breite und Höhe sind abhängig vom Maßstab (scale).
       - Die Balast-Spline wird mit niedriger Dichte (Subsampling) erzeugt.
       - Speicherbedarf steigt mit Dichte und Länge der Spline.
    
    3) Slicer (Schienen) (RNA1):
       - Werden als U-Profile ohne Boden generiert.
       - Bestehen aus Seitenwänden und Dach, keine Unterseite.
       - Slicer sind schmaler und höher als der Balast.
       - Es werden zwei Slicer erzeugt: linke und rechte Schiene, jeweils entlang der Spline versetzt.
       - Slicer erhalten eine eigene Farbe für Seiten und Dach.
       - Subsampling reduziert Speicher und Rechenlast.
    
    4) Sleeper (Schwellen) (RNA1):
       - Werden als Quader erzeugt, keine U-Profile.
       - Liegen quer zur Spline und verbinden die beiden Slicer.
       - Die Platzierung erfolgt gleichmäßig entlang der Spline, am Anfang, Ende und dazwischen.
       - Sleeper erhalten eigene Farben für Seiten und Oberseite.
       - Anzahl und Abstand beeinflussen die Komplexität.
    
    5) Geometrie-Erzeugung (RNA1):
       - Die Methode GenerateVbo erzeugt die Einzelteile entlang der Spline.
       - Für Balast und Slicer wird die Spline mit unterschiedlichen Schrittweiten abgetastet (Subsampling).
       - Die Geometrie wird als Vertex-, Normal-, Color- und Index-Arrays für das VBO erzeugt.
       - Datenfluss: Spline -> Sampling -> Einzelteil -> VBO.
    
    6) Farben und Materialien (RNA1):
       - Jede Komponente (Balast, Slicer, Sleeper) erhält eigene Farben für Seiten und Dach/Oberseite.
       - Die Farben werden beim Erzeugen der Vertices zugewiesen.
       - Farbwechsel im Code beachten.
    
    7) VBO-Handling (RNA1):
       - Die erzeugten Geometrien werden in einem VBOSet gespeichert.
       - Nach der Erzeugung wird das VBO für die Darstellung vorbereitet.
       - Speicherbedarf und Performance hängen von der Anzahl der Vertices und Indices ab.
    
    8) Besonderheiten (RNA1):
       - Die Geometrie ist so ausgelegt, dass keine Bodenflächen für U-Profile entstehen.
       - Die Verteilung und Ausrichtung der Einzelteile erfolgt dynamisch entlang der Eingangsspline.
       - Fehlerquellen: Indizierung, Subsampling, falsche Parameter.
    
    9) Komplexitätsfaktoren (KI-relevant):
       - Anzahl und Dichte der Einzelteile (Balast, Slicer, Sleeper).
       - Länge und Form der Eingangsspline.
       - Subsampling-Parameter und Profilgrößen.
       - VBO-Speicherbedarf und Render-Performance.
       - Korrekte Indizierung und Farbzuteilung.
       - Robustheit gegen fehlerhafte Eingabedaten.
    
    10) Speicherverwaltung und Vektorzugriffe (RNA1):
       - Alle Geometrie-Daten werden in std::vector gespeichert.
       - Jeder push/remove/clear/erase-Aufruf ruft automatisch die Destruktoren der enthaltenen Objekte auf.
       - Dadurch werden Ressourcen immer korrekt freigegeben.
       - Speicherlecks sind ausgeschlossen, solange keine rohen Zeiger verwendet werden.
    
    11) LOD-Rendering für entfernte Fragmente (RNA1):
       - Ab einer Entfernung von TRACK_LOD_DIST werden weniger Indices im VBO gezeichnet.
       - Ein visueller Hinweis (z.B. "LOD aktiv") wird rechtsbündig am Fragment angezeigt.
       - Die LOD-Distanz ist als TRACK_LOD_DIST im Code definiert.
       
       LOD-Priorität (VBO-Reihenfolge, Index 0 = höchste Priorität):
       +-------+------------------+---------------+---------------------------+
       | Phase | Komponente       | LOD-Priorität | Beschreibung              |
       +-------+------------------+---------------+---------------------------+
       |   1   | Gauges           | Höchste       | Lichttafel (4 Lichter)    |
       |   1   | Dipper           | Höchste       | Signalflügel              |
       |   2   | Switch-Cube      | Hoch          | Weichen-Hitbox            |
       |   3   | Terminal         | Hoch          | Bahnsteig-Geometrie       |
       |   4   | Signal-Mast      | Mittel        | Stange + Sockel           |
       |   5   | Prellbock        | Mittel        | Buffer Stop               |
       |   6   | Ballast          | Niedrig       | Schotter (viele Faces)    |
       |   6   | Slicer           | Niedrig       | Schienen (viele Faces)    |
       |   6   | Sleeper          | Niedrig       | Schwellen (viele Faces)   |
       +-------+------------------+---------------+---------------------------+
       
       Bei LOD-Reduktion werden zuerst Sleeper/Slicer/Ballast weggelassen,
       Gauges und Dipper bleiben bis zuletzt sichtbar.
    
    12) Switch-Cube für Weichen (RNA2):
       - Wird nur bei TYPE_SWITCH_TRK erzeugt.
       - Besteht aus einem Würfel (Cube) und einem Sockel (Pyramiden-Stumpf) neben dem Gleis am Ende der Spline.
       - Position: Neben dem Balast in Fahrtrichtung, versetzt um die halbe Balast-Breite plus zusätzlichen Abstand.
       - Größe: Würfel hat scale * 1.6f Größe, Sockel ist doppelt so breit.
       - Farben: Würfel in Slicer-Farben (Grün=Open, Rot=Closed), Sockel in Balast-Farben.
       - Zweck: Visuelle Darstellung von Weichen-Switches mit Statusanzeige (Open/Closed).
       - Bounding Box: Separate m_SwitchBBoxMin/Max für Kollision oder Selektion.
    
    13) Signal-Mast für Weichen & Strecken (RNA3):
       - Wird bei TYPE_SWITCH_TRK (automatisch) oder via SnapIn (manuell) erzeugt.
       - Besteht aus mehreren Komponenten:
         a) Sockel: Pyramiden-Stumpf auf Terrain-Höhe
         b) Mast: Zwei vertikale Streben (Leiter-Stil) mit Sprossen
         c) Kopfstück: Horizontaler Balken am oberen Ende
         d) Ausleger: Horizontale Stäbe zur Seitenstabilisierung
         e) Dipper: Beweglicher Signal-Flügel (Formsignal)
         f) Gauge: Lichttafel mit 4 Signallichtern (Hp0/Hp1/Hp2)
       - Position:
         * Weichen: Automatisch in der Mitte der Spline.
         * Strecken: An definierter SnapIn-Position (dotIdx).
       - Dipper-Zustände:
         * TYPE_SIGNAL_OPEN gesetzt: Dipper horizontal (Hp0 = Halt)
         * TYPE_SIGNAL_OPEN nicht gesetzt: Dipper nach unten (Hp1 = Fahrt frei)
       - Farben: Mast in Balast-Farben, Dipper rot mit weißer Füllung und Ring.
       - Bounding Box: Separate m_SignalBoxes für Klick-Erkennung (umschließt gesamten Mast).
       - Debug-Anzeige: Weiße Box, roter Text "Hp0"/"Hp1".
       - Lok-Steuerung: Signal-Status wird in SnapIn.flags gespeichert.
    
    14) Prellbock / Buffer Stop (RNA4):
       - Wird automatisch bei TYPE_PILLER_TRK erzeugt (Track mit offenem Ende).
       - Erkennung: forwardFragmentId == -1 ODER backwardFragmentId == -1.
       - Position: Am Gleisende, versetzt um 3 * SLEEPER_PITCH nach hinten.
       - Struktur:
         a) Gleis-Extension: Echtes Gleis (Balast/Slicer/Sleeper) vom Ende bis zum Prellbock.
         b) Schraege Stuetzen: 2x 45-Grad Hypotenuse (links/rechts auf Schienen).
         c) Vertikale Kateten: 2x senkrechte Pfosten hinter den Stuetzen.
         d) Querbalken oben: Horizontaler Balken zwischen den Kateten-Spitzen.
         e) Querbalken unten: Horizontaler Balken am Fuss der Stuetzen.
       - Doppelgleis: 2 separate Prellboecke mit Gleisabstand versetzt.
       - Verkehrsvertrag: Am Prellbock dreht nur die Fahrtrichtung der Lok. Ihre
         m_TrackSide bleibt unveraendert; der seitliche Render-Offset wird gespiegelt.
         Bei einem Doppelgleis-Hostwechsel wird zusaetzlich die Weltposition fuer
         die Seitenkontinuitaet ausgewertet, damit Weichen keinen Spurwechsel erzeugen.
       - Farben: Stuetzen grau, Querbalken in Sleeper-Farben.
       - Rekursionsschutz: TYPE_PILLER_TRK wird fuer Extension-Spline entfernt.
    
    15) Terminal / Bahnsteig (RNA5):
       - Länge basiert auf Zuglänge: TERMINAL_WAGON_LEN × TERMINAL_MAX_WAGONS = 15000 units
       - 1 Waggon/Lok = 1500 units, Max 10 Waggons am Bahnsteig
       - Z-Ebene: Einheitliche Höhe (Mittelwert aller Dots) - keine Wellen!
       - Zwei Signale (A + B) an den Enden für Face-to-Face Steuerung.
       - Dynamische Verkürzung wenn Track zu kurz ist.
    
    16) Verkehr & Fahrplaner (RNA6):
       - Die Basis-Verkehrslogik ist aktiv: physische Doppelgleis-Seiten,
         Bremsen und Disaster-Erkennung laufen in GLSceneTraffic.
       - Ein globaler Fahrplaner/Scheduler über dieser Basis bleibt geplant.
       - Er nutzt RNA1-5 (Gleise, Weichen, Signale, Prellboecke, Terminals)
         fuer Zeitsteuerung und Automatisierung von Weichen/Signalen.
    */
    
    

    nicht in jedem Frame machen. Die Geometrie der Lok und Waggons bedingt ja einen minimalen Krümmungsradius.
    Tatsächlich wird es sogar öfters berechnet als pro jedem Frame, das geht mit bis zu 20 tausend Iterationen also 20Khz
    in den Threads, die Loks fahren seidig und extrem Pixel genau auf den Gleiskörpern.
    Durch die Translationen für die Lok -Mesh Körper werden diese exact an der Spline geführt dazu wird für die Loks eine Pivot-Translation verwendet:

    void CGLmatrix::TranslatePivotXYZTrack(const CVertex& world, CVertex& pivot, const CVertex& forward, const CVertex& right, const CVertex& up)
    {
    	float* m = m_m0;
    
    	// Snapshot original orientation (R0) and translation (t0)
    	const float r0x = m[0],  r0y = m[1],  r0z = m[2];
    	const float r1x = m[4],  r1y = m[5],  r1z = m[6];
    	const float r2x = m[8],  r2y = m[9],  r2z = m[10];
    	float       tx  = m[12], ty  = m[13], tz  = m[14];
    
    	// Phase 1: t1 = t0 + R0 * (world + pivot)
    	const float v0x = world.x + pivot.x;
    	const float v0y = world.y + pivot.y;
    	const float v0z = world.z + pivot.z;
    	tx += r0x * v0x + r1x * v0y + r2x * v0z;
    	ty += r0y * v0x + r1y * v0y + r2y * v0z;
    	tz += r0z * v0x + r1z * v0y + r2z * v0z;
    
    	// Phase 2: DIREKTE Matrix-Orientierung aus Spline-Basis (statt Euler-Rotation)
    	// R' = R0 * SplineBasis (Forward, Right, Up direkt verwenden)
    	m[0] = r0x * forward.x + r1x * forward.y + r2x * forward.z;
    	m[1] = r0y * forward.x + r1y * forward.y + r2y * forward.z;
    	m[2] = r0z * forward.x + r1z * forward.y + r2z * forward.z;
    	
    	m[4] = r0x * right.x + r1x * right.y + r2x * right.z;
    	m[5] = r0y * right.x + r1y * right.y + r2y * right.z;
    	m[6] = r0z * right.x + r1z * right.y + r2z * right.z;
    	
    	m[8] = r0x * up.x + r1x * up.y + r2x * up.z;
    	m[9] = r0y * up.x + r1y * up.y + r2y * up.z;
    	m[10] = r0z * up.x + r1z * up.y + r2z * up.z;
    
    	// Phase 3: t' = t1 + R' * (-pivot)
    	tx += m[0] * (-pivot.x) + m[4] * (-pivot.y) + m[8]  * (-pivot.z);
    	ty += m[1] * (-pivot.x) + m[5] * (-pivot.y) + m[9]  * (-pivot.z);
    	tz += m[2] * (-pivot.x) + m[6] * (-pivot.y) + m[10] * (-pivot.z);
    	m[12] = tx; m[13] = ty; m[14] = tz; // keep m[15] as is
    }
    
    

    Und für normale nicht dem Track folgender Objekte eine Pivot normale Translation (Beides Teil der GL Engine wo jede Matrix wenn sie fertig im Speicher liegt nur einmal per Frame an GL gesendet wird was zu wahnsinnigen Ablaufraten führt:

    void CGLmatrix::TranslatePivotXYZ(const CVertex& world,  CVertex& pivot, const CVertex& rot)
    {
    	float* m = m_m0;
    
    	// Snapshot original orientation (R0) and translation (t0)
    	const float r0x = m[0],  r0y = m[1],  r0z = m[2];
    	const float r1x = m[4],  r1y = m[5],  r1z = m[6];
    	const float r2x = m[8],  r2y = m[9],  r2z = m[10];
    	float       tx  = m[12], ty  = m[13], tz  = m[14];
    
    	// Phase 1: t1 = t0 + R0 * (world + pivot)
    	const float v0x = world.x + pivot.x;
    	const float v0y = world.y + pivot.y;
    	const float v0z = world.z + pivot.z;
    	tx += r0x * v0x + r1x * v0y + r2x * v0z;
    	ty += r0y * v0x + r1y * v0y + r2y * v0z;
    	tz += r0z * v0x + r1z * v0y + r2z * v0z;
    
    	// Phase 2: R' = R0 * R(rot) – RotXYZ(rot) on the basis columns
    	const float cX = CCalculate::LutCos(rot.x); const float sX = CCalculate::LutSin(rot.x);
    	const float cY = CCalculate::LutCos(rot.y); const float sY = CCalculate::LutSin(rot.y);
    	const float cZ = CCalculate::LutCos(rot.z); const float sZ = CCalculate::LutSin(rot.z);
    
    	// Start from current m (R0) and apply X, then Y, then Z exactly like RotXYZ
    	float x0, y0, z0, x1, y1, z1, x2, y2, z2;
    	// X-axis
    	x0 =  m[0];  y0 =  m[1];  z0 =  m[2];
    	x1 =  m[4] * cX + m[8] * sX;   y1 =  m[5] * cX + m[9] * sX;   z1 =  m[6] * cX + m[10] * sX;
    	x2 =  m[4] * -sX + m[8] * cX;  y2 =  m[5] * -sX + m[9] * cX;  z2 =  m[6] * -sX + m[10] * cX;
    
    	m[0] = x0; m[1] = y0; m[2] = z0;
    	m[4] = x1; m[5] = y1; m[6] = z1;
    	m[8] = x2; m[9] = y2; m[10] = z2;
    
    	// Y-axis
    	x0 =  m[0] * cY + m[8] * -sY;  y0 =  m[1] * cY + m[9] * -sY;  z0 =  m[2] * cY + m[10] * -sY;
    	x1 =  m[4];                    y1 =  m[5];                     z1 =  m[6];
    	x2 =  m[0] * sY + m[8] * cY;   y2 =  m[1] * sY + m[9] * cY;   z2 =  m[2] * sY + m[10] * cY;
    
    	m[0] = x0; m[1] = y0; m[2] = z0;
    	m[4] = x1; m[5] = y1; m[6] = z1;
    	m[8] = x2; m[9] = y2; m[10] = z2;
    
    	// Z-axis
    	x0 =  m[0] * cZ + m[4] * sZ;   y0 =  m[1] * cZ + m[5] * sZ;   z0 =  m[2] * cZ + m[6] * sZ;
    	x1 =  m[0] * -sZ + m[4] * cZ;  y1 =  m[1] * -sZ + m[5] * cZ;  z1 =  m[2] * -sZ + m[6] * cZ;
    	x2 =  m[8];                    y2 =  m[9];                     z2 =  m[10];
    
    	m[0] = x0; m[1] = y0; m[2] = z0;
    	m[4] = x1; m[5] = y1; m[6] = z1;
    	m[8] = x2; m[9] = y2; m[10] = z2;
    
    	// Phase 3: t' = t1 + R' * (-pivot)
    	tx += m[0] * (-pivot.x) + m[4] * (-pivot.y) + m[8]  * (-pivot.z);
    	ty += m[1] * (-pivot.x) + m[5] * (-pivot.y) + m[9]  * (-pivot.z);
    	tz += m[2] * (-pivot.x) + m[6] * (-pivot.y) + m[10] * (-pivot.z);
    	m[12] = tx; m[13] = ty; m[14] = tz; // keep m[15] as is
    }
    

    einfacher sein könnte, dem User das Feintuning zu überlassen
    Der braucht ein klares Spielerlebnis , das da beim gleise Legen irgendwelche Kapriolen passieren soll der nicht handhaben,
    es spielen ja auch viele Kinder oder bei großen Gleisanlagen währ es zu frikelig, der Editor unterstellt dem User ganz klare Edit Regeln die unbedingt von der Software sichergestellt werden müssen so wie bei einem CAD Programm.

    50 Spieler "MMO" ist schon nen hartes Ziel.
    Auf jeden Fall, die können sogar Server mieten und ihr eigenes Terrain (Ressourcen) über Tunnels an das Maingrid anschließen, das läuft dann aber über Linux auf Mietservern, das Thema alleine sprengt schnell den Rahmen da muss man aufpassen, es beginnt in kürze mit der Fahrplan Erstellung und dem Zug-Roster der die Wagongs koppelt(folge Jahr) . Sicher kommen dort Hacker und Einkreiser usw. muss man sehen das sich das selber verwaltet. UDP SSL Kodierter Transfer mal sehen das ist noch ein Weilchen hin. Aber die Kommunikation ist im Prinzip klar, läuft dann zuerst auf ner VM am Nebentisch...

    Viel Erfolg damit! Ich glaub da gibts auch sicher einige Foren und Communities für derartige Spiele.
    Na erstmal noch nicht , in dem Zustand interessiert das nur Experten, es gibt ja noch kein Game, in Foren habe ich bis heute noch keinen gehört dem das gefällt 🙂 Da bist Du der erste mit dem ich drüber Chatten kann 🙂
    Das OpenTTD ist eine andere Liga, da will ich nicht hin, ich mache ein ganz spezielles Railroad wo der User Signale weichen Bahnhöfe auch über Lua -Skript kontrollieren kann, also Stichwort alles ist volldynamisch und generisch.

    Ego-Perspektive (Lokführer)
    Genau ich habe einen Gleistracker der mit der Kamera alle bestehnden Gleise abfährt (Neben der Zugverfolgung) Das jetzt schon ein Wahnsinns ScreenBlanker !

    Danke für deine Aufmerksamkeit
    Ich berichte mal immer gerne über solche Sachen 🙂
    Gruß Karsten



  • Das klingt tatsächlich nach einem ziemlich speziellen Ansatz. Gerade die Kombination aus dynamischer Gleislogik, Lua-Steuerung und Ego-Perspektive stelle ich mir spannend vor. Ich hatte selbst schon mal mit einem kleinen Simulationsprojekt herumgespielt und gemerkt, wie schnell allein die Darstellung der Gleise komplex wird. Dein Gleistracker klingt jedenfalls nach einem netten „ScreenBlanker“. Nebenbei bin ich auf diese Seite gestoßen: Mehr zum Thema Kimono Herren. Bin gespannt, wie sich das weiterentwickelt!



  • @Sternlicht4384 Hallo Sternlicht,

    ja Du sagst es sehr schnell kann man mit so einem Riesenprojekt in die Eskalation wandern, ich bin dabei nun nichts neues hinzuzufügen, sondern die Stabilität und Prozesse innert der komplexen Vorgänge zu stabilisieren damit eine Veränderung nicht an anderer Stelle Auswirkungen haben kann muss alles in Aktionstiefen verankert sein die sich nicht gegenseitig berühren.
    Zb. Das ein schneller Zug einen langsamen einholt, nun muss der rechtzeitig dessen Tempo übernehmen um nicht aufzufahren. Oder wenn ein Terminal(Bahnhof) bereits belegt ist, der Zug am Eingangssignal wartet bis der andere Zug das Terminal verlassen hat, da gibt es viele Szenarien an Signalen die vor einer Kollision erkannt sein müssen. Es gibt auch in der Demo noch viele Gleis Anschluss Unpässlichkeiten, außerdem fehlen die Tunnels noch. Es gibt also Doppelgleise Single Gleise Taper(Reduzierstück) Signale Terminals Weichen die man stellen kann über Lua oder händisch. Ich brauch das ganze restliche Jahr nur zur Optimierung und Sicherstellung der Abhängkeitsisolation. Das wird auch noch einige Jahre so weiter gehen bis wir old Sid Mayer das Wasser reichen werden, das müssen echte Programmierer gewesen sein, die das mit Assembler auf dem Amiga oder 286 ziger implemtiert haben.
    Wenn das alles läuft und die Vorlage Assets plausible sind (siehe Binary all die meshes die dann optimiert mit drehenden rädern als Anim gespeichert werden müssen in Foldern mit einzelnen 3DS Dateien.. Haufen Holz alles für einen zu viel.

    Danke für deine Hinweise, vielleicht machst'e später mit, wenn es mehr zu bieten hat als aktuell ? 🙂
    Grüße aus Preußen
    K.


  • Mod

    @KahnSoft : Ich muss dich leider enttäuschen. Das wird ein Spammer sein. Leute melden sich nicht in Foren neu an, um dann als ersten und einzigen Beitrag solche Texte zu verfassen. Da wird in den nächsten Tagen ein Edit kommen, der dann Werbung für ein Kasino macht.

    @Sternlicht4384 : Für die 0.1% Chance, dass du kein Bot sein solltest: Entschuldigung. Ich bin ja auch immer fair und warte, bis der Werbelink tatsächlich erscheint, bevor ich jemanden sperre. Falls ich also Unrecht haben sollte, ist noch nichts passiert.



  • @SeppJ Ich hatte auch schon so den Verdacht... aber lass uns erst warten, was passiert. Offenbar scheinen Programmierer genau die Zielgruppe für (Online-) Kasinos zu sein.

    Aber mal btt... Zu den MFCs in C++ kann ich nicht all zu viel sagen.



  • @KahnSoft Bin gerade beim Aufräumen wieder über mein altes Projekt mit dem Heightmap-Terrain-LOD gestolpert und dachte ich kann bei der Gelegenheit auch mal ein paar Screenshots posten, um das Prinzip zu verdeutlichen. Das soll nicht heißen, dass es das Nonplusultra ist - ich will das nur als potentielle Inspiration in den Raum stellen.

    Heightmaps haben ja den Vorteil, dass man sie mit einer simplen Bild 1: Schachbrett-Triangulierung rendern kann, bei der die Vertices dann mit senkrecht zur Fläche den Werten der Heightmap moduliert werden.

    Die Idee des "kontinuierlichen" LOD das ich implementiert habe ist, dass die Anzahl der Dreiecke mit jeder LOD-Stufe vervierfacht wird und dabei aus einer quadratischen "Zelle" mit zwei durch eine diagonale Kante verbundenen Dreiecken vier solcher Zellen mit 8 Dreiecken werden. Um den Übergang "fließend" zu machen wird zwischen der einen Zelle und den vier Zellen "gemorpht", was bei dieser dieser Methode bedeutet, den zentralen Vertex einer Vierer-Zelle in Richtung eines der Eckpunkte zu verschieben (Bild 2: LOD-Übergangs-Moprhing) bis 6 der Dreiecke verschwunden sind. Ab dem Punkt wird dann das niedriger aufgelöste Mesh verwendet.

    Diesen Übergang kann man dann von der Distanz zur Kamera abhängig machen, was dann so aussieht:

    Bild 3: Kontinuierliches LOD auf Mesh

    Oder mit aufmodulierter Heightmap:

    Bild 4: Kontinuierliches LOD mit Heightmap

    Und texturiert und mit simpler Beleuchtung:

    Bild 5: Kontinuierliches LOD mit Texturen

    Ich finde die Methode insofern ganz gut, weil das Landschafts-Mesh nur langsam in eine höhere oder niedrigere Auflösung "morpht", wenn man sich nähert oder entfernt. Die Veränderungen der Geometrie sind dabei nicht so offensichtlich, als wenn man ab einer gewissen Distanz direkt in eine andere Auflösung "springt".



  • Hallo Finnegan,
    ah so meinst du das der Lod mit der Höhe zunimmt, tatsächlich habe ich das hier so, das es Grundplatten(Chunks) gibt, die mit zunehmender Entfernung weniger Dreiecke zeigen. Die Höhe der Hügel hat hier keine Kopplung mit der Grid-Auflösung.
    Eine Highmap kam leider für das Projekt nicht in Frage weil die Dimension der Karte fast eine Millionen Dreiecke Seitenlänge aufweist. Mit dem Chunks erreiche so trotz der riesigen Dimension bis zu 1Khz Bildwiederholraten, denn ich muss auch noch Züge und Gleise rendern, diese unterliegen auch einem Lod, der ist einfach je weiter ein Mesh entfernt ist desto weniger seiner Dreiecke werden gerendert, die kleinen stücke fliegen zuerst aus der Pipe , und die größten teile zuletzt.
    https://www.flexxvision.de/Download/Chunks.jpg
    So kann ich unendlich viele Gleise Loks und Terrains rendern, das Terrain wurde Z-Seitig mit der Perlin Noise angehoben was diese Hügel produziert, da kann ich dann generisch nich mehr machen, allerdings verfügt die Engine auch über Highmap Terrains.
    https://www.flexxvision.de/Download/Highmap.jpg
    Hier im Bild in einem anderen Kontext ohne Lod's
    Ja die Weise die Dreiecke zu verdichten bei unterschiedlichen Z -Höhen habe ich noch nicht implementiert, das würde sich gut mischen mit den Chunks die im zweiten Bild jedoch nicht eingesetzt werden, da die Größe mit 30Hz Echtzeit Z Variation von einer Kamera kommend schnell genug war.

    Sehr interessant wie Du das umgesetzt hast, obschon das erste Raster ohne Textur viele doppelte Linien aufwies.

    Im Moment mache ich nicht gerade viel am Projekt, mal sehen wann ich wieder loslege.

    Vielen Dank für deine Ausführung , das ganze heißt Vertice Quaternation https://medium.com/@Ksatese/b-spline-terrain-reconstruction-and-quaternion-camera-in-opengl-ec66491c0623

    Naja ist ja bald wieder kalt draußen, und dann hat man mehr musse 🙂
    Gruß
    K aus B.



  • @KahnSoft sagte in Rail Road Tycoon MFC in Action:

    ah so meinst du das der Lod mit der Höhe zunimmt, tatsächlich habe ich das hier so, das es Grundplatten(Chunks) gibt, die mit zunehmender Entfernung weniger Dreiecke zeigen. Die Höhe der Hügel hat hier keine Kopplung mit der Grid-Auflösung.

    Nein, das hat nichts mit der Höhe zu tun, sondern ist auch ein System, das mit zunehmender Entfernung von der Kamera die Grid-Auflösung reduziert. Es würde aber durchaus Sinn machen, auch mit zunehmender Steigung die Auflösung zu erhöhen, bei besonders steilen Anstiegen werden ansonsten einzelne Dreiecke sehr lang gezogen. Das ist aber ein sehr simples Heightmap-Rendering, dass ich da implementiert habe: Die Der Heightmap-Wert an der Position eines Grid-Vertex bestimmt direkt seine Höhe. Das ließe sich sicher noch etwas intelligenter machen, um die Qualität des Renderings zu erhöhen.

    Eine Highmap kam leider für das Projekt nicht in Frage weil die Dimension der Karte fast eine Millionen Dreiecke Seitenlänge aufweist. Mit dem Chunks erreiche so trotz der riesigen Dimension bis zu 1Khz Bildwiederholraten,

    Das Grid-Rendering sollte mit sowas klar kommen dank LOD, aber die Heightmap selbst kann schon ziemlich groß werden. In meinem Fall ist das eine simple 16-Bit Grausttufen-Textur (8-Bit ist eher ungeeignet für Heightmaps, da es wegen der nur 256 Abstufungen unschöne Treppenartefakte erzeugt). Und mit der Methode hast du natürlich recht: Eine 1000000x1000000 Heightmap-Textur mit 2 Byte pro Pixel wäre etwa 1,8TiB groß.

    Alternativ könnte man die Heightmap aber auch prozedural auf der GPU und ebenfalls in einer entfernungsabnhänigen Auflösung generieren, dann sollte der Speicherbedarf nicht so hoch sein. Das ist ja auch bei dir bereits bereits eine prozedurale Landschaft, wenn ich das mit dem Perlin Noise richtig verstehe.

    Vielen Dank für deine Ausführung , das ganze heißt Vertice Quaternation https://medium.com/@Ksatese/b-spline-terrain-reconstruction-and-quaternion-camera-in-opengl-ec66491c0623

    Ist das die Technik, die du für das Landschaftsrendering verwendest? Wenn ich das beim groben Überfliegen richtig verstanden habe, werden dabei implizite B-Spline-Flächen auf die Höhendaten (Heightmap-Textur oder on-the-fly-generierter Perlin Noise) gefittet und dann daraus die Geometrie generiert. Ich ziehe da keine so klare Grenze und würde das ebenfalls den Heightmap-Techniken zuordnen. Allerdings den fortgeschritteneren, während mein Ansatz eher primitiv ist.

    Es ist auch so, dass die von mir beschriebene Methode für kontinuierliches LOD nicht unbedingt an Heightmaps gekoppelt ist. Die kann man in der einen oder anderen Form eigentlich immer anwenden, wenn man es mit so einer gleichförmigen Schachbrett-Triangulierung zu tun hat, wie das z.B. auch bei deinen Chunks der Fall ist. Es ist halt lediglich eine einfache Methode um verschiedene LOD-Stufen fließend ineinander übergehen zu lassen, anstatt harte Grenzen zu haben.

    Hier is z.B. so eine harte Grenze bei dir (aus dem "Chunks" Screenshot). Was passiert da, wenn man näher an den hinteren Chunk herankommt? Ich vermute mal, der springt von einem Frame auf den anderen in eine höhere Auflösung (?). Die primäre Motivation meines Ansatzes ist nämlich, eben solche Sprünge zu vermeiden und die LOD-Übergange kontinuierlich mit der Kamera "mitwandern" zu lassen und so die auflösungsbedingte Veränderung der Geometrie weitgehend vor dem Betrachter zu verstecken.

    Das LOD wäre damit nicht mehr an die Chunks gebunden, sondern ausschließlich an Kameraposition. Chunks in diesem Übergangsbereich müssen natürlich eine höhere Auflösung haben, damit man z.B. in einem Vertex-Shader diesen Überhang hinein-transformieren kann. Ich denke schon, dass sich dein chunk-basiertes System und so ein kontinuierliches LOD nicht unbedingt ausschließen.

    Aber letztendlich ist das nur ein nicht ganz so wichtiges Detail und du hast gewiss erstmal andere Prioritäten. Auch ist es fraglich, ob diese LOD-Sprünge überhaupt auffallen, so wie du die Landschaft aktuell renderst. Das ist also eher was für weit unten auf der Prioritätsliste - aber vielleicht ist es ja eine Idee für den letzten Feinschliff irgendwann mal 😉

    Sehr interessant wie Du das umgesetzt hast, obschon das erste Raster ohne Textur viele doppelte Linien aufwies.

    Ich denke das was du mit den "doppelten Linien" sind diese Übergänge hier von einer LOD-Stufe in die nächste. Ja, die sind etwas unschön und sie erzeugen stellenweise ein paar degenerierte Dreiecke. Die treten aber nur in diesen Übergangsbereichen auf und driften dann reissverschlussartig auseinander, wenn man näher herankommt. Diese "doppelten Linien" hat man dort, wo das Gitter z.B. 10% in die höhere Auflösung "gemorpht" ist und die niedrigere Auflösung noch zu 90% dominiert. Die ergeben sich schlicht aus dem Schema, dass ich anwende um zwei LOD-Stufen ineinander zu überführen.

    Vielen Dank für deine Ausführung , das ganze heißt Vertice Quaternation https://medium.com/@Ksatese/b-spline-terrain-reconstruction-and-quaternion-camera-in-opengl-ec66491c0623

    Auch dir vielen Dank. macht Spass sich über sowas auszutauschen und interessant zu sehen, wie du sowas umsetzt. Den verlinkten Artikel werd ich mir nochmal in Ruhe durchlesen. Wenn ich nochmal sowas umsetze, würde ich sicher auch in Richtung NURBS/B-Splines mit GPU-generierterter, prozeduraler Landschaft gehen und vielleicht versuchen, das kontiunuierliche LOD dabei zu integrieren. Auch ist mein Ansatz noch lange nicht perfekt, vor allem wegen dem eher simplistischen "Morphing"-Schema. Das lässt sich sicher noch weiter verfeinern.



  • @Finnegan sagte in Rail Road Tycoon MFC in Action:

    s

    Hi Finnegan ,

    ist das die Technik, die du für das Landschaftsrendering verwendest?
    Nein aber die Seite hat viele Aspekte die man sich ansehen kann, oder integrieren könnte, ist rein sporadisch aus dem Netz gewählt, mein Chunksystem musste ich selber herstellen, und ist von anderen erstmal abgekoppelt .

    Die primäre Motivation meines Ansatzes ist nämlich, eben solche Sprünge zu vermeiden und die LOD-Übergange kontinuierlich mit der Kamera "mitwandern"

    Ja also das Chunksystem basiert ausschließlich auf den Kameraabstand, es ist vollständig flüssig Sprünge sind mit dem Auge nicht erkennbar es hat 5 Detailstufen, in der Ferne werden mehr als 90% der Dreiecke entfernt, und das kann man gar nicht sehen, die Sache läuft sehr feinstufig , und das ist erst der Anfang, durchaus könnte es sein das ich da nochmal dran gehe, aber für den Moment gibt es keine Probleme Artefakte oder Sprünge, auch nicht wenn ich eine Lok verfolge mit hoher Geschwindigkeit. Sie erreichen umgerechnet bis zu 160 Km/h die Zeit Basis und die Lokomotive länge geben diesen Wert der Physischen Geschwindigkeit, das Terrain fliegt wie bei einem Racing an einem vorbei.
    Auch die primitive Mesh reduzierung liefert sehr gute LOD Ergebnisse, allerdings kann ich hier noch mehr fine Tuning später ansetzen, zb ein Haus mit einem Tisch auf dem ein becher steht, der muss natürlich zuerst verschwinden auf Vertex ebene und am ende erst das Dach des Hauses, das ist noch nicht 100% gelöst, allerdings sieht man auch keine Problem beim vergrößern des Kameraabstandes.

    Sehr wichtig war das bei den Gleisen die bestehen aus Balast(Schotterbett) Sleeper(Schwellen) und Slicer (Schienen) hier fliegen zuerst die Schienen raus dann der Balast, und das Schottertet bleibt sehr lange liegen so hat man aus großer Entfernung noch immer die Gleisführung im Bilde. Allerdings wie bei den Meshes werden in der Entfernung immer mehr Dreiecke entfernt.

    Naja nette Spielerei, ich muss nach den Aufräumarbeiten die fast abgeschlossen sind, weiter in die Zugsteuerung investieren was passiert an welchen Signalen Terminals, Weichen Doppelgleise das heißt bei mit RNA1-7 Das wird noch viel Zeit benötigen, schließlich wird das Verhalten auch über Lua gesteuert in Echtzeit.

    Das kann schnell zur Lebensaufgabe mutieren , denn so einfach ist das alles nicht, mal ebend ein Spiel zu bauen , da muss man wirklich viel Motivation immer wieder neu aufbauen, und gerade diskussionen am Thema steigern genau diese.

    In dem Sinne Netter Thread geworden, alles mögliche angesprochen, vielleicht gibt es auch andere die eine Frage dazu haben ?

    Gruß Karsten


Anmelden zum Antworten