I'm using "Learn OpenGL" Book, but I stopped on displaying a texture.
My screen would show only the Clear Color and nothing else.
If I change back shader code to show only color it will work.
Had I missed something?
My Code
sgl.rs:
use epoxy as gl;
use gl::types::*;
pub struct Buffer {}
pub struct Texture(GLuint);
impl Texture {
pub fn from_file(path: &str) -> Result<Self, &'static str> {
let img = match image::open(path) {
Ok(img) => img.into_rgba8(),
Err(_) => return Err("Can't open an image")
};
let mut texture: Self = Self(0);
unsafe {
gl::GenTextures(1, &mut texture.0);
}
if texture.0 == 0 {
return Err("Couldn't initialize texture");
}
texture.bind();
unsafe {
gl::TexParameteri(
gl::TEXTURE_2D_ARRAY,
gl::TEXTURE_MIN_FILTER,
gl::LINEAR as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D_ARRAY,
gl::TEXTURE_MAG_FILTER,
gl::LINEAR as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D_ARRAY,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D_ARRAY,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as i32,
);
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGBA8 as i32,
img.width() as i32,
img.height() as i32,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
(&img as &[u8]).as_ptr() as *const _, // TODO
);
gl::GenerateMipmap(gl::TEXTURE_2D);
}
return Ok( texture );
}
pub fn bind(&self) {
unsafe {
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, self.0);
}
}
}
pub enum ShaderType {
Vertex = gl::VERTEX_SHADER as isize,
Fragment = gl::FRAGMENT_SHADER as isize,
}
pub struct Shader(pub GLuint);
impl Shader {
fn new(ty: ShaderType) -> Option<Self> {
let shader = unsafe { gl::CreateShader(ty as GLenum) };
if shader != 0 {
Some(Self(shader))
} else {
None
}
}
fn set_source(&self, src: &str) {
unsafe {
gl::ShaderSource(
self.0,
1,
&(src.as_bytes().as_ptr().cast()),
&(src.len().try_into().unwrap()),
);
}
}
fn compile(&self) {
unsafe { gl::CompileShader(self.0) };
}
fn compile_success(&self) -> bool {
let mut compiled = 0;
unsafe { gl::GetShaderiv(self.0, gl::COMPILE_STATUS, &mut compiled) };
compiled == i32::from(gl::TRUE)
}
fn info_log(&self) -> String {
let mut needed_len = 0;
unsafe { gl::GetShaderiv(self.0, gl::INFO_LOG_LENGTH, &mut needed_len) };
let mut v: Vec<u8> = Vec::with_capacity(needed_len.try_into().unwrap());
let mut len_written = 0_i32;
unsafe {
gl::GetShaderInfoLog(
self.0,
v.capacity().try_into().unwrap(),
&mut len_written,
v.as_mut_ptr().cast(),
);
v.set_len(len_written.try_into().unwrap());
}
String::from_utf8_lossy(&v).into_owned()
}
pub fn delete(self) {
unsafe { gl::DeleteShader(self.0) };
}
pub fn from_source(ty: ShaderType, source: &str) -> Result<Self, String> {
let id = Self::new(ty)
.ok_or_else(|| "Couldn't allocate new shader".to_string())?;
id.set_source(source);
id.compile();
if id.compile_success() {
Ok(id)
} else {
let out = id.info_log();
id.delete();
Err(out)
}
}
}
refarea.rs
use gtk4 as gtk;
use epoxy as gl;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{glib, gdk::GLContext, GLArea, Widget};
use crate::sgl::*;
use core::mem::{size_of, size_of_val};
use std::cell::Cell;
use std::ffi::CString;
glib::wrapper! {
pub struct RefAreaWidget (
ObjectSubclass<RefAreaWidgetPriv>
) @extends GLArea, Widget;
}
impl RefAreaWidget {
pub fn new() -> Self {
glib::Object::new::<Self>()
}
}
impl Default for RefAreaWidget {
fn default() -> Self {
Self::new()
}
}
pub struct RefAreaWidgetPriv {
shader_program: Cell<gl::types::GLuint>
}
impl ObjectImpl for RefAreaWidgetPriv {
fn constructed(&self) {
self.parent_constructed();
}
}
#[glib::object_subclass]
impl ObjectSubclass for RefAreaWidgetPriv {
const NAME: &'static str = "RefAreaWidget";
type Type = RefAreaWidget;
type ParentType = GLArea;
fn new() -> Self {
let mut refarea = Self {
shader_program: Cell::new(0),
};
return refarea;
}
}
impl WidgetImpl for RefAreaWidgetPriv {
fn realize(&self) {
self.parent_realize();
}
}
impl GLAreaImpl for RefAreaWidgetPriv {
fn create_context(&self) -> Option<GLContext> {
println!("Create Context");
let new_ctx = self.parent_create_context();
if let Some(ctx) = &new_ctx {
ctx.make_current();
unsafe {
gl::ClearColor(0.1, 0.0, 0.1, 1.);
//gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
//gl::Enable(gl::BLEND);
//gl::FrontFace(gl::CULL_FACE);
}
let texture = Texture::from_file("debug2.png").unwrap();
texture.bind();
unsafe {
let mut vao = 0;
gl::GenVertexArrays(1, &mut vao);
assert_ne!(vao, 0);
gl::BindVertexArray(vao);
}
type Vertex = [f32; 8];
type TriIndexes = [u32; 3];
const VERTICES: [Vertex; 4] = [
[0.5, 0.5, 0., 1., 0., 0., 1., 1.],
[0.5, -0.5, 0., 0., 1., 0., 1., 0.],
[-0.5, -0.5, 0., 0., 0., 1., 0., 0.],
[-0.5, 0.5, 0., 1., 1., 1., 0., 1.]
];
const INDICES: [TriIndexes; 2] = [[0, 1, 3], [1, 2, 3]];
unsafe {
let mut vbo = 0;
gl::GenBuffers(1, &mut vbo);
assert_ne!(vbo, 0);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
size_of_val(&VERTICES) as isize,
VERTICES.as_ptr().cast(),
gl::STATIC_DRAW,
);
}
unsafe {
let mut ebo = 0;
gl::GenBuffers(2, &mut ebo);
assert_ne!(ebo, 0);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
size_of_val(&INDICES) as isize,
INDICES.as_ptr().cast(),
gl::STATIC_DRAW,
);
}
unsafe {
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
size_of::<Vertex>().try_into().unwrap(),
0 as *const _
);
gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(
1,
3,
gl::FLOAT,
gl::FALSE,
size_of::<Vertex>().try_into().unwrap(),
size_of::<[f32; 3]>() as *const _
);
gl::EnableVertexAttribArray(1);
gl::VertexAttribPointer(
2,
2,
gl::FLOAT,
gl::FALSE,
size_of::<Vertex>().try_into().unwrap(),
size_of::<[f32; 6]>() as *const _
);
gl::EnableVertexAttribArray(2);
}
// Vertex Shader
const VERT_SHADER: &str = r#"
#version 330 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texture;
out vec4 outColor;
out vec2 texturePos;
void main() {
gl_Position = vec4(pos, 1.0);
outColor = vec4(color, 1.0);
texturePos = texture;
}
"#;
let vertex_shader = match Shader::from_source(ShaderType::Vertex, VERT_SHADER) {
Ok(ok) => ok,
Err(err) => panic!("Vertex Compile Error: {}", err)
};
// Fragment Shader
const FRAG_SHADER: &str = r#"
#version 330 core
out vec4 finalColor;
in vec4 outColor;
in vec2 texturePos;
uniform sampler2D texture0;
void main() {
finalColor = texture(texture0, texturePos);
//finalColor = outColor;
}
"#;
let fragment_shader = match Shader::from_source(ShaderType::Fragment, FRAG_SHADER) {
Ok(ok) => ok,
Err(err) => panic!("Fragment Compile Error: {}", err)
};
// Setup Program
unsafe {
self.shader_program.set(gl::CreateProgram());
gl::AttachShader(self.shader_program.get(), vertex_shader.0);
gl::AttachShader(self.shader_program.get(), fragment_shader.0);
gl::LinkProgram(self.shader_program.get());
let mut success = 0;
gl::GetProgramiv(self.shader_program.get(), gl::LINK_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetProgramInfoLog(
self.shader_program.get(),
1024,
&mut log_len,
v.as_mut_ptr().cast(),
);
v.set_len(log_len.try_into().unwrap());
panic!("Program Link Error: {}", String::from_utf8_lossy(&v));
}
vertex_shader.delete();
fragment_shader.delete();
gl::UseProgram(self.shader_program.get());
}
unsafe {
let cstr = CString::new("texture0").unwrap();
gl::Uniform1i(
gl::GetUniformLocation(
self.shader_program.get(),
cstr.as_ptr()
),
0
);
}
}
return new_ctx;
}
fn render(&self, ctx: &GLContext) -> bool {
println!("Render");
ctx.make_current();
unsafe {
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, 0 as *const _);
}
//self.parent_render(context)
return true;
}
fn resize(&self, width: i32, height: i32) {
println!("Resize");
self.parent_resize(width, height)
}
}
main.rs
use gtk4 as gtk;
use gtk::prelude::*;
use gtk::{glib, Application, ApplicationWindow, Label};
use core::ptr;
mod sgl;
mod refarea;
use refarea::*;
fn main() -> glib::ExitCode {
// Load GL pointers from epoxy (GL context management library used by GTK).
{
#[cfg(target_os = "macos")]
let library = unsafe { libloading::os::unix::Library::new("libepoxy.0.dylib") }.unwrap();
#[cfg(all(unix, not(target_os = "macos")))]
let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap();
#[cfg(windows)]
let library = libloading::os::windows::Library::open_already_loaded("epoxy-0.dll").unwrap();
epoxy::load_with(|name| {
unsafe { library.get::<_>(name.as_bytes()) }
.map(|symbol| *symbol)
.unwrap_or(ptr::null())
});
}
let app = Application::builder()
.application_id("xyz.jptrzy.Grefe")
.build();
app.connect_activate(|app| {
let window = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Grefe")
.build();
let label = Label::builder()
.label("GTK 😍 RUST")
.build();
let refarea = RefAreaWidget::new();
window.set_child(Some(&refarea));
window.present();
});
app.run()
}