Created
August 16, 2021 06:55
-
-
Save stillwwater/f265b1b9d35a39b530cdefb9976346d6 to your computer and use it in GitHub Desktop.
Pixel perfect camera in SDL2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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