Skip to content

Instantly share code, notes, and snippets.

@stillwwater
Created August 16, 2021 06:55
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stillwwater/f265b1b9d35a39b530cdefb9976346d6 to your computer and use it in GitHub Desktop.
Save stillwwater/f265b1b9d35a39b530cdefb9976346d6 to your computer and use it in GitHub Desktop.
Pixel perfect camera in SDL2
#include "SDL.h"
#include "stb/stb_image.h"
#define CAMERA_BORDER 1
typedef struct {
SDL_Renderer *renderer;
SDL_Texture *target;
int width;
int height;
int target_width;
int target_height;
float x, y;
} Camera;
typedef struct {
SDL_Texture *texture;
int width;
int height;
float x, y;
} Sprite;
Camera make_camera(SDL_Renderer *renderer, int width, int height) {
Camera camera = {
.renderer = renderer,
.x = 0,
.y = 0,
.width = width,
.height = height,
.target_width = width + CAMERA_BORDER * 2,
.target_height = height + CAMERA_BORDER * 2,
};
camera.target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
camera.target_width, camera.target_height);
SDL_assert(camera.target);
SDL_SetTextureBlendMode(camera.target, SDL_BLENDMODE_BLEND);
return camera;
}
Sprite load_sprite(SDL_Renderer *renderer, const char *filename) {
Sprite sprite = {0};
int channels;
stbi_uc *image = stbi_load(filename, &sprite.width, &sprite.height, &channels, STBI_default);
int fmt = channels == 3 ? SDL_PIXELFORMAT_RGB24 : SDL_PIXELFORMAT_RGBA32;
int pitch = sprite.width * channels;
sprite.texture = SDL_CreateTexture(renderer, fmt, SDL_TEXTUREACCESS_STATIC, sprite.width, sprite.height);
SDL_UpdateTexture(sprite.texture, NULL, (const void *)image, pitch);
stbi_image_free(image);
return sprite;
}
int main(int argc, char **argv) {
const int Window_Width = 640;
const int Window_Height = 360;
//
// Window creation
//
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_Window *window = SDL_CreateWindow("Pixel Perfect", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Window_Width, Window_Height, 0);
SDL_assert(window);
SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_assert(renderer);
//
// Init
//
Sprite sprite = load_sprite(renderer, "sprite.png");
Camera camera = make_camera(renderer, 32, 18);
int direction = 0;
//
// Main loop
//
SDL_Event e;
while (1) {
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
goto exit;
case SDL_KEYDOWN:
if (e.key.keysym.sym == SDLK_SPACE) {
if (direction == 0)
direction = 1;
else
direction *= -1;
}
break;
default:
break;
}
}
camera.y += 0.05f * direction;
//
// Camera target
//
SDL_SetRenderTarget(renderer, camera.target);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
SDL_RenderClear(renderer);
//
// Draw Sprite
//
SDL_Rect sprite_rect;
sprite_rect.x = (int)(sprite.x - camera.x) + CAMERA_BORDER;
sprite_rect.y = (int)(sprite.y - camera.y) + CAMERA_BORDER;
sprite_rect.w = sprite.width;
sprite_rect.h = sprite.height;
SDL_RenderCopy(renderer, sprite.texture, NULL, &sprite_rect);
//
// Screen target
//
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(renderer);
//
// Draw camera texture
//
float pixel_h = (float)Window_Height / camera.height;
float correction_x = (int)camera.x - camera.x;
float correction_y = (int)camera.y - camera.y;
SDL_Rect dst;
dst.x = correction_x * pixel_h - pixel_h * CAMERA_BORDER;
dst.y = correction_y * pixel_h - pixel_h * CAMERA_BORDER;
dst.w = camera.target_width * pixel_h;
dst.h = camera.target_height * pixel_h;
SDL_RenderCopy(renderer, camera.target, NULL, &dst);
SDL_RenderPresent(renderer);
}
exit:
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment