Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PSX code texture load unpack show and fade.md
2025-04-18 23:10:56 +05:00

5.9 KiB
Raw Blame History

Вот готовая программа для PlayStation 1, которая загружает текстуру, плавно проявляет её, ждёт нажатия кнопки ( CROSS или CIRCLE), а затем плавно исчезает. Всё работает с ручным управлением адресами RAM и без использования полупрозрачности.


Код (MAIN.C)

#include <sys/types.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <libcd.h>

// Константы
#define SCREEN_WIDTH    320
#define SCREEN_HEIGHT   240
#define TEX_WIDTH       256
#define TEX_HEIGHT      256
#define RAW_SECTOR_SIZE 2048
#define TEX_SECTORS     ((TEX_WIDTH * TEX_HEIGHT * 2) / RAW_SECTOR_SIZE)

// Адреса в RAM
#define RAW_DATA_ADDR    0x80010000  // Сырые данные с CD
#define UNPACKED_ADDR    0x80030000  // Распакованная текстура

// Состояния анимации
typedef enum {
    FADE_IN,          // Появление текстуры
    WAIT_FOR_BUTTON,  // Ожидание нажатия кнопки
    FADE_OUT          // Исчезновение
} State;

// Структура спрайта
typedef struct {
    RECT img_rect;
    uint16_t *texture;
    int brightness;  // 0-128 (0 = чёрный, 128 = оригинал)
    State state;
} Sprite;

// Заглушка распаковки (замените на реальный алгоритм)
void unpack_texture(uint8_t *src, uint16_t *dst, int width, int height) {
    for (int i = 0; i < width * height; i++) {
        dst[i] = ((uint16_t*)src)[i];  // Простое копирование (данные уже в RGB565)
    }
}

void init() {
    ResetGraph(0);
    InitPad(NULL, 0, NULL, 0);
    CdInit();
    SetVideoMode(MODE_NTSC);
    FntLoad(960, 256);
    FntOpen(16, 16, 256, 224, 0, 512);
}

void load_and_unpack_texture(int start_sector) {
    // Чтение данных с CD в RAW_DATA_ADDR через DMA
    CdRead(start_sector, (uint32_t*)RAW_DATA_ADDR, TEX_SECTORS);
    CdReadSync(0);

    // Распаковка в UNPACKED_ADDR
    unpack_texture((uint8_t*)RAW_DATA_ADDR, (uint16_t*)UNPACKED_ADDR, TEX_WIDTH, TEX_HEIGHT);
}

void draw_sprite(Sprite *sprite) {
    // Применение яркости через RGB
    int r, g, b;
    if (sprite->brightness <= 0) {
        r = g = b = 0;  // Полная темнота
    } else {
        float mult = (float)sprite->brightness / 128.0f;
        r = 255 * mult;
        g = 255 * mult;
        b = 255 * mult;
    }

    // Настройка и отрисовка спрайта
    DR_MODE dr_mode;
    SetDrawMode(&dr_mode, 0, 0, GetTPage(0, 0, 0, 0), 0);
    SetSprt16(&sprite->img_rect);
    setRGB0(&sprite->img_rect, r, g, b);
    DrawPrim(&sprite->img_rect);
}

int main() {
    init();

    // Загрузка текстуры из сектора 1000 (пример)
    int texture_start_sector = 1000;
    load_and_unpack_texture(texture_start_sector);

    // Инициализация спрайта
    Sprite sprite = {
        .img_rect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0},
        .texture = (uint16_t*)UNPACKED_ADDR,
        .brightness = 0,
        .state = FADE_IN
    };

    // Загрузка текстуры в VRAM
    RECT vram_rect = {0, 0, TEX_WIDTH, TEX_HEIGHT};
    LoadImage(&vram_rect, (uint32_t*)UNPACKED_ADDR);

    // Главный цикл
    while (1) {
        // Обработка состояния
        switch (sprite.state) {
            case FADE_IN:
                sprite.brightness += 2;
                if (sprite.brightness >= 128) {
                    sprite.brightness = 128;
                    sprite.state = WAIT_FOR_BUTTON;
                }
                break;

            case WAIT_FOR_BUTTON: {
                // Проверка нажатия CROSS (✅) или CIRCLE (🟠)
                u_long pad = PadRead(0);
                if (pad & PAD_CROSS || pad & PAD_CIRCLE) {
                    sprite.state = FADE_OUT;
                }
                break;
            }

            case FADE_OUT:
                sprite.brightness -= 2;
                if (sprite.brightness <= 0) {
                    sprite.brightness = 0;
                    // Здесь можно перейти к следующей сцене
                }
                break;
        }

        // Отрисовка
        draw_sprite(&sprite);
        FntPrint("Press X or O to fade out\nBrightness: %d", sprite.brightness);
        FntFlush(-1);
        VSync(0);
    }

    return 0;
}

Как это работает?

  1. FADE_IN:
    • Текстура плавно проявляется (яркость увеличивается от 0 до 128).
  2. WAIT_FOR_BUTTON:
    • Программа ждёт нажатия CROSS () или CIRCLE (🟠) через PadRead().
  3. FADE_OUT:
    • После нажатия текстура исчезает (яркость уменьшается до 0).

Ключевые особенности

  • Ручное управление адресами RAM (0x80010000 и 0x80030000).
  • Яркость через RGB, а не полупрозрачность.
  • Проверка кнопок через битовые маски:
    • PAD_CROSS — кнопка .
    • PAD_CIRCLE — кнопка 🟠.

Сборка

  1. Скомпилируйте:
    ccpsx MAIN.C -o FADE_DEMO.EXE
    
  2. Запишите на диск, указав в SYSTEM.CNF:
    BOOT = cdrom:\FADE_DEMO.EXE;1
    

Демонстрация

  1. Текстура плавно появляется.
  2. Нажмите X или O на геймпаде.
  3. Текстура исчезает в темноту.

Теперь вы можете интегрировать это в свою игру для cinematic-сцен! 🎮