2024-10-21 01:18:48 -03:00
|
|
|
#include <stdio.h>
|
2024-10-27 21:06:46 -03:00
|
|
|
#include <assert.h>
|
2024-10-21 01:18:48 -03:00
|
|
|
|
|
|
|
#include "parallax.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "memalloc.h"
|
2024-10-23 02:53:23 -03:00
|
|
|
#include "camera.h"
|
|
|
|
#include "render.h"
|
2024-10-21 01:18:48 -03:00
|
|
|
|
|
|
|
extern ArenaAllocator _level_arena;
|
2024-10-23 02:53:23 -03:00
|
|
|
extern uint8_t level_fade;
|
2024-10-21 01:18:48 -03:00
|
|
|
|
2024-10-27 21:06:46 -03:00
|
|
|
// Pre-allocated parallax polygons
|
|
|
|
uint8_t prl_current_buffer = 0;
|
|
|
|
|
2024-10-27 22:41:31 -03:00
|
|
|
// Sorry for this mess, but it's needed:
|
|
|
|
// prl_pols[i]: Array of polygons (POLY_FT4**) for current buffer (double buffering)
|
|
|
|
// prl_pols[i][j]: Pointer to list of polygons (POLY_FT4*) for the parallax strip #j
|
|
|
|
// prl_pols[i][j][k]: A single polygon for current strip
|
|
|
|
POLY_FT4 **prl_pols[2];
|
|
|
|
|
2024-10-21 01:18:48 -03:00
|
|
|
void
|
2024-10-27 22:41:31 -03:00
|
|
|
load_parallax(Parallax *parallax, const char *filename,
|
|
|
|
uint8_t tx_mode, int32_t px, int32_t py,
|
|
|
|
int32_t cx, int32_t cy)
|
2024-10-21 01:18:48 -03:00
|
|
|
{
|
|
|
|
uint8_t *bytes;
|
|
|
|
uint32_t b, length;
|
|
|
|
|
|
|
|
// If we fail to read the file, prepare it so we get no surprises
|
|
|
|
// when rendering a parallax structure
|
|
|
|
parallax->num_strips = 0;
|
|
|
|
parallax->strips = NULL;
|
|
|
|
|
2024-10-23 02:53:23 -03:00
|
|
|
b = 0;
|
2024-10-21 01:18:48 -03:00
|
|
|
bytes = file_read(filename, &length);
|
|
|
|
if(bytes == NULL) {
|
|
|
|
printf("Error reading PRL file %s from the CD. Using defaults\n", filename);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
parallax->num_strips = get_byte(bytes, &b);
|
|
|
|
parallax->strips = alloc_arena_malloc(
|
|
|
|
&_level_arena,
|
|
|
|
sizeof(ParallaxStrip) * parallax->num_strips);
|
2024-10-27 22:41:31 -03:00
|
|
|
|
|
|
|
// Prepare polygon lists
|
|
|
|
prl_pols[0] = alloc_arena_malloc(
|
|
|
|
&_level_arena,
|
|
|
|
sizeof(POLY_FT4 **) * parallax->num_strips);
|
|
|
|
prl_pols[1] = alloc_arena_malloc(
|
|
|
|
&_level_arena,
|
|
|
|
sizeof(POLY_FT4 **) * parallax->num_strips);
|
|
|
|
|
2024-10-21 01:18:48 -03:00
|
|
|
for(uint8_t i = 0; i < parallax->num_strips; i++) {
|
|
|
|
ParallaxStrip *strip = ¶llax->strips[i];
|
2024-10-29 03:31:53 -03:00
|
|
|
uint8_t u0 = get_byte(bytes, &b);
|
|
|
|
uint8_t v0 = get_byte(bytes, &b);
|
2024-10-27 21:06:46 -03:00
|
|
|
strip->width = get_short_be(bytes, &b);
|
|
|
|
strip->height = get_short_be(bytes, &b);
|
2024-10-29 03:31:53 -03:00
|
|
|
uint8_t bgindex = get_byte(bytes, &b);
|
2024-10-21 01:18:48 -03:00
|
|
|
strip->is_single = get_byte(bytes, &b);
|
2024-10-23 00:56:42 -03:00
|
|
|
strip->scrollx = get_long_be(bytes, &b);
|
2024-10-25 00:49:22 -03:00
|
|
|
strip->speedx = get_long_be(bytes, &b);
|
2024-10-21 01:18:48 -03:00
|
|
|
strip->y0 = get_short_be(bytes, &b);
|
|
|
|
|
2024-10-27 21:06:46 -03:00
|
|
|
strip->rposx = 0;
|
|
|
|
|
2024-10-27 22:41:31 -03:00
|
|
|
/* RENDERING OPTIMIZATION */
|
|
|
|
// 1. Calculate number of polygons needed for this strip, round up
|
|
|
|
uint32_t polygons_per_strip = (SCREEN_XRES / strip->width) + 3;
|
|
|
|
|
|
|
|
// 2. Allocate polygons for this strip
|
|
|
|
prl_pols[0][i] = alloc_arena_malloc(
|
|
|
|
&_level_arena,
|
|
|
|
sizeof(POLY_FT4) * polygons_per_strip);
|
|
|
|
prl_pols[1][i] = alloc_arena_malloc(
|
|
|
|
&_level_arena,
|
|
|
|
sizeof(POLY_FT4) * polygons_per_strip);
|
|
|
|
|
|
|
|
// 3. Preload and prepare polygons for this strip
|
|
|
|
// TODO: 6 or 8 Depends on CLUT!!!
|
2024-10-29 03:31:53 -03:00
|
|
|
uint16_t curr_px = (uint16_t)(px + ((uint32_t)bgindex << 6));
|
|
|
|
uint16_t curr_cy = (uint16_t)(cy + bgindex);
|
2024-10-27 22:41:31 -03:00
|
|
|
|
|
|
|
for(uint32_t p = 0; p < polygons_per_strip; p++) {
|
|
|
|
POLY_FT4 *poly0 = &prl_pols[0][i][p];
|
|
|
|
POLY_FT4 *poly1 = &prl_pols[1][i][p];
|
|
|
|
|
|
|
|
setPolyFT4(poly0);
|
|
|
|
setRGB0(poly0, 0, 0, 0);
|
|
|
|
poly0->tpage = getTPage(tx_mode & 0x3, 0, curr_px, py);
|
|
|
|
poly0->clut = getClut(cx, curr_cy);
|
|
|
|
//setXYWH(poly0, 0, strip->y0, strip->width, strip->height);
|
2024-10-29 03:31:53 -03:00
|
|
|
setUVWH(poly0, u0, v0, strip->width - 1, strip->height - 1);
|
2024-10-27 22:41:31 -03:00
|
|
|
|
|
|
|
setPolyFT4(poly1);
|
|
|
|
setRGB0(poly1, 0, 0, 0);
|
|
|
|
poly1->tpage = getTPage(tx_mode & 0x3, 0, curr_px, py);
|
|
|
|
poly1->clut = getClut(cx, curr_cy);
|
|
|
|
//setXYWH(poly1, 0, strip->y0, strip->width, strip->height);
|
2024-10-29 03:31:53 -03:00
|
|
|
setUVWH(poly1, u0, v0, strip->width - 1, strip->height - 1);
|
2024-10-27 21:06:46 -03:00
|
|
|
}
|
2024-10-27 22:41:31 -03:00
|
|
|
|
2024-10-27 21:06:46 -03:00
|
|
|
}
|
2024-10-27 22:41:31 -03:00
|
|
|
|
|
|
|
free(bytes);
|
2024-10-21 01:18:48 -03:00
|
|
|
}
|
2024-10-23 02:53:23 -03:00
|
|
|
|
|
|
|
void
|
2024-10-27 22:41:31 -03:00
|
|
|
parallax_draw(Parallax *prl, Camera *camera)
|
2024-10-23 02:53:23 -03:00
|
|
|
{
|
|
|
|
// Camera left boundary (fixed 20.12 format)
|
|
|
|
int32_t camera_vx = (camera->pos.vx - (CENTERX << 12));
|
|
|
|
|
2024-10-23 23:27:28 -03:00
|
|
|
// Strips are draw bottom to top so we can have further stuff
|
|
|
|
// (e.g. clouds) drawn on back
|
|
|
|
for(int8_t si = prl->num_strips - 1; si >= 0; si--) {
|
2024-10-23 02:53:23 -03:00
|
|
|
ParallaxStrip *strip = &prl->strips[si];
|
2024-10-23 18:45:15 -03:00
|
|
|
// Cast multiplication to avoid sign extension on bit shift
|
|
|
|
// This gets the mult. result but also removes the decimal part
|
2024-10-27 21:06:46 -03:00
|
|
|
int32_t stripx = (uint32_t)(camera_vx * -strip->scrollx) >> 24;
|
|
|
|
|
|
|
|
// Coordinates currently start drawing at screen center, so
|
|
|
|
// push them back one screem
|
2024-10-27 22:41:31 -03:00
|
|
|
stripx -= SCREEN_XRES + SCREEN_XRES;
|
2024-10-25 00:49:22 -03:00
|
|
|
|
|
|
|
// Update strip relative position when there's speed involved
|
|
|
|
strip->rposx -= strip->speedx;
|
2024-10-27 21:06:46 -03:00
|
|
|
|
|
|
|
// Calculate part X position based on factor and camera (int format)
|
|
|
|
int32_t vx = stripx + (strip->rposx >> 12);
|
|
|
|
|
|
|
|
// Given that each part is a horizontal piece of a strip, we assume
|
|
|
|
// that these parts repeat at every (strip width), so just draw
|
|
|
|
// all equal parts now at once, until we exhaust the screen width
|
2024-10-27 22:41:31 -03:00
|
|
|
uint32_t poly_n = 0;
|
2024-10-27 21:06:46 -03:00
|
|
|
for(int32_t wx = vx;
|
|
|
|
wx < (int32_t)(SCREEN_XRES + strip->width);
|
|
|
|
wx += strip->width)
|
|
|
|
{
|
|
|
|
// Don't draw if strip is outside screen
|
|
|
|
if((wx + strip->width) < 0) continue;
|
|
|
|
|
2024-10-27 22:41:31 -03:00
|
|
|
POLY_FT4 *poly = &prl_pols[prl_current_buffer][si][poly_n];
|
|
|
|
setRGB0(poly, level_fade, level_fade, level_fade);
|
2024-10-27 21:06:46 -03:00
|
|
|
setXYWH(poly, wx, strip->y0, strip->width, strip->height);
|
|
|
|
sort_prim(poly, OT_LENGTH - 2); // Layer 5: Background
|
|
|
|
|
2024-10-27 22:41:31 -03:00
|
|
|
poly_n++;
|
2024-10-27 21:06:46 -03:00
|
|
|
// If drawing a single time, stop now
|
|
|
|
if(strip->is_single) break;
|
2024-10-23 02:53:23 -03:00
|
|
|
}
|
|
|
|
}
|
2024-10-27 21:06:46 -03:00
|
|
|
|
|
|
|
prl_current_buffer ^= 1;
|
2024-10-23 02:53:23 -03:00
|
|
|
}
|