-
Notifications
You must be signed in to change notification settings - Fork 364
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds an example using SDL3 as a renderer
This example is rather basic but it does provide an example how one could setup a rendering loop between clay and SDL3. Although SDL3 is in its infancy and doesn't have an official stable release the API has been locked so there shouldn't be any code adaptations required for this example if one was to update to a more recent SDL3 release in the future.
- Loading branch information
1 parent
6a9b723
commit ba715a6
Showing
3 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
cmake_minimum_required(VERSION 3.27) | ||
|
||
# Project setup | ||
project(clay_sdl3_renderer C) | ||
set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Werror") | ||
set(CMAKE_C_FLAGS_RELEASE "-O3") | ||
|
||
include(FetchContent) | ||
set(FETCHCONTENT_QUIET FALSE) | ||
|
||
# Download SDL3 | ||
FetchContent_Declare( | ||
SDL | ||
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git | ||
GIT_TAG preview-3.1.6 | ||
GIT_SHALLOW TRUE | ||
GIT_PROGRESS TRUE | ||
) | ||
message(STATUS "Using SDL via FetchContent") | ||
FetchContent_MakeAvailable(SDL) | ||
set_property(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_deps/sdl-src" PROPERTY EXCLUDE_FROM_ALL TRUE) | ||
|
||
# Download SDL_ttf | ||
FetchContent_Declare( | ||
SDL_ttf | ||
GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git | ||
GIT_TAG 40219a6 | ||
GIT_SHALLOW TRUE | ||
GIT_PROGRESS TRUE | ||
) | ||
message(STATUS "Using SDL_ttf via FetchContent") | ||
FetchContent_MakeAvailable(SDL_ttf) | ||
set_property(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_deps/sdl_ttf-src" PROPERTY EXCLUDE_FROM_ALL TRUE) | ||
|
||
# Example executable | ||
add_executable(${PROJECT_NAME} main.c) | ||
target_link_libraries(${PROJECT_NAME} PRIVATE | ||
SDL3::SDL3 | ||
SDL3_ttf::SDL3_ttf | ||
) | ||
|
||
add_custom_command( | ||
TARGET ${PROJECT_NAME} POST_BUILD | ||
COMMAND ${CMAKE_COMMAND} -E copy_directory | ||
${CMAKE_CURRENT_SOURCE_DIR}/resources | ||
${CMAKE_CURRENT_BINARY_DIR}/resources | ||
) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
#define SDL_MAIN_USE_CALLBACKS | ||
#include <SDL3/SDL_main.h> | ||
#include <SDL3/SDL.h> | ||
#include <SDL3_ttf/SDL_ttf.h> | ||
|
||
#define CLAY_IMPLEMENTATION | ||
#include "../../clay.h" | ||
|
||
static const Uint32 FONT_ID = 0; | ||
|
||
static const Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255}; | ||
static const Clay_Color COLOR_BLUE = (Clay_Color) {111, 173, 162, 255}; | ||
static const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255}; | ||
|
||
typedef struct app_state { | ||
SDL_Window *window; | ||
SDL_Renderer *renderer; | ||
} AppState; | ||
|
||
/* This needs to be global because the "MeasureText" callback doesn't have a | ||
* user data parameter */ | ||
static TTF_Font *gFonts[1]; | ||
|
||
static inline Clay_Dimensions SDL_MeasureText(Clay_String *text, Clay_TextElementConfig *config) | ||
{ | ||
TTF_Font *font = gFonts[config->fontId]; | ||
int width, height; | ||
|
||
if (!TTF_GetStringSize(font, text->chars, text->length, &width, &height)) { | ||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError()); | ||
} | ||
|
||
return (Clay_Dimensions) { (float) width, (float) height }; | ||
} | ||
|
||
static void Label(Clay_String text) | ||
{ | ||
CLAY(CLAY_LAYOUT({ .padding = {16, 8} }), CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE })) { | ||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ | ||
.textColor = { 255, 255, 255, 255 }, | ||
.fontId = FONT_ID, | ||
.fontSize = 24, | ||
})); | ||
} | ||
} | ||
|
||
static Clay_RenderCommandArray Clay_CreateLayout() | ||
{ | ||
Clay_BeginLayout(); | ||
CLAY(CLAY_ID("MainContent"), | ||
CLAY_LAYOUT({ | ||
.sizing = { | ||
.width = CLAY_SIZING_GROW(), | ||
.height = CLAY_SIZING_GROW(), | ||
}, | ||
.childAlignment = { | ||
.x = CLAY_ALIGN_X_CENTER, | ||
.y = CLAY_ALIGN_Y_CENTER, | ||
}, | ||
.childGap = 10, | ||
.padding = { 10, 10 }, | ||
.layoutDirection = CLAY_TOP_TO_BOTTOM, | ||
}), | ||
CLAY_RECTANGLE({ | ||
.color = COLOR_LIGHT, | ||
}) | ||
) { | ||
Label(CLAY_STRING("Button 1")); | ||
Label(CLAY_STRING("Button 2")); | ||
Label(CLAY_STRING("Button 3")); | ||
} | ||
return Clay_EndLayout(); | ||
} | ||
|
||
static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArray *rcommands) | ||
{ | ||
for (size_t i = 0; i < rcommands->length; i++) { | ||
Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i); | ||
Clay_BoundingBox bounding_box = rcmd->boundingBox; | ||
SDL_FRect rect = { bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height }; | ||
|
||
switch (rcmd->commandType) { | ||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { | ||
Clay_RectangleElementConfig *config = rcmd->config.rectangleElementConfig; | ||
Clay_Color color = config->color; | ||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); | ||
SDL_RenderFillRect(renderer, &rect); | ||
} break; | ||
case CLAY_RENDER_COMMAND_TYPE_TEXT: { | ||
Clay_TextElementConfig *config = rcmd->config.textElementConfig; | ||
Clay_String *text = &rcmd->text; | ||
SDL_Color color = { config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a }; | ||
|
||
TTF_Font *font = gFonts[config->fontId]; | ||
SDL_Surface *surface = TTF_RenderText_Blended(font, text->chars, text->length, color); | ||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); | ||
SDL_RenderTexture(renderer, texture, NULL, &rect); | ||
|
||
SDL_DestroySurface(surface); | ||
SDL_DestroyTexture(texture); | ||
} break; | ||
default: | ||
SDL_Log("Unknown render command type: %d", rcmd->commandType); | ||
} | ||
} | ||
} | ||
|
||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) | ||
{ | ||
(void) argc; | ||
(void) argv; | ||
|
||
if (!TTF_Init()) { | ||
return SDL_APP_FAILURE; | ||
} | ||
|
||
AppState *state = SDL_calloc(1, sizeof(AppState)); | ||
if (!state) { | ||
return SDL_APP_FAILURE; | ||
} | ||
*appstate = state; | ||
|
||
if (!SDL_CreateWindowAndRenderer("Clay Demo", 640, 480, 0, &state->window, &state->renderer)) { | ||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window and renderer: %s", SDL_GetError()); | ||
return SDL_APP_FAILURE; | ||
} | ||
SDL_SetWindowResizable(state->window, true); | ||
|
||
TTF_Font *font = TTF_OpenFont("resources/Roboto-Regular.ttf", 24); | ||
if (!font) { | ||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load font: %s", SDL_GetError()); | ||
return SDL_APP_FAILURE; | ||
} | ||
|
||
gFonts[FONT_ID] = font; | ||
|
||
/* Initialize Clay */ | ||
uint64_t totalMemorySize = Clay_MinMemorySize(); | ||
Clay_Arena clayMemory = (Clay_Arena) { | ||
.label = CLAY_STRING("Clay Memory Arena"), | ||
.memory = SDL_malloc(totalMemorySize), | ||
.capacity = totalMemorySize | ||
}; | ||
|
||
int width, height; | ||
SDL_GetWindowSize(state->window, &width, &height); | ||
Clay_SetMeasureTextFunction(SDL_MeasureText); | ||
Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }); | ||
|
||
*appstate = state; | ||
return SDL_APP_CONTINUE; | ||
} | ||
|
||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) | ||
{ | ||
SDL_AppResult ret_val = SDL_APP_CONTINUE; | ||
|
||
switch (event->type) { | ||
case SDL_EVENT_QUIT: | ||
ret_val = SDL_APP_SUCCESS; | ||
break; | ||
case SDL_EVENT_WINDOW_RESIZED: | ||
Clay_SetLayoutDimensions((Clay_Dimensions) { (float) event->window.data1, (float) event->window.data2 }); | ||
break; | ||
case SDL_EVENT_MOUSE_MOTION: | ||
Clay_SetPointerState((Clay_Vector2) { event->motion.x, event->motion.y }, | ||
event->motion.state & SDL_BUTTON_LEFT); | ||
break; | ||
case SDL_EVENT_MOUSE_WHEEL: | ||
Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->motion.xrel, event->motion.yrel }, 0.01f); | ||
break; | ||
default: | ||
break; | ||
}; | ||
|
||
return ret_val; | ||
} | ||
|
||
SDL_AppResult SDL_AppIterate(void *appstate) | ||
{ | ||
AppState *state = appstate; | ||
|
||
Clay_RenderCommandArray render_commands = Clay_CreateLayout(); | ||
|
||
SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, 255); | ||
SDL_RenderClear(state->renderer); | ||
|
||
SDL_RenderClayCommands(state->renderer, &render_commands); | ||
|
||
SDL_RenderPresent(state->renderer); | ||
|
||
return SDL_APP_CONTINUE; | ||
} | ||
|
||
void SDL_AppQuit(void *appstate, SDL_AppResult result) | ||
{ | ||
(void) result; | ||
|
||
if (result != SDL_APP_SUCCESS) { | ||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Application failed to run"); | ||
} | ||
|
||
AppState *state = appstate; | ||
|
||
if (state) { | ||
if (state->renderer) | ||
SDL_DestroyRenderer(state->renderer); | ||
|
||
if (state->window) | ||
SDL_DestroyWindow(state->window); | ||
|
||
SDL_free(state); | ||
} | ||
TTF_Quit(); | ||
} |
Binary file not shown.