add tilemap
This commit is contained in:
parent
5be299ad4c
commit
61e5edb8cf
23
src/assets/kenney-micro-roguelike/License.txt
Normal file
23
src/assets/kenney-micro-roguelike/License.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Micro Roguelike (1.4)
|
||||||
|
|
||||||
|
Created/distributed by Kenney (www.kenney.nl)
|
||||||
|
Creation date: 01-11-2021
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
License: (Creative Commons Zero, CC0)
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
This content is free to use in personal, educational and commercial projects.
|
||||||
|
|
||||||
|
Support us by crediting Kenney or www.kenney.nl (this is not mandatory)
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Donate: http://support.kenney.nl
|
||||||
|
Patreon: http://patreon.com/kenney/
|
||||||
|
|
||||||
|
Follow on Twitter for updates:
|
||||||
|
http://twitter.com/KenneyNL
|
||||||
BIN
src/assets/kenney-micro-roguelike/colored_tilemap_packed.png
Normal file
BIN
src/assets/kenney-micro-roguelike/colored_tilemap_packed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
@ -23,7 +23,7 @@ pub const Input = struct {
|
|||||||
move_right: Window.KeyState,
|
move_right: Window.KeyState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const tile_size = Vec2.init(10, 10);
|
const tile_size = Vec2.init(8, 8);
|
||||||
|
|
||||||
canvas_size: Vec2,
|
canvas_size: Vec2,
|
||||||
entities: Entity.List,
|
entities: Entity.List,
|
||||||
@ -37,7 +37,7 @@ last_right_repeat_at: ?f64 = null,
|
|||||||
|
|
||||||
pub fn init(gpa: Allocator) !Game {
|
pub fn init(gpa: Allocator) !Game {
|
||||||
var game = Game{
|
var game = Game{
|
||||||
.canvas_size = .init(320, 180),
|
.canvas_size = (Vec2.init(20, 15)).multiply(tile_size),
|
||||||
.entities = .empty,
|
.entities = .empty,
|
||||||
.timers = .empty,
|
.timers = .empty,
|
||||||
.last_move = .init(0, 0)
|
.last_move = .init(0, 0)
|
||||||
@ -129,7 +129,7 @@ pub fn tick(self: *Game, input: Input) !void {
|
|||||||
// entity.position = entity.position.add(velocity.multiplyScalar(input.dt));
|
// entity.position = entity.position.add(velocity.multiplyScalar(input.dt));
|
||||||
entity.position = entity.position.add(move.multiply(tile_size));
|
entity.position = entity.position.add(move.multiply(tile_size));
|
||||||
|
|
||||||
Gfx.drawRectangle(entity.position, .init(10, 10), rgb(255, 0, 0));
|
Gfx.drawTileById(.player, entity.position, tile_size, rgb(255, 255, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
518
src/graphics.zig
518
src/graphics.zig
@ -451,8 +451,11 @@ pub const Font = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const ImageId = enum {
|
pub const ImageId = enum {
|
||||||
// package,
|
tilemap
|
||||||
// trash
|
};
|
||||||
|
|
||||||
|
pub const TileId = enum {
|
||||||
|
player
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Borders = struct {
|
pub const Borders = struct {
|
||||||
@ -659,7 +662,13 @@ var font_id_map: std.EnumArray(FontVariant, c_int) = .initFill(c.FONS_INVALID);
|
|||||||
|
|
||||||
var image_map: std.EnumArray(ImageId, sg.Image) = .initFill(.{});
|
var image_map: std.EnumArray(ImageId, sg.Image) = .initFill(.{});
|
||||||
var image_view_map: std.EnumArray(ImageId, sg.View) = .initFill(.{});
|
var image_view_map: std.EnumArray(ImageId, sg.View) = .initFill(.{});
|
||||||
|
|
||||||
var linear_sampler: sg.Sampler = .{};
|
var linear_sampler: sg.Sampler = .{};
|
||||||
|
var nearest_sampler: sg.Sampler = .{};
|
||||||
|
|
||||||
|
var tile_coords: std.EnumArray(TileId, Vec2) = .initUndefined();
|
||||||
|
const tile_size: Vec2 = .init(8, 8);
|
||||||
|
var tilemap_size: Vec2 = .init(0, 0);
|
||||||
|
|
||||||
const Options = struct {
|
const Options = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
@ -679,38 +688,47 @@ fn loadEmbededFont(fs: ?*c.FONScontext, name: [*c]const u8, comptime path: []co
|
|||||||
return font_id;
|
return font_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeImageFromMemory(image_datas: []const []const u8) !sg.Image {
|
const ImageData = struct {
|
||||||
var stbi_images_buffer: [16][*c]u8 = undefined;
|
rgba8_pixels: [*c]u8,
|
||||||
var stbi_images: std.ArrayListUnmanaged([*c]u8) = .initBuffer(&stbi_images_buffer);
|
width: u32,
|
||||||
defer {
|
height: u32,
|
||||||
for (stbi_images.items) |stbi_image| {
|
|
||||||
c.stbi_image_free(stbi_image);
|
fn load(png_data: []const u8) !ImageData {
|
||||||
}
|
var width: c_int = undefined;
|
||||||
|
var height: c_int = undefined;
|
||||||
|
const pixels = c.stbi_load_from_memory(png_data.ptr, @intCast(png_data.len), &width, &height, null, 4);
|
||||||
|
if (pixels == null) {
|
||||||
|
return error.InvalidPng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ImageData{
|
||||||
|
.rgba8_pixels = pixels,
|
||||||
|
.width = @intCast(width),
|
||||||
|
.height = @intCast(height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *const ImageData) void {
|
||||||
|
c.stbi_image_free(self.rgba8_pixels);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn makeImageWithMipMaps(image_datas: []const ImageData) !sg.Image {
|
||||||
var mip_levels_buffer = [_]sg.Range{.{}} ** 16;
|
var mip_levels_buffer = [_]sg.Range{.{}} ** 16;
|
||||||
var mip_levels: std.ArrayListUnmanaged(sg.Range) = .initBuffer(&mip_levels_buffer);
|
var mip_levels: std.ArrayListUnmanaged(sg.Range) = .initBuffer(&mip_levels_buffer);
|
||||||
|
|
||||||
var image_width: c_int = -1;
|
var image_width: c_int = -1;
|
||||||
var image_height: c_int = -1;
|
var image_height: c_int = -1;
|
||||||
|
|
||||||
for (image_datas) |image_data| {
|
for (image_datas) |mipmap_image| {
|
||||||
var width: c_int = undefined;
|
|
||||||
var height: c_int = undefined;
|
|
||||||
const pixels = c.stbi_load_from_memory(image_data.ptr, @intCast(image_data.len), &width, &height, null, 4);
|
|
||||||
if (pixels == null) {
|
|
||||||
return error.InvalidPng;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image_height == -1) {
|
if (image_height == -1) {
|
||||||
image_width = width;
|
image_width = @intCast(mipmap_image.width);
|
||||||
image_height = height;
|
image_height = @intCast(mipmap_image.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
try stbi_images.appendBounded(pixels);
|
|
||||||
try mip_levels.appendBounded(.{
|
try mip_levels.appendBounded(.{
|
||||||
.ptr = pixels,
|
.ptr = mipmap_image.rgba8_pixels,
|
||||||
.size = @intCast(width * height * 4)
|
.size = mipmap_image.width * mipmap_image.height * 4
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,6 +753,31 @@ fn makeImageFromMemory(image_datas: []const []const u8) !sg.Image {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn makeImageFromMemory(image_datas: []const []const u8) !sg.Image {
|
||||||
|
var stbi_images_buffer: [16]ImageData = undefined;
|
||||||
|
var stbi_images: std.ArrayListUnmanaged(ImageData) = .initBuffer(&stbi_images_buffer);
|
||||||
|
defer {
|
||||||
|
for (stbi_images.items) |image| {
|
||||||
|
image.deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image_datas.len > stbi_images.capacity) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (image_datas) |image_data| {
|
||||||
|
const mipmap_image = try ImageData.load(image_data);
|
||||||
|
stbi_images.appendAssumeCapacity(mipmap_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
return try makeImageWithMipMaps(stbi_images.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tileCoordToQuad(coord: Vec2) Rect {
|
||||||
|
_ = coord; // autofix
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(options: Options) !void {
|
pub fn init(options: Options) !void {
|
||||||
dpi_scale = sapp.dpiScale();
|
dpi_scale = sapp.dpiScale();
|
||||||
|
|
||||||
@ -792,32 +835,30 @@ pub fn init(options: Options) !void {
|
|||||||
.bold = loadEmbededFont(g_fons_context, "bold", "./assets/roboto-font/Roboto-Bold.ttf"),
|
.bold = loadEmbededFont(g_fons_context, "bold", "./assets/roboto-font/Roboto-Bold.ttf"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Generate mipmap from SVG.
|
const tilemap = try ImageData.load(@embedFile("./assets/kenney-micro-roguelike/colored_tilemap_packed.png"));
|
||||||
// image_map = .init(.{
|
defer tilemap.deinit();
|
||||||
// .package = try makeImageFromMemory(&.{
|
|
||||||
// // https://www.iconfinder.com/icons/9026684/package_thin_icon
|
|
||||||
// @embedFile("../assets/icons/package-512x512.png"),
|
|
||||||
// @embedFile("../assets/icons/package-256x256.png"),
|
|
||||||
// @embedFile("../assets/icons/package-128x128.png"),
|
|
||||||
// @embedFile("../assets/icons/package-64x64.png"),
|
|
||||||
// @embedFile("../assets/icons/package-32x32.png"),
|
|
||||||
// }),
|
|
||||||
// .trash = try makeImageFromMemory(&.{
|
|
||||||
// // TODO: Provide attribution for image
|
|
||||||
// // https://www.iconfinder.com/icons/9027022/trash_thin_icon
|
|
||||||
// @embedFile("../assets/icons/trash-32x32.png"),
|
|
||||||
// })
|
|
||||||
// });
|
|
||||||
|
|
||||||
// var image_iter = image_map.iterator();
|
image_map = .init(.{
|
||||||
// while (image_iter.next()) |entry| {
|
.tilemap = try makeImageWithMipMaps(&.{
|
||||||
// const image_view = sg.makeView(.{
|
tilemap
|
||||||
// .texture = .{ .image = entry.value.* }
|
})
|
||||||
// });
|
});
|
||||||
// assert(image_view.id != sg.invalid_id);
|
|
||||||
//
|
tilemap_size = Vec2.init(@floatFromInt(tilemap.width), @floatFromInt(tilemap.height));
|
||||||
// image_view_map.set(entry.key, image_view);
|
|
||||||
// }
|
tile_coords = .init(.{
|
||||||
|
.player = .init(4, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
var image_iter = image_map.iterator();
|
||||||
|
while (image_iter.next()) |entry| {
|
||||||
|
const image_view = sg.makeView(.{
|
||||||
|
.texture = .{ .image = entry.value.* }
|
||||||
|
});
|
||||||
|
assert(image_view.id != sg.invalid_id);
|
||||||
|
|
||||||
|
image_view_map.set(entry.key, image_view);
|
||||||
|
}
|
||||||
|
|
||||||
linear_sampler = sg.makeSampler(.{
|
linear_sampler = sg.makeSampler(.{
|
||||||
.min_filter = .LINEAR,
|
.min_filter = .LINEAR,
|
||||||
@ -825,6 +866,13 @@ pub fn init(options: Options) !void {
|
|||||||
.mipmap_filter = .LINEAR,
|
.mipmap_filter = .LINEAR,
|
||||||
.label = "linear-sampler",
|
.label = "linear-sampler",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nearest_sampler = sg.makeSampler(.{
|
||||||
|
.min_filter = .NEAREST,
|
||||||
|
.mag_filter = .NEAREST,
|
||||||
|
.mipmap_filter = .NEAREST,
|
||||||
|
.label = "nearest-sampler",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
@ -883,16 +931,17 @@ pub fn drawRectangle(pos: Vec2, size: Vec2, color: Vec4) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawImage(image_id: ImageId, pos: Vec2, size: Vec2, tint: Vec4) void {
|
pub fn drawTile(tile_coord: Vec2, pos: Vec2, size: Vec2, tint: Vec4) void {
|
||||||
sgl.enableTexture();
|
sgl.enableTexture();
|
||||||
defer sgl.disableTexture();
|
defer sgl.disableTexture();
|
||||||
|
|
||||||
|
|
||||||
sgl.texture(
|
sgl.texture(
|
||||||
image_view_map.get(image_id),
|
image_view_map.get(.tilemap),
|
||||||
linear_sampler
|
nearest_sampler
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const tile_quad = Rect.init(tile_coord.x, tile_coord.y, 1, 1).multiply(tile_size).divide(tilemap_size);
|
||||||
|
|
||||||
const top_left = pos;
|
const top_left = pos;
|
||||||
const top_right = pos.add(.{ .x = size.x, .y = 0 });
|
const top_right = pos.add(.{ .x = size.x, .y = 0 });
|
||||||
const bottom_right = pos.add(size);
|
const bottom_right = pos.add(size);
|
||||||
@ -901,352 +950,39 @@ pub fn drawImage(image_id: ImageId, pos: Vec2, size: Vec2, tint: Vec4) void {
|
|||||||
sgl.beginQuads();
|
sgl.beginQuads();
|
||||||
defer sgl.end();
|
defer sgl.end();
|
||||||
|
|
||||||
v2fT2Color(top_left.x, top_left.y, 0, 0, tint);
|
v2fT2Color(
|
||||||
v2fT2Color(top_right.x, top_right.y, 1, 0, tint);
|
top_left.x,
|
||||||
v2fT2Color(bottom_right.x, bottom_right.y, 1, 1, tint);
|
top_left.y,
|
||||||
v2fT2Color(bottom_left.x, bottom_left.y, 0, 1, tint);
|
tile_quad.left(),
|
||||||
}
|
tile_quad.top(),
|
||||||
|
tint
|
||||||
fn getCircleSegmentCount(radius: f32, from_angle: f32, to_angle: f32) usize {
|
|
||||||
const pi2 = 2 * std.math.pi;
|
|
||||||
|
|
||||||
const C = pi2 * radius;
|
|
||||||
const C_segment = C * (@abs(to_angle - from_angle) / pi2);
|
|
||||||
|
|
||||||
return @intFromFloat(C_segment / circle_quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawCorner(pos: Vec2, radius: f32, color: Vec4, from_angle: f32, to_angle: f32) void {
|
|
||||||
sgl.beginTriangles();
|
|
||||||
defer sgl.end();
|
|
||||||
|
|
||||||
const detail = getCircleSegmentCount(radius, from_angle, to_angle);
|
|
||||||
|
|
||||||
// TODO: Use precomputed angles
|
|
||||||
for (0..detail) |i| {
|
|
||||||
const angle = std.math.lerp(from_angle, to_angle, @as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(detail)));
|
|
||||||
const angle1 = std.math.lerp(from_angle, to_angle, @as(f32, @floatFromInt(i + 1)) / @as(f32, @floatFromInt(detail)));
|
|
||||||
const x = @cos(angle);
|
|
||||||
const y = @sin(angle);
|
|
||||||
const x1 = @cos(angle1);
|
|
||||||
const y1 = @sin(angle1);
|
|
||||||
|
|
||||||
v2fColor(pos.x , pos.y , color);
|
|
||||||
v2fColor(pos.x + x * radius, pos.y + y * radius, color);
|
|
||||||
v2fColor(pos.x + x1 * radius, pos.y + y1 * radius, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawOutlineCornerSegment(
|
|
||||||
pos: Vec2,
|
|
||||||
color: Vec4,
|
|
||||||
outer_radius: f32, inner_radius: f32,
|
|
||||||
from_angle: f32, to_angle: f32
|
|
||||||
) void {
|
|
||||||
const zone = tracy.initZone(@src(), .{ });
|
|
||||||
defer zone.deinit();
|
|
||||||
|
|
||||||
var detail = getCircleSegmentCount(outer_radius, from_angle, to_angle);
|
|
||||||
detail = @max(detail, 1);
|
|
||||||
|
|
||||||
// TODO: Use precomputed angles
|
|
||||||
for (0..detail) |i| {
|
|
||||||
const angle = std.math.lerp(from_angle, to_angle, @as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(detail)));
|
|
||||||
const angle1 = std.math.lerp(from_angle, to_angle, @as(f32, @floatFromInt(i + 1)) / @as(f32, @floatFromInt(detail)));
|
|
||||||
|
|
||||||
const circle_point0 = Vec2.init(@cos(angle), @sin(angle));
|
|
||||||
const circle_point1 = Vec2.init(@cos(angle1), @sin(angle1));
|
|
||||||
|
|
||||||
drawQuad(
|
|
||||||
.{
|
|
||||||
pos.add(circle_point1.multiplyScalar(outer_radius)),
|
|
||||||
pos.add(circle_point0.multiplyScalar(outer_radius)),
|
|
||||||
pos.add(circle_point0.multiplyScalar(@max(0, inner_radius))),
|
|
||||||
pos.add(circle_point1.multiplyScalar(@max(0, inner_radius))),
|
|
||||||
},
|
|
||||||
color
|
|
||||||
);
|
);
|
||||||
}
|
v2fT2Color(
|
||||||
}
|
top_right.x,
|
||||||
|
top_right.y,
|
||||||
pub fn drawRoundedRectangle(pos: Vec2, size: Vec2, color: Vec4, corners: Corners) void {
|
tile_quad.right(),
|
||||||
const zone = tracy.initZone(@src(), .{ });
|
tile_quad.top(),
|
||||||
defer zone.deinit();
|
tint
|
||||||
|
|
||||||
const left = pos.x;
|
|
||||||
const right = pos.x + size.x;
|
|
||||||
const top = pos.y;
|
|
||||||
const bottom = pos.y + size.y;
|
|
||||||
|
|
||||||
const tr = corners.top_right;
|
|
||||||
const tl = corners.top_left;
|
|
||||||
const bl = corners.bottom_left;
|
|
||||||
const br = corners.bottom_right;
|
|
||||||
|
|
||||||
{
|
|
||||||
sgl.beginQuads();
|
|
||||||
defer sgl.end();
|
|
||||||
|
|
||||||
vertexesQuad(
|
|
||||||
Vec2.init(left + tl, top),
|
|
||||||
Vec2.init(right - tr, top),
|
|
||||||
Vec2.init(right - tr, top + tr),
|
|
||||||
Vec2.init(left + tl, top + tl),
|
|
||||||
color
|
|
||||||
);
|
);
|
||||||
|
v2fT2Color(
|
||||||
vertexesQuad(
|
bottom_right.x,
|
||||||
Vec2.init(right - tr, top + tr),
|
bottom_right.y,
|
||||||
Vec2.init(right, top + tr),
|
tile_quad.right(),
|
||||||
Vec2.init(right, bottom - br),
|
tile_quad.bottom(),
|
||||||
Vec2.init(right - br, bottom - br),
|
tint
|
||||||
color
|
|
||||||
);
|
);
|
||||||
|
v2fT2Color(
|
||||||
vertexesQuad(
|
bottom_left.x,
|
||||||
Vec2.init(left + bl, bottom - bl),
|
bottom_left.y,
|
||||||
Vec2.init(right - br, bottom - br),
|
tile_quad.left(),
|
||||||
Vec2.init(right - br, bottom),
|
tile_quad.bottom(),
|
||||||
Vec2.init(left + bl, bottom),
|
tint
|
||||||
color
|
|
||||||
);
|
|
||||||
|
|
||||||
vertexesQuad(
|
|
||||||
Vec2.init(left, top + tl),
|
|
||||||
Vec2.init(left + tl, top + tl),
|
|
||||||
Vec2.init(left + bl, bottom - bl),
|
|
||||||
Vec2.init(left, bottom - bl),
|
|
||||||
color
|
|
||||||
);
|
|
||||||
|
|
||||||
vertexesQuad(
|
|
||||||
Vec2.init(left + tl, top + tl),
|
|
||||||
Vec2.init(right - tr, top + tr),
|
|
||||||
Vec2.init(right - br, bottom - br),
|
|
||||||
Vec2.init(left + bl, bottom - bl),
|
|
||||||
color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawCorner(
|
|
||||||
Vec2.init(right - tr, top + tr),
|
|
||||||
tr,
|
|
||||||
color,
|
|
||||||
std.math.pi * (3.0 / 2.0),
|
|
||||||
std.math.pi * 2.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
drawCorner(
|
|
||||||
Vec2.init(left + tl, top + tl),
|
|
||||||
tl,
|
|
||||||
color,
|
|
||||||
std.math.pi,
|
|
||||||
std.math.pi * (3.0 / 2.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
drawCorner(
|
|
||||||
Vec2.init(left + bl, bottom - bl),
|
|
||||||
bl,
|
|
||||||
color,
|
|
||||||
std.math.pi,
|
|
||||||
std.math.pi * (1.0 / 2.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
drawCorner(
|
|
||||||
Vec2.init(right - br, bottom - br),
|
|
||||||
br,
|
|
||||||
color,
|
|
||||||
0,
|
|
||||||
std.math.pi * (1.0 / 2.0)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const OutlineCorner = struct {
|
pub fn drawTileById(tile_id: TileId, pos: Vec2, size: Vec2, tint: Vec4) void {
|
||||||
point: Vec2,
|
const tile_coord = tile_coords.get(tile_id);
|
||||||
corner_radius: f32,
|
drawTile(tile_coord, pos, size, tint);
|
||||||
dir_to_center: Vec2,
|
|
||||||
|
|
||||||
pub fn getCircleCenter(self: OutlineCorner) Vec2 {
|
|
||||||
return self.point.add(self.dir_to_center.multiplyScalar(self.corner_radius));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getEdge(self: OutlineCorner, segment: OutlineSegment, border: f32) [2]Vec2 {
|
|
||||||
const inward_sign = switch (segment.axis) {
|
|
||||||
.X => self.dir_to_center.x,
|
|
||||||
.Y => self.dir_to_center.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
var bottom_left = self.point;
|
|
||||||
if (segment.axis == .X) {
|
|
||||||
bottom_left.x += self.corner_radius * inward_sign;
|
|
||||||
} else {
|
|
||||||
bottom_left.y += self.corner_radius * inward_sign;
|
|
||||||
}
|
|
||||||
|
|
||||||
var top_left = bottom_left.add(segment.inward_direction.multiplyScalar(border));
|
|
||||||
if (segment.axis == .X) {
|
|
||||||
top_left.x += @max(border - self.corner_radius, 0) * inward_sign;
|
|
||||||
} else {
|
|
||||||
top_left.y += @max(border - self.corner_radius, 0) * inward_sign;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ bottom_left, top_left };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(self: OutlineCorner, segment: OutlineSegment, border_size: f32, corner_angle_sign: f32, edge: [2]Vec2,) void {
|
|
||||||
if (self.corner_radius > 0) {
|
|
||||||
const center = self.getCircleCenter();
|
|
||||||
|
|
||||||
const inner_radius = self.corner_radius - border_size;
|
|
||||||
if (inner_radius < 0) {
|
|
||||||
drawTriangle(
|
|
||||||
.{
|
|
||||||
edge[0],
|
|
||||||
edge[1],
|
|
||||||
center
|
|
||||||
},
|
|
||||||
segment.color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawOutlineCornerSegment(
|
|
||||||
center,
|
|
||||||
segment.color,
|
|
||||||
self.corner_radius,
|
|
||||||
inner_radius,
|
|
||||||
segment.outward_angle,
|
|
||||||
segment.outward_angle + corner_angle_sign * std.math.pi / 4.0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const OutlineSegment = struct {
|
|
||||||
// TODO: Figure out a way to remove `lower_side_angle_dir` and `upper_side_angle_dir`?
|
|
||||||
|
|
||||||
lower_side: OutlineCorner,
|
|
||||||
lower_side_angle_dir: f32,
|
|
||||||
upper_side: OutlineCorner,
|
|
||||||
upper_side_angle_dir: f32,
|
|
||||||
|
|
||||||
outward_angle: f32,
|
|
||||||
color: Vec4,
|
|
||||||
axis: enum { X, Y },
|
|
||||||
inward_direction: Vec2,
|
|
||||||
|
|
||||||
pub fn getLowerEdge(self: OutlineSegment, border: f32) [2]Vec2 {
|
|
||||||
return self.lower_side.getEdge(self, border);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getUpperEdge(self: OutlineSegment, border: f32) [2]Vec2 {
|
|
||||||
return self.upper_side.getEdge(self, border);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(self: OutlineSegment, border: f32) void {
|
|
||||||
if (self.color.w != 0) {
|
|
||||||
const lower_edge = self.getLowerEdge(border);
|
|
||||||
const upper_edge = self.getUpperEdge(border);
|
|
||||||
|
|
||||||
drawQuad(
|
|
||||||
.{
|
|
||||||
lower_edge[0],
|
|
||||||
lower_edge[1],
|
|
||||||
|
|
||||||
upper_edge[1],
|
|
||||||
upper_edge[0],
|
|
||||||
},
|
|
||||||
self.color
|
|
||||||
);
|
|
||||||
|
|
||||||
self.lower_side.draw(self, border, self.lower_side_angle_dir, lower_edge);
|
|
||||||
self.upper_side.draw(self, border, self.upper_side_angle_dir, upper_edge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn drawRectangleOutlineRounded(pos: Vec2, size: Vec2, corners: Corners, borders: Borders) void {
|
|
||||||
const zone = tracy.initZone(@src(), .{ });
|
|
||||||
defer zone.deinit();
|
|
||||||
|
|
||||||
if (borders.size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const left = pos.x;
|
|
||||||
const right = pos.x+size.x;
|
|
||||||
const top = pos.y;
|
|
||||||
const bottom = pos.y+size.y;
|
|
||||||
|
|
||||||
const bottom_left_corner = OutlineCorner{
|
|
||||||
.point = Vec2.init(left, bottom),
|
|
||||||
.corner_radius = corners.bottom_left,
|
|
||||||
.dir_to_center = Vec2.init(1, -1),
|
|
||||||
};
|
|
||||||
|
|
||||||
const bottom_right_corner = OutlineCorner{
|
|
||||||
.point = Vec2.init(right, bottom),
|
|
||||||
.corner_radius = corners.bottom_right,
|
|
||||||
.dir_to_center = Vec2.init(-1, -1),
|
|
||||||
};
|
|
||||||
|
|
||||||
const top_right_corner = OutlineCorner{
|
|
||||||
.point = Vec2.init(right, top),
|
|
||||||
.corner_radius = corners.top_right,
|
|
||||||
.dir_to_center = Vec2.init(-1, 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
const top_left_corner = OutlineCorner{
|
|
||||||
.point = Vec2.init(left, top),
|
|
||||||
.corner_radius = corners.top_left,
|
|
||||||
.dir_to_center = Vec2.init(1, 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
const segments = .{
|
|
||||||
OutlineSegment{
|
|
||||||
.lower_side = bottom_left_corner,
|
|
||||||
.lower_side_angle_dir = 1,
|
|
||||||
.upper_side = bottom_right_corner,
|
|
||||||
.upper_side_angle_dir = -1,
|
|
||||||
.outward_angle = std.math.pi * 1.0 / 2.0,
|
|
||||||
.color = borders.bottom,
|
|
||||||
.axis = .X,
|
|
||||||
.inward_direction = Vec2.init(0, -1)
|
|
||||||
},
|
|
||||||
OutlineSegment{
|
|
||||||
.lower_side = top_left_corner,
|
|
||||||
.lower_side_angle_dir = -1,
|
|
||||||
.upper_side = top_right_corner,
|
|
||||||
.upper_side_angle_dir = 1,
|
|
||||||
.outward_angle = std.math.pi * 3.0 / 2.0,
|
|
||||||
.color = borders.top,
|
|
||||||
.axis = .X,
|
|
||||||
.inward_direction = Vec2.init(0, 1)
|
|
||||||
},
|
|
||||||
OutlineSegment{
|
|
||||||
.lower_side = top_left_corner,
|
|
||||||
.lower_side_angle_dir = 1,
|
|
||||||
.upper_side = bottom_left_corner,
|
|
||||||
.upper_side_angle_dir = -1,
|
|
||||||
.outward_angle = std.math.pi,
|
|
||||||
.color = borders.left,
|
|
||||||
.axis = .Y,
|
|
||||||
.inward_direction = Vec2.init(1, 0)
|
|
||||||
},
|
|
||||||
OutlineSegment{
|
|
||||||
.lower_side = top_right_corner,
|
|
||||||
.lower_side_angle_dir = -1,
|
|
||||||
.upper_side = bottom_right_corner,
|
|
||||||
.upper_side_angle_dir = 1,
|
|
||||||
.outward_angle = 0,
|
|
||||||
.color = borders.right,
|
|
||||||
.axis = .Y,
|
|
||||||
.inward_direction = Vec2.init(-1, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline for (segments) |segment| {
|
|
||||||
segment.draw(borders.size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawRectanglOutline(pos: Vec2, size: Vec2, color: Vec4, width: f32) void {
|
pub fn drawRectanglOutline(pos: Vec2, size: Vec2, color: Vec4, width: f32) void {
|
||||||
|
|||||||
14
src/math.zig
14
src/math.zig
@ -351,6 +351,20 @@ pub const Rect = struct {
|
|||||||
return self.pos.y + self.size.y;
|
return self.pos.y + self.size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn multiply(self: Rect, xy: Vec2) Rect {
|
||||||
|
return Rect{
|
||||||
|
.pos = self.pos.multiply(xy),
|
||||||
|
.size = self.size.multiply(xy),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide(self: Rect, xy: Vec2) Rect {
|
||||||
|
return Rect{
|
||||||
|
.pos = self.pos.divide(xy),
|
||||||
|
.size = self.size.divide(xy),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn isInside(self: Rect, pos: Vec2) bool {
|
pub fn isInside(self: Rect, pos: Vec2) bool {
|
||||||
const x_overlap = self.pos.x <= pos.x and pos.x < self.pos.x + self.size.x;
|
const x_overlap = self.pos.x <= pos.x and pos.x < self.pos.x + self.size.x;
|
||||||
const y_overlap = self.pos.y <= pos.y and pos.y < self.pos.y + self.size.y;
|
const y_overlap = self.pos.y <= pos.y and pos.y < self.pos.y + self.size.y;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user