WIP2: Text/texture rendering
This commit is contained in:
		
							parent
							
								
									ebe6aa8d9a
								
							
						
					
					
						commit
						9d23bbc39e
					
				
							
								
								
									
										222
									
								
								src/Renderer.zig
								
								
								
								
							
							
						
						
									
										222
									
								
								src/Renderer.zig
								
								
								
								
							|  | @ -8,18 +8,24 @@ const Self = @This(); | ||||||
| 
 | 
 | ||||||
| window: sdl.Window, | window: sdl.Window, | ||||||
| context: sdl.gl.Context, | context: sdl.gl.Context, | ||||||
| //color_loc: u32, |  | ||||||
| buffer: gl.Buffer, | 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, | // The index of the program is the enum converted to int | ||||||
| //colors: [max_objects][4]f32 = .{[4]f32{ 0.0, 0.0, 0.0, 1.0 }} ** max_objects, | programs: [nPrograms]gl.Program = .{undefined} ** nPrograms, | ||||||
| vbo_index: usize = 0, |  | ||||||
| 
 | 
 | ||||||
| const max_objects: usize = 16384; | 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 { | fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque { | ||||||
|     _ = p; |     _ = p; | ||||||
|     return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc)); |     return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc)); | ||||||
|  | @ -43,57 +49,30 @@ pub fn init() !Self { | ||||||
|         .{ .context = .opengl, .vis = .shown }, |         .{ .context = .opengl, .vis = .shown }, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     const proc: []const u8 = undefined; | ||||||
|  |     try gl.loadExtensions(proc, glGetProcAddress); | ||||||
|  | 
 | ||||||
|     const ctx = try sdl.gl.createContext(window); |     const ctx = try sdl.gl.createContext(window); | ||||||
|     sdl.gl.setSwapInterval(.immediate) catch { |     sdl.gl.setSwapInterval(.immediate) catch { | ||||||
|         std.debug.print("WARNING: Unable to configure the swap interval.\n", .{}); |         std.debug.print("WARNING: Unable to configure the swap interval.\n", .{}); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const proc: []const u8 = undefined; |     gl.enable(.blend); | ||||||
|     try gl.loadExtensions(proc, glGetProcAddress); |     gl.blendFunc(.src_alpha, .one_minus_src_alpha); | ||||||
| 
 | 
 | ||||||
|     // Shader stuff |     var buf = gl.Buffer.gen(); | ||||||
|     var mvp_loc: u32 = undefined; |     buf.bind(.array_buffer); | ||||||
|     const 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/vector.vs")}); |     var renderer = Self{ .window = window, .context = ctx, .buffer = buf }; | ||||||
|         fs.source(1, &.{@embedFile("shaders/fragment.fs")}); |  | ||||||
| 
 | 
 | ||||||
|         vs.compile(); |     // Load the shader programs according to the names in the enum | ||||||
|         fs.compile(); |     inline for (std.meta.fields(shaderProgram)) |programEnum| { | ||||||
| 
 |         renderer.programs[programEnum.value] = renderer.loadProgram(programEnum.name); | ||||||
|         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}); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var vertex_array = gl.VertexArray.gen(); |     var vertex_array = gl.VertexArray.gen(); | ||||||
|     vertex_array.bind(); |     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.vertexAttribPointer(0, 2, .float, false, @sizeOf(f32) * 8, @sizeOf(f32) * 0); | ||||||
|     gl.enableVertexAttribArray(0); |     gl.enableVertexAttribArray(0); | ||||||
| 
 | 
 | ||||||
|  | @ -105,31 +84,74 @@ pub fn init() !Self { | ||||||
| 
 | 
 | ||||||
|     gl.clearColor(0.91, 0.85, 0.65, 1.00); |     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 |     var text = Texture.fromText("testing123"); | ||||||
|     gl.enable(.blend); |     _ = text; | ||||||
|     gl.blendFunc(.src_alpha, .one_minus_src_alpha); |     //text.deinit(); | ||||||
| 
 | 
 | ||||||
|     var text = Text.init("testing123"); |     return renderer; | ||||||
|     text.deinit(); | } | ||||||
| 
 | 
 | ||||||
|     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 { | pub fn render(self: *Self) void { | ||||||
|     //std.debug.print("Number of rendered elements: {}\n", .{self.test_counter}); |     // Render with each of the defined shader programs | ||||||
|     self.test_counter = 0; |     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); | ||||||
| 
 | 
 | ||||||
|     gl.drawArrays(.triangles, 0, self.vbo_index); |         switch (@intToEnum(shaderProgram, program_i)) { | ||||||
|     sdl.gl.swapWindow(self.window); |             .color => { | ||||||
|     gl.clear(.{ .color = true }); |                 gl.drawArrays(.triangles, 0, self.vbo_index[program_i]); | ||||||
|  |             }, | ||||||
|  |             .texture => { | ||||||
|  |                 gl.drawArrays(.triangles, 0, self.vbo_index[program_i]); | ||||||
|  |             }, | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|     self.vbo_index = 0; |         self.vbo_index[program_i] = 0; | ||||||
| 
 | 
 | ||||||
|         // Clear the vbo, this really shouldn't be necessary as the index is set to zero, |         // Clear the vbo, this really shouldn't be necessary as the index is set to zero, | ||||||
|         // but otherwise it leads to bugs |         // but otherwise it leads to bugs | ||||||
|     self.vbo = .{0.0} ** max_objects; |         self.vbo[program_i] = .{0.0} ** max_objects; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sdl.gl.swapWindow(self.window); | ||||||
|  |     gl.clear(.{ .color = true }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn deinit(self: *Self) void { | 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 { | pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void { | ||||||
|     // TODO: Is this still necessary? |     const program_i = @enumToInt(shaderProgram.texture); | ||||||
|     self.test_counter += 1; |  | ||||||
| 
 | 
 | ||||||
|     var xf = @intToFloat(f32, x); |     var xf = @intToFloat(f32, x); | ||||||
|     var yf = @intToFloat(f32, y); |     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 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{ |     const vertex_data = [_]f32{ | ||||||
|         xf + skew_x_offset, yf, // up-left |         xf + skew_x_offset, yf, // up-left | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],      self.color[1], | ||||||
|  |         self.color[2],      self.color[3], | ||||||
|         0.0,                0.0, |         0.0,                0.0, | ||||||
|         xf + wf + skew_x_offset, yf, // up-right |         xf + wf + skew_x_offset, yf, // up-right | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],           self.color[1], | ||||||
|  |         self.color[2],           self.color[3], | ||||||
|         1.0,                     0.0, |         1.0,                     0.0, | ||||||
|         xf - skew_x_offset, yf + hf, // down-left |         xf - skew_x_offset, yf + hf, // down-left | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],      self.color[1], | ||||||
|  |         self.color[2],      self.color[3], | ||||||
|         0.0,                1.0, |         0.0,                1.0, | ||||||
|         xf + wf - skew_x_offset, yf + hf, // down-right |         xf + wf - skew_x_offset, yf + hf, // down-right | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],           self.color[1], | ||||||
|  |         self.color[2],           self.color[3], | ||||||
|         1.0,                     1.0, |         1.0,                     1.0, | ||||||
|         xf + wf + skew_x_offset, yf, // up-right |         xf + wf + skew_x_offset, yf, // up-right | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],           self.color[1], | ||||||
|  |         self.color[2],           self.color[3], | ||||||
|         1.0,                     0.0, |         1.0,                     0.0, | ||||||
|         xf - skew_x_offset, yf + hf, // down-left |         xf - skew_x_offset, yf + hf, // down-left | ||||||
|         self.color[0], self.color[1], self.color[2], self.color[3], |         self.color[0],      self.color[1], | ||||||
|  |         self.color[2],      self.color[3], | ||||||
|         0.0,                1.0, |         0.0,                1.0, | ||||||
|     }; |     }; | ||||||
|     std.mem.copy(f32, self.vbo[i .. i + vertex_data.len], &vertex_data); |     std.mem.copy(f32, self.vbo[program_i][i..], &vertex_data); | ||||||
|     self.vbo_index += vertex_data.len; |     self.vbo_index[program_i] += vertex_data.len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void { | 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{ | const Texture = struct { | ||||||
|     // 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 { |  | ||||||
|     texture: gl.Texture, |     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(); |         var tex = gl.genTexture(); | ||||||
|         //defer gl.Texture.delete(tex); |         //defer gl.Texture.delete(tex); | ||||||
|         gl.bindTexture(tex, .@"2d"); |         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.textureImage2D(.@"2d", 0, .rgba, width, height, .rgba, .unsigned_byte, data); | ||||||
| 
 | 
 | ||||||
|         gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge); |         gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge); | ||||||
|  | @ -263,12 +258,27 @@ const Text = struct { | ||||||
|         gl.texParameter(.@"2d", .min_filter, .linear); |         gl.texParameter(.@"2d", .min_filter, .linear); | ||||||
|         gl.texParameter(.@"2d", .mag_filter, .linear); |         gl.texParameter(.@"2d", .mag_filter, .linear); | ||||||
| 
 | 
 | ||||||
|         return Text { |         return Texture{ | ||||||
|             .texture = tex, |             .texture = tex, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn deinit(self: *Text) void { |     pub fn fromText(text: [:0]const u8) Texture { | ||||||
|         _ = self; |         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 | #version 330 core | ||||||
| 
 | 
 | ||||||
| layout (location = 0) in vec2 pos; | layout (location = 0) in vec2 pos; | ||||||
| layout (location = 1) in vec4 v_color; | layout (location = 1) in vec4 vColor; | ||||||
| layout (location = 2) in vec2 texcoord; | out vec4 color; | ||||||
| out vec2 Texcoord; |  | ||||||
| 
 | 
 | ||||||
| uniform mat4 mvp; | uniform mat4 mvp; | ||||||
| 
 | 
 | ||||||
| out vec4 color; |  | ||||||
| 
 |  | ||||||
| void main() { | void main() { | ||||||
|      Texcoord = texcoord; |      color = vColor; | ||||||
|      color = v_color; |  | ||||||
|      gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1); |      gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1); | ||||||
| } | } | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| #version 330 core | #version 330 core | ||||||
| 
 | 
 | ||||||
|  | in vec2 texcoord; | ||||||
| in vec4 color; | in vec4 color; | ||||||
| in vec2 Texcoord; |  | ||||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||||
| 
 | 
 | ||||||
| void main(){ | 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