From a11a4738f7b2c09001b1328b12f8022a3d635ecd Mon Sep 17 00:00:00 2001 From: Alex Sanchez Brotons Date: Sat, 20 Aug 2022 11:13:29 +0200 Subject: Initial commit Setting up a build process for a macOS OpenGL application. --- .gitignore | 1 + Makefile | 56 ++++++++++++++++ src/main.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shorthand.h | 27 ++++++++ 4 files changed, 288 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/main.c create mode 100755 src/shorthand.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..57c2774 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +.POSIX: +.SUFFIXES: + +# Source code location and files to watch for changes. +SRC_DIR := src +BUILD_DIR := build +SRC_MAIN := $(SRC_DIR)/main.c +WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") +INC_DIRS := $(shell find $(SRC_DIR) -type d) +INC_FLAGS := $(addprefix -I,$(INC_DIRS)) + +# Output executable. +TARGET := ogl +BIN := $(BUILD_DIR)/$(TARGET) + +# Compiler and linker configuration. +CC := cc +CFLAGS := -Wall -Wextra -pedantic -DBIN_NAME=\"$(TARGET)\" -Wno-missing-braces +CFLAGS += $(INC_FLAGS) +NASM_FLAGS ?= -felf64 +LDFLAGS := +LDLIBS := -lglfw -framework OpenGL +RELEASE_CFLAGS := -DNDEBUG -O2 +DEBUG_CFLAGS := -DDEBUG -O0 -g + +.PHONY: build tests clean + +# Setup debug/release builds. +# make clean && make DEBUG=0 +# make clean && make DEBUG=1 +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + CFLAGS += $(DEBUG_CFLAGS) + NASM_FLAGS += -g -F dwarf +else ifeq ($(DEBUG), 2) + CFLAGS += $(DEBUG_CFLAGS) -fsanitize=address + NASM_FLAGS += -g -F dwarf +else + CFLAGS += $(RELEASE_CFLAGS) +endif + +main: $(BIN) + +$(BIN): $(SRC_MAIN) $(WATCH_SRC) $(BUILD_DIR) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(BIN) $(SRC_MAIN) $(LDLIBS) + +# Create build directory if needed. +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +run: $(BIN) + $(BIN) example.bdl + +# Remove build directory. +clean: + rm -rf $(BUILD_DIR) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b30280a --- /dev/null +++ b/src/main.c @@ -0,0 +1,204 @@ +#define GL_SILENCE_DEPRECATION +#define GLFW_INCLUDE_GLCOREARB +#include + +#include +#include + +#include "shorthand.h" + +// +// Callbacks. +// + +void +glfw_error_callback(int error, const char* description) { + (void)error; + fprintf(stderr, "error: %s\n", description); +} + +void +glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { + (void)mods; + (void)scancode; + if (action == GLFW_PRESS && + (key == GLFW_KEY_ESCAPE || key == GLFW_KEY_CAPS_LOCK)) { + glfwSetWindowShouldClose(window, GLFW_TRUE); + } +} + +typedef struct Context { + GLFWwindow *window; + u32 gl_version_major; + u32 gl_version_minor; + u32 win_width; + u32 win_height; + char win_title[256]; + bool win_resizable; + + struct { + f64 prev; + f64 elapsed; + size_t n_frames; + } frame_time; +} Context; + +u32 +compile_program(Context *ctx, const char *vert_src, const char *frag_src) { + // Compile vertex shader. + u32 vert_shader = glCreateShader(GL_VERTEX_SHADER); + { + glShaderSource(vert_shader, 1, &vert_src, NULL); + glCompileShader(vert_shader); + int success = 0; + glGetShaderiv(vert_shader, GL_COMPILE_STATUS, &success); + if (!success) { + fprintf(stderr, "error: vertex shader compilation failed\n"); + glfwDestroyWindow(ctx->window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + // Compile fragment shader. + u32 frag_shader = glCreateShader(GL_FRAGMENT_SHADER); + { + glShaderSource(frag_shader, 1, &frag_src, NULL); + glCompileShader(frag_shader); + int success = 0; + glGetShaderiv(vert_shader, GL_COMPILE_STATUS, &success); + if (!success) { + fprintf(stderr, "error: fragment shader compilation failed\n"); + glfwDestroyWindow(ctx->window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + // Link shader program. + u32 program = glCreateProgram(); + { + glAttachShader(program, vert_shader); + glAttachShader(program, frag_shader); + glLinkProgram(program); + int success = 0; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if(!success) { + fprintf(stderr, "error: program shader linking failed\n"); + glfwDestroyWindow(ctx->window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + // Delete unused objects. + glDeleteShader(vert_shader); + glDeleteShader(frag_shader); + return program; +} + +void +init_context(Context *ctx) { + if (!glfwInit()) { + fprintf(stderr, "error: GLFW initialization failed"); + exit(EXIT_FAILURE); + } + + // Init window and OpenGL context. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, ctx->gl_version_major); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, ctx->gl_version_minor); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_RESIZABLE, ctx->win_resizable); + GLFWwindow* window = glfwCreateWindow( + ctx->win_width, ctx->win_height, ctx->win_title, NULL, NULL); + if (!window) { + fprintf(stderr, "error: couldn't open OpenGL window"); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + // Enable current OpenGL context and init GLAD. + glfwMakeContextCurrent(window); + // TODO: Working on macOS for now, need to check if this is needed. + // if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { + // fprintf(stderr, "error: GLAD initialization failed"); + // glfwDestroyWindow(window); + // glfwTerminate(); + // exit(EXIT_FAILURE); + // } + + ctx->window = window; +} + +void +setup_callbacks(Context *ctx) { + glfwSetErrorCallback(glfw_error_callback); + glfwSetKeyCallback(ctx->window, glfw_key_callback); +} + +void +update_frame_time(Context *ctx) { + // Measure frame times and fps. + f64 cur_time = glfwGetTime(); + f64 delta_time = cur_time - ctx->frame_time.prev; + ctx->frame_time.elapsed += delta_time; + ctx->frame_time.n_frames++; + if (ctx->frame_time.elapsed >= 1.0) { + f64 fps = 0; + fps = ctx->frame_time.n_frames / ctx->frame_time.elapsed; + ctx->frame_time.elapsed = 0; + ctx->frame_time.n_frames = 0; + + // Update title with timing data. + char title_buf[256 * 2]; + sprintf(title_buf, + "%s [%.2fms, %.2f FPS]", + ctx->win_title, delta_time * 1000, fps); + glfwSetWindowTitle(ctx->window, title_buf); + } + ctx->frame_time.prev = cur_time; +} + +void +update(Context *ctx) { + update_frame_time(ctx); +} + +void +render(Context *ctx) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glfwSwapBuffers(ctx->window); +} + +int +main(void) { + Context ctx = (Context){ + .gl_version_major = 3, + .gl_version_minor = 2, + .win_width = 800, + .win_height = 600, + .win_title = "OpenGL experiments", + }; + init_context(&ctx); + setup_callbacks(&ctx); + + // + // Main loop. + // + + ctx.frame_time.prev = glfwGetTime(); + ctx.frame_time.elapsed = 0; + ctx.frame_time.n_frames = 0; + while (!glfwWindowShouldClose(ctx.window)) { + update(&ctx); + render(&ctx); + glfwPollEvents(); // constant updates. + // glfwWaitEvents(); // updates only when input arrives (better cpu usage). + } + + glfwDestroyWindow(ctx.window); + glfwTerminate(); + return EXIT_SUCCESS; +} + diff --git a/src/shorthand.h b/src/shorthand.h new file mode 100755 index 0000000..b5174a1 --- /dev/null +++ b/src/shorthand.h @@ -0,0 +1,27 @@ +#ifndef SHORTHAND_H +#define SHORTHAND_H + +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; +typedef float f32; +typedef double f64; + +#endif // SHORTHAND_H -- cgit v1.2.1