K
@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