149 lines
4.2 KiB
C
149 lines
4.2 KiB
C
#include "render.h"
|
|
#include <gint/display.h>
|
|
|
|
//---
|
|
// Camera management
|
|
//---
|
|
|
|
void camera_init(struct camera *c, struct map *m)
|
|
{
|
|
c->zoom = 1;
|
|
|
|
c->limits.x_min = fix(-CAMERA_BORDER);
|
|
c->limits.x_max = fix(m->width + CAMERA_BORDER);
|
|
c->limits.y_min = fix(-CAMERA_BORDER);
|
|
c->limits.y_max = fix(m->height + CAMERA_BORDER);
|
|
|
|
/* Fullscreen */
|
|
c->viewport.x_min = 0;
|
|
c->viewport.x_max = DWIDTH;
|
|
c->viewport.y_min = 0;
|
|
c->viewport.y_max = DHEIGHT;
|
|
|
|
c->width = fix(c->viewport.x_max - c->viewport.x_min) / TILE_WIDTH;
|
|
c->height = fix(c->viewport.y_max - c->viewport.y_min) / TILE_HEIGHT;
|
|
|
|
c->x = (c->limits.x_min + c->limits.x_max) / 2;
|
|
c->y = (c->limits.y_min + c->limits.y_max) / 2;
|
|
}
|
|
|
|
ipoint_t camera_map2screen(struct camera *c, fpoint_t p)
|
|
{
|
|
return (ipoint_t){
|
|
.x = DWIDTH / 2 + fround((p.x - c->x) * TILE_WIDTH * c->zoom),
|
|
.y = DHEIGHT / 2 + fround((p.y - c->y) * TILE_HEIGHT * c->zoom),
|
|
};
|
|
}
|
|
/* Translate screen coordinates to map coordinates */
|
|
fpoint_t camera_screen2map(struct camera *c, ipoint_t p)
|
|
{
|
|
return (fpoint_t){
|
|
.x = c->x + fix(p.x - DWIDTH / 2) / TILE_WIDTH / c->zoom,
|
|
.y = c->y + fix(p.y - DHEIGHT / 2) / TILE_HEIGHT / c->zoom,
|
|
};
|
|
}
|
|
|
|
/* Lock the camera at the center if set by settings. */
|
|
static bool camera_lock(struct camera *c)
|
|
{
|
|
if(c->zoom == 1 && CAMERA_LOCK_AT_x1)
|
|
{
|
|
c->x = (c->limits.x_min + c->limits.x_max) / 2;
|
|
c->y = (c->limits.y_min + c->limits.y_max) / 2;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Bound the camera to the map limits */
|
|
static void camera_bound(struct camera *c)
|
|
{
|
|
/* Project top left and bottom-right corners of viewport onto the map */
|
|
ipoint_t v_tl = { c->viewport.x_min, c->viewport.y_min };
|
|
ipoint_t v_br = { c->viewport.x_max, c->viewport.y_max };
|
|
|
|
fpoint_t m_tl = camera_screen2map(c, v_tl);
|
|
fpoint_t m_br = camera_screen2map(c, v_br);
|
|
|
|
/* Bound viewport to map limits */
|
|
if(m_tl.x < c->limits.x_min)
|
|
c->x += (c->limits.x_min - m_tl.x);
|
|
else if(m_br.x > c->limits.x_max)
|
|
c->x += (c->limits.x_max - m_br.x);
|
|
if(m_tl.y < c->limits.y_min)
|
|
c->y += (c->limits.y_min - m_tl.y);
|
|
else if(m_br.y > c->limits.y_max)
|
|
c->y += (c->limits.y_max - m_br.y);
|
|
}
|
|
|
|
void camera_move(struct camera *c, map_coord_t dx, map_coord_t dy)
|
|
{
|
|
if(camera_lock(c)) return;
|
|
c->x += dx;
|
|
c->y += dy;
|
|
camera_bound(c);
|
|
}
|
|
|
|
void camera_zoom(struct camera *c, int zoom)
|
|
{
|
|
if(zoom < ZOOM_MIN) zoom = ZOOM_MIN;
|
|
if(zoom > ZOOM_MAX) zoom = ZOOM_MAX;
|
|
|
|
c->zoom = zoom;
|
|
if(camera_lock(c)) return;
|
|
camera_bound(c);
|
|
}
|
|
|
|
fixed_t camera_ppu(struct camera *c)
|
|
{
|
|
/* Since this assumes isotropic space we can use TILE_WIDTH or TILE_HEIGHT
|
|
indifferently (they're assumed equal) */
|
|
return fix(1) * c->zoom * TILE_WIDTH;
|
|
}
|
|
|
|
//---
|
|
// Rendering
|
|
//---
|
|
|
|
void render_map(struct map *m, struct camera *c)
|
|
{
|
|
extern bopti_image_t img_tileset_base;
|
|
extern bopti_image_t img_tileset_decor;
|
|
|
|
/* Render floor and walls */
|
|
for(int row = 0; row < m->height; row++)
|
|
for(int col = 0; col < m->width; col++) {
|
|
struct tile *t = map_tile(m, col, row);
|
|
if(!t) continue;
|
|
|
|
fpoint_t tile_pos = { fix(col), fix(row) };
|
|
ipoint_t p = camera_map2screen(c, tile_pos);
|
|
|
|
/* Floor/wall layer */
|
|
dsubimage(p.x, p.y, &img_tileset_base, TILE_WIDTH * (t->base % 16),
|
|
TILE_HEIGHT * (t->base / 16), TILE_WIDTH, TILE_HEIGHT,
|
|
DIMAGE_NONE);
|
|
/* Decoration layer */
|
|
dsubimage(p.x, p.y, &img_tileset_decor, TILE_WIDTH * (t->decor % 16),
|
|
TILE_HEIGHT * (t->decor / 16), TILE_WIDTH, TILE_HEIGHT,
|
|
DIMAGE_NONE);
|
|
}
|
|
|
|
/* Render entities */
|
|
for(int i = 0; i < m->entity_count; i++) {
|
|
struct entity *e = m->entities[i];
|
|
|
|
ipoint_t p = camera_map2screen(c, (fpoint_t){ e->x, e->y });
|
|
|
|
/* Translate entity hitbox to the map coordinate system */
|
|
shape_t s = e->hitbox;
|
|
shape_scale(&s, camera_ppu(c));
|
|
shape_translate(&s, point_i2f(p));
|
|
shape_draw(&s);
|
|
|
|
/* Show entity center */
|
|
dline(p.x-1, p.y, p.x+1, p.y, s.color);
|
|
dline(p.x, p.y-1, p.x, p.y+1, s.color);
|
|
}
|
|
}
|