vrust 0.0.1

VRust game engine
pub mod manager;

use std::sync::Arc;
use std::default::Default;
use super::super::core::application::ApplicationTrait;
use super::super::math::matrix::Mat4x4;
use super::super::system::file::File;
use super::super::util::cell::DebugCell;
use super::command::buffer::Buffer as CmdBuff;
use super::engine::RenderEngine;
use super::material::Material;
use super::mesh::{Mesh, OccMesh};
use super::scene::UniformData as ScUniData;

#[derive(Default)]
pub struct UniformData {
    pub mvp: Mat4x4<f32>,
    pub m: Mat4x4<f32>,
}

pub enum Dynamism {
    Dynamic,
    Static,
}

pub trait Model {
    fn parent_update_uniform(&mut self, _sud: &ScUniData, _frame_index: usize) {
        logf!("This struct does not implement parent_update_uniform because it is not parent.");
    }
    fn child_update_uniform(&mut self, _sud: &ScUniData, _pud: &UniformData, _frame_index: usize) {
        logf!("This struct does not implement child_update_uniform because it is not child.");
    }
    fn get_dynamism(&self) -> Dynamism;
    fn rec_occ(&mut self, cmd_buff: &mut CmdBuff, mat: &Arc<DebugCell<Material>>, frame_index: usize);
    fn is_static(&self) -> bool {
        return false;
    }
    fn to_static(&self) -> &StaticModel {
        logf!("This struct is not static.");
    }
    fn is_dynamic(&self) -> bool {
        return false;
    }
    fn to_dynamic(&self) -> &DynamicModel {
        logf!("This struct is not dynamic.");
    }
}

pub struct StaticModel {
    pub draw_mesh: Mesh,
    pub children: Vec<Box<Model>>,
}

impl StaticModel {
    pub fn new<CoreApp>(
        file: &Arc<DebugCell<File>>,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        let mesh = Mesh::new(file, engine);
        let children_count: u64 = file.borrow_mut().read_type();
        let mut children = Vec::new();
        for _ in 0..children_count {
            children.push(read_boxed_model(file, engine));
        }
        StaticModel {
            draw_mesh: mesh,
            children: children,
        }
    }
}

impl Model for StaticModel {
    fn child_update_uniform(&mut self, sud: &ScUniData, pud: &UniformData, frame_index: usize) {
        let count = self.children.len();
        for i in 0..count {
            self.children[i].child_update_uniform(sud, pud, frame_index);
        }
        self.draw_mesh.update_uniform(sud, pud, frame_index);
    }

    fn get_dynamism(&self) -> Dynamism {
        Dynamism::Static
    }

    fn rec_occ(&mut self, cmd_buff: &mut CmdBuff, mat: &Arc<DebugCell<Material>>, frame_index: usize) {
        for m in &mut self.children {
            m.rec_occ(cmd_buff, mat, frame_index);
        }
    }

    fn is_static(&self) -> bool {
        true
    }
    
    fn to_static(&self) -> &StaticModel {
        self
    }
}

pub struct RootStaticModel {
    pub occ_mesh: OccMesh,
    pub children: Vec<Box<Model>>,
    pub ud: UniformData,
}

impl RootStaticModel {
    pub fn new<CoreApp>(
        file: &Arc<DebugCell<File>>,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        let mesh = OccMesh::new(file, engine);
        let children_count = file.borrow_mut().read_count();
        let mut children = Vec::new();
        for _ in 0..children_count {
            children.push(read_boxed_model(file, engine));
        }
        RootStaticModel {
            occ_mesh: mesh,
            children: children,
            ud: UniformData::default(),
        }
    }
}

impl Model for RootStaticModel {
    fn parent_update_uniform(&mut self, sud: &ScUniData, frame_index: usize) {
        self.ud.mvp = sud.vp;
        let count = self.children.len();
        for i in 0..count {
            self.children[i].child_update_uniform(sud, &self.ud, frame_index);
        }
    }

    fn child_update_uniform(&mut self, sud: &ScUniData, pud: &UniformData, frame_index: usize) {
        let count = self.children.len();
        for i in 0..count {
            self.children[i].child_update_uniform(sud, pud, frame_index);
        }
    }

    fn get_dynamism(&self) -> Dynamism {
        Dynamism::Static
    }

    fn rec_occ(&mut self, cmd_buff: &mut CmdBuff, mat: &Arc<DebugCell<Material>>, frame_index: usize) {
        // todo;
        for m in &mut self.children {
            m.rec_occ(cmd_buff, mat, frame_index);
        }
    }
}

pub struct DynamicModel {
    pub ud: UniformData,
    pub occ_mesh: OccMesh,
    pub children: Vec<Box<Model>>,
}

impl DynamicModel {
    pub fn new<CoreApp>(
        file: &Arc<DebugCell<File>>,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        let m = Mat4x4::new_from_file(file);
        let mesh = OccMesh::new(file, engine);
        let children_count: u64 = file.borrow_mut().read_type();
        let mut children = Vec::new();
        for _ in 0..children_count {
            children.push(read_boxed_model(file, engine));
        }
        let mut ud = UniformData::default();
        ud.m = m;
        DynamicModel {
            ud: ud,
            occ_mesh: mesh,
            children: children,
        }
    }
}

impl Model for DynamicModel {
    fn parent_update_uniform(&mut self, sud: &ScUniData, frame_index: usize) {
        self.ud.mvp = &sud.vp * &self.ud.m;
        let count = self.children.len();
        for i in 0..count {
            self.children[i].child_update_uniform(sud, &self.ud, frame_index);
        }
    }

    fn child_update_uniform(&mut self, sud: &ScUniData, _pud: &UniformData, frame_index: usize) {
        self.parent_update_uniform(sud, frame_index);
    }

    fn get_dynamism(&self) -> Dynamism {
        Dynamism::Dynamic
    }

    fn rec_occ(&mut self, cmd_buff: &mut CmdBuff, mat: &Arc<DebugCell<Material>>, frame_index: usize) {
        // todo;
        for m in &mut self.children {
            m.rec_occ(cmd_buff, mat, frame_index);
        }
    }
}

pub struct CopyModel {
    pub ud: UniformData,
    pub sm: Arc<DebugCell<Model>>,
}

impl CopyModel {
    pub fn new<CoreApp>(
        file: &Arc<DebugCell<File>>,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        let m = Mat4x4::new_from_file(file);
        let id = file.borrow_mut().read_id();
        let offset = file.borrow_mut().tell();
        let model_manager = engine.os_app.asset_manager.model_manager.clone();
        let sm = unsafe { model_manager.untraced_mut_ref().get(id, engine) };
        file.borrow_mut().goto(offset);
        let mut ud = UniformData::default();
        ud.m = m;
        CopyModel {
            ud: ud, 
            sm: sm 
        }
    }
}

impl Model for CopyModel {
    fn parent_update_uniform(&mut self, sud: &ScUniData, frame_index: usize) {
        self.ud.mvp = &sud.vp * &self.ud.m;
        self.sm.borrow_mut().child_update_uniform(sud, &self.ud, frame_index);
    }

    fn child_update_uniform(&mut self, sud: &ScUniData, _pud: &UniformData, frame_index: usize) {
        self.parent_update_uniform(sud, frame_index);
    }

    fn get_dynamism(&self) -> Dynamism {
        Dynamism::Dynamic
    }

    fn rec_occ(&mut self, cmd_buff: &mut CmdBuff, mat: &Arc<DebugCell<Material>>, frame_index: usize) {
        self.sm.borrow_mut().rec_occ(cmd_buff, mat, frame_index);
    }
}

pub fn read_model<CoreApp>(
    file: &Arc<DebugCell<File>>,
    engine: &mut RenderEngine<CoreApp>
) -> Arc<DebugCell<Model>>
where CoreApp: ApplicationTrait {
    return if file.borrow_mut().read_bool() {
        Arc::new(DebugCell::new(CopyModel::new(file, engine)))
    } else if file.borrow_mut().read_bool() {
        Arc::new(DebugCell::new(DynamicModel::new(file, engine)))
    } else {
        Arc::new(DebugCell::new(RootStaticModel::new(file, engine)))
    };
}

fn read_boxed_model<CoreApp>(
    file: &Arc<DebugCell<File>>,
    engine: &mut RenderEngine<CoreApp>
) -> Box<Model> 
where CoreApp: ApplicationTrait {
    return if file.borrow_mut().read_bool() {
        Box::new(CopyModel::new(file, engine))
    } else if file.borrow_mut().read_bool() {
        Box::new(DynamicModel::new(file, engine))
    } else {
        Box::new(StaticModel::new(file, engine))
    };
}