Wire up the viewer for material animations

Currently they don't do anything though.
This commit is contained in:
scurest 2019-09-05 00:26:39 -05:00
parent 60ed16ad2d
commit af0d02da34
6 changed files with 171 additions and 21 deletions

View File

@ -7,7 +7,7 @@
//! out for ourselves. This modules contains the heuristics for that. //! out for ourselves. This modules contains the heuristics for that.
use clap::ArgMatches; use clap::ArgMatches;
use db::{Database, AnimationId, TextureId, PaletteId, ModelId, PatternId}; use db::{Database, AnimationId, TextureId, PaletteId, ModelId, PatternId, MatAnimId};
use errors::Result; use errors::Result;
/// A Connection records interrelationships between Nitro resources, namely how /// A Connection records interrelationships between Nitro resources, namely how
@ -25,6 +25,8 @@ pub struct ModelConnection {
/// List of patterns that can be applied to the model (and how to apply /// List of patterns that can be applied to the model (and how to apply
/// them). /// them).
pub patterns: Vec<PatternConnection>, pub patterns: Vec<PatternConnection>,
/// List of material animations that can be applied to the model.
pub mat_anims: Vec<MatAnimConnection>,
} }
/// Result of resolving which texture/palette a material should use. /// Result of resolving which texture/palette a material should use.
@ -118,7 +120,8 @@ impl Connection {
let animations = find_applicable_animations(db, model_id, options); let animations = find_applicable_animations(db, model_id, options);
let patterns = find_applicable_patterns(db, model_id); let patterns = find_applicable_patterns(db, model_id);
ModelConnection { materials, animations, patterns } let mat_anims = find_applicable_mat_anims(db, model_id);
ModelConnection { materials, animations, patterns, mat_anims }
}).collect(); }).collect();
if missing_textures { if missing_textures {
@ -285,3 +288,20 @@ fn find_applicable_patterns(db: &Database, model_id: ModelId) -> Vec<PatternConn
}) })
}).collect() }).collect()
} }
/// Indicates that a model can have the specified material animations applied to
/// it.
pub struct MatAnimConnection {
pub mat_anim_id: MatAnimId,
}
fn find_applicable_mat_anims(db: &Database, model_id: ModelId) -> Vec<MatAnimConnection> {
let model = &db.models[model_id];
db.mat_anims.iter().enumerate().filter_map(|(mat_anim_id, mat_anim)| {
// Check if all the tracks target valid materials
let valid = mat_anim.tracks.iter().all(|track| {
model.materials.iter().any(|mat| mat.name == track.name)
});
if !valid { None } else { Some(MatAnimConnection { mat_anim_id }) }
}).collect()
}

View File

@ -6,7 +6,7 @@ use cgmath::{Matrix4, One};
use convert::image_namer::ImageNamer; use convert::image_namer::ImageNamer;
use db::{Database, ModelId}; use db::{Database, ModelId};
use skeleton::{Skeleton, Transform, SMatrix}; use skeleton::{Skeleton, Transform, SMatrix};
use primitives::{self, Primitives}; use primitives::{self, Primitives, DynamicState};
use nitro::Model; use nitro::Model;
use petgraph::{Direction}; use petgraph::{Direction};
use petgraph::graph::NodeIndex; use petgraph::graph::NodeIndex;
@ -42,7 +42,17 @@ pub fn write(
let objects = &model.objects.iter() let objects = &model.objects.iter()
.map(|o| make_invertible(&o.matrix)) .map(|o| make_invertible(&o.matrix))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let prims = &Primitives::build(model, primitives::PolyType::TrisAndQuads, objects); let uv_mats = &model.materials.iter()
.map(|mat| {
if mat.params.texcoord_transform_mode() == 1 {
mat.texture_mat
} else {
Matrix4::one()
}
})
.collect::<Vec<_>>();
let state = DynamicState { objects, uv_mats };
let prims = &Primitives::build(model, primitives::PolyType::TrisAndQuads, state);
let skel = &Skeleton::build(model, objects); let skel = &Skeleton::build(model, objects);
let ctx = Ctx { model_id, model, db, conn, image_namer, objects, prims, skel }; let ctx = Ctx { model_id, model, db, conn, image_namer, objects, prims, skel };

View File

@ -6,10 +6,10 @@ mod primitive;
use nitro::Model; use nitro::Model;
use db::{Database, ModelId}; use db::{Database, ModelId};
use connection::Connection; use connection::Connection;
use primitives::{Primitives, PolyType}; use primitives::{Primitives, PolyType, DynamicState};
use skeleton::{Skeleton, Transform, SMatrix}; use skeleton::{Skeleton, Transform, SMatrix};
use super::image_namer::ImageNamer; use super::image_namer::ImageNamer;
use cgmath::Matrix4; use cgmath::{Matrix4, One};
use json::JsonValue; use json::JsonValue;
use self::gltf::{GlTF, Buffer, ByteVec, VecExt}; use self::gltf::{GlTF, Buffer, ByteVec, VecExt};
use self::object_trs::ObjectTRSes; use self::object_trs::ObjectTRSes;
@ -45,7 +45,17 @@ pub fn to_gltf(
let objects = rest_trses.objects.iter() let objects = rest_trses.objects.iter()
.map(Matrix4::from) .map(Matrix4::from)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let prims = Primitives::build(model, PolyType::TrisAndQuads, &objects); let uv_mats = model.materials.iter()
.map(|mat| {
if mat.params.texcoord_transform_mode() == 1 {
mat.texture_mat
} else {
Matrix4::one()
}
})
.collect::<Vec<Matrix4<f64>>>();
let state = DynamicState { objects: &objects, uv_mats: &uv_mats };
let prims = Primitives::build(model, PolyType::TrisAndQuads, state);
let prims = &encode_ngons(prims); let prims = &encode_ngons(prims);
let skel = &Skeleton::build(model, &objects); let skel = &Skeleton::build(model, &objects);

View File

@ -15,7 +15,7 @@ pub type TextureId = usize;
pub type PaletteId = usize; pub type PaletteId = usize;
pub type AnimationId = usize; pub type AnimationId = usize;
pub type PatternId = usize; pub type PatternId = usize;
//pub type MatAnimId = usize; pub type MatAnimId = usize;
#[derive(Default)] #[derive(Default)]
pub struct Database { pub struct Database {

View File

@ -31,6 +31,15 @@ pub enum PolyType {
TrisAndQuads, TrisAndQuads,
} }
/// Dynamic state for a model (ie. stuff that changes during an animation, as
/// opposed to static state in the Model object).
pub struct DynamicState<'a> {
/// Object matrices to use.
pub objects: &'a [Matrix4<f64>],
/// UV-transform matrices to use for each material.
pub uv_mats: &'a [Matrix4<f64>],
}
/// Info about the result of a draw call, ie. the result of drawing a mesh (a set /// Info about the result of a draw call, ie. the result of drawing a mesh (a set
/// of GPU commands) while in a particular GPU state (matrix stack, bound material, /// of GPU commands) while in a particular GPU state (matrix stack, bound material,
/// etc.). /// etc.).
@ -78,8 +87,8 @@ impl Default for Vertex {
implement_vertex!(Vertex, position, texcoord, color, normal); implement_vertex!(Vertex, position, texcoord, color, normal);
impl Primitives { impl Primitives {
pub fn build(model: &Model, poly_type: PolyType, objects: &[Matrix4<f64>]) -> Primitives { pub fn build(model: &Model, poly_type: PolyType, state: DynamicState) -> Primitives {
let mut b = Builder::new(model, poly_type, objects); let mut b = Builder::new(model, poly_type, state);
use nitro::render_cmds::Op; use nitro::render_cmds::Op;
for op in &model.render_ops { for op in &model.render_ops {
match *op { match *op {
@ -125,7 +134,7 @@ impl GpuState {
struct Builder<'a, 'b> { struct Builder<'a, 'b> {
model: &'a Model, model: &'a Model,
objects: &'b [Matrix4<f64>], state: DynamicState<'b>,
poly_type: PolyType, poly_type: PolyType,
gpu: GpuState, gpu: GpuState,
@ -140,14 +149,13 @@ struct Builder<'a, 'b> {
cur_draw_call: DrawCall, cur_draw_call: DrawCall,
next_vertex: Vertex, next_vertex: Vertex,
} }
impl<'a, 'b> Builder<'a, 'b> { impl<'a, 'b> Builder<'a, 'b> {
fn new(model: &'a Model, poly_type: PolyType, objects: &'b [Matrix4<f64>]) -> Builder<'a, 'b> { fn new(model: &'a Model, poly_type: PolyType, state: DynamicState<'b>) -> Builder<'a, 'b> {
Builder { Builder {
model, model,
objects, state,
poly_type, poly_type,
gpu: GpuState::new(), gpu: GpuState::new(),
vertices: vec![], vertices: vec![],
@ -174,6 +182,9 @@ impl<'a, 'b> Builder<'a, 'b> {
let vert_len = self.vertices.len() as u16; let vert_len = self.vertices.len() as u16;
let ind_len = self.indices.len(); let ind_len = self.indices.len();
// Bind material
self.gpu.texture_matrix = self.state.uv_mats[mat_id as usize];
self.cur_draw_call = DrawCall { self.cur_draw_call = DrawCall {
vertex_range: vert_len..vert_len, vertex_range: vert_len..vert_len,
index_range: ind_len..ind_len, index_range: ind_len..ind_len,
@ -215,7 +226,7 @@ impl<'a, 'b> Builder<'a, 'b> {
} }
fn mul_by_object(&mut self, object_id: u8) { fn mul_by_object(&mut self, object_id: u8) {
self.gpu.mul_matrix(&self.objects[object_id as usize]); self.gpu.mul_matrix(&self.state.objects[object_id as usize]);
} }
fn blend(&mut self, terms: &[SkinTerm]) { fn blend(&mut self, terms: &[SkinTerm]) {
@ -369,12 +380,14 @@ fn run_gpu_cmds(b: &mut Builder, commands: &[u8]) {
GpuCmd::TexCoord { texcoord } => { GpuCmd::TexCoord { texcoord } => {
b.cur_draw_call.used_texcoords = true; b.cur_draw_call.used_texcoords = true;
// Apply texture matrix
let texcoord = b.gpu.texture_matrix * vec4(texcoord.x, texcoord.y, 0.0, 1.0);
// Transform into OpenGL-type [0,1]x[0,1] texture space. // Transform into OpenGL-type [0,1]x[0,1] texture space.
let texcoord = Point2::new( let texcoord = Point2::new(
texcoord.x / b.cur_texture_dim.0 as f64, texcoord.x / b.cur_texture_dim.0 as f64,
1.0 - texcoord.y / b.cur_texture_dim.1 as f64, // y-down to y-up 1.0 - texcoord.y / b.cur_texture_dim.1 as f64, // y-down to y-up
); );
let texcoord = b.gpu.texture_matrix * vec4(texcoord.x, texcoord.y, 0.0, 0.0);
b.next_vertex.texcoord = [texcoord.x as f32, texcoord.y as f32]; b.next_vertex.texcoord = [texcoord.x as f32, texcoord.y as f32];
} }
GpuCmd::Color { color } => { GpuCmd::Color { color } => {

View File

@ -1,12 +1,12 @@
use super::model_viewer::{ModelViewer, MaterialTextureBinding}; use super::model_viewer::{ModelViewer, MaterialTextureBinding};
use db::{Database, ModelId, AnimationId, PatternId}; use db::{Database, ModelId, AnimationId, PatternId, MatAnimId};
use connection::Connection; use connection::Connection;
use glium::Display; use glium::Display;
use glium::glutin::ElementState; use glium::glutin::ElementState;
use glium::{Frame, Surface}; use glium::{Frame, Surface};
use glium::glutin::VirtualKeyCode; use glium::glutin::VirtualKeyCode;
use nitro::{Model, Animation, Pattern}; use nitro::{Model, Animation, Pattern, MaterialAnimation};
use primitives::{Primitives, PolyType}; use primitives::{Primitives, PolyType, DynamicState};
use cgmath::{Matrix4, InnerSpace, One, Vector3, vec3, vec2}; use cgmath::{Matrix4, InnerSpace, One, Vector3, vec3, vec2};
use super::fps::FpsCounter; use super::fps::FpsCounter;
use super::{FRAMERATE, BG_COLOR}; use super::{FRAMERATE, BG_COLOR};
@ -18,14 +18,21 @@ pub struct Viewer {
/// ID of the viewed model. /// ID of the viewed model.
model_id: ModelId, model_id: ModelId,
/// The connection has a list of all the animations that can be applied to /// The connection has a list of all the animations that can be applied to
/// this model. This is the index of the current animation in that list (or /// this model. This is the index of the current animation in that list (or
/// None if no animation is selected). /// None if no animation is selected).
anim_idx: Option<usize>, anim_idx: Option<usize>,
/// Current animation frame. /// Current animation frame.
anim_frame: u16, anim_frame: u16,
pat_idx: Option<usize>, pat_idx: Option<usize>,
pat_anim_frame: u16, pat_anim_frame: u16,
mat_anim_idx: Option<usize>,
mat_anim_frame: u16,
// TODO: refactor this
/// When single-stepping is enabled, the user advanced the animation /// When single-stepping is enabled, the user advanced the animation
/// manually. When disabled, the animation advanced with time (ie. plays /// manually. When disabled, the animation advanced with time (ie. plays
/// normally). /// normally).
@ -56,6 +63,7 @@ pub static CONTROL_HELP: &'static str =
" OP Prev/Next Animation\n", " OP Prev/Next Animation\n",
" [] Single-step Animation\n", " [] Single-step Animation\n",
" KL Prev/Next Pattern Animation\n", " KL Prev/Next Pattern Animation\n",
" ;' Prev/Next Material Animation\n",
" Space Print Info\n", " Space Print Info\n",
); );
@ -75,6 +83,8 @@ impl Viewer {
anim_frame: 0, anim_frame: 0,
pat_idx: None, pat_idx: None,
pat_anim_frame: 0, pat_anim_frame: 0,
mat_anim_idx: None,
mat_anim_frame: 0,
single_stepping: false, single_stepping: false,
move_vector: vec3(0.0, 0.0, 0.0), move_vector: vec3(0.0, 0.0, 0.0),
speed_idx: DEFAULT_SPEED_IDX, speed_idx: DEFAULT_SPEED_IDX,
@ -108,6 +118,7 @@ impl Viewer {
self.next_frame(); self.next_frame();
} }
self.next_pattern_frame(display); self.next_pattern_frame(display);
self.next_mat_anim_frame();
self.time_acc -= FRAMERATE; self.time_acc -= FRAMERATE;
} }
} }
@ -187,6 +198,18 @@ impl Viewer {
self.change_pat_idx(display, new_pat_idx); self.change_pat_idx(display, new_pat_idx);
} }
// Next/prev material animation
Key::Apostrophe => {
let num = self.conn.models[self.model_id].mat_anims.len();
let new_idx = maybe_next(self.mat_anim_idx, 0..num);
self.change_mat_anim_idx(new_idx);
}
Key::Semicolon => {
let num = self.conn.models[self.model_id].mat_anims.len();
let new_idx = maybe_prev(self.mat_anim_idx, 0..num);
self.change_mat_anim_idx(new_idx);
}
// Speed up/down // Speed up/down
Key::LShift => { Key::LShift => {
if self.speed_idx != SPEEDS.len() - 1 { if self.speed_idx != SPEEDS.len() - 1 {
@ -252,6 +275,18 @@ impl Viewer {
} else { } else {
write!(s, "No Pattern === ").unwrap() write!(s, "No Pattern === ").unwrap()
} }
if let Some(mat_anim_id) = self.mat_anim_id() {
let mat_anim = self.cur_mat_anim().unwrap();
write!(s, "{anim_name}[{anim_id}/{num_anims}] ({cur_frame}/{num_frames}) === ",
anim_name = mat_anim.name,
anim_id = mat_anim_id,
num_anims = self.db.mat_anims.len(),
cur_frame = self.mat_anim_frame,
num_frames = mat_anim.num_frames,
).unwrap()
} else {
write!(s, "No Material Animation === ").unwrap()
}
write!(s, "{:5.2}fps", self.fps_counter.fps()).unwrap(); write!(s, "{:5.2}fps", self.fps_counter.fps()).unwrap();
} }
@ -287,6 +322,18 @@ impl Viewer {
println!("No Pattern Animation Playing") println!("No Pattern Animation Playing")
} }
if let Some(mat_anim_id) = self.mat_anim_id() {
let mat_anim = self.cur_mat_anim().unwrap();
println!("Material Animation: {:?} [{}/{}]",
mat_anim.name,
mat_anim_id,
self.db.mat_anims.len(),
);
println!("Found in file: {}", self.db.file_paths[self.db.mat_anims_found_in[mat_anim_id]].display());
} else {
println!("No Material Animation Playing")
}
println!(); println!();
} }
@ -299,9 +346,21 @@ impl Viewer {
self.stop_animations(); self.stop_animations();
self.model_id = model_id; self.model_id = model_id;
let objects = self.cur_model().objects.iter() let objects = self.cur_model().objects.iter()
.map(|ob| ob.matrix) .map(|ob| ob.matrix)
.collect::<Vec<Matrix4<f64>>>(); .collect::<Vec<Matrix4<f64>>>();
let uv_mats = self.cur_model().materials.iter()
.map(|mat| {
if mat.params.texcoord_transform_mode() == 1 {
mat.texture_mat
} else {
Matrix4::one()
}
})
.collect::<Vec<Matrix4<f64>>>();
let state = DynamicState { objects: &objects, uv_mats: &uv_mats };
let material_map = self.conn.models[self.model_id].materials.iter().map(|mat_conn| { let material_map = self.conn.models[self.model_id].materials.iter().map(|mat_conn| {
match mat_conn.image_id() { match mat_conn.image_id() {
Ok(Some(image_id)) => MaterialTextureBinding::ImageId(image_id), Ok(Some(image_id)) => MaterialTextureBinding::ImageId(image_id),
@ -309,7 +368,7 @@ impl Viewer {
Err(_) => MaterialTextureBinding::Missing, Err(_) => MaterialTextureBinding::Missing,
} }
}).collect(); }).collect();
let prims = Primitives::build(self.cur_model(), PolyType::Tris, &objects); let prims = Primitives::build(self.cur_model(), PolyType::Tris, state);
self.model_viewer.change_model(display, &self.db, prims, material_map); self.model_viewer.change_model(display, &self.db, prims, material_map);
} }
@ -335,7 +394,17 @@ impl Viewer {
.collect::<Vec<Matrix4<f64>>>() .collect::<Vec<Matrix4<f64>>>()
} }
}; };
let prims = Primitives::build(self.cur_model(), PolyType::Tris, &objects); let uv_mats = self.cur_model().materials.iter()
.map(|mat| {
if mat.params.texcoord_transform_mode() == 1 {
mat.texture_mat
} else {
Matrix4::one()
}
})
.collect::<Vec<Matrix4<f64>>>();
let state = DynamicState { objects: &objects, uv_mats: &uv_mats };
let prims = Primitives::build(self.cur_model(), PolyType::Tris, state);
self.model_viewer.update_vertices(&prims.vertices); self.model_viewer.update_vertices(&prims.vertices);
} }
@ -436,6 +505,16 @@ impl Viewer {
self.update_materials(display); self.update_materials(display);
} }
fn change_mat_anim_idx(&mut self, mat_anim_idx: Option<usize>) {
if self.mat_anim_idx == mat_anim_idx {
return;
}
self.mat_anim_idx = mat_anim_idx;
self.mat_anim_frame = 0;
self.update_vertices();
}
pub fn next_pattern_frame(&mut self, display: &Display) { pub fn next_pattern_frame(&mut self, display: &Display) {
let num_frames = self.cur_pattern().map(|pat| pat.num_frames); let num_frames = self.cur_pattern().map(|pat| pat.num_frames);
if let Some(num_frames) = num_frames { if let Some(num_frames) = num_frames {
@ -445,6 +524,15 @@ impl Viewer {
} }
} }
pub fn next_mat_anim_frame(&mut self) {
let num_frames = self.cur_mat_anim().map(|anim| anim.num_frames);
if let Some(num_frames) = num_frames {
self.mat_anim_frame += 1;
self.mat_anim_frame %= num_frames;
self.update_vertices();
}
}
pub fn set_aspect_ratio(&mut self, aspect_ratio: f64) { pub fn set_aspect_ratio(&mut self, aspect_ratio: f64) {
self.model_viewer.aspect_ratio = aspect_ratio as f32; self.model_viewer.aspect_ratio = aspect_ratio as f32;
} }
@ -461,6 +549,10 @@ impl Viewer {
Some(&self.db.patterns[self.pattern_id()?]) Some(&self.db.patterns[self.pattern_id()?])
} }
fn cur_mat_anim(&self) -> Option<&MaterialAnimation> {
Some(&self.db.mat_anims[self.mat_anim_id()?])
}
fn animation_id(&self) -> Option<AnimationId> { fn animation_id(&self) -> Option<AnimationId> {
let idx = self.anim_idx?; let idx = self.anim_idx?;
Some(self.conn.models[self.model_id].animations[idx]) Some(self.conn.models[self.model_id].animations[idx])
@ -471,6 +563,11 @@ impl Viewer {
Some(self.conn.models[self.model_id].patterns[idx].pattern_id) Some(self.conn.models[self.model_id].patterns[idx].pattern_id)
} }
fn mat_anim_id(&self) -> Option<MatAnimId> {
let idx = self.mat_anim_idx?;
Some(self.conn.models[self.model_id].mat_anims[idx].mat_anim_id)
}
pub fn speed(&self) -> f32 { pub fn speed(&self) -> f32 {
SPEEDS[self.speed_idx] SPEEDS[self.speed_idx]
} }