use anyhow::Result; use opengl_rust::{ prelude::*, physics::Triangle, }; pub struct LoadedLevel { pub player_spawn: Vec3, pub camera: Camera, pub phys_tris: Vec , pub buffer: Vec , pub level: gltf::Document, } impl LoadedLevel { pub fn from_path (path: &str) -> Result { 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 = 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 ()) }