Compiled track view - #6
Conversation
|
you need to rebase and add files to CMakeLists.txt |
|
This is great, but I have a number of things...
|
| track->drawTrack(display, TRUE); | ||
| track->drawCCLines(display); | ||
| } else if (track->showComputed) { | ||
| // bit-exact compiled-track + cc-line view (replaces the normal draws) |
There was a problem hiding this comment.
why replace... and not just overlay
There was a problem hiding this comment.
yes I think since you can show and hide the track too, overlaying makes much more sense
| // heading orientation. (If the shape comes out mirrored/wrong-scale, adjust | ||
| // S / the axis pairing here.) | ||
| // --------------------------------------------------------------------------- | ||
| void GPTrack::drawComputed(Display *g) |
There was a problem hiding this comment.
I think we should have 2 options, draw computed track and draw computed CC line, both independent of any other drawing.. I want to understand the algorithim of the CC line, this is brute force reverse engineering of GP2.exe (I never actually did that...) as such my editor was always relatively clean of their original code. Don't get me wrong this is great.. and really solves what is a 20 year+ old problem that I was never able to solve.
I can see some substiles that caused me to be wrong.
There was a problem hiding this comment.
I understand. And I think I jumped the gun a little bit. Since it is basically just math I just used the "ported" code. But there was a better approach and I will update it.
It was a mix of trying to make sense of data dumped from memory and then looking into RE for help. But in the end all the info ended up coming from RE anyway.
I think this not being solved before is a mix of the integer math (floating-point carries a lot of error when you compound a whole track) and the warping effect which is very hard to figure out by just looking at the data, especially if you are comparing against floating-point math.
Hi!
|
| return 0; | ||
| } | ||
|
|
||
| static inline int s16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } | ||
|
|
||
| /* GetSinusVal (0x104B9): cos, interpolated. */ | ||
| int gp2cc_gv(int ax) { | ||
| ax = s16(ax); | ||
| if (ax < 0) ax = (-ax) & 0xFFFF; | ||
| int frac = ax & 7; | ||
| int widx = ((ax >> 2) & 0xFFFE) >> 1; | ||
| int base = COS[widx], nxt = COS[widx + 1]; | ||
| int d = s16(nxt - base); | ||
| return s16(base + ((d * frac) >> 3)); | ||
| } | ||
| /* raw (non-interpolated) cosine for the position step (InitTrackSegs 0x79A9F). */ | ||
| static int scos(int a) { | ||
| a = s16(a); | ||
| if (a < 0) a = -a; | ||
| return COS[((a >> 2) & 0xFFFE) >> 1]; | ||
| } |
There was a problem hiding this comment.
I believe all of this can be replaced with
/* Convert to signed 16-bit: keep low 16 bits, then sign-extend into int.
* Examples: 0x7FFF->32767, 0x8000->-32768, 0xFFFF->-1. */
static inline int s16(int v) {
const int low16 = v & 0xFFFF;
return (low16 >= 0x8000) ? (low16 - 0x10000) : low16;
}
/* raw (non-interpolated) cosine for the position step (InitTrackSegs 0x79A9F). */
int scos(int a) {
/* GP2 angles use 65536 units per full turn; output is cosine scaled to 0x4000. */
a = s16(a);
double angle = a * (2.0 * M_PI / 65536.0);
return (int)round(cos(angle) * 16384.0);
}Then there is no need for the tables at all (I don't like pulling hex code from the GP2.exe binary)
There was a problem hiding this comment.
Hmm... maybe not..
There was a problem hiding this comment.
I'll have to run it in the editor tonight to check how much drift it would create, if any. Small deviations compounded produce catastrophic results in tracks like Hockenheim. My best guess is this for scos:
/* the game samples its cosine table every 8 units with NO interpolation, so the
* angle must be floored to the table grid via (a>>3) before the cosine — taking
* cos() of the exact angle over-resolves and diverges on ~7/8 of angles. */
int scos(int a) {
a = s16(a);
if (a < 0) a = -a; /* cosine is even */
const int idx = a >> 3; /* floor to the 8-unit table grid (NOT exact angle) */
return (int)lround(16384.0 * cos(idx * (M_PI / 4096.0)));
}
ATAN1 can be easily replaced with round(atan(i/2048) · 65536/2π) (also checking this tonight)
Add a toggleable editor view (View menu + toolbar "C" button) that draws GP2's exact compiled track geometry and cc-line (racing line), with no pitlane/kerbs/objects. When active it recompiles from the live track on each repaint, so it updates as cc-line sectors are edited, and mirrors the other cc-line views: a small perpendicular tick where each cc-line sector starts, plus a highlight on the selected sector. Drawing goes through a new standalone C module (gp2cc.c/.h/gp2cc_tables.c): a bit-exact port of GP2's geometry compiler + cc-line (UACalcBestLine) and its helpers, validated to the byte against GP2Lap dumps on F1CT01/02a/03/ 09/11/16: - geometry: heading, startAngle, road widths, side vectors - exact integer position warp (Bresenham gap distribution, sub_76D39) - cc-line bestLine / angle18 t_Sinus and t_ArithTab1 are embedded in gp2cc_tables.c so no GP2.EXE is needed at runtime.
Make the existing "Track Section Numbers" and "Show Finish Line" toggles work in the Show Compiled Track view: - showFinishLine: thick line across the road at segment 0 + checkered flag. - showTrackNumbers: a perpendicular divider across the road at each track section boundary + the section index drawn mid-section. Section->segment mapping uses cumulative TrackSection::getLength() (= the geometry-command word gp2cc consumes), so the markers land bit-exactly on the compiled road. GPTrack.cpp only; no gp2cc change.
…ed view Rewrites GP2's geometry compilation as the geometric process it actually is — a fixed-point "turtle" with named steps (turn, advance one fixed chord, midpoint half-step, ramp width, compute side vectors, close the loop) — instead of a transcription of the disassembly. Every fixed-point op is a named helper whose comment derives the constant (0x6488 = round(pi/2*2^14); the half-step = midpoint integration; the warp = DDA error diffusion; the chord = a fixed 128-unit step). Same exact integers: validated BYTE-IDENTICAL to the original gp2cc compiler on F1CT01/02a/03/09/11/16 (every field) and the cc-line on it is bit-exact. The cosine it uses comes from gp2cos.hpp: the table expressed as its formula (round(16384*cos(i*pi/4096)) + the one documented COS[1992]=703 quirk), generated at compile time and static_assert'd against GP2's extracted bytes. If a toolchain struggles with the constexpr generation, define GP2GEOM_EMBEDDED_COS to use the existing table (bit-identical); MSVC also gets a raised /constexpr:steps budget. New files only — the original gp2cc.c (incl. the chaotic cc-line kernel, kept as-is) is untouched. drawComputed now calls gp2geom::compileGeometry; the cc-line still runs through the existing kernel. Note: ATAN1's formula header (gp2atan.hpp) is proven but not wired in yet — it belongs with a later cc-line-kernel cleanup. CMakeLists isn't on this branch (predates the upstream CMake merge); when this lands on dev, add gp2cc.c, gp2cc_tables.c and gp2geom.cpp to its SOURCES.
Ports GP2's racing-line kernel (UACalcBestLine + helpers: reproject, build_centre, nudge, q30 trig, the fixed-iteration integer sqrts, atan2) to C++, fed by the formula-generated tables (gp2cos.hpp cosine + gp2atan.hpp arctan). It is a faithful transcription of the chaotic fixed-point math, not a re-derivation — validated BIT-IDENTICAL to the original gp2cc_compute_ccline (bestLine, angle18, cmdStartSeg, numCmds) on F1CT01/02a/03/09/11/16. drawComputed now runs entirely on the C++/formula path: gp2geom::compileGeometry for the geometry and gp2geom::computeCcLine for the racing line. gp2cc.c/.h/_tables.c stay compiled (fallback, the shared structs, and gp2cc_gv which the view's drawing still uses) until the new files are tested in the editor. gp2atan.hpp now wired in (was sandbox-only). vcxproj builds gp2ccline.cpp (stdcpp17, NotUsing PCH, raised /constexpr:steps). View drawing unchanged.
Scrap the standalone compiled-track view (which replaced the normal view). The two compiled features are now independent, toggleable overlays drawn on top of the normal editor view: - Compiled Track overlay (showComputed, reuses ID_VIEW_COMPILED): bit-exact road edges + per-section dividers, BLUE. - Compiled CC-Line overlay (new showComputedCCLine / ID_VIEW_COMPILED_CCLINE): bit-exact racing line + per-sector start ticks + selected-sector highlight, ORANGE / bright YELLOW. Both compile once per repaint (buildCompiledOverlay) and share the result. Fix the selected-sector off-by-one: CCLineSection[c] starts at cmdSeg[c+1] (cmdSeg[0] is the seed/start command = the editor's CCLineStart header, not a user sector), so selecting sector N now highlights N. drawComputed() is split into buildCompiledOverlay + drawCompiledTrack + drawCompiledCcLine; OnDraw's standalone branch is removed and the overlays are appended to the normal-view branch. Menu: "Show Compiled Track" -> "Compiled Track Overlay" + new "Compiled CC-Line Overlay". Toolbar: ID_VIEW_COMPILED button re-tinted blue (track-tool icon); new ID_VIEW_COMPILED_CCLINE button (ccline-tool icon, orange via repurposed palette idx 5). gp2cc.c untouched. View-only.
Replace the compiled-track and compiled-CC-line toolbar icons with recolored copies of the existing Show Track / Show CC Line glyphs (track in blue, CC line in orange) and add status-bar/tooltip strings for both buttons.
Add gp2cc.c, gp2cc_tables.c, gp2ccline.cpp and gp2geom.cpp to the CMake SOURCES list so the CMake build matches the .vcxproj.
An earlier edit had re-saved the file as UTF-8 and mangled the three non-ASCII characters (the two (c) copyright signs and the e-umlaut in 'Michael') into U+FFFD. Restore the original ISO-8859-1 bytes so those lines match upstream byte-for-byte.
The C++ modules gp2geom (geometry) and gp2ccline (cc-line) are the live compilers now; the old C kernel was dead except for one cosine helper (gp2cc_gv) used to draw the overlay. Move that into gp2geom::gv (the interpolated GetSinusVal, bit-identical over all 65536 angles, verified against the old table) and delete both C files. gp2cc.h is trimmed to just the shared output structs (gp2cc_track / gp2cc_ccgeo). gp2geom now always uses the build-time formula cosine table (gp2cos.hpp); the GP2GEOM_EMBEDDED_COS fallback is dropped since its table source is gone. Removed from the .vcxproj and CMakeLists SOURCES.
Replace the GP2 sub_/address/field references in the cc-line solver's comments with plain descriptions of what each step does, rename the register-named 'ebp' local to 'step', and name the 0xCBB0C constant kInvPiQ16 (round(2^16/pi)) with a derivation. Pure comment/identifier changes — the fixed-point math is unchanged (still bit-exact).
The cosine/atan tables are generated from their formulas at compile time; gp2cos_reference.hpp and gp2atan_reference.hpp existed only to hold GP2's extracted bytes for a static_assert that proved the generated tables matched. Remove both headers and the matchesReference/atanMatchesReference guards (and their .vcxproj entries) so no extracted GP2 data ships in the editor. In their place, each table header now warns that the entries come from compile-time float rounding: a different compiler/std-lib could round a .5-boundary entry the other way, and a drifting compiled track or cc-line is the symptom to watch for. The single load-bearing boundary (cos index 1992) is still pinned by a one-line spot-check static_assert. Verified the generated tables still match GP2's bytes bit-for-bit on the current toolchain.
6e7350d to
b17df67
Compare
|
Hey, @mydeveloperday. I've updated the PR with a lot of changes. Both the "compiled" track and cc-line geometry now have their own overlays over the normal view (their extra view was scrapped). The atan and scos lookup tables were replaced with formulas, but I had to add a single magic number to make the scos formula correct. That single number (the compiler rounding something like 703.504 to 704 instead of 703) reduced the accuracy of Hockenheim cc-line geometry from 100% to 29% if missing. I tried to port the code to C++ and make it semantically make sense but I have to be honest, trying to simplify or make it accurate without all the bitwise operations led me nowhere. So let me know if the current code is acceptable. I undertand it if it is not. |
buildCompiledOverlay drew widthL/widthR with the perpendicular sign opposite to the normal view's getLeftSide (angle-90) / getRightSide (angle+90), so a Track Width Change Right (0xB5) command moved the on-screen LEFT edge and vice-versa. Flip the edge signs so widthL uses -pv (left) and widthR uses +pv (right), matching the normal view and the game.
e66f103 to
f5816f9
Compare
Adds a new Show Compiled Track view (View menu + toolbar "C" button) that draws GP2's exact compiled track geometry and racing line (attempting to be bit-exact), updated live as cc-line sectors are edited. No pitlane/kerbs/objects.
EDIT: I don't advise using this view until your track is a closed loop/circuit. It distorts the track geometry the same way GP2 does when the loop is not closed, making it hard for you to work with the track data.
Drawing engine
Tries the same math GP2 when compiling the track to the 3D world
New standalone C module
gp2cc.c/gp2cc.h/gp2cc_tables.c— a bit-exact port of GP2's geometry compiler + cc-line (UACalcBestLine) and helpers, validated to the byte against GP2Lap dumps of the geometry:sub_76D39)t_Sinus+t_ArithTab1are embedded ingp2cc_tables.c, so no GP2.EXE is needed at runtime.All the tracks I've tested so far work correctly. Example:

Original view:
New view:
