I try to implement a trivial graphical engine for my app based on OpenGL (glutin
+ glow
). Below is my draw_object()
function:
fn draw_object(&mut self, object: &Object) {
unsafe {
let mesh_ref = object.mesh();
let material_ref = object.material();
let mesh = mesh_ref.borrow();
let material = material_ref.borrow();
let gl_mesh = mesh.as_ref().as_any().downcast_ref::<GlMesh>().unwrap();
let gl_material = material.as_ref().as_any().downcast_ref::<GlMaterial>().unwrap();
self.gl.use_program(Some(gl_material.program));
self.gl.bind_buffer(glow::ARRAY_BUFFER, gl_mesh.vbo);
match &gl_material.attrs {
MaterialAttributes::VertexColors => {
if gl_mesh.has_colors() {
self.gl.bind_vertex_array(gl_mesh.color_vao);
}
},
MaterialAttributes::Color(c) => {
let color_location = self.gl.get_uniform_location(gl_material.program, "solidColor");
self.gl.uniform_4_f32(color_location.as_ref(), c.r, c.g, c.b, c.a);
},
MaterialAttributes::Texture(t) => {
if gl_mesh.has_uvs {
let t = t.as_ref().as_ref().as_any().downcast_ref::<GlTexture>().unwrap();
self.gl.bind_texture(glow::TEXTURE_2D,Some(t.texture));
self.gl.bind_vertex_array(gl_mesh.uv_vao);
}
},
}
self.gl.bind_vertex_array(gl_mesh.mesh_vao);
self.gl.draw_arrays(glow::TRIANGLES, 0, gl_mesh.vertex_count().try_into().unwrap());
}
}
Into this function I pass three objects:
fn main() {
let app = App::builder()
.title(String::from(APP_NAME))
.build();
let event_loop = GlEventLoop::new();
let window = GlWindow::new(&event_loop, app.title.to_owned(), 800, 600).unwrap();
let renderer = GlRenderer::new(&window).unwrap();
let mut id_gen = IdGen::new();
let fabric = GlFabric::new(&renderer);
let solid_attrs = MaterialAttributes::Color(ColorF::rgb(1.0, 0.4, 0.0));
let solid_material = create_material(app.assets(), &fabric, id_gen.next(), "SolidColor".to_owned(), solid_attrs);
let texture: Rc<Box<dyn Texture>> = Rc::new(Box::new(fabric.load_asset(id_gen.next(), app.assets(), "textures/wood.jpg").unwrap()));
let texture_attrs = MaterialAttributes::Texture(texture.clone());
let texture_material = create_material(app.assets(), &fabric, id_gen.next(), "Texture".to_owned(), texture_attrs);
let colorful_attrs = MaterialAttributes::VertexColors;
let colorful_material = create_material(app.assets(), &fabric, id_gen.next(), "ColoredVertex".to_owned(), colorful_attrs);
let vertices = [
Vertex3::new(-0.7, -0.7, 0.0),
Vertex3::new(0.7, -0.7, 0.0),
Vertex3::new(0.7, 0.7, 0.0),
Vertex3::new(-0.7, -0.7, 0.0),
Vertex3::new(0.7, 0.7, 0.0),
Vertex3::new(-0.7, 0.7, 0.0),
];
let mut box_object = create_object(&fabric, &mut id_gen, "Box".to_owned(), &vertices, None, None, solid_material.clone());
let vertices = [
Vertex3::new(-0.5, -0.5, 0.0),
Vertex3::new(0.5, 0.5, 0.0),
Vertex3::new(-0.5, 0.5, 0.0),
];
let uvs = [
Vertex2::new(0.0, 0.0),
Vertex2::new(1.0, 1.0),
Vertex2::new(0.0, 1.0),
];
let mut top_left = create_object(&fabric, &mut id_gen, "TopLeft".to_owned(), &vertices, None, Some(&uvs), texture_material.clone());
let vertices = [
Vertex3::new(-0.5, -0.5, 0.0),
Vertex3::new(0.5, -0.5, 0.0),
Vertex3::new(0.5, 0.5, 0.0),
];
let colors = [
ColorF::rgb(1.0, 0.0, 0.0),
ColorF::rgb(0.0, 1.0, 0.0),
ColorF::rgb(0.0, 0.0, 1.0),
];
let mut bottom_right = create_object(&fabric, &mut id_gen, "BottomRight".to_owned(), &vertices, Some(&colors), None, colorful_material.clone());
let background_color = ColorF::rgb(1.0, 1.0, 1.0);
event_loop.run(move |event| {
match event {
Event::START => {
box_object.on_init();
top_left.on_init();
bottom_right.on_init();
},
Event::UDPATE => {
let mut frame = renderer.draw();
frame.clear_color(&background_color);
frame.draw_object(&box_object);
frame.draw_object(&top_left);
frame.draw_object(&bottom_right);
frame.finish();
window.raw_window.request_redraw();
renderer.swap_buffers();
},
Event::EXIT => {
bottom_right.on_delete();
top_left.on_delete();
box_object.on_delete();
},
}
});
}
The first object is a box (two triangles) with solid color.
The second one is a textured triangle (it fails too but I will resolve problems one by one).
Finally, the third one is a coloured vertex triangle. However, when I launch my app, it outputs a black triangle:
And I can't get why... Here is my coloured vertex shaders:
Vertex shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 aColor;
out vec4 ourColor;
void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}
Fragment shader:
#version 330 core
out vec4 FragColor;
in vec4 ourColor;
void main() {
FragColor = ourColor;
}
And Mesh
implementation:
#[derive(Debug)]
pub struct GlMesh {
gl: Rc<Context>,
pub id: Id,
pub name: String,
pub draw_mode: u32,
pub visible: bool,
pub has_colors: bool,
pub has_uvs: bool,
pub data: Vec<f32>,
pub vbo: Option<NativeBuffer>,
pub mesh_vao: Option<NativeVertexArray>,
pub color_vao: Option<NativeVertexArray>,
pub uv_vao: Option<NativeVertexArray>,
}
impl GlMesh {
pub fn new(gl: Rc<Context>, id: Id, name: String, vertices: &[Vertex3], colors: Option<&[ColorF]>, uvs: Option<&[Vertex2]>) -> Result<Self, GlMeshError> {
let draw_mode = glow::STATIC_DRAW;
let data = Self::create_data(vertices, colors, uvs)?;
Ok(Self {
gl,
id,
name,
draw_mode,
visible: true,
has_colors: colors.is_some(),
has_uvs: uvs.is_some(),
data,
vbo: None,
mesh_vao: None,
color_vao: None,
uv_vao: None,
})
}
fn create_data(vertices: &[Vertex3], colors: Option<&[ColorF]>, uvs: Option<&[Vertex2]>) -> Result<Vec<f32>, GlMeshError> {
let colors_len: usize;
if colors.is_some() {
colors_len = colors.unwrap().len();
if vertices.len() != colors_len {
return Err(GlMeshError::InvalidData("The number of colors and vertices must be same".to_owned()));
}
} else {
colors_len = 0;
}
let uvs_len: usize;
if uvs.is_some() {
uvs_len = uvs.unwrap().len();
if vertices.len() != uvs.unwrap().len() {
return Err(GlMeshError::InvalidData("The number of UV vertices and mesh vertices must be same".to_owned()));
}
} else {
uvs_len = 0;
}
let data_len = 3 * vertices.len() + 4 * colors_len + 2 * uvs_len;
let mut data = Vec::with_capacity(data_len);
for (i, v) in vertices.iter().enumerate() {
data.extend(v.data);
if colors.is_some() {
let color = colors.unwrap().get(i).unwrap();
data.push(color.r);
data.push(color.g);
data.push(color.b);
data.push(color.a);
}
if uvs.is_some() {
let uv_vertex = uvs.unwrap().get(i).unwrap();
data.extend(uv_vertex.data);
}
}
Ok(data)
}
fn rebuild_buffers(&mut self) -> Result<(), GlMeshError> {
let vbo = Self::create_vbo(&self.gl)?;
Self::set_buffer_data(&self.gl, vbo, &self.data, self.draw_mode);
self.vbo = Some(vbo);
let mut stride = 12;
if self.has_colors {
stride += 16;
}
if self.has_uvs {
stride += 8;
}
let mut offset = 0;
self.mesh_vao = Some(Self::create_vao(&self.gl, 0, 3, false, stride, offset)?);
offset += 12;
if self.has_colors {
self.color_vao = Some(Self::create_vao(&self.gl, 1, 4, false, stride, offset)?);
offset += 16;
}
if self.has_uvs {
self.uv_vao = Some(Self::create_vao(&self.gl, 2, 2, false, stride, offset)?);
}
Ok(())
}
fn create_vbo(gl: &Context) -> Result<NativeBuffer, GlMeshError> {
unsafe {
match gl.create_buffer() {
Ok(vbo) => Ok(vbo),
Err(msg) => Err(GlMeshError::InternalGlError(msg)),
}
}
}
fn set_buffer_data(gl: &Context, vbo: NativeBuffer, data: &Vec<f32>, draw_mode: u32) {
unsafe {
let data_u8: &[u8] = core::slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * core::mem::size_of::<f32>(),
);
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, data_u8, draw_mode);
}
}
fn delete_vbo(gl: &Context, vbo: NativeBuffer) {
unsafe {
gl.delete_buffer(vbo);
}
}
fn create_vao(gl: &Context, index: u32, size: i32, normalized: bool, stride: i32, offset: i32) -> Result<NativeVertexArray, GlMeshError> {
unsafe {
match gl.create_vertex_array() {
Ok(vao) => {
gl.bind_vertex_array(Some(vao));
gl.vertex_attrib_pointer_f32(index, size, glow::FLOAT, normalized, stride, offset);
gl.enable_vertex_attrib_array(index);
Ok(vao)
},
Err(msg) => Err(GlMeshError::InternalGlError(msg)),
}
}
}
fn delete_vao(gl: &Context, vao: NativeVertexArray) {
unsafe {
gl.delete_vertex_array(vao);
}
}
}
impl LifeCycle for GlMesh {
fn on_init(&mut self) {
self.rebuild_buffers().unwrap();
}
fn on_delete(&mut self) {
if self.vbo.is_some() {
Self::delete_vbo(&self.gl, self.vbo.unwrap());
}
if self.mesh_vao.is_some() {
Self::delete_vao(&self.gl, self.mesh_vao.unwrap());
}
if self.color_vao.is_some() {
Self::delete_vao(&self.gl, self.color_vao.unwrap());
}
if self.uv_vao.is_some() {
Self::delete_vao(&self.gl, self.uv_vao.unwrap());
}
}
}
I've checked VBO and VAOs in debugger (although my debugger can't work with Rust well) and I didn't notice any errors.
Where can the problem be?