power_play/src/draw.c

379 lines
12 KiB
C

#include "draw.h"
#include "renderer.h"
#include "math.h"
#include "font.h"
#include "scratch.h"
#include "sprite.h"
#include "collider.h"
GLOBAL struct {
struct renderer_texture solid_white;
} G = ZI, DEBUG_ALIAS(G, G_draw);
/* ========================== *
* Startup
* ========================== */
struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *renderer_sr,
struct font_startup_receipt *font_sr)
{
(UNUSED)renderer_sr;
(UNUSED)font_sr;
u32 pixel_white = 0xFFFFFFFF;
G.solid_white = renderer_texture_alloc(RENDERER_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, V2I32(1, 1), &pixel_white);
return (struct draw_startup_receipt) { 0 };
}
/* ========================== *
* Texture
* ========================== */
void draw_quad_texture_ex(struct renderer_cmd_buffer *cmdbuff, struct renderer_texture texture, struct sprite_tag sprite, struct clip_rect clip, u32 tint0, u32 tint1, struct quad quad)
{
struct renderer_cmd_parameters cmd_params = ZI;
cmd_params.kind = SHADER_TRIANGLE;
cmd_params.texture_params.texture = texture;
cmd_params.texture_params.sprite = sprite;
renderer_cmd_buffer_ensure_cmd(cmdbuff, &cmd_params);
struct triangle_shader_vertex *vertices = NULL;
vidx *indices = NULL;
u32 offset = renderer_cmd_buffer_push_vertices(cmdbuff, (u8 **)&vertices, &indices, 4, 6);
/* Top left */
vertices[0] = (struct triangle_shader_vertex) {
.pos = quad.p0,
.uv = { clip.p0.x, clip.p0.y },
.color = tint0
};
/* Top right */
vertices[1] = (struct triangle_shader_vertex) {
.pos = quad.p1,
.uv = { clip.p1.x, clip.p0.y },
.color = tint0
};
/* Bottom right */
vertices[2] = (struct triangle_shader_vertex) {
.pos = quad.p2,
.uv = { clip.p1.x, clip.p1.y },
.color = tint1
};
/* Bottom left */
vertices[3] = (struct triangle_shader_vertex) {
.pos = quad.p3,
.uv = { clip.p0.x, clip.p1.y },
.color = tint1
};
/* Top / right triangle */
indices[0] = offset + 0;
indices[1] = offset + 1;
indices[2] = offset + 2;
/* Bottom / left triangle */
indices[3] = offset + 0;
indices[4] = offset + 2;
indices[5] = offset + 3;
}
void draw_quad_texture(struct renderer_cmd_buffer *cmdbuff, struct draw_texture_params params, struct quad quad)
{
draw_quad_texture_ex(cmdbuff, params.texture, params.sprite, params.clip, params.tint, params.tint, quad);
}
/* ========================== *
* Solid fill shapes
* ========================== */
void draw_poly(struct renderer_cmd_buffer *cmdbuff, struct v2_array array, u32 color)
{
if (array.count < 3) {
return;
}
struct renderer_cmd_parameters cmd_params = ZI;
cmd_params.kind = SHADER_TRIANGLE;
cmd_params.texture_params.texture = G.solid_white;
renderer_cmd_buffer_ensure_cmd(cmdbuff, &cmd_params);
u32 num_tris = array.count - 2;
u32 num_indices = num_tris * 3;
struct triangle_shader_vertex *vertices = NULL;
vidx *indices = NULL;
u32 idx_offset = renderer_cmd_buffer_push_vertices(cmdbuff, (u8 **)&vertices, &indices, array.count, num_indices);
/* Fill vertices */
for (u32 i = 0; i < array.count; ++i) {
vertices[i] = (struct triangle_shader_vertex) {
.pos = array.points[i],
.color = color
};
}
/* Fill indices */
for (u32 i = 0; i < num_tris; ++i) {
u32 tri_offset = i * 3;
indices[tri_offset + 0] = idx_offset + 0;
indices[tri_offset + 1] = idx_offset + (i + 1);
indices[tri_offset + 2] = idx_offset + (i + 2);
}
}
void draw_circle(struct renderer_cmd_buffer *cmdbuff, struct v2 pos, f32 radius, u32 color, u32 detail)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct v2 *points = arena_push_array(scratch.arena, struct v2, detail);
for(u32 i = 0; i < detail; ++i) {
struct v2 p = V2(
radius * math_cos(i * (PI * 2.f) / detail),
radius * math_sin(i * (PI * 2.f) / detail)
);
points[i] = v2_add(pos, p);
}
struct v2_array a = {
.points = points,
.count = detail
};
draw_poly(cmdbuff, a, color);
scratch_end(scratch);
}
void draw_quad(struct renderer_cmd_buffer *cmdbuff, struct quad quad, u32 color)
{
draw_quad_texture_ex(cmdbuff, G.solid_white, sprite_tag_nil(), CLIP_ALL, color, color, quad);
}
/* ========================== *
* Solid line shapes
* ========================== */
void draw_gradient_line(struct renderer_cmd_buffer *cmdbuff, struct v2 start, struct v2 end, f32 thickness, u32 color_start, u32 color_end)
{
struct quad quad = quad_from_line(start, end, thickness);
draw_quad_texture_ex(cmdbuff, G.solid_white, sprite_tag_nil(), CLIP_ALL, color_start, color_end, quad);
}
void draw_line(struct renderer_cmd_buffer *cmdbuff, struct v2 start, struct v2 end, f32 thickness, u32 color)
{
struct quad quad = quad_from_line(start, end, thickness);
draw_quad(cmdbuff, quad, color);
}
void draw_ray(struct renderer_cmd_buffer *cmdbuff, struct v2 pos, struct v2 rel, f32 thickness, u32 color)
{
struct quad quad = quad_from_ray(pos, rel, thickness);
draw_quad(cmdbuff, quad, color);
}
void draw_poly_line(struct renderer_cmd_buffer *cmdbuff, struct v2_array array, b32 loop, f32 thickness, u32 color)
{
if (array.count < 2) {
return;
}
for (u64 i = 1; i < array.count; ++i) {
struct v2 p1 = array.points[i - 1];
struct v2 p2 = array.points[i];
struct quad q = quad_from_line(p1, p2, thickness);
draw_quad(cmdbuff, q, color);
}
if (loop && array.count > 2) {
struct v2 p1 = array.points[array.count - 1];
struct v2 p2 = array.points[0];
struct quad q = quad_from_line(p1, p2, thickness);
draw_quad(cmdbuff, q, color);
}
}
void draw_circle_line(struct renderer_cmd_buffer *cmdbuff, struct v2 pos, f32 radius, f32 thickness, u32 color, u32 detail)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct v2 *points = arena_push_array(scratch.arena, struct v2, detail);
for (u32 i = 0; i < detail; ++i) {
struct v2 p = V2(
radius * math_cos(i * (PI * 2.f) / detail),
radius * math_sin(i * (PI * 2.f) / detail)
);
points[i] = v2_add(pos, p);
}
struct v2_array a = {
.points = points,
.count = detail
};
draw_poly_line(cmdbuff, a, true, thickness, color);
scratch_end(scratch);
}
void draw_quad_line(struct renderer_cmd_buffer *cmdbuff, struct quad quad, f32 thickness, u32 color)
{
struct v2 points[] = { quad.p0, quad.p1, quad.p2, quad.p3 };
struct v2_array a = { .points = points, .count = ARRAY_COUNT(points) };
draw_poly_line(cmdbuff, a, true, thickness, color);
}
void draw_arrow_line(struct renderer_cmd_buffer *cmdbuff, struct v2 start, struct v2 end, f32 thickness, f32 arrowhead_height, u32 color)
{
const f32 head_width_ratio = 0.5f; /* Width of arrowhead relative to its length */
const f32 max_height_to_line_ratio = 0.9f; /* Maximum length of arrowhead relative to total line length */
arrowhead_height = min_f32(arrowhead_height, v2_len(v2_sub(end, start)) * max_height_to_line_ratio);
struct v2 head_start_dir = v2_sub(start, end);
head_start_dir = v2_norm(head_start_dir);
head_start_dir = v2_mul(head_start_dir, arrowhead_height);
struct v2 head_start = v2_add(end, head_start_dir);
struct v2 head_p1_dir = v2_perp_mul(head_start_dir, head_width_ratio);
struct v2 head_p2_dir = v2_neg(head_p1_dir);
struct v2 head_p1 = v2_add(head_start, head_p1_dir);
struct v2 head_p2 = v2_add(head_start, head_p2_dir);
struct v2 head_points[] = { end, head_p1, head_p2 };
struct v2_array head_points_v2_array = {
.points = head_points,
.count = ARRAY_COUNT(head_points)
};
draw_poly(cmdbuff, head_points_v2_array, color);
struct quad line_quad = quad_from_line(start, head_start, thickness);
draw_quad(cmdbuff, line_quad, color);
}
void draw_arrow_ray(struct renderer_cmd_buffer *cmdbuff, struct v2 pos, struct v2 rel, f32 thickness, f32 arrowhead_height, u32 color)
{
struct v2 end = v2_add(pos, rel);
draw_arrow_line(cmdbuff, pos, end, thickness, arrowhead_height, color);
}
void draw_collider_line(struct renderer_cmd_buffer *cmdbuff, struct xform draw_xf, struct collider_shape shape, struct xform shape_xf, f32 thickness, u32 color, u32 detail)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct v2 *points = arena_push_array(scratch.arena, struct v2, detail);
for (u32 i = 0; i < detail; ++i) {
f32 angle = ((f32)i / (f32)detail) * (2 * PI);
struct v2 dir = V2(math_cos(angle), math_sin(angle));
struct v2 p = collider_get_support_point(&shape, shape_xf, dir).p;
p = xform_mul_v2(draw_xf, p);
points[i] = p;
}
#if 1
struct v2_array poly = { .points = points, .count = detail };
draw_poly_line(cmdbuff, poly, true, thickness, color);
#else
for (u64 i = 0; i < detail; ++i) {
struct v2 point = points[i];
(UNUSED)thickness;
draw_circle(cmdbuff, point, 3, color, 10);
}
#endif
scratch_end(scratch);
}
/* ========================== *
* Grid
* ========================== */
void draw_grid(struct renderer_cmd_buffer *cmdbuff, struct rect rect, u32 color, f32 thickness, f32 spacing, struct v2 offset)
{
struct renderer_cmd_parameters cmd_params = ZI;
cmd_params.kind = SHADER_GRID;
renderer_cmd_buffer_ensure_cmd(cmdbuff, &cmd_params);
struct grid_shader_vertex *vertices = NULL;
vidx *indices = NULL;
u32 index_offset = renderer_cmd_buffer_push_vertices(cmdbuff, (u8 **)&vertices, &indices, 4, 6);
struct quad quad = quad_from_rect(rect);
struct grid_shader_vertex attributes = ZI;
attributes.color = color;
attributes.line_thickness = thickness;
attributes.line_spacing = spacing;
attributes.offset = offset;
/* Top left */
vertices[0] = attributes;
vertices[0].pos = quad.p0;
/* Top right */
vertices[1] = attributes;
vertices[1].pos = quad.p1;
/* Bottom right */
vertices[2] = attributes;
vertices[2].pos = quad.p2;
/* Bottom left */
vertices[3] = attributes;
vertices[3].pos = quad.p3;
/* Top / right triangle */
indices[0] = index_offset + 0;
indices[1] = index_offset + 1;
indices[2] = index_offset + 2;
/* Bottom / left triangle */
indices[3] = index_offset + 0;
indices[4] = index_offset + 2;
indices[5] = index_offset + 3;
}
/* ========================== *
* Text
* ========================== */
void draw_text_ex(struct renderer_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, f32 scale, struct string str)
{
struct v2 draw_pos = pos;
draw_pos.y += font->point_size * scale;
struct string_codepoint_iter iter = string_codepoint_iter_begin(str);
while (string_codepoint_iter_next(&iter)) {
u32 codepoint = iter.codepoint;
/* TODO: Remove this (placeholder \n) */
if (codepoint == '\n') {
draw_pos.x = pos.x;
draw_pos.y += (font->point_size * 1.5f) * scale;
continue;
}
struct font_glyph *glyph = font_get_glyph(font, codepoint);
f32 x = draw_pos.x + glyph->off_x * scale;
f32 y = draw_pos.y + glyph->off_y * scale;
f32 width = glyph->width * scale;
f32 height = glyph->height * scale;
struct clip_rect clip = {
{
glyph->atlas_rect.x / (f32)font->image_width,
glyph->atlas_rect.y / (f32)font->image_height
},
{
(glyph->atlas_rect.x + glyph->atlas_rect.width) / (f32)font->image_width,
(glyph->atlas_rect.y + glyph->atlas_rect.height) / (f32)font->image_height
}
};
struct quad quad = quad_from_rect(RECT(x, y, width, height));
draw_quad_texture_ex(cmdbuff, font->texture, sprite_tag_nil(), clip, 0xFFFFFFFF, 0xFFFFFFFF, quad);
draw_pos.x += glyph->advance * scale;
}
string_codepoint_iter_end(&iter);
}
void draw_text(struct renderer_cmd_buffer *cmdbuff, struct font *font, struct v2 pos, struct string str)
{
draw_text_ex(cmdbuff, font, pos, 1.0, str);
}