2021-32-bit-holiday-jam/src/bin/platformer/level_loader.rs

153 lines
3.6 KiB
Rust

use anyhow::Result;
use opengl_rust::{
prelude::*,
physics::Triangle,
};
pub struct LoadedLevel {
pub player_spawn: Vec3,
pub camera: Camera,
pub phys_tris: Vec <Triangle>,
pub buffer: Vec <u8>,
pub level: gltf::Document,
}
impl LoadedLevel {
pub fn from_path (path: &str) -> Result <Self> {
use gltf::{
Semantic,
};
let (level, buffers, _) = gltf::import (path)?;
let buffer = match buffers.get (0) {
None => bail! ("gltf didn't load any buffers"),
Some (x) => x.0.to_vec (),
};
let scene = match level.scenes ().next () {
None => bail! ("No scenes in glTF file"),
Some (x) => x,
};
let mut player_spawn = None;
let mut camera = None;
let mut phys_tris: Vec <opengl_rust::physics::Triangle> = Vec::new ();
for node in scene.nodes () {
if node.camera ().is_some () {
camera = Some (Camera {
transform: node.transform ().clone (),
});
}
if node.name () == Some ("Player Spawn") {
let (translation, _, _) = node.transform ().decomposed ();
player_spawn = Some (Vec3::from (translation));
}
else if let Some (_camera) = node.camera () {
}
else if let Some (mesh) = node.mesh () {
let m = gltf_node_get_mat4 (&node);
for (_, prim) in mesh.primitives ().enumerate () {
let positions = match prim.get (&Semantic::Positions) {
None => continue,
Some (x) => x,
};
let normals = match prim.get (&Semantic::Normals) {
None => continue,
Some (x) => x,
};
let indices = match prim.indices () {
None => continue,
Some (x) => x,
};
let pos_view = match positions.view () {
None => continue,
Some (x) => x,
};
let _norm_view = match normals.view () {
None => continue,
Some (x) => x,
};
let indices_view = match indices.view () {
None => continue,
Some (x) => x,
};
let idx_start = indices.offset () + indices_view.offset ();
let idx_slice = &buffer [idx_start..idx_start + indices.count () * 2];
let vert_start = positions.offset () + pos_view.offset ();
let vert_slice = &buffer [vert_start..vert_start + positions.count () * 4 * 3];
for chunk in idx_slice.chunks_exact (2 * 3) {
let read_idx = |i: usize| u16::from_le_bytes ([chunk [i * 2 + 0], chunk [i * 2 + 1]]);
let idxs = [
read_idx (0),
read_idx (1),
read_idx (2),
];
let read_pos_coord = |i| f32::from_le_bytes ([
vert_slice [i * 4 + 0],
vert_slice [i * 4 + 1],
vert_slice [i * 4 + 2],
vert_slice [i * 4 + 3],
]);
let read_pos = move |i| {
let i = usize::try_from (i).unwrap ();
m.transform_point3 (Vec3::new (
read_pos_coord (i * 3 + 0),
read_pos_coord (i * 3 + 1),
read_pos_coord (i * 3 + 2),
))
};
// glTF triangle winding is backwards from what I expected
// no biggie
let verts = [
read_pos (idxs [0]),
read_pos (idxs [2]),
read_pos (idxs [1]),
];
phys_tris.push (opengl_rust::physics::Triangle {
verts,
});
}
}
}
}
let player_spawn = match player_spawn {
None => bail! ("glTF file must have `Player Spawn` node"),
Some (x) => x,
};
let camera = match camera {
None => bail! ("Couldn't find camera node in glTF file"),
Some (x) => x,
};
Ok (Self {
buffer,
camera,
level,
phys_tris,
player_spawn,
})
}
}
pub struct Camera {
pub transform: gltf::scene::Transform,
}
pub fn gltf_node_get_mat4 (node: &gltf::Node) -> Mat4 {
Mat4::from_cols_array_2d (&node.transform ().matrix ())
}