Start to rig stuff for the aerodynamics test I wanted to make in the first place

main
_ 2020-03-09 00:18:23 +00:00
parent afb0238c1b
commit 8a6d5518f1
2 changed files with 409 additions and 179 deletions

View File

@ -32,6 +32,7 @@ where V: Into <Vec3>
))
}
// TODO: Use iota macro
const KEY_LEFT: usize = 0;
const KEY_RIGHT: usize = KEY_LEFT + 1;
const KEY_UP: usize = KEY_RIGHT + 1;
@ -60,41 +61,79 @@ impl ControllerState {
}
}
struct EulerAngles {
pub azimuth: f32,
pub altitude: f32,
}
impl Default for EulerAngles {
fn default () -> Self {
Self {
azimuth: 0.0,
altitude: 0.0,
}
}
}
impl EulerAngles {
pub fn to_vec3 (&self) -> Vec3 {
let alt = self.altitude * std::f32::consts::PI / 180.0;
let azi = self.azimuth * std::f32::consts::PI / 180.0;
let z = alt.sin ();
let xy_len = alt.cos ();
let x = xy_len * azi.sin ();
let y = xy_len * azi.cos ();
(x, y, z).into ()
}
}
struct WorldState {
azimuth: f32,
altitude: f32,
camera: EulerAngles,
wind: EulerAngles,
spin_speed: i32,
}
impl WorldState {
pub fn new () -> Self {
Self {
azimuth: 0.0,
altitude: 0.0,
camera: Default::default (),
wind: Default::default (),
spin_speed: 0,
}
}
pub fn step (&mut self, controller: &ControllerState) {
pub fn step (
&mut self,
controller: &ControllerState,
user_control: &UserControl
) {
const SPIN_RAMP_TIME: i32 = 30;
let spin_f = 4.0 * self.spin_speed as f32 / SPIN_RAMP_TIME as f32;
let controlled_angle = match user_control {
UserControl::Camera => &mut self.camera,
UserControl::Wind => &mut self.wind,
};
if controller.is_pressed (KEY_LEFT) {
self.spin_speed = std::cmp::min (self.spin_speed + 1, SPIN_RAMP_TIME);
self.azimuth += spin_f;
controlled_angle.azimuth += spin_f;
}
else if controller.is_pressed (KEY_RIGHT) {
self.spin_speed = std::cmp::min (self.spin_speed + 1, SPIN_RAMP_TIME);
self.azimuth -= spin_f;
controlled_angle.azimuth -= spin_f;
}
else if controller.is_pressed (KEY_UP) {
self.spin_speed = std::cmp::min (self.spin_speed + 1, SPIN_RAMP_TIME);
self.altitude = f32::min (90.0, self.altitude + spin_f);
controlled_angle.altitude = f32::min (90.0, controlled_angle.altitude + spin_f);
}
else if controller.is_pressed (KEY_DOWN) {
self.spin_speed = std::cmp::min (self.spin_speed + 1, SPIN_RAMP_TIME);
self.altitude = f32::max (-90.0, self.altitude - spin_f);
controlled_angle.altitude = f32::max (-90.0, controlled_angle.altitude - spin_f);
}
else {
self.spin_speed = 0;
@ -145,11 +184,13 @@ where P: AsRef <std::path::Path>
struct Arrow {
origin: Vec3,
direction: Vec3,
color: Vec3,
}
struct RenderableArrow {
model_mat: Mat4,
inv_model_mat: Mat4,
color: Vec3,
}
#[derive (Copy, Clone, PartialEq, Eq)]
@ -233,6 +274,37 @@ impl From <StencilFunc> for u32 {
}
}
#[derive (Copy, Clone, PartialEq, Eq)]
pub enum DepthFunc {
Never,
Always,
Less,
LessEqual,
Equal,
Greater,
GreaterEqual,
NotEqual,
}
impl From <DepthFunc> for u32 {
fn from (v: DepthFunc) -> Self {
use DepthFunc::*;
{
use gl::*;
match v {
Never => NEVER,
Always => ALWAYS,
Less => LESS,
LessEqual => LEQUAL,
Equal => EQUAL,
Greater => GREATER,
GreaterEqual => GEQUAL,
NotEqual => NOTEQUAL,
}
}
}
}
#[derive (Copy, Clone, PartialEq, Eq)]
pub struct StencilOpState {
sfail: StencilOp,
@ -257,9 +329,12 @@ pub struct StencilState {
// to safely use the flags / numbers
pub struct IsoGlState {
shader_id: Option <u32>,
flags: HashMap <u32, bool>,
front_face: Option <FrontFace>,
stencil: Option <StencilState>,
depth_func: Option <DepthFunc>,
color_mask: Option <(u8, u8, u8, u8)>,
depth_mask: Option <u8>,
@ -269,9 +344,11 @@ pub struct IsoGlState {
impl std::default::Default for IsoGlState {
fn default () -> Self {
Self {
shader_id: None,
flags: hashmap! {},
front_face: None,
stencil: None,
depth_func: None,
color_mask: None,
depth_mask: None,
stencil_mask: None,
@ -282,13 +359,11 @@ impl std::default::Default for IsoGlState {
// These are POD IDs or hashes of non-POD data
pub struct NonIsoGlState {
shader_id: Option <u32>,
}
impl std::default::Default for NonIsoGlState {
fn default () -> Self {
Self {
shader_id: None,
}
}
}
@ -307,17 +382,13 @@ impl std::default::Default for GlState {
}
}
pub struct Pass <'a> {
pub struct Pass {
// In the context of a Pass, "None" means "Don't care"
iso: IsoGlState,
// Non-iso state must have its sources here
shader: Option <&'a ShaderClosure>,
}
impl Pass <'_> {
impl Pass {
pub fn apply_diff (&self, old_state: &mut IsoGlState) {
let state = &self.iso;
@ -381,6 +452,16 @@ impl Pass <'_> {
}
}
if let Some (v) = &state.depth_func {
if old_state.depth_func != state.depth_func {
glezz::depth_func ((*v).into ());
old_state.depth_func = state.depth_func;
}
else {
//println! ("Elided depth_func ()");
}
}
if let Some ((r, g, b, a)) = &state.color_mask {
if old_state.color_mask != state.color_mask {
glezz::color_mask (*r, *g, *b, *a);
@ -425,15 +506,20 @@ impl Pass <'_> {
callback ();
}
pub fn with_shader <F> (&self, gl_state: &mut GlState, callback: F)
where F: Fn (BorrowedShaderVars)
pub fn with_shader <F, S> (
&self,
gl_state: &mut GlState,
shader_component: &S,
callback: F
)
where F: Fn (BorrowedShaderVars), S: ShaderLookup
{
if let Some (s) = self.shader {
if let Some (s) = self.iso.shader_id {
self.apply_diff (&mut gl_state.iso);
s.with (gl_state.non_iso.shader_id, |shader_vars| {
shader_component.lookup (s).with (gl_state.iso.shader_id, |shader_vars| {
callback (shader_vars);
});
gl_state.non_iso.shader_id = Some (s.get_id ());
gl_state.iso.shader_id = Some (s);
}
else {
panic! ("Called with_shader on a pass with no shader");
@ -441,9 +527,15 @@ impl Pass <'_> {
}
}
pub trait ShaderLookup {
fn lookup <'a> (&'a self, id: u32) -> &'a ShaderClosure;
}
struct GameGraphics {
shader_diffuse: ShaderClosure,
shader_shadow: ShaderClosure,
passes: Vec <Pass>,
shaders: Vec <ShaderClosure>,
shader_lookup: HashMap <u32, usize>,
mesh_pumpkin: RenderableModel,
mesh_sky: RenderableModel,
@ -456,6 +548,12 @@ struct GameGraphics {
grass_index: usize,
}
impl ShaderLookup for GameGraphics {
fn lookup <'a> (&'a self, id: u32) -> &'a ShaderClosure {
&self.shaders [self.shader_lookup [&id]]
}
}
impl GameGraphics {
pub fn new () -> Self {
let uniform_names = {
@ -480,10 +578,21 @@ impl GameGraphics {
]
};
let shader_diffuse = ShaderClosure::new (shader_from_files ("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"), &uniform_names, &attr_names);
let shader_shadow = ShaderClosure::new (shader_from_files ("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"), &uniform_names, &attr_names);
let shaders: Vec <_> = [
("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"),
("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"),
].iter ()
.map (|(v, f)| {
ShaderClosure::new (shader_from_files (v, f), &uniform_names, &attr_names)
})
.collect ();
shader_diffuse.with (None, |shader_vars| {
let shader_lookup = HashMap::from_iter (shaders.iter ().enumerate ()
.map (|(i, s)| {
(s.get_id (), i)
}));
shaders [0].with (None, |shader_vars| {
let attrs = shader_vars.attrs;
use renderable_model::attributes::*;
glezz::enable_vertex_attrib_array (attrs [POS]);
@ -538,9 +647,183 @@ impl GameGraphics {
(colors, grass_index.unwrap ())
};
let passes = vec! [
// Clear everything
Pass {
iso: IsoGlState {
shader_id: None,
flags: hashmap! {},
front_face: None,
stencil: None,
depth_func: None,
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (255),
},
},
// Draw world
Pass {
iso: IsoGlState {
shader_id: Some (shaders [0].get_id ()),
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => false,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Always,
reference: 0,
mask: 0,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
depth_func: Some (DepthFunc::Less),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
// Write shadows into stencil buffer
Pass {
iso: IsoGlState {
shader_id: Some (shaders [1].get_id ()),
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Ccw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Always,
reference: 1,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Replace,
},
}),
depth_func: Some (DepthFunc::Less),
color_mask: Some ((0, 0, 0, 0)),
depth_mask: Some (0),
stencil_mask: Some (255),
},
},
// Draw lit ground
Pass {
iso: IsoGlState {
shader_id: Some (shaders [0].get_id ()),
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::NotEqual,
reference: 0,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
depth_func: Some (DepthFunc::Less),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
// Draw unlit ground
Pass {
iso: IsoGlState {
shader_id: Some (shaders [0].get_id ()),
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Equal,
reference: 0,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
depth_func: Some (DepthFunc::Less),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
// Clear depth
Pass {
iso: IsoGlState {
shader_id: None,
flags: hashmap! {},
front_face: None,
stencil: None,
depth_func: None,
color_mask: None,
depth_mask: Some (1),
stencil_mask: None,
},
},
// Draw arrows
Pass {
iso: IsoGlState {
shader_id: Some (shaders [0].get_id ()),
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => false,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Always,
reference: 0,
mask: 0,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
depth_func: Some (DepthFunc::Less),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
];
Self {
shader_diffuse,
shader_shadow,
passes,
shaders,
shader_lookup,
mesh_pumpkin,
mesh_sky,
@ -574,8 +857,8 @@ impl GameGraphics {
green,
];
let longitude = state.azimuth.to_radians ();
let latitude = (state.altitude - 90.0).to_radians ();
let longitude = state.camera.azimuth.to_radians ();
let latitude = (state.camera.altitude - 90.0).to_radians ();
let proj_mat = Mat4::perspective_rh_gl (30.0f32.to_radians (), 1280.0 / 720.0, 0.5, 500.0);
@ -598,122 +881,9 @@ impl GameGraphics {
//println! ("Started frame");
let passes = vec! [
// Clear everything
Pass {
shader: None,
iso: IsoGlState {
flags: hashmap! {},
front_face: None,
stencil: None,
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (255),
},
},
// Draw world
Pass {
shader: Some (&self.shader_diffuse),
iso: IsoGlState {
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => false,
},
front_face: Some (FrontFace::Cw),
stencil: None,
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
// Write shadows into stencil buffer
Pass {
shader: Some (&self.shader_shadow),
iso: IsoGlState {
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Ccw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Always,
reference: 1,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Replace,
},
}),
color_mask: Some ((0, 0, 0, 0)),
depth_mask: Some (0),
stencil_mask: Some (255),
},
},
// Draw lit ground
Pass {
shader: Some (&self.shader_diffuse),
iso: IsoGlState {
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::NotEqual,
reference: 0,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
// Draw unlit ground
Pass {
shader: Some (&self.shader_diffuse),
iso: IsoGlState {
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::STENCIL_TEST => true,
},
front_face: Some (FrontFace::Cw),
stencil: Some (StencilState {
func: StencilFuncState {
func: StencilFunc::Equal,
reference: 0,
mask: 1,
},
op: StencilOpState {
sfail: StencilOp::Keep,
dpfail: StencilOp::Keep,
dppass: StencilOp::Keep,
},
}),
color_mask: Some ((1, 1, 1, 1)),
depth_mask: Some (1),
stencil_mask: Some (0),
},
},
];
let mut passes = self.passes.iter ();
passes [0].with (gl_state, || {
passes.next ().unwrap ().with (gl_state, || {
glezz::clear_color (1.0f32, 0.0f32, 1.0f32, 1.0f32);
glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
});
@ -730,7 +900,8 @@ impl GameGraphics {
use uniforms::*;
// Draw the world except the ground plane
passes [1].with_shader (gl_state, |shader_vars| {
passes.next ().unwrap ().with_shader (gl_state, self,
|shader_vars| {
let unis = shader_vars.unis;
let attrs = shader_vars.attrs;
@ -759,19 +930,6 @@ impl GameGraphics {
i != self.grass_index
});
glezz::uniform_3fv (unis [&ALBEDO], &magenta);
for arrow in arrows.iter () {
let mvp = view_mat * arrow.model_mat;
glezz::uniform_matrix_4fv (unis [&MVP], &mvp);
let object_space_light = make_object_space_vec (&arrow.inv_model_mat, &light);
let object_space_sky = make_object_space_vec (&arrow.inv_model_mat, &Vec3::from ((0.0, 0.0, 1.0)));
glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light);
glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky);
self.mesh_arrow.draw_all (attrs, |_| true);
}
let draw_sky = true;
if draw_sky {
self.texture.bind ();
@ -786,7 +944,8 @@ impl GameGraphics {
});
// Draw shadows into stencil buffer
passes [2].with_shader (gl_state, |shader_vars| {
passes.next ().unwrap ().with_shader (gl_state, self,
|shader_vars| {
let unis = shader_vars.unis;
let attrs = shader_vars.attrs;
@ -813,7 +972,8 @@ impl GameGraphics {
let object_space_light = make_object_space_vec (&inverse_pumpkin, &light);
// Draw unlit ground
passes [3].with_shader (gl_state, |shader_vars| {
passes.next ().unwrap ().with_shader (gl_state, self,
|shader_vars| {
let unis = shader_vars.unis;
let attrs = shader_vars.attrs;
@ -835,16 +995,51 @@ impl GameGraphics {
});
// Draw lit ground
passes [4].with_shader (gl_state, |shader_vars| {
passes.next ().unwrap ().with_shader (gl_state, self,
|shader_vars| {
let unis = shader_vars.unis;
let attrs = shader_vars.attrs;
glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light);
self.mesh_pitch.draw (attrs, self.grass_index);
});
// Clear depth
passes.next ().unwrap ().with (gl_state, || {
glezz::clear (gl::DEPTH_BUFFER_BIT);
});
// Draw arrows
passes.next ().unwrap ().with_shader (gl_state, self,
|shader_vars| {
let unis = &shader_vars.unis;
glezz::uniform_3fv (unis [&MIN_BRIGHT], &black);
glezz::uniform_3fv (unis [&MIN_ALBEDO], &white);
for arrow in arrows.iter () {
let mvp = view_mat * arrow.model_mat;
glezz::uniform_matrix_4fv (unis [&MVP], &mvp);
let object_space_light = make_object_space_vec (&arrow.inv_model_mat, &light);
let object_space_sky = make_object_space_vec (&arrow.inv_model_mat, &Vec3::from ((0.0, 0.0, 1.0)));
glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light);
glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky);
glezz::uniform_3fv (unis [&ALBEDO], &arrow.color);
self.mesh_arrow.draw_all (shader_vars.attrs, |_| true);
}
});
}
}
#[derive (Copy, Clone, PartialEq, Eq)]
enum UserControl {
Camera,
Wind,
}
fn main () {
let sdl_context = sdl2::init ().unwrap ();
let video_subsystem = sdl_context.video ().unwrap ();
@ -871,16 +1066,13 @@ fn main () {
let graphics = GameGraphics::new ();
let mut gl_state = Default::default ();
let mut user_control = UserControl::Camera;
let mut graphics_frames = 0;
let mut event_pump = sdl_context.event_pump ().unwrap ();
'running: loop {
let frames_to_do = time_step.step ();
let controller = ControllerState::from_sdl_keyboard (&event_pump.keyboard_state ());
for _ in 0..frames_to_do {
state.step (&controller);
}
let _mouse = event_pump.mouse_state ();
for event in event_pump.poll_iter() {
@ -889,27 +1081,55 @@ fn main () {
Event::KeyDown { keycode: Some (Keycode::Escape), .. } => {
break 'running
},
Event::KeyDown { keycode: Some (Keycode::C), .. } => {
user_control = UserControl::Camera;
},
Event::KeyDown { keycode: Some (Keycode::W), .. } => {
user_control = UserControl::Wind;
},
_ => (),
}
}
let controller = ControllerState::from_sdl_keyboard (&event_pump.keyboard_state ());
for _ in 0..frames_to_do {
state.step (&controller, &user_control);
}
let gravity = (0.0, 0.0, -1.0).into ();
let wind = state.wind.to_vec3 () * -1.0;
let origin: Vec3 = (0.0, 0.0, 1.35).into ();
let control_flash = if graphics_frames % 16 >= 8 {
(1.0, 1.0, 1.0).into ()
}
else {
(1.0, 0.0, 0.0).into ()
};
let arrows = vec![
Arrow {
origin: (0.0, 0.0, 1.0).into (),
direction: (0.0, 0.0, 1.0).into (),
origin: (0.0, 0.0, 1.35).into (),
direction: gravity,
color: (1.0, 0.5, 0.5).into (),
},
Arrow {
origin: (1.0, 0.0, 1.0).into (),
direction: (1.0, 0.0, 0.0).into (),
},
Arrow {
origin: (0.0, 1.0, 1.0).into (),
direction: (0.0, 1.0, 0.0).into (),
origin: origin + wind * -2.0,
direction: wind,
color: if user_control == UserControl::Wind {
control_flash
}
else {
(1.0, 0.5, 1.0).into ()
},
},
];
let renderable_arrows: Vec <_> = arrows.iter ().map (|arrow| {
let d = arrow.direction;
let dir_len = arrow.direction.length ();
let d = arrow.direction / dir_len;
let up: Vec3 = if d.z () > 0.5 {
(-1.0, 0.0, 0.0)
@ -932,9 +1152,11 @@ fn main () {
dir_mat.set_y_axis ((up.x (), up.y (), up.z (), 0.0).into ());
dir_mat.set_z_axis ((d.x (), d.y (), d.z (), 0.0).into ());
let s = dir_len * 0.0625;
let model_mat =
Mat4::from_translation (arrow.origin) *
Mat4::from_scale ((0.125, 0.125, 0.125).into ()) *
Mat4::from_scale ((s, s, s).into ()) *
dir_mat;
let inv_model_mat = model_mat.inverse ();
@ -942,6 +1164,7 @@ fn main () {
RenderableArrow {
model_mat,
inv_model_mat,
color: arrow.color,
}
}).collect ();
@ -950,6 +1173,7 @@ fn main () {
graphics.draw (&state, &mut gl_state, &renderable_arrows);
window.gl_swap_window ();
graphics_frames += 1;
std::thread::sleep (Duration::from_millis (15));
}

View File

@ -97,6 +97,12 @@ pub fn stencil_mask (v: u32) {
}
}
pub fn depth_func (v: u32) {
unsafe {
gl::DepthFunc (v);
}
}
// More abstract things below here