35 Commits
ov2 ... ov3-03

Author SHA1 Message Date
f0e62a6d83 Update .gitignore 2025-10-02 12:41:05 +02:00
f7fcc2359b fix: correct glm::mat3 usage and add missing argument in draw_scene call 2025-10-02 12:11:22 +02:00
46bdbfb889 fix: rotate normals with model to correct helicopter lighting during rotation 2025-10-02 12:04:45 +02:00
e6987e506f fix: correct rotation order to pitch, yaw, roll for proper orientation 2025-10-02 11:28:48 +02:00
030df64fc1 feat: animate helicopter using simple_heading_animation with full 3-axis rotation 2025-10-02 11:25:53 +02:00
978c3c3936 fix: rotate tail rotor on x-axis instead of y-axis to match rigth axis 2025-10-02 11:23:27 +02:00
a729d28841 feat: animate helicopter rotors spinning and draw scene via scene graph 2025-10-02 11:21:56 +02:00
e139ca67f6 feat: add model matrix transforms and set tail rotor reference point for scene nodes 2025-10-02 11:19:15 +02:00
c6c77f7030 fix: declare scene graph nodes as mutable to fix borrow errors 2025-10-02 11:06:35 +02:00
6e53caae56 refactor: replace manual VAO draws with recursive scene graph draw function 2025-10-02 10:38:37 +02:00
ac1368c509 feat: create scene graph with terrain and helicopter nodes in main.rs 2025-10-02 10:19:11 +02:00
ed0ccb854d feat: load and draw helicopter model with separate VAOs for each part 2025-10-02 10:19:01 +02:00
ba305c56fd feat: add Lambertian lighting to fragment shader for surface shading 2025-10-02 01:29:03 +02:00
bdfafea108 fix: swap W and S key movement directions back to original behavior 2025-10-02 01:24:35 +02:00
12a2d5bb5e fix: correct camera movement to be relative to current direction vectors 2025-10-02 01:16:51 +02:00
aedd576afe fix: swap W and S movement directions and double camera speed for correct control 2025-10-02 01:14:23 +02:00
4149277dde fix: double movement speed for smoother camera control 2025-10-02 01:09:26 +02:00
b1d0bbdfb6 feat: visualize vertex normals as colors by passing normals to fragment shader 2025-10-02 01:06:30 +02:00
07a0b0260d feat: add normal vectors to VAO creation for lighting calculations 2025-10-02 01:02:53 +02:00
20c7dc975c fix: remove sine-cosine transformations from model matrix calculation 2025-10-02 00:56:02 +02:00
ebaf03237e refactor: remove moving triangles and related draw calls from main.rs 2025-10-02 00:52:37 +02:00
f4b85afff8 feat: load and draw lunar terrain with extended far plane in perspective matrix 2025-10-02 00:44:51 +02:00
fcb365bcf5 chore: added given sample code. 2025-10-02 00:39:34 +02:00
5d643ddb4e feat: render a large grid of point-particles as billboards with program point size enabled 2025-09-18 10:55:48 +02:00
74342b5d1a feat: add camera-facing billboard quad rendering with rotation invariance 2025-09-18 10:55:43 +02:00
e3509a7ece feat: move camera relative to its orientation and clamp pitch angle 2025-09-18 00:31:13 +02:00
de7cc20db3 fix: apply correct model, view, and projection matrices for camera motion each frame 2025-09-17 23:49:33 +02:00
861c6ebd62 feat: add key handlers for tripod camera movement and rotation controls 2025-09-17 23:49:33 +02:00
f6422f403e feat: add camera position and orientation variables for tripod motion control 2025-09-17 23:49:33 +02:00
b08a38ce74 feat: apply translation and perspective projection to transform matrix before shader upload 2025-09-17 23:49:33 +02:00
0400e0969d feat: bind transform uniform to location 0 in vertex shader for clarity 2025-09-17 23:49:33 +02:00
64f04a8b4c vert 2025-09-17 23:49:33 +02:00
ff9600071f feat: cycle through transform matrix params a-f over time in main.rs uniform update 2025-09-17 23:49:31 +02:00
179da91770 fix: add type annotation to resolve ambiguous transform matrix type in main.rs 2025-09-17 23:49:27 +02:00
70f6812fb4 feat: add uniform transform matrix with modifiable elements in main.rs 2025-09-17 23:49:19 +02:00
9 changed files with 372986 additions and 32 deletions

2
.gitignore vendored
View File

@@ -3,7 +3,5 @@
*.pdf
*.html
source.zip
.aider*
flake.lock

229659
resources/helicopter.obj Normal file

File diff suppressed because it is too large Load Diff

142855
resources/lunarsurface.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,11 @@
out layout(location=0) vec4 color;
in vec4 gl_FragCoord;
in vec4 vColor;
in vec3 vNormal;
void main() {
color = vColor;
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);
}

View File

@@ -2,18 +2,14 @@
in layout(location=0) vec3 position;
in layout(location=1) vec4 aColor;
in layout(location=2) vec3 aNormal;
out vec4 vColor;
mat4 transform;
out vec3 vNormal;
layout(location = 0) uniform mat4 transform;
uniform mat3 normalMatrix;
void main() {
transform = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
gl_Position = transform * vec4(position, 1.0f);
vColor = aColor;
vNormal = normalize(normalMatrix * aNormal);
}

View File

@@ -14,6 +14,11 @@ 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,
@@ -59,7 +64,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>, colors: &Vec<f32>, indices: &Vec<u32>) -> u32 {
unsafe fn create_vao(vertices: &Vec<f32>, normals: &Vec<f32>, colors: &Vec<f32>, indices: &Vec<u32>) -> u32 {
let mut vao_id = 0;
gl::GenVertexArrays(1, &mut vao_id);
gl::BindVertexArray(vao_id);
@@ -108,9 +113,57 @@ unsafe fn create_vao(vertices: &Vec<f32>, colors: &Vec<f32>, indices: &Vec<u32>)
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();
@@ -163,6 +216,7 @@ 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());
@@ -180,6 +234,37 @@ 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
@@ -218,7 +303,40 @@ fn main() {
let my_vao = unsafe { create_vao(&vertices, &colors, &indices) };
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 pointparticles
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;
// == // Set up your shaders here
@@ -236,10 +354,18 @@ fn main() {
.attach_file("./shaders/simple.frag")
.link()
};
// 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;
@@ -263,23 +389,38 @@ fn main() {
}
}
// Handle keyboard input
// Handle keyboard input for camera movement and rotation
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 {
// 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:
// 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; }
_ => {}
}
}
// 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() {
@@ -298,13 +439,36 @@ fn main() {
// == // Issue the necessary gl:: commands to draw your scene here
simple_shader.activate();
gl::BindVertexArray(my_vao);
gl::DrawElements(
gl::TRIANGLES,
indices.len() as i32,
gl::UNSIGNED_INT,
std::ptr::null(),
);
// 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);
}
}
// 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());
}
// Display the new color buffer on the display

124
src/mesh.rs Normal file
View File

@@ -0,0 +1,124 @@
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]),
}
}
}

118
src/scene_graph.rs Normal file
View File

@@ -0,0 +1,118 @@
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])
}
}
}

36
src/toolbox.rs Normal file
View File

@@ -0,0 +1,36 @@
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,
}
}