Skip to content

Commit

Permalink
ECS Refactor. (#86)
Browse files Browse the repository at this point in the history
Big refactor around the structure of inox2d nodes and its public API.
  • Loading branch information
Speykious authored Sep 24, 2024
2 parents abd1257 + 21136c2 commit 9854a63
Show file tree
Hide file tree
Showing 34 changed files with 2,436 additions and 1,935 deletions.
31 changes: 22 additions & 9 deletions examples/render-opengl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{error::Error, fs};

use inox2d::formats::inp::parse_inp;
use inox2d::model::Model;
use inox2d::render::InoxRenderer;
use inox2d::render::InoxRendererExt;
use inox2d_opengl::OpenglRenderer;

use clap::Parser;
Expand Down Expand Up @@ -41,12 +41,18 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing::info!("Parsing puppet");

let data = fs::read(cli.inp_path)?;
let model = parse_inp(data.as_slice())?;
let mut model = parse_inp(data.as_slice())?;
tracing::info!(
"Successfully parsed puppet: {}",
(model.puppet.meta.name.as_deref()).unwrap_or("<no puppet name specified in file>")
);

tracing::info!("Setting up puppet for transforms, params and rendering.");
model.puppet.init_transforms();
model.puppet.init_rendering();
model.puppet.init_params();
model.puppet.init_physics();

tracing::info!("Setting up windowing and OpenGL");
let app_frame = AppFrame::init(
WindowBuilder::new()
Expand Down Expand Up @@ -81,10 +87,9 @@ impl Inox2dOpenglExampleApp {

impl App for Inox2dOpenglExampleApp {
fn resume_window(&mut self, gl: glow::Context) {
match OpenglRenderer::new(gl) {
match OpenglRenderer::new(gl, &self.model) {
Ok(mut renderer) => {
tracing::info!("Initializing Inox2D renderer");
renderer.prepare(&self.model).unwrap();
renderer.resize(self.width, self.height);
renderer.camera.scale = Vec2::splat(0.15);
tracing::info!("Inox2D renderer initialized");
Expand Down Expand Up @@ -119,12 +124,20 @@ impl App for Inox2dOpenglExampleApp {
renderer.clear();

let puppet = &mut self.model.puppet;
puppet.begin_set_params();
puppet.begin_frame();
let t = scene_ctrl.current_elapsed();
let _ = puppet.set_named_param("Head:: Yaw-Pitch", Vec2::new(t.cos(), t.sin()));
puppet.end_set_params(scene_ctrl.dt());

renderer.render(puppet);
let _ = puppet
.param_ctx
.as_mut()
.unwrap()
.set("Head:: Yaw-Pitch", Vec2::new(t.cos(), t.sin()));
// Actually, not providing 0 for the first frame will not create too big a problem.
// Just that physics simulation will run for the provided time, which may be big and causes a startup delay.
puppet.end_frame(scene_ctrl.dt());

renderer.on_begin_draw(puppet);
renderer.draw(puppet);
renderer.on_end_draw(puppet);
}

fn handle_window_event(&mut self, event: WindowEvent, elwt: &EventLoopWindowTarget<()>) {
Expand Down
35 changes: 26 additions & 9 deletions examples/render-webgl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
use std::cell::RefCell;
use std::rc::Rc;

use inox2d::{formats::inp::parse_inp, render::InoxRenderer};
use inox2d::formats::inp::parse_inp;
use inox2d::render::InoxRendererExt;
use inox2d_opengl::OpenglRenderer;

use glam::Vec2;
Expand Down Expand Up @@ -86,13 +87,18 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
.await?;

let model_bytes = res.bytes().await?;
let model = parse_inp(model_bytes.as_ref())?;
let mut model = parse_inp(model_bytes.as_ref())?;

tracing::info!("Setting up puppet for transforms, params and rendering.");
model.puppet.init_transforms();
model.puppet.init_rendering();
model.puppet.init_params();
model.puppet.init_physics();

info!("Initializing Inox2D renderer");
let mut renderer = OpenglRenderer::new(gl)?;
let mut renderer = OpenglRenderer::new(gl, &model)?;

info!("Creating buffers and uploading model textures");
renderer.prepare(&model)?;
renderer.camera.scale = Vec2::splat(0.15);
info!("Inox2D renderer initialized");

Expand All @@ -115,15 +121,26 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
*anim_loop_g.borrow_mut() = Some(Closure::new(move || {
scene_ctrl.borrow_mut().update(&mut renderer.borrow_mut().camera);

renderer.borrow().clear();
{
renderer.borrow().clear();

let mut puppet = puppet.borrow_mut();
puppet.begin_set_params();
puppet.begin_frame();
let t = scene_ctrl.borrow().current_elapsed();
puppet.set_named_param("Head:: Yaw-Pitch", Vec2::new(t.cos(), t.sin()));
puppet.end_set_params(scene_ctrl.borrow().dt());
let _ = puppet
.param_ctx
.as_mut()
.unwrap()
.set("Head:: Yaw-Pitch", Vec2::new(t.cos(), t.sin()));

// Actually, not providing 0 for the first frame will not create too big a problem.
// Just that physics simulation will run for the provided time, which may be big and causes a startup delay.
puppet.end_frame(scene_ctrl.borrow().dt());

renderer.borrow().on_begin_draw(&puppet);
renderer.borrow().draw(&puppet);
renderer.borrow().on_end_draw(&puppet);
}
renderer.borrow().render(&puppet.borrow());

request_animation_frame(anim_loop_f.borrow().as_ref().unwrap());
}));
Expand Down
109 changes: 34 additions & 75 deletions inox2d-opengl/src/gl_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,85 +1,44 @@
use glam::Vec2;
use glow::HasContext;

use inox2d::render::RenderCtx;

use super::OpenglRendererError;

unsafe fn upload_array_to_gl<T>(gl: &glow::Context, array: &[T], target: u32, usage: u32) -> glow::Buffer {
/// Create and BIND an OpenGL buffer and upload data.
///
/// # Errors
///
/// This function will return an error if it couldn't create a buffer.
///
/// # Safety
///
/// `target` and `usage` must be valid OpenGL constants.
pub unsafe fn upload_array_to_gl<T>(
gl: &glow::Context,
array: &[T],
target: u32,
usage: u32,
) -> Result<glow::Buffer, OpenglRendererError> {
// Safety:
// - array is already a &[T], satisfying all pointer and size requirements.
// - data only accessed immutably in this function, satisfying lifetime requirements.
let bytes: &[u8] = core::slice::from_raw_parts(array.as_ptr() as *const u8, std::mem::size_of_val(array));
let buffer = gl.create_buffer().unwrap();
let buffer = gl.create_buffer().map_err(OpenglRendererError::Opengl)?;
gl.bind_buffer(target, Some(buffer));
gl.buffer_data_u8_slice(target, bytes, usage);
buffer
}

unsafe fn reupload_array_to_gl<T>(gl: &glow::Context, array: &[T], target: u32, start_idx: usize, end_idx: usize) {
let slice = &array[start_idx..end_idx];
let bytes: &[u8] = core::slice::from_raw_parts(slice.as_ptr() as *const u8, core::mem::size_of_val(slice));
gl.buffer_sub_data_u8_slice(target, start_idx as i32, bytes);
}

pub trait RenderCtxOpenglExt {
unsafe fn setup_gl_buffers(
&self,
gl: &glow::Context,
vao: glow::VertexArray,
) -> Result<glow::Buffer, OpenglRendererError>;
unsafe fn upload_deforms_to_gl(&self, gl: &glow::Context, buffer: glow::Buffer);
Ok(buffer)
}

impl RenderCtxOpenglExt for RenderCtx {
/// Uploads the vertex and index buffers to OpenGL.
///
/// # Errors
///
/// This function will return an error if it couldn't create a vertex array.
///
/// # Safety
///
/// Only call this function once when loading a new puppet.
unsafe fn setup_gl_buffers(
&self,
gl: &glow::Context,
vao: glow::VertexArray,
) -> Result<glow::Buffer, OpenglRendererError> {
gl.bind_vertex_array(Some(vao));

upload_array_to_gl(gl, &self.vertex_buffers.verts, glow::ARRAY_BUFFER, glow::STATIC_DRAW);
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 0, 0);
gl.enable_vertex_attrib_array(0);

upload_array_to_gl(gl, &self.vertex_buffers.uvs, glow::ARRAY_BUFFER, glow::STATIC_DRAW);
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, 0, 0);
gl.enable_vertex_attrib_array(1);

let deform_buffer =
upload_array_to_gl(gl, &self.vertex_buffers.deforms, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW);
gl.vertex_attrib_pointer_f32(2, 2, glow::FLOAT, false, 0, 0);
gl.enable_vertex_attrib_array(2);

upload_array_to_gl(
gl,
&self.vertex_buffers.indices,
glow::ELEMENT_ARRAY_BUFFER,
glow::STATIC_DRAW,
);

Ok(deform_buffer)
}

/// # Safety
///
/// unsafe as initiating GL calls. can be safely called for multiple times,
/// but only needed once after deform update and before rendering.
unsafe fn upload_deforms_to_gl(&self, gl: &glow::Context, buffer: glow::Buffer) {
gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));

reupload_array_to_gl(
gl,
&self.vertex_buffers.deforms,
glow::ARRAY_BUFFER,
0,
self.vertex_buffers.deforms.len(),
);
}
}
/// Upload full deform buffer content.
///
/// # Safety
///
/// The vertex array object created in `setup_gl_buffers()` must be bound and no new ARRAY_BUFFER is enabled.
pub unsafe fn upload_deforms_to_gl(gl: &glow::Context, deforms: &[Vec2], buffer: glow::Buffer) {
gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));

// Safety: same as those described in upload_array_to_gl().
let bytes: &[u8] = core::slice::from_raw_parts(deforms.as_ptr() as *const u8, std::mem::size_of_val(deforms));
// if the above preconditions are met, deform is then the currently bound ARRAY_BUFFER.
gl.buffer_sub_data_u8_slice(glow::ARRAY_BUFFER, 0, bytes);
}
Loading

0 comments on commit 9854a63

Please sign in to comment.