391 lines
9.3 KiB
Rust
391 lines
9.3 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
};
|
|
|
|
use glam::{
|
|
Quat,
|
|
Vec4,
|
|
};
|
|
|
|
use opengl_rust::{
|
|
prelude::*,
|
|
gl_state::{
|
|
FrontFace,
|
|
GlState,
|
|
Pass,
|
|
},
|
|
renderable_model::{
|
|
attributes,
|
|
RenderableModel,
|
|
},
|
|
shader_closure::ShaderLookup,
|
|
texture::Texture,
|
|
};
|
|
|
|
use crate::{
|
|
GameState,
|
|
TriangleStream,
|
|
};
|
|
|
|
mod uniforms {
|
|
use iota::iota;
|
|
iota! {
|
|
pub const
|
|
MVP: u32 = iota;
|
|
, OBJECT_SPACE_LIGHT
|
|
, OBJECT_SPACE_SKY
|
|
, ALBEDO
|
|
, MIN_ALBEDO
|
|
, MIN_BRIGHT
|
|
, TEXTURE
|
|
}
|
|
}
|
|
|
|
pub struct Graphics {
|
|
passes: Vec <Pass>,
|
|
|
|
shaders: Vec <ShaderClosure>,
|
|
shader_lookup: HashMap <u32, usize>,
|
|
|
|
mesh_cube: RenderableModel,
|
|
mesh_protag_kun: RenderableModel,
|
|
mesh_sky: RenderableModel,
|
|
mesh_sphere: RenderableModel,
|
|
|
|
text_stream: TriangleStream,
|
|
|
|
texture_crate: Texture,
|
|
texture_earth: Texture,
|
|
texture_sky: Texture,
|
|
texture_white: Texture,
|
|
|
|
pub frames: u64,
|
|
}
|
|
|
|
impl ShaderLookup for Graphics {
|
|
fn lookup <'a> (&'a self, id: u32) -> &'a ShaderClosure {
|
|
&self.shaders [self.shader_lookup [&id]]
|
|
}
|
|
}
|
|
|
|
impl Graphics {
|
|
pub fn new () -> Self {
|
|
let uniform_names = {
|
|
vec! [
|
|
(uniforms::MVP, "uni_mvp"),
|
|
(uniforms::OBJECT_SPACE_LIGHT, "uni_object_space_light"),
|
|
(uniforms::OBJECT_SPACE_SKY, "uni_object_space_sky"),
|
|
(uniforms::ALBEDO, "uni_albedo"),
|
|
(uniforms::MIN_ALBEDO, "uni_min_albedo"),
|
|
(uniforms::MIN_BRIGHT, "uni_min_bright"),
|
|
(uniforms::TEXTURE, "uni_texture"),
|
|
]
|
|
};
|
|
|
|
let attr_names = {
|
|
vec! [
|
|
(attributes::POS, "attr_pos"),
|
|
(attributes::UV, "attr_uv"),
|
|
(attributes::NORMAL, "attr_normal"),
|
|
]
|
|
};
|
|
|
|
let shaders: Vec <_> = [
|
|
("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"),
|
|
("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"),
|
|
("shaders/terrain-vert.glsl", "shaders/terrain-frag.glsl"),
|
|
].into_iter ()
|
|
.map (|(v, f)| {
|
|
ShaderClosure::new (shader_from_files (v, f), &uniform_names, &attr_names)
|
|
})
|
|
.collect ();
|
|
|
|
let mesh_cube = renderable_from_iqm_file ("cube.iqm");
|
|
let mesh_protag_kun = renderable_from_iqm_file ("protag-kun.iqm");
|
|
let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm");
|
|
let mesh_sphere = renderable_from_iqm_file ("sphere.iqm");
|
|
|
|
let passes = vec![
|
|
// Clear everything
|
|
Pass::default ()
|
|
.color_mask ([1, 1, 1, 1])
|
|
.depth_mask (1)
|
|
.clone (),
|
|
// Draw world
|
|
Pass::default ()
|
|
.shader (&shaders [0])
|
|
.flags ([
|
|
(gl::CULL_FACE, false),
|
|
(gl::DEPTH_TEST, true),
|
|
(gl::TEXTURE_2D, true),
|
|
(gl::STENCIL_TEST, false),
|
|
].into_iter ())
|
|
.front_face (FrontFace::Ccw)
|
|
.color_mask ([1, 1, 1, 1])
|
|
.depth_mask (1)
|
|
.clone (),
|
|
];
|
|
|
|
let text_stream = TriangleStream {
|
|
verts: gpu_buffers::VertexBuffer::streaming (4 * 5 * 6 * 1024),
|
|
indices: {
|
|
let quad = [
|
|
0, 1, 2,
|
|
0, 2, 3,
|
|
];
|
|
|
|
let v: Vec <u32> = (0u32..1024).map (|i| {
|
|
quad.iter ().map (move |j| 4 * i + j)
|
|
}).flatten ().collect ();
|
|
gpu_buffers::IndexBuffer::from_slice_u32 (&v)
|
|
}
|
|
};
|
|
|
|
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;
|
|
glezz::enable_vertex_attrib_array (attrs [attributes::POS]);
|
|
glezz::enable_vertex_attrib_array (attrs [attributes::UV]);
|
|
glezz::enable_vertex_attrib_array (attrs [attributes::NORMAL]);
|
|
});
|
|
|
|
Graphics {
|
|
mesh_cube,
|
|
mesh_protag_kun,
|
|
mesh_sky,
|
|
mesh_sphere,
|
|
passes,
|
|
shader_lookup,
|
|
shaders,
|
|
text_stream,
|
|
texture_crate: Texture::from_file ("crate.png"),
|
|
texture_earth: Texture::from_file ("earth.png"),
|
|
texture_sky: Texture::from_file ("sky.png"),
|
|
texture_white: Texture::from_file ("white.png"),
|
|
frames: 0,
|
|
}
|
|
}
|
|
|
|
pub fn draw (
|
|
&self,
|
|
state: &GameState,
|
|
gl_state: &mut GlState,
|
|
level: &crate::LoadedLevel,
|
|
camera: &crate::Camera,
|
|
) {
|
|
use serde::Deserialize;
|
|
use uniforms as u;
|
|
|
|
#[derive (Default, Deserialize)]
|
|
struct Config {
|
|
camera: CameraCfg,
|
|
}
|
|
|
|
#[derive (Default, Deserialize)]
|
|
struct CameraCfg {
|
|
#[serde(default)]
|
|
rotate_mix: f32,
|
|
#[serde(default)]
|
|
translate_mix: f32,
|
|
}
|
|
/*
|
|
let cfg: Option <Config> = std::fs::read_to_string ("config.json").ok ()
|
|
.map (|s| serde_json::from_str (s.as_str ()).ok ())
|
|
.flatten ();
|
|
|
|
let cfg = cfg.unwrap_or_else (Config::default);
|
|
*/
|
|
let white = color_from_255 ((255.0, 255.0, 255.0));
|
|
let black = color_from_255 ((0.0, 0.0, 0.0));
|
|
let shadow_blue = color_from_255 ((68.0, 36.0, 52.0));
|
|
|
|
let screen_size = (320.0, 240.0);
|
|
|
|
let fov = 39.6f32 * screen_size.1 / screen_size.0;
|
|
|
|
let proj_mat = Mat4::perspective_rh_gl (fov.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0);
|
|
|
|
let (
|
|
cam_trans,
|
|
cam_rot,
|
|
_
|
|
) = camera.transform.clone ().decomposed ();
|
|
|
|
let cam_trans = Vec3::from (cam_trans);
|
|
let cam_trans = Vec3::new (cam_trans.x.max (state.logic.player.pos.x + 4.0), cam_trans.y, cam_trans.z);
|
|
|
|
let view_mat =
|
|
Mat4::from_quat (Quat::from_array (cam_rot).inverse ()) *
|
|
Mat4::from_translation (-cam_trans);
|
|
|
|
// println! ("Camera is at {:?}", view_mat.inverse ().transform_point3 ((0.0, 0.0, 0.0).into ()));
|
|
|
|
let view_mat = proj_mat * view_mat;
|
|
|
|
let world_model_mat = Mat4::IDENTITY;
|
|
|
|
self.passes [0].with (gl_state, || {
|
|
glezz::clear_color (109f32 / 255.0, 194f32 / 255.0, 202f32 / 255.0, 1.0f32);
|
|
glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
|
|
});
|
|
|
|
self.passes [1].with_shader (gl_state, self, |shader_vars| {
|
|
let attrs = shader_vars.attrs;
|
|
let unis = shader_vars.unis;
|
|
|
|
glezz::uniform_3fv (unis [&u::ALBEDO], &white);
|
|
glezz::uniform_3fv (unis [&u::MIN_BRIGHT], &white);
|
|
|
|
if true {
|
|
self.texture_earth.bind ();
|
|
|
|
let mvp = view_mat *
|
|
Mat4::from_translation (state.logic.player.pos) *
|
|
Mat4::from_scale ((0.5, 0.5, 0.5).into ());
|
|
glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp);
|
|
glezz::uniform_3fv (unis [&u::ALBEDO], &white);
|
|
|
|
self.mesh_protag_kun.draw_all (attrs, |_| {
|
|
true
|
|
});
|
|
}
|
|
|
|
self.texture_crate.bind ();
|
|
glezz::uniform_3fv (unis [&u::ALBEDO], &white);
|
|
|
|
if true {
|
|
use opengl_rust::renderable_model::attributes as a;
|
|
|
|
let scene = level.level.scenes ().next ().unwrap ();
|
|
|
|
unsafe {
|
|
gl::BindBuffer (gl::ARRAY_BUFFER, 0);
|
|
gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
for node in scene.nodes () {
|
|
let mesh = match node.mesh () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
|
|
let mvp = view_mat *
|
|
Mat4::from_cols_array_2d (&node.transform ().matrix ());
|
|
glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp);
|
|
|
|
for prim in mesh.primitives () {
|
|
use gltf::{
|
|
Semantic,
|
|
};
|
|
|
|
let positions = match prim.get (&Semantic::Positions) {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
let pos_view = match positions.view () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
let uv_coords = match prim.get (&Semantic::TexCoords (0)) {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
let uv_view = match uv_coords.view () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
let indices = match prim.indices () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
let indices_view = match indices.view () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
|
|
unsafe {
|
|
gl::VertexAttribPointer (
|
|
attrs [a::POS].unwrap (),
|
|
3,
|
|
gl::FLOAT,
|
|
0,
|
|
4 * 3,
|
|
&level.buffer [positions.offset () + pos_view.offset ()] as *const u8 as *const c_void,
|
|
);
|
|
gl::VertexAttribPointer (
|
|
attrs [a::UV].unwrap (),
|
|
2,
|
|
gl::FLOAT,
|
|
0,
|
|
4 * 2,
|
|
&level.buffer [uv_coords.offset () + uv_view.offset ()] as *const u8 as *const c_void,
|
|
);
|
|
gl::DrawElements (
|
|
gl::TRIANGLES,
|
|
indices.count ().try_into ().unwrap (),
|
|
gl::UNSIGNED_SHORT,
|
|
&level.buffer [indices.offset () + indices_view.offset ()] as *const u8 as *const c_void,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
glezz::uniform_3fv (unis [&u::ALBEDO], &shadow_blue);
|
|
|
|
if true {
|
|
// Raycast for player shadow
|
|
let coll = opengl_rust::physics::get_candidate (
|
|
&state.static_level.tris, &[],
|
|
state.logic.player.pos,
|
|
state.logic.player.pos + Vec3::new (0.0, 0.0, -100.0),
|
|
0.0
|
|
);
|
|
|
|
self.texture_white.bind ();
|
|
|
|
// kill_z
|
|
if coll.t <= 1.0 && coll.p_impact.z > -3.0 {
|
|
let mvp = view_mat *
|
|
Mat4::from_translation (coll.p_impact + Vec3::new (0.0, 0.0, 0.0625)) *
|
|
Mat4::from_scale ((0.5, 0.5, 0.0).into ());
|
|
glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp);
|
|
|
|
self.mesh_sphere.draw_all (attrs, |_| {
|
|
true
|
|
});
|
|
}
|
|
}
|
|
|
|
if true {
|
|
let sky_mvp_mat = view_mat * Mat4::from_scale ((64.0, 64.0, 64.0).into ());
|
|
|
|
self.texture_sky.bind ();
|
|
glezz::uniform_matrix_4fv (unis [&u::MVP], &sky_mvp_mat);
|
|
glezz::uniform_3fv (unis [&u::ALBEDO], &white);
|
|
glezz::uniform_3fv (unis [&u::MIN_BRIGHT], &white);
|
|
glezz::uniform_3fv (unis [&u::MIN_ALBEDO], &black);
|
|
glezz::uniform_1i (unis [&u::TEXTURE], 0);
|
|
|
|
self.mesh_sky.draw_all (attrs, |_| true);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
fn color_from_255 <V> (rgb: V) -> Vec3
|
|
where V: Into <Vec3>
|
|
{
|
|
let rgb: Vec3 = rgb.into ();
|
|
|
|
Vec3::from ((
|
|
rgb.x / 255.0,
|
|
rgb.y / 255.0,
|
|
rgb.z / 255.0
|
|
))
|
|
}
|