WIP2: Text/texture rendering
This commit is contained in:
parent
ebe6aa8d9a
commit
9d23bbc39e
238
src/Renderer.zig
238
src/Renderer.zig
|
@ -8,18 +8,24 @@ const Self = @This();
|
|||
|
||||
window: sdl.Window,
|
||||
context: sdl.gl.Context,
|
||||
//color_loc: u32,
|
||||
buffer: gl.Buffer,
|
||||
color: [4]f32,
|
||||
color: [4]f32 = .{ 0, 0, 0, 0 },
|
||||
|
||||
test_counter: usize = 0,
|
||||
// There's a vbo for each shader program
|
||||
vbo: [nPrograms][max_objects]f32 = .{.{0.0} ** max_objects} ** nPrograms,
|
||||
vbo_index: [nPrograms]usize = .{0} ** nPrograms,
|
||||
|
||||
vbo: [max_objects]f32 = .{0.0} ** max_objects,
|
||||
//colors: [max_objects][4]f32 = .{[4]f32{ 0.0, 0.0, 0.0, 1.0 }} ** max_objects,
|
||||
vbo_index: usize = 0,
|
||||
// The index of the program is the enum converted to int
|
||||
programs: [nPrograms]gl.Program = .{undefined} ** nPrograms,
|
||||
|
||||
const max_objects: usize = 16384;
|
||||
|
||||
const shaderProgram = enum {
|
||||
color,
|
||||
texture,
|
||||
};
|
||||
const nPrograms = std.meta.fields(shaderProgram).len;
|
||||
|
||||
fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque {
|
||||
_ = p;
|
||||
return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc));
|
||||
|
@ -43,57 +49,30 @@ pub fn init() !Self {
|
|||
.{ .context = .opengl, .vis = .shown },
|
||||
);
|
||||
|
||||
const proc: []const u8 = undefined;
|
||||
try gl.loadExtensions(proc, glGetProcAddress);
|
||||
|
||||
const ctx = try sdl.gl.createContext(window);
|
||||
sdl.gl.setSwapInterval(.immediate) catch {
|
||||
std.debug.print("WARNING: Unable to configure the swap interval.\n", .{});
|
||||
};
|
||||
|
||||
const proc: []const u8 = undefined;
|
||||
try gl.loadExtensions(proc, glGetProcAddress);
|
||||
gl.enable(.blend);
|
||||
gl.blendFunc(.src_alpha, .one_minus_src_alpha);
|
||||
|
||||
// Shader stuff
|
||||
var mvp_loc: u32 = undefined;
|
||||
const program = gl.Program.create();
|
||||
{
|
||||
const vs = gl.Shader.create(.vertex);
|
||||
defer vs.delete();
|
||||
const fs = gl.Shader.create(.fragment);
|
||||
defer fs.delete();
|
||||
var buf = gl.Buffer.gen();
|
||||
buf.bind(.array_buffer);
|
||||
|
||||
vs.source(1, &.{@embedFile("shaders/vector.vs")});
|
||||
fs.source(1, &.{@embedFile("shaders/fragment.fs")});
|
||||
var renderer = Self{ .window = window, .context = ctx, .buffer = buf };
|
||||
|
||||
vs.compile();
|
||||
fs.compile();
|
||||
|
||||
program.attach(vs);
|
||||
defer program.detach(vs);
|
||||
program.attach(fs);
|
||||
defer program.detach(fs);
|
||||
|
||||
program.link();
|
||||
|
||||
mvp_loc = program.uniformLocation("mvp").?;
|
||||
}
|
||||
program.use();
|
||||
|
||||
// Get the matrix that converts coordinates to 1:1 on the screen from the OpenGL default
|
||||
{
|
||||
const wsize = window.getSize();
|
||||
const xunit = @intToFloat(f32, wsize.width);
|
||||
const yunit = @intToFloat(f32, wsize.height);
|
||||
|
||||
const ortho = m.Mat4.createOrthogonal(0.0, xunit, yunit, 0.0, 0.0, 100.0);
|
||||
|
||||
gl.uniformMatrix4fv(mvp_loc, false, &.{ortho.fields});
|
||||
// Load the shader programs according to the names in the enum
|
||||
inline for (std.meta.fields(shaderProgram)) |programEnum| {
|
||||
renderer.programs[programEnum.value] = renderer.loadProgram(programEnum.name);
|
||||
}
|
||||
|
||||
var vertex_array = gl.VertexArray.gen();
|
||||
vertex_array.bind();
|
||||
|
||||
const buf = gl.Buffer.gen();
|
||||
buf.bind(.array_buffer);
|
||||
|
||||
gl.vertexAttribPointer(0, 2, .float, false, @sizeOf(f32) * 8, @sizeOf(f32) * 0);
|
||||
gl.enableVertexAttribArray(0);
|
||||
|
||||
|
@ -105,31 +84,74 @@ pub fn init() !Self {
|
|||
|
||||
gl.clearColor(0.91, 0.85, 0.65, 1.00);
|
||||
|
||||
// TODO: See if there's a more optimal solution to this, maybe activating only when necessary
|
||||
gl.enable(.blend);
|
||||
gl.blendFunc(.src_alpha, .one_minus_src_alpha);
|
||||
var text = Texture.fromText("testing123");
|
||||
_ = text;
|
||||
//text.deinit();
|
||||
|
||||
var text = Text.init("testing123");
|
||||
text.deinit();
|
||||
return renderer;
|
||||
}
|
||||
|
||||
return Self{ .window = window, .context = ctx, .buffer = buf, .color = .{ 0, 0, 0, 0 } };
|
||||
fn loadProgram(self: Self, comptime name: []const u8) gl.Program {
|
||||
var program = gl.Program.create();
|
||||
|
||||
const vs = gl.Shader.create(.vertex);
|
||||
defer vs.delete();
|
||||
const fs = gl.Shader.create(.fragment);
|
||||
defer fs.delete();
|
||||
|
||||
vs.source(1, &.{@embedFile("shaders/" ++ name ++ ".vert")});
|
||||
fs.source(1, &.{@embedFile("shaders/" ++ name ++ ".frag")});
|
||||
|
||||
vs.compile();
|
||||
fs.compile();
|
||||
|
||||
program.attach(vs);
|
||||
defer program.detach(vs);
|
||||
program.attach(fs);
|
||||
defer program.detach(fs);
|
||||
|
||||
program.link();
|
||||
|
||||
// Get the matrix that converts coordinates to 1:1 on the screen from the OpenGL default
|
||||
if (program.uniformLocation("mvp")) |mvpLocation| {
|
||||
const wsize = self.window.getSize();
|
||||
const xunit = @intToFloat(f32, wsize.width);
|
||||
const yunit = @intToFloat(f32, wsize.height);
|
||||
|
||||
const ortho = m.Mat4.createOrthogonal(0.0, xunit, yunit, 0.0, 0.0, 100.0);
|
||||
|
||||
program.use();
|
||||
gl.uniformMatrix4fv(mvpLocation, false, &.{ortho.fields});
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
pub fn render(self: *Self) void {
|
||||
//std.debug.print("Number of rendered elements: {}\n", .{self.test_counter});
|
||||
self.test_counter = 0;
|
||||
// Render with each of the defined shader programs
|
||||
for (self.programs) |_, program_i| {
|
||||
self.programs[program_i].use();
|
||||
|
||||
self.buffer.data(f32, &self.vbo, .static_draw);
|
||||
self.buffer.data(f32, &(self.vbo[program_i]), .static_draw);
|
||||
|
||||
switch (@intToEnum(shaderProgram, program_i)) {
|
||||
.color => {
|
||||
gl.drawArrays(.triangles, 0, self.vbo_index[program_i]);
|
||||
},
|
||||
.texture => {
|
||||
gl.drawArrays(.triangles, 0, self.vbo_index[program_i]);
|
||||
},
|
||||
}
|
||||
|
||||
self.vbo_index[program_i] = 0;
|
||||
|
||||
// Clear the vbo, this really shouldn't be necessary as the index is set to zero,
|
||||
// but otherwise it leads to bugs
|
||||
self.vbo[program_i] = .{0.0} ** max_objects;
|
||||
}
|
||||
|
||||
gl.drawArrays(.triangles, 0, self.vbo_index);
|
||||
sdl.gl.swapWindow(self.window);
|
||||
gl.clear(.{ .color = true });
|
||||
|
||||
self.vbo_index = 0;
|
||||
|
||||
// Clear the vbo, this really shouldn't be necessary as the index is set to zero,
|
||||
// but otherwise it leads to bugs
|
||||
self.vbo = .{0.0} ** max_objects;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
|
@ -168,8 +190,7 @@ pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
|
|||
}
|
||||
|
||||
pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void {
|
||||
// TODO: Is this still necessary?
|
||||
self.test_counter += 1;
|
||||
const program_i = @enumToInt(shaderProgram.texture);
|
||||
|
||||
var xf = @intToFloat(f32, x);
|
||||
var yf = @intToFloat(f32, y);
|
||||
|
@ -178,29 +199,35 @@ pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32)
|
|||
|
||||
const skew_x_offset = @intToFloat(f32, skew_x) * hf / 100;
|
||||
|
||||
const i = self.vbo_index;
|
||||
const i = self.vbo_index[program_i];
|
||||
const vertex_data = [_]f32{
|
||||
xf + skew_x_offset, yf, // up-left
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
0.0, 0.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
0.0, 0.0,
|
||||
xf + wf + skew_x_offset, yf, // up-right
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
1.0, 0.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
1.0, 0.0,
|
||||
xf - skew_x_offset, yf + hf, // down-left
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
0.0, 1.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
0.0, 1.0,
|
||||
xf + wf - skew_x_offset, yf + hf, // down-right
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
1.0, 1.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
1.0, 1.0,
|
||||
xf + wf + skew_x_offset, yf, // up-right
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
1.0, 0.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
1.0, 0.0,
|
||||
xf - skew_x_offset, yf + hf, // down-left
|
||||
self.color[0], self.color[1], self.color[2], self.color[3],
|
||||
0.0, 1.0,
|
||||
self.color[0], self.color[1],
|
||||
self.color[2], self.color[3],
|
||||
0.0, 1.0,
|
||||
};
|
||||
std.mem.copy(f32, self.vbo[i .. i + vertex_data.len], &vertex_data);
|
||||
self.vbo_index += vertex_data.len;
|
||||
std.mem.copy(f32, self.vbo[program_i][i..], &vertex_data);
|
||||
self.vbo_index[program_i] += vertex_data.len;
|
||||
}
|
||||
|
||||
fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
|
||||
|
@ -216,46 +243,14 @@ pub fn getOutputSize(self: Self) OutputSize {
|
|||
};
|
||||
}
|
||||
|
||||
const image = [_]u8{
|
||||
// 1
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
// 2
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
// 1
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
// 2
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
255,255,255,
|
||||
0,0,0,
|
||||
};
|
||||
|
||||
const Text = struct {
|
||||
const Texture = struct {
|
||||
texture: gl.Texture,
|
||||
|
||||
pub fn init(text: [:0]const u8) Text {
|
||||
pub fn init(data: [*]const u8, width: usize, height: usize) Texture {
|
||||
var tex = gl.genTexture();
|
||||
//defer gl.Texture.delete(tex);
|
||||
gl.bindTexture(tex, .@"2d");
|
||||
|
||||
var font = sdl.ttf.openFont("res/fonts/MarginaliaRegular-8XlZ.ttf", 200) catch unreachable;
|
||||
//defer font.close();
|
||||
var surface = font.renderTextBlended(text, sdl.Color.white) catch unreachable;
|
||||
const width = @intCast(usize, surface.ptr.w);
|
||||
const height = @intCast(usize, surface.ptr.h);
|
||||
var newsurf = sdl.c.SDL_ConvertSurfaceFormat(surface.ptr, @enumToInt(sdl.PixelFormatEnum.argb8888), 0) orelse unreachable;
|
||||
const data = @ptrCast([*]const u8, newsurf.pixels);
|
||||
//defer surface.destroy();
|
||||
|
||||
gl.textureImage2D(.@"2d", 0, .rgba, width, height, .rgba, .unsigned_byte, data);
|
||||
|
||||
gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge);
|
||||
|
@ -263,12 +258,27 @@ const Text = struct {
|
|||
gl.texParameter(.@"2d", .min_filter, .linear);
|
||||
gl.texParameter(.@"2d", .mag_filter, .linear);
|
||||
|
||||
return Text {
|
||||
return Texture{
|
||||
.texture = tex,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Text) void {
|
||||
_ = self;
|
||||
pub fn fromText(text: [:0]const u8) Texture {
|
||||
var font = sdl.ttf.openFont("res/fonts/MarginaliaRegular-8XlZ.ttf", 200) catch unreachable;
|
||||
defer font.close();
|
||||
|
||||
var surface = font.renderTextBlended(text, sdl.Color.white) catch unreachable;
|
||||
defer surface.destroy();
|
||||
|
||||
const width = @intCast(usize, surface.ptr.w);
|
||||
const height = @intCast(usize, surface.ptr.h);
|
||||
var newsurf = sdl.c.SDL_ConvertSurfaceFormat(surface.ptr, @enumToInt(sdl.PixelFormatEnum.argb8888), 0) orelse unreachable;
|
||||
const data = @ptrCast([*]const u8, newsurf.pixels);
|
||||
|
||||
return Texture.init(data, width, height);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Texture) void {
|
||||
gl.Texture.delete(self.texture);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#version 330 core
|
||||
|
||||
in vec4 color;
|
||||
|
||||
void main(){
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -1,16 +1,12 @@
|
|||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec2 pos;
|
||||
layout (location = 1) in vec4 v_color;
|
||||
layout (location = 2) in vec2 texcoord;
|
||||
out vec2 Texcoord;
|
||||
layout (location = 1) in vec4 vColor;
|
||||
out vec4 color;
|
||||
|
||||
uniform mat4 mvp;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
Texcoord = texcoord;
|
||||
color = v_color;
|
||||
color = vColor;
|
||||
gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1);
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 texcoord;
|
||||
in vec4 color;
|
||||
in vec2 Texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main(){
|
||||
gl_FragColor = texture(tex, Texcoord) * color;
|
||||
gl_FragColor = texture(tex, texcoord) * color;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec2 pos;
|
||||
layout (location = 1) in vec4 vColor;
|
||||
out vec4 color;
|
||||
layout (location = 2) in vec2 vTexcoord;
|
||||
out vec2 texcoord;
|
||||
|
||||
uniform mat4 mvp;
|
||||
|
||||
void main() {
|
||||
texcoord = vTexcoord;
|
||||
color = vColor;
|
||||
gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1);
|
||||
}
|
Loading…
Reference in New Issue