use spottedcat::{Context, DrawOption3D, Spot, WindowConfig};
use spottedcat::graphics::{Bone, identity};
use spottedcat::model::{Vertex, Model};
struct GltfApp {
model: Model,
rotation: f32,
}
impl Spot for GltfApp {
fn initialize(context: &mut Context) -> Self {
context.set_ambient_light([0.2, 0.2, 0.2, 1.0]);
context.set_light(0, [10.0, 10.0, 10.0, 0.0], [1.0, 1.0, 1.0, 1.0]);
context.set_camera_pos([0.0, 0.0, 5.0]);
let model = Model::sphere(1.0).unwrap();
Self {
model,
rotation: 0.0,
}
}
fn update(&mut self, _context: &mut Context, dt: std::time::Duration) {
self.rotation += dt.as_secs_f32() * 0.5;
}
fn draw(&mut self, context: &mut Context) {
let opts = DrawOption3D::default()
.with_position([0.0, 0.0, 0.0]) .with_rotation([0.0, self.rotation, 0.0]);
self.model.draw(context, opts);
}
fn remove(&self) {}
}
fn main() {
spottedcat::run::<GltfApp>(WindowConfig::default());
}
pub fn load_gltf(ctx: &Context, path: &str) -> anyhow::Result<(Model, u32)> {
let (document, buffers, _) = gltf::import(path)?;
let mut all_vertices = Vec::new();
let mut all_indices = Vec::new();
for mesh in document.meshes() {
for primitive in mesh.primitives() {
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
let pos_iter = reader.read_positions().ok_or_else(|| anyhow::anyhow!("No positions"))?;
let mut uv_iter = reader.read_tex_coords(0).map(|v| v.into_f32());
let mut norm_iter = reader.read_normals();
let mut joint_iter = reader.read_joints(0).map(|v| v.into_u16());
let mut weight_iter = reader.read_weights(0).map(|v| v.into_f32());
let base_idx = all_vertices.len() as u32;
for pos in pos_iter {
let uv = uv_iter.as_mut().and_then(|i| i.next()).unwrap_or([0.0, 0.0]);
let norm = norm_iter.as_mut().and_then(|i| i.next()).unwrap_or([0.0, 0.0, 1.0]);
let joints = joint_iter.as_mut().and_then(|i| i.next()).unwrap_or([0, 0, 0, 0]);
let weights = weight_iter.as_mut().and_then(|i| i.next()).unwrap_or([0.0, 0.0, 0.0, 0.0]);
all_vertices.push(Vertex {
pos,
uv,
normal: norm,
tangent: [1.0, 0.0, 0.0], joint_indices: [joints[0] as u32, joints[1] as u32, joints[2] as u32, joints[3] as u32],
joint_weights: weights,
});
}
if let Some(indices_reader) = reader.read_indices() {
for idx in indices_reader.into_u32() {
all_indices.push(base_idx + idx);
}
}
}
}
let model = Model::new(&all_vertices, &all_indices)?;
let mut skin_id = 0;
if let Some(skin) = document.skins().next() {
let reader = skin.reader(|buffer| Some(&buffers[buffer.index()]));
let ibms: Vec<[[f32; 4]; 4]> = reader.read_inverse_bind_matrices()
.map(|i| i.collect()).unwrap_or_default();
let mut node_parents = std::collections::HashMap::new();
for node in document.nodes() {
for child in node.children() {
node_parents.insert(child.index(), node.index());
}
}
let mut bones = Vec::new();
let skin_joints: Vec<_> = skin.joints().collect();
for (i, joint_node) in skin_joints.iter().enumerate() {
let ibm = ibms.get(i).copied().unwrap_or(identity());
let parent_index = node_parents.get(&joint_node.index()).and_then(|p_idx| {
skin_joints.iter().position(|node| node.index() == *p_idx)
});
bones.push(Bone {
parent_index,
inverse_bind_matrix: ibm,
});
}
let initial_matrices = vec![identity(); bones.len()];
skin_id = ctx.create_skin(bones, initial_matrices);
}
Ok((model, skin_id))
}