use crate::*;
use crate::rs_math3d::*;
use crate::nc_renderer::*;
use rs_ctypes::*;
use egui::{
paint::{Color32, Mesh, Texture},
vec2, ClippedMesh,
};
render_data! {
vertex Vertex {
a_pos : Vec2f,
a_tc : Vec2f,
s_rgba : Color4b,
}
uniforms Uniforms {
u_screen_size : Vec2f,
}
}
#[derive(Default)]
struct UserTexture {
size: (usize, usize),
pixels: Vec<u8>,
texture: Option<TexturePtr>,
filtering: bool,
dirty: bool,
}
const VS_SRC: &str = r#"
#version 300 es
uniform vec2 u_screen_size;
in highp vec2 a_pos;
in highp vec4 s_rgba;
in highp vec2 a_tc;
in highp vec3 b;
out highp vec4 v_rgba;
out vec2 v_tc;
void main() {
gl_Position = vec4(
2.0 * a_pos.x / u_screen_size.x - 1.0,
1.0 - 2.0 * a_pos.y / u_screen_size.y,
0.0,
1.0);
v_rgba = s_rgba;
v_tc = a_tc;
}
"#;
const FS_SRC: &str = r#"
#version 300 es
uniform lowp sampler2D u_sampler;
in highp vec4 v_rgba;
in highp vec2 v_tc;
in highp vec3 v_b;
layout(location = 0) out lowp vec4 f_color;
void main() {
highp vec4 tcol = texture(u_sampler, v_tc);
f_color = tcol * v_rgba;
}
"#;
pub struct Painter {
driver : DriverPtr,
pipeline: PipelinePtr,
index_buffer: DeviceBufferPtr,
vertex_buffer: DeviceBufferPtr,
canvas_width: u32,
canvas_height: u32,
egui_texture: Option<TexturePtr>,
egui_texture_version: Option<u64>,
user_textures: Vec<UserTexture>,
}
impl Painter {
pub fn new(
drv: &mut DriverPtr,
window: &mut glfw::Window,
canvas_width: u32,
canvas_height: u32,
) -> Painter {
let program = drv.create_shader(ShaderDesc {
vertex_shader : String::from(VS_SRC),
pixel_shader : String::from(FS_SRC),
vertex_attributes : vec!{
Vertex::get_attribute_names(),
},
vertex_uniforms : Uniforms::get_uniform_names(),
vertex_surfaces : vec!{},
pixel_uniforms : vec!{},
pixel_surfaces : vec!{ String::from("u_sampler") }
}).unwrap();
let vertex_layout = VertexBufferLayout {
buffer_id : 0,
vertex_attributes : Vertex::get_attribute_descriptors(),
stride : Vertex::stride(),
divisor : 0,
};
let pipeline_desc = PipelineDesc {
primitive_type : PrimitiveType::Triangles,
shader : program,
buffer_layouts : vec! { vertex_layout.clone() },
uniform_descs : Uniforms::get_uniform_descriptors(),
index_type : IndexType::UInt16,
face_winding : FaceWinding::CCW,
cull_mode : CullMode::None,
depth_write : true,
depth_test : false,
blend : BlendOp::Add(Blend::default()),
};
let pipeline = drv.create_pipeline(pipeline_desc).unwrap();
let index_buffer = drv.create_device_buffer(DeviceBufferDesc::Index(Usage::Dynamic(65536 * 4))).unwrap();
let vertex_buffer = drv.create_device_buffer(DeviceBufferDesc::Vertex(Usage::Dynamic(65536 * 4 * std::mem::size_of::<Vertex>()))).unwrap();
Painter {
driver: drv.clone(),
pipeline,
canvas_width,
canvas_height,
index_buffer,
vertex_buffer,
egui_texture: None,
egui_texture_version: None,
user_textures: Default::default(),
}
}
pub fn new_user_texture(
&mut self,
size: (usize, usize),
srgba_pixels: &[Color32],
filtering: bool,
) -> egui::TextureId {
assert_eq!(size.0 * size.1, srgba_pixels.len());
let mut pixels: Vec<u8> = Vec::with_capacity(srgba_pixels.len() * 4);
for srgba in srgba_pixels {
pixels.push(srgba.r());
pixels.push(srgba.g());
pixels.push(srgba.b());
pixels.push(srgba.a());
}
let id = egui::TextureId::User(self.user_textures.len() as u64);
self.user_textures.push(UserTexture {
size,
pixels,
texture: None,
filtering,
dirty: true,
});
id
}
fn upload_egui_texture(&mut self, texture: &Texture) {
if self.egui_texture_version == Some(texture.version) {
return; }
println!("uploading egui texture: {}x{}", texture.width, texture.height);
let mut pixels: Vec<u8> = Vec::with_capacity(texture.pixels.len() * 4);
for &alpha in &texture.pixels {
let srgba = Color32::from_white_alpha(alpha);
pixels.push(srgba.r());
pixels.push(srgba.g());
pixels.push(srgba.b());
pixels.push(srgba.a());
}
let tex_desc = TextureDesc {
sampler_desc : SamplerDesc::default(texture.width, texture.height)
.with_pixel_format(PixelFormat::RGBA8(MinMagFilter::default()
.with_mag_filter(Filter::Linear)
.with_min_filter(Filter::Linear)))
.with_wrap_mode(WrapMode::ClampToEdge),
payload : Some(Box::new(pixels))
};
let tex = self.driver.create_texture(tex_desc).unwrap();
self.egui_texture = Some(tex);
self.egui_texture_version = Some(texture.version);
}
fn upload_user_textures(&mut self) {
for user_texture in &mut self.user_textures {
if !user_texture.texture.is_none() && !user_texture.dirty {
continue;
}
let pixels = std::mem::take(&mut user_texture.pixels);
let tex_desc = TextureDesc {
sampler_desc : SamplerDesc::default(user_texture.size.0, user_texture.size.1).with_pixel_format(PixelFormat::RGBA8(MinMagFilter::default().with_mag_filter(Filter::Linear).with_min_filter(Filter::Linear))),
payload : Some(Box::new(pixels))
};
if user_texture.texture.is_none() {
println!("uploading user texture");
let tex = self.driver.create_texture(tex_desc).unwrap();
user_texture.texture = Some(tex);
} else {
self.driver.update_texture(&mut user_texture.texture.as_mut().unwrap(), tex_desc.payload.unwrap())
}
user_texture.dirty = false;
}
}
fn get_texture(&self, texture_id: egui::TextureId) -> TexturePtr {
match texture_id {
egui::TextureId::Egui => {
self.egui_texture.as_ref().unwrap().clone()
},
egui::TextureId::User(id) => {
let id = id as usize;
assert!(id < self.user_textures.len());
let texture = self.user_textures[id].texture.clone();
texture.expect("Should have been uploaded")
}
}
}
pub fn update_user_texture_data(&mut self, texture_id: egui::TextureId, pixels: &[Color32]) {
match texture_id {
egui::TextureId::Egui => {}
egui::TextureId::User(id) => {
let id = id as usize;
assert!(id < self.user_textures.len());
self.user_textures[id].pixels = Vec::with_capacity(pixels.len() * 4);
for p in pixels {
self.user_textures[id].pixels.push(p.r());
self.user_textures[id].pixels.push(p.g());
self.user_textures[id].pixels.push(p.b());
self.user_textures[id].pixels.push(p.a());
}
self.user_textures[id].dirty = true;
}
}
}
pub fn paint_jobs(
&mut self,
frame_buffer: Option<FrameBufferPtr>,
bg_color: Option<Color32>,
meshes: Vec<ClippedMesh>,
egui_texture: &Texture,
pixels_per_point: f32,
) {
self.upload_egui_texture(egui_texture);
self.upload_user_textures();
let screen_size_pixels = vec2(self.canvas_width as f32, self.canvas_height as f32);
let screen_size_points = screen_size_pixels / pixels_per_point;
for ClippedMesh(clip_rect, mesh) in meshes {
let clip_min_x = pixels_per_point * clip_rect.min.x;
let clip_min_y = pixels_per_point * clip_rect.min.y;
let clip_max_x = pixels_per_point * clip_rect.max.x;
let clip_max_y = pixels_per_point * clip_rect.max.y;
let clip_min_x = clip_min_x.clamp(0.0, screen_size_pixels.x);
let clip_min_y = clip_min_y.clamp(0.0, screen_size_pixels.y);
let clip_max_x = clip_max_x.clamp(clip_min_x, screen_size_pixels.x);
let clip_max_y = clip_max_y.clamp(clip_min_y, screen_size_pixels.y);
let clip_min_x = clip_min_x.round() as i32;
let clip_min_y = clip_min_y.round() as i32;
let clip_max_x = clip_max_x.round() as i32;
let clip_max_y = clip_max_y.round() as i32;
self.driver.set_scissor (
clip_min_x as u32,
self.canvas_height as u32 - clip_max_y as u32,
(clip_max_x - clip_min_x) as u32,
(clip_max_y - clip_min_y) as u32,
);
if mesh.vertices.len() > 0 {
self.paint_mesh(&mesh, Vec2f::new(screen_size_points.x, screen_size_points.y));
}
}
}
fn paint_mesh(&mut self, mesh: &Mesh, screen_size: Vec2f) {
debug_assert!(mesh.is_valid());
let indices: Vec<u16> = mesh.indices.iter().map(|idx| *idx as u16).collect();
let mut vertices: Vec<Vertex> = Vec::with_capacity(mesh.vertices.len());
for v in &mesh.vertices {
let vert = Vertex {
a_pos : Vec2f::new(v.pos.x, v.pos.y),
s_rgba : color4b(v.color[0], v.color[1], v.color[2], v.color[3]),
a_tc : Vec2f::new(v.uv.x, v.uv.y),
};
vertices.push(vert);
}
self.driver.update_device_buffer(&mut self.vertex_buffer, 0, &vertices);
self.driver.update_device_buffer(&mut self.index_buffer, 0, &indices);
let bindings = Bindings {
vertex_buffers : vec!{ self.vertex_buffer.clone() },
index_buffer : Some(self.index_buffer.clone()),
vertex_images : Vec::new(),
pixel_images : Vec::from([self.get_texture(mesh.texture_id)]),
};
let u = Uniforms { u_screen_size: screen_size };
self.driver.draw(&self.pipeline, &bindings, &u as *const Uniforms as *const c_void, (indices.len() / 3) as u32, 1);
}
}