diff --git a/assets/levels/R2/parallax.toml b/assets/levels/R2/parallax.toml index 0488a3f..3b69c7f 100644 --- a/assets/levels/R2/parallax.toml +++ b/assets/levels/R2/parallax.toml @@ -41,16 +41,14 @@ single = false # ============= [island2] +width = 256 height = 40 +u0 = 0 +v0 = 44 scrollx = 0.25 y0 = 80 single = false -[[island2.parts]] -u0 = 0 -v0 = 44 -width = 256 - # ============= # Water starts at BG1. # Anything with v0 >= 256 is at BG1 diff --git a/include/parallax.h b/include/parallax.h index 76b4eae..79cf32e 100644 --- a/include/parallax.h +++ b/include/parallax.h @@ -20,32 +20,18 @@ height: u16 */ -// Holds a single parallax strip part. -// Parts are a horizontal slice of a single strip. +// Holds a single parallax strip for a level. +// A strip is a horizontally-repeating quad. typedef struct { uint8_t u0; uint8_t v0; - uint8_t bgindex; uint16_t width; uint16_t height; - - // Calculated on load - uint16_t offsetx; -} ParallaxPart; - -// Holds a single parallax strip for a level. -// A strip may be a horizontally-repeating quad, or it -// may be composed of more than one quad that forms a bigger strip. -typedef struct { - uint8_t num_parts; + uint8_t bgindex; uint8_t is_single; int32_t scrollx; int32_t speedx; int16_t y0; - ParallaxPart *parts; - - // Calculated on load - uint16_t width; // State int32_t rposx; diff --git a/src/parallax.c b/src/parallax.c index 833419d..c980fdd 100644 --- a/src/parallax.c +++ b/src/parallax.c @@ -1,4 +1,5 @@ #include +#include #include "parallax.h" #include "util.h" @@ -9,6 +10,11 @@ extern ArenaAllocator _level_arena; extern uint8_t level_fade; +// Pre-allocated parallax polygons +#define PRL_POLYS 200 +POLY_FT4 prl_polys[2][PRL_POLYS]; +uint8_t prl_current_buffer = 0; + void load_parallax(Parallax *parallax, const char *filename) { @@ -33,40 +39,38 @@ load_parallax(Parallax *parallax, const char *filename) sizeof(ParallaxStrip) * parallax->num_strips); for(uint8_t i = 0; i < parallax->num_strips; i++) { ParallaxStrip *strip = ¶llax->strips[i]; - strip->width = 0; - - strip->num_parts = get_byte(bytes, &b); + strip->u0 = get_byte(bytes, &b); + strip->v0 = get_byte(bytes, &b); + strip->width = get_short_be(bytes, &b); + strip->height = get_short_be(bytes, &b); + strip->bgindex = get_byte(bytes, &b); strip->is_single = get_byte(bytes, &b); strip->scrollx = get_long_be(bytes, &b); strip->speedx = get_long_be(bytes, &b); strip->y0 = get_short_be(bytes, &b); + strip->rposx = 0; - - strip->parts = alloc_arena_malloc( - &_level_arena, - sizeof(ParallaxPart) * strip->num_parts); - for(uint8_t j = 0; j < strip->num_parts; j++) { - ParallaxPart *part = &strip->parts[j]; - - part->u0 = get_byte(bytes, &b); - part->v0 = get_byte(bytes, &b); - part->bgindex = get_byte(bytes, &b); - part->width = get_short_be(bytes, &b); - part->height = get_short_be(bytes, &b); - part->offsetx = (j == 0) - ? 0 - : (strip->parts[j-1].offsetx + strip->parts[j-1].width); - strip->width += part->width; - } } free(bytes); + + // Pre-allocate polygons + prl_current_buffer = 0; + for(uint32_t i = 0; i < PRL_POLYS; i++) { + for(int j = 0; j < 2; j++) { + POLY_FT4 *poly = &prl_polys[j][i]; + setPolyFT4(poly); + setRGB0(poly, 0, 0, 0); + } + } } void parallax_draw(Parallax *prl, Camera *camera, uint8_t tx_mode, int32_t px, int32_t py, int32_t cx, int32_t cy) { + uint32_t poly_count = 0; + // Camera left boundary (fixed 20.12 format) int32_t camera_vx = (camera->pos.vx - (CENTERX << 12)); @@ -76,46 +80,52 @@ parallax_draw(Parallax *prl, Camera *camera, ParallaxStrip *strip = &prl->strips[si]; // Cast multiplication to avoid sign extension on bit shift // This gets the mult. result but also removes the decimal part - int32_t stripx = (uint32_t)(camera_vx * strip->scrollx) >> 24; + int32_t stripx = (uint32_t)(camera_vx * -strip->scrollx) >> 24; + + // Coordinates currently start drawing at screen center, so + // push them back one screem + stripx -= SCREEN_XRES; // Update strip relative position when there's speed involved strip->rposx -= strip->speedx; - if((strip->rposx >> 12) < -((int32_t)strip->width)) - strip->rposx = 0; - for(uint8_t pi = 0; pi < strip->num_parts; pi++) { - ParallaxPart *part = &strip->parts[pi]; + // Calculate part X position based on factor and camera (int format) + int32_t vx = stripx + (strip->rposx >> 12); - // Calculate part X position based on factor and camera (int format) - int32_t vx = ((int32_t)part->offsetx) - 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 + 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; - // 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 - for(int32_t wx = vx; - wx < (int32_t)(SCREEN_XRES + part->width); - wx += part->width) - { - // Don't draw if strip is outside screen - if((wx + part->width) < 0) continue; + // TODO: 6 or 8 Depends on CLUT!!! + uint16_t curr_px = (uint16_t)(px + ((uint32_t)strip->bgindex << 6)); + uint16_t curr_cy = (uint16_t)(cy + strip->bgindex); - // TODO: 6 or 8 Depends on CLUT!!! - uint16_t curr_px = (uint16_t)(px + ((uint32_t)part->bgindex << 6)); - uint16_t curr_cy = (uint16_t)(cy + part->bgindex); + //POLY_FT4 *poly = (POLY_FT4 *)get_next_prim(); + //increment_prim(sizeof(POLY_FT4)); + //setPolyFT4(poly); + POLY_FT4 *poly = &prl_polys[prl_current_buffer][poly_count]; - POLY_FT4 *poly = (POLY_FT4 *)get_next_prim(); - increment_prim(sizeof(POLY_FT4)); - setPolyFT4(poly); + if(poly->r0 != level_fade) setRGB0(poly, level_fade, level_fade, level_fade); - poly->tpage = getTPage(tx_mode & 0x3, 0, curr_px, py); - poly->clut = getClut(cx, curr_cy); - setXYWH(poly, wx, strip->y0, part->width, part->height); - setUVWH(poly, part->u0, part->v0, part->width - 1, part->height - 1); - sort_prim(poly, OT_LENGTH - 2); // Layer 5: Background + poly->tpage = getTPage(tx_mode & 0x3, 0, curr_px, py); + poly->clut = getClut(cx, curr_cy); + setXYWH(poly, wx, strip->y0, strip->width, strip->height); + setUVWH(poly, strip->u0, strip->v0, strip->width - 1, strip->height - 1); + sort_prim(poly, OT_LENGTH - 2); // Layer 5: Background + + poly_count++; + assert(poly_count < PRL_POLYS); - // If drawing a single time, stop now - if(strip->is_single) break; - } + // If drawing a single time, stop now + if(strip->is_single) break; } } + + prl_current_buffer ^= 1; } diff --git a/tools/buildprl/buildprl.py b/tools/buildprl/buildprl.py index 9bc851b..25d79d8 100755 --- a/tools/buildprl/buildprl.py +++ b/tools/buildprl/buildprl.py @@ -21,37 +21,27 @@ def tofixed12(value: float) -> int: @dataclass -class ParallaxPart: +class ParallaxStrip: u0: int = 0 v0: int = 0 - bg_index: int = 0 width: int = 0 height: int = 0 - - def write_to(self, f): - f.write(c_ubyte(self.u0)) - f.write(c_ubyte(self.v0)) - f.write(c_ubyte(self.bg_index)) - f.write(c_ushort(self.width)) - f.write(c_ushort(self.height)) - - -@dataclass -class ParallaxStrip: + bg_index: int = 0 single: bool = False scrollx: float = 0 speedx: float = 0 y0: int = 0 - parts: [ParallaxPart] = field(default_factory=list) def write_to(self, f): - f.write(c_ubyte(len(self.parts))) + f.write(c_ubyte(self.u0)) + f.write(c_ubyte(self.v0)) + f.write(c_ushort(self.width)) + f.write(c_ushort(self.height)) + f.write(c_ubyte(self.bg_index)) f.write(c_ubyte(int(self.single))) f.write(c_int(tofixed12(self.scrollx))) f.write(c_int(tofixed12(self.speedx))) f.write(c_short(self.y0)) - for p in self.parts: - p.write_to(f) @dataclass @@ -73,29 +63,13 @@ def parse(data) -> Parallax: strip.scrollx = strip_data.get("scrollx", 0) strip.speedx = strip_data.get("speedx", 0) strip.y0 = strip_data.get("y0") - height = strip_data.get("height") # Reserved - parts = strip_data.get("parts") + strip.width = strip_data.get("width") + strip.height = strip_data.get("height") - if not parts: - # This is a single-piece strip - part = ParallaxPart() - v0 = strip_data.get("v0") - part.bg_index = int(v0 / 256) - part.u0 = strip_data.get("u0") - part.v0 = v0 % 256 - part.width = strip_data.get("width") - part.height = height - strip.parts.append(part) - else: - for part_data in parts: - part = ParallaxPart() - v0 = part_data.get("v0") - part.bg_index = int(v0 / 256) - part.u0 = part_data.get("u0") - part.v0 = v0 % 256 - part.width = part_data.get("width") - part.height = height - strip.parts.append(part) + v0 = strip_data.get("v0") + strip.bg_index = int(v0 / 256) + strip.u0 = strip_data.get("u0") + strip.v0 = v0 % 256 p.strips.append(strip) return p diff --git a/tools/layouts/prl.hexpat b/tools/layouts/prl.hexpat index 54e4291..9282cf6 100644 --- a/tools/layouts/prl.hexpat +++ b/tools/layouts/prl.hexpat @@ -1,20 +1,15 @@ // -*- mode: c; -*- -struct ParallaxPart { +struct ParallaxStrip { u8 u0; u8 v0; - u8 tex_idx; be u16 width; be u16 height; -}; - -struct ParallaxStrip { - u8 num_parts; + u8 tex_idx; u8 is_single; be s32 scrollx; be s32 speedx; be s16 y0; - ParallaxPart parts[num_parts]; }; struct Parallax {