add function for drawing a blurred rectangle

This commit is contained in:
Rokas Puzonas 2024-12-29 04:14:22 +02:00
parent b80a356739
commit 522840ca22
2 changed files with 206 additions and 28 deletions

View File

@ -7,6 +7,9 @@ const UIStack = @import("./ui_stack.zig");
const RectUtils = @import("./rect_utils.zig");
const rl = @import("raylib");
const srcery = @import("./srcery.zig");
const rlgl_h = @cImport({
@cInclude("rlgl.h");
});
const assert = std.debug.assert;
@ -229,13 +232,181 @@ fn createOrGetRenderTexture(maybe_render_texture: *?rl.RenderTexture) !rl.Render
return maybe_render_texture.*.?;
}
fn drawRectangleRoundedUV(rect: rl.Rectangle, roundness: f32, color: rl.Color) void {
if (roundness == 0) {
rl.drawRectangleRec(rect, color);
// Modified version of `DrawRectangleRounded` where the UV texture coordiantes are consistent and align
fn drawRectangleRoundedUV(rec: rl.Rectangle, roundness: f32, color: rl.Color) void {
assert(roundness < 1);
if (roundness <= 0 or rec.width <= 1 or rec.height <= 1) {
rl.drawRectangleRec(rec, color);
return;
}
const radius: f32 = @min(rec.width, rec.height) * roundness / 2;
if (radius <= 0.0) return;
// Calculate the maximum angle between segments based on the error rate (usually 0.5f)
const smooth_circle_error_rate = 0.5;
const th: f32 = std.math.acos(2 * std.math.pow(f32, 1 - smooth_circle_error_rate / radius, 2) - 1);
var segments: i32 = @intFromFloat(@ceil(2 * std.math.pi / th) / 4.0);
if (segments <= 0) segments = 4;
const step_length = 90.0 / @as(f32, @floatFromInt(segments));
// Quick sketch to make sense of all of this,
// there are 9 parts to draw, also mark the 12 points we'll use
//
// P0____________________P1
// /| |\
// /1| 2 |3\
// P7 /__|____________________|__\ P2
// | |P8 P9| |
// | 8 | 9 | 4 |
// | __|____________________|__ |
// P6 \ |P11 P10| / P3
// \7| 6 |5/
// \|____________________|/
// P5 P4
// Coordinates of the 12 points that define the rounded rect
const radius_u = radius / rec.width;
const radius_v = radius / rec.height;
const points = [_]rl.Vector2{
.{ .x = radius_u , .y = 0 }, // P0
.{ .x = 1 - radius_u , .y = 0 }, // P1
.{ .x = 1 , .y = radius_v }, // P2
.{ .x = 1 , .y = 1 - radius_v }, // P3
.{ .x = 1 - radius_u , .y = 1 }, // P4
.{ .x = radius_u , .y = 1 }, // P5
.{ .x = 0 , .y = 1 - radius_v }, // P6
.{ .x = 0 , .y = radius_v }, // P7
.{ .x = radius_u , .y = radius_v }, // P8
.{ .x = 1 - radius_u , .y = radius_v }, // P9
.{ .x = 1 - radius_u , .y = 1 - radius_v }, // P10
.{ .x = radius_u , .y = 1 - radius_v }, // P11
};
const texture = rl.getShapesTexture();
const shape_rect = rl.getShapesTextureRectangle();
const texture_width: f32 = @floatFromInt(texture.width);
const texture_height: f32 = @floatFromInt(texture.height);
rl.gl.rlBegin(rlgl_h.RL_TRIANGLES);
defer rl.gl.rlEnd();
rl.gl.rlSetTexture(texture.id);
defer rl.gl.rlSetTexture(0);
// Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
const centers = [_]rl.Vector2{ points[8], points[9], points[10], points[11] };
const angles = [_]f32{ 180.0, 270.0, 0.0, 90.0 };
for (0..4) |k| {
var angle = angles[k];
const center = centers[k];
for (0..@intCast(segments)) |_| {
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
const rad_per_deg = std.math.rad_per_deg;
const triangle = .{
center,
.{
.x = center.x + @cos(rad_per_deg*(angle + step_length))*radius_u,
.y = center.y + @sin(rad_per_deg*(angle + step_length))*radius_v
},
.{
.x = center.x + @cos(rad_per_deg * angle)*radius_u,
.y = center.y + @sin(rad_per_deg * angle)*radius_v
}
};
inline for (triangle) |point| {
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
angle += step_length;
}
}
// [2] Upper Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 0, 8, 9, 1, 0, 9 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [4] Right Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 9, 10, 3, 2, 9, 3 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [6] Bottom Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 11, 5, 4, 10, 11, 4 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [8] Left Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 7, 6, 11, 8, 7, 11 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
// [9] Middle Rectangle
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
inline for (.{ 8, 11, 10, 9, 8, 10 }) |index| {
const point = points[index];
rl.gl.rlTexCoord2f(
(shape_rect.x + shape_rect.width * point.x) / texture_width,
(shape_rect.y + shape_rect.height * point.y) / texture_height
);
rl.gl.rlVertex2f(
rec.x + rec.width * point.x,
rec.y + rec.height * point.y
);
}
}
fn drawBlurredWorld(self: *App, rect: rl.Rectangle, roundness: f32, color: rl.Color) !void {
fn drawBlurredWorld(self: *App, rect: rl.Rectangle, color: rl.Color) !void {
const blur_both = try createOrGetRenderTexture(&self.blur_texture_both);
const previous_texture = rl.getShapesTexture();
@ -250,10 +421,35 @@ fn drawBlurredWorld(self: *App, rect: rl.Rectangle, roundness: f32, color: rl.Co
.height = -rect.height,
};
rl.setShapesTexture(blur_both.texture, shape_rect);
drawRectangleRoundedUV(rect, roundness, 0, color);
const border = 2;
const roundness = 0.2;
drawRectangleRoundedUV(rect, roundness, color);
rl.drawRectangleRoundedLinesEx(RectUtils.shrink(rect, border - 1, border - 1), roundness, 0, border, srcery.bright_white.alpha(0.3));
}
pub fn drawWorld(self: *App) !void {
pub fn drawWorld(self: *App) void {
rl.clearBackground(srcery.black);
rl.drawCircleV(rl.Vector2.zero(), 5, rl.Color.red);
const map_size = self.map_position_max.subtract(self.map_position_min);
for (0..@intCast(map_size.y)) |oy| {
for (0..@intCast(map_size.x)) |ox| {
const map_index = @as(usize, @intCast(map_size.x)) * oy + ox;
const x = self.map_position_min.x + @as(i64, @intCast(ox));
const y = self.map_position_min.y + @as(i64, @intCast(oy));
const texture_index = self.map_texture_indexes.items[map_index];
const texture = self.map_textures.items[texture_index].texture;
const tile_size = rl.Vector2.init(224, 224);
const position = rl.Vector2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size);
rl.drawTextureV(texture, position, rl.Color.white);
}
}
}
pub fn drawWorldAndBlur(self: *App) !void {
const screen_width = rl.getScreenWidth();
const screen_height = rl.getScreenHeight();
const screen_size = rl.Vector2.init(@floatFromInt(screen_width), @floatFromInt(screen_height));
@ -267,27 +463,10 @@ pub fn drawWorld(self: *App) !void {
blur_original.begin();
defer blur_original.end();
rl.clearBackground(rl.Color.black.alpha(0));
self.camera.begin();
defer self.camera.end();
rl.drawCircleV(rl.Vector2.zero(), 5, rl.Color.red);
const map_size = self.map_position_max.subtract(self.map_position_min);
for (0..@intCast(map_size.y)) |oy| {
for (0..@intCast(map_size.x)) |ox| {
const map_index = @as(usize, @intCast(map_size.x)) * oy + ox;
const x = self.map_position_min.x + @as(i64, @intCast(ox));
const y = self.map_position_min.y + @as(i64, @intCast(oy));
const texture_index = self.map_texture_indexes.items[map_index];
const texture = self.map_textures.items[texture_index].texture;
const tile_size = rl.Vector2.init(224, 224);
const position = rl.Vector2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size);
rl.drawTextureV(texture, position, rl.Color.white);
}
}
self.drawWorld();
}
// 2 pass. Apply horizontal blur
@ -421,12 +600,11 @@ pub fn tick(self: *App) !void {
rl.clearBackground(srcery.black);
try self.drawWorld();
try self.drawWorldAndBlur();
try self.drawBlurredWorld(
.{ .x = 20, .y = 20, .width = 200, .height = 200 },
0.5,
rl.Color.gray
srcery.xgray10
);
rl.drawFPS(

View File

@ -114,7 +114,7 @@ pub fn main() anyerror!void {
rl.setWindowMinSize(200, 200);
rl.setWindowState(.{
.vsync_hint = true,
// .window_resizable = true
.window_resizable = true
});
var app = try App.init(allocator, &artificer);