Created
          October 15, 2019 15:12 
        
      - 
      
 - 
        
Save NullSense/c94a8c578d30b1fac5bf5167edda21bb to your computer and use it in GitHub Desktop.  
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] | |
| //! A demo about how to use a Surface (CPU-side image memory). | |
| //! | |
| //! Our demo is that we'll store where the mouse goes, and turn those pixels | |
| //! white, so you see a "trail" of sorts. | |
| use ber::ray::Ray; | |
| use beryllium::*; | |
| use nalgebra::{Point3, Vector3}; | |
| use std::f32; | |
| type Vec3f = Vector3<f32>; | |
| type Point3f = Point3<f32>; | |
| fn rgb_to_u32(r: u32, g: u32, b: u32) -> u32 { | |
| r << 16 | g << 8 | b | |
| } | |
| fn vec_to_u32(vec: Vec3f) -> u32 { | |
| (vec.x as u32) << 16 | (vec.y as u32) << 8 | (vec.z as u32) | |
| } | |
| fn hit_sphere(center: &Vec3f, radius: f32, ray: &Ray) -> f32 { | |
| let oc: Point3f = ray.origin - center; | |
| let oc = Vec3f::new(oc.x, oc.y, oc.z); | |
| let a = ray.direction.dot(&ray.direction); | |
| let b = 2.0 * oc.dot(&ray.direction); | |
| let c = oc.dot(&oc) - radius * radius; | |
| let discriminant = b * b - 4 as f32 * a * c; | |
| if discriminant < 0.0 { | |
| return -1.0; | |
| } else { | |
| return (-b - f32::sqrt(discriminant)) / (a * 2.0); | |
| } | |
| } | |
| fn color(ray: &Ray) -> u32 { | |
| let mut t = hit_sphere(&Vec3f::new(0.0, 0.0, -1.0), 0.5, ray); | |
| if t > 0.0 { | |
| let point = ray.point_at(t); | |
| let norm = (Vec3f::new(point.x, point.y, point.z) - Vec3f::new(0.0, 0.0, -1.0)).normalize(); | |
| return vec_to_u32(125.0 * (Vec3f::new(norm.x + 1.0, norm.y + 1.0, norm.z + 1.0))); | |
| } | |
| // blue to white gradient going down interpolated | |
| t = 0.5 * (ray.direction.normalize().y + 1.0); | |
| let col = (1.0 - t) * Vec3f::new(255.0, 255.0, 255.0) + t * Vec3f::new(98.0, 155.0, 255.0); | |
| rgb_to_u32(col.x as u32, col.y as u32, col.z as u32) | |
| } | |
| fn main() -> Result<(), String> { | |
| let sdl = beryllium::init()?; | |
| const WIDTH: i32 = 800; | |
| const HEIGHT: i32 = 600; | |
| let window = sdl.create_window( | |
| "Surface Demo", // title | |
| WINDOW_POSITION_CENTERED, // x | |
| WINDOW_POSITION_CENTERED, // y | |
| WIDTH, // width | |
| HEIGHT, // height | |
| WindowFlags::default(), // flags | |
| )?; | |
| let mut surface = sdl.create_rgb_surface(WIDTH, HEIGHT, SurfaceFormat::DIRECT32_DEFAULT)?; | |
| let pitch = surface.pitch(); | |
| // Safety Rules: Each renderer goes with exactly one Window, and you can't use | |
| // them with the wrong Window. Similarly, Textures come from a Renderer, and | |
| // you can't use a texture with the wrong Renderer. If you only make a single | |
| // Renderer that's easy to do. If you make more than one it's up to you to | |
| // keep it straight. | |
| let renderer = unsafe { | |
| window | |
| .try_into_renderer( | |
| None, | |
| RendererFlags::default() | |
| .with_accelerated(true) | |
| .with_present_vsync(true), | |
| ) | |
| .map_err(|(_win, msg)| msg)? | |
| }; | |
| 'game_loop: loop { | |
| while let Some(event) = sdl.poll_event() { | |
| match event { | |
| Event::Quit { timestamp } => { | |
| println!("Quitting the program after {} milliseconds.", timestamp); | |
| break 'game_loop; | |
| } | |
| _ => (), | |
| } | |
| } | |
| // Safety Rules: We have to lock the surface before it's safe to edit the | |
| // pixel data directly. We can't store this pointer past the closure's use, | |
| // and we also must follow standard 2D pixel buffer editing rules to not go | |
| // out of bounds, etc. This method doesn't know your target pixel format, | |
| // you just get a byte pointer and you have to cast it to the type for the | |
| // size of pixel data you're working with. | |
| unsafe { | |
| #[allow(clippy::cast_ptr_alignment)] | |
| surface.lock_edit(|ptr| { | |
| assert_eq!( | |
| (ptr as usize) & 3, | |
| 0, | |
| "Got an unaligned pointer from the surface" | |
| ); | |
| let aspect_ratio = WIDTH / HEIGHT; | |
| let lower_left_corner = Vec3f::new(-2.0, -1.0, -1.0); | |
| let horizontal = Vec3f::new(4.0, 0.0, 0.0); | |
| let vertical = Vec3f::new(0.0, 2.0, 0.0); | |
| let origin = Point3f::new(0.0, 0.0, 0.0); | |
| for x in (0..WIDTH).rev() { | |
| for y in 0..HEIGHT { | |
| let u = x as f32 / WIDTH as f32; | |
| let v = y as f32 / HEIGHT as f32; | |
| let r: Ray = Ray::new( | |
| origin, | |
| lower_left_corner + u as f32 * horizontal + v as f32 * vertical, | |
| 0.0, | |
| ); | |
| let color = color(&r); | |
| // Note: pitch values are provided **in bytes**, so cast to the pixel | |
| // type after you offset to the start of the target row. | |
| let row_ptr = ptr.offset((y * pitch) as isize) as *mut u32; | |
| row_ptr.offset(x as isize).write(color); | |
| } | |
| } | |
| })?; | |
| } | |
| renderer.clear()?; | |
| { | |
| let texture = renderer.create_texture_from_surface(&surface)?; | |
| renderer.copy(&texture, None, None)?; | |
| } | |
| renderer.present(); | |
| } | |
| Ok(()) | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment