-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Renderers/SDL3] Adds an example using SDL3 as a renderer (#107)
Co-authored-by: Nic Barker <[email protected]>
- Loading branch information
1 parent
9d3fba3
commit 8e7e30d
Showing
6 changed files
with
286 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
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,49 @@ | ||
cmake_minimum_required(VERSION 3.27) | ||
|
||
# Project setup | ||
project(clay_examples_sdl3_simple_demo C) | ||
set(CMAKE_C_STANDARD 99) | ||
|
||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -Wall -Werror") | ||
set(CMAKE_C_FLAGS_RELEASE "${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 "${sdl_SOURCE_DIR}" PROPERTY EXCLUDE_FROM_ALL TRUE) | ||
|
||
# Download SDL_ttf | ||
FetchContent_Declare( | ||
SDL_ttf | ||
GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git | ||
GIT_TAG main # Slightly risky to use main branch, but it's the only one available | ||
GIT_SHALLOW TRUE | ||
GIT_PROGRESS TRUE | ||
) | ||
message(STATUS "Using SDL_ttf via FetchContent") | ||
FetchContent_MakeAvailable(SDL_ttf) | ||
set_property(DIRECTORY "${sdl_ttf_SOURCE_DIR}" 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,185 @@ | ||
#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" | ||
|
||
#include <stdio.h> | ||
|
||
#include "../../renderers/SDL3/clay_renderer_SDL3.c" | ||
|
||
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; | ||
|
||
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(); | ||
} | ||
|
||
void HandleClayErrors(Clay_ErrorData errorData) { | ||
printf("%s", errorData.errorText.chars); | ||
} | ||
|
||
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) { | ||
.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 }, (Clay_ErrorHandler) { HandleClayErrors }); | ||
|
||
*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.
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,6 @@ | ||
Please note, the SDL3 renderer is not 100% feature complete. It is currently missing: | ||
|
||
- Rounded rectangle corners | ||
- Borders | ||
- Images | ||
- Scroll / Scissor handling |
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,41 @@ | ||
#include "../../clay.h" | ||
#include <SDL3/SDL_main.h> | ||
#include <SDL3/SDL.h> | ||
#include <SDL3_ttf/SDL_ttf.h> | ||
|
||
/* This needs to be global because the "MeasureText" callback doesn't have a | ||
* user data parameter */ | ||
static TTF_Font *gFonts[1]; | ||
|
||
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; | ||
const 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); | ||
} | ||
} | ||
} |