Skip to content

Instantly share code, notes, and snippets.

@NullSense
Created October 15, 2019 15:12
Show Gist options
  • Save NullSense/c94a8c578d30b1fac5bf5167edda21bb to your computer and use it in GitHub Desktop.
Save NullSense/c94a8c578d30b1fac5bf5167edda21bb to your computer and use it in GitHub Desktop.
#![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