This commit is contained in:
2026-06-21 11:02:44 +02:00
parent 80403f9b7b
commit 0a34ff323f
7 changed files with 5612 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
src/liasion
*.dSYM
Generated
+61
View File
@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1781577229,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+37
View File
@@ -0,0 +1,37 @@
{
description = "Liasion (Clay)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
gcc
gnumake
pkg-config
raylib
gdb
curl
];
shellHook = ''
echo "Clay + raylib dev shell ready cd src && make"
'';
};
}
);
}
+5058
View File
File diff suppressed because it is too large Load Diff
+123
View File
@@ -0,0 +1,123 @@
#define CLAY_IMPLEMENTATION
#include "clay.h"
#include "renderers/raylib/clay_renderer_raylib.c"
#include <stdio.h>
#include <stdlib.h>
// Catppuccin Mocha Palette
const Clay_Color Rosewater = {245, 224, 220, 255};
const Clay_Color Flamingo = {242, 205, 205, 255};
const Clay_Color Pink = {245, 194, 231, 255};
const Clay_Color Mauve = {203, 166, 247, 255};
const Clay_Color Red = {238, 153, 160, 255};
const Clay_Color Maroon = {235, 160, 172, 255};
const Clay_Color Peach = {250, 179, 135, 255};
const Clay_Color Yellow = {249, 226, 175, 255};
const Clay_Color Green = {166, 227, 161, 255};
const Clay_Color Teal = {148, 226, 213, 255};
const Clay_Color Sky = {137, 220, 235, 255};
const Clay_Color Sapphire = {116, 199, 236, 255};
const Clay_Color Blue = {137, 180, 250, 255};
const Clay_Color Lavender = {180, 190, 254, 255};
const Clay_Color Text = {205, 214, 244, 255};
const Clay_Color Subtext1 = {186, 194, 222, 255};
const Clay_Color Subtext0 = {166, 173, 200, 255};
const Clay_Color Overlay2 = {147, 153, 178, 255};
const Clay_Color Overlay1 = {127, 132, 156, 255};
const Clay_Color Overlay0 = {108, 112, 134, 255};
const Clay_Color Surface2 = {88, 91, 112, 255};
const Clay_Color Surface1 = {69, 71, 90, 255};
const Clay_Color Surface0 = {49, 50, 68, 255};
const Clay_Color Base = {30, 30, 46, 255};
const Clay_Color Mantle = {24, 24, 37, 255};
const Clay_Color Crust = {17, 17, 27, 255};
void HandleClayErrors(Clay_ErrorData errorData)
{
printf("Clay Error: %s\n", errorData.errorText.chars);
}
void RenderNavbar(void)
{
CLAY(CLAY_ID("Navbar"),
{.layout = {.sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(60)},
.layoutDirection = CLAY_LEFT_TO_RIGHT,
.childAlignment = {.y = CLAY_ALIGN_Y_CENTER},
.padding = {20, 20, 0, 0},
.childGap = 16},
.backgroundColor = Surface0})
{
CLAY_TEXT(CLAY_STRING("Liasion"),
{.fontSize = 22, .textColor = Text});
// Spacer that grows to push the compose button to the right
CLAY(CLAY_ID("NavbarSpacer"),
{.layout = {.sizing = {CLAY_SIZING_GROW(0),
CLAY_SIZING_FIXED(1)}}})
{
}
CLAY(CLAY_ID("ComposeButton"),
{.layout = {.padding = {16, 16, 8, 8}},
.backgroundColor = Clay_Hovered() ? Peach : Blue,
.cornerRadius = CLAY_CORNER_RADIUS(6)})
{
CLAY_TEXT(CLAY_STRING("Compose"),
{.fontSize = 16, .textColor = Text});
}
}
}
int main(void)
{
Clay_Raylib_Initialize(1000, 700, "Liasion",
FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
Font fonts[1];
fonts[0] = GetFontDefault();
uint64_t clayMemSize = Clay_MinMemorySize();
Clay_Arena clayArena = Clay_CreateArenaWithCapacityAndMemory(
clayMemSize, malloc(clayMemSize));
Clay_Initialize(clayArena,
(Clay_Dimensions){GetScreenWidth(), GetScreenHeight()},
(Clay_ErrorHandler){HandleClayErrors});
Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);
while (!WindowShouldClose()) {
Clay_SetLayoutDimensions(
(Clay_Dimensions){GetScreenWidth(), GetScreenHeight()});
Vector2 mouse = GetMousePosition();
Clay_SetPointerState((Clay_Vector2){mouse.x, mouse.y},
IsMouseButtonDown(MOUSE_BUTTON_LEFT));
Clay_UpdateScrollContainers(
true,
(Clay_Vector2){GetMouseWheelMoveV().x,
GetMouseWheelMoveV().y},
GetFrameTime());
Clay_BeginLayout();
CLAY(CLAY_ID("Root"),
{.layout = {.sizing = {CLAY_SIZING_GROW(0),
CLAY_SIZING_GROW(0)}},
.backgroundColor = Base})
{
RenderNavbar();
}
Clay_RenderCommandArray renderCommands =
Clay_EndLayout(GetFrameTime());
BeginDrawing();
ClearBackground(BLACK);
Clay_Raylib_Render(renderCommands, fonts);
EndDrawing();
}
Clay_Raylib_Close();
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -O0 -g
INCLUDES = $(shell pkg-config --cflags raylib)
LIBS = $(shell pkg-config --libs raylib) -lm
liasion: main.c clay.h renderers/raylib/clay_renderer_raylib.c
$(CC) $(CFLAGS) $(INCLUDES) main.c -o liasion $(LIBS)
clean:
rm -f liasion
+321
View File
@@ -0,0 +1,321 @@
#include "raylib.h"
#include "raymath.h"
#include "stdint.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#define CLAY_RECTANGLE_TO_RAYLIB_RECTANGLE(rectangle) (Rectangle) { .x = rectangle.x, .y = rectangle.y, .width = rectangle.width, .height = rectangle.height }
#define CLAY_COLOR_TO_RAYLIB_COLOR(color) (Color) { .r = (unsigned char)roundf(color.r), .g = (unsigned char)roundf(color.g), .b = (unsigned char)roundf(color.b), .a = (unsigned char)roundf(color.a) }
Camera Raylib_camera;
typedef enum
{
CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL
} CustomLayoutElementType;
typedef struct
{
Model model;
float scale;
Vector3 position;
Matrix rotation;
} CustomLayoutElement_3DModel;
typedef struct
{
CustomLayoutElementType type;
union {
CustomLayoutElement_3DModel model;
} customData;
} CustomLayoutElement;
const char* overlayShaderCode = "#version 330\n"
"\n"
"in vec2 fragTexCoord;\n"
"in vec4 fragColor;\n"
"\n"
"uniform sampler2D texture0;\n"
"uniform vec4 overlayColor;\n"
"\n"
"out vec4 finalColor;\n"
"\n"
"void main()\n"
"{\n"
" vec4 texelColor = texture(texture0, fragTexCoord) * fragColor;\n"
"\n"
" vec3 blendedRGB = mix(texelColor.rgb, overlayColor.rgb, overlayColor.a);\n"
"\n"
" finalColor = vec4(blendedRGB, texelColor.a);\n"
"}";
Shader overlayShader;
int colorLoc;
bool overlayEnabled = false;
void InitOverlay() {
overlayShader = LoadShaderFromMemory(0, overlayShaderCode);
colorLoc = GetShaderLocation(overlayShader, "overlayColor");
}
void SetColorOverlay(Color color) {
overlayEnabled = true;
float colorFloat[4] = {
(float)color.r/255.0f,
(float)color.g/255.0f,
(float)color.b/255.0f,
(float)color.a/255.0f,
};
SetShaderValue(overlayShader, colorLoc, colorFloat, SHADER_UNIFORM_VEC4);
BeginShaderMode(overlayShader);
}
void DisableColorOverlay() {
if (overlayEnabled) {
EndShaderMode();
overlayEnabled = false;
}
}
// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
Ray GetScreenToWorldPointWithZDistance(Vector2 position, Camera camera, int screenWidth, int screenHeight, float zDistance)
{
Ray ray = { 0 };
// Calculate normalized device coordinates
// NOTE: y value is negative
float x = (2.0f*position.x)/(float)screenWidth - 1.0f;
float y = 1.0f - (2.0f*position.y)/(float)screenHeight;
float z = 1.0f;
// Store values in a vector
Vector3 deviceCoords = { x, y, z };
// Calculate view matrix from camera look at
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
Matrix matProj = MatrixIdentity();
if (camera.projection == CAMERA_PERSPECTIVE)
{
// Calculate projection matrix from perspective
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)screenWidth/(double)screenHeight), 0.01f, zDistance);
}
else if (camera.projection == CAMERA_ORTHOGRAPHIC)
{
double aspect = (double)screenWidth/(double)screenHeight;
double top = camera.fovy/2.0;
double right = top*aspect;
// Calculate projection matrix from orthographic
matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
}
// Unproject far/near points
Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Calculate normalized direction vector
Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
ray.position = farPoint;
// Apply calculated vectors to ray
ray.direction = direction;
return ray;
}
static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
// Measure string size for Font
Clay_Dimensions textSize = { 0 };
float maxTextWidth = 0.0f;
float lineTextWidth = 0;
int maxLineCharCount = 0;
int lineCharCount = 0;
float textHeight = config->fontSize;
Font* fonts = (Font*)userData;
Font fontToUse = fonts[config->fontId];
// Font failed to load, likely the fonts are in the wrong place relative to the execution dir.
// RayLib ships with a default font, so we can continue with that built in one.
if (!fontToUse.glyphs) {
fontToUse = GetFontDefault();
}
float scaleFactor = config->fontSize/(float)fontToUse.baseSize;
for (int i = 0; i < text.length; ++i, lineCharCount++)
{
if (text.chars[i] == '\n') {
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);
lineTextWidth = 0;
lineCharCount = 0;
continue;
}
int index = text.chars[i] - 32;
if (fontToUse.glyphs[index].advanceX != 0) lineTextWidth += fontToUse.glyphs[index].advanceX;
else lineTextWidth += (fontToUse.recs[index].width + fontToUse.glyphs[index].offsetX);
}
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);
textSize.width = maxTextWidth * scaleFactor + (lineCharCount * config->letterSpacing);
textSize.height = textHeight;
return textSize;
}
void Clay_Raylib_Initialize(int width, int height, const char *title, unsigned int flags) {
SetConfigFlags(flags);
InitWindow(width, height, title);
InitOverlay();
// EnableEventWaiting();
}
// A MALLOC'd buffer, that we keep modifying inorder to save from so many Malloc and Free Calls.
// Call Clay_Raylib_Close() to free
static char *temp_render_buffer = NULL;
static int temp_render_buffer_len = 0;
// Call after closing the window to clean up the render buffer
void Clay_Raylib_Close()
{
if(temp_render_buffer) free(temp_render_buffer);
temp_render_buffer_len = 0;
CloseWindow();
}
void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
{
for (int j = 0; j < renderCommands.length; j++)
{
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);
Clay_BoundingBox boundingBox = {renderCommand->boundingBox.x, renderCommand->boundingBox.y, renderCommand->boundingBox.width, renderCommand->boundingBox.height};
switch (renderCommand->commandType)
{
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *textData = &renderCommand->renderData.text;
Font fontToUse = fonts[textData->fontId];
int strlen = textData->stringContents.length + 1;
if(strlen > temp_render_buffer_len) {
// Grow the temp buffer if we need a larger string
if(temp_render_buffer) free(temp_render_buffer);
temp_render_buffer = (char *) malloc(strlen);
temp_render_buffer_len = strlen;
}
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
memcpy(temp_render_buffer, textData->stringContents.chars, textData->stringContents.length);
temp_render_buffer[textData->stringContents.length] = '\0';
DrawTextEx(fontToUse, temp_render_buffer, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));
break;
}
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
Texture2D imageTexture = *(Texture2D *)renderCommand->renderData.image.imageData;
Clay_Color tintColor = renderCommand->renderData.image.backgroundColor;
if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) {
tintColor = (Clay_Color) { 255, 255, 255, 255 };
}
DrawTexturePro(
imageTexture,
(Rectangle) { 0, 0, imageTexture.width, imageTexture.height },
(Rectangle){boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height},
(Vector2) {},
0,
CLAY_COLOR_TO_RAYLIB_COLOR(tintColor));
break;
}
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
BeginScissorMode((int)roundf(boundingBox.x), (int)roundf(boundingBox.y), (int)roundf(boundingBox.width), (int)roundf(boundingBox.height));
break;
}
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
EndScissorMode();
break;
}
case CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_START: {
SetColorOverlay(CLAY_COLOR_TO_RAYLIB_COLOR(renderCommand->renderData.overlayColor.color));
break;
}
case CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_END: {
DisableColorOverlay();
}
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
if (config->cornerRadius.topLeft > 0) {
float radius = (config->cornerRadius.topLeft * 2) / (float)((boundingBox.width > boundingBox.height) ? boundingBox.height : boundingBox.width);
DrawRectangleRounded((Rectangle) { boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));
} else {
DrawRectangle(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &renderCommand->renderData.border;
// Left border
if (config->width.left > 0) {
DrawRectangleV((Vector2) { boundingBox.x, boundingBox.y + config->cornerRadius.topLeft }, (Vector2) { config->width.left, boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft }, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
// Right border
if (config->width.right > 0) {
DrawRectangleV((Vector2) { boundingBox.x + boundingBox.width - config->width.right, boundingBox.y + config->cornerRadius.topRight }, (Vector2) { config->width.right, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight }, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
// Top border
if (config->width.top > 0) {
DrawRectangleV((Vector2) { boundingBox.x + config->cornerRadius.topLeft, boundingBox.y }, (Vector2) { boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight, (int)config->width.top }, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
// Bottom border
if (config->width.bottom > 0) {
DrawRectangleV((Vector2) { boundingBox.x + config->cornerRadius.bottomLeft, boundingBox.y + boundingBox.height - config->width.bottom }, (Vector2) { boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight, (int)config->width.bottom }, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
if (config->cornerRadius.topLeft > 0) {
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.topLeft), roundf(boundingBox.y + config->cornerRadius.topLeft) }, roundf(config->cornerRadius.topLeft - config->width.top), config->cornerRadius.topLeft, 180, 270, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
if (config->cornerRadius.topRight > 0) {
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.topRight), roundf(boundingBox.y + config->cornerRadius.topRight) }, roundf(config->cornerRadius.topRight - config->width.top), config->cornerRadius.topRight, 270, 360, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
if (config->cornerRadius.bottomLeft > 0) {
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.bottomLeft), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomLeft) }, roundf(config->cornerRadius.bottomLeft - config->width.bottom), config->cornerRadius.bottomLeft, 90, 180, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
if (config->cornerRadius.bottomRight > 0) {
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.bottomRight), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomRight) }, roundf(config->cornerRadius.bottomRight - config->width.bottom), config->cornerRadius.bottomRight, 0.1, 90, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
Clay_CustomRenderData *config = &renderCommand->renderData.custom;
CustomLayoutElement *customElement = (CustomLayoutElement *)config->customData;
if (!customElement) continue;
switch (customElement->type) {
case CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL: {
Clay_BoundingBox rootBox = renderCommands.internalArray[0].boundingBox;
float scaleValue = CLAY__MIN(CLAY__MIN(1, 768 / rootBox.height) * CLAY__MAX(1, rootBox.width / 1024), 1.5f);
Ray positionRay = GetScreenToWorldPointWithZDistance((Vector2) { renderCommand->boundingBox.x + renderCommand->boundingBox.width / 2, renderCommand->boundingBox.y + (renderCommand->boundingBox.height / 2) + 20 }, Raylib_camera, (int)roundf(rootBox.width), (int)roundf(rootBox.height), 140);
BeginMode3D(Raylib_camera);
DrawModel(customElement->customData.model.model, positionRay.position, customElement->customData.model.scale * scaleValue, WHITE); // Draw 3d model with texture
EndMode3D();
break;
}
default: break;
}
break;
}
default: {
printf("Error: unhandled render command.");
exit(1);
}
}
}
}