Verified Commit 2dc217b3 authored by insert's avatar insert

Add texture, framebuffer, expand mesh.

parent 4a83bbda
......@@ -7,13 +7,14 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = [ "native" ]
default = [ "native", "image" ]
native = [ "glutin", "takeable-option" ]
[dependencies]
hashbrown = "0.7.2"
glutin = { version = "0.24.0", optional = true }
takeable-option = { version = "0.5.0", optional = true }
gl = "0.14.0"
nalgebra-glm = "0.7.0"
glutin = { version = "0.24.0", optional = true }
takeable-option = { version = "0.5.0", optional = true }
image = { version = "0.23.4", optional = true }
......@@ -2,5 +2,5 @@
out vec4 FragColor;
void main() {
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
FragColor = vec4(1.0, 0.0, 0.0, 0.0);
}
#version 330 core
layout (location = 0) in vec3 vPos;
uniform mat4 mvp;
void main() {
gl_Position = vec4(vPos, 1.0);
gl_Position = mvp * vec4(vPos, 1.0);
}
use super::ct::{ContextTracker, ContextWrapper};
use super::viewport::Viewport;
use super::window::{Window, WindowOptions};
use glutin::PossiblyCurrent;
use glutin::event::WindowEvent;
use glutin::event_loop::EventLoop;
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowId;
use glutin::PossiblyCurrent;
use hashbrown::HashMap;
use std::sync::{Arc, Mutex};
pub struct ByolOptions {
swap_after_draw: bool,
}
......@@ -51,13 +53,22 @@ pub enum Event<'a> {
window_id: WindowId,
ctx: &'a mut ContextWrapper<PossiblyCurrent>,
},
CloseRequested {
window_id: WindowId,
},
Cleanup,
}
pub struct WindowReference {
ctx_id: usize,
viewport: Arc<Mutex<Viewport>>,
}
pub struct Byol {
options: ByolOptions,
event_loop: EventLoop<()>,
context_tracker: ContextTracker,
window_context: HashMap<WindowId, usize>,
window_context: HashMap<WindowId, WindowReference>,
}
impl Byol {
......@@ -105,32 +116,53 @@ impl Byol {
&mut self.context_tracker
}
pub fn register_window(&mut self, window: WindowId, ctx_id: usize) {
self.window_context.insert(window, ctx_id);
pub fn register_window(
&mut self,
window: WindowId,
ctx_id: usize,
viewport: Arc<Mutex<Viewport>>,
) {
self.window_context
.insert(window, WindowReference { ctx_id, viewport });
}
pub fn run<F: FnMut(Event) + 'static>(self, mut func: F) {
pub fn run<F: FnMut(Event, &mut dyn FnMut()) + 'static>(self, mut func: F) {
let options = self.options;
let win_ctx = self.window_context;
let mut ctx_tracker = self.context_tracker;
self.event_loop.run(move |event, _, _control_flow| {
println!("{:?}", event);
self.event_loop.run(move |event, _, control_flow| {
let mut close = || {
*control_flow = ControlFlow::Exit;
};
//println!("{:?}", event);
match event {
glutin::event::Event::LoopDestroyed => return,
glutin::event::Event::WindowEvent { event, .. } => match event { //window_id
WindowEvent::Resized(_physical_size) => {
//
glutin::event::Event::LoopDestroyed => {
func(Event::Cleanup, &mut close);
return;
}
glutin::event::Event::WindowEvent {
event, window_id, ..
} => match event {
glutin::event::WindowEvent::Resized(size) => {
let window_ref = win_ctx.get(&window_id).unwrap().clone();
window_ref
.viewport
.lock()
.unwrap()
.update_size(size.width as i32, size.height as i32);
}
WindowEvent::CloseRequested => {
//
glutin::event::WindowEvent::CloseRequested => {
func(Event::CloseRequested { window_id }, &mut close);
}
_ => (),
},
glutin::event::Event::RedrawRequested(window_id) => {
let ctx_id = win_ctx.get(&window_id).unwrap().clone();
let ctx = ctx_tracker.get_current(ctx_id).unwrap();
let window_ref = win_ctx.get(&window_id).unwrap().clone();
let ctx = ctx_tracker.get_current(window_ref.ctx_id).unwrap();
func(Event::Draw { window_id, ctx });
window_ref.viewport.lock().unwrap().enable();
func(Event::Draw { window_id, ctx }, &mut close);
if options.swap_after_draw {
ctx.windowed().swap_buffers().unwrap();
......
......@@ -2,12 +2,12 @@ use super::ct::{ContextCurrentWrapper, ContextWrapper};
use super::viewport::Viewport;
use super::Byol;
use glutin::window::{WindowBuilder, WindowId, Fullscreen};
use glutin::dpi::{LogicalSize, PhysicalSize, Size};
use glutin::window::{Fullscreen, WindowBuilder, WindowId};
use glutin::ContextBuilder;
use glutin::dpi::{Size, LogicalSize, PhysicalSize};
use glutin::{self};
use gl;
use std::sync::{Arc, Mutex};
pub struct WindowOptions<'a> {
pub title: &'a str,
......@@ -23,7 +23,10 @@ impl<'a> WindowOptions<'a> {
pub fn new() -> Self {
WindowOptions {
title: "byol",
size: Size::Logical(LogicalSize { width: 1280.0, height: 720.0 }),
size: Size::Logical(LogicalSize {
width: 1280.0,
height: 720.0,
}),
clear_color: [0.4, 0.2, 0.6, 1.0],
resizable: true,
fullscreen: None,
......@@ -71,7 +74,7 @@ impl<'a> WindowOptions<'a> {
pub struct Window {
id: WindowId,
_ctx_id: usize,
viewport: Viewport,
viewport: Arc<Mutex<Viewport>>,
}
impl Window {
......@@ -88,11 +91,11 @@ impl Window {
.with_fullscreen(options.fullscreen)
.with_decorations(options.decorations)
.with_transparent(options.transparent);
let ctx = ContextBuilder::new()
.build_windowed(wb, &byol.el())
.unwrap();
let ctx = unsafe { ctx.make_current().unwrap() };
let id = ctx.window().id();
......@@ -103,8 +106,6 @@ impl Window {
ContextWrapper::Windowed(ctx),
));
byol.register_window(id, ctx_id);
unsafe {
gl::ClearColor(
options.clear_color[0],
......@@ -118,6 +119,9 @@ impl Window {
let viewport = Viewport::new(physical_size.width, physical_size.height);
viewport.enable();
let viewport = Arc::new(Mutex::new(viewport));
byol.register_window(id, ctx_id, viewport.clone());
Ok(Window {
id,
_ctx_id: ctx_id,
......@@ -136,18 +140,18 @@ impl Window {
}
pub fn enable_viewport(&self) {
self.viewport.enable();
self.viewport.lock().unwrap().enable();
}
pub fn get_aspect_ratio(&self) -> f32 {
self.viewport.aspect()
self.viewport.lock().unwrap().aspect()
}
pub fn width(&self) -> i32 {
self.viewport.w
self.viewport.lock().unwrap().w
}
pub fn height(&self) -> i32 {
self.viewport.h
self.viewport.lock().unwrap().h
}
}
use super::Texture;
use std::rc::Rc;
#[derive(Clone)]
pub struct Framebuffer {
id: gl::types::GLuint,
references: Rc<()>,
}
impl Drop for Framebuffer {
fn drop(&mut self) {
if Rc::strong_count(&self.references) == 1 {
unsafe {
println!("Dropping framebuffer [{}]!", self.id);
gl::DeleteFramebuffers(1, &self.id);
}
}
}
}
impl Framebuffer {
pub fn new() -> Framebuffer {
let mut id = 0;
unsafe {
gl::GenFramebuffers(1, &mut id);
}
let references = Rc::new(());
Framebuffer { id, references }
}
pub fn get_id(&self) -> gl::types::GLuint {
self.id
}
pub fn enable(&self) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.id);
}
}
pub fn disable(&self) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
}
}
pub mod framebuffer;
pub mod texture;
pub use framebuffer::*;
pub use texture::*;
#[cfg(feature = "image")]
use image::{ColorType, DynamicImage, GenericImageView};
use std::os::raw::c_void;
use std::rc::Rc;
pub enum TextureType {
TEX2D,
CUBEMAP,
}
pub struct Texture {
id: gl::types::GLuint,
texture_type: TextureType,
mipmap: bool,
references: Rc<()>,
}
impl Drop for Texture {
fn drop(&mut self) {
if Rc::strong_count(&self.references) == 1 {
unsafe {
println!("Dropping texture [{}]!", self.id);
gl::DeleteTextures(1, &self.id);
}
}
}
}
pub struct TextureOptions {
pub texture_type: TextureType,
pub min_filter: i32,
pub mag_filter: i32,
pub wrap_s: Option<i32>,
pub wrap_t: Option<i32>,
pub wrap_r: Option<i32>,
pub border_color: Option<Vec<f32>>,
pub mipmap: bool,
}
impl TextureOptions {
pub fn new() -> TextureOptions {
TextureOptions {
texture_type: TextureType::TEX2D,
min_filter: gl::LINEAR as i32,
mag_filter: gl::NEAREST as i32,
wrap_s: None,
wrap_t: None,
wrap_r: None,
border_color: None,
mipmap: true,
}
}
}
fn find_gl_type(texture_type: &TextureType) -> u32 {
match texture_type {
TextureType::TEX2D => gl::TEXTURE_2D,
TextureType::CUBEMAP => gl::TEXTURE_CUBE_MAP,
}
}
impl Texture {
pub fn new<O: Into<Option<TextureOptions>>>(options: O) -> Texture {
let options = options.into().unwrap_or(TextureOptions::new());
let mut id = 0;
let texture_type = find_gl_type(&options.texture_type);
unsafe {
gl::GenTextures(1, &mut id);
gl::BindTexture(texture_type, id);
gl::TexParameteri(texture_type, gl::TEXTURE_MIN_FILTER, options.min_filter);
gl::TexParameteri(texture_type, gl::TEXTURE_MAG_FILTER, options.mag_filter);
if let Some(v) = options.wrap_s {
gl::TexParameteri(texture_type, gl::TEXTURE_WRAP_S, v);
}
if let Some(v) = options.wrap_t {
gl::TexParameteri(texture_type, gl::TEXTURE_WRAP_T, v);
}
if let Some(v) = options.wrap_r {
gl::TexParameteri(texture_type, gl::TEXTURE_WRAP_R, v);
}
if let Some(v) = options.border_color {
gl::TexParameterfv(texture_type, gl::TEXTURE_BORDER_COLOR, &v[0] as *const f32);
}
}
let references = Rc::new(());
Texture {
id,
texture_type: options.texture_type,
mipmap: options.mipmap,
references,
}
}
#[cfg(feature = "image")]
pub fn load_image(&self, data: &[u8]) -> Result<(), image::error::ImageError> {
let img = image::load_from_memory(data)?;
self.load_dynamic_image(img);
Ok(())
}
#[cfg(feature = "image")]
pub fn load_dynamic_image(&self, img: DynamicImage) {
let color = match img.color() {
ColorType::L8 | ColorType::L16 | ColorType::La8 | ColorType::La16 => {
panic!("Luminance textures not supported!")
}
ColorType::Rgb8 | ColorType::Rgb16 => gl::RGB,
ColorType::Rgba8 | ColorType::Rgba16 => gl::RGBA,
ColorType::Bgr8 => gl::BGR,
ColorType::Bgra8 => gl::BGRA,
ColorType::__NonExhaustive(_) => unimplemented!(),
};
let depth = match img.color() {
ColorType::L8
| ColorType::La8
| ColorType::Rgb8
| ColorType::Rgba8
| ColorType::Bgr8
| ColorType::Bgra8 => gl::UNSIGNED_BYTE,
ColorType::L16 | ColorType::La16 | ColorType::Rgb16 | ColorType::Rgba16 => {
gl::UNSIGNED_INT
}
ColorType::__NonExhaustive(_) => unimplemented!(),
};
let data = img.to_bytes();
self.load(
&data,
color as i32,
img.width() as i32,
img.height() as i32,
depth as i32,
);
}
pub fn load(&self, data: &[u8], color: i32, width: i32, height: i32, depth: i32) {
unsafe {
gl::TexImage2D(
find_gl_type(&self.texture_type),
0,
color,
width,
height,
0,
color as u32,
depth as u32,
&data[0] as *const u8 as *const c_void,
);
}
if self.mipmap {
unsafe {
gl::GenerateMipmap(gl::TEXTURE_2D);
}
}
}
pub fn enable(&self, slot: u32) {
unsafe {
gl::ActiveTexture(gl::TEXTURE0 + slot);
gl::BindTexture(find_gl_type(&self.texture_type), self.id);
}
}
pub fn id(&self) -> gl::types::GLuint {
self.id
}
}
......@@ -4,7 +4,7 @@ use super::{MeshData, Render};
use std::convert::TryInto;
pub struct Mesh {
_vbo: u32,
vbo: Vec<u32>,
vao: u32,
indices: gl::types::GLsizei,
indexed: bool,
......@@ -35,63 +35,68 @@ fn bind_attribute(attribute: gl::types::GLuint, components: usize, stride: usize
}
impl Mesh {
pub fn from(data: &MeshData) -> Mesh {
// create and bind a VAO for rendering
pub fn new() -> Mesh {
let mut vao: gl::types::GLuint = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
}
// ? Create and bind all VBOs
let vbo = Vec::new();
Mesh {
vbo,
vao,
indices: 0,
indexed: false
}
}
pub fn from(data: &MeshData) -> Mesh {
let mut mesh = Mesh::new();
mesh.bind();
mesh.indices = data.vertices.try_into().unwrap();
if let Some(v) = &data.vertex {
v.generate_vbo(gl::ARRAY_BUFFER);
mesh.vbo.push(v.generate_vbo(gl::ARRAY_BUFFER));
bind_attribute(0, 3, 3, 0);
}
match &data.render {
Render::RGB(v) => {
v.generate_vbo(gl::ARRAY_BUFFER);
mesh.vbo.push(v.generate_vbo(gl::ARRAY_BUFFER));
bind_attribute(1, 3, 0, 0);
}
Render::RGBA(v) => {
v.generate_vbo(gl::ARRAY_BUFFER);
mesh.vbo.push(v.generate_vbo(gl::ARRAY_BUFFER));
bind_attribute(1, 4, 0, 0);
}
Render::UV(v) => {
v.generate_vbo(gl::ARRAY_BUFFER);
mesh.vbo.push(v.generate_vbo(gl::ARRAY_BUFFER));
bind_attribute(1, 2, 0, 0);
}
_ => {}
}
if let Some(v) = &data.normal {
v.generate_vbo(gl::ARRAY_BUFFER);
mesh.vbo.push(v.generate_vbo(gl::ARRAY_BUFFER));
bind_attribute(2, 3, 0, 0);
}
let indexed = if let Some(v) = &data.indices {
v.generate_vbo(gl::ELEMENT_ARRAY_BUFFER);
true
} else {
false
};
if let Some(v) = &data.indices {
mesh.indexed = true;
mesh.vbo.push(v.generate_vbo(gl::ELEMENT_ARRAY_BUFFER));
}
// unbind and return Mesh
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
Mesh {
_vbo: 0,
vao,
indices: data.vertices.try_into().unwrap(),
indexed,
}
mesh
}
pub fn bind_only(&self) {
pub fn bind(&self) {
unsafe {
gl::BindVertexArray(self.vao);
}
......@@ -112,6 +117,12 @@ impl Mesh {
}
}
pub fn unbind() {
unsafe {
gl::BindVertexArray(0);
}
}
pub fn draw(&self) {
unsafe {
gl::BindVertexArray(self.vao);
......
pub mod gl;
pub mod mesh;
pub mod shader;
pub use self::gl::*;
......@@ -3,8 +3,8 @@ use crate::util::cstring::create_whitespace_cstring_with_len;
use hashbrown::HashMap;
use std::rc::Rc;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
......@@ -74,65 +74,78 @@ impl Program {
gl::UseProgram(self.id);
}
}
pub fn get_uniform_location(&mut self, name: &str) -> Result<gl::types::GLint, gl::types::GLuint> {
pub fn get_uniform_location(
&mut self,
name: &str,
) -> Result<gl::types::GLint, gl::types::GLuint> {
let mut locations = self.locations.lock().unwrap();
if let Some(loc) = locations.get(name) {
return Ok(*loc);
}
if let Some(loc) = locations.get(name) {
return Ok(*loc);
}
let cstr = CString::new(name).unwrap();
let cstr = CString::new(name).unwrap();
let location = unsafe { gl::GetUniformLocation(self.id, cstr.as_ptr()) };
locations.insert(name.to_owned(), location);
if location == -1 {
let result = unsafe { gl::GetError() };
Err(result)
} else {
Ok(location)
Ok(location)
}
}
pub fn upload_i32(&mut self, name: &str, i: i32) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe {
gl::Uniform1i(location, i);
}
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_f32(&mut self, name: &str, f: f32) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe {
gl::Uniform1f(location, f);
}
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_vec3(&mut self, name: &str, vec: &glm::Vec3) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe {
gl::Uniform3fv(location, 1, vec.as_ptr());
}
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_vec4(&mut self, name: &str, vec: &glm::Vec4) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe {
gl::Uniform4fv(location, 1, vec.as_ptr());
}
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_i32(&mut self, name: &str, i: i32) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe { gl::Uniform1i(location, i); }
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_f32(&mut self, name: &str, f: f32) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe { gl::Uniform1f(location, f); }
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_vec3(&mut self, name: &str, vec: &glm::Vec3) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe { gl::Uniform3fv(location, 1, vec.as_ptr()); }
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}
pub fn upload_vec4(&mut self, name: &str, vec: &glm::Vec4) {
if let Ok(location) = self.get_uniform_location(&name) {
unsafe { gl::Uniform4fv(location, 1, vec.as_ptr()); }
} else {
println!("Could not find location, {}. [{}]", name, self.id);
}
}