Repository navigation
Track and camera
Трасса рисуется построчно: сетка тайлов 16 x 1024 проецируется в 17 полос, каждая - трапеция, у которой известны четыре угла и высота. Ни матриц, ни Z-буфера, ни текстурных координат с плавающей точкой в кадре нет.
Одна функция, всё остальное - производное от неё (jb_track.c):
static float Proj(float t)
{
return (t * JB_PROJ_NUM) / (t * JB_PROJ_DEN_A + JB_PROJ_DEN_B) * JB_PROJ_SCALE;
}JB_PROJ_NUM 70.0, JB_PROJ_DEN_A 60.0, JB_PROJ_DEN_B 100.0,
JB_PROJ_SCALE 163.0 - четыре float из .data (0x0002643c, 0x00026440,
0x00026444, 0x00026438). Гипербола: близкие строки высокие, дальние
сжимаются к горизонту, Proj(16) = 172 - это JB_PROJ_SPAN, полная глубина
кадра в пикселях.
Track_BuildProjTables() считает её один раз на 29 значений
(g_rowProjOfs 0x00061438) и рядом - разности соседних, то есть высоты строк
(g_rowHeight 0x000614a8, элемент 1 задан константой 0x96).
Track_DrawFrame идёт от дальней строки к ближней: i от 0 до 17,
row = 17 - i. Так близкие полосы перекрывают дальние без сортировки.
y_near = 320.0f - Proj(row - cam);
y_far = 320.0f - Proj(row + 1 - cam);
if (i == 0)
y_far = 320.0f - Proj(row + 1); /* Track_DrawFrame 0x0001e1c4 */cam = cam_pixel_ofs / 10 - дробная часть позиции камеры в строках; из-за неё
трасса едет плавно, а не прыгает по 10 пикселей. Первая итерация считает
дальний край без поправки на камеру - это не описка порта, а поведение
оригинала на 0x0001e1c4.
Каждая строка получает четыре X: дальние левый/правый и ближние левый/правый. Считаются они интерполяцией между «полным» масштабом и «дальним»:
FullX(s) = s * tile_size + view_center_x - map_cols * tile_half; /* 0x0001e2a0 */
FarX(s) = s * 4 + view_center_x - map_cols * 2; /* 0x0001e2c0 */
EdgeX(s) = round((FarX(s) - FullX(s)) / 172 * proj + FullX(s)); /* 0x0001e2f0 */tile_size - 140 (в раскладке 176x220 - 134), JB_FAR_TILE_SIZE - 4.
Округление - не floor и не lround, а точная копия арифметики оригинала:
static int RoundHalfUp(float x)
{
return (int)((double)x - 0.5) + 1;
}В s подставляется row_shift[row] + ball_prev_x. Первое слагаемое - изгиб
трассы: 50 float, скопированных из g_rowShiftSrc со смещением g_camRow
(Level_Generate 0x00013490), то есть уровень «извивается» по заранее
заготовленной кривой. Второе - горизонтальная камера: смещается не мяч, а
трасса под ним, поэтому используется ball_prev_x, положение мяча на
предыдущем кадре.
Полосе высотой h пикселей нужно h шагов по 100-пиксельной текстуре.
Gfx_BuildTexVStep() (оригинал Game_Init 0x00011560) заполняет 299 строк по
300 элементов: шаг 100.0 / h накапливается и усекается в short, минимум 1.
В кадре остаётся только выборка из таблицы.
Камера - это сам мяч, без сглаживания и без зоны нечувствительности
(jb_player.c):
st->cam_row = (int)st->ball_row;
st->cam_pixel_ofs = (st->ball_row - (float)st->cam_row) * (float)JB_ROW_PIXELS;JB_ROW_PIXELS = 10. Целая часть выбирает, с какой строки сетки читать тайлы,
дробная уезжает в Proj как cam. Финиш уровня - cam_row > 0x3fe
(WndProc 0x00020920), при этом сетка размечена только до 0x3fc: последние
строки - обнулённый хвост массива, поэтому уровень заканчивается «пустотой», а
не обрывом.
Track_DrawFrame вызывает колбэк на каждую строку, а jb_main.c выбирает
обработчик по jb_stg.theme (оригинал - таблица переходов в
Track_DrawFrame 0x0001e858):
| Тема | Функция порта | Оригинал | Спрайты |
|---|---|---|---|
| ICE 0 | TrackRow_DrawIce |
0x00016e98 |
лёд, вода (H и V) |
| DESERT 1 | TrackRow_DrawDesert |
0x00015b80 |
песок (H и V), сетка |
| FOREST 2 | TrackRow_DrawForest |
0x00017ad4 |
лес, дерево, дощатый настил |
| SKY 3 | TrackRow_DrawSky |
0x000167bc |
плитка неба, свечение |
| GRASS 4 | TrackRow_DrawGrass |
0x00019198 |
трава, настил, знак |
| - | TrackRow_DrawIceAlt |
0x00019e28 |
вариант льда |
Темы 2 и 3 недостижимы. Level_Begin 0x0001376c присваивает
theme = menu_index, затем переписывает 2 -> 4 и 3 -> 1, так что лес и небо
остаются в коде и в ассетах, но не выпадают ни при каком выборе уровня. Код
для них портирован полностью - см. Deviations.
Общие для всех тем детали, вынесенные в jb_trackrow_ctx:
-
JB_KEYED_ROW_FIRST10 - начиная с этого номера строки блит переключается на вариант с ключом прозрачности; у травы порог свой,0x64= 100 (TrackRow_DrawGrass 0x00019370cmp r0,#0x64); -
JB_NEAR_ROW_EXTRA300 - добавка к высоте самой ближней строки; -
JB_TEX_ANIM_PERIOD250 /JB_TEX_ANIM_HALF125 - остатокg_animMsпо этим числам переключает кадр воды; -
JB_GLOW_TINT250 иJB_GLOW_SPREAD3.0 - свечение в теме неба (TrackRow_DrawSky 0x00016c9cmov r5,#0xfa).
Фон за трассой едет с коэффициентом JB_PARALLAX 0.08
(Game_DrawFrame 0x0001f37c) и выбирается по теме: 321 лёд, 316 пустыня,
322 лес, 324 небо - см. Levels.
Устройство порта
Сборка и происхождение