153 lines
3.6 KiB
Rust
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 ())
|
|
}
|