Add textures for the button and window.

This commit is contained in:
IntegratedQuantum 2023-02-24 13:25:59 +01:00
parent ff7df1bdbc
commit 61e2e417a2
12 changed files with 209 additions and 49 deletions

View File

@ -0,0 +1,20 @@
#version 330
layout (location=0) out vec4 frag_color;
uniform sampler2D image;
flat in vec4 fColor;
in vec2 startCoord;
uniform bool pressed;
uniform float scale;
uniform vec2 randomOffset;
void main() {
frag_color = texture(image, (gl_FragCoord.xy - startCoord)/(2*scale)/textureSize(image, 0) + randomOffset);
frag_color.a *= fColor.a;
frag_color.rgb += fColor.rgb;
if(pressed) {
frag_color.rgb *= 0.8;
}
}

View File

@ -0,0 +1,27 @@
#version 330 core
layout (location=0) in vec2 vertex_pos;
out vec2 startCoord;
flat out vec4 fColor;
//in pixel
uniform vec2 start;
uniform vec2 size;
uniform vec2 screen;
uniform int color;
void main() {
// Convert to opengl coordinates:
vec2 position_percentage = (start + vertex_pos*size)/screen;
startCoord.x = start.x;
startCoord.y = screen.y - start.y;
vec2 position = vec2(position_percentage.x, -position_percentage.y)*2+vec2(-1, 1);
gl_Position = vec4(position, 0, 1);
fColor = vec4((color & 0xff0000)>>16, (color & 0xff00)>>8, color & 0xff, (color>>24) & 255)/255.0;
}

BIN
assets/cubyz/ui/button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

View File

@ -45,8 +45,10 @@ pub fn build(b: *std.build.Builder) !void {
}
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c"}, &[_][]const u8{"-g"});
exe.addAnonymousModule("gui", .{.source_file = .{.path = "src/gui/gui.zig"}});
exe.addModule("freetype", freetype.module(b));
exe.addModule("harfbuzz", freetype.harfbuzzModule(b));
const harfbuzzModule = freetype.harfbuzzModule(b);
const freetypeModule = harfbuzzModule.dependencies.get("freetype").?;
exe.addModule("harfbuzz", harfbuzzModule);
exe.addModule("freetype", freetypeModule);
freetype.link(b, exe, .{ .harfbuzz = .{} });
//exe.sanitize_thread = true;
exe.install();

View File

@ -346,6 +346,25 @@ pub const draw = struct {
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
// ----------------------------------------------------------------------------
pub fn customShadedRect(uniforms: anytype, _pos: Vec2f, _dim: Vec2f) void {
var pos = _pos;
var dim = _dim;
pos *= @splat(2, scale);
pos += translation;
dim *= @splat(2, scale);
c.glUniform2f(uniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(uniforms.start, pos[0], pos[1]);
c.glUniform2f(uniforms.size, dim[0], dim[1]);
c.glUniform1i(uniforms.color, color);
c.glUniform1f(uniforms.scale, scale);
c.glBindVertexArray(rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
// ----------------------------------------------------------------------------
pub fn text(_text: []const u8, x: f32, y: f32, fontSize: f32) !void {
@ -1220,6 +1239,35 @@ pub const TextureArray = struct {
}
};
pub const Texture = struct {
textureID: c_uint,
pub fn init() Texture {
var self: Texture = undefined;
c.glGenTextures(1, &self.textureID);
return self;
}
pub fn deinit(self: Texture) void {
c.glDeleteTextures(1, &self.textureID);
}
pub fn bind(self: Texture) void {
c.glBindTexture(c.GL_TEXTURE_2D, self.textureID);
}
/// (Re-)Generates the GPU buffer.
pub fn generate(self: Texture, image: Image) !void {
self.bind();
c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_RGBA8, image.width, image.height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, image.imageData.ptr);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_REPEAT);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_REPEAT);
}
};
pub const Color = extern struct {
r: u8,
g: u8,

View File

@ -4,6 +4,9 @@ const Allocator = std.mem.Allocator;
const main = @import("root");
const graphics = main.graphics;
const draw = graphics.draw;
const Image = graphics.Image;
const Shader = graphics.Shader;
const Texture = graphics.Texture;
const settings = main.settings;
const vec = main.vec;
const Vec2f = vec.Vec2f;
@ -66,6 +69,43 @@ onCloseFn: *const fn()void = &defaultFunction,
var grabPosition: ?Vec2f = null;
var selfPositionWhenGrabbed: Vec2f = undefined;
var backgroundTexture: Texture = undefined;
var titleTexture: Texture = undefined;
var shader: Shader = undefined;
var windowUniforms: struct {
screen: c_int,
start: c_int,
size: c_int,
color: c_int,
scale: c_int,
image: c_int,
randomOffset: c_int,
} = undefined;
pub fn __init() !void {
shader = try Shader.create("assets/cubyz/shaders/ui/button.vs", "assets/cubyz/shaders/ui/button.fs");
windowUniforms = shader.bulkGetUniformLocation(@TypeOf(windowUniforms));
shader.bind();
graphics.c.glUniform1i(windowUniforms.image, 0);
backgroundTexture = Texture.init();
const backgroundImage = try Image.readFromFile(main.threadAllocator, "assets/cubyz/ui/window_background.png");
defer backgroundImage.deinit(main.threadAllocator);
try backgroundTexture.generate(backgroundImage);
titleTexture = Texture.init();
const titleImage = try Image.readFromFile(main.threadAllocator, "assets/cubyz/ui/window_title.png");
defer titleImage.deinit(main.threadAllocator);
try titleTexture.generate(titleImage);
}
pub fn __deinit() void {
shader.delete();
backgroundTexture.deinit();
titleTexture.deinit();
}
pub fn defaultFunction() void {}
pub fn defaultErrorFunction() Allocator.Error!void {}
@ -351,21 +391,31 @@ pub fn render(self: *const GuiWindow) !void {
mousePosition /= @splat(2, scale);
const oldTranslation = draw.setTranslation(self.pos);
const oldScale = draw.setScale(scale);
draw.setColor(0xff000000);
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
shader.bind();
backgroundTexture.bind();
draw.customShadedRect(windowUniforms, .{0, 0}, self.size);
for(self.components) |*component| {
try component.render(mousePosition);
}
if(self.showTitleBar) {
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
shader.bind();
titleTexture.bind();
if(self == gui.selectedWindow) {
draw.setColor(0xff000040);
} else {
draw.setColor(0xff000000);
}
draw.customShadedRect(windowUniforms, .{0, 0}, .{self.size[0], 16});
}
draw.restoreTranslation(oldTranslation);
draw.restoreScale(oldScale);
if(self.showTitleBar) {
var text = try graphics.TextBuffer.init(main.threadAllocator, self.title, .{}, false);
defer text.deinit();
const titleDimension = try text.calculateLineBreaks(16*scale, self.size[0]*scale);
if(self == gui.selectedWindow) {
draw.setColor(0xff80b080);
} else {
draw.setColor(0xffb08080);
}
draw.rect(self.pos, Vec2f{self.size[0]*scale, titleDimension[1]});
try text.render(self.pos[0] + self.size[0]*scale/2 - titleDimension[0]/2, self.pos[1], 16*scale);
}
if(self == gui.selectedWindow and grabPosition != null) {

View File

@ -8,6 +8,7 @@ const Image = graphics.Image;
const Shader = graphics.Shader;
const TextBuffer = graphics.TextBuffer;
const Texture = graphics.Texture;
const random = main.random;
const vec = main.vec;
const Vec2f = vec.Vec2f;
@ -19,15 +20,50 @@ const Button = @This();
const border: f32 = 3;
const fontSize: f32 = 16;
var texture: Texture = undefined;
pub var shader: Shader = undefined;
var buttonUniforms: struct {
screen: c_int,
start: c_int,
size: c_int,
color: c_int,
scale: c_int,
image: c_int,
pressed: c_int,
randomOffset: c_int,
} = undefined;
pressed: bool = false,
onAction: *const fn() void,
text: TextBuffer,
textSize: Vec2f = undefined,
randomOffset: Vec2f = undefined,
pub fn __init() !void {
shader = try Shader.create("assets/cubyz/shaders/ui/button.vs", "assets/cubyz/shaders/ui/button.fs");
buttonUniforms = shader.bulkGetUniformLocation(@TypeOf(buttonUniforms));
shader.bind();
graphics.c.glUniform1i(buttonUniforms.image, 0);
texture = Texture.init();
const image = try Image.readFromFile(main.threadAllocator, "assets/cubyz/ui/button.png");
defer image.deinit(main.threadAllocator);
try texture.generate(image);
}
pub fn __deinit() void {
shader.delete();
texture.deinit();
}
pub fn init(pos: Vec2f, width: f32, allocator: Allocator, text: []const u8, onAction: *const fn() void) Allocator.Error!GuiComponent {
var self = Button {
.onAction = onAction,
.text = try TextBuffer.init(allocator, text, .{}, false),
.randomOffset = Vec2f{
random.nextFloat(&main.seed),
random.nextFloat(&main.seed),
},
};
self.textSize = try self.text.calculateLineBreaks(fontSize, width - 3*border);
return GuiComponent {
@ -54,49 +90,21 @@ pub fn mainButtonReleased(self: *Button, component: *const GuiComponent, mousePo
}
}
const buttonColor: [5]u32 = [_]u32{
0xff9ca6bf, // center
0xffa6b0cc, // top
0xffa0aac4, // right
0xff919ab3, // bottom
0xff97a1ba, // left
};
const buttonPressedColor: [5]u32 = [_]u32{
0xff929ab3, // center
0xff878fa6, // top
0xff8e96ad, // right
0xff9ca5bf, // bottom
0xff969fb8, // left
};
const buttonHoveredColor: [5]u32 = [_]u32{
0xff9ca6dd, // center
0xffa6b0ea, // top
0xffa0aae2, // right
0xff919ad1, // bottom
0xff97a1d8, // left
};
pub fn render(self: *Button, component: *const GuiComponent, mousePosition: Vec2f) !void {
// TODO: I should really just add a proper button texture.
var colors = &buttonColor;
if(component.contains(mousePosition)) {
colors = &buttonHoveredColor;
}
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
texture.bind();
shader.bind();
graphics.c.glUniform2f(buttonUniforms.randomOffset, self.randomOffset[0], self.randomOffset[1]);
graphics.c.glUniform1i(buttonUniforms.pressed, 0);
if(self.pressed) {
colors = &buttonPressedColor;
draw.setColor(0xff000000);
graphics.c.glUniform1i(buttonUniforms.pressed, 1);
} else if(component.contains(mousePosition)) {
draw.setColor(0xff000040);
} else {
draw.setColor(0xff000000);
}
draw.setColor(colors[0]);
draw.rect(component.pos + @splat(2, border), component.size - @splat(2, 2*border));
draw.setColor(colors[1]);
draw.rect(component.pos, Vec2f{component.size[0] - border, border});
draw.setColor(colors[2]);
draw.rect(component.pos + Vec2f{component.size[0] - border, 0}, Vec2f{border, component.size[1] - border});
draw.setColor(colors[3]);
draw.rect(component.pos + Vec2f{border, component.size[1] - border}, Vec2f{component.size[0] - border, border});
draw.setColor(colors[4]);
draw.rect(component.pos + Vec2f{0, border}, Vec2f{border, component.size[1] - border});
draw.customShadedRect(buttonUniforms, component.pos, component.size);
const textPos = component.pos + component.size/@splat(2, @as(f32, 2.0)) - self.textSize/@splat(2, @as(f32, 2.0));
try self.text.render(textPos[0], textPos[1], fontSize);
}

View File

@ -7,6 +7,7 @@ const settings = main.settings;
const vec = main.vec;
const Vec2f = vec.Vec2f;
const Button = @import("components/Button.zig");
pub const GuiComponent = @import("GuiComponent.zig");
pub const GuiWindow = @import("GuiWindow.zig");
@ -24,6 +25,8 @@ pub fn init() !void {
inline for(@typeInfo(windowlist).Struct.decls) |decl| {
try @field(windowlist, decl.name).init();
}
try GuiWindow.__init();
try Button.__init();
}
pub fn deinit() void {
@ -33,6 +36,8 @@ pub fn deinit() void {
window.onCloseFn();
}
openWindows.deinit();
GuiWindow.__deinit();
Button.__deinit();
}
pub fn addWindow(window: *GuiWindow, isHudWindow: bool) !void {

View File

@ -174,7 +174,7 @@ pub const Window = struct {
}
}
fn glDebugOutput(_: c_uint, _: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
if(severity == c.GL_DEBUG_SEVERITY_HIGH) {
if(severity == c.GL_DEBUG_SEVERITY_HIGH) { // TODO: Capture the stack traces.
std.log.err("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});
} else if(severity == c.GL_DEBUG_SEVERITY_MEDIUM) {
std.log.warn("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});