В документе приводится описание алгоритмов цветопередачи (Палитра => RGB, RGB => Индекс в палитру, Анимированные цвета) используемых в Fallout.
Информация о них была получена в ходе разбора соответствующей функции в файле Mapper2.exe и Fallout2.exe.
Автор документа: Anchorite (anchorite2001@yandex.ru).
Преобразование 'Палитра => RGB' представлено на следующей схеме.
+--------------------------------+ | Яркость (currentGamma) (****) | +--------------------------------+ | +--------------+ V | Palette | +----------------------------+ +-------------+ +-------------+ | from | | Таблица перобразования | | System | | DirectDraw | | PAL-file (*) | | цвета (**) | | palette | | palette | +-----+--------+ +----+-----------------------+ +-----+-------+ +-----+-------+ | | Red |-------------------->| 0 | pow(0, currentGamma) | | | Red | | | Red | | +--------+ +----+-----------------------+ | +-------+ | +-------+ | 0 | Green | | 1 | pow(1, currentGamma) |-------->| 0 | Green | | 0 | Green | | +--------+ +----+-----------------------+ | +-------+ | +-------+ | | Blue | | ... | | | Blue | | | Blue | +-----+--------+ +----+-----------------------+ +-----+-------+ Red << 2 +-----+-------+ | | Red | +---->| 63 | pow(63, currentGamma)| | | Red |-- Green << 2 -->| | Red | | +--------+ | +----+-----------------------+ | +-------+ Blue << 2 | +-------+ | 1 | Green | | | 1 | Green | | 1 | Green | | +--------+ | | +-------+ | +-------+ | | Blue | | | | Blue | | | Blue | +-----+--------+ | +-----+-------+ +-----+-------+ | ... | | | ... | | ... | +-----+--------+ +-----+-------+ +-----+-------+ | | Red | Red >> 2 | | Red | | | Red | | +--------+ Green >> 2 | +-------+ | +-------+ | 255 | Green | Blue >> 2 | 255 | Green | | 255 | Green | | +--------+ | +-------+ | +-------+ | | Blue | | | | Blue | | | Blue | +-----+--------+ | +-----+-------+ +-----+-------+ | | | +---------------------------+ | | Анимированные цвета (***) |--+ +---------------------------+ * - Если при загрузке из PAL-файла значение одного из компонента цвета не находится в диапазоне 0..63, то значения всех компонентов этого цвета сбрасывается в 0. ** - Если вычисленное значение функции получается меньшим 0, значение соответствующего элемента таблицы сбрасывается в 0. Если вычисленное значение функции получается большим 63, значение соответствующего элемента таблицы устанавливается в 63. (pow - функция возведения в степень) *** - См. 3. Анимированные цвета **** - В Fallout переменная currentGamma имеет тип double и изменяется в пределах от 1.000000 до 1.179993.
Следует заметить, что используемый алгоритм изменения яркости имеет дефект - нулевой и первый элемент таблицы преобразования цвета никогда не изменяются.
Преобразование 'RGB => Индекс в палитре' представлено на следующей схеме.
Red Green Blue +---------|-----+ +---------|-----+ +---------|-----+ |7|6|5|4|3|2|1|0| |7|6|5|4|3|2|1|0| |7|6|5|4|3|2|1|0| +---------|-----+ +---------|-----+ +---------|-----+ | | | | | | +-----+ | +-----+ | | | V V V +-|--------------|--------------|--------------+ |0|R7|R6|R5|R4|R3|G7|G6|G5|G4|G3|B7|B6|B5|B4|B3| +-|--------------|--------------|--------------+
Полученное в результате число используется как индекс в считанную из PAL-файла таблицу преобразования 'RGB => Индекс в палитре'. Значение таблицы по соответствующему индексу и является искомым индексом в палитру.
В Fallout'Анимированные цвета' были использованы для уменьшения размеров графических файлов, т.к. позволяют обойтись одним кадром с изменяющимеся цветами, вместо нескольких кадров со статичными цветами.
Пользователь может контролировать 'Анимированные цвета' с помощью двух параметров в файлах fallout2.cfg и mapper2.cfg
[system] color_cycling=1 cycle_speed_factor=1
С помощью параметра color_cycling разрешается (1) / запрещается (0) анимация цветов.
Параметр cycle_speed_factor регулирует скорость анимации - чем больше значение параметра тем реже изменяются цвета.
Всего существует шесть групп анимированных цветов с различными начальными параметрами (см. Таблица 3.1).
Название | Число элементов | Индексы в палитре | Значения компонент цвета | Период изменения | ||
---|---|---|---|---|---|---|
Слизь | 4 | 229..232 | 0 | Red | 0 | 200 мс |
Green | 108 | |||||
Blue | 0 | |||||
1 | Red | 11 | ||||
Green | 115 | |||||
Blue | 7 | |||||
2 | Red | 27 | ||||
Green | 123 | |||||
Blue | 15 | |||||
3 | Red | 43 | ||||
Green | 131 | |||||
Blue | 27 | |||||
Побережье | 6 | 248..253 | 0 | Red | 83 | 200 мс |
Green | 63 | |||||
Blue | 43 | |||||
1 | Red | 75 | ||||
Green | 59 | |||||
Blue | 43 | |||||
2 | Red | 67 | ||||
Green | 55 | |||||
Blue | 39 | |||||
3 | Red | 63 | ||||
Green | 51 | |||||
Blue | 39 | |||||
4 | Red | 55 | ||||
Green | 47 | |||||
Blue | 35 | |||||
5 | Red | 51 | ||||
Green | 43 | |||||
Blue | 35 | |||||
Медленногорящее плямя | 5 | 238..242 | 0 | Red | 255 | 200 мс |
Green | 0 | |||||
Blue | 0 | |||||
1 | Red | 215 | ||||
Green | 0 | |||||
Blue | 0 | |||||
2 | Red | 147 | ||||
Green | 43 | |||||
Blue | 11 | |||||
3 | Red | 255 | ||||
Green | 119 | |||||
Blue | 0 | |||||
4 | Red | 255 | ||||
Green | 59 | |||||
Blue | 0 | |||||
Быстрогорящее плямя | 5 | 243..247 | 0 | Red | 71 | 142 мс |
Green | 0 | |||||
Blue | 0 | |||||
1 | Red | 123 | ||||
Green | 0 | |||||
Blue | 0 | |||||
2 | Red | 179 | ||||
Green | 0 | |||||
Blue | 0 | |||||
3 | Red | 123 | ||||
Green | 0 | |||||
Blue | 0 | |||||
4 | Red | 71 | ||||
Green | 0 | |||||
Blue | 0 | |||||
Мониторы | 5 | 233..237 | 0 | Red | 107 | 100 мс |
Green | 107 | |||||
Blue | 111 | |||||
1 | Red | 99 | ||||
Green | 103 | |||||
Blue | 127 | |||||
2 | Red | 87 | ||||
Green | 107 | |||||
Blue | 143 | |||||
3 | Red | 0 | ||||
Green | 147 | |||||
Blue | 163 | |||||
4 | Red | 107 | ||||
Green | 187 | |||||
Blue | 255 | |||||
Сигнализация | 1 | 254 | 0 | Red | 252 | 33 мс |
Green | 0 | |||||
Blue | 0 | |||||
Примечание: Периоды изменения приведены для cycle_speed_factor = 1 |
Законы изменения цвета приведены ниже в виде функции.
// Палитра BYTE g_Palette[768]; // Начальное значение цвета BYTE g_nSlime[] = { 0, 108, 0, 11, 115, 7, 27, 123, 15, 43, 131, 27 }; // Слизь BYTE g_nMonitors[] = { 107, 107, 111, 99, 103, 127, 87, 107, 143, 0, 147, 163, 107, 187, 255 }; // Мониторы BYTE g_nFireSlow[] = { 255, 0, 0, 215, 0, 0 , 147, 43, 11, 255, 119, 0, 255, 59, 0 }; // Медленногорящий огонь BYTE g_nFireFast[] = { 71, 0, 0, 123, 0, 0, 179, 0, 0, 123, 0, 0, 71, 0, 0 }; // Быстрогорящий огонь BYTE g_nShoreline[] = { 83, 63, 43, 75, 59, 43, 67, 55, 39, 63, 51, 39, 55, 47, 35, 51, 43, 35 }; // Побережье BYTE g_nBlinkingRed = 252; // Сигнализация // Текущее значение параметра цикла DWORD g_dwSlimeCurrent = 0; DWORD g_dwMonitorsCurrent = 0; DWORD g_dwFireSlowCurrent = 0; DWORD g_dwFireFastCurrent = 0; DWORD g_dwShorelineCurrent = 0; BYTE g_nBlinkingRedCurrent = 0; // Время предыдущего изменения цвета DWORD g_dwLastCycleSlow = 0; DWORD g_dwLastCycleMedium = 0; DWORD g_dwLastCycleFast = 0; DWORD g_dwLastCycleVeryFast = 0; // Текущее значение коэффициета скорости анимации DWORD g_dwCycleSpeedFactor = 1; void AnimatePalette() { BOOL bPaletteChanged = FALSE; DWORD dwCurrentTime = GetTickCount(); if (dwCurrentTime - g_dwLastCycleSlow >= 200 * g_dwCycleSpeedFactor) { // Slime DWORD dwSlimeCurrentWork = g_dwSlimeCurrent; for(int i = 3; i >= 0; i--) { g_Palette[687 + i * 3] = g_nSlime[dwSlimeCurrentWork * 3] >> 2; // Red g_Palette[687 + i * 3 + 1] = g_nSlime[dwSlimeCurrentWork * 3 + 1] >> 2; // Green g_Palette[687 + i * 3 + 2] = g_nSlime[dwSlimeCurrentWork * 3 + 2] >> 2; // Blue if (dwSlimeCurrentWork == 3) dwSlimeCurrentWork = 0; else dwSlimeCurrentWork++; } if (g_dwSlimeCurrent == 3) g_dwSlimeCurrent = 0; else g_dwSlimeCurrent++; // Shoreline DWORD dwShorelineCurrentWork = g_dwShorelineCurrent; for(int i = 5; i >= 0; i--) { g_Palette[744 + i * 3] = g_nShoreline[dwShorelineCurrentWork * 3] >> 2; // Red g_Palette[744 + i * 3 + 1] = g_nShoreline[dwShorelineCurrentWork * 3 + 1] >> 2; // Green g_Palette[744 + i * 3 + 2] = g_nShoreline[dwShorelineCurrentWork * 3 + 2] >> 2; // Blue if (dwShorelineCurrentWork == 5) dwShorelineCurrentWork = 0; else dwShorelineCurrentWork++; } if (g_dwShorelineCurrent == 5) g_dwShorelineCurrent = 0; else g_dwShorelineCurrent++; // Fire_slow DWORD dwFireSlowCurrentWork = g_dwFireSlowCurrent; for(int i = 4; i >= 0; i--) { g_Palette[714 + i * 3] = g_nFireSlow[dwFireSlowCurrentWork * 3] >> 2; // Red g_Palette[714 + i * 3 + 1] = g_nFireSlow[dwFireSlowCurrentWork * 3 + 1] >> 2; // Green g_Palette[714 + i * 3 + 2] = g_nFireSlow[dwFireSlowCurrentWork * 3 + 2] >> 2; // Blue if (dwFireSlowCurrentWork == 4) dwFireSlowCurrentWork = 0; else dwFireSlowCurrentWork++; } if (g_dwFireSlowCurrent == 4) g_dwFireSlowCurrent = 0; else g_dwFireSlowCurrent++; g_dwLastCycleSlow = dwCurrentTime; bPaletteChanged = TRUE; } dwCurrentTime = GetTickCount(); if (dwCurrentTime - g_dwLastCycleMedium >= 142 * g_dwCycleSpeedFactor) { // Fire_fast DWORD dwFireFastCurrentWork = g_dwFireFastCurrent; for(int i = 4; i >= 0; i--) { g_Palette[729 + i * 3] = g_nFireFast[dwFireFastCurrentWork * 3] >> 2; // Red g_Palette[729 + i * 3 + 1] = g_nFireFast[dwFireFastCurrentWork * 3 + 1] >> 2; // Green g_Palette[729 + i * 3 + 2] = g_nFireFast[dwFireFastCurrentWork * 3 + 2] >> 2; // Blue if (dwFireFastCurrentWork == 4) dwFireFastCurrentWork = 0; else dwFireFastCurrentWork++; } if (g_dwFireFastCurrent == 4) g_dwFireFastCurrent = 0; else g_dwFireFastCurrent++; g_dwLastCycleMedium = dwCurrentTime; bPaletteChanged = TRUE; } dwCurrentTime = GetTickCount(); if (dwCurrentTime - g_dwLastCycleFast >= 100 * g_dwCycleSpeedFactor) { // Monitors DWORD dwMonitorsCurrentWork = g_dwMonitorsCurrent; for(int i = 4; i >= 0; i--) { g_Palette[699 + i * 3] = g_nMonitors[dwMonitorsCurrentWork * 3] >> 2; // Red g_Palette[699 + i * 3 + 1] = g_nMonitors[dwMonitorsCurrentWork * 3 + 1] >> 2; // Green g_Palette[699 + i * 3 + 2] = g_nMonitors[dwMonitorsCurrentWork * 3 + 2] >> 2; // Blue if (dwMonitorsCurrentWork == 4) dwMonitorsCurrentWork = 0; else dwMonitorsCurrentWork++; } if (g_dwMonitorsCurrent == 4) g_dwMonitorsCurrent = 0; else g_dwMonitorsCurrent++; g_dwLastCycleFast = dwCurrentTime; bPaletteChanged = TRUE; } dwCurrentTime = GetTickCount(); if (dwCurrentTime - g_dwLastCycleVeryFast >= 33 * g_dwCycleSpeedFactor) { // Blinking red if ((g_nBlinkingRedCurrent == 0) ||(g_nBlinkingRedCurrent == 60)) g_nBlinkingRed = BYTE(-g_nBlinkingRed); g_Palette[762] = g_nBlinkingRed + g_nBlinkingRedCurrent; // Red g_Palette[763] = 0; // Green g_Palette[764] = 0; // Blue g_nBlinkingRedCurrent = g_nBlinkingRed + g_nBlinkingRedCurrent; g_dwLastCycleVeryFast = dwCurrentTime; bPaletteChanged = TRUE; } if (bPaletteChanged) UpdatePalette(); }