Rail Road Tycoon MFC in Action
-
Hallo zusammen,
nach Monate langer Entwicklung in OpenGL C/C++ habe ich einen ziemlichen Grundstatus erreicht in Sachen Eisenbahn als Rail Road Tycoon Clon erreicht, noch immer suche ich nach Leuten die Bock haben
mit zumachen, ich gebe bereits einen Download mit allen Loks und Anhängern und dem dynamischen Lua Gleis-System zum Download preis. Alles in 3DSmax gehalten.Man kann die im Moment einzige Lok einfach durch eine ähnliche ersetzen. Das wird mit den Jahren wachsen bis zum 50 Player Game, es ist aber noch in der Alpha Phase, aber bereits jetzt sind alle
Grafiken und Skripte zugänglich und modifizierbar, erstmal nur ein Preview aber weichen gleise usw. haben bereits RNA1-6 Erreicht Desaster Kollisionen sind schon integriert aber offline sonnst würden alle sich
aus den Gleisen werfen, der Gleis Editor ist Spline Line basierend und an Komplexität zum Wahnsinnig werden, aber es wird bald die Basis gelegt sein, es ist massiv mit KI Unterstützung erstellt, bezüglich dynamischer Szenarien. Grüße aus Berlin ich freue mich wenn wer auch bock drauf hat. www.flexxvision.de/flexxrail.html
Karsten
-
@KahnSoft sagte in Rail Road Tycoon MFC in Action:
Habe den Link mal repariert und kurz reingeschaut. Ziemlich cooles Projekt!
Auch interessant, dass du das mit einer industriellen Visualisierungs-Engine umgesetzt hast. Die ist für so ein Spiel in diesem Simulations-Genre sicher nicht ganz ungeeignet, da es weniger auf neueste photorealistische Grafikeffekte sondern mehr auf passende Tools zur Modellierung und Simulation ankommt. Ich kann mir vorstellen, dass ein Action-Shooter vielleicht schwieriger wäre. "Professionell" und Spiele schließen sich aber nicht aus, letztendlich sind so ziemlich alle Spiele auch "Simulationen"
.Was mich bei dem Vorführungsvideo interessiert hat: Haben die Chunks ein fixes LOD oder gibt es da einen kontinuierlichen Übergang? Und wenn fix, wie werden die "Nähte" an den Rändern gehandhabt? Die passen ja meist meist nicht sauber zusammen und erzeugen Löcher, wenn man die einfach so nebeneinander setzt.
Ich habe vor Jahren mal einen Heightmap-basierten Landschaftsrenderer gebaut und da ein kontinuierliches LOD verwendet, dass sich das gleichförmige Gitter der Heightmap-Geometrie zunutze gemacht hat. Habe nur noch vage Erinnerung, aber die Grundidee war 4 quadratische Zellen mit je 2 Dreiecken (Vertices in Y-Richtung durch die Heightmap moduliert) kontinuierlich zu einer Zelle mit zwei Dreiecken zu "morphen" (bzw. umgekehrt, wenn man in höheres Detail übergeht). Die Operation ist für so ein Heightmap-Gitter ziemlich simpel zu berechnen und kann in einem Vertex-Shader für die "Übergangs-Chunks" gemacht werden, die zwischen zwei LOD liegen. Ich fand das Resultat sehr brauchbar, da es visuelle "Sprünge" zischen den LOD vermieden hat (wenn man näher kommt oder sich entfernt). Fall so du sowas in die Richtung nicht eh schon machst, kann ich das auch gern nochmal rauskramen.
Hah! Und das hüpfen der Lok. Ich glaub ich sehe was hier passiert: Die Lok ist immer parallel zur Tangente der Schienen-Kurve, oder? So aus dem Bauch heraus würde ich denken, dass man am besten die Punkte findet, wo die Räder der Lok die Schiene berühren und das für die Neigung der Lok heranzieht. Z.B. zwei Punkte: Vorderrad und Hinterrad (für ne ganz simple "Lok"). Vielleicht kann man auch schauen ob die Schienen dabei die Unterseite der Lok berühren und so bestimmen, ob das ein Hügel ist, wo die Lok überhaupt drüber kommt ohne stecken zu bleiben.
Und was den Schienenbau angeht: Splines können echt fummelig sein, saubere Arbeit! Was für Splines sind das? Dass man die Punkte definiert, durch die die Schienen laufen sollen ist erstmal sehr intuitiv und eine brauchbare Funktion.
Anregung:
Ich persönlich bin bei so einer Schienen-Modellierung ein wenig "Stetigkeitsfanatiker" und versuche dabei immer auch die Krümmung stetig zu bekommen, wenn ich zwei Kurvensegmente verbinde. Bei interpolierenden Splines (z.B. Catmull-Rom), die durch die Kontrollpunkte laufen, finde ich das meist extrem schwierig und die Kurve wirkt dann oft ein wenig "matschig". Approximierende Splines (wie Bézier- oder B-Splines), bei denen die inneren Kontrollpunkte meist nicht auf der Kurve liegen fällt mir das wesentlich leichter. Da wird dann die Modellierung aber weniger intuitiv.
Man kann das denke ich auch automatisiert "glätten" und Koeffizienten finden, bei denen die Kurve durch die vorgegebenen Punkte läuft und an den Verbindungen krümmungsstetig ist. Da glaube ich aber, dass das nicht immer das gewünschte Ergebnis erzeugt. Das könnte sehr "volatil" sein und die Verbindung erstmal "Überschwingen" und eine riesige Schleife bilden lassen bevor sie dann (mathematisch korrekt) krümmungsstetig in das nächste Kurvensegment übergeht. Ich denke ein hybrider Modus wäre vielleicht ein guter Ansatz: Standardmäßig definiert man die Interpolationspunkte, durch die die Kurve laufen soll, aber man kann dann bei Bedarf umschalten und mit Approximations-Kontrollpunkten modellieren um die Krümmung besser "tunen" zu können. Die Kurven sollten sich für sowas von einer Repräsentation in die jeweils andere überführen lassen (z.B. Catmull-Rom <-> B-Splines).
Aber vielleicht hast du ja ganz andere Prioritäten im Moment, besonders wenn du schreibst, dass die Komplexität bereits jetzt "zum wahnsinnig werden" ist. Lass dich nicht von meinem Krümmungsfetisch kirre machen!

Wie gesagt: Cooles Projekt, halt uns gern auf dem laufenden - auch wenn ich persönlich gerade nicht die Zeit habe, da allzu tief einzusteigen

-
@Finnegan sagte in Rail Road Tycoon MFC in Action:
Lass dich nicht von meinem Krümmungsfetisch kirre machen!
Ähh, ich wollte gerade anmerken dass Klothoide im Straßen- und Eisenbahnbau eingesetzt werden.

Bei einer Kurvenfahrt muss das Lenkrad gedreht werden, um das Fahrzeug in den Bogen einzulenken. Bei konstanter Fahrgeschwindigkeit und gleichmäßiger Zunahme des Lenkeinschlages bewegt sich das Fahrzeug auf einer Linie, die näherungsweise einer Klothoide entspricht. Durch die Klothoide als Übergangselement wird sichergestellt, dass der Fahrer nicht zu einem abrupten Lenkmanöver gezwungen wird, sondern die Querbeschleunigung linear wächst oder abnimmt.
Diese Eigenschaft finde ich spannend. Ich habe aber damit noch nicht gearbeitet.
-
@Finnegan sagte in Rail Road Tycoon MFC in Action:
ht has
Hallo Finnegan,
du hast in deiner Antwort alle wesentlichen Punkte erkannt, das ist schon mal eine Hausnummer, ich versuche mal auf deine Hinweise zu Antworten:industriellen Visualisierungs-Engine
Ja Die Engine mit shadern und Terraingenerations ist von mir seit 26 Jahren in der Entwicklung, sie basiert auf Glide (Heute) , und enthält die gesamte Visualisierung von Meshes mats lights und Netze Genrationen, die ist fähig alles abzubilden, und was nicht läuft ergänze ich dann entsprechend, gutes Zeichen ich muss sehr selten an der Engin arbeiten, die Anrufer der Engine haben also keine Ahnung was in dieser passiert, das ist gut weggekapselt.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.bool CGLchunk::Create(int Width, int Height, float SceneScale, bool bTexOn, bool bColOn, CGLfont* pFnt, CVertex* pVert, CVertex* pNorm, CTexvert* pTVert, CVertex* pColor, GLuint* pIdx, CGLShader* pShader) { Delete(); DWORD ti(GetTickCount()); m_ChunkSize = 256 + 2; //A128 + 4 + (LODS-2) DD to the normal 128 Chunk the extra Size of 2 (for inspect deeper into neighnbors !! m_Width = Width; m_Height = Height; m_pIdx = pIdx; m_SzeneScale = SceneScale; m_SceneSize = max(Width, Height) * SceneScale; m_pVert = pVert; m_pNorm = pNorm; m_pTVert = pTVert; m_pColor = pColor; m_pFnt = pFnt; m_pShader = pShader; m_SzeneMinZ = FLT_MAX; m_SzeneMaxZ = FLT_MIN; CVersions::DbgMsg(0, "CGLchunk::Create ChunkSize=%d SzeneSize=%d ChunksNeed=%d\n", m_ChunkSize, m_SceneSize, m_SceneSize / m_ChunkSize); for (int y = 0; y < Height; y += m_ChunkSize) { const int extraY((y + m_ChunkSize < Height) ? 1 : 0); int xChunk(m_ChunkSize + extraY); if (y + xChunk > Height) xChunk = Height - y; for (int x = 0; x < Width; x += m_ChunkSize) { const int extraX((x + m_ChunkSize < Width) ? 1 : 0); int yChunk(m_ChunkSize + extraX); if (x + yChunk > Width) yChunk = Width - x; TerrainChunk chunk; chunk.yStart = y; //if(chunk.yStart + chunk.height >= Height) || (chunk.xStart + chunk.width >= Width)) //TEST Debug check only border chunks chunk.xStart = x; chunk.height = xChunk; chunk.width = yChunk; const int indexTopLeft((y * Width) + x); const int indexBottomRight(((y + xChunk - 1) * Width) + (x + yChunk - 1)); chunk.MinX = m_pVert[indexTopLeft].x; chunk.MinY = m_pVert[indexTopLeft].y; chunk.MaxX = m_pVert[indexBottomRight].x; chunk.MaxY = m_pVert[indexBottomRight].y; chunk.MinZ = FLT_MAX; chunk.MaxZ = FLT_MIN; for (int r = 0; r < chunk.height; ++r) for (int c = 0; c < chunk.width; ++c) { const int globalIndex((chunk.yStart + r) * Width + (chunk.xStart + c)); const float z(m_pVert[globalIndex].z); if (z < chunk.MinZ) chunk.MinZ = z; if (z > chunk.MaxZ) chunk.MaxZ = z; if (z < m_SzeneMinZ)//need for frag shader texel tile m_SzeneMinZ = z; if (z > m_SzeneMaxZ)//need for frag shader texel tile m_SzeneMaxZ = z; } for (int level = 0; level < LODS; level++) //generate for each level an index grid { const int lodFactor(1 << level); chunk.startIndex[level] = (GLuint)m_GlobalIndices.size(); // Setze den Startindex im globalen Buffer for (int r = 0; r < chunk.height; r += lodFactor) for (int c = 0; c < chunk.width; c += lodFactor) { if ((chunk.yStart + r + lodFactor) >= Height || (chunk.xStart + c + lodFactor) >= Width) continue; const int topLeft = (chunk.yStart + r) * Width + (chunk.xStart + c); const int topRight = topLeft + lodFactor; const int bottomLeft = (chunk.yStart + r + lodFactor) * Width + (chunk.xStart + c); const int bottomRight = bottomLeft + lodFactor; m_GlobalIndices.push_back(topLeft); m_GlobalIndices.push_back(bottomLeft); m_GlobalIndices.push_back(topRight); m_GlobalIndices.push_back(bottomLeft); m_GlobalIndices.push_back(bottomRight); m_GlobalIndices.push_back(topRight); } chunk.indexCount[level] = (GLuint)m_GlobalIndices.size() - chunk.startIndex[level]; // Setze die Anzahl der Indices } chunk.center = CVertex(0.5f * (chunk.MinX + chunk.MaxX), 0.5f * (chunk.MinY + chunk.MaxY), 0.5f * (chunk.MinZ + chunk.MaxZ)); m_Chunks.push_back(chunk); } } CVersions::DbgMsg(true, "CGLchunk::Create InnertCalcTime=%f [s] \n", ((float)GetTickCount() - ti)/1000.0f); float ChunkLenght(m_ChunkSize * m_SzeneScale); for (int n(0); n < LODS; n++) //Generate a list with accurate distances to any chunk for camera based on each chunk Size m_LODThresholds[n] = ChunkLenght * (n + 1); //n+2 means nearby chunks full rendered //------------------------ Init Sahder Attributes and VBO's once here ------------------------------ m_Vbo.Create(pVert, pNorm, pColor, pTVert, m_GlobalIndices.data(), bTexOn, bColOn, Width * Height, m_GlobalIndices.size()); m_pShader->Enable(); if (bTexOn) { if (!Load4Textures()) return CVersions::DbgMsg(0, "CGLchunk::Create Failed load 4 Texels\n"); m_pShader->SetUniform1i("uWaterTex", 1); m_pShader->SetUniform1i("uGrassTex", 2); m_pShader->SetUniform1i("uRockTex", 3); m_pShader->SetUniform1i("uMountainTex", 4); m_pShader->SetUniform1f("uMinHeight", m_SzeneMinZ); m_pShader->SetUniform1f("uMaxHeight", m_SzeneMaxZ); float tile = 0.001f; m_pShader->SetUniform1f("uWaterTileFactor", tile); m_pShader->SetUniform1f("uGrassTileFactor", tile); m_pShader->SetUniform1f("uRockTileFactor", tile); m_pShader->SetUniform1f("uMountainTileFactor", tile); m_pShader->SetUniform1f("uFallbackStart", 5000.0f); m_pShader->SetUniform1f("uFallbackEnd", 20000.0f); m_pShader->SetUniform1i("uUseTexture", 1); } else { m_pShader->SetUniform1i("uUseTexture", 0); } m_pShader->SetAttributePointers(m_pVert, m_pNorm, m_pTVert, m_pColor, bTexOn); m_pShader->EnableAttributes(bTexOn); m_pShader->SetUniform3f("vLightPos", 0, 0, 50000.0f); m_pShader->SetUniform3f("uLightColor", 1, 1, 1); m_pShader->SetUniform3f("vViewPos", 0, 0, 0); m_pShader->Disable(); return CVersions::DbgMsg(true, "CGLchunk::Create CalcTime=%d\n", GetTickCount() - ti); }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.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)
#version 330 core /* ==================================================================== FLEXXRAIL SHADER: Fragment-Shader für Terrain (reduziert) ==================================================================== */ in vec3 FragPos; in vec3 Normal; in float vHeight; in vec3 VertexColor; in float isBillboard; in vec2 TexCoord; // neu: aus VS/GS übernommen out vec4 FragColor; uniform sampler2D uWaterTex, uGrassTex, uRockTex, uMountainTex; uniform float uWaterTileFactor, uGrassTileFactor, uRockTileFactor, uMountainTileFactor; uniform float uMinHeight, uMaxHeight; uniform bool uUseTexture; void main() { if (!uUseTexture || isBillboard > 0.5) { FragColor = vec4(VertexColor, 1.0); return; } // Höhe normieren float normH = clamp((vHeight - uMinHeight) / max(uMaxHeight - uMinHeight, 1e-6), 0.0, 1.0); // Layer-Gewichte (weich) float wWater = smoothstep(-0.03, 0.07, normH); float wGrass = smoothstep(0.18, 0.28, normH); float wRock = smoothstep(0.40, 0.50, normH); float wMount = smoothstep(0.65, 0.85, normH); // Projektive UVs: Worldspace-Planar mapping per dominanter Achse vec3 an = abs(Normal); vec2 uv = TexCoord; // fallback: aus VS if (an.x > an.y && an.x > an.z) uv = FragPos.yz; else if (an.y > an.z) uv = FragPos.xz; else uv = FragPos.xy; vec4 cWater = texture(uWaterTex, uv * uWaterTileFactor); vec4 cGrass = texture(uGrassTex, uv * uGrassTileFactor); vec4 cRock = texture(uRockTex, uv * uRockTileFactor); vec4 cMount = texture(uMountainTex, uv * uMountainTileFactor); vec4 mix1 = mix(cWater, cGrass, wWater); vec4 mix2 = mix(mix1, cRock, wGrass); vec4 mix3 = mix(mix2, cMount, wRock); FragColor = mix3; }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
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.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.
Damit werden auch Bezieher Anschluss stellen erzeugt (Weichen) .automatisiert "glätten" und Koeffizienten
Genau lässt der User den Tracking Kontrollmodus los, wird die Strecke sofort weich gerechnet Optimiert."Ü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.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 -10Das sieht im Moment(1Jahr) etwa so aus:
/* ================================================================================ SPLINE DOT SYSTEM - TRACK CONSTRUCTION & MANAGEMENT ================================================================================ STABILISIERUNGS-PHASE 1: Nur Verhalten sichern, keine Quer-Optimierungen. Aenderungen klein halten; nach jeder Aenderung Build + kurzer Interaktions-Test. AKTIONSPUNKT-ANKER (nur neben Aktionen in CPP, kurz/grepbar): // A1: Mutation (Rule #xx / RNAy) // A2: Guard/Abort (Rule #xx) // A3: Topology-Sync (RNA1/RNA2/RNA3/RNA5) INVARIANTEN (immer): EXAKTE Endpunkte (bit-identisch); Vorwaerts => neuer Host; Backtracking nur im aktiven Track; Switch nie verlaengern. ================================================================================ RNA UEBERSICHT (ACTIVE NETWORK ARCHITECTURE) ================================================================================ +------+----------------------------------+------------------+------------------+ | RNA | BESCHREIBUNG | DATEIEN | STATUS | +------+----------------------------------+------------------+------------------+ | RNA1 | Basis-Vernetzung | SplineObjFollow* | AKTIV | | | Endpunkt-zu-Endpunkt via | SplineTopology | | | | ConManager (fwd/bwd FragmentId) | | | +------+----------------------------------+------------------+------------------+ | RNA2 | Switch-Handling (Weichen) | SplineObjFollow* | AKTIV | | | Midpoint-Vernetzung via SnapIn | SplineDot | | | | TYPE_OPEN_TRK fuer Weichenstate | | | +------+----------------------------------+------------------+------------------+ | RNA3 | Signal-Handling (Dipper) | SplineDotSignal | AKTIV | | | Streckensignale, einseitig | SplineAccessory* | | | | TYPE_SIGNAL_TRK + TYPE_OPEN_TRK | | | +------+----------------------------------+------------------+------------------+ | RNA4 | Geometrie-Generierung | TrackUtil | AKTIV | | | Prellboecke, Single<->Double | SplineDot | | | | Uebergaenge, Piller | | | +------+----------------------------------+------------------+------------------+ | RNA5 | Terminal-Handling (Bahnhoefe) | SplineDotTerminal| AKTIV | | | Gauge-Signale (A+B), Face-to-Face| SplineAccessory* | | | | TYPE_TERMINAL_TRK + SIG_A/SIG_B | | | +------+----------------------------------+------------------+------------------+ | RNA6 | Verkehr & Kollisionen auf Track | GLSceneTraffic | AKTIV | | | Disaster nur gleiche phys. Spur | | TRAFFIC_DISASTER | | | Lua-Event flags=8 | | =1 (aktiv) | +------+----------------------------------+------------------+------------------+ KURZFORM: RNA1 = Gleise verbinden (ConManager, forwardFragmentId/backwardFragmentId) RNA2 = Weichen (Switch Entry/Exit, TYPE_OPEN_TRK fuer Stellung) RNA3 = Dipper-Signale (Streckensignale, TYPE_SIGNAL_TRK) RNA4 = Geometrie (Prellboecke, Single<->Double Uebergaenge) RNA5 = Terminals (Bahnhoefe mit Face-to-Face Signallogik) RNA6 = Verkehr (Spurfilter, Bremsen, Disaster bei gleicher physischer Spur) ================================================================================ SPLINE INTERAKTIONEN - REGELWERK ================================================================================ Hinweis: Spline-Fusion ist nicht aktiv. Geschlossene Loops werden als TRACK_LOOPED behandelt. Alle Punkte beschreiben den aktuellen Ist-Zustand. 1) Track-Cursor & Simulation: - Track startet an frei selektiertem Start-/Endpunkt einer offenen Spline oder frei im Raum. - Wird ein End-/Startpunkt gegriffen, genau dieser Punkt wird erster Trackeintrag; Original-Host wird temporaer entfernt. - Punkte folgen Maus live; Commit manuell (Button Up) oder automatisch bei Distanzlimit; Backtracking bis nur 1 Punkt => verwerfen. 2) Start-/Endpunkt vs. mittlerer Kontrollpunkt: - Nur Endpunkte dienen als Anknuepfung fuer Fortsetzung; Mittelpunkte koennen nicht zum Start promotiert werden. - Mittelpunkte erlauben nur Verschiebung innerhalb begrenzter Amplitude / Projektion (siehe Regel 6). - Host wird beim Fortsetzen aus Endpunkt aus Liste geloest bis Commit (Konsistenz, kein Doppel). 3) Snap-In (nur visuell): - Andocken fuehrt keine strukturelle Verschmelzung durch; Track bleibt separater Host nach Commit. - Snap darf nicht ausgeloest werden bei Rueckbau-Intent, Startpunkt, Near-Duplicate oder erreichten MAXTRKDIST. - Reihenfolge der Prioritaeten: Backtracking vor Snap vor Auto-Segment (siehe 24). 4) Optimize (Taste O): - Fuehrt Spline::Optimize mit Laengentoleranz durch, entfernt lineare Ueberfluessige und glättet Verlauf. - Setzt update=true (VBO Rebuild) sowie optDirty/curvedDirty zur spaeteren Anzeigeaktualisierung. - Kurven-Stuetzpunkte koennen nach Optimize erneut synthetisch eingefuegt werden (Regel 9 / 14). 5) Winkel-Latch (MINCURANG): - Wird MINCURANG ueberschritten setzt m_BadAngleLatch Aufnahme-Stopp (stabilisiert gegen Zickzack / Ruecksprung). - Entlatch bei Rueckkehr unter MINCURANG * 0.85, Basisrichtung aktualisiert sobald Vorwaertsprojektion > 0. - Latch interagiert mit Backtracking (Regel 25) indem Rueckbau die Winkelbasis ungueltig macht. 6) Verschieben mittlerer Kontrollpunkte: - Projektion auf Achse zwischen Nachbarn limitiert durch radiale Schutzsphaeren (RADSPHERE) und Mindest-Abstaende. - Orthogonale Amplitude wird gekapt (allowedAmplitude) adaptiv nach Segmentlaengen + ctrlAmpMul + CTRLAMPFACT. - Projektion ausserhalb Sicherheitsfenster oder unzulaessige Verkuerzung => Bewegung verworfen (keine Mutation). 7) Backtracking: - Rueckwaertsbewegung (projDist < Segmentende) entfernt letzte Punkte iterativ solange Bedingung anhalt. - Seitliche Abweichung (latSq) wird toleriert nur innerhalb Threshold (DOTPITCH_06_SQ) falls kein expliziter Rueckintent. - Nach Reduktion wird Referenzrichtung neu gesetzt, Latch zurueckgesetzt und Winkelbasis als unset markiert. 8) Track-Limit (MAXTRKDIST): - Erreicht Distanz Start->Cursor MAXTRKDIST => Auto-Commit des aktuellen Tracks (Segmentierung fuer Performance/Budget). - Neuer Track startet sofort mit letztem Endpunkt & m_RefDir (letztes Segment) fuer kontinuierliche Richtung. - Backtracking unterbricht Auto-Limit; Snap ist waehrend Limit-Trigger blockiert (Intent Vorrang). 9) Kurven-Stuetzpunkte (deaktiviert): - Historisch: Spline::IsCurved basierte Stuetzpunkteinfügung (Vor-/Nachlauf) bei Distanzfenster. - Aktuell: Optimizer löscht keine Kontrollpunkte mehr, daher keine zusätzliche Stuetzpunkteinfügung. - Cleanup entfällt. 10) Spline/VBO Update: - Bei update=true: HostSpline neu generiert (SPLINERES) & fragment VBO rekonstruiert; danach update=false. - Opt/Curved Dirty Flags veranlassen spaetere On-Demand Anzeige (Prozent & Kurvenmarkierung) ohne Rebuild. - Kein Redo solange keine Hostmutation (Punkte / Typ) oder Sichtbarkeitswechsel erfolgt. 11) Auswahl: - Keine Auswahl wenn (m_SelDot==0 && m_SelSpline==-1); Endpunkt-Fortsetzung setzt m_SelSpline temporaer -1. - Commit oder Abbruch (Backtracking bis 1 Punkt) ruft ClearSelect zwecks Zustand Reset auf. - Mittelpunktauswahl fuer Move prueft Typ-/Moduskompatibilitaet (BASE_TYPE_MISMATCH). 12) Sortierung: - Hosts werden in Folge minimaler Endpunktdistanz verkettet (Nearest-Neighbour Sequenz) mit optionalem Reverse. - Bei Nachbarnaehe < ALIGN_MAX_DIST erfolgt Ausrichtung (Translate/Rotate) ohne Merge / Dup-Elimination. - Nach Sortierung erfolgt AlignSortedSplines fuer glattere Uebergaenge (Dir Toleranz + Distanzkriterien). 13) Uebergangsverfeinerung: - m_TrackNeighbor Marker liefert anfangs m_RefDir fuer Startwinkel-Bewertung und Latch-Hysterese. - Erlaubt sanftere Anfahrtsphasen (verhindert zu fruehes Latch bei minimalem ersten Segment). - Wird beim Fortsetzen aus Host-Endpunkt gesetzt (Nachbar = zweiter Punkt). 14) Punkt-Reduktion: - ValidateTrack entfernt Near-Duplicates / sehr kurze Segmente bevor Optimize arbeitet (Qualitaet Filter). - Optimize reduziert lineare Ketten behält Kurven- u. Nachlaufbereiche; later Supportpunkte ggf. reinsert. - Ergebnis host.dots ersetzt Eingangsliste (swap) fuer minimale Kopiekosten. 15) Guard-Zonen: - Naehe zum Start (CLOSE_GUARD_SQ) oder Selbstannäherung (SELF_APPROACH_SQ) aktiviert Guard (dichtere Sampling Steps). - Guard reduziert stepSize (0.75 Faktor) in PlaceStandardPoints zum Stabilisieren frischer Kurven / enger Passagen. - Verhindert Zickzack durch zu große erste Schritte und Kreuzungen bei Ruecknaeherung. 16) Performance: - VBO Rebuild strikt an update Flag; Linien-Overlay nutzt HostSpline Cache (leichtgewichtige Vertexliste). - Angle / Optimize Prozent Lazy Evaluation (optDirty / curvedDirty) reduziert Berechnungen pro Frame. - Auto-Segmentierung (Regel 8) begrenzt Host-Laenge fuer bessere Frustum-Culling Granularitaet. 17) Persistenz: - hostlist.bin speichert: Anzahl Hosts, Punktanzahl, Punkte (CVertex Raw), update-Flag, Typ (Version >=2) + Mapping. - Version 8: Speichert forwardFragmentId und backwardFragmentId fuer Connection-Persistenz. - Laden erzeugt sofort VBO + HostSpline (update=false) und setzt Dirty Flags fuer Anzeige. 18) Stabilisierung: - Fortgesetzter Host wird nicht dupliziert sondern entfernt bis Abschluss (verhindert Inkonsistenzen / Doppelrender). - Backtracking nach Wiederaufnahme erzeugt keinen neuen Host sondern modifiziert laufenden Track. - Latch Reset & Richtungsbasis Handling nach Rueckbau verhindert Winkelsprung. 19) Weichen-Erzeugung: - Nach Commit: Distanz vom neuen Track-Ende zu Nicht-Endpunkt eines Fremd-Hosts <= DOTSEARCHRADIUS => Switch Host. - TYPE_SWITCH_TRK markiert Sondergeometrie (Ausschluss aus Optimize, Regel 20) und getrennte Verarbeitung. - rawTargetIdx Auswahl spaeter fuer Tangenten (Regel 21) im inneren Bereich (keine Randpunkte). 20) Switch & Optimize: - TYPE_SWITCH_TRK Hosts von Spline::Optimize ausgeschlossen (bewahrt kritische Abzweig-Stuetzpunkte). - Normale Single/Double Hosts werden optimiert fuer geringere Punktlast. - Switch Flags bleiben stabil bei Sortierung / Persistenz (Regel 27). 21) Switch-Zielwahl (DetermineTargetIdx): - Richtung basierend auf Anflugrichtung UND Host-Geometrie (beide Seiten pruefen). - Eindringtiefe dynamisch: 3-5 Steps je nach verfuegbarem Platz und Kruemmung. - Fallback zu anderer Richtung wenn gewaehlte Seite blockiert ist. - Minimum 3 Steps, Maximum durch maxPossible begrenzt. 22) Bezier Handles (BuildBezier): - Handle-Laenge: 0.40 * Distanz, skaliert mit Winkel, Clamp [1.5..3.5]*DOTPITCH. - Lateraler Offset seitenvalidiert (kein Durchdringen / Mindestabstand >=0.35*DOTPITCH). - Handles adaptiert bei enger Kurve um Ueberziehung / Selbstschneiden zu vermeiden. 23) Bezier Sampling: - Adaptives Parameter t Inkrement (TTSTEP Basis) bis segmentlaengenkriterium >= DOTPITCH erreicht oder Ende. - Fruehphase prueft Clearance gegen Host (keine Selbstkollision) vor Punktannahme. - Abschluss bereinigt zu dicht liegende Schlusssegmente fuer gleichmaessige Verteilung. 24) Snap-Prioritaet: - Prioritaet strikt: Backtracking > Snap > Auto-Segmentierung fuer klare Benutzerintention. - Snap wird nur evaluiert falls kein Rueckintent & Budget nicht ueberschritten & kein Near-Duplicate. - Auto-Segmentierung final nur ohne Snap/Backtracking Trigger in diesem Tick. 25) Angle-Latch & Backtracking: - Rueckbau invalidiert Winkelbasis (AngleBaseUnset) wodurch erste Vorwaertsbewegung Basis neu bestimmt. - Latch (m_BadAngleLatch) bleibt aktiv bis Winkelprojektion wieder innerhalb Hysterese (MINCURANG*0.85). - Verhindert Zickzack / sofortige Re-Latch Schleifen nach Korrekturbewegung. 26) Auto-Segment Neustart: - Nach Limit-Commit Startpunkt = altes Ende, m_RefDir = letztes Segment (kohärente Richtungskontinuitaet). - Winkelbasis neu initialisiert sobald ausreichend Vorwaertsschritt laenger als EPS erfolgt. - Verhindert Ausrichtungsspruenge im aufeinanderfolgendem Segmentfluss. 27) Switch Persistenz: - Weichen-Typ Flags dauerhaft (kein Downgrade durch Optimize / Sort / Persistenzmapping). - Keine automatische Punktverdichtung auf Switch Hosts zur Wahrung exakter Abzweig Geometrie. - Kurven-Flags bei Switch ebenfalls lazy neu generiert (curvedDirty) nach Mutationen. 28) Track-Limit Darstellung: - Kein visuelles Overlay fuer Limit (Reduktion visueller Komplexitaet / Fokus auf Cursor Feedback). - Logische Segmentierung steuert Speicher, Culling and Performance statt UI Indikatoren. - Limitpruefung vergleicht Start->Cursor Distanz (MAXTRKDIST_SQ) (Quadratvergleich ohne sqrt). 29) Connected-Index (geplant, noch inaktiv): - Spatial-Manager für verbundene Splines vorgesehen. 30) Weichen-Schalter (ToggleSwitchAt) [RNA2]: - Zur Laufzeit: Klick auf Switch-Cube toggelt TYPE_OPEN_TRK im host.type. - Suche: Iteration ueber alle Hosts mit TYPE_SWITCH_TRK. - Effekt: TYPE_OPEN_TRK bestimmt ob Lok in Switch einfahren darf. - VBO: Nach Toggle wird GenerateVbo aufgerufen (Farbe Gruen/Rot). 31) Signal-Schalter (ToggleSignalAt) [RNA3]: - Zur Laufzeit: Klick auf Signal-Box toggelt TYPE_OPEN_TRK. - Suche: Iteration ueber normale Hosts und deren m_SignalBoxes. - Effekt: Synchronisiert Flag in SnapIn (Logik) und Signal-Host (Optik). 32) Klick-Priorisierung (HandleSplines): - Nicht-Konstruktions-Modus: Mausklick prueft erst Switch, dann Signal. - Reihenfolge: if (!ToggleSwitchAt(pos)) ToggleSignalAt(pos); 33) RNA3 Signal-Editor (Modus): - Aktiv wenn g_InpMgr.GetSignalMode() != 0. - Phantom-Signal: RenderSignalCursor zeigt Vorschau. - Platzierung: Erzeugt SnapIn am Gleis UND separaten Signal-Host. 34) RNA3 Signal-Loeschung (DEL): - Taste DEL im Signal-Modus entfernt SnapIn UND Signal-Host. 35) RNA4 Prellbock (TYPE_PILLER_TRK): - Automatische Erkennung: forwardFragmentId==-1 ODER backwardFragmentId==-1. - Connection-IDs: Werden zentral in FindConnections() aufgebaut. 36) RNA4 Prellbock-Geometrie (GeneratePiller): - Position: Am Gleisende, versetzt um 3 * SLEEPER_PITCH. - Struktur: Klassischer Prellboecke mit 45-Grad Hypotenuse. 37) RNA4 Connection-Logik (FindConnections): - IDs werden nach jeder Topologieaenderung zentral aus exakten Endpunkten aufgebaut. - Switch-IDs bleiben autoritativ; normale Gegenreferenzen werden im selben Pipeline-Schritt synchronisiert. 38) RNA4 Double-to-Single Taper (Verjuengung): - Automatische Erkennung: Double-Track an Single-Track grenzt. - Uebergangszone: 20% der Spline-Laenge. 39) RNA5 Terminal-Editor (Modus): - Aktiv wenn g_InpMgr.GetTerminalMode() != 0. - Phantom-Terminal: RenderTerminalCursor zeigt Vorschau. - Platzierung: Erzeugt SnapIn mit TYPE_TERMINAL_TRK. 40) RNA5 Terminal Face-to-Face Logik (KRITISCH!): - Die Lok reagiert NUR auf das Signal das ihr ENTGEGENKOMMT. - Forward: Pruefe TYPE_TERM_SIG_A, halte bei signalBIdx. - Backward: Pruefe TYPE_TERM_SIG_B, halte bei signalAIdx. - FALSCH: if (m_Forward) red = (TYPE_TERM_SIG_B != 0); - RICHTIG: if (m_Forward) red = (TYPE_TERM_SIG_A != 0); 41) RNA5 Terminal-Signal Toggle: - Klick auf Signal A toggelt TYPE_TERM_SIG_A. - Klick auf Signal B toggelt TYPE_TERM_SIG_B. - Beide Signale unabhaengig schaltbar. 42) RNA6 Verkehr & Kollisions-Erkennung: - Aktivierung: #define TRAFFIC_DISASTER 1 in GLSceneTraffic.cpp - Pruefung: Beide Loks auf demselben Host (m_FollowFrag). - Doppelgleis-Vertrag: Jede Phantomlok besitzt m_TrackSide (-1/+1); Single-Track ist immer 0. Unterschiedliche Seiten sind zwei physische Gleise und duerfen niemals miteinander kollidieren. - Prellbock: Die Fahrtrichtung wechselt, die physische m_TrackSide nicht; der Render-Offset wird separat gespiegelt. - Beim Doppelgleis-Hostwechsel wird die Weltposition zur Seitenkontinuitaet ausgewertet, damit Weichen keinen Sprung auf das Nachbargleis erzeugen. - UI-Reset: Doppelgleis-Seiten werden pro Lok unabhaengig zufaellig gesetzt; gleiche oder unterschiedliche Seiten sind beide gueltig. - Lua-Event: OnTrain() mit flags=8 bei Kollision. ================================================================================ BEKANNTE SCHWERE BUGS (GEFIXT) - DOKUMENTATION FUER WARTUNG ================================================================================ BUG#1: PHANTOM-SPLINE BEI SWITCH-VERLAENGERUNG (Gefixt 2024) -------------------------------------------------------------------------------- SYMPTOM: - User zieht Spline aus Host heraus -> Switch[A] entsteht - User verlaengert Switch[A] und verbindet mit Host[B] (Midpoint-Snap) - RESULTAT: Zusaetzlicher "Phantom-Spline" entsteht am Switch[A]-Ausgang! - Dieser Stummel ist sichtbar aber logisch nicht verbunden URSACHE: - HandleSnapAndSwitch() ruft CommitTrack() -> erstellt Client[X] - GenTrack() macht SPLIT wenn Client > 8 Dots (absorbClient=false) - Finalize() erstellt Switch[B] UND laesst Client[X] (4-9 Dots) bestehen - Client[X] ist ein orphan Track zwischen Switch[A] und Switch[B] LOESUNG (SplineControl.cpp HandleSnapAndSwitch): 1. Wenn startedFromSwitch=true: TrackMkLst trimmen (Switch-Dots entfernen) 2. Nach GenTrack(): Pruefen ob Client ein orphan Stummel ist 3. Client-Dots an neuen Switch VORNE anhaengen (Merge) 4. Dann Client loeschen und Fragment-IDs korrigieren 5. origSwitch.forwardFragmentId -> newSwitch setzen BETROFFENE DATEIEN: - SplineControl.cpp: HandleSnapAndSwitch(), CommitTrack() - SplineSwitch.cpp: GenTrack(), Finalize() - SplineDotUtil.cpp: HandleDotMove() - SplineTopology.cpp: UpdateSwitchConnections() WARNUNG: - Bei Aenderungen an Switch-Logik IMMER testen: 1. Neuen Switch durch Herausziehen erstellen 2. Switch mit anderem Host verbinden (Midpoint-Snap) 3. Pruefen ob NUR EIN neuer Switch entsteht, KEIN Stummel! ================================================================================ LUA OnTrain EVENT FLAGS ================================================================================ +------+-------------+------------------+------------------+ | Bit | Flag | extra1 | extra2 | +------+-------------+------------------+------------------+ | 0 | Terminal | dotIdx | mode (0=enter | | | | | 1=arrived)| |------+-------------+------------------+------------------+ | 1 | Signal | dotIdx | - | +------+-------------+------------------+------------------+ | 2 | Switch | switchHostId | dotIdx | | | | (otherSplineId) | (Andockpunkt) | +------+-------------+------------------+------------------+ | 3 | Disaster | phantomIdx1 | phantomIdx2 | | | | (erste Lok) | (zweite Lok) | +------+-------------+------------------+------------------+ ================================================================================ VERSION HISTORY ================================================================================ v1.0: Basis-Regelwerk (Regeln 1-28) v1.1: RNA3 Signal-System (Regeln 31-34) v1.2: RNA4 Prellboecke/Taper (Regeln 35-38) v1.3: RNA5 Terminals (Regeln 39-41) v1.4: RNA6 Kollisionen (Regel 42) v1.5: RNA-Tabelle hinzugefuegt, Zuordnungen korrigiert v1.6: BUG#1 Phantom-Spline dokumentiert und gefixt v1.7: Endpoint Smoothing (Regel 32) hinzugefügt ================================================================================ */nicht die Zeit habe, da allzu tief einzusteigen
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....Grüße aus Berlin, und danke für deine Hinweise Mega Antwort leider geworden sorry...
Karsten
-
@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 das 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 -10Das 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 SkyBoxParametrischen 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