checkbox and msdf sprite rendering
This commit is contained in:
parent
14359a9b7e
commit
588a417413
6
TODO
6
TODO
@ -27,10 +27,10 @@ to maintain focus until mouse release (fix scroll bars)
|
||||
[ ] .jpg
|
||||
[ ] gif support?
|
||||
[ ] layout_set_max_rows() and layout_set_max_columns()
|
||||
[ ] Maybe SDF sprites??
|
||||
[x] Maybe SDF sprites??
|
||||
[ ] Stylesheets and stylesheet import
|
||||
[ ] use SDF to draw anti-aliased rounded rectangles https://zed.dev/blog/videogame
|
||||
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
||||
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
||||
internal use functions (used to create widgets)
|
||||
|
||||
## Layout
|
||||
@ -77,4 +77,4 @@ _ border radius
|
||||
[ ] Text Input box
|
||||
[ ] Icon Buttons
|
||||
[ ] Switch
|
||||
[ ] Checkbox
|
||||
[x] Checkbox
|
||||
|
BIN
resources/tick_sdf.qoi
Normal file
BIN
resources/tick_sdf.qoi
Normal file
Binary file not shown.
54
src/main.c3
54
src/main.c3
@ -44,6 +44,35 @@ fn TimeStats Times.get_stats(×)
|
||||
}
|
||||
|
||||
|
||||
const ZString MSDF_FS = `
|
||||
#version 330
|
||||
|
||||
in vec2 fragTexCoord;
|
||||
out vec4 fragColor;
|
||||
uniform sampler2D texture0;
|
||||
uniform vec4 colDiffuse;
|
||||
|
||||
const float pxRange = 4.0f;
|
||||
|
||||
float screenPxRange() {
|
||||
vec2 unitRange = vec2(pxRange)/vec2(textureSize(texture0, 0));
|
||||
vec2 screenTexSize = vec2(1.0)/fwidth(fragTexCoord);
|
||||
return max(0.5*dot(unitRange, screenTexSize), 1.0);
|
||||
}
|
||||
|
||||
float median(float r, float g, float b) {
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 msd = texture(texture0, fragTexCoord).rgb;
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = screenPxRange()*(sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
fragColor = colDiffuse * opacity;
|
||||
}
|
||||
`;
|
||||
|
||||
const ZString FONT_FS = `
|
||||
#version 330
|
||||
|
||||
@ -85,6 +114,7 @@ fn int main(String[] args)
|
||||
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
|
||||
ui.sprite_atlas_create("icons", AtlasType.ATLAS_RGBA32, 512, 512)!!;
|
||||
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
|
||||
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
|
||||
|
||||
short width = 800;
|
||||
short height = 450;
|
||||
@ -103,10 +133,13 @@ fn int main(String[] args)
|
||||
// font stuff
|
||||
rl::Shader font_shader = rl::loadShaderFromMemory(null, FONT_FS);
|
||||
rl::Image font_atlas;
|
||||
rl::Image sprite_atlas;
|
||||
rl::Texture2D font_texture;
|
||||
rl::Texture2D sprite_texture;
|
||||
ugui::Id font_id = ui.get_font_id("font1");
|
||||
|
||||
// sprite stuff
|
||||
rl::Shader msdf_shader = rl::loadShaderFromMemory(null, MSDF_FS);
|
||||
rl::Image sprite_atlas;
|
||||
rl::Texture2D sprite_texture;
|
||||
ugui::Id sprite_id = ui.get_sprite_atlas_id("icons");
|
||||
|
||||
// Main loop
|
||||
@ -184,7 +217,7 @@ fn int main(String[] args)
|
||||
|
||||
ui.layout_next_row()!!;
|
||||
static bool check;
|
||||
ui.checkbox("check1", "", ugui::Point{}, &check)!!;
|
||||
ui.checkbox("check1", "", ugui::Point{}, &check, "tick")!!;
|
||||
|
||||
/*
|
||||
ui.layout_set_column()!!;
|
||||
@ -288,11 +321,16 @@ fn int main(String[] args)
|
||||
rl::drawTextureRec(font_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
||||
rl::endShaderMode();
|
||||
} else if (cmd.sprite.texture_id == sprite_id) {
|
||||
rl::Vector2 position = {
|
||||
.x = cmd.sprite.rect.x,
|
||||
.y = cmd.sprite.rect.y,
|
||||
};
|
||||
rl::drawTextureRec(sprite_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
||||
// FIXME: THIS CODE IS SHIT, REAL DOO DOO
|
||||
{|
|
||||
if (cmd.sprite.type == SpriteType.SPRITE_MSDF) {
|
||||
rl::beginShaderMode(msdf_shader);
|
||||
defer rl::endShaderMode();
|
||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
||||
} else {
|
||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
||||
}
|
||||
|};
|
||||
} else {
|
||||
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.m
|
||||
|
||||
// FIXME: this should be inside the style
|
||||
const ushort DEFAULT_CHECKBOX_SIZE = 16;
|
||||
fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* state)
|
||||
fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {})
|
||||
{
|
||||
Id id = ctx.gen_id(label)!;
|
||||
|
||||
@ -116,12 +116,19 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
|
||||
if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state);
|
||||
|
||||
Color col;
|
||||
if (*state) {
|
||||
col = 0xff0000ffu.to_rgba();
|
||||
if (tick_sprite != String{}) {
|
||||
col = ctx.style.bgcolor;
|
||||
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
||||
if (*state) {
|
||||
ctx.draw_sprite_raw(tick_sprite, elem.bounds)!;
|
||||
}
|
||||
} else {
|
||||
col = 0xff00ffffu.to_rgba();
|
||||
if (*state) {
|
||||
col = 0xff0000ffu.to_rgba();
|
||||
} else {
|
||||
col = 0xff00ffffu.to_rgba();
|
||||
}
|
||||
// Draw the button
|
||||
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
||||
}
|
||||
|
||||
// Draw the button
|
||||
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
||||
}
|
||||
|
@ -23,14 +23,12 @@ struct CmdUpdateAtlas {
|
||||
short width, height, bpp;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// 1. Add atlases as a data type
|
||||
// 2. Each atlas has an id
|
||||
struct CmdSprite {
|
||||
Id texture_id;
|
||||
SpriteType type;
|
||||
Rect rect;
|
||||
Rect texture_rect;
|
||||
Color hue;
|
||||
Id texture_id;
|
||||
}
|
||||
|
||||
// if rect is zero Rect{0} then reset the scissor
|
||||
@ -103,10 +101,11 @@ fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, boo
|
||||
}
|
||||
|
||||
// TODO: add texture id
|
||||
fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba())
|
||||
fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL)
|
||||
{
|
||||
Cmd cmd = {
|
||||
.type = CMD_SPRITE,
|
||||
.sprite.type = type,
|
||||
.sprite.rect = bounds,
|
||||
.sprite.texture_rect = texture,
|
||||
.sprite.texture_id = texture_id,
|
||||
|
@ -6,8 +6,16 @@ import mqoi;
|
||||
|
||||
const usz SRITES_PER_ATLAS = 64;
|
||||
|
||||
enum SpriteType {
|
||||
SPRITE_NORMAL,
|
||||
SPRITE_SDF,
|
||||
SPRITE_MSDF,
|
||||
SPRITE_ANIMATED,
|
||||
}
|
||||
|
||||
struct Sprite {
|
||||
Id id;
|
||||
SpriteType type;
|
||||
ushort u, v;
|
||||
ushort w, h;
|
||||
}
|
||||
@ -46,10 +54,11 @@ fn void! SpriteAtlas.free(&this)
|
||||
}
|
||||
|
||||
// FIXME: this should throw an error when a different pixel format than the atlas' is used
|
||||
fn Sprite*! SpriteAtlas.insert(&this, String name, char[] pixels, ushort w, ushort h, ushort stride)
|
||||
fn Sprite*! SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixels, ushort w, ushort h, ushort stride)
|
||||
{
|
||||
Sprite s;
|
||||
s.id = name.hash();
|
||||
s.type = type;
|
||||
Point uv = this.atlas.place(pixels, w, h, stride)!;
|
||||
s.w = w;
|
||||
s.h = h;
|
||||
@ -81,12 +90,12 @@ fn Id Ctx.get_sprite_atlas_id(&ctx, String name)
|
||||
return name.hash();
|
||||
}
|
||||
|
||||
fn void! Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride)
|
||||
fn void! Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride, SpriteType type = SPRITE_NORMAL)
|
||||
{
|
||||
ctx.sprite_atlas.insert(name, pixels, w, h, stride)!;
|
||||
ctx.sprite_atlas.insert(name, type, pixels, w, h, stride)!;
|
||||
}
|
||||
|
||||
fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path)
|
||||
fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL)
|
||||
{
|
||||
mqoi::Desc image_desc;
|
||||
uint w, h;
|
||||
@ -118,7 +127,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.sprite_atlas.insert(name, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
||||
ctx.sprite_atlas.insert(name, type, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
||||
}
|
||||
|
||||
fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
||||
@ -146,9 +155,18 @@ fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
||||
|
||||
// if the bounds are null the element is outside the div view,
|
||||
// no interaction should occur so just return
|
||||
if (elem.bounds.is_null()) return;
|
||||
if (elem.bounds.is_null()) return;
|
||||
|
||||
Id tex_id = ctx.sprite_atlas.id;
|
||||
|
||||
return ctx.push_sprite(elem.bounds, uv, tex_id)!;
|
||||
}
|
||||
|
||||
fn void! Ctx.draw_sprite_raw(&ctx, String name, Rect bounds)
|
||||
{
|
||||
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
||||
Rect uv = { sprite.u, sprite.v, sprite.w, sprite.h };
|
||||
//Rect bounds = Rect{ 0, 0, sprite.w, sprite.h }.off(off);
|
||||
Id tex_id = ctx.sprite_atlas.id;
|
||||
return ctx.push_sprite(bounds, uv, tex_id, type: sprite.type)!;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user