mirror of
https://github.com/scurest/apicula.git
synced 2025-06-18 22:55:33 -04:00
Wire up the viewer for material animations
Currently they don't do anything though.
This commit is contained in:
parent
60ed16ad2d
commit
af0d02da34
@ -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()
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 } => {
|
||||
|
@ -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]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user