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.
use clap::ArgMatches;
use db::{Database, AnimationId, TextureId, PaletteId, ModelId, PatternId};
use db::{Database, AnimationId, TextureId, PaletteId, ModelId, PatternId, MatAnimId};
use errors::Result;
/// 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
/// them).
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.
@ -118,7 +120,8 @@ impl Connection {
let animations = find_applicable_animations(db, model_id, options);
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();
if missing_textures {
@ -285,3 +288,20 @@ fn find_applicable_patterns(db: &Database, model_id: ModelId) -> Vec<PatternConn
})
}).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 db::{Database, ModelId};
use skeleton::{Skeleton, Transform, SMatrix};
use primitives::{self, Primitives};
use primitives::{self, Primitives, DynamicState};
use nitro::Model;
use petgraph::{Direction};
use petgraph::graph::NodeIndex;
@ -42,7 +42,17 @@ pub fn write(
let objects = &model.objects.iter()
.map(|o| make_invertible(&o.matrix))
.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 ctx = Ctx { model_id, model, db, conn, image_namer, objects, prims, skel };

View File

@ -6,10 +6,10 @@ mod primitive;
use nitro::Model;
use db::{Database, ModelId};
use connection::Connection;
use primitives::{Primitives, PolyType};
use primitives::{Primitives, PolyType, DynamicState};
use skeleton::{Skeleton, Transform, SMatrix};
use super::image_namer::ImageNamer;
use cgmath::Matrix4;
use cgmath::{Matrix4, One};
use json::JsonValue;
use self::gltf::{GlTF, Buffer, ByteVec, VecExt};
use self::object_trs::ObjectTRSes;
@ -45,7 +45,17 @@ pub fn to_gltf(
let objects = rest_trses.objects.iter()
.map(Matrix4::from)
.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 skel = &Skeleton::build(model, &objects);

View File

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

View File

@ -31,6 +31,15 @@ pub enum PolyType {
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
/// of GPU commands) while in a particular GPU state (matrix stack, bound material,
/// etc.).
@ -78,8 +87,8 @@ impl Default for Vertex {
implement_vertex!(Vertex, position, texcoord, color, normal);
impl Primitives {
pub fn build(model: &Model, poly_type: PolyType, objects: &[Matrix4<f64>]) -> Primitives {
let mut b = Builder::new(model, poly_type, objects);
pub fn build(model: &Model, poly_type: PolyType, state: DynamicState) -> Primitives {
let mut b = Builder::new(model, poly_type, state);
use nitro::render_cmds::Op;
for op in &model.render_ops {
match *op {
@ -125,7 +134,7 @@ impl GpuState {
struct Builder<'a, 'b> {
model: &'a Model,
objects: &'b [Matrix4<f64>],
state: DynamicState<'b>,
poly_type: PolyType,
gpu: GpuState,
@ -140,14 +149,13 @@ struct Builder<'a, 'b> {
cur_draw_call: DrawCall,
next_vertex: Vertex,
}
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 {
model,
objects,
state,
poly_type,
gpu: GpuState::new(),
vertices: vec![],
@ -174,6 +182,9 @@ impl<'a, 'b> Builder<'a, 'b> {
let vert_len = self.vertices.len() as u16;
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 {
vertex_range: vert_len..vert_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) {
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]) {
@ -369,12 +380,14 @@ fn run_gpu_cmds(b: &mut Builder, commands: &[u8]) {
GpuCmd::TexCoord { texcoord } => {
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.
let texcoord = Point2::new(
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
);
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];
}
GpuCmd::Color { color } => {

View File

@ -1,12 +1,12 @@
use super::model_viewer::{ModelViewer, MaterialTextureBinding};
use db::{Database, ModelId, AnimationId, PatternId};
use db::{Database, ModelId, AnimationId, PatternId, MatAnimId};
use connection::Connection;
use glium::Display;
use glium::glutin::ElementState;
use glium::{Frame, Surface};
use glium::glutin::VirtualKeyCode;
use nitro::{Model, Animation, Pattern};
use primitives::{Primitives, PolyType};
use nitro::{Model, Animation, Pattern, MaterialAnimation};
use primitives::{Primitives, PolyType, DynamicState};
use cgmath::{Matrix4, InnerSpace, One, Vector3, vec3, vec2};
use super::fps::FpsCounter;
use super::{FRAMERATE, BG_COLOR};
@ -18,14 +18,21 @@ pub struct Viewer {
/// ID of the viewed model.
model_id: ModelId,
/// 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
/// None if no animation is selected).
anim_idx: Option<usize>,
/// Current animation frame.
anim_frame: u16,
pat_idx: Option<usize>,
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
/// manually. When disabled, the animation advanced with time (ie. plays
/// normally).
@ -56,6 +63,7 @@ pub static CONTROL_HELP: &'static str =
" OP Prev/Next Animation\n",
" [] Single-step Animation\n",
" KL Prev/Next Pattern Animation\n",
" ;' Prev/Next Material Animation\n",
" Space Print Info\n",
);
@ -75,6 +83,8 @@ impl Viewer {
anim_frame: 0,
pat_idx: None,
pat_anim_frame: 0,
mat_anim_idx: None,
mat_anim_frame: 0,
single_stepping: false,
move_vector: vec3(0.0, 0.0, 0.0),
speed_idx: DEFAULT_SPEED_IDX,
@ -108,6 +118,7 @@ impl Viewer {
self.next_frame();
}
self.next_pattern_frame(display);
self.next_mat_anim_frame();
self.time_acc -= FRAMERATE;
}
}
@ -187,6 +198,18 @@ impl Viewer {
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
Key::LShift => {
if self.speed_idx != SPEEDS.len() - 1 {
@ -252,6 +275,18 @@ impl Viewer {
} else {
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();
}
@ -287,6 +322,18 @@ impl Viewer {
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!();
}
@ -299,9 +346,21 @@ impl Viewer {
self.stop_animations();
self.model_id = model_id;
let objects = self.cur_model().objects.iter()
.map(|ob| ob.matrix)
.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| {
match mat_conn.image_id() {
Ok(Some(image_id)) => MaterialTextureBinding::ImageId(image_id),
@ -309,7 +368,7 @@ impl Viewer {
Err(_) => MaterialTextureBinding::Missing,
}
}).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);
}
@ -335,7 +394,17 @@ impl Viewer {
.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);
}
@ -436,6 +505,16 @@ impl Viewer {
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) {
let num_frames = self.cur_pattern().map(|pat| pat.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) {
self.model_viewer.aspect_ratio = aspect_ratio as f32;
}
@ -461,6 +549,10 @@ impl Viewer {
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> {
let idx = self.anim_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)
}
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 {
SPEEDS[self.speed_idx]
}