mach/examples/gkurve/draw.zig
Stephen Gutekanst 0a8e22bb49 examples: import mach-examples@20ceb359231ff284cf343dddba8cf25112ffe717
Helps hexops/mach#1165

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2024-03-06 11:08:19 -07:00

150 lines
4.9 KiB
Zig

const std = @import("std");
const mach = @import("mach");
const App = @import("main.zig").App;
const gpu = mach.gpu;
const math = mach.math;
const AtlasUV = mach.gfx.Atlas.Region.UV;
const Mat4x4 = math.Mat4x4;
const vec3 = math.vec3;
const Vec2 = @Vector(2, f32);
pub const Vertex = struct {
pos: @Vector(4, f32),
uv: Vec2,
};
const VERTEX_ATTRIBUTES = [_]gpu.VertexAttribute{
.{ .format = .float32x4, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 1 },
};
pub const VERTEX_BUFFER_LAYOUT = gpu.VertexBufferLayout{
.array_stride = @sizeOf(Vertex),
.step_mode = .vertex,
.attribute_count = VERTEX_ATTRIBUTES.len,
.attributes = &VERTEX_ATTRIBUTES,
};
pub const VertexUniform = Mat4x4;
const GkurveType = enum(u32) {
quadratic_convex = 0,
semicircle_convex = 1,
quadratic_concave = 2,
semicircle_concave = 3,
triangle = 4,
};
pub const FragUniform = struct {
type: GkurveType = .triangle,
// Padding for struct alignment to 16 bytes (minimum in WebGPU uniform).
padding: @Vector(3, f32) = undefined,
blend_color: @Vector(4, f32) = @Vector(4, f32){ 1, 1, 1, 1 },
};
pub fn equilateralTriangle(app: *App, position: Vec2, scale: f32, uniform: FragUniform, uv: AtlasUV, height_scale: f32) !void {
const triangle_height = scale * @sqrt(0.75) * height_scale;
try app.vertices.appendSlice(&[3]Vertex{
.{
.pos = .{ position[0] + scale / 2, position[1] + triangle_height, 0, 1 },
.uv = .{ uv.x + uv.width * 0.5, uv.y + uv.height },
},
.{
.pos = .{ position[0], position[1], 0, 1 },
.uv = .{ uv.x, uv.y },
},
.{
.pos = .{ position[0] + scale, position[1], 0, 1 },
.uv = .{ uv.x + uv.width, uv.y },
},
});
try app.fragment_uniform_list.append(uniform);
app.update_vertex_buffer = true;
app.update_frag_uniform_buffer = true;
}
pub fn quad(app: *App, position: Vec2, scale: Vec2, uniform: FragUniform, uv: AtlasUV) !void {
const bottom_left_uv = Vec2{ uv.x, uv.y };
const bottom_right_uv = Vec2{ uv.x + uv.width, uv.y };
const top_left_uv = Vec2{ uv.x, uv.y + uv.height };
const top_right_uv = Vec2{ uv.x + uv.width, uv.y + uv.height };
try app.vertices.appendSlice(&[6]Vertex{
.{ .pos = .{ position[0], position[1] + scale[1], 0, 1 }, .uv = top_left_uv },
.{ .pos = .{ position[0], position[1], 0, 1 }, .uv = bottom_left_uv },
.{ .pos = .{ position[0] + scale[0], position[1], 0, 1 }, .uv = bottom_right_uv },
.{ .pos = .{ position[0] + scale[0], position[1] + scale[1], 0, 1 }, .uv = top_right_uv },
.{ .pos = .{ position[0], position[1] + scale[1], 0, 1 }, .uv = top_left_uv },
.{ .pos = .{ position[0] + scale[0], position[1], 0, 1 }, .uv = bottom_right_uv },
});
try app.fragment_uniform_list.appendSlice(&.{ uniform, uniform });
app.update_vertex_buffer = true;
app.update_frag_uniform_buffer = true;
}
pub fn circle(app: *App, position: Vec2, radius: f32, blend_color: @Vector(4, f32), uv: AtlasUV) !void {
const low_mid = Vertex{
.pos = .{ position[0], position[1] - (radius * 2.0), 0, 1 },
.uv = .{ uv.x + uv.width * 0.5, uv.y },
};
const high_mid = Vertex{
.pos = .{ position[0], position[1] + (radius * 2.0), 0, 1 },
.uv = .{ uv.x + uv.width * 0.5, uv.y + uv.height },
};
const mid_left = Vertex{
.pos = .{ position[0] - radius, position[1], 0, 1 },
.uv = .{ uv.x, uv.y + uv.height * 0.5 },
};
const mid_right = Vertex{
.pos = .{ position[0] + radius, position[1], 0, 1 },
.uv = .{ uv.x + uv.width, uv.y + uv.height * 0.5 },
};
try app.vertices.appendSlice(&[_]Vertex{
high_mid,
mid_left,
mid_right,
low_mid,
mid_left,
mid_right,
});
try app.fragment_uniform_list.appendSlice(&[_]FragUniform{
.{
.type = .semicircle_convex,
.blend_color = blend_color,
},
.{
.type = .semicircle_convex,
.blend_color = blend_color,
},
});
app.update_vertex_buffer = true;
app.update_frag_uniform_buffer = true;
}
pub fn getVertexUniformBufferObject() !VertexUniform {
// Note: We use window width/height here, not framebuffer width/height.
// On e.g. macOS, window size may be 640x480 while framebuffer size may be
// 1280x960 (subpixels.) Doing this lets us use a pixel, not subpixel,
// coordinate system.
const window_size = mach.core.size();
const proj = Mat4x4.projection2D(.{
.left = 0,
.right = @floatFromInt(window_size.width),
.bottom = 0,
.top = @floatFromInt(window_size.height),
.near = -0.1,
.far = 100,
});
const mvp = proj.mul(&Mat4x4.translate(vec3(-1, -1, 0)));
return mvp;
}