198 lines
9.0 KiB
Markdown
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.
|