nsys-gl-utils 0.11.10

OpenGL and graphics utilities
Documentation
#![feature(more_float_constants)]

use std::time;
use env_logger;
use glium;
use line_2d;
use log;
use winit;

use gl_utils::*;
use gl_utils::render::resource::demo;

const LOG_LEVEL : log::LevelFilter = log::LevelFilter::Info;

const INITIAL_2D_ZOOM       : f32      = 1.0;
const INITIAL_3D_POSITION   : [f32; 3] = [0.0, -3.0, 2.0];
const INITIAL_CLEAR_COLOR   : [f32; 4] = [0.0, 0.0, 0.0, 1.0];

const DRAWING_TEXTURE_INDEX : usize = 0;

fn main() {
  use std::f32::consts::{FRAC_PI_4, PI, SQRT_3};
  use math::num::Inv;
  println!("gl-utils demo main...");

  // log init
  env_logger::Builder::new().filter_level (LOG_LEVEL).parse_default_env().init();
  log::info!("logging initialized");

  // initialize glium
  let (mut events_loop, window, _gl_config, glium_display) =
    init::glium_init_gl33core ("gl-utils demo window");

  { // glium context info
    use glium::backend::Facade;
    let context = glium_display.get_context();
    log::info!("opengl version: {:?}", context.get_opengl_version());
    log::info!("opengl profile: {:?}", context.get_opengl_profile());
    log::info!("supported glsl version: {:?}", context.get_supported_glsl_version());
    context.get_free_video_memory().map_or_else(
      || log::warn!("could not get free video memory"),
      |nbytes| log::info!("free video memory: {}MB", nbytes/1_000_000))
  }
  // debug prints
  Render::<render::resource::Default>::report_sizes();

  // test loading gltf file
  // TODO: render imported mesh
  let load_mesh_path = std::path::Path::new ("box.gltf");
  let mesh = Mesh::load_gltf (load_mesh_path);
  log::info!("loaded mesh ({}): {:?}", load_mesh_path.display(), mesh);
  // test writing gltf file
  let write_mesh_path = std::path::Path::new ("sphere.gltf");
  Mesh::from (mesh::Lines3d::sphere (0, 12, 24)).write_gltf (write_mesh_path);
  log::info!("wrote sphere mesh: {}", write_mesh_path.display());

  // acquire and setup render context
  let mut app = demo::App::new (glium_display, window);

  app.render.clear_color = INITIAL_CLEAR_COLOR;
  app.render.camera3d_position_set (INITIAL_3D_POSITION.into());
  app.render.camera2d_zoom_set     (INITIAL_2D_ZOOM);
  // drawing texture
  let (width, height) = app.render.glium_display.get_framebuffer_dimensions();
  let mut drawing : Vec <Vec <(f32, f32, f32, f32)>> =
    vec![vec![(0.0, 0.0, 0.0, 0.0); width as usize]; height as usize];
  let drawing_texture = glium::texture::Texture2d::new (
    &app.render.glium_display, drawing.clone()).unwrap();
  assert!(app.render.resource.textures_anysize
    .insert (DRAWING_TEXTURE_INDEX, drawing_texture).is_none());
  assert!(app.render.resource.draw2d.rectangle_anysize_vertices.insert (
    DRAWING_TEXTURE_INDEX,
    glium::VertexBuffer::new (&app.render.glium_display, &[
      vertex::Vert2dRectUv {
        bottom_left: [-(width as f32) / 2.0, -(height as f32) / 2.0],
        dimensions:  [width as f32, height as f32],
        uv:          [0.0, 0.0]
      }
    ]).unwrap()
  ).is_none());

  // red rectangle in viewport 0
  /*
  app.render.resource.draw2d
    .rectangle_2d_vertices_set (
      &app.render.glium_display,
      &[ vertex::Vert2dRectColor {
          bottom_left: [5.0, 5.0],
          dimensions:  [50.0, 50.0],
          color:       color::rgba_u8_to_rgba_f32 (&color::RED)
        }
      ]
    );
  let mut viewport_resources = app.render.resource.draw2d
    .viewport_resources_get (render::resource::MAIN_VIEWPORT).clone();
  viewport_resources.rectangle_vertex_range = 0..1;
  app.render.resource.draw2d.viewport_resources_set (
    render::resource::MAIN_VIEWPORT, viewport_resources);
  */

  // main loop
  let mut frame            = 0;
  let mut fps              = 0;
  let mut last_t           = time::Instant::now();
  let mut prev_mouse_position = app.mouse_position;
  let mut drawing_state    = false;
  let mut cube_orientation = math::Rotation3::from_angle_x (FRAC_PI_4.into());
  let cube_rotation        = math::Rotation3::from_axis_angle (
    cube_orientation.inv() * math::Vector3::unit_z(), (PI / 100.0).into());
  println!("start time: {last_t:?}");
  while app.running {
    use winit::platform::pump_events::EventLoopExtPumpEvents;
    // TODO: review
    // pump winit events:
    // - window events includes input events received by the window
    // - device events are received independent of window focus
    // there may be differences in the events received depending on platform:
    // - on Linux keyboard input is received as both a device and window event
    // - on Windows only mouse motion device events are received, all other
    //   input is received as window events
    // here we just handle input contained in window events
    events_loop.pump_app_events (Some (time::Duration::ZERO), &mut app);
    // drawing texture
    match app.mouse_button_event {
      Some (winit::event::ElementState::Pressed)  => drawing_state = true,
      Some (winit::event::ElementState::Released) => drawing_state = false,
      None => {}
    }
    let (width, height) = app.render.glium_display.get_framebuffer_dimensions();
    drawing.iter_mut()
      .for_each (|line| line.resize (width as usize, (0.0, 0.0, 0.0, 0.0)));
    drawing.resize_with (
      height as usize, || vec![(0.0, 0.0, 0.0, 0.0); width as usize]);
    if drawing_state {
      let start = line_2d::ICoord::new (
        prev_mouse_position.0 as i32, prev_mouse_position.1 as i32);
      let end   = line_2d::ICoord::new (
        app.mouse_position.0 as i32, app.mouse_position.1 as i32);
      if start != end {
        let segment = line_2d::LineSegment::new (start, end);
        for coord in segment.iter() {
          drawing[coord.y as usize][coord.x as usize] = (1.0, 1.0, 0.0, 1.0);
          let drawing_texture = app.render.resource.textures_anysize
            .get_mut (DRAWING_TEXTURE_INDEX).unwrap();
          //let bottom = drawing_texture.height() - drawing.len() as u32;
          let bottom = drawing_texture.height() - coord.y as u32;
          let rect = glium::Rect {
            left: coord.x as u32, bottom, width: 1, height: 1
          };
          drawing_texture.write (rect, vec![vec![(1.0, 1.0, 0.0, 1.0)]]);
        }
      } else {
        let drawing_texture = app.render.resource.textures_anysize
          .get_mut (DRAWING_TEXTURE_INDEX).unwrap();
        //let bottom = drawing_texture.height() - drawing.len() as u32;
        let bottom = drawing_texture.height() - app.mouse_position.1 as u32;
        let rect = glium::Rect {
          left: app.mouse_position.0 as u32, bottom, width: 1, height: 1
        };
        drawing_texture.write (rect, vec![vec![(1.0, 1.0, 0.0, 1.0)]]);
      }
      /*
      let left   = prev_mouse_position.0.min (app.mouse_position.0) as u32;
      let bottom = (drawing_texture.height() - prev_mouse_position.1 as u32)
        .min (drawing_texture.height() - app.mouse_position.1 as u32);
      let right  = prev_mouse_position.0.max (app.mouse_position.0) as u32;
      let top    = (drawing_texture.height() - prev_mouse_position.1 as u32)
        .max (drawing_texture.height() - app.mouse_position.1 as u32);
      let width  = right - left;
      let height = top - bottom;
      let rect   = glium::Rect { left, bottom, width, height };
      drawing_texture.write (rect, vec![vec![(1.0, 0.0, 0.0, 1.0)]]);
      */
    }
    prev_mouse_position = app.mouse_position;
    assert!(app.render.resource.draw2d.rectangle_anysize_vertices.insert (
      DRAWING_TEXTURE_INDEX,
      glium::VertexBuffer::new (&app.render.glium_display, &[
        vertex::Vert2dRectUv {
          bottom_left: [-(width as f32) / 2.0, -(height as f32) / 2.0],
          dimensions:  [width as f32, height as f32],
          uv:          [0.0, 0.0]
        }
      ]).unwrap()
    ).is_some());
    // rotate cube
    cube_orientation *= cube_rotation;
    let cube_z = (1.0 - 6.0f32.sqrt() / 6.0)
      * (2.0 * PI * ((frame % 150) as f32 / 150.0)).sin();
    app.render.resource.draw3d.instance_vertices_mesh_write (
      render::resource::draw3d::MeshId::Cube as usize,
      &[vertex::Vert3dOrientationScaleColor {
        position:    [-1.5, 2.5, 1.0 + cube_z],
        orientation: cube_orientation.into_col_arrays(),
        scale:       [SQRT_3 / 6.0; 3],
        color:       color::rgba_u8_to_rgba_f32 (color::CYAN)
      }][..]);
    // draw frame
    app.render.do_frame (None);
    frame += 1;
    // fps
    let t = time::Instant::now();
    if (t - last_t).as_secs() >= 1 {
      // debug
      //println!("fps: {}", fps);
      //println!("num viewports: {:?}", app.render.viewports().len());
      let [_tile_width, tile_height] = app.render.resource.tile_dimensions (
        render::resource::DefaultTilesetId::EasciiAcorn128);
      let tile_range = app.render.resource.draw2d
        .viewport_resources_get (render::resource::OVERLAY_VIEWPORT).unwrap()
        .draw_indices[0].draw_tiles.as_ref().unwrap().vertex_range.clone();
      let fps_row = ((height / tile_height) - 1) as i32;
      let tiles = tile::vertices (&format!("FPS: {fps:<6}"), (fps_row, 0));
      app.render.resource.draw2d.tile_2d_vertices.slice (
        tile_range.start as usize .. tile_range.end as usize
      ).unwrap().write (&tiles);
      last_t = t;
      fps = 0;
    } else {
      fps += 1;
    }
  }

  println!("...gl-utils demo main");
}