Skip to content

Instantly share code, notes, and snippets.

@matthewjberger
Created January 2, 2024 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewjberger/dce0bae948f5941d076260b20413e0fd to your computer and use it in GitHub Desktop.
Save matthewjberger/dce0bae948f5941d076260b20413e0fd to your computer and use it in GitHub Desktop.
OpenGL (glow) 3D interactive triangle in wasm and native using eframe. (This replaces app.rs in eframe, assumes "eframe/glow" feature is enabled)
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
pub struct TemplateApp {
angle: f32,
rotating_triangle: std::sync::Arc<egui::mutex::Mutex<RotatingTriangle>>,
}
impl TemplateApp {
/// Called once before the first frame.
pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Self {
let gl = cc.gl.as_ref().expect("Failed to get a GL context");
Self {
angle: 0.0,
rotating_triangle: std::sync::Arc::new(egui::mutex::Mutex::new(
RotatingTriangle::new(gl).expect("Failed to create triangle"),
)),
}
}
fn custom_painting(&mut self, ui: &mut egui::Ui) {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());
self.angle += response.drag_delta().x * 0.01;
// Clone locals so we can move them into the paint callback:
let angle = self.angle;
let rotating_triangle = self.rotating_triangle.clone();
let cb = eframe::egui_glow::CallbackFn::new(move |_info, painter| {
rotating_triangle.lock().paint(painter.gl(), angle);
});
let callback = egui::PaintCallback {
rect,
callback: std::sync::Arc::new(cb),
};
ui.painter().add(callback);
}
}
impl eframe::App for TemplateApp {
/// Called each time the UI needs repainting, which may be many times per second.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {
egui::Frame::canvas(ui.style()).show(ui, |ui| {
self.custom_painting(ui);
});
});
});
}
fn on_exit(&mut self, gl: Option<&eframe::glow::Context>) {
if let Some(gl) = gl {
self.rotating_triangle.lock().destroy(gl);
}
}
}
struct RotatingTriangle {
program: eframe::glow::Program,
vertex_array: eframe::glow::VertexArray,
}
#[allow(unsafe_code)] // we need unsafe code to use glow
impl RotatingTriangle {
fn new(gl: &eframe::glow::Context) -> Option<Self> {
use eframe::glow::HasContext as _;
let shader_version = eframe::egui_glow::ShaderVersion::get(gl);
unsafe {
let program = gl.create_program().expect("Cannot create program");
if !shader_version.is_new_shader_interface() {
log::warn!(
"Custom 3D painting hasn't been ported to {:?}",
shader_version
);
return None;
}
let (vertex_shader_source, fragment_shader_source) = (
r#"
const vec2 verts[3] = vec2[3](
vec2(0.0, 1.0),
vec2(-1.0, -1.0),
vec2(1.0, -1.0)
);
const vec4 colors[3] = vec4[3](
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0)
);
out vec4 v_color;
uniform float u_angle;
void main() {
v_color = colors[gl_VertexID];
gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);
gl_Position.x *= cos(u_angle);
}
"#,
r#"
precision mediump float;
in vec4 v_color;
out vec4 out_color;
void main() {
out_color = v_color;
}
"#,
);
let shader_sources = [
(eframe::glow::VERTEX_SHADER, vertex_shader_source),
(eframe::glow::FRAGMENT_SHADER, fragment_shader_source),
];
let shaders: Vec<_> = shader_sources
.iter()
.map(|(shader_type, shader_source)| {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(
shader,
&format!(
"{}\n{}",
shader_version.version_declaration(),
shader_source
),
);
gl.compile_shader(shader);
assert!(
gl.get_shader_compile_status(shader),
"Failed to compile custom_3d_glow {shader_type}: {}",
gl.get_shader_info_log(shader)
);
gl.attach_shader(program, shader);
shader
})
.collect();
gl.link_program(program);
assert!(
gl.get_program_link_status(program),
"{}",
gl.get_program_info_log(program)
);
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");
Some(Self {
program,
vertex_array,
})
}
}
fn destroy(&self, gl: &eframe::glow::Context) {
use eframe::glow::HasContext as _;
unsafe {
gl.delete_program(self.program);
gl.delete_vertex_array(self.vertex_array);
}
}
fn paint(&self, gl: &eframe::glow::Context, angle: f32) {
use eframe::glow::HasContext as _;
unsafe {
gl.use_program(Some(self.program));
gl.uniform_1_f32(
gl.get_uniform_location(self.program, "u_angle").as_ref(),
angle,
);
gl.bind_vertex_array(Some(self.vertex_array));
gl.draw_arrays(eframe::glow::TRIANGLES, 0, 3);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment