Files
openroller/docs/re_gc_track.md
T

198 lines
9.0 KiB
Markdown

# GC Track and Background Reverse Notes
Status: linear track evaluation and the base background color table are
implemented in the Vectorail player. Camera mode bytes, fade modes and stage
objects still need deeper mapping.
## Track wire data
The main stage file stores track keys as big-endian records:
```text
u32 count
repeat count times:
u32 time_ms
f32 x
f32 y
f32 z
```
The stage constructor `FUN_005ed4c0` loads these into 16-byte runtime entries.
The fourth float is not read from disk: the constructor computes the physical
length to the next point and stores it there. It also accumulates the complete
track length at stage-object offset `+0xb0`.
## Confirmed evaluation
`FUN_005e9690` evaluates a track position at an integer timestamp. It finds the
adjacent `time_ms` keys and computes:
```text
u = (time_ms - key[i].time_ms) / (key[i+1].time_ms - key[i].time_ms)
position = key[i] + (key[i+1] - key[i]) * u
```
There is no Catmull-Rom or other spline interpolation in this path.
The Android symbol `GameScene::DrawWay` (`0x001b3b68`) establishes how the
visible route itself is rendered. It clips the authored point list to the
requested first and last timestamps, inserts linearly interpolated points at
both exact boundaries, interpolates an RGBA color over that time interval, and
submits the result as primitive mode 3: a line strip. It does not generate a
camera-facing ribbon, resample by physical distance, or cull triangles.
`FUN_005e9990` is a separate physical-distance sampler used while constructing
duration-note paths. It carries leftover distance between segments so those
generated samples remain uniform over corners; it is not the normal route
renderer.
The player now mirrors `DrawWay`: it uploads the original authored points plus
the two exact timestamp intersections and draws separate past/current and
current/future line strips with endpoint color gradients. Android
`DrawGameStageCharacter` proves that the active `forwardDrawDist` value is a
count of beats, not seconds. If `beat_ms = 60000 / bpm`, it computes:
```text
beat_start = floor(current_ms / beat_ms) * beat_ms
future_ms = forwardDrawDist * beat_ms
first_ms = max(0, beat_start - max(beat_ms, future_ms))
last_ms = min(song_end, beat_start + future_ms)
```
The same active value drives both sides, but only the past side has a minimum
one-beat span. Thus a dynamic value of zero removes the future route while
retaining one past beat. The first `backwardsDrawDist` config float is retained
while parsing but is not consumed by this normal Android rendering path.
Dynamic `TrackDrawDist` records are step changes, not interpolation keys. The
StageConfig forward distance remains active until the timestamp of the first
matching record, then each record replaces it. This matters for Knight Rider:
its only record is `128400 ms: 0`, which hides the route at the end of the song;
using the first record before its timestamp incorrectly hid the entire future
route from the beginning.
For `ac_10pt8tion_hard.dat`:
```text
first track key: 0 ms, (0, 0, 0)
second track key: 6486 ms, (0, 0, 199.985)
first range field: 10
active range: 7 beats
```
The rail brightness is also chart-clock driven. Over a two-beat triangle wave,
`pulse = triangle * 0.6 + 0.4`; the past segment grades from `0.7*pulse` to
`pulse` alpha and the future segment from `pulse` to `0.5*pulse`. Both are
submitted with additive blending.
## Track colors
The stage config stores two RGBA colors directly after the draw-range values.
Android uses the first for the past/current call and the second for the
current/future call. For `10pt8tion_hard` they are `(255,0,128)` and
`(255,255,255)` respectively; the older ahead/behind field names were inferred
backwards.
## Base background
The main `.dat` contains a `ColorTable` section. Each 22-byte record is:
```text
u32 time_ms
rgba top_right
rgba top_left
rgba bottom_right
rgba bottom_left
u8 interpolate_to_next
u8 rhythm_reactive_color
```
`FUN_00642390` holds the active colors unless `interpolate_to_next` is set.
The transition uses `CalcCrossFadeColor`'s 25% overlap rather than a plain
linear mix. `rhythm_reactive_color` multiplies HSV brightness by a 0.5..0.75
triangle wave derived from chart time and the active BPM in `SetCommonParam`;
it is not sourced from an audio analyser. The Linux player implements both.
`data/stage/2d/<song>_menu.dds` is not the gameplay background. It is a
512x256 UI atlas whose top-left 197x197 cell is the song jacket. The player has
a small uncompressed DDS loader, but the jacket is disabled by default and is
only an explicit debug comparison layer (`B`). The inherited procedural
blue/black square grid has also been removed: the base layer is now only the
stage-authored color table before particles, visualizers and objects are drawn.
The remaining original scene is produced by the stage FlowItem,
`visualizer`, and `objects` sections (plus `.tumo` models), not by a single
background bitmap. These sections are now decoded completely by
`StagePattern`: particle records are 44 bytes, visualizer records are 12 bytes,
and every variable-length object record is consumed through its visibility,
movement, scaling, rotation and color-key arrays. Oshama contains 5 particle
keys, 19 visualizer keys, 46 model names and 316 object instances; 10pt8tion
contains 2, 28, 37 and 352 respectively. The Vectorail level data retains this
decoded scene for the model-rendering pass.
Android `GameScene::ExecFlowItem` scales both `repeatMeasure` and
`lifespanMeasure` by the active beat duration. Recovered spawn layouts are:
type 1, a random screen point; type 2, a screen/grid group using
`groupShapeSize`; type 3, eight points at 45-degree intervals around a circle.
The player follows those timing/layout rules, unprojects the points to route
depth and applies the authored velocity, but substitutes colored billboards
for original texture selectors 34/35.
The common `.tumo` container is also big-endian. Its outer count is followed,
for each mesh, by resource names, an XYZ vertex table, eight bound floats,
render parts, and polygon lists. A polygon corner is `u32 vertexIndex, f32 u,
f32 v`; a separate block contains explicit line-index pairs. The player now
triangulates polygon fans, retains line primitives, and renders stage objects
with the original five animation channels: visibility, movement, scaling,
linearly interpolated Euler rotation, and RGBA color. The final rotation uses
the recovered `(-Y, X, Z)` quaternion-builder convention. Base and animated transform
components are composed as separate matrices, matching `FUN_00643b20`;
visibility fades use the original fixed 250 ms window. All 46 unique Oshama
models and all 37 unique 10pt8tion models decode. `O` toggles this object layer
for comparison.
For stage format versions newer than `0x29ce`, the supplemental table beginning
at the header's `ColorTable2` offset has a relative pointer at `base + 8`. Its
stream is `u32 objectCount` followed by one signed big-endian `int16`
`parentIndex` per object (`-1` means no parent). `FUN_006445b0` evaluates one
parent level and renders `parentTransform * childTransform`; parent and child
RGBA are multiplied component-wise. Oshama uses this on 80 of 316 objects and
10pt8tion on 87 of 352, so omitting it visibly loses grouped scale, placement,
and opacity.
The object's `wireframe` byte also selects mutually exclusive model paths:
`FUN_005dd8e0` draws polygon parts, while `FUN_005dd7f0` draws the explicit TUMO
edge buffer. The player previously drew both. Polygon/edge selection now
matches the flag. `FUN_00648c30` enables `D3DRS_ZENABLE` and
`D3DRS_ZWRITEENABLE` for the complete authored object loop, then disables both
before the rail and markers are submitted. Objects therefore depth-test one
another in file order, but never hide gameplay geometry drawn afterwards.
### Per-frame object clipping
The stage loader also opens `data/stage/<chart>_clip.dat`. `FUN_005ed4c0`
decodes it as:
```text
u32be object_count
u32be frame_count
u8 visible[object_count][frame_count]
```
The payload is object-major. `FUN_006445b0` selects frame
`round(time_ms / (1000 / 60))` and skips the object when the byte is zero;
when the table is absent or the requested frame is past its end, it falls
back to the normal authored-visibility path. Knight Rider has 178 objects and
7909 clip frames. Ignoring this table submits many objects which the original
never draws in that camera frame and turns its background into overlapping
full-screen geometry. The Linux player now applies the same test before object
animation and submission.
`FUN_005dd8e0` itself supports two TUMO part types. Type `0` is a triangle
list; type `1` is a line list. Type-1 source polygons contain exactly two
corners. Oshama's models contain ten such parts, including one with 1498 line
vertices; treating them as polygon fans silently discarded all of them. The
player now keeps solid line parts separate from both triangle parts and the
wireframe edge buffer. All Oshama and 10pt8tion models end with a zero node
count, so an internal model hierarchy is not responsible for their transforms.