diff --git a/Makefile b/Makefile index 538f6dc3d1ae4713b4e1d8d4ee2fd73c4aa07266..cc7b8d49d1c729aa4de759b8bb370ab19ec7340a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ CC := clang -CFLAGS := -std=c99 -Wall -Wextra +CFLAGS := -std=c99 -Wall -Wextra -ggdb LDFLAGS := -lm SYSTEM := $(shell uname -s) + +SOURCES := main.c ppm.c ifeq ($(SYSTEM), Linux) LDFLAGS += -lGL -lglut @@ -11,5 +13,5 @@ ifeq ($(SYSTEM), Darwin) LDFLAGS += -framework OpenGL -framework GLUT endif -main: main.c - clang $(CFLAGS) main.c -o main $(LDFLAGS) +main: $(SOURCES) + clang $(CFLAGS) $(SOURCES) -o main $(LDFLAGS) diff --git a/all.h b/all.h new file mode 100644 index 0000000000000000000000000000000000000000..2ae5e7b23038501d1eb1a987189efe268230d008 --- /dev/null +++ b/all.h @@ -0,0 +1,37 @@ +#ifndef ALL_H +#define ALL_H + +typedef struct { + float x, y, z; +} Vec2; + +typedef struct { + float x, y, z; +} Vec3; + +typedef struct { + float x, y, z, w; +} Vec4; + +typedef struct { + char format[3]; + int width; + int height; + int color_space; + unsigned char *pixels; +} Image; + +#define COLOR_RED (Vec3){ .x = 1.0f, .y = 0.0f, .z = 0.0f } +#define COLOR_GREEN (Vec3){ .x = 0.0f, .y = 1.0f, .z = 0.0f } +#define COLOR_BLUE (Vec3){ .x = 0.0f, .y = 0.0f, .z = 1.0f } +#define COLOR_WHITE (Vec3){ .x = 1.0f, .y = 1.0f, .z = 1.0f } +#define COLOR_BLACK (Vec3){ .x = 0.0f, .y = 0.0f, .z = 0.0f } +#define COLOR_YELLOW (Vec3){ .x = 1.0f, .y = 1.0f, .z = 0.0f } +#define COLOR_CYAN (Vec3){ .x = 0.0f, .y = 1.0f, .z = 1.0f } +#define COLOR_MAGENTA (Vec3){ .x = 1.0f, .y = 0.0f, .z = 1.0f } +#define COLOR_ORANGE (Vec3){ .x = 1.0f, .y = 0.5f, .z = 0.0f } +#define COLOR_PURPLE (Vec3){ .x = 0.5f, .y = 0.0f, .z = 0.5f } + +Image read_ppm_image(const char *filename); + +#endif diff --git a/main.c b/main.c index cd3fbf1ef4fde8d54c2d8ad1020168b1b4074b0a..4d070acbbcfbc397563140bd69df13b0eb570f31 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,8 @@ // 1. Calculate all vertex data (Position, Color, UV) on the CPU. // 2. Store them in a contiguous memory buffer (Batch). // 3. Send the entire buffer to the GPU in a single call (glDrawArrays). +#include "all.h" + #include #include #include @@ -25,18 +27,6 @@ #else #include #include #endif - -typedef struct { - float x, y, z; -} Vec2; - -typedef struct { - float x, y, z; -} Vec3; - -typedef struct { - float x, y, z, w; -} Vec4; // A packed structure representing a single vertex. // The GPU needs to know the "stride" (size) of this struct to jump between vertices. @@ -79,17 +69,6 @@ v.z*c + cross.z*s + z*dot*(1-c) }; } -#define COLOR_RED (Vec3){ .x = 1.0f, .y = 0.0f, .z = 0.0f } -#define COLOR_GREEN (Vec3){ .x = 0.0f, .y = 1.0f, .z = 0.0f } -#define COLOR_BLUE (Vec3){ .x = 0.0f, .y = 0.0f, .z = 1.0f } -#define COLOR_WHITE (Vec3){ .x = 1.0f, .y = 1.0f, .z = 1.0f } -#define COLOR_BLACK (Vec3){ .x = 0.0f, .y = 0.0f, .z = 0.0f } -#define COLOR_YELLOW (Vec3){ .x = 1.0f, .y = 1.0f, .z = 0.0f } -#define COLOR_CYAN (Vec3){ .x = 0.0f, .y = 1.0f, .z = 1.0f } -#define COLOR_MAGENTA (Vec3){ .x = 1.0f, .y = 0.0f, .z = 1.0f } -#define COLOR_ORANGE (Vec3){ .x = 1.0f, .y = 0.5f, .z = 0.0f } -#define COLOR_PURPLE (Vec3){ .x = 0.5f, .y = 0.0f, .z = 0.5f } - typedef struct { int target_fps; int width; @@ -104,65 +83,9 @@ } Game; Game game = {0}; -unsigned char* load_ppm(const char* filename, int* width, int* height) { - FILE* fp = fopen(filename, "rb"); - if (!fp) { - fprintf(stderr, "Unable to open file '%s'\n", filename); - return NULL; - } - - char header[3]; - if (!fgets(header, sizeof(header), fp) || header[0] != 'P' || header[1] != '6') { - fprintf(stderr, "Invalid PPM file (must be P6)\n"); - fclose(fp); - return NULL; - } - - // Skip comments - int c = getc(fp); - while (c == '#') { - while (getc(fp) != '\n'); - c = getc(fp); - } - ungetc(c, fp); - - if (fscanf(fp, "%d %d", width, height) != 2) { - fprintf(stderr, "Invalid PPM dimensions\n"); - fclose(fp); - return NULL; - } - - int max_val; - if (fscanf(fp, "%d", &max_val) != 1 || max_val != 255) { - fprintf(stderr, "Invalid PPM max value\n"); - fclose(fp); - return NULL; - } - - while (getc(fp) != '\n'); // skip single whitespace - - unsigned char* data = ALLOC(unsigned char, (*width) * (*height) * 3); - if (!data) { - fprintf(stderr, "Memory allocation failed\n"); - fclose(fp); - return NULL; - } - - if (fread(data, 1, (*width) * (*height) * 3, fp) != (size_t)((*width) * (*height) * 3)) { - fprintf(stderr, "Error reading PPM data\n"); - FREE(data); - fclose(fp); - return NULL; - } - - fclose(fp); - return data; -} - void init_texture(void) { - int width, height; - unsigned char* data = load_ppm("textures/test.ppm", &width, &height); - if (!data) return; + Image img = read_ppm_image("textures/test.ppm"); + if (!img.pixels) return; glGenTextures(1, &game.texture_id); glBindTexture(GL_TEXTURE_2D, game.texture_id); @@ -170,9 +93,9 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img.pixels); - FREE(data); + FREE(img.pixels); } void draw_text(float x, float y, void* font, const char* string, Vec3 color) { diff --git a/ppm.c b/ppm.c new file mode 100644 index 0000000000000000000000000000000000000000..85b8bf6bb9698bf3afba6397cfaa5059444d0bfd --- /dev/null +++ b/ppm.c @@ -0,0 +1,105 @@ +#include "all.h" + +#include +#include +#include +#include + +Image read_ppm_image(const char *filename) { + /* const char *filename = "textures/test.ppm"; */ + + Image img = {0}; + + FILE *f = fopen(filename, "rb"); + if (!f) { + perror("fopen"); + return (Image){}; + } + + + int j = 0; + int in_token = 0; + int header_done = 0; + + size_t pixel_count = 0; + size_t pixel_capacity = 0; + + unsigned char buf[4096]; + size_t nread; + + while ((nread = fread(buf, 1, sizeof buf, f)) > 0) { + for (size_t i = 0; i < nread; ++i) { + unsigned char c = buf[i]; + + if (!header_done) { + if (isspace(c)) { + if (in_token) { + j++; + in_token = 0; + + if (j == 4) { + header_done = 1; + + pixel_capacity = img.width * img.height * 3; + img.pixels = malloc(pixel_capacity); + + if (!img.pixels) { + perror("malloc"); + fclose(f); + return (Image){}; + } + } + } + + continue; + } + + in_token = 1; + + switch (j) { + case 0: + { + size_t len = strlen(img.format); + if (len + 1 < sizeof img.format) { + img.format[len] = (char)c; + img.format[len + 1] = '\0'; + } + } break; + + case 1: + { + if (isdigit(c)) + img.width = img.width * 10 + (c - '0'); + } break; + + case 2: + { + if (isdigit(c)) + img.height = img.height * 10 + (c - '0'); + } break; + + case 3: + { + if (isdigit(c)) + img.color_space = img.color_space * 10 + (c - '0'); + } break; + } + } else { + if (pixel_count < pixel_capacity) { + img.pixels[pixel_count++] = c; + } + } + } + } + + fclose(f); + + printf("format: %s\n", img.format); + printf("width: %d\n", img.width); + printf("height: %d\n", img.height); + printf("color_space: %d\n", img.color_space); + printf("pixel_count: %zu\n", pixel_count); + printf("expected: %zu\n", pixel_capacity); + + return img; +}