const Self = @This(); const rl = @import("raylib"); const gl = @import("opengl"); const std = @import("std"); const Allocator = std.mem.Allocator; const ShaderCode = @import("./shader-code.zig"); const Light = @import("./light.zig"); const assert = std.debug.assert; const max_lights = 1; const SHADER_LOC_VECTOR_VIEW = @intFromEnum(rl.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW); shader: rl.Shader, bloom_shader: rl.Shader, frame_buffer: rl.RenderTexture2D, bloom_texture: rl.Texture2D, camera: rl.Camera3D, lights: [max_lights]Light, rect: OpenglRect, cube: rl.Model, const OpenglRect = struct { vbo: u32, vao: u32, fn init() OpenglRect { var rect: OpenglRect = undefined; const vertices = [_]f32{ // Coords // texCoords 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 0.0, }; gl.genVertexArrays(1, &rect.vao); gl.genBuffers(1, &rect.vbo); gl.bindVertexArray(rect.vao); gl.bindBuffer(gl.ARRAY_BUFFER, rect.vbo); gl.bufferData(gl.ARRAY_BUFFER, vertices.len * @sizeOf(f32), @ptrCast(&vertices), gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 4 * @sizeOf(f32), @ptrFromInt(0)); gl.enableVertexAttribArray(1); gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 4 * @sizeOf(f32), @ptrFromInt(2 * @sizeOf(f32))); return rect; } fn draw(self: OpenglRect) void { gl.bindVertexArray(self.vao); gl.disable(gl.DEPTH_TEST); gl.drawArrays(gl.TRIANGLES, 0, 6); } }; fn loadEmptyTexture(width: i32, height: i32, format: rl.PixelFormat) rl.Texture2D { var target: rl.Texture2D = undefined; target.id = rl.rlLoadTexture(null, width, height, format, 1); target.width = width; target.height = height; target.format = format; target.mipmaps = 1; return target; } pub fn init(allocator: Allocator) !Self { const main_shader_code = try ShaderCode.init(allocator, max_lights); defer main_shader_code.deinit(); const shader = rl.LoadShaderFromMemory(main_shader_code.vertex, main_shader_code.fragment); errdefer rl.UnloadShader(shader); if (shader.id == rl.rlGetShaderIdDefault()) { return error.CompileShader; } const bloom_shader = rl.LoadShaderFromMemory(@embedFile("shaders/bloom_vs.glsl"), @embedFile("shaders/bloom_fs.glsl")); errdefer rl.UnloadShader(bloom_shader); if (bloom_shader.id == rl.rlGetShaderIdDefault()) { return error.CompileShader; } const screen_width = rl.GetScreenWidth(); const screen_height = rl.GetScreenHeight(); const frame_buffer = rl.LoadRenderTexture(screen_width, screen_height); errdefer rl.UnloadRenderTexture(frame_buffer); var bloom_texture = loadEmptyTexture(screen_width, screen_height, .PIXELFORMAT_UNCOMPRESSED_R8G8B8); rl.rlFramebufferAttach(frame_buffer.id, bloom_texture.id, .RL_ATTACHMENT_COLOR_CHANNEL1, .RL_ATTACHMENT_TEXTURE2D, 0); if (rl.rlFramebufferComplete(frame_buffer.id)) std.debug.print("INFO: Successfully attached bloom texture", .{}); rl.rlActiveDrawBuffers(2); var lights: [max_lights]Light = undefined; for (0..max_lights) |i| { lights[i] = Light.init(shader, i); } Light.init(shader, 0).set_point(rl.RED, rl.Vector3{ .x = 1, .y = 1, .z = 0.5 }, 2.0); shader.locs.?[SHADER_LOC_VECTOR_VIEW] = rl.GetShaderLocation(shader, "viewPos"); const ambientLoc = rl.GetShaderLocation(shader, "ambient"); rl.SetShaderValue(shader, ambientLoc, &[4]f32{ 0.6, 0.6, 1, 1.0 }, .SHADER_UNIFORM_VEC4); const rect = OpenglRect.init(); return Self{ .shader = shader, .bloom_shader = bloom_shader, .rect = rect, .frame_buffer = frame_buffer, .bloom_texture = bloom_texture, .camera = rl.Camera3D{ .position = rl.Vector3.new(0, 0, -10), .target = rl.Vector3.new(0.0, 0.0, 0.0), .up = rl.Vector3.new(0.0, 1.0, 0.0), .fovy = 45.0, .projection = rl.CameraProjection.CAMERA_PERSPECTIVE, }, .lights = lights, .cube = rl.LoadModelFromMesh(rl.GenMeshCube(1.0, 1.0, 1.0)) }; } pub fn deinit(self: Self) void { rl.UnloadShader(self.shader); rl.UnloadRenderTexture(self.frame_buffer); } pub fn update(self: *Self, dt: f32) void { const screen_width = rl.GetScreenWidth(); const screen_height = rl.GetScreenHeight(); if (self.frame_buffer.texture.width != screen_width or self.frame_buffer.texture.height != screen_height) { rl.UnloadRenderTexture(self.frame_buffer); rl.UnloadTexture(self.bloom_texture); self.frame_buffer = rl.LoadRenderTexture(screen_width, screen_height); self.bloom_texture = loadEmptyTexture(screen_width, screen_height, .PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); rl.rlFramebufferAttach(self.frame_buffer.id, self.bloom_texture.id, .RL_ATTACHMENT_COLOR_CHANNEL1, .RL_ATTACHMENT_TEXTURE2D, 0); if (rl.rlFramebufferComplete(self.frame_buffer.id)) std.debug.print("INFO: Successfully attached bloom texture", .{}); } rl.UpdateCamera(&self.camera, .CAMERA_THIRD_PERSON); const cameraPos = [3]f32{ self.camera.position.x, self.camera.position.y, self.camera.position.z }; rl.SetShaderValue(self.shader, self.shader.locs.?[SHADER_LOC_VECTOR_VIEW], @ptrCast(&cameraPos), .SHADER_UNIFORM_VEC3); _ = dt; } pub fn draw(self: *Self) void { rl.BeginTextureMode(self.frame_buffer); { rl.rlActiveDrawBuffers(2); const bg_color = [_]f32{ 33.0 / 255.0, 33.0 / 255.0, 33.0 / 255.0, 1.0 }; gl.clearBufferfv(gl.COLOR, 0, &bg_color); const transparent_color = [_]f32{ 0.0, 0.0, 0.0, 0.0 }; gl.clearBufferfv(gl.COLOR, 1, &transparent_color); gl.clear(gl.DEPTH_BUFFER_BIT); rl.BeginMode3D(self.camera); rl.BeginShaderMode(self.shader); self.cube.materials.?[0].shader = self.shader; rl.DrawModel(self.cube, rl.Vector3Zero(), 1.0, rl.WHITE); rl.EndShaderMode(); rl.EndMode3D(); } rl.EndTextureMode(); const screen_width = rl.GetScreenWidth(); const screen_height = rl.GetScreenHeight(); // const screen_rect = rl.Rectangle{ .x = 0, .y = 0, .width = @floatFromInt(screen_width), .height = @floatFromInt(screen_height) }; rl.BeginDrawing(); gl.useProgram(self.bloom_shader.id); rl.rlDisableDepthTest(); const renderSize = [_]f32{ @floatFromInt(screen_width), @floatFromInt(screen_height) }; rl.rlSetUniform(rl.rlGetLocationUniform(self.bloom_shader.id, "renderSize"), &renderSize, @intFromEnum(rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2), 1); rl.rlSetUniformSampler(rl.rlGetLocationUniform(self.bloom_shader.id, "screenTexture"), self.frame_buffer.texture.id); rl.rlSetUniformSampler(rl.rlGetLocationUniform(self.bloom_shader.id, "bloomTexture"), self.bloom_texture.id); gl.bindVertexArray(self.rect.vao); rl.rlActiveTextureSlot(1); rl.rlEnableTexture(self.frame_buffer.texture.id); rl.rlActiveTextureSlot(2); rl.rlEnableTexture(self.bloom_texture.id); gl.drawArrays(gl.TRIANGLES, 0, 6); rl.EndDrawing(); }