[Bindings/Odin] Odin Raylib renderer rewrite (#395)

This commit is contained in:
Rats 2025-04-30 21:31:05 -05:00 committed by GitHub
parent 970919e1fb
commit ea8288158e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 145 additions and 135 deletions

View File

@ -475,11 +475,11 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
} }
loadFont :: proc(fontId: u16, fontSize: u16, path: cstring) { loadFont :: proc(fontId: u16, fontSize: u16, path: cstring) {
raylibFonts[fontId] = RaylibFont { assign_at(&raylib_fonts,fontId,Raylib_Font{
font = raylib.LoadFontEx(path, cast(i32)fontSize * 2, nil, 0), font = raylib.LoadFontEx(path, cast(i32)fontSize * 2, nil, 0),
fontId = cast(u16)fontId, fontId = cast(u16)fontId,
} })
raylib.SetTextureFilter(raylibFonts[fontId].font.texture, raylib.TextureFilter.TRILINEAR) raylib.SetTextureFilter(raylib_fonts[fontId].font.texture, raylib.TextureFilter.TRILINEAR)
} }
errorHandler :: proc "c" (errorData: clay.ErrorData) { errorHandler :: proc "c" (errorData: clay.ErrorData) {
@ -493,7 +493,7 @@ main :: proc() {
memory := make([^]u8, minMemorySize) memory := make([^]u8, minMemorySize)
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory) arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory)
clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}, { handler = errorHandler }) clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}, { handler = errorHandler })
clay.SetMeasureTextFunction(measureText, nil) clay.SetMeasureTextFunction(measure_text, nil)
raylib.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .MSAA_4X_HINT}) raylib.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .MSAA_4X_HINT})
raylib.InitWindow(windowWidth, windowHeight, "Raylib Odin Example") raylib.InitWindow(windowWidth, windowHeight, "Raylib Odin Example")
@ -536,7 +536,7 @@ main :: proc() {
clay.SetLayoutDimensions({cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}) clay.SetLayoutDimensions({cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()})
renderCommands: clay.ClayArray(clay.RenderCommand) = createLayout(animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue)) renderCommands: clay.ClayArray(clay.RenderCommand) = createLayout(animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue))
raylib.BeginDrawing() raylib.BeginDrawing()
clayRaylibRender(&renderCommands) clay_raylib_render(&renderCommands)
raylib.EndDrawing() raylib.EndDrawing()
} }
} }

View File

@ -3,189 +3,199 @@ package main
import clay "../../clay-odin" import clay "../../clay-odin"
import "core:math" import "core:math"
import "core:strings" import "core:strings"
import "vendor:raylib" import rl "vendor:raylib"
RaylibFont :: struct { Raylib_Font :: struct {
fontId: u16, fontId: u16,
font: raylib.Font, font: rl.Font,
} }
clayColorToRaylibColor :: proc(color: clay.Color) -> raylib.Color { clay_color_to_rl_color :: proc(color: clay.Color) -> rl.Color {
return raylib.Color{cast(u8)color.r, cast(u8)color.g, cast(u8)color.b, cast(u8)color.a} return {u8(color.r), u8(color.g), u8(color.b), u8(color.a)}
} }
raylibFonts := [10]RaylibFont{} raylib_fonts := [dynamic]Raylib_Font{}
measureText :: proc "c" (text: clay.StringSlice, config: ^clay.TextElementConfig, userData: rawptr) -> clay.Dimensions { measure_text :: proc "c" (text: clay.StringSlice, config: ^clay.TextElementConfig, userData: rawptr) -> clay.Dimensions {
// Measure string size for Font line_width: f32 = 0
textSize: clay.Dimensions = {0, 0}
maxTextWidth: f32 = 0 font := raylib_fonts[config.fontId].font
lineTextWidth: f32 = 0
textHeight := cast(f32)config.fontSize for i in 0 ..< text.length {
fontToUse := raylibFonts[config.fontId].font glyph_index := text.chars[i] - 32
for i in 0 ..< int(text.length) { glyph := font.glyphs[glyph_index]
if (text.chars[i] == '\n') {
maxTextWidth = max(maxTextWidth, lineTextWidth) if glyph.advanceX != 0 {
lineTextWidth = 0 line_width += f32(glyph.advanceX)
continue
}
index := cast(i32)text.chars[i] - 32
if (fontToUse.glyphs[index].advanceX != 0) {
lineTextWidth += cast(f32)fontToUse.glyphs[index].advanceX
} else { } else {
lineTextWidth += (fontToUse.recs[index].width + cast(f32)fontToUse.glyphs[index].offsetX) line_width += font.recs[glyph_index].width + f32(glyph.offsetX)
} }
} }
maxTextWidth = max(maxTextWidth, lineTextWidth) return {width = line_width / 2, height = f32(config.fontSize)}
textSize.width = maxTextWidth / 2
textSize.height = textHeight
return textSize
} }
clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), allocator := context.temp_allocator) { clay_raylib_render :: proc(render_commands: ^clay.ClayArray(clay.RenderCommand), allocator := context.temp_allocator) {
for i in 0 ..< int(renderCommands.length) { for i in 0 ..< render_commands.length {
renderCommand := clay.RenderCommandArray_Get(renderCommands, cast(i32)i) render_command := clay.RenderCommandArray_Get(render_commands, i)
boundingBox := renderCommand.boundingBox bounds := render_command.boundingBox
switch (renderCommand.commandType) {
case clay.RenderCommandType.None: switch render_command.commandType {
{} case .None: // None
case clay.RenderCommandType.Text: case .Text:
config := renderCommand.renderData.text config := render_command.renderData.text
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
text := string(config.stringContents.chars[:config.stringContents.length]) text := string(config.stringContents.chars[:config.stringContents.length])
cloned := strings.clone_to_cstring(text, allocator)
fontToUse: raylib.Font = raylibFonts[config.fontId].font // Raylib uses C strings instead of Odin strings, so we need to clone
raylib.DrawTextEx( // Assume this will be freed elsewhere since we default to the temp allocator
fontToUse, cstr_text := strings.clone_to_cstring(text, allocator)
cloned,
raylib.Vector2{boundingBox.x, boundingBox.y}, font := raylib_fonts[config.fontId].font
cast(f32)config.fontSize, rl.DrawTextEx(font, cstr_text, {bounds.x, bounds.y}, f32(config.fontSize), f32(config.letterSpacing), clay_color_to_rl_color(config.textColor))
cast(f32)config.letterSpacing, case .Image:
clayColorToRaylibColor(config.textColor), config := render_command.renderData.image
) tint := config.backgroundColor
case clay.RenderCommandType.Image: if tint == 0 {
config := renderCommand.renderData.image tint = {255, 255, 255, 255}
tintColor := config.backgroundColor
if (tintColor.rgba == 0) {
tintColor = { 255, 255, 255, 255 }
} }
// TODO image handling
imageTexture := cast(^raylib.Texture2D)config.imageData imageTexture := (^rl.Texture2D)(config.imageData)
raylib.DrawTextureEx(imageTexture^, raylib.Vector2{boundingBox.x, boundingBox.y}, 0, boundingBox.width / cast(f32)imageTexture.width, clayColorToRaylibColor(tintColor)) rl.DrawTextureEx(imageTexture^, {bounds.x, bounds.y}, 0, bounds.width / f32(imageTexture.width), clay_color_to_rl_color(tint))
case clay.RenderCommandType.ScissorStart: case .ScissorStart:
raylib.BeginScissorMode( rl.BeginScissorMode(i32(math.round(bounds.x)), i32(math.round(bounds.y)), i32(math.round(bounds.width)), i32(math.round(bounds.height)))
cast(i32)math.round(boundingBox.x), case .ScissorEnd:
cast(i32)math.round(boundingBox.y), rl.EndScissorMode()
cast(i32)math.round(boundingBox.width), case .Rectangle:
cast(i32)math.round(boundingBox.height), config := render_command.renderData.rectangle
) if config.cornerRadius.topLeft > 0 {
case clay.RenderCommandType.ScissorEnd: radius: f32 = (config.cornerRadius.topLeft * 2) / min(bounds.width, bounds.height)
raylib.EndScissorMode() draw_rect_rounded(bounds.x, bounds.y, bounds.width, bounds.height, radius, config.backgroundColor)
case clay.RenderCommandType.Rectangle:
config := renderCommand.renderData.rectangle
if (config.cornerRadius.topLeft > 0) {
radius: f32 = (config.cornerRadius.topLeft * 2) / min(boundingBox.width, boundingBox.height)
raylib.DrawRectangleRounded(raylib.Rectangle{boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height}, radius, 8, clayColorToRaylibColor(config.backgroundColor))
} else { } else {
raylib.DrawRectangle(cast(i32)boundingBox.x, cast(i32)boundingBox.y, cast(i32)boundingBox.width, cast(i32)boundingBox.height, clayColorToRaylibColor(config.backgroundColor)) draw_rect(bounds.x, bounds.y, bounds.width, bounds.height, config.backgroundColor)
} }
case clay.RenderCommandType.Border: case .Border:
config := renderCommand.renderData.border config := render_command.renderData.border
// Left border // Left border
if (config.width.left > 0) { if config.width.left > 0 {
raylib.DrawRectangle( draw_rect(
cast(i32)math.round(boundingBox.x), bounds.x,
cast(i32)math.round(boundingBox.y + config.cornerRadius.topLeft), bounds.y + config.cornerRadius.topLeft,
cast(i32)config.width.left, f32(config.width.left),
cast(i32)math.round(boundingBox.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft), bounds.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft,
clayColorToRaylibColor(config.color), config.color,
) )
} }
// Right border // Right border
if (config.width.right > 0) { if config.width.right > 0 {
raylib.DrawRectangle( draw_rect(
cast(i32)math.round(boundingBox.x + boundingBox.width - cast(f32)config.width.right), bounds.x + bounds.width - f32(config.width.right),
cast(i32)math.round(boundingBox.y + config.cornerRadius.topRight), bounds.y + config.cornerRadius.topRight,
cast(i32)config.width.right, f32(config.width.right),
cast(i32)math.round(boundingBox.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight), bounds.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight,
clayColorToRaylibColor(config.color), config.color,
) )
} }
// Top border // Top border
if (config.width.top > 0) { if config.width.top > 0 {
raylib.DrawRectangle( draw_rect(
cast(i32)math.round(boundingBox.x + config.cornerRadius.topLeft), bounds.x + config.cornerRadius.topLeft,
cast(i32)math.round(boundingBox.y), bounds.y,
cast(i32)math.round(boundingBox.width - config.cornerRadius.topLeft - config.cornerRadius.topRight), bounds.width - config.cornerRadius.topLeft - config.cornerRadius.topRight,
cast(i32)config.width.top, f32(config.width.top),
clayColorToRaylibColor(config.color), config.color,
) )
} }
// Bottom border // Bottom border
if (config.width.bottom > 0) { if config.width.bottom > 0 {
raylib.DrawRectangle( draw_rect(
cast(i32)math.round(boundingBox.x + config.cornerRadius.bottomLeft), bounds.x + config.cornerRadius.bottomLeft,
cast(i32)math.round(boundingBox.y + boundingBox.height - cast(f32)config.width.bottom), bounds.y + bounds.height - f32(config.width.bottom),
cast(i32)math.round(boundingBox.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight), bounds.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight,
cast(i32)config.width.bottom, f32(config.width.bottom),
clayColorToRaylibColor(config.color), config.color,
) )
} }
if (config.cornerRadius.topLeft > 0) {
raylib.DrawRing( // Rounded Borders
raylib.Vector2{math.round(boundingBox.x + config.cornerRadius.topLeft), math.round(boundingBox.y + config.cornerRadius.topLeft)}, if config.cornerRadius.topLeft > 0 {
math.round(config.cornerRadius.topLeft - cast(f32)config.width.top), draw_arc(
bounds.x + config.cornerRadius.topLeft,
bounds.y + config.cornerRadius.topLeft,
config.cornerRadius.topLeft - f32(config.width.top),
config.cornerRadius.topLeft, config.cornerRadius.topLeft,
180, 180,
270, 270,
10, config.color,
clayColorToRaylibColor(config.color),
) )
} }
if (config.cornerRadius.topRight > 0) { if config.cornerRadius.topRight > 0 {
raylib.DrawRing( draw_arc(
raylib.Vector2{math.round(boundingBox.x + boundingBox.width - config.cornerRadius.topRight), math.round(boundingBox.y + config.cornerRadius.topRight)}, bounds.x + bounds.width - config.cornerRadius.topRight,
math.round(config.cornerRadius.topRight - cast(f32)config.width.top), bounds.y + config.cornerRadius.topRight,
config.cornerRadius.topRight - f32(config.width.top),
config.cornerRadius.topRight, config.cornerRadius.topRight,
270, 270,
360, 360,
10, config.color,
clayColorToRaylibColor(config.color),
) )
} }
if (config.cornerRadius.bottomLeft > 0) { if config.cornerRadius.bottomLeft > 0 {
raylib.DrawRing( draw_arc(
raylib.Vector2{math.round(boundingBox.x + config.cornerRadius.bottomLeft), math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft)}, bounds.x + config.cornerRadius.bottomLeft,
math.round(config.cornerRadius.bottomLeft - cast(f32)config.width.top), bounds.y + bounds.height - config.cornerRadius.bottomLeft,
config.cornerRadius.bottomLeft - f32(config.width.top),
config.cornerRadius.bottomLeft, config.cornerRadius.bottomLeft,
90, 90,
180, 180,
10, config.color,
clayColorToRaylibColor(config.color),
) )
} }
if (config.cornerRadius.bottomRight > 0) { if config.cornerRadius.bottomRight > 0 {
raylib.DrawRing( draw_arc(
raylib.Vector2 { bounds.x + bounds.width - config.cornerRadius.bottomRight,
math.round(boundingBox.x + boundingBox.width - config.cornerRadius.bottomRight), bounds.y + bounds.height - config.cornerRadius.bottomRight,
math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomRight), config.cornerRadius.bottomRight - f32(config.width.bottom),
},
math.round(config.cornerRadius.bottomRight - cast(f32)config.width.bottom),
config.cornerRadius.bottomRight, config.cornerRadius.bottomRight,
0.1, 0.1,
90, 90,
10, config.color,
clayColorToRaylibColor(config.color),
) )
} }
case clay.RenderCommandType.Custom: case clay.RenderCommandType.Custom:
// Implement custom element rendering here // Implement custom element rendering here
} }
} }
} }
// Helper procs, mainly for repeated conversions
@(private = "file")
draw_arc :: proc(x, y: f32, inner_rad, outer_rad: f32,start_angle, end_angle: f32, color: clay.Color){
rl.DrawRing(
{math.round(x),math.round(y)},
math.round(inner_rad),
outer_rad,
start_angle,
end_angle,
10,
clay_color_to_rl_color(color),
)
}
@(private = "file")
draw_rect :: proc(x, y, w, h: f32, color: clay.Color) {
rl.DrawRectangle(
i32(math.round(x)),
i32(math.round(y)),
i32(math.round(w)),
i32(math.round(h)),
clay_color_to_rl_color(color)
)
}
@(private = "file")
draw_rect_rounded :: proc(x,y,w,h: f32, radius: f32, color: clay.Color){
rl.DrawRectangleRounded({x,y,w,h},radius,8,clay_color_to_rl_color(color))
}