Display two not textured triangles in 3D with camera with horizontal rotation
Thanks to `glium/examples/teapot` which was loading an OBJ and not having any rotation for the camera.
This commit is contained in:
+2
-2
@@ -3,6 +3,6 @@ name = "lemnoslife"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
glium = "0.32.1"
|
||||||
|
glutin = "0.29.1"
|
||||||
+107
-1
@@ -1,3 +1,109 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate glium;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use glium::{glutin, Surface};
|
||||||
|
|
||||||
|
mod support;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
// building the display, ie. the main object
|
||||||
|
let event_loop = glutin::event_loop::EventLoop::new();
|
||||||
|
let wb = glutin::window::WindowBuilder::new();
|
||||||
|
let cb = glutin::ContextBuilder::new().with_depth_buffer(24);
|
||||||
|
let display = glium::Display::new(wb, cb, &event_loop).unwrap();
|
||||||
|
|
||||||
|
// building the vertex and index buffers
|
||||||
|
let vertex_buffer = support::load_ground(&display);
|
||||||
|
|
||||||
|
// the program
|
||||||
|
let program = program!(&display,
|
||||||
|
140 => {
|
||||||
|
vertex: "
|
||||||
|
#version 140
|
||||||
|
|
||||||
|
uniform mat4 persp_matrix;
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
|
||||||
|
in vec3 position;
|
||||||
|
in vec3 normal;
|
||||||
|
out vec3 v_position;
|
||||||
|
out vec3 v_normal;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_position = position;
|
||||||
|
v_normal = normal;
|
||||||
|
gl_Position = persp_matrix * view_matrix * vec4(v_position * 0.005, 1.0);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
|
||||||
|
fragment: "
|
||||||
|
#version 140
|
||||||
|
|
||||||
|
in vec3 v_normal;
|
||||||
|
out vec4 f_color;
|
||||||
|
|
||||||
|
const vec3 LIGHT = vec3(0.0, -1.0, 0.0);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
|
||||||
|
vec3 color = (0.3 + 0.7 * lum) * vec3(1.0, 1.0, 1.0);
|
||||||
|
f_color = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut camera = support::camera::CameraState::new();
|
||||||
|
|
||||||
|
// the main loop
|
||||||
|
support::start_loop(event_loop, move |events| {
|
||||||
|
camera.update();
|
||||||
|
|
||||||
|
// building the uniforms
|
||||||
|
let uniforms = uniform! {
|
||||||
|
persp_matrix: camera.get_perspective(),
|
||||||
|
view_matrix: camera.get_view(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// draw parameters
|
||||||
|
let params = glium::DrawParameters {
|
||||||
|
depth: glium::Depth {
|
||||||
|
test: glium::DepthTest::IfLess,
|
||||||
|
write: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// drawing a frame
|
||||||
|
let mut target = display.draw();
|
||||||
|
target.clear_color_and_depth((0.0, 0.0, 0.0, 0.0), 1.0);
|
||||||
|
target
|
||||||
|
.draw(
|
||||||
|
&vertex_buffer,
|
||||||
|
&glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
|
||||||
|
&program,
|
||||||
|
&uniforms,
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
target.finish().unwrap();
|
||||||
|
|
||||||
|
let mut action = support::Action::Continue;
|
||||||
|
|
||||||
|
// polling and handling the events received by the window
|
||||||
|
for event in events {
|
||||||
|
match event {
|
||||||
|
glutin::event::Event::WindowEvent { event, .. } => match event {
|
||||||
|
glutin::event::WindowEvent::CloseRequested => action = support::Action::Stop,
|
||||||
|
ev => camera.process_input(&ev),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
pub struct CameraState {
|
||||||
|
aspect_ratio: f32,
|
||||||
|
position: (f32, f32, f32),
|
||||||
|
direction: (f32, f32, f32),
|
||||||
|
|
||||||
|
moving_up: bool,
|
||||||
|
moving_left: bool,
|
||||||
|
moving_down: bool,
|
||||||
|
moving_right: bool,
|
||||||
|
moving_forward: bool,
|
||||||
|
moving_backward: bool,
|
||||||
|
rotate_left: bool,
|
||||||
|
rotate_right: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraState {
|
||||||
|
pub fn new() -> CameraState {
|
||||||
|
CameraState {
|
||||||
|
aspect_ratio: 1024.0 / 768.0,
|
||||||
|
position: (0.1, 0.1, 1.0),
|
||||||
|
// The second coordinate is for the altitude.
|
||||||
|
direction: (0.0, 0.0, -1.0),
|
||||||
|
moving_up: false,
|
||||||
|
moving_left: false,
|
||||||
|
moving_down: false,
|
||||||
|
moving_right: false,
|
||||||
|
moving_forward: false,
|
||||||
|
moving_backward: false,
|
||||||
|
rotate_left: false,
|
||||||
|
rotate_right: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_perspective(&self) -> [[f32; 4]; 4] {
|
||||||
|
let fov: f32 = 3.141592 / 2.0;
|
||||||
|
let zfar = 8000.0;
|
||||||
|
let znear = 0.01;
|
||||||
|
|
||||||
|
let f = 1.0 / (fov / 2.0).tan();
|
||||||
|
|
||||||
|
// note: remember that this is column-major, so the lines of code are actually columns
|
||||||
|
[
|
||||||
|
[f / self.aspect_ratio, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, f, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, (zfar + znear) / (zfar - znear), 1.0],
|
||||||
|
[0.0, 0.0, -(2.0 * zfar * znear) / (zfar - znear), 0.0],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view(&self) -> [[f32; 4]; 4] {
|
||||||
|
let f = {
|
||||||
|
let f = self.direction;
|
||||||
|
let len = f.0 * f.0 + f.1 * f.1 + f.2 * f.2;
|
||||||
|
let len = len.sqrt();
|
||||||
|
(f.0 / len, f.1 / len, f.2 / len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let up = (0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
let s = (
|
||||||
|
f.1 * up.2 - f.2 * up.1,
|
||||||
|
f.2 * up.0 - f.0 * up.2,
|
||||||
|
f.0 * up.1 - f.1 * up.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let s_norm = {
|
||||||
|
let len = s.0 * s.0 + s.1 * s.1 + s.2 * s.2;
|
||||||
|
let len = len.sqrt();
|
||||||
|
(s.0 / len, s.1 / len, s.2 / len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let u = (
|
||||||
|
s_norm.1 * f.2 - s_norm.2 * f.1,
|
||||||
|
s_norm.2 * f.0 - s_norm.0 * f.2,
|
||||||
|
s_norm.0 * f.1 - s_norm.1 * f.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let p = (
|
||||||
|
-self.position.0 * s.0 - self.position.1 * s.1 - self.position.2 * s.2,
|
||||||
|
-self.position.0 * u.0 - self.position.1 * u.1 - self.position.2 * u.2,
|
||||||
|
-self.position.0 * f.0 - self.position.1 * f.1 - self.position.2 * f.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// note: remember that this is column-major, so the lines of code are actually columns
|
||||||
|
[
|
||||||
|
[s_norm.0, u.0, f.0, 0.0],
|
||||||
|
[s_norm.1, u.1, f.1, 0.0],
|
||||||
|
[s_norm.2, u.2, f.2, 0.0],
|
||||||
|
[p.0, p.1, p.2, 1.0],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
let f = {
|
||||||
|
let f = self.direction;
|
||||||
|
let len = f.0 * f.0 + f.1 * f.1 + f.2 * f.2;
|
||||||
|
let len = len.sqrt();
|
||||||
|
(f.0 / len, f.1 / len, f.2 / len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let up = (0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
let s = (
|
||||||
|
f.1 * up.2 - f.2 * up.1,
|
||||||
|
f.2 * up.0 - f.0 * up.2,
|
||||||
|
f.0 * up.1 - f.1 * up.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = {
|
||||||
|
let len = s.0 * s.0 + s.1 * s.1 + s.2 * s.2;
|
||||||
|
let len = len.sqrt();
|
||||||
|
(s.0 / len, s.1 / len, s.2 / len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let u = (
|
||||||
|
s.1 * f.2 - s.2 * f.1,
|
||||||
|
s.2 * f.0 - s.0 * f.2,
|
||||||
|
s.0 * f.1 - s.1 * f.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.moving_up {
|
||||||
|
self.position.0 += u.0 * 0.01;
|
||||||
|
self.position.1 += u.1 * 0.01;
|
||||||
|
self.position.2 += u.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.moving_left {
|
||||||
|
self.position.0 -= s.0 * 0.01;
|
||||||
|
self.position.1 -= s.1 * 0.01;
|
||||||
|
self.position.2 -= s.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.moving_down {
|
||||||
|
self.position.0 -= u.0 * 0.01;
|
||||||
|
self.position.1 -= u.1 * 0.01;
|
||||||
|
self.position.2 -= u.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.moving_right {
|
||||||
|
self.position.0 += s.0 * 0.01;
|
||||||
|
self.position.1 += s.1 * 0.01;
|
||||||
|
self.position.2 += s.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.moving_forward {
|
||||||
|
self.position.0 += f.0 * 0.01;
|
||||||
|
self.position.1 += f.1 * 0.01;
|
||||||
|
self.position.2 += f.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.moving_backward {
|
||||||
|
self.position.0 -= f.0 * 0.01;
|
||||||
|
self.position.1 -= f.1 * 0.01;
|
||||||
|
self.position.2 -= f.2 * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rotate_left {
|
||||||
|
let theta: f32 = -0.1;
|
||||||
|
|
||||||
|
let a_x = self.direction.0;
|
||||||
|
let a_y = self.direction.2;
|
||||||
|
|
||||||
|
let cos_theta = theta.cos();
|
||||||
|
let sin_theta = theta.sin();
|
||||||
|
|
||||||
|
self.direction.0 = cos_theta * a_x - sin_theta * a_y;
|
||||||
|
self.direction.2 = sin_theta * a_x + cos_theta * a_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rotate_right {
|
||||||
|
let theta: f32 = 0.1;
|
||||||
|
|
||||||
|
let a_x = self.direction.0;
|
||||||
|
let a_y = self.direction.2;
|
||||||
|
|
||||||
|
let cos_theta = theta.cos();
|
||||||
|
let sin_theta = theta.sin();
|
||||||
|
|
||||||
|
self.direction.0 = cos_theta * a_x - sin_theta * a_y;
|
||||||
|
self.direction.2 = sin_theta * a_x + cos_theta * a_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rotate_left || self.rotate_right {
|
||||||
|
let norm = (self.direction.0.powi(2) + self.direction.2.powi(2)).sqrt();
|
||||||
|
self.direction.0 /= norm;
|
||||||
|
self.direction.1 /= norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_input(&mut self, event: &glutin::event::WindowEvent<'_>) {
|
||||||
|
let input = match *event {
|
||||||
|
glutin::event::WindowEvent::KeyboardInput { input, .. } => input,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let pressed = input.state == glutin::event::ElementState::Pressed;
|
||||||
|
let key = match input.virtual_keycode {
|
||||||
|
Some(key) => key,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
match key {
|
||||||
|
glutin::event::VirtualKeyCode::Up => self.moving_up = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::Down => self.moving_down = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::Q => self.moving_left = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::D => self.moving_right = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::Z => self.moving_forward = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::S => self.moving_backward = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::A => self.rotate_left = pressed,
|
||||||
|
glutin::event::VirtualKeyCode::E => self.rotate_right = pressed,
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
use glium::glutin::event::{Event, StartCause};
|
||||||
|
use glium::glutin::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use glium::vertex::VertexBufferAny;
|
||||||
|
use glium::{self, Display};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
pub mod camera;
|
||||||
|
|
||||||
|
pub enum Action {
|
||||||
|
Stop,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_loop<F>(event_loop: EventLoop<()>, mut callback: F) -> !
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(&Vec<Event<'_, ()>>) -> Action,
|
||||||
|
{
|
||||||
|
let mut events_buffer = Vec::new();
|
||||||
|
let mut next_frame_time = Instant::now();
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
let run_callback = match event.to_static() {
|
||||||
|
Some(Event::NewEvents(cause)) => match cause {
|
||||||
|
StartCause::ResumeTimeReached { .. } | StartCause::Init => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Some(event) => {
|
||||||
|
events_buffer.push(event);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Ignore this event.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = if run_callback {
|
||||||
|
let action = callback(&events_buffer);
|
||||||
|
next_frame_time = Instant::now() + Duration::from_nanos(16666667);
|
||||||
|
// TODO: Add back the old accumulator loop in some way
|
||||||
|
|
||||||
|
events_buffer.clear();
|
||||||
|
action
|
||||||
|
} else {
|
||||||
|
Action::Continue
|
||||||
|
};
|
||||||
|
|
||||||
|
match action {
|
||||||
|
Action::Continue => {
|
||||||
|
*control_flow = ControlFlow::WaitUntil(next_frame_time);
|
||||||
|
}
|
||||||
|
Action::Stop => *control_flow = ControlFlow::Exit,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a vertex buffer that should be rendered as `TrianglesList`.
|
||||||
|
pub fn load_ground(display: &Display) -> VertexBufferAny {
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Vertex {
|
||||||
|
position: [f32; 3],
|
||||||
|
normal: [f32; 3],
|
||||||
|
texture: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
implement_vertex!(Vertex, position, normal, texture);
|
||||||
|
|
||||||
|
let mut vertex_data = Vec::new();
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [0.0, 0.0, 0.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [1.0, 0.0, 0.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [0.0, 0.0, 1.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [1.0, 0.0, 0.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [0.0, 0.0, 1.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position: [1.0, 0.0, 1.0],
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
texture: [0.0, 0.0],
|
||||||
|
});
|
||||||
|
|
||||||
|
/*for object in data.objects.iter() {
|
||||||
|
for polygon in object.groups.iter().flat_map(|g| g.polys.iter()) {
|
||||||
|
match polygon {
|
||||||
|
obj::SimplePolygon(indices) => {
|
||||||
|
for v in indices.iter() {
|
||||||
|
let position = data.position[v.0];
|
||||||
|
let texture = v.1.map(|index| data.texture[index]);
|
||||||
|
let normal = v.2.map(|index| data.normal[index]);
|
||||||
|
|
||||||
|
let texture = texture.unwrap_or([0.0, 0.0]);
|
||||||
|
let normal = normal.unwrap_or([0.0, 0.0, 0.0]);
|
||||||
|
|
||||||
|
vertex_data.push(Vertex {
|
||||||
|
position,
|
||||||
|
normal,
|
||||||
|
texture,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
glium::vertex::VertexBuffer::new(display, &vertex_data)
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user