vrust 0.0.1

VRust game engine
use std::default::Default;
use std::sync::Arc;
use std::mem::{size_of, transmute};
use super::super::core::application::ApplicationTrait;
use super::super::math::matrix::Mat4x4;
use super::super::math::vector::Vec3;
use super::super::system::file::File;
use super::super::util::cell::DebugCell;
use super::buffer::{SceneDynamics, UniformBuffer};
use super::engine::RenderEngine;
use super::model::UniformData as MdlUniData;
use super::pipeline::Pipeline;
use super::scene::UniformData as ScnUniData;
use super::shader;
use super::shader::{read_id, Id as ShaderId};
use super::texture::Texture;

pub const FLOAT_SIZE: u64 = 4;
pub const POSITION_ELEMENT: u64 = FLOAT_SIZE * 3;
pub const NORMAL_ELEMENT: u64 = FLOAT_SIZE * 3;
pub const UV_ELEMENT: u64 = FLOAT_SIZE * 2;
pub const POSITION_VERTEX_SIZE: u64 = POSITION_ELEMENT;
pub const POSITION_NORMAL_VERTEX_SIZE: u64 = POSITION_ELEMENT + NORMAL_ELEMENT;
pub const POSITION_UV_VERTEX_SIZE: u64 = POSITION_ELEMENT + UV_ELEMENT;
pub const POSITION_NORMAL_UV_VERTEX_SIZE: u64 = POSITION_ELEMENT + NORMAL_ELEMENT + UV_ELEMENT;

pub trait Material {
    fn update_uniform(&mut self, sud: &ScnUniData, mud: &MdlUniData, frame_index: usize);
    fn get_pipeline(&self) -> &Arc<DebugCell<Pipeline>>;
    fn get_vertex_size(&self) -> usize;
    fn init_uniforms(
        &mut self, scene_dynamics: &Vec<Arc<DebugCell<SceneDynamics>>>);
}

struct Base {
    uniform_size: usize,
    uniforms: Vec<Arc<DebugCell<UniformBuffer>>>,
    pipeline: Arc<DebugCell<Pipeline>>,
}

impl Base {
    fn new<CoreApp>(
        uniform_size: usize,
        shader_id: ShaderId,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        Base {
            uniform_size: uniform_size,
            uniforms: Vec::new(),
            pipeline: engine.pipeline_manager.as_mut().unwrap().borrow_mut().get(shader_id),
        }
    }

    fn init_uniforms(
        &mut self, 
        scene_dynamics: &Vec<Arc<DebugCell<SceneDynamics>>>) {
        self.uniforms.clear();
        for sd in scene_dynamics {
            self.uniforms.push(sd.borrow_mut().create_uniform(self.uniform_size));
        }
    }
}

pub struct DirectionalTexturedSpeculatedNocubeFullshadowOpaque {
    texture: Arc<DebugCell<Texture>>,
    base: Base,
    uniform_data: DirectionalTexturedSpeculatedNocubeFullshadowOpaqueUniform,
}

#[repr(C)]
#[derive(Default)]
pub struct DirectionalTexturedSpeculatedNocubeFullshadowOpaqueUniform {
    pub mvp: Mat4x4<f32>,
    pub transform: Mat4x4<f32>,
    pub eye_loc: Vec3<f32>,
    pub sun_dir: Vec3<f32>,
    pub spec_color: Vec3<f32>,
    pub spec_intensity: f32,
}

impl DirectionalTexturedSpeculatedNocubeFullshadowOpaque {
    pub fn new<CoreApp>(
        file: &Arc<DebugCell<File>>,
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        let texture_id = file.borrow_mut().read_id();
        let offset = file.borrow_mut().tell();
        let texture = engine.os_app.asset_manager.get_texture(texture_id);
        file.borrow_mut().goto(offset);
        let speculation_color = Vec3::new_from_file(file);
        let speculation_intensity = file.borrow_mut().read_type();
        #[cfg(material_debug)]
        {
            logi!("speculation_color: {:?}", speculation_color);
            logi!("speculation_intensity: {}", speculation_intensity);
        }
        let mut uni = DirectionalTexturedSpeculatedNocubeFullshadowOpaqueUniform::default();
        uni.spec_color = speculation_color;
        uni.spec_intensity = speculation_intensity;
        DirectionalTexturedSpeculatedNocubeFullshadowOpaque {
            texture: texture,
            base: Base::new(
                size_of::<DirectionalTexturedSpeculatedNocubeFullshadowOpaqueUniform>(),
                shader::DIRECTIONAL_TEXTURED_SPECULATED_NOCUBE_FULLSHADOW_OPAQUE_ID,
                engine
            ),
            uniform_data: DirectionalTexturedSpeculatedNocubeFullshadowOpaqueUniform::default(),
        }
    }
}

impl Material for DirectionalTexturedSpeculatedNocubeFullshadowOpaque {
    fn update_uniform(&mut self, sud: &ScnUniData, mud: &MdlUniData, frame_index: usize) {
        self.uniform_data.mvp = mud.mvp;
        self.uniform_data.transform = mud.m;
        self.uniform_data.eye_loc = sud.eye_loc;
        self.uniform_data.sun_dir = sud.sun_dir;
        self.base.uniforms[frame_index].borrow_mut().upload(
            unsafe { transmute(&self.uniform_data) });
    }

    fn get_pipeline(&self) -> &Arc<DebugCell<Pipeline>> {
        &self.base.pipeline
    }

    fn get_vertex_size(&self) -> usize {
        POSITION_NORMAL_UV_VERTEX_SIZE as usize
    }

    fn init_uniforms(
        &mut self, 
        scene_dynamics: &Vec<Arc<DebugCell<SceneDynamics>>>) {
        self.base.init_uniforms(scene_dynamics);
    }
}

#[repr(C)]
#[derive(Default)]
pub struct WhiteUniform {
    pub mvp: Mat4x4<f32>,
}

pub struct White {
    uniform_data: WhiteUniform,
    base: Base,
}

impl White {
    pub fn new<CoreApp>(
        engine: &mut RenderEngine<CoreApp>
    ) -> Self where CoreApp: ApplicationTrait {
        White { 
            uniform_data: WhiteUniform::default(),
            base: Base::new(
                size_of::<WhiteUniform>(),
                shader::WHITE_ID,
                engine),
        }
    }
}

impl Material for White {
    fn update_uniform(&mut self, _sud: &ScnUniData, mud: &MdlUniData, frame_index: usize) {
        self.uniform_data.mvp = mud.mvp;
        self.base.uniforms[frame_index].borrow_mut().upload(
            unsafe { transmute(&self.uniform_data) });
    }

    fn get_pipeline(&self) -> &Arc<DebugCell<Pipeline>> {
        &self.base.pipeline
    }

    fn get_vertex_size(&self) -> usize {
        POSITION_VERTEX_SIZE as usize
    }

    fn init_uniforms(
        &mut self, 
        scene_dynamics: &Vec<Arc<DebugCell<SceneDynamics>>>) {
        self.base.init_uniforms(scene_dynamics);
    }
}

pub fn read_material<CoreApp>(
    file: &Arc<DebugCell<File>>,
    engine: &mut RenderEngine<CoreApp>
) -> Arc<DebugCell<Material>> 
where CoreApp: ApplicationTrait {
    let shader_id = read_id(file);
    return match shader_id {
        shader::WHITE_ID => {
            logf!("This shader must not be send to material");
        }
        shader::DIRECTIONAL_TEXTURED_SPECULATED_NOCUBE_FULLSHADOW_OPAQUE_ID => Arc::new(DebugCell::new(
            DirectionalTexturedSpeculatedNocubeFullshadowOpaque::new(
                file,
                engine,
            ),
        )),
        _ => {
            logf!("Unexpected shader id!");
        }
    };
}