use rinblender;
use rinecs::{World, Entity, Read, ReadNot, ReadHierarchical, Write};
use rin::util::{Error, Result};
use rin::graphics::{self, IndexT, Mesh};
use rin::color::consts::*;
use crate::{
components::{Name, Visible, Ty, SourcePath},
geometry::{Submesh, Geometry, GeometryRef, VertexGroups},
transformation::{Transformation, DynamicTransformation, Bone, PreviousTransformation},
physics::components::RigidBody,
skinning::components::*,
light::{Light, DirectionalLight, SpotLight, PointLight, shadow},
material::{
self,
StandardMaterialBuilder, LambertMaterialBuilder,
ClothMaterialBuilder, ClothSubsurfaceMaterialBuilder,
AnisotropicMaterialBuilder, ClearcoatMaterialBuilder,
SubsurfaceMaterialBuilder, MaterialRef, AlphaType,
texture::{TextureRef, TextureSampler},
basic_material,
},
scene::CreationContext,
};
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
use rin::gl;
use std::collections::HashMap;
use std::path::Path;
use na::{ToVec, one};
use angle::{Angle, Rad};
use self::components::*;
use rinblender::idtree;
use std::collections::{VecDeque, HashSet};
use rin::color::*;
use std::mem;
use crate::Builder;
pub use self::actions::{
Action, ActionBlend, ActionMix, ActionOffset, ActionExt,
ActionClock, GameClock, ActionController, ActionControllerBuilder, ActionClockExt, LoopClock
};
pub use self::components::{
Vertex,
CharacterEntity,
SceneIndex,
ShapeKey,
};
use crate::Scene;
mod components;
mod textures;
mod actions;
mod animation_system;
const SKELETON_MATERIAL: &'static str = "__BLENDER_SKELETON_MATERIAL";
const MISSING_TEXTURE: &'static str = "__BLENDER_MISSING_TEXTURE";
pub struct Bundle;
impl crate::Bundle for Bundle{
fn register(world: &mut Scene){
world.register_component::<components::Rotation>();
world.register_component::<components::SceneIndex>();
world.register_component::<components::ShapeKey>();
world.register_component::<ActionClock>();
world.register_component::<Action>();
world.register_vertex_type::<Vertex>();
world.register_vertex_type::<rin::graphics::Vertex3D>();
}
fn setup(self, world: &mut Scene){
crate::transformation::update_all(world.entities(), world.resources());
world.add_system_with_name(animation_system::animation_update, "animation update");
world.add_barrier();
world.add_system_with_name(animation_system::animation_skeletons, "animation skeletons");
world.add_barrier();
world.add_system_with_name(animation_system::animation_scenes, "animation scenes");
world.add_barrier();
world.add_system_with_name(animation_system::animation_individual_entities, "animation entities");
}
}
pub struct Blender<'a, C: CreationContext + 'a = World>{
parent: Option<Entity>,
visible: bool,
transformation: Option<rin::graphics::Node>,
alpha_type: AlphaType,
world: &'a mut C,
}
impl<'a, C: CreationContext> crate::Builder<'a, C> for Blender<'a, C>{
type Settings = ();
fn new(world: &'a mut C, _:()) -> Blender<'a, C>{
Blender{
parent: None,
visible: true,
transformation: None,
alpha_type: AlphaType::None,
world,
}
}
}
impl<'a, C: CreationContext> Blender<'a, C>{
pub fn alpha_type(&mut self, alpha_type: AlphaType) -> &mut Blender<'a, C>{
self.alpha_type = alpha_type;
self
}
pub fn transformation<T: Into<rin::graphics::Node>>(&mut self, trafo: T) -> &mut Blender<'a, C>{
self.transformation = Some(trafo.into());
self
}
pub fn invisible(&mut self) -> &mut Blender<'a, C>{
self.visible = false;
self
}
pub fn parent(&mut self, parent: Entity) -> &mut Blender<'a, C>{
self.parent = Some(parent);
self
}
pub fn from_scene_data<P: AsRef<Path>>(&mut self, scene: &rinblender::SceneData, path: P, name: &str) -> Result<(rinecs::Entity, SceneIndex)>{
let group = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty);
let transformation = self.transformation.unwrap_or_else(|| one() );
let group = if let Some(parent) = self.parent.as_ref(){
group.add_child(parent, Transformation::from_node(transformation.clone()))
}else{
group.add(Transformation::from_node(transformation.clone()))
};
let group = group.add(Visible::new(true))
.build();
let index = self.load(name, path, &scene, Some(group));
if let Ok(index) = index.as_ref() {
self.world.add_component_to(&group, index.clone());
}
index.map(|scene_index| (group, scene_index))
}
pub fn load_scene<P: AsRef<Path>>(&mut self, path: P, name: &str) -> Result<(rinecs::Entity, rinblender::SceneData, SceneIndex)>{
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::load(&file);
self.from_scene_data(&scene, path, name)
.map(|(entity,scene_index)| (entity, scene, scene_index))
}
pub fn character_from_scene_data<P: AsRef<Path>>(&mut self, scene: &rinblender::SceneData, path: P, name: &str) -> Result<(CharacterEntity, SceneIndex)>{
let character = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Visible::new(true))
.add(Ty::Empty);
let transformation = self.transformation.unwrap_or_else(|| one() );
let character = if let Some(parent) = self.parent.as_ref(){
character.add_child(parent, Transformation::from_node(transformation.clone()))
}else{
character.add(Transformation::from_node(transformation.clone()))
};
let character = character.add(PreviousTransformation(transformation))
.add(DynamicTransformation)
.build();
self.load(name, path, scene, Some(character))
.map(|scene_index|{
self.world.add_component_to(&character, scene_index.clone());
let character = CharacterEntity{
character: character.clone(),
skeleton: scene_index.skeletons.first().map(|s| s.clone())
};
(character, scene_index)
})
}
pub fn load_character<P: AsRef<Path>>(&mut self, path: P, name: &str) -> Result<(CharacterEntity, rinblender::SceneData, SceneIndex)>{
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::load(&file);
self.character_from_scene_data(&scene, path, name)
.map(|(entity, scene_index)| (entity, scene, scene_index))
}
pub fn load_group<P: AsRef<Path>>(&mut self, path: P, name: &str, groups: &[&str]) -> Result<(rinecs::Entity, rinblender::SceneData, SceneIndex)> {
trace!("Loading {} from {}", name, path.as_ref().display());
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::load_groups(&file, groups)
.map_err(|err| Error::with_cause("Error loading blender groups", err))?;
let group = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty);
let transformation = self.transformation.unwrap_or_else(|| one() );
let group = if let Some(parent) = self.parent.as_ref(){
group.add_child(parent, Transformation::from_node(transformation.clone()))
}else{
group.add(Transformation::from_node(transformation.clone()))
};
let group = group.add(Visible::new(true))
.build();
let index = self.load(name, path, &scene, Some(group));
if let Ok(index) = index.as_ref() {
self.world.add_component_to(&group, index.clone());
}
index.map(|index| (group, scene, index))
}
fn load_meshes<'s, P: AsRef<Path>>(&mut self, scene: &'s rinblender::SceneData, path: P) -> HashMap<&'s str, Entity>{
let existing_geom_index = self.world.iter_for::<(
Entity,
Read<SourcePath>,
Read<Name>,
Read<Geometry<Vertex>>)>()
.map(|(e, path, name, _)| ((name.0.clone(), path.0.clone()), e))
.collect::<HashMap<_,_>>();
scene.trimeshes.iter().filter_map(|(name, trimesh)| {
if let Some(entity) = existing_geom_index.get(&(name.to_owned(), path.as_ref().to_owned())){
trace!("Reusing already existing mesh by name: {}", name);
Some((name.as_ref(), *entity))
}else{
trace!("Preloading mesh: {} with {} vertices and {} indices: {}Mb",
name,
trimesh.original_vertices().len(),
trimesh.original_indices().len(),
(trimesh.original_vertices().len() * mem::size_of::<Vertex>() +
trimesh.original_indices().len() * mem::size_of::<glin::IndexT>()) / 1024 / 1024);
let vertices = trimesh.original_vertices().iter()
.map(|v| Vertex::from((v, 1.)))
.collect();
let submeshes;
if trimesh.submeshes_indices().is_some() {
submeshes = trimesh.submeshes().filter_map(|submesh|{
let indices = submesh.indices.unwrap();
if indices.is_empty() {
None
}else{
Some(Submesh(indices.to_vec()))
}
}).collect::<Vec<_>>();
}else{
submeshes = trimesh.submeshes().scan(0, |count, submesh|{
if submesh.vertices.is_empty() {
Some(None)
}else{
let indices = (*count .. *count + submesh.vertices.len())
.map(|i| i as IndexT)
.collect();
*count += submesh.vertices.len();
Some(Some(Submesh(indices)))
}
}).filter_map(|s| s).collect();
}
let original_indices = trimesh.original_indices().to_vec();
let full_mesh = Mesh::new(vertices, original_indices, graphics::PrimitiveType::Triangles);
if !submeshes.is_empty() {
let entity = self.world.new_entity()
.add(Name(name.to_owned()))
.add(SourcePath(path.as_ref().to_owned()))
.add(Geometry::new(full_mesh))
.add(Ty::Mesh)
.add_slice(submeshes)
.build();
Some((name.as_ref(), entity))
}else{
trace!("Discarding empty mesh {}", name);
None
}
}
}).collect::<HashMap<_,_>>()
}
#[cfg(not(any(feature="gl", feature="gles", feature="webgl")))]
fn load_textures<'s, P: AsRef<Path>>(&mut self, scene: &'s rinblender::SceneData, path: P) -> HashMap<&'s str, TextureRef>{
unimplemented!()
}
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
fn load_textures<'s, P: AsRef<Path>>(&mut self, scene: &'s rinblender::SceneData, path: P) -> HashMap<&'s str, TextureRef>{
let missing_tex = self.world.find_named_texture(MISSING_TEXTURE).unwrap_or_else(||{
let data = MAGENTA.as_ref();
let format = glin::texture::LoadDataFormat{
target: gl::TEXTURE_2D,
internal_format: gl::RGB8,
width: 1,
height: 1,
format: gl::RGB,
ty: gl::UNSIGNED_BYTE,
levels: 1,
samples: 0,
};
self.world.register_texture_from_data_format(format, data)
.unwrap()
});
let is_used = |name: &str|{
scene.materials.values().find(|mat|{
mat.texture.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.normal_map.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.occlusion_map.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.metallic_roughness_map.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.emissive_map.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.anisotropy_map.as_ref().map(|t| t.data == name).unwrap_or(false) ||
mat.subsurface_map.as_ref().map(|t| t.data == name).unwrap_or(false)
}).is_some()
};
scene.images.iter().filter_map(|(name, data)|{
if is_used(name) {
let path_name = if name.starts_with("//"){
path.as_ref().to_str().unwrap().to_owned() + "," + name
}else{
name.clone()
};
let textureref = self.world.find_named_texture(&path_name).unwrap_or_else(||{
textures::load_image_data(&path_name, &data, self.world).unwrap_or(missing_tex)
});
Some((name.as_ref(), textureref))
}else{
None
}
}).collect()
}
#[cfg(not(any(feature="gl", feature="gles", feature="webgl")))]
fn load_materials<'s, P: AsRef<Path>>(&mut self,
scene: &'s rinblender::SceneData,
path: P,
textures: &'s HashMap<&'s str, TextureRef>) -> HashMap<&'s str, MaterialRef>
{
unimplemented!()
}
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
fn load_materials<'s, P: AsRef<Path>>(&mut self,
scene: &'s rinblender::SceneData,
path: P,
textures: &'s HashMap<&'s str, TextureRef>) -> HashMap<&'s str, MaterialRef>
{
let texture_sampler = |image: Option<&'s rinblender::Image>, world: &mut C|{
image.and_then(|img| {
let texture = if !img.modifiers.is_empty() {
let tex = rinblender::Texture{
image: img,
data: &scene.images[&img.data]
};
textures::load_texture(tex, world)
.or(textures.get(&img.data.as_ref()).cloned())
}else{
textures.get(&img.data.as_ref()).cloned()
}.or(textures.get(MISSING_TEXTURE).cloned());
texture.map(|texture| {
let sampler = textures::sampler_from_blender(img);
let sampler = world.register_sampler(sampler);
TextureSampler{
sampler: Some(sampler),
texture,
}
})
})
};
let skeleton_material = self.world.find_material(SKELETON_MATERIAL).unwrap_or_else(||{
let skeleton_material = basic_material::Builder::new()
.color(&rgba!(BLACK, 0.999))
.properties(vec![material::Property::DepthTest(false)])
.build();
self.world.register_material(SKELETON_MATERIAL, skeleton_material)
});
let scene_materials = scene.materials.iter().map(|(name, mat)| {
let path_name = path.as_ref().join(name).to_str().unwrap().to_owned();
self.world.find_material(&path_name).map(|mat| (name.as_ref(), mat)).unwrap_or_else(||{
let base_color_sampler = texture_sampler(mat.texture.as_ref(), self.world);
let normal_map_sampler = texture_sampler(mat.normal_map.as_ref(), self.world);
let occlusion_map_sampler = texture_sampler(mat.occlusion_map.as_ref(), self.world);
let metallic_roughness_map_sampler = texture_sampler(mat.metallic_roughness_map.as_ref(), self.world);
let emissive_map_sampler = texture_sampler(mat.emissive_map.as_ref(), self.world);
let subsurface_map_sampler = texture_sampler(mat.subsurface_map.as_ref(), self.world);
let anisotropy_map_sampler = texture_sampler(mat.anisotropy_map.as_ref(), self.world);
let base_color = rgba!(mat.albedo, mat.alpha);
match mat.ty{
rinblender::material::Type::Lambert => {
let material = LambertMaterialBuilder::default()
.color(base_color)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.metallic_roughness_map(metallic_roughness_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}
rinblender::material::Type::Standard => if let Some(subsurface_color) = mat.subsurface {
let material = SubsurfaceMaterialBuilder::default()
.color(base_color)
.metallic(mat.metallicness)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.reflectance(mat.specular.r.max(mat.specular.g).max(mat.specular.b))
.reflectance_tint(mat.specular_tint)
.subsurface_color(subsurface_color)
.thickness(mat.subsurface_blur)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.metallic_roughness_map(metallic_roughness_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}else{
let material = StandardMaterialBuilder::default()
.color(base_color)
.metallic(mat.metallicness)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.reflectance(mat.specular.r.max(mat.specular.g).max(mat.specular.b))
.reflectance_tint(mat.specular_tint)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.metallic_roughness_map(metallic_roughness_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}
rinblender::material::Type::Cloth => if let Some(subsurface_color) = mat.subsurface {
let material = ClothSubsurfaceMaterialBuilder::default()
.color(base_color)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.subsurface_color(subsurface_color)
.sheen_color(mat.specular)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}else{
let material = ClothMaterialBuilder::default()
.color(base_color)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.sheen_color(mat.specular)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}
rinblender::material::Type::Anisotropic => {
let material = AnisotropicMaterialBuilder::default()
.color(base_color)
.metallic(mat.metallicness)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.reflectance(mat.specular.r.max(mat.specular.g).max(mat.specular.b))
.reflectance_tint(mat.specular_tint)
.anisotropy(mat.anisotropy)
.anisotropic_rotation(Rad::pi() * mat.anisotropic_rotation)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.metallic_roughness_map(metallic_roughness_map_sampler)
.emissive_map(emissive_map_sampler)
.anisotropy_map(anisotropy_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}
rinblender::material::Type::Clearcoat => {
let material = ClearcoatMaterialBuilder::default()
.color(base_color)
.metallic(mat.metallicness)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(mat.doublesided)
.reflectance(mat.specular.r.max(mat.specular.g).max(mat.specular.b))
.reflectance_tint(mat.specular_tint)
.clearcoat(mat.clearcoat_factor)
.clearcoat_roughness(mat.clearcoat_roughness)
.texture(base_color_sampler)
.normal_map(normal_map_sampler)
.occlusion_map(occlusion_map_sampler)
.metallic_roughness_map(metallic_roughness_map_sampler)
.emissive_map(emissive_map_sampler)
.alpha_type(self.alpha_type)
.build();
(name.as_ref(), self.world.register_material(&path_name, material))
}
}
})
});
scene_materials
.chain(Some((SKELETON_MATERIAL, skeleton_material)))
.collect()
}
fn load<P: AsRef<Path>>(&mut self, name: &str, path: P, scene: &rinblender::SceneData, parent: Option<Entity>) -> Result<SceneIndex>{
trace!("Loading {} from {}", name, path.as_ref().display());
let meshes = self.load_meshes(&scene, path.as_ref());
let textures = self.load_textures(&scene, path.as_ref());
let materials = self.load_materials(&scene, path.as_ref(), &textures);
let proxies = scene.proxies.iter()
.map(|(proxy, proxied)| (name.to_owned() + "_" + proxy, name.to_owned() + "_" + proxied))
.collect::<HashMap<_,_>>();
let mut scene_index = SceneIndex{
skeletons: vec![],
poses: vec![],
models: vec![],
lights: vec![],
empties: vec![],
};
for root in scene.roots.iter(){
let (parent, ty) = build_enitity(*root,
&scene, &meshes, &materials,
self.world, parent, name);
match ty{
Ty::Armature => scene_index.skeletons.push(parent.clone()),
Ty::Model => scene_index.models.push(parent.clone()),
Ty::Light => scene_index.lights.push(parent.clone()),
Ty::Empty => scene_index.empties.push(parent.clone()),
_ => unreachable!()
}
parse_children(*root, &scene, &meshes, &materials,
self.world, parent, &mut scene_index, name);
}
if !scene_index.skeletons.is_empty(){
let model_skeleton = self.world.iter_for_entities::<(
Entity,
Read<AnimatedGeometry<Vertex>>,
Read<SkeletonName>,
Read<Name>), _>(scene_index.models.iter().cloned()).map(|(entity, _, skeleton_name, name)|{
trace!("Assigning skeleton {} to model {}", skeleton_name, name);
let (skeleton_entity, skeleton_name, num_bones) = self.world
.iter_for::<(Entity, Read<Skeleton>, Read<Name>, Read<BoneBase>)>()
.find(|&(_entity, _skeleton, name, _)| {
let found = name.0 == skeleton_name.0;
found || proxies.get(&skeleton_name.0)
.map(|proxy| *proxy == name.0)
.unwrap_or(false)
})
.map(|(e,_s,n, _b)| {
let num_bones = self.world
.iter_for::<Read<SkeletonRef>>()
.filter(|sk_ref| sk_ref.0.guid() == e.guid())
.count();
(e, n.0.clone(), num_bones)
}).expect("Couldn't find skeleton");
(entity, skeleton_entity, skeleton_name, num_bones)
}).collect::<Vec<_>>();
for (model, skeleton, skeleton_name, num_bones) in model_skeleton.into_iter(){
self.world.add_component_to(&model, SkeletonRef(skeleton));
let blender_name = &skeleton_name[name.len() + 1..];
if let rinblender::BlenderObject::Armature(skeleton) = scene.arena[scene.skeletons[blender_name]].data(){
self.world.add_component_to(&model, ArmatureCache::from(skeleton));
self.world.add_slice_component_to::<ArmatureMatrices, _>(&model, unsafe{ vec![mem::uninitialized(); num_bones] });
}else{
panic!("Trying to add non exisiting skeleton")
}
}
let iter = self.world.iter_for_entities::<(
Read<SkeletonRef>,
Read<VertexGroups>,
), _>(scene_index.models.iter().cloned());
for (skeleton_ref, vertex_groups) in iter{
for (mut bone,_) in self.world.iter_for::<(Write<Bone>, Read<SkeletonRef>)>()
.filter(|(_, bone_sk_ref)| *bone_sk_ref == skeleton_ref)
{
let used = vertex_groups.vertex_groups.iter()
.find(|vertex_group| name.to_owned() + "_" + vertex_group == bone.name)
.is_some();
if used {
bone.used = true;
}
}
}
let mut need_used = HashSet::new();
for parent in self.world.ordered_iter_for::<ReadHierarchical<Bone>>(){
if parent.used {
for bone in parent.ancestors_ref().skip(1){
if !bone.used {
need_used.insert(bone.name.clone());
}
}
}
}
for bone in self.world.iter_for::<Write<Bone>>().filter(|bone| need_used.contains(&bone.name)){
bone.used = true;
}
}
Ok(scene_index)
}
}
fn find_bone<C: rinecs::CreationContext>(world: &C, skeleton: rinecs::Entity, bone_name: &str) -> Option<rinecs::Entity>{
world.iter_for::<(Entity, Read<BoneName>, Read<SkeletonRef>)>()
.find(|&(_entity, name, skeleton_ref)| name.0 == bone_name && skeleton_ref.guid() == skeleton.guid())
.map(|(e, _, _)| e)
}
fn common_entity<'w, C: rinecs::CreationContext>(world: &'w mut C,
blender_obj: &rinblender::BlenderObject,
node: Transformation,
ty: Ty,
parent: Option<rinecs::Entity>,
prefix: &str ) -> rinecs::EntityBuilder<'w>
{
let parent_ty = parent.map(|p| **world.component_for::<Ty>(&p).unwrap());
let dynamic = parent.and_then(|p|
if world.component_for::<DynamicTransformation>(&p).is_some() {
Some(true)
}else if world.component_for::<DynamicTransformation>(&p).is_some() {
Some(false)
}else{
None
}
);
let full_name = prefix.to_owned() + "_" + &blender_obj.name();
let builder = if let Some(parent) = parent {
let parent_bone = blender_obj.local_transformation().parent_bone.as_ref();
if let (Some(parent_bone), Some(Ty::Armature)) = (parent_bone, parent_ty){
let node = Transformation::from_node_parent_bone(node.node, parent_bone.clone());
debug!("parenting object {} with bone {}", blender_obj.name(), parent_bone);
if let Some(parent_bone_entity) = find_bone(world, parent, &parent_bone){
world.new_entity()
.add(Name(full_name))
.add_child(&parent_bone_entity, node)
.add(ty)
}else{
debug!("bone not found");
world.new_entity()
.add(Name(full_name))
.add_child(&parent, node)
.add(ty)
}
}else{
world.new_entity()
.add(Name(full_name))
.add_child(&parent, node)
.add(ty)
}
}else{
world.new_entity()
.add(Name(full_name))
.add(node)
.add(ty)
};
let builder = match dynamic{
Some(true) => builder.add(DynamicTransformation),
_ => builder,
};
builder.add(Rotation(blender_obj.local_transformation().rotation))
}
fn build_enitity<'s, C: rinecs::CreationContext>(node: idtree::NodeId,
scene: &'s rinblender::SceneData,
geometries_index: &HashMap<&'s str, Entity>,
materials_index: &HashMap<&'s str, MaterialRef>,
world: &mut C,
parent: Option<rinecs::Entity>,
prefix: &str) -> (rinecs::Entity, Ty)
{
let arena = &scene.arena;
let blender_obj = arena[node].data();
let node = Transformation::from(blender_obj.local_transformation());
let ty = Ty::from(blender_obj);
let e = match *blender_obj{
rinblender::BlenderObject::Armature(ref armature) => {
trace!("Creating armature {}", armature.name());
let skeleton = common_entity(world, blender_obj, node, ty, parent, prefix)
.add(DynamicTransformation)
.add(Skeleton::new())
.build();
let blender_bones = armature.bones_tree();
let mut trafo_parent_stack = VecDeque::new();
trafo_parent_stack.push_back(skeleton.clone());
let mut bone_parent_stack = VecDeque::new();
for base in armature.bone_base().iter(){
for node in base.traverse(blender_bones){
match node{
idtree::NodeEdge::Start(node) => {
let bone = Bone::from((blender_bones[node].data(), prefix));
let bone_entity;
let parent_trafo = trafo_parent_stack.back().unwrap().clone();
{
let bone_builder = world.new_entity()
.add_child(&parent_trafo, Transformation::from(blender_bones[node].data()))
.add(Ty::Bone)
.add(SkeletonRef(skeleton.clone()))
.add(Rotation(blender_bones[node].data().transformations().rotation));
bone_entity = if let Some(parent_bone) = bone_parent_stack.back(){
bone_builder.add_child(parent_bone, bone)
.add_child(parent_bone, BoneName(blender_bones[node].name().to_owned()))
.add(Name(blender_bones[node].name().to_owned()))
.build()
}else{
bone_builder.add(bone)
.add(BoneName(blender_bones[node].name().to_owned()))
.add(Name(blender_bones[node].name().to_owned()))
.build()
};
}
if parent_trafo.guid() == skeleton.guid() {
world.add_component_to(&skeleton, BoneBase(bone_entity));
}
trafo_parent_stack.push_back(bone_entity);
bone_parent_stack.push_back(bone_entity);
}
idtree::NodeEdge::End(_) => {
trafo_parent_stack.pop_back().unwrap();
bone_parent_stack.pop_back().unwrap();
}
}
}
}
let skeleton_geometries = {
let skeletons = world.iter_for::<(Entity, ReadNot<Skeleton, GeometryRef>)>();
skeletons.filter_map(|(skeleton, _)|{
world.component_for::<BoneBase>(&skeleton).map(|bones|{
let mut geometry = bones.iter().flat_map(|bone_base|{
if let Some(bone) = world.tree_node_for::<Bone>(&bone_base.0) {
bone.descendants_ref().flat_map(|bone|{
vec![graphics::vertex3d(bone.global_head().to_vec()),
graphics::vertex3d(bone.global_tail().to_vec())]
}).collect::<Vec<_>>()
}else{
vec![]
}
}).collect::<graphics::Mesh3D>();
geometry.set_primitive_type(graphics::PrimitiveType::Lines);
(skeleton.clone(), geometry)
})
}).collect::<Vec<_>>()
};
for (skeleton, geom) in skeleton_geometries{
let mesh = world.new_entity()
.add(Name("skeleton_geometry".to_owned()))
.add(Geometry::new(geom))
.build();
world.add_component_to(&skeleton, Visible::new(false));
world.add_component_to(&skeleton, GeometryRef(mesh));
world.add_component_to(&skeleton, materials_index[SKELETON_MATERIAL]);
}
skeleton
}
rinblender::BlenderObject::Model(ref model) => {
let e = if let Some(mesh) = geometries_index.get(model.mesh()) {
trace!("Creating model {} with mesh {}", model.name(), model.mesh());
let trimesh = &scene.trimeshes[model.mesh()];
let materials = trimesh.materials().iter()
.zip(trimesh.submeshes())
.filter_map(|(mat_name, submesh)| {
let submesh_count = if let Some(indices) = submesh.indices {
indices.len()
}else{
submesh.vertices.len()
};
if submesh_count > 0 {
mat_name.as_ref().map(|mat_name| materials_index[&mat_name.as_ref()])
}else{
None
}
}).collect::<Vec<_>>();
if !materials.is_empty() {
let shapekey_name = trimesh.key().map(|key| key);
let shapekey = shapekey_name.map(|shapekey| {
let action = scene.trimesh_keys[shapekey].default_action()
.map(|a|{
let action = scene.actions[a].clone();
Action::new(action)
});
ShapeKey{
key: scene.trimesh_keys[shapekey].clone(),
action,
}
});
let dynamic_trafo = model.skeleton().map(|_| DynamicTransformation);
let skeleton_name = model.skeleton().map(|skeleton| SkeletonName(prefix.to_owned() + "_" + skeleton));
let animated_geom = if model.skeleton().is_some() || shapekey.is_some(){
Some(AnimatedGeometry::from(scene.trimodel(model.name()).unwrap()))
}else{
None
};
common_entity(world, blender_obj, node, ty, parent, prefix)
.add(VertexGroups::from(model))
.add(Visible::new(model.is_visible()))
.add_slice(materials)
.add(GeometryRef(*mesh))
.add_option(model.rigid_body().map(|rb| RigidBody::from(rb)))
.add_option(shapekey)
.add_option(dynamic_trafo)
.add_option(skeleton_name)
.add_option(animated_geom)
}else{
trace!("Couldn't find materials for model {} in preloaded meshes. Adding as an empty", model.name());
common_entity(world, blender_obj, node, ty, parent, prefix)
}
}else{
trace!("Couldn't find mesh {} for model {} in preloaded meshes. Probably empty.", model.mesh(), model.name());
common_entity(world, blender_obj, node, ty, parent, prefix)
};
let e = e.add_option(model.default_action().map(|default| {
let default = scene.actions[default].clone();
Action::new(default)
})).build();
#[cfg(debug_assertions)]
{
if let Some(geomref) = world.component_for::<GeometryRef>(&e) {
let submeshes = world.component_for::<Submesh>(&geomref).unwrap();
let materials = world.component_for::<MaterialRef>(&e).unwrap();
assert_eq!(submeshes.len(), materials.len());
}
}
e
}
rinblender::BlenderObject::Light(ref light) => {
trace!("Loading light {}", light.name);
let e = common_entity(world, blender_obj, node.clone(), ty, parent, prefix)
.add(Light);
let e = match light.ty {
rinblender::LightType::Directional => {
e.add(DirectionalLight{
color: light.color.c,
strength: light.strength,
radius: light.size
})
}
rinblender::LightType::Spot => {
let mut spot = SpotLight{
color: light.color.c,
strength: light.strength,
radius: light.size,
spot_angle: light.angle / 2.,
spot_cos_cutoff: 0.,
spot_cos_inner_cutoff: 0.,
spot_blend: light.spot_blend,
};
spot.recalculate_cos();
e.add(spot)
}
rinblender::LightType::Point => {
e.add(PointLight {
color: light.color.c,
strength: light.strength,
radius: light.size
})
}
ty => {
error!("Scene contains a not supported light type {:?}", ty);
e
}
};
if light.shadowmap_type != rinblender::ShadowMapType::No {
e.add(shadow::Map(shadow::Parameters::from(light)))
.add(shadow::StaticMap(shadow::Parameters::from(light)))
.add(shadow::Type::Gaussian)
.build()
}else{
e.build()
}
}
rinblender::BlenderObject::Empty(ref empty) => {
trace!("Loading empty {}", prefix);
common_entity(world, blender_obj, node, ty, parent, prefix)
.add_option(empty.default_action.as_ref().map(|default|{
let default = scene.actions[default].clone();
Action::new(default)
})).build()
}
};
(e, ty)
}
fn parse_children<C: rinecs::CreationContext>(node: idtree::NodeId,
scene: &rinblender::SceneData,
geometries_index: &HashMap<&str, Entity>,
materials_index: &HashMap<&str, MaterialRef>,
world: &mut C,
parent: rinecs::Entity,
scene_index: &mut SceneIndex,
prefix: &str)
{
for child in node.children(&scene.arena){
let (e, ty) = build_enitity(child,
scene, geometries_index, materials_index,
world, Some(parent), prefix);
match ty{
Ty::Armature => scene_index.skeletons.push(e),
Ty::Model => scene_index.models.push(e),
Ty::Light => scene_index.lights.push(e),
Ty::Empty => scene_index.empties.push(e),
_ => unreachable!()
}
parse_children(child,
scene, geometries_index, materials_index,
world, e, scene_index, prefix);
}
}
pub struct BlenderMaterials<'a>{
alpha_type: AlphaType,
world: &'a mut World,
}
impl<'a> crate::Builder<'a> for BlenderMaterials<'a>{
type Settings = ();
fn new(world: &'a mut World, _: ()) -> BlenderMaterials<'a>{
BlenderMaterials{
world,
alpha_type: AlphaType::None,
}
}
}
impl<'a> BlenderMaterials<'a>{
pub fn alpha_type(&mut self, alpha_type: AlphaType) -> &mut BlenderMaterials<'a>{
self.alpha_type = alpha_type;
self
}
pub fn from_scene_data<P: AsRef<Path>>(&mut self, scene: &rinblender::SceneData, path: P) -> Result<HashMap<String, MaterialRef>>{
let mut blender = Blender::new(self.world, ());
let blender = blender.alpha_type(self.alpha_type);
let textures = blender.load_textures(&scene, path.as_ref());
let materials = blender.load_materials(&scene, path, &textures)
.into_iter()
.map(|(n,m)| (n.to_owned(), m))
.collect();
Ok(materials)
}
pub fn load<P: AsRef<Path>>(&mut self, path: P) -> Result<HashMap<String, MaterialRef>>{
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::load(&file);
self.from_scene_data(&scene, path)
}
}