use std::num::NonZeroU32; use glow::HasContext; use glutin::{ config::ConfigTemplateBuilder, context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext}, display::{GetGlDisplay, GlDisplay}, surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface}, }; use imgui_winit_support::WinitPlatform; use raw_window_handle::HasWindowHandle; use winit::{ dpi::LogicalSize, event_loop::EventLoop, window::{Window, WindowAttributes}, }; pub fn create_window( title: &str, context_api: Option, ) -> ( EventLoop<()>, Window, Surface, PossiblyCurrentContext, ) { let event_loop = EventLoop::new().unwrap(); let window_attributes = WindowAttributes::default() .with_title(title) .with_inner_size(LogicalSize::new(1024, 768)); let (window, cfg) = glutin_winit::DisplayBuilder::new() .with_window_attributes(Some(window_attributes)) .build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| { configs.next().unwrap() }) .expect("Failed to create OpenGL window"); let window = window.unwrap(); let mut context_attribs = ContextAttributesBuilder::new(); if let Some(context_api) = context_api { context_attribs = context_attribs.with_context_api(context_api); } let context_attribs = context_attribs.build(Some(window.window_handle().unwrap().as_raw())); let context = unsafe { cfg.display() .create_context(&cfg, &context_attribs) .expect("Failed to create OpenGL context") }; let surface_attribs = SurfaceAttributesBuilder::::new() .with_srgb(Some(true)) .build( window.window_handle().unwrap().as_raw(), NonZeroU32::new(1024).unwrap(), NonZeroU32::new(768).unwrap(), ); let surface = unsafe { cfg.display() .create_window_surface(&cfg, &surface_attribs) .expect("Failed to create OpenGL surface") }; let context = context .make_current(&surface) .expect("Failed to make OpenGL context current"); surface .set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) .expect("Failed to set swap interval"); (event_loop, window, surface, context) } pub fn glow_context(context: &PossiblyCurrentContext) -> glow::Context { unsafe { glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast()) } } pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { let mut imgui_context = imgui::Context::create(); imgui_context.set_ini_filename(None); let mut winit_platform = WinitPlatform::new(&mut imgui_context); winit_platform.attach_window( imgui_context.io_mut(), window, imgui_winit_support::HiDpiMode::Rounded, ); imgui_context .fonts() .add_font(&[imgui::FontSource::DefaultFontData { config: None }]); imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32; (winit_platform, imgui_context) } pub struct Triangler { pub program: ::Program, pub vertex_array: ::VertexArray, } impl Triangler { pub fn new(gl: &glow::Context, shader_header: &str) -> Self { const VERTEX_SHADER_SOURCE: &str = r#" const vec2 verts[3] = vec2[3]( vec2(0.5f, 1.0f), vec2(0.0f, 0.0f), vec2(1.0f, 0.0f) ); out vec2 vert; out vec4 color; vec4 srgb_to_linear(vec4 srgb_color) { // Calcuation as documented by OpenGL vec3 srgb = srgb_color.rgb; vec3 selector = ceil(srgb - 0.04045); vec3 less_than_branch = srgb / 12.92; vec3 greater_than_branch = pow((srgb + 0.055) / 1.055, vec3(2.4)); return vec4( mix(less_than_branch, greater_than_branch, selector), srgb_color.a ); } void main() { vert = verts[gl_VertexID]; color = srgb_to_linear(vec4(vert, 0.5, 1.0)); gl_Position = vec4(vert - 0.5, 0.0, 1.0); } "#; const FRAGMENT_SHADER_SOURCE: &str = r#" in vec2 vert; in vec4 color; out vec4 frag_color; vec4 linear_to_srgb(vec4 linear_color) { vec3 linear = linear_color.rgb; vec3 selector = ceil(linear - 0.0031308); vec3 less_than_branch = linear * 12.92; vec3 greater_than_branch = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055; return vec4( mix(less_than_branch, greater_than_branch, selector), linear_color.a ); } void main() { frag_color = linear_to_srgb(color); } "#; let mut shaders = [ (glow::VERTEX_SHADER, VERTEX_SHADER_SOURCE, None), (glow::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE, None), ]; unsafe { let vertex_array = gl .create_vertex_array() .expect("Cannot create vertex array"); let program = gl.create_program().expect("Cannot create program"); for (kind, source, handle) in &mut shaders { let shader = gl.create_shader(*kind).expect("Cannot create shader"); gl.shader_source(shader, &format!("{}\n{}", shader_header, *source)); gl.compile_shader(shader); if !gl.get_shader_compile_status(shader) { panic!("{}", gl.get_shader_info_log(shader)); } gl.attach_shader(program, shader); *handle = Some(shader); } gl.link_program(program); if !gl.get_program_link_status(program) { panic!("{}", gl.get_program_info_log(program)); } for &(_, _, shader) in &shaders { gl.detach_shader(program, shader.unwrap()); gl.delete_shader(shader.unwrap()); } Self { program, vertex_array, } } } pub fn render(&self, gl: &glow::Context) { unsafe { gl.clear_color(0.05, 0.05, 0.1, 1.0); gl.clear(glow::COLOR_BUFFER_BIT); gl.use_program(Some(self.program)); gl.bind_vertex_array(Some(self.vertex_array)); gl.draw_arrays(glow::TRIANGLES, 0, 3); } } pub fn destroy(&self, gl: &glow::Context) { unsafe { gl.delete_program(self.program); gl.delete_vertex_array(self.vertex_array); } } }