Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 214a6f1fd7 | |||
| ca239bbd15 | |||
| a4b1f252ad | |||
| 5e9af0aca7 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,5 +3,7 @@
|
||||
*.pdf
|
||||
*.html
|
||||
source.zip
|
||||
|
||||
.aider*
|
||||
flake.lock
|
||||
|
||||
|
||||
229659
resources/helicopter.obj
229659
resources/helicopter.obj
File diff suppressed because it is too large
Load Diff
142855
resources/lunarsurface.obj
142855
resources/lunarsurface.obj
File diff suppressed because it is too large
Load Diff
@@ -3,11 +3,7 @@
|
||||
out layout(location=0) vec4 color;
|
||||
in vec4 gl_FragCoord;
|
||||
in vec4 vColor;
|
||||
in vec3 vNormal;
|
||||
|
||||
void main() {
|
||||
vec3 lightDirection = normalize(vec3(0.8, -0.5, 0.6));
|
||||
float lambert = max(0.0, dot(normalize(vNormal), -lightDirection));
|
||||
vec3 litColor = vColor.rgb * lambert;
|
||||
color = vec4(litColor, vColor.a);
|
||||
color = vColor;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,10 @@
|
||||
|
||||
in layout(location=0) vec3 position;
|
||||
in layout(location=1) vec4 aColor;
|
||||
in layout(location=2) vec3 aNormal;
|
||||
out vec4 vColor;
|
||||
out vec3 vNormal;
|
||||
layout(location = 0) uniform mat4 transform;
|
||||
uniform mat3 normalMatrix;
|
||||
uniform mat4 transform;
|
||||
|
||||
void main() {
|
||||
gl_Position = transform * vec4(position, 1.0f);
|
||||
vColor = aColor;
|
||||
vNormal = normalize(normalMatrix * aNormal);
|
||||
}
|
||||
|
||||
216
src/main.rs
216
src/main.rs
@@ -14,11 +14,6 @@ use std::{mem, os::raw::c_void, ptr};
|
||||
|
||||
mod shader;
|
||||
mod util;
|
||||
mod mesh;
|
||||
mod scene_graph;
|
||||
use scene_graph::SceneNode;
|
||||
mod toolbox;
|
||||
use toolbox::simple_heading_animation;
|
||||
|
||||
use glutin::event::{
|
||||
DeviceEvent,
|
||||
@@ -64,7 +59,7 @@ fn offset<T>(n: u32) -> *const c_void {
|
||||
// Get a null pointer (equivalent to an offset of 0)
|
||||
// ptr::null()
|
||||
|
||||
unsafe fn create_vao(vertices: &Vec<f32>, normals: &Vec<f32>, colors: &Vec<f32>, indices: &Vec<u32>) -> u32 {
|
||||
unsafe fn create_vao(vertices: &Vec<f32>, colors: &Vec<f32>, indices: &Vec<u32>) -> u32 {
|
||||
let mut vao_id = 0;
|
||||
gl::GenVertexArrays(1, &mut vao_id);
|
||||
gl::BindVertexArray(vao_id);
|
||||
@@ -113,57 +108,9 @@ unsafe fn create_vao(vertices: &Vec<f32>, normals: &Vec<f32>, colors: &Vec<f32>,
|
||||
gl::VertexAttribPointer(1, 4, gl::FLOAT, gl::FALSE, 0, std::ptr::null());
|
||||
gl::EnableVertexAttribArray(1);
|
||||
|
||||
// normal buffer
|
||||
let mut nbo_id = 0;
|
||||
gl::GenBuffers(1, &mut nbo_id);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, nbo_id);
|
||||
gl::BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
byte_size_of_array(&normals),
|
||||
normals.as_ptr() as *const c_void,
|
||||
gl::STATIC_DRAW,
|
||||
);
|
||||
gl::VertexAttribPointer(2, DIMENSIONS, gl::FLOAT, gl::FALSE, 0, std::ptr::null());
|
||||
gl::EnableVertexAttribArray(2);
|
||||
|
||||
vao_id
|
||||
}
|
||||
|
||||
// Scene graph drawing function
|
||||
unsafe fn draw_scene(node: &scene_graph::SceneNode, transform_loc: i32, normal_loc: i32, view_projection: &glm::Mat4, transform_so_far: &glm::Mat4) {
|
||||
// Build model matrix from node transformations
|
||||
let translation = glm::translation(&node.position);
|
||||
let rot_x = glm::rotation(node.rotation.x, &glm::vec3(1.0, 0.0, 0.0));
|
||||
let rot_y = glm::rotation(node.rotation.y, &glm::vec3(0.0, 1.0, 0.0));
|
||||
let rot_z = glm::rotation(node.rotation.z, &glm::vec3(0.0, 0.0, 1.0));
|
||||
let rotation = rot_z * rot_y * rot_x;
|
||||
let scaling = glm::scaling(&node.scale);
|
||||
let refp = node.reference_point;
|
||||
let to_ref = glm::translation(&refp);
|
||||
let from_ref = glm::translation(&-refp);
|
||||
let model = translation * to_ref * rotation * scaling * from_ref;
|
||||
let new_transform = *transform_so_far * model;
|
||||
|
||||
// Draw this node if drawable
|
||||
if node.index_count > 0 {
|
||||
let mvp = view_projection * new_transform;
|
||||
gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, mvp.as_ptr());
|
||||
let nm4 = glm::transpose(&glm::inverse(&new_transform));
|
||||
let normal_matrix = glm::mat3(
|
||||
nm4[(0,0)], nm4[(0,1)], nm4[(0,2)],
|
||||
nm4[(1,0)], nm4[(1,1)], nm4[(1,2)],
|
||||
nm4[(2,0)], nm4[(2,1)], nm4[(2,2)],
|
||||
);
|
||||
gl::UniformMatrix3fv(normal_loc, 1, gl::FALSE, normal_matrix.as_ptr());
|
||||
gl::BindVertexArray(node.vao_id);
|
||||
gl::DrawElements(gl::TRIANGLES, node.index_count, gl::UNSIGNED_INT, ptr::null());
|
||||
}
|
||||
// Recurse to children
|
||||
for &child in &node.children {
|
||||
draw_scene(&*child, transform_loc, normal_loc, view_projection, &new_transform);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Set up the necessary objects to deal with windows and event handling
|
||||
let el = glutin::event_loop::EventLoop::new();
|
||||
@@ -216,7 +163,6 @@ fn main() {
|
||||
gl::Disable(gl::MULTISAMPLE);
|
||||
gl::Enable(gl::BLEND);
|
||||
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
|
||||
gl::Enable(gl::PROGRAM_POINT_SIZE);
|
||||
gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
gl::DebugMessageCallback(Some(util::debug_callback), ptr::null());
|
||||
|
||||
@@ -234,37 +180,6 @@ fn main() {
|
||||
}
|
||||
|
||||
// == // Set up your VAO around here
|
||||
let terrain_mesh = mesh::Terrain::load("resources/lunarsurface.obj");
|
||||
let terrain_vao = unsafe {
|
||||
create_vao(&terrain_mesh.vertices, &terrain_mesh.normals, &terrain_mesh.colors, &terrain_mesh.indices)
|
||||
};
|
||||
// helicopter VAOs
|
||||
let helicopter = mesh::Helicopter::load("resources/helicopter.obj");
|
||||
let heli_body_vao = unsafe { create_vao(&helicopter.body.vertices, &helicopter.body.normals, &helicopter.body.colors, &helicopter.body.indices) };
|
||||
let heli_door_vao = unsafe { create_vao(&helicopter.door.vertices, &helicopter.door.normals, &helicopter.door.colors, &helicopter.door.indices) };
|
||||
let heli_main_rotor_vao = unsafe { create_vao(&helicopter.main_rotor.vertices, &helicopter.main_rotor.normals, &helicopter.main_rotor.colors, &helicopter.main_rotor.indices) };
|
||||
let heli_tail_rotor_vao = unsafe { create_vao(&helicopter.tail_rotor.vertices, &helicopter.tail_rotor.normals, &helicopter.tail_rotor.colors, &helicopter.tail_rotor.indices) };
|
||||
|
||||
// Scene Graph setup
|
||||
let mut root_node = SceneNode::new();
|
||||
let mut terrain_node = SceneNode::from_vao(terrain_vao, terrain_mesh.index_count);
|
||||
root_node.add_child(&*terrain_node);
|
||||
|
||||
let mut heli_root = SceneNode::new();
|
||||
let body_node = SceneNode::from_vao(heli_body_vao, helicopter.body.index_count);
|
||||
let door_node = SceneNode::from_vao(heli_door_vao, helicopter.door.index_count);
|
||||
let mut main_rotor_node = SceneNode::from_vao(heli_main_rotor_vao, helicopter.main_rotor.index_count);
|
||||
let mut tail_rotor_node = SceneNode::from_vao(heli_tail_rotor_vao, helicopter.tail_rotor.index_count);
|
||||
// Set reference point for tail rotor (pivot around its hub)
|
||||
let mut tail_rotor_pin = tail_rotor_node.as_mut();
|
||||
unsafe { tail_rotor_pin.get_unchecked_mut().reference_point = glm::vec3(0.35, 2.3, 10.4); }
|
||||
|
||||
heli_root.add_child(&*body_node);
|
||||
heli_root.add_child(&*door_node);
|
||||
heli_root.add_child(&*main_rotor_node);
|
||||
heli_root.add_child(&*tail_rotor_node);
|
||||
|
||||
terrain_node.add_child(&*heli_root);
|
||||
|
||||
let vertices = vec![
|
||||
// triangle 1
|
||||
@@ -303,40 +218,7 @@ fn main() {
|
||||
|
||||
|
||||
|
||||
|
||||
let billboard_vertices = vec![
|
||||
-0.5, -0.5, 0.0,
|
||||
-0.5, 0.5, 0.0,
|
||||
0.5, 0.5, 0.0,
|
||||
0.5, -0.5, 0.0,
|
||||
];
|
||||
let billboard_indices = vec![
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
];
|
||||
let billboard_colors = vec![
|
||||
1.0, 1.0, 1.0, 1.0,
|
||||
1.0, 1.0, 1.0, 1.0,
|
||||
1.0, 1.0, 1.0, 1.0,
|
||||
1.0, 1.0, 1.0, 1.0,
|
||||
];
|
||||
let billboard_vao = unsafe { create_vao(&billboard_vertices, &Vec::new(), &billboard_colors, &billboard_indices) };
|
||||
|
||||
// Generate a grid of point‐particles
|
||||
let grid_size = 200;
|
||||
let mut particle_vertices = Vec::with_capacity(grid_size * grid_size * 3);
|
||||
let mut particle_colors = Vec::with_capacity(grid_size * grid_size * 4);
|
||||
for i in 0..grid_size {
|
||||
for j in 0..grid_size {
|
||||
let x = (i as f32 / grid_size as f32 - 0.5) * 20.0;
|
||||
let y = (j as f32 / grid_size as f32 - 0.5) * 20.0;
|
||||
let z = -5.0;
|
||||
particle_vertices.extend_from_slice(&[x, y, z]);
|
||||
particle_colors.extend_from_slice(&[1.0, 1.0, 1.0, 1.0]);
|
||||
}
|
||||
}
|
||||
let particle_vao = unsafe { create_vao(&particle_vertices, &Vec::new(), &particle_colors, &Vec::new()) };
|
||||
let particle_count = (grid_size * grid_size) as i32;
|
||||
let my_vao = unsafe { create_vao(&vertices, &colors, &indices) };
|
||||
|
||||
// == // Set up your shaders here
|
||||
|
||||
@@ -356,16 +238,10 @@ fn main() {
|
||||
};
|
||||
// get uniform location for the matrix (named `transform` in your vertex shader)
|
||||
let transform_loc = unsafe { simple_shader.get_uniform_location("transform") };
|
||||
let normal_loc = unsafe { simple_shader.get_uniform_location("normalMatrix") };
|
||||
|
||||
// Used to demonstrate keyboard handling for exercise 2.
|
||||
let mut _arbitrary_number = 0.0; // feel free to remove
|
||||
|
||||
// camera position (x, y, z) and orientation (yaw, pitch)
|
||||
let mut cam_pos: glm::Vec3 = glm::vec3(0.0, 0.0, 0.0);
|
||||
let mut cam_yaw: f32 = 0.0;
|
||||
let mut cam_pitch: f32 = 0.0;
|
||||
|
||||
// The main rendering loop
|
||||
let first_frame_time = std::time::Instant::now();
|
||||
let mut previous_frame_time = first_frame_time;
|
||||
@@ -389,38 +265,23 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keyboard input for camera movement and rotation
|
||||
// Handle keyboard input
|
||||
if let Ok(keys) = pressed_keys.lock() {
|
||||
// movement speed
|
||||
let speed = 10.0 * delta_time;
|
||||
// camera direction vectors based on yaw & pitch
|
||||
let front = glm::vec3(
|
||||
cam_yaw.sin() * cam_pitch.cos(),
|
||||
cam_pitch.sin(),
|
||||
-cam_yaw.cos() * cam_pitch.cos(),
|
||||
);
|
||||
let right = glm::normalize(&glm::cross(&front, &glm::vec3(0.0, 1.0, 0.0)));
|
||||
let up_vec = glm::normalize(&glm::cross(&right, &front));
|
||||
for key in keys.iter() {
|
||||
match key {
|
||||
// Move relative to camera orientation
|
||||
VirtualKeyCode::W => { cam_pos -= front * speed; }
|
||||
VirtualKeyCode::S => { cam_pos += front * speed; }
|
||||
VirtualKeyCode::A => { cam_pos -= right * speed; }
|
||||
VirtualKeyCode::D => { cam_pos += right * speed; }
|
||||
VirtualKeyCode::Space => { cam_pos += up_vec * speed; }
|
||||
VirtualKeyCode::LShift => { cam_pos -= up_vec * speed; }
|
||||
// Rotation: adjust yaw and pitch
|
||||
VirtualKeyCode::Left => { cam_yaw -= delta_time; }
|
||||
VirtualKeyCode::Right => { cam_yaw += delta_time; }
|
||||
VirtualKeyCode::Up => { cam_pitch += delta_time; }
|
||||
VirtualKeyCode::Down => { cam_pitch -= delta_time; }
|
||||
// The `VirtualKeyCode` enum is defined here:
|
||||
// https://docs.rs/winit/0.25.0/winit/event/enum.VirtualKeyCode.html
|
||||
VirtualKeyCode::A => {
|
||||
_arbitrary_number += delta_time;
|
||||
}
|
||||
VirtualKeyCode::D => {
|
||||
_arbitrary_number -= delta_time;
|
||||
}
|
||||
|
||||
// default handler:
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// clamp pitch to avoid flipping
|
||||
let pitch_limit = std::f32::consts::FRAC_PI_2 - 0.01;
|
||||
cam_pitch = cam_pitch.clamp(-pitch_limit, pitch_limit);
|
||||
}
|
||||
// Handle mouse movement. delta contains the x and y movement of the mouse since last frame in pixels
|
||||
if let Ok(mut delta) = mouse_delta.lock() {
|
||||
@@ -439,36 +300,29 @@ fn main() {
|
||||
|
||||
// == // Issue the necessary gl:: commands to draw your scene here
|
||||
simple_shader.activate();
|
||||
// build View matrix: translate and rotate world relative to camera
|
||||
let view_translation: glm::Mat4 = glm::translation(&(-cam_pos));
|
||||
let view_rot_y: glm::Mat4 = glm::rotation(-cam_yaw, &glm::vec3(0.0, 1.0, 0.0));
|
||||
let view_rot_x: glm::Mat4 = glm::rotation(-cam_pitch, &glm::vec3(1.0, 0.0, 0.0));
|
||||
let view: glm::Mat4 = view_rot_x * view_rot_y * view_translation;
|
||||
// build Projection matrix
|
||||
let projection: glm::Mat4 =
|
||||
glm::perspective(window_aspect_ratio, std::f32::consts::PI / 4.0, 1.0, 1000.0);
|
||||
|
||||
// Animate helicopter root via simple_heading_animation
|
||||
let heading = simple_heading_animation(elapsed);
|
||||
{
|
||||
let mut hr_pin = heli_root.as_mut();
|
||||
unsafe {
|
||||
let node = hr_pin.get_unchecked_mut();
|
||||
node.position = glm::vec3(heading.x, 0.0, heading.z);
|
||||
node.rotation = glm::vec3(heading.pitch, heading.yaw, heading.roll);
|
||||
}
|
||||
// build and send the transform matrix (start from identity)
|
||||
let mut transform: glm::Mat4 = glm::identity();
|
||||
// cycle through each transform parameter (a,b,c,d,e,f) over time
|
||||
let cycle_duration = 2.0;
|
||||
let idx = ((elapsed / cycle_duration).floor() as usize) % 6;
|
||||
let v = elapsed.sin();
|
||||
match idx {
|
||||
0 => transform[(0,0)] = v, // a
|
||||
1 => transform[(0,1)] = v, // b
|
||||
2 => transform[(0,3)] = v, // c
|
||||
3 => transform[(1,0)] = v, // d
|
||||
4 => transform[(1,1)] = v, // e
|
||||
5 => transform[(1,3)] = v, // f
|
||||
_ => {}
|
||||
}
|
||||
// Update rotor rotations
|
||||
{
|
||||
let mut mr_pin = main_rotor_node.as_mut();
|
||||
unsafe { mr_pin.get_unchecked_mut().rotation.y = elapsed * 10.0; }
|
||||
let mut tr_pin = tail_rotor_node.as_mut();
|
||||
unsafe { tr_pin.get_unchecked_mut().rotation.x = elapsed * 10.0; }
|
||||
}
|
||||
|
||||
// Draw scene via scene graph
|
||||
let view_proj = projection * view;
|
||||
draw_scene(&*root_node, transform_loc, normal_loc, &view_proj, &glm::identity());
|
||||
gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, transform.as_ptr());
|
||||
gl::BindVertexArray(my_vao);
|
||||
gl::DrawElements(
|
||||
gl::TRIANGLES,
|
||||
indices.len() as i32,
|
||||
gl::UNSIGNED_INT,
|
||||
std::ptr::null(),
|
||||
);
|
||||
}
|
||||
|
||||
// Display the new color buffer on the display
|
||||
|
||||
124
src/mesh.rs
124
src/mesh.rs
@@ -1,124 +0,0 @@
|
||||
use tobj;
|
||||
|
||||
// internal helper
|
||||
fn generate_color_vec(color: [f32; 4], num: usize) -> Vec<f32> {
|
||||
color.iter().cloned().cycle().take(num*4).collect()
|
||||
}
|
||||
|
||||
// Mesh
|
||||
|
||||
pub struct Mesh {
|
||||
pub vertices : Vec<f32>,
|
||||
pub normals : Vec<f32>,
|
||||
pub colors : Vec<f32>,
|
||||
pub indices : Vec<u32>,
|
||||
pub index_count : i32,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn from(mesh: tobj::Mesh, color: [f32; 4]) -> Self {
|
||||
let num_verts = mesh.positions.len() / 3;
|
||||
let index_count = mesh.indices.len() as i32;
|
||||
Mesh {
|
||||
vertices: mesh.positions,
|
||||
normals: mesh.normals,
|
||||
indices: mesh.indices,
|
||||
colors: generate_color_vec(color, num_verts),
|
||||
index_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lunar terrain
|
||||
|
||||
pub struct Terrain;
|
||||
impl Terrain {
|
||||
pub fn load(path: &str) -> Mesh {
|
||||
println!("Loading terrain model...");
|
||||
let before = std::time::Instant::now();
|
||||
let (models, _materials)
|
||||
= tobj::load_obj(path,
|
||||
&tobj::LoadOptions{
|
||||
triangulate: true,
|
||||
single_index: true,
|
||||
..Default::default()
|
||||
}
|
||||
).expect("Failed to load terrain model");
|
||||
let after = std::time::Instant::now();
|
||||
println!("Done in {:.3}ms.", after.duration_since(before).as_micros() as f32 / 1e3);
|
||||
|
||||
if models.len() > 1 || models.len() == 0 {
|
||||
panic!("Please use a model with a single mesh!")
|
||||
// You could try merging the vertices and indices
|
||||
// of the separate meshes into a single mesh.
|
||||
// I'll leave that as an optional exercise. ;)
|
||||
}
|
||||
|
||||
let terrain = models[0].to_owned();
|
||||
println!("Loaded {} with {} points and {} triangles.",
|
||||
terrain.name,
|
||||
terrain.mesh.positions.len() /3,
|
||||
terrain.mesh.indices.len() / 3,
|
||||
);
|
||||
|
||||
Mesh::from(terrain.mesh, [1.0, 1.0, 1.0, 1.0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helicopter
|
||||
|
||||
pub struct Helicopter {
|
||||
pub body : Mesh,
|
||||
pub door : Mesh,
|
||||
pub main_rotor : Mesh,
|
||||
pub tail_rotor : Mesh,
|
||||
}
|
||||
|
||||
// You can use square brackets to access the components of the helicopter, if you want to use loops!
|
||||
use std::ops::Index;
|
||||
impl Index<usize> for Helicopter {
|
||||
type Output = Mesh;
|
||||
fn index<'a>(&'a self, i: usize) -> &'a Mesh {
|
||||
match i {
|
||||
0 => &self.body,
|
||||
1 => &self.main_rotor,
|
||||
2 => &self.tail_rotor,
|
||||
3 => &self.door,
|
||||
_ => panic!("Invalid index, try [0,3]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Helicopter {
|
||||
pub fn load(path: &str) -> Self {
|
||||
println!("Loading helicopter model...");
|
||||
let before = std::time::Instant::now();
|
||||
let (models, _materials)
|
||||
= tobj::load_obj(path,
|
||||
&tobj::LoadOptions{
|
||||
triangulate: true,
|
||||
single_index: true,
|
||||
..Default::default()
|
||||
}
|
||||
).expect("Failed to load helicopter model");
|
||||
let after = std::time::Instant::now();
|
||||
println!("Done in {:.3}ms!", after.duration_since(before).as_micros() as f32 / 1e3);
|
||||
|
||||
for model in &models {
|
||||
println!("Loaded {} with {} points and {} triangles.", model.name, model.mesh.positions.len() / 3, model.mesh.indices.len() / 3);
|
||||
}
|
||||
|
||||
let body_model = models.iter().find(|m| m.name == "Body_body").expect("Incorrect model file!").to_owned();
|
||||
let door_model = models.iter().find(|m| m.name == "Door_door").expect("Incorrect model file!").to_owned();
|
||||
let main_rotor_model = models.iter().find(|m| m.name == "Main_Rotor_main_rotor").expect("Incorrect model file!").to_owned();
|
||||
let tail_rotor_model = models.iter().find(|m| m.name == "Tail_Rotor_tail_rotor").expect("Incorrect model file!").to_owned();
|
||||
|
||||
Helicopter {
|
||||
body: Mesh::from(body_model.mesh, [0.3, 0.3, 0.3, 1.0]),
|
||||
door: Mesh::from(door_model.mesh, [0.1, 0.1, 0.3, 1.0]),
|
||||
main_rotor: Mesh::from(main_rotor_model.mesh, [0.3, 0.1, 0.1, 1.0]),
|
||||
tail_rotor: Mesh::from(tail_rotor_model.mesh, [0.1, 0.3, 0.1, 1.0]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
extern crate nalgebra_glm as glm;
|
||||
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::pin::Pin;
|
||||
|
||||
// Used to create an unholy abomination upon which you should not cast your gaze. This ended up
|
||||
// being a necessity due to wanting to keep the code written by students as "straight forward" as
|
||||
// possible. It is very very double plus ungood Rust, and intentionally leaks memory like a sieve.
|
||||
// But it works, and you're more than welcome to pretend it doesn't exist! In case you're curious
|
||||
// about how it works: It allocates memory on the heap (Box), promises to prevent it from being
|
||||
// moved or deallocated until dropped (Pin) and finally prevents the compiler from dropping it
|
||||
// automatically at all (ManuallyDrop).
|
||||
// ...
|
||||
// If that sounds like a janky solution, it's because it is!
|
||||
// Prettier, Rustier and better solutions were tried numerous times, but were all found wanting of
|
||||
// having what I arbitrarily decided to be the required level of "simplicity of use".
|
||||
pub type Node = ManuallyDrop<Pin<Box<SceneNode>>>;
|
||||
|
||||
pub struct SceneNode {
|
||||
pub position : glm::Vec3, // Where I should be in relation to my parent
|
||||
pub rotation : glm::Vec3, // How I should be rotated, around the X, the Y and the Z axes
|
||||
pub scale : glm::Vec3, // How I should be scaled
|
||||
pub reference_point : glm::Vec3, // The point I shall rotate and scale about
|
||||
|
||||
pub vao_id : u32, // What I should draw
|
||||
pub index_count : i32, // How much of it there is to draw
|
||||
|
||||
pub children: Vec<*mut SceneNode>, // Those I command
|
||||
}
|
||||
|
||||
impl SceneNode {
|
||||
|
||||
pub fn new() -> Node {
|
||||
ManuallyDrop::new(Pin::new(Box::new(SceneNode {
|
||||
position : glm::zero(),
|
||||
rotation : glm::zero(),
|
||||
scale : glm::vec3(1.0, 1.0, 1.0),
|
||||
reference_point : glm::zero(),
|
||||
vao_id : 0,
|
||||
index_count : -1,
|
||||
children : vec![],
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn from_vao(vao_id: u32, index_count: i32) -> Node {
|
||||
ManuallyDrop::new(Pin::new(Box::new(SceneNode {
|
||||
position : glm::zero(),
|
||||
rotation : glm::zero(),
|
||||
scale : glm::vec3(1.0, 1.0, 1.0),
|
||||
reference_point : glm::zero(),
|
||||
vao_id,
|
||||
index_count,
|
||||
children: vec![],
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn add_child(&mut self, child: &SceneNode) {
|
||||
self.children.push(child as *const SceneNode as *mut SceneNode)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_child(& mut self, index: usize) -> & mut SceneNode {
|
||||
unsafe {
|
||||
&mut (*self.children[index])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn n_children(&self) -> usize {
|
||||
self.children.len()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print(&self) {
|
||||
println!(
|
||||
"SceneNode {{
|
||||
VAO: {}
|
||||
Indices: {}
|
||||
Children: {}
|
||||
Position: [{:.2}, {:.2}, {:.2}]
|
||||
Rotation: [{:.2}, {:.2}, {:.2}]
|
||||
Reference: [{:.2}, {:.2}, {:.2}]
|
||||
}}",
|
||||
self.vao_id,
|
||||
self.index_count,
|
||||
self.children.len(),
|
||||
self.position.x,
|
||||
self.position.y,
|
||||
self.position.z,
|
||||
self.rotation.x,
|
||||
self.rotation.y,
|
||||
self.rotation.z,
|
||||
self.reference_point.x,
|
||||
self.reference_point.y,
|
||||
self.reference_point.z,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// You can also use square brackets to access the children of a SceneNode
|
||||
use std::ops::{Index, IndexMut};
|
||||
impl Index<usize> for SceneNode {
|
||||
type Output = SceneNode;
|
||||
fn index(&self, index: usize) -> &SceneNode {
|
||||
unsafe {
|
||||
& *(self.children[index] as *const SceneNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IndexMut<usize> for SceneNode {
|
||||
fn index_mut(&mut self, index: usize) -> &mut SceneNode {
|
||||
unsafe {
|
||||
&mut (*self.children[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
extern crate nalgebra_glm as glm;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
pub struct Heading {
|
||||
pub x : f32,
|
||||
pub z : f32,
|
||||
pub roll : f32, // measured in radians
|
||||
pub pitch : f32, // measured in radians
|
||||
pub yaw : f32, // measured in radians
|
||||
}
|
||||
|
||||
pub fn simple_heading_animation(time: f32) -> Heading {
|
||||
let t = time as f64;
|
||||
let step = 0.05f64;
|
||||
let path_size = 15f64;
|
||||
let circuit_speed = 0.8f64;
|
||||
|
||||
let xpos = path_size * (2.0 * (t+ 0.0) * circuit_speed).sin();
|
||||
let xpos_next = path_size * (2.0 * (t+step) * circuit_speed).sin();
|
||||
let zpos = 3.0 * path_size * ((t+ 0.0) * circuit_speed).cos();
|
||||
let zpos_next = 3.0 * path_size * ((t+step) * circuit_speed).cos();
|
||||
|
||||
let delta_pos = glm::vec2(xpos_next - xpos, zpos_next - zpos);
|
||||
|
||||
let roll = (t * circuit_speed).cos() * 0.5;
|
||||
let pitch = -0.175 * glm::length(&delta_pos);
|
||||
let yaw = PI + delta_pos.x.atan2(delta_pos.y);
|
||||
|
||||
Heading {
|
||||
x : xpos as f32,
|
||||
z : zpos as f32,
|
||||
roll : roll as f32,
|
||||
pitch : pitch as f32,
|
||||
yaw : yaw as f32,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user