Compare commits

...

2 commits

Author SHA1 Message Date
fc862a279f merge
Merge branch 'main' of git.tavo.one:tavo/skr
2025-10-30 00:54:30 -06:00
fc2b17a3f5 updates 2025-10-30 00:53:55 -06:00
2 changed files with 301 additions and 15 deletions

39
examples/1-window.md Normal file
View file

@ -0,0 +1,39 @@
# Empty window
```c
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cglm/cglm.h>
#define SKR_BACKEND_API 0 // opengl
#define SKR_BACKEND_WINDOW 0 // glfw
#include <skr/skr.h>
int main(void) {
SkrState state = SkrInit(&(SkrWindow){
.Title = "Hello SKR",
.Width = 800,
.Height = 600,
});
if (!SKR_OK) {
fprintf(stderr, "Failed to init window: %s\n", SKR_LAST_ERROR);
return 1;
}
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to init GLEW\n");
return 1;
}
// SkrTriangle(&state);
while (!SkrWindowShouldClose(state.Window)) {
SkrRendererRender(&state);
}
SkrFinalize(&state);
return 0;
}
```

277
skr/skr.h
View file

@ -67,6 +67,13 @@ typedef enum SkrApiBackendType {
/* Default API if none was specified: GL */ /* Default API if none was specified: GL */
#define SKR_BACKEND_API SKR_BACKEND_API_GL #define SKR_BACKEND_API SKR_BACKEND_API_GL
#else
#if !defined(VULKAN_H_)
typedef struct VkPipeline_T* VkPipeline;
typedef struct VkPipelineLayout_T* VkPipelineLayout;
#endif
#endif #endif
/** /**
@ -190,6 +197,22 @@ typedef struct SkrShader {
const char* Path; const char* Path;
} SkrShader; } SkrShader;
static SkrShader skr_fps_camera_vert = {
GL_VERTEX_SHADER,
(char*){"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main() {\n"
"gl_Position = projection * view * model * vec4(aPos, "
"1.0f);\n"
"TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\n"},
NULL};
/** /**
* @brief Vertex structure used by the rendering engine. * @brief Vertex structure used by the rendering engine.
* *
@ -367,6 +390,24 @@ typedef struct SkrMesh {
*/ */
SkrTexture* Textures; SkrTexture* Textures;
unsigned int TextureCount; unsigned int TextureCount;
/**
* @brief Backend-specific rendering data.
*
* Tagged union that stores handles or objects specific to the graphics
* API backend in use. Only the member corresponding to the current
* backend type is valid.
*/
union {
struct {
GLuint Program;
} GL;
struct {
VkPipeline Pipeline;
VkPipelineLayout Layout;
} VK;
} Backend;
} SkrMesh; } SkrMesh;
/** /**
@ -424,13 +465,64 @@ typedef struct SkrWindow {
SkrWindowBackend Backend; /*!< Backend type and handle. */ SkrWindowBackend Backend; /*!< Backend type and handle. */
} SkrWindow; } SkrWindow;
typedef struct SkrShaderProgram {
GLuint ID;
char* Name;
SkrShader* Shaders;
unsigned int ShaderCount;
} SkrShaderProgram;
typedef struct SkrState { typedef struct SkrState {
SkrWindow* Window; SkrWindow* Window;
SkrModel* Models; SkrModel* Models;
unsigned int ModelCount; unsigned int ModelCount;
SkrShaderProgram* Programs;
unsigned int ProgramCount;
} SkrState; } SkrState;
/**
* @brief First-person camera structure.
*
* Stores position, orientation, and field-of-view.
* Can be controlled by mouse and keyboard input.
*/
typedef struct SkrCamera {
vec3 Position; /*!< Camera position in world space. */
vec3 Front; /*!< Normalized forward vector. */
vec3 Up; /*!< Normalized up vector. */
vec3 Right; /*!< Normalized right vector. */
vec3 WorldUp; /*!< Global up direction, usually {0, 1, 0}. */
float Yaw;
float Pitch;
float FOV;
float Sensitivity;
float LastX; /*!< Last cursor X position. */
float LastY; /*!< Last cursor Y position. */
bool FirstMouse; /*!< Whether the first mouse movement has been
captured. */
bool Initialized;
} SkrCamera;
#define SkrDefaultFPSCamera \
(&(SkrCamera){ \
.Position = {0.0f, 0.0f, 3.0f}, \
.Front = {0.0f, 0.0f, -1.0f}, \
.Up = {0.0f, 1.0f, 0.0f}, \
.Yaw = -90.0f, \
.Pitch = 0.0f, \
.FOV = 70.0f, \
.Sensitivity = 0.1f, \
.LastX = 400.0f, \
.LastY = 300.0f, \
.FirstMouse = true, \
})
extern SkrCamera* g_skr_camera;
/** /**
* @internal * @internal
* @brief Set the last error message with source metadata. * @brief Set the last error message with source metadata.
@ -586,10 +678,54 @@ static inline void m_skr_gl_framebuffer_size_callback(const int width,
static inline void m_skr_gl_glfw_framebuffer_size_callback(GLFWwindow* window, static inline void m_skr_gl_glfw_framebuffer_size_callback(GLFWwindow* window,
const int width, const int width,
const int height) { const int height) {
(void)window;
m_skr_gl_framebuffer_size_callback(width, height); m_skr_gl_framebuffer_size_callback(width, height);
m_skr_last_error_clear(); m_skr_last_error_clear();
} }
static inline void m_skr_gl_glfw_mouse_callback(GLFWwindow* window,
double xposIn, double yposIn) {
float xpos = xposIn;
float ypos = yposIn;
if (g_skr_camera->FirstMouse) {
g_skr_camera->LastX = xpos;
g_skr_camera->LastY = ypos;
g_skr_camera->FirstMouse = false;
}
float xoffset = xpos - g_skr_camera->LastX;
float yoffset =
g_skr_camera->LastY -
ypos; // reversed since y-coordinates go from bottom to top
g_skr_camera->LastX = xpos;
g_skr_camera->LastY = ypos;
float sensitivity = g_skr_camera->Sensitivity;
xoffset *= sensitivity;
yoffset *= sensitivity;
g_skr_camera->Yaw += xoffset;
g_skr_camera->Pitch += yoffset;
// make sure that when pitch is out of bounds, screen doesn't get
// flipped
if (g_skr_camera->Pitch > 89.0f)
g_skr_camera->Pitch = 89.0f;
if (g_skr_camera->Pitch < -89.0f)
g_skr_camera->Pitch = -89.0f;
vec3 front = {0.0f, 0.0f, 0.0f};
front[0] = cosf(glm_rad(g_skr_camera->Yaw)) *
cosf(glm_rad(g_skr_camera->Pitch));
front[1] = sinf(glm_rad(g_skr_camera->Pitch));
front[2] = sinf(glm_rad(g_skr_camera->Yaw)) *
cosf(glm_rad(g_skr_camera->Pitch));
glm_vec3_normalize_to(front, g_skr_camera->Front);
}
/** /**
* @internal * @internal
* @brief Initialize a GLFW window for OpenGL rendering. * @brief Initialize a GLFW window for OpenGL rendering.
@ -626,6 +762,11 @@ static inline int m_skr_gl_glfw_init(SkrWindow* w) {
m_skr_gl_glfw_framebuffer_size_callback); m_skr_gl_glfw_framebuffer_size_callback);
glfwMakeContextCurrent(w->Backend.Handler.GLFW); glfwMakeContextCurrent(w->Backend.Handler.GLFW);
if (g_skr_camera) {
glfwSetCursorPosCallback(w->Backend.Handler.GLFW,
m_skr_gl_glfw_mouse_callback);
}
m_skr_last_error_clear(); m_skr_last_error_clear();
return 1; return 1;
} }
@ -922,8 +1063,27 @@ static inline void m_skr_gl_renderer_init() {}
* @internal * @internal
* @brief GL clear screen (color + depth). * @brief GL clear screen (color + depth).
*/ */
static inline void m_skr_gl_renderer_render(void) { static inline void m_skr_gl_renderer_render(SkrState* s) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (unsigned int i = 0; i < s->ModelCount; ++i) {
SkrModel* model = &s->Models[i];
if (!model->Meshes)
continue;
for (unsigned int j = 0; j < model->MeshCount; ++j) {
SkrMesh* mesh = &model->Meshes[j];
if (mesh->VAO == 0 || mesh->VertexCount == 0)
continue;
glUseProgram(mesh->Backend.GL.Program);
glBindVertexArray(mesh->VAO);
glDrawArrays(GL_TRIANGLES, 0, mesh->VertexCount);
}
}
glBindVertexArray(0);
} }
/** /**
@ -944,6 +1104,14 @@ static inline void m_skr_gl_renderer_finalize(SkrState* s) {
for (unsigned int j = 0; j < model->MeshCount; ++j) { for (unsigned int j = 0; j < model->MeshCount; ++j) {
SkrMesh* mesh = &model->Meshes[j]; SkrMesh* mesh = &model->Meshes[j];
if (mesh->Textures && mesh->TextureCount > 0) {
for (unsigned int t = 0; t < mesh->TextureCount;
++t) {
glDeleteTextures(1,
&mesh->Textures[t].ID);
}
}
if (mesh->VAO) if (mesh->VAO)
glDeleteVertexArrays(1, &mesh->VAO); glDeleteVertexArrays(1, &mesh->VAO);
if (mesh->VBO) if (mesh->VBO)
@ -952,8 +1120,14 @@ static inline void m_skr_gl_renderer_finalize(SkrState* s) {
glDeleteBuffers(1, &mesh->EBO); glDeleteBuffers(1, &mesh->EBO);
mesh->VAO = mesh->VBO = mesh->EBO = 0; mesh->VAO = mesh->VBO = mesh->EBO = 0;
mesh->Backend.GL.Program = 0;
} }
} }
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glUseProgram(0);
} }
/** /**
@ -975,24 +1149,14 @@ static inline void m_skr_gl_glfw_renderer_render(SkrState* s) {
s->Window->InputHandler(s->Window); s->Window->InputHandler(s->Window);
} }
glfwPollEvents();
glfwGetFramebufferSize(s->Window->Backend.Handler.GLFW, glfwGetFramebufferSize(s->Window->Backend.Handler.GLFW,
&s->Window->Width, &s->Window->Height); &s->Window->Width, &s->Window->Height);
m_skr_gl_renderer_render(); m_skr_gl_renderer_render(s);
glfwSwapBuffers(s->Window->Backend.Handler.GLFW); glfwSwapBuffers(s->Window->Backend.Handler.GLFW);
}
/** glfwPollEvents();
* @internal
* @brief GLFW Shutdown OpenGL renderer and GLFW.
*/
static inline void m_skr_gl_glfw_renderer_finalize(SkrState* s) {
m_skr_gl_renderer_finalize(s);
glfwTerminate();
} }
/** /**
@ -1102,6 +1266,9 @@ static inline int SkrWindowShouldClose(SkrWindow* w) {
} }
static inline void SkrRendererRender(SkrState* s) { static inline void SkrRendererRender(SkrState* s) {
if (!s || !s->Window)
return;
if (SKR_BACKEND_API == SKR_BACKEND_API_GL) { if (SKR_BACKEND_API == SKR_BACKEND_API_GL) {
if (SKR_BACKEND_WINDOW == SKR_BACKEND_WINDOW_GLFW) { if (SKR_BACKEND_WINDOW == SKR_BACKEND_WINDOW_GLFW) {
m_skr_gl_glfw_renderer_render(s); m_skr_gl_glfw_renderer_render(s);
@ -1111,10 +1278,90 @@ static inline void SkrRendererRender(SkrState* s) {
static inline void SkrFinalize(SkrState* s) { static inline void SkrFinalize(SkrState* s) {
if (SKR_BACKEND_API == SKR_BACKEND_API_GL) { if (SKR_BACKEND_API == SKR_BACKEND_API_GL) {
if (SKR_BACKEND_WINDOW == SKR_BACKEND_WINDOW_GLFW) { m_skr_gl_renderer_finalize(s);
m_skr_gl_glfw_renderer_finalize(s);
if (s->Window->Backend.Type == SKR_BACKEND_WINDOW_GLFW) {
glfwTerminate();
} }
} }
s->Models = NULL;
s->ModelCount = 0;
s->Window = NULL;
}
static inline GLuint m_skr_gl_triangle(SkrState* s) {
static SkrMesh mesh = {0};
static SkrModel model = {0};
const char* triangle_vert = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main() {\n"
" gl_Position = vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
"}\n";
const char* triangle_frag = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main() {\n"
" FragColor = vec4(ourColor, 1.0f);\n"
"}\n";
SkrShader shaders[] = {
{GL_VERTEX_SHADER, triangle_vert, NULL},
{GL_FRAGMENT_SHADER, triangle_frag, NULL},
};
mesh.Backend.GL.Program =
m_skr_gl_create_program_from_shaders(shaders, 2);
glUseProgram(mesh.Backend.GL.Program);
glGenVertexArrays(1, &mesh.VAO);
glGenBuffers(1, &mesh.VBO);
glBindVertexArray(mesh.VAO);
glBindBuffer(GL_ARRAY_BUFFER, mesh.VBO);
float vertices[] = {0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
(void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
(void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
mesh.VertexCount = 3;
model.Meshes = &mesh;
model.MeshCount = 1;
s->Models = &model;
s->ModelCount = 1;
return mesh.Backend.GL.Program;
}
static inline void SkrTriangle(SkrState* s) {
if (SKR_BACKEND_API == SKR_BACKEND_API_GL) {
m_skr_gl_triangle(s);
}
}
static inline void SkrInitCamera(SkrState* s, SkrShader vert) {}
static inline void SkrCaptureCursor(SkrState* s) {
if (s->Window->Backend.Type == SKR_BACKEND_WINDOW_GLFW) {
glfwSetInputMode(s->Window->Backend.Handler.GLFW, GLFW_CURSOR,
GLFW_CURSOR_DISABLED);
}
} }
#ifdef __cplusplus #ifdef __cplusplus