use rinecs::{Entities, EntitiesCreationExt, EntitiesExt, Entity, IntoStorages, Read, ReadHierarchical, ReadNot, ReadOption, Resources, ResourcesThreadLocalExt, System, UniqueEntities, UnorderedData, World, Write, system, StorageRef};
#[cfg(feature = "async")]
use rinecs::CreationProxy;
use rin_util::{Error, Result};
use rin_graphics::{self as graphics, IndexT, Mesh, Node, Vertex3D, node::DynamicTransformation};
use color::consts::*;
use rin_math::{clamp, vec4};
use rin_scene::{Builder, CreationProxy, components::{Name, Visible, Ty, SourcePath}, geometry::{Submesh, Geometry, GeometryRef, VertexGroups}, geometry::AnimatedGeometry, light::{Light, DirectionalLight, SpotLight, PointLight, shadow}, physics::{rigidbody::{self, RigidBody, RigidBodyShape}}, scene::CreationContext, transformation::{
Bone, PreviousTransformation, SkinningUpToDate,
}};
use crate::skinning::*;
use rin_material::{
self as material,
StandardMaterialBuilder, LambertMaterialBuilder,
ClothMaterialBuilder, ClothSubsurfaceMaterialBuilder,
AnisotropicMaterialBuilder, ClearcoatMaterialBuilder,
SubsurfaceMaterialBuilder, MaterialRef, AlphaType,
texture::{TextureRef, TextureSampler, TextureCreationFlags},
BasicMaterialBuilder, ShaderPrecision,
};
use rinblender::{idtree, ObjectId};
#[cfg(feature="debug_geometry")]
use rin_scene::geometry::DebugGeometryRef;
#[cfg(gl)]
use self::components::OriginalGeometryBuffer;
#[cfg(gl)]
use rin_scene::renderer::{PostFragmentMaterialBuilder};
#[cfg(gl)]
use rin_gl as gl;
use std::collections::HashMap;
use std::path::Path;
use rin_math::{ToVec, one, Angle, Rad};
use self::components::*;
use std::collections::{VecDeque, HashSet};
use color::*;
use std::mem;
#[cfg(feature="rayon")]
use rayon::prelude::*;
use itertools::{Itertools, Either};
#[cfg(gui)]
use rin_gui::ParameterGroup;
use rin_events::{Parameter, ParamsDefault, Property, TryIter};
pub use rinblender::File;
pub use self::actions::{
Action, ActionBlend, ActionMix, ActionOffset, ActionExt, ActionCollection,
ActionClock, GameClock, GameClockNoFps, ActionController, ActionControllerBuilder,
ActionClockExt, LoopClock, RootMotionRemove, ClockOffset, ReverseClock, ConstantClock,
ActionSpacer, ActionUpdate, ActionNeedsUpdate, ActionUpdateExt, ActionControllerArc
};
pub use self::components::{
Vertex,
CharacterEntity,
SceneIndex,
ShapeKey,
Selectable,
Rotation,
RotOrder
};
pub use animation_system::{ActionClockUpdated, ActionUpdated};
#[cfg(feature = "async")]
use async_loader::AsyncLoader;
#[cfg(feture="debug_times")]
macro_rules! time{
($descr: expr, $func: block) => (
{
use std::time;
let then = time::Instant::now();
let ret = $func;
let duration = time::Instant::now() - then;
log::debug!("{} time: {}ms", $descr, duration.as_secs_f32() as f32 * 1000.);
ret
}
);
}
#[cfg(not(feture="debug_times"))]
macro_rules! time {
($descr: expr, $func: block) => ( $func );
}
mod components;
mod textures;
mod actions;
mod animation_system;
#[cfg(feature = "async")]
mod async_loader;
pub mod skinning;
pub mod scene_data;
const SKELETON_MATERIAL: &'static str = "__BLENDER_SKELETON_MATERIAL";
const MISSING_TEXTURE: &'static str = "__BLENDER_MISSING_TEXTURE";
#[cfg_attr(gui, derive(ParameterGroup))]
#[derive(ParamsDefault)]
pub struct Parameters {
#[param(default = true)]
pub apply_blend_shapes: Property<'static, bool>,
#[param(default = false)]
show_skeletons_geometry: Property<'static, bool>,
#[param(default = true)]
defer_skinning: Property<'static, bool>,
}
pub struct ApplyBlendShaoes {
pub apply_blend_shapes: Parameter<'static, bool>,
}
pub struct Bundle{
parameters: Parameters
}
impl Bundle {
pub fn new() -> Bundle {
Bundle {
parameters: Parameters::default(),
}
}
}
impl rin_scene::Bundle for Bundle {
type Parameters = Parameters;
fn parameters(&self) -> Option<&Parameters> {
Some(&self.parameters)
}
fn name(&self) -> &str{
"blender"
}
fn setup(self, world: &mut rin_scene::DeferredScene){
#[cfg(feature = "async")]
{
let (loader, updater) = AsyncLoader::new();
world.add_resource_thread_local(loader);
world.add_creation_system_async(updater);
}
world.add_system(animation_system::animation_update);
world.add_system(animation_system::animation_skeletons);
world.add_system(animation_system::animation_scenes_trafos);
world.add_system(animation_system::animation_scenes_visibles);
world.add_system(animation_system::animation_scenes_skeletons);
world.add_system(animation_system::animation_scenes_shapekeys);
world.add_system(animation_system::animation_individual_entities);
world.add_system(bone_updater);
world.add_system(feet_updater);
world.add_system(skeleton_cache_updater);
let show_skeletons_geometry = self.parameters.show_skeletons_geometry.clone();
let last_show_skeletons = show_skeletons_geometry.clone().try_iter();
world.add_system(ShowSkeletonsSystem{last_show_skeletons});
#[cfg(all(gl, not(target_os="macos"), not(target_arch = "wasm32")))]
{
let skinning = GpuSkinningSystem::<crate::Vertex>::new(&world.resource::<gl::Renderer>().unwrap());
world.add_system_thread_local(skinning);
}
#[cfg(any(not(gl), target_os="macos", target_arch = "wasm32"))]
{
world.add_system_thread_local(SkinningSystem::<crate::Vertex>::new(self.parameters.defer_skinning.clone()));
}
world.add_system(
SkeletonGeometryUpdater::new(
show_skeletons_geometry.clone().to_parameter()
)
);
world.add_system(skinning::skeleton_changed_reseter);
world.add_resource_thread_local(ApplyBlendShaoes{
apply_blend_shapes: self.parameters.apply_blend_shapes.to_parameter()
});
}
}
struct ShowSkeletonsSystem{
last_show_skeletons: TryIter<'static, bool>,
}
#[system(name="show skeletons")]
#[writes("Skeleton", "Visible")]
impl System for ShowSkeletonsSystem {
fn run(&mut self, mut entities: Entities, _resources: Resources) {
if let Some(show_skeletons) = self.last_show_skeletons.by_ref().last(){
for (skeleton, visible) in entities.iter_for_mut::<(Write<Skeleton>, Write<Visible>)>() {
visible.set(show_skeletons);
skeleton.changed = true;
}
}
}
}
pub struct Blender<'a> {
parent: Option<Entity>,
entity: Option<Entity>,
visible: bool,
transformation: Option<Node>,
dynamic: bool,
alpha_type: Option<AlphaType>,
blend_hashed_as_alpha_to_coverage: bool,
use_post_fragment_materials: bool,
instancing: bool,
scene_rigid_body: Option<RigidBodyShape>,
shader_precision: ShaderPrecision,
world: CreationProxy<'a>,
}
impl<'a> Builder<'a> for Blender<'a> {
type Settings = ();
type IdType = components::Vertex;
type Depends = Bundle;
fn register(world: &mut World){
#[cfg(gl)]
world.register_vertex_type::<Vertex>();
world.register_vertex_type::<Vertex3D>();
}
fn new(world: CreationProxy<'a>, _:()) -> Blender<'a> {
Blender{
parent: None,
entity: None,
visible: true,
dynamic: false,
transformation: None,
alpha_type: None,
use_post_fragment_materials: false,
instancing: true,
scene_rigid_body: None,
shader_precision: ShaderPrecision::default(),
blend_hashed_as_alpha_to_coverage: false,
world,
}
}
}
impl<'a> Blender<'a> {
pub fn alpha_type(&mut self, alpha_type: AlphaType) -> &mut Blender<'a> {
self.alpha_type = Some(alpha_type);
self
}
pub fn blend_hashed_as_alpha_to_coverage(&mut self) -> &mut Blender<'a> {
self.blend_hashed_as_alpha_to_coverage = true;
self
}
pub fn shader_precision(&mut self, shader_precision: ShaderPrecision) -> &mut Blender<'a> {
self.shader_precision = shader_precision;
self
}
pub fn transformation<T: Into<Node>>(&mut self, trafo: T) -> &mut Blender<'a> {
self.transformation = Some(trafo.into());
self
}
pub fn dynamic_transformation<T: Into<Node>>(&mut self, trafo: T) -> &mut Blender<'a> {
self.transformation = Some(trafo.into());
self.dynamic = true;
self
}
pub fn invisible(&mut self) -> &mut Blender<'a> {
self.visible = false;
self
}
pub fn parent(&mut self, parent: Entity) -> &mut Blender<'a> {
self.parent = Some(parent);
self
}
pub fn post_fragment_materials(&mut self) -> &mut Blender<'a> {
self.use_post_fragment_materials = true;
self
}
pub fn entity(&mut self, entity: Entity) -> &mut Blender<'a> {
self.entity = Some(entity);
self
}
pub fn no_instancing(&mut self) -> &mut Blender<'a> {
self.instancing = false;
self
}
pub fn scene_rigid_body(&mut self, shape: RigidBodyShape) -> &mut Blender<'a> {
self.scene_rigid_body = Some(shape);
self
}
pub fn from_scene_data<P: AsRef<Path>>(
&mut self,
scene: &rinblender::SceneData,
path: P,
name: &str) -> Result<(rinecs::Entity, SceneIndex)>
{
let transformation = self.transformation.unwrap_or_else(|| one() );
let group = if let Some(entity) = self.entity {
self.world.add_component_to(&entity, Name(name.to_owned()));
self.world.add_component_to(&entity, Ty::Empty);
if let Some(parent) = self.parent.as_ref(){
self.world.add_child_component_to(&parent, &entity, transformation)
}else{
self.world.add_component_to(&entity, transformation)
}
self.world.add_component_to(&entity, Visible::new(self.visible));
entity
}else{
let group = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty);
let group = if let Some(parent) = self.parent.as_ref(){
group.add_child(parent, transformation)
}else{
group.add(transformation)
};
group.add(Visible::new(self.visible)).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());
if self.dynamic{
self.world.add_tag_to::<DynamicTransformation>(&group);
for empty in index.empties.iter() {
self.world.add_tag_to::<DynamicTransformation>(&empty);
}
for model in index.models.iter() {
self.world.add_tag_to::<DynamicTransformation>(&model);
}
}
if let Some(shape_ty) = self.scene_rigid_body {
let shape_offset = scene_index_shape_and_offset(
&self.world,
&transformation,
index.models.iter(),
false,
shape_ty
);
if let Some((shape, offset)) = shape_offset {
log::trace!("Adding rigid body to {}", name);
self.world.add_component_to(&group, shape);
self.world.add_component_to(&group, offset);
}else{
log::error!(
"Couldnt create rigid body shape of type {:?} for {}",
shape_ty,
path.as_ref().display()
);
}
}
}
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 scene = time!("Load rinblender", {
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
rinblender::SceneData::load(&file)
});
self.from_scene_data(&scene, path, name)
.map(|(entity,scene_index)| (entity, scene, scene_index))
}
#[cfg(feature="async")]
pub fn load_scene_async<P: AsRef<Path>>(
&mut self,
path: P,
name: &str) -> Entity
{
let scene = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty)
.build();
self.world
.resource_mut::<AsyncLoader>()
.unwrap()
.load(
path,
name,
scene,
self.transformation,
self.visible,
self.parent,
self.use_post_fragment_materials
);
scene
}
pub fn character_from_scene_data<P: AsRef<Path>>(
&mut self,
scene: &rinblender::SceneData,
path: P,
name: &str) -> Result<(CharacterEntity, SceneIndex)>
{
let transformation = self.transformation.unwrap_or_else(|| one() );
let character = if let Some(entity) = self.entity {
self.world.add_component_to(&entity, Name(name.to_owned()));
self.world.add_component_to(&entity, Visible::new(self.visible));
self.world.add_component_to(&entity, Ty::Empty);
self.world.add_component_to(&entity, SkinningUpToDate(false));
if let Some(parent) = self.parent.as_ref(){
self.world.add_child_component_to(&parent, &entity, transformation)
}else{
self.world.add_component_to(&entity, transformation)
}
self.world.add_component_to(&entity, PreviousTransformation(transformation));
self.world.add_tag_to::<DynamicTransformation>(&entity);
entity
}else{
let character = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Visible::new(self.visible))
.add(Ty::Empty);
let character = if let Some(parent) = self.parent.as_ref(){
character.add_child(parent, transformation.clone())
}else{
character.add(transformation.clone())
};
character.add(PreviousTransformation(transformation))
.add_tag::<DynamicTransformation>()
.build()
};
self.load(name, path, scene, Some(character)).map(|scene_index|{
self.world.add_component_to(&character, scene_index.clone());
if let Some(shape) = self.scene_rigid_body {
let shape_offset = scene_index_shape_and_offset(
&self.world,
&transformation,
scene_index.models.iter(),
false,
shape
);
if let Some((shape, offset)) = shape_offset {
self.world.add_component_to(&character, shape);
self.world.add_component_to(&character, offset);
}
}
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::SceneData::load(&file);
self.character_from_scene_data(&scene, path, name)
.map(|(entity, scene_index)| (entity, scene, scene_index))
}
#[cfg(feature = "async")]
pub fn load_character_async<P: AsRef<Path>, F>(
&mut self,
path: P,
name: &str,
on_ready: F) -> Entity
where F: Fn(
CharacterEntity,
rinblender::SceneData,
SceneIndex,
&mut CreationProxy) + 'static
{
let character = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty)
.build();
self.world.resource_mut::<AsyncLoader>()
.unwrap()
.load_character(
path,
name,
character,
self.transformation,
self.visible,
self.parent,
self.use_post_fragment_materials,
on_ready
);
character
}
pub fn load_group<P: AsRef<Path>>(
&mut self,
path: P,
name: &str,
groups: &[&str]) -> Result<(rinecs::Entity, rinblender::SceneData, SceneIndex)>
{
log::trace!("Loading {} groups {:?} from {}. {}", name, groups, path.as_ref().display(),
if self.dynamic {
"dyanmic"
} else {
"static"
});
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::SceneData::load_groups(&file, groups)
.map_err(|err| Error::with_cause("Error loading blender groups", err))?;
self.from_scene_data(&scene, path, name).map(|(entity, scene_index)|{
(entity, scene, scene_index)
})
}
#[cfg(feature = "async")]
pub fn load_group_async<P: AsRef<Path>>(
&mut self,
path: P,
name: &str,
groups: &[&str]) -> Entity
{
let group = self.world.new_entity()
.add(Name(name.to_owned()))
.add(Ty::Empty)
.build();
self.world.resource_mut::<AsyncLoader>()
.unwrap()
.load_group(
path,
name,
group,
groups,
self.transformation,
self.visible,
self.parent,
self.use_post_fragment_materials
);
group
}
fn load_meshes<'s, P: AsRef<Path>>(
&mut self,
scene: &'s rinblender::SceneData,
path: P) -> HashMap<&'s ObjectId, 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(|(objectid, trimesh)| {
let name = objectid.name.clone();
let path = objectid.to_path_with_original_path(&path);
if self.instancing {
if let Some(entity) = existing_geom_index.get(&(name, path.clone())) {
log::trace!("Reusing already existing mesh by name: {}", objectid);
return Some((objectid, *entity))
}
}
log::trace!("Preloading mesh: {} with {} vertices and {} indices: {}Mb",
objectid,
trimesh.original_vertices().len(),
trimesh.original_indices().len(),
(trimesh.original_vertices().len() * mem::size_of::<Vertex>() +
trimesh.original_indices().len() * mem::size_of::<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(objectid.name.clone()))
.add(SourcePath(path.clone()))
.add(Geometry::new(full_mesh))
.add(Ty::Mesh)
.add_slice(submeshes)
.build();
Some((objectid, entity))
}else{
log::trace!("Discarding empty mesh {}", objectid);
None
}
}).collect::<HashMap<_,_>>()
}
#[cfg(not(all(gl, image_enabled)))]
fn load_textures<'s, P: AsRef<Path>>(
&mut self,
scene: &'s rinblender::SceneData,
path: P) -> HashMap<ObjectId, TextureRef>
{
unimplemented!()
}
#[cfg(all(gl, image_enabled))]
fn load_textures<'s, P: AsRef<Path>>(
&mut self,
scene: &'s rinblender::SceneData,
path: P) -> HashMap<ObjectId, TextureRef>
{
let missing_tex = self.world.find_named_texture(MISSING_TEXTURE).unwrap_or_else(||{
let data = MAGENTA.as_ref();
let format = gl::texture::LoadDataFormat::new(
gl::RGB8,
1,
1,
).unwrap();
self.world
.register_texture_from_data_format(format, TextureCreationFlags::empty(), data)
.unwrap()
});
let is_used = |objectid: &ObjectId|{
scene.materials.values().find(|mat|{
mat.texture.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.normal_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.occlusion_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.metallic_roughness_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.metallic_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.roughness_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.emissive_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.anisotropy_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false) ||
mat.subsurface_map.as_ref().map(|t| t.data == *objectid).unwrap_or(false)
}).is_some()
};
let (mut textures, non_found): (HashMap<_, _>, Vec<_>) = scene.images.iter()
.filter(|(objectid, _)| is_used(objectid))
.partition_map(|(objectid, data)| {
let path = objectid.to_path_with_original_path(&path);
let path_name = path.to_str().unwrap();
if let Some(textureref) = self.world.find_named_texture(path_name) {
Either::Left((objectid.clone(), textureref))
}else{
Either::Right((objectid, path_name.to_owned(), data))
}
});
#[cfg(feature="rayon")]
let non_found = non_found.into_par_iter();
#[cfg(not(feature="rayon"))]
let non_found = non_found.into_iter();
textures.extend(
textures::load_images_data(non_found, &mut self.world)
.map(|(objectid, img)| {
let tex = img.unwrap_or(missing_tex);
(objectid, tex)
})
);
textures
}
#[cfg(not(all(gl, image_enabled)))]
fn load_materials<'s, P: AsRef<Path>>(&mut self,
scene: &'s rinblender::SceneData,
path: P,
textures: &'s HashMap<ObjectId, TextureRef>) -> (HashMap<&'s ObjectId, MaterialRef>, HashMap<&'s ObjectId, MaterialRef>)
{
unimplemented!()
}
#[cfg(all(gl, image_enabled))]
fn load_materials<'s, P: AsRef<Path>>(&mut self,
scene: &'s rinblender::SceneData,
path: P,
textures: &'s HashMap<ObjectId, TextureRef>) -> (HashMap<&'s ObjectId, MaterialRef>, HashMap<&'s ObjectId, MaterialRef>)
{
let texture_sampler = |image: Option<&'s rinblender::Image>, world: &mut CreationProxy|{
image.and_then(|img| {
let texture = if !img.modifiers.is_empty() {
let tex = rinblender::Texture{
image: img,
data: &scene.images[&img.data]
};
let texture = textures::load_texture(tex, world);
texture.or_else(|| textures.get(&img.data).cloned())
}else{
textures.get(&img.data)
.or_else(|| textures.get(&img.data))
.cloned()
}.or_else(|| textures.get(&ObjectId::new("".into(), MISSING_TEXTURE.to_owned())).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 = BasicMaterialBuilder::new()
.base_color(&rgba!(BLACK, 0.999))
.properties(vec![material::Property::DepthTest(false)])
.build();
self.world.register_material(SKELETON_MATERIAL, skeleton_material)
});
let mut blender_to_mutiny_material = |mat_id: &'s ObjectId, mat: &'s rinblender::Material, doublesided: bool|{
let path_name = mat_id.to_path_with_original_path(&path).to_str().unwrap().to_owned()
+ if doublesided { "_doublesided" } else { "" };
if let Some(materialref) = self.world.find_material(&path_name){
return (mat_id, materialref);
}
let base_color_sampler = texture_sampler(mat.texture.as_ref(), &mut self.world);
let normal_map_sampler = texture_sampler(mat.normal_map.as_ref(), &mut self.world);
let occlusion_map_sampler = texture_sampler(mat.occlusion_map.as_ref(), &mut self.world);
let metallic_roughness_map_sampler = texture_sampler(mat.metallic_roughness_map.as_ref(), &mut self.world);
let metallic_map_sampler = texture_sampler(mat.metallic_map.as_ref(), &mut self.world);
let roughness_map_sampler = texture_sampler(mat.roughness_map.as_ref(), &mut self.world);
let emissive_map_sampler = texture_sampler(mat.emissive_map.as_ref(), &mut self.world);
let subsurface_map_sampler = texture_sampler(mat.subsurface_map.as_ref(), &mut self.world);
let anisotropy_map_sampler = texture_sampler(mat.anisotropy_map.as_ref(), &mut self.world);
let base_color = rgba_linear!(mat.albedo, mat.alpha);
let doublesided = doublesided | mat.doublesided;
let alpha_type = self.alpha_type
.or_else(|| mat.blend_mode.map(|blend_mode| match blend_mode {
rinblender::material::BlendMode::Opaque => AlphaType::None,
rinblender::material::BlendMode::Clip => AlphaType::Threshold,
rinblender::material::BlendMode::Hashed => if self.blend_hashed_as_alpha_to_coverage {
AlphaType::AlphaToCoverage
}else{
AlphaType::ScreenDoor
},
rinblender::material::BlendMode::Blend => AlphaType::Blending,
})).unwrap_or(AlphaType::None);
match mat.ty{
rinblender::material::Type::Lambert => {
let material = LambertMaterialBuilder::default()
.color(base_color)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(doublesided)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.roughness_map_opt(roughness_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, 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(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)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.metallic_roughness_map_opt(metallic_roughness_map_sampler)
.metallic_map_opt(metallic_map_sampler)
.roughness_map_opt(roughness_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, 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(doublesided)
.reflectance(mat.specular.r.max(mat.specular.g).max(mat.specular.b))
.reflectance_tint(mat.specular_tint)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.metallic_roughness_map_opt(metallic_roughness_map_sampler)
.metallic_map_opt(metallic_map_sampler)
.roughness_map_opt(roughness_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, 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(doublesided)
.subsurface_color(subsurface_color)
.sheen_color(mat.specular)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, self.world.register_material(&path_name, material))
}
}else{
let material = ClothMaterialBuilder::default()
.color(base_color)
.roughness(mat.roughness)
.emissive_color(mat.emissive)
.double_sided(doublesided)
.sheen_color(mat.specular)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, 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(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)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.metallic_roughness_map_opt(metallic_roughness_map_sampler)
.metallic_map_opt(metallic_map_sampler)
.roughness_map_opt(roughness_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.anisotropy_map_opt(anisotropy_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, 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(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)
.base_color_map_opt(base_color_sampler)
.normal_map_opt(normal_map_sampler)
.occlusion_map_opt(occlusion_map_sampler)
.metallic_roughness_map_opt(metallic_roughness_map_sampler)
.metallic_map_opt(metallic_map_sampler)
.roughness_map_opt(roughness_map_sampler)
.emissive_map_opt(emissive_map_sampler)
.alpha_type(alpha_type)
.shader_precision(self.shader_precision)
.normal_scale(mat.normal_scale)
.build();
if self.use_post_fragment_materials {
let material = PostFragmentMaterialBuilder::new(material)
.build();
(mat_id, self.world.register_material(&path_name, material))
}else{
(mat_id, self.world.register_material(&path_name, material))
}
}
}
};
let scene_materials = scene.materials.iter().map(|(mat_id, mat)| {
blender_to_mutiny_material(&mat_id, &mat, false)
})
.collect();
let scene_doublesided_materials = scene.materials.iter().filter_map(|(mat_id, mat)| {
let needs_double_sided = scene.trimodels()
.find(|model: &rinblender::TriModel| {
if model.mesh().flag.contains(rinblender::mesh::Flag::TWOSIDED){
model.materials().contains(&Some(mat_id.clone()))
}else{
false
}
}).is_some();
if needs_double_sided && !mat.doublesided{
Some(blender_to_mutiny_material(&mat_id, &mat, true))
}else{
None
}
}).collect();
(scene_materials, scene_doublesided_materials)
}
fn load<P: AsRef<Path>>(
&mut self,
name: &str,
path: P,
scene: &rinblender::SceneData,
parent: Option<Entity>) -> Result<SceneIndex>
{
log::trace!("Loading {} from {}. {}", name, path.as_ref().display(),
if self.dynamic {
"dyanmic"
} else {
"static"
});
let meshes = time!("Load meshes", { self.load_meshes(&scene, path.as_ref()) });
let textures = time!("Load textures", { self.load_textures(&scene, path.as_ref()) });
let (materials, doublesided_materials)
= time!("Load materials", { self.load_materials(&scene, path.as_ref(), &textures) });
let mut skeletons = vec![];
let mut poses = vec![];
let mut models = vec![];
let mut lights = vec![];
let mut empties = vec![];
time!("Create entities", {
for root in scene.roots.iter(){
let (parent, ty) = build_enitity(
*root,
&scene,
&meshes,
&materials,
&doublesided_materials,
&mut self.world,
parent,
self.dynamic,
name
);
match ty{
Ty::Armature => skeletons.push(parent.clone()),
Ty::Model => models.push(parent.clone()),
Ty::Light => lights.push(parent.clone()),
Ty::Empty => empties.push(parent.clone()),
_ => unreachable!()
}
parse_children(
*root,
&scene,
&meshes,
&materials,
&doublesided_materials,
&mut self.world,
parent,
self.dynamic,
&mut skeletons,
&mut models,
&mut lights,
&mut empties,
name
);
}
});
let scene_index = unsafe{
SceneIndex {
skeletons: UniqueEntities::new_from_unchecked(skeletons),
poses: UniqueEntities::new_from_unchecked(poses),
models: UniqueEntities::new_from_unchecked(models),
lights: UniqueEntities::new_from_unchecked(lights),
empties: UniqueEntities::new_from_unchecked(empties),
}
};
if !scene_index.skeletons.is_empty(){
let model_skeleton = self.world.iter_for_entities::<(
Entity,
Read<AnimatedGeometry<Vertex>>,
ReadNot<SkeletonName, SkeletonRef>,
Read<Name>), _>(&scene_index.models)
.map(|(model, _, skeleton_name, name)|{
log::trace!("Assigning skeleton {} to model {}", skeleton_name, name);
let skeleton_entity = self.world
.iter_for::<(Entity, Read<Skeleton>, Read<components::ObjectId>)>()
.find(|&(_entity, _skeleton, skeleton_id)| {
let found = skeleton_id.0 == skeleton_name.0;
found || scene.proxies.get(&skeleton_name)
.map(|proxy| *proxy == **skeleton_id)
.unwrap_or(false)
})
.map(|(skeleton_entity,_, _)| skeleton_entity)
.expect("Couldn't find skeleton");
(model, skeleton_entity, skeleton_name.clone())
}).collect::<Vec<_>>();
for (model, skeleton, skeleton_name) in model_skeleton.into_iter(){
self.world.add_component_to(&model, SkeletonRef(skeleton));
let skeleton = scene.skeletons.get(&skeleton_name)
.unwrap_or_else(|| {
let proxy = scene.proxies.get(&skeleton_name).unwrap();
&scene.skeletons[&proxy]
});
if let rinblender::BlenderObject::Armature(skeleton) = scene.arena[*skeleton].data(){
let armature_cache = ArmatureCache::from_blender(skeleton);
self.world.add_component_to(&model, armature_cache);
}else{
panic!("Trying to add non exisiting skeleton")
}
}
let (vertex_groups, mut bones, names) = self.world.storage_for_mut::<(
(
Entity,
Read<SkeletonRef>,
Read<VertexGroups>,
ReadOption<ShapeKey>
),
(Write<Bone>, Read<SkeletonRef>),
Read<Name>,
)>().unwrap().into_storages();
let iter = vertex_groups
.iter_for_entities(&scene_index.models);
for (model, skeleton_ref, vertex_groups, shapekey) in iter {
log::trace!(
"Setting used bones for model {}, skeleton {}",
names.get(&model).unwrap(),
names.get(&skeleton_ref).unwrap(),
);
for (mut bone, _) in bones.iter_mut()
.filter(|(_, bone_sk_ref)| *bone_sk_ref == skeleton_ref)
{
let used_vertex_group = vertex_groups.vertex_groups.iter()
.any(|vertex_group| name.to_owned() + "_" + vertex_group == bone.name);
let used_shape_keys = if let Some(shapekey) = shapekey {
shapekey.key.drivers().iter()
.any(|driver| driver.driver().unwrap().variables().iter().any(|var| {
match var.ty() {
rinblender::curves::DriverVarType::TransformChannel => {
name.to_owned() + "_" + var.pchan_name() == bone.name
}
rinblender::curves::DriverVarType::SingleProp =>
todo!("Single property not implemented yet"),
rinblender::curves::DriverVarType::RotDiff =>
todo!("Rotation diff not implemented yet"),
rinblender::curves::DriverVarType::LocDiff =>
todo!("Location diff not implemented yet"),
}
}))
}else{
false
};
if used_vertex_group || used_shape_keys {
bone.used = true;
}
}
}
drop((vertex_groups, bones, names));
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_mut::<Write<Bone>>()
.filter(|bone| need_used.contains(&bone.name))
{
bone.used = true;
}
let model_skeletons = self.world
.iter_for_entities::<(Entity, Read<SkeletonRef>, Read<GeometryWeights>),_>(&scene_index.models)
.map(|(model, skeleton_ref, weights)| (model, *skeleton_ref, weights.deformflags))
.collect::<Vec<_>>();
for (model, model_skeleton, deformflags) in model_skeletons {
log::trace!(
"Optimizing skeleton and weights for model {}, skeleton {}",
*self.world.component_for::<Name>(&model).unwrap(),
*self.world.component_for::<Name>(&model_skeleton).unwrap(),
);
let mut num_bones = 0;
let mut used_bones_id_remap = densevec::DenseVec::new();
for (bone_id, (bone, _)) in self.world.iter_for_mut::<(Write<Bone>, Read<SkeletonRef>)>()
.filter(|(bone, bone_skeleton)| **bone_skeleton == model_skeleton && bone.used)
.enumerate()
{
used_bones_id_remap.insert(bone.animated_id, bone_id);
bone.optimized_id = bone_id;
num_bones = bone_id + 1;
}
if deformflags.contains(rinblender::ArmatureDeformFlag::QUATERNIONS) {
self.world.add_slice_component_to::<ArmatureDualQuats, _>(
&model,
unsafe{ vec![mem::MaybeUninit::uninit().assume_init(); num_bones] }
);
let gpu_dualquats = self.world.resource_thread_local::<gl::Renderer>().unwrap()
.new_shared_buffer()
.create_target(num_bones, gl::STREAM_DRAW, gl::UNIFORM_BUFFER)
.unwrap();
self.world.add_component_to_thread_local(
&model,
ArmatureDualQuatsBuffer(gpu_dualquats)
);
}else{
self.world.add_slice_component_to::<ArmatureMatrices, _>(
&model,
unsafe{ vec![mem::MaybeUninit::uninit().assume_init(); num_bones] }
);
let gpu_matrices = self.world.resource_thread_local::<gl::Renderer>().unwrap()
.new_shared_buffer()
.create_target(num_bones, gl::STREAM_DRAW, gl::UNIFORM_BUFFER)
.unwrap();
self.world.add_component_to_thread_local(
&model,
ArmatureMatricesBuffer(gpu_matrices)
);
}
let (mut vertex_groups_sto, bones, names) = self.world.storage_for_mut::<(
(
Read<VertexGroups>,
Write<ArmatureCache>,
Write<GeometryWeights>
),
(Entity, Read<Bone>, Read<SkeletonRef>),
Read<Name>,
)>().unwrap().into_storages();
let (vertex_groups, armature_cache, weights) = vertex_groups_sto
.get_mut(&model)
.unwrap();
let dweight = &weights.dweight;
let mut final_weights = Vec::new();
let mut final_dverts = Vec::new();
let mut gpu_weights = Vec::new();
for weights in weights.dvert.iter()
.map(move |dvert| dvert.as_ref().map(move |dvert| {
(&dweight[dvert.dw_start .. dvert.dw_end], dvert.flag)
}))
{
if let Some((weights, flag)) = weights {
let dw_start = final_weights.len();
final_weights.extend(weights.iter().filter_map(|weight| {
let name = vertex_groups.vertex_groups.get(weight.def_nr as usize).unwrap();
let vertex_group_id = armature_cache.index.get(name).copied();
vertex_group_id.map(|id| {
let mut final_weight = *weight;
final_weight.def_nr = used_bones_id_remap[id] as i32;
final_weight
})
}).sorted_by_key(|w| (w.weight * 10000.) as i32).rev().take(4));
let dw_end = final_weights.len();
let num_weights = dw_end - dw_start;
if num_weights < 4 {
let mut last = final_weights.last().copied().unwrap_or(rinblender::mesh::MDeformWeight{
weight: 0.,
def_nr: 0
});
last.weight = 0.;
final_weights.extend(std::iter::repeat(last).take(4 - num_weights))
}
final_dverts.push(Some(rinblender::mesh::MDeformVert{
dw_start,
dw_end,
flag,
}));
let weight_indices4 = &final_weights[dw_start .. dw_start + 4];
let weights = vec4!(
weight_indices4[0].weight,
weight_indices4[1].weight,
weight_indices4[2].weight,
weight_indices4[3].weight
);
let bone_indices = vec4!(
weight_indices4[0].def_nr as u32,
weight_indices4[1].def_nr as u32,
weight_indices4[2].def_nr as u32,
weight_indices4[3].def_nr as u32
);
gpu_weights.push(SkinWeights{ weights, bone_indices });
}else{
let last = final_weights.last().map(|l| l.def_nr).unwrap_or(0);
final_weights.extend(std::iter::repeat(rinblender::mesh::MDeformWeight{
weight: 0.,
def_nr: last,
}).take(4));
gpu_weights.push(SkinWeights{
weights: vec4!(0.),
bone_indices: vec4!(last as u32),
});
final_dverts.push(None);
}
}
weights.final_weights = final_weights;
weights.final_dverts = final_dverts;
weights.gpu_weights = gpu_weights;
let skinning_bones_index = bones.iter()
.filter_map(|(bone_entity, bone, bone_sk_ref)| {
if **bone_sk_ref == *model_skeleton && vertex_groups.vertex_groups.iter()
.any(|vertex_group| name.to_owned() + "_" + vertex_group == bone.name)
{
Some(bone_entity)
}else{
None
}
}).collect();
armature_cache.skinning_bones_index = skinning_bones_index;
log::trace!(
"Bones with weights for {} {}",
*names.get(&model).unwrap(),
armature_cache.skinning_bones_index.len()
);
let gpu_weights = weights.gpu_weights.clone();
drop((vertex_groups, armature_cache, weights));
drop((vertex_groups_sto, bones, names));
let gpu_weights = self.world.resource_thread_local::<gl::Renderer>().unwrap()
.new_shared_buffer()
.from_data_target(&gpu_weights, gl::STATIC_DRAW, gl::ARRAY_BUFFER)
.unwrap();
self.world.add_component_to_thread_local(&model, BoneWeightsAndIndicesBuffer(gpu_weights));
}
}
Ok(scene_index)
}
}
fn find_bone(world: &CreationProxy, 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>(world: &'w mut CreationProxy,
blender_obj: &rinblender::BlenderObject,
node: Node,
ty: Ty,
parent: Option<rinecs::Entity>,
mut dynamic: bool,
prefix: &str ) -> rinecs::EntityBuilder<'w>
{
let parent_ty = parent.map(|p| **world.component_for::<Ty>(&p).unwrap());
dynamic |= parent
.map(|p| world.has_component::<DynamicTransformation>(&p))
.unwrap_or(false);
let full_name = prefix.to_owned() + "_" + &blender_obj.name().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){
log::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_tag_if::<DynamicTransformation>(dynamic)
.add(ty)
}else{
log::debug!("bone not found");
world.new_entity()
.add(Name(full_name))
.add_child(&parent, node)
.add_tag_if::<DynamicTransformation>(dynamic)
.add(ty)
}
}else{
world.new_entity()
.add(Name(full_name))
.add_child(&parent, node)
.add_tag_if::<DynamicTransformation>(dynamic)
.add(ty)
}
}else{
world.new_entity()
.add(Name(full_name))
.add(node)
.add_tag_if::<DynamicTransformation>(dynamic)
.add(ty)
};
let rotation: Rotation = blender_obj.local_transformation().rotation.into();
builder
.add(rotation)
.add(components::ObjectId(blender_obj.name().clone()))
}
fn build_enitity<'s>(node: idtree::NodeId,
scene: &'s rinblender::SceneData,
geometries_index: &HashMap<&'s ObjectId, Entity>,
materials_index: &HashMap<&'s ObjectId, MaterialRef>,
doublesided_materials_index: &HashMap<&'s ObjectId, MaterialRef>,
world: &mut CreationProxy,
parent: Option<rinecs::Entity>,
dynamic: bool,
prefix: &str) -> (rinecs::Entity, Ty)
{
let arena = &scene.arena;
let blender_obj = arena[node].data();
let node = Node::from_blender(blender_obj.local_transformation());
let ty = Ty::from_blender(blender_obj);
let e = match *blender_obj{
rinblender::BlenderObject::Armature(ref armature) => {
log::trace!("Creating armature {}", armature.name());
let skeleton = common_entity(
world,
blender_obj,
node,
ty,
parent,
true,
prefix
).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 node = &blender_bones[node];
let parent = bone_parent_stack.back()
.and_then(|p| world.component_for(p));
let bone = Bone::from_blender((node.data(), prefix, parent));
let bone_entity;
let parent_trafo = trafo_parent_stack.back().unwrap().clone();
{
let rotation: Rotation = node.transformations().rotation.into();
let bone_builder = world.new_entity()
.add_child(&skeleton, Node::from_blender(node.data()))
.add(Ty::Bone)
.add(SkeletonRef(skeleton.clone()))
.add(rotation);
bone_entity = if let Some(parent_bone) = bone_parent_stack.back(){
bone_builder.add_child(parent_bone, bone)
.add_child(parent_bone, BoneName(node.name().to_owned()))
.add(Name(node.name().to_owned()))
.build()
}else{
bone_builder.add(bone)
.add(BoneName(node.name().to_owned()))
.add(Name(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();
}
}
}
}
if let Some(default) = armature.default_action(){
let default = scene.actions[default].clone();
world.add_component_to(&skeleton, Action::new(default));
}
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::new(mesh));
world.add_component_to(&skeleton, world.find_material(SKELETON_MATERIAL).unwrap());
}
skeleton
}
rinblender::BlenderObject::Model(ref model) => {
let e = if let Some(mesh) = geometries_index.get(model.mesh()) {
let trimesh = &scene.trimeshes[&model.mesh()];
let doublesided = scene.meshes[&model.mesh()]
.flag
.contains(rinblender::mesh::Flag::TWOSIDED);
log::trace!("Creating model {} with mesh {} {}",
model.name(),
model.mesh(),
if doublesided {"doublesided"} else {""});
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 {
if doublesided{
mat_name.as_ref()
.map(|mat_name| {
if let Some(mat) = doublesided_materials_index.get(&mat_name) {
*mat
}else{
materials_index[&mat_name]
}
})
}else{
mat_name
.as_ref()
.map(|mat_name| materials_index[&mat_name])
}
}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 has_skeleton = model.skeleton().is_some();
let skeleton_name = model.skeleton().map(|skeleton| SkeletonName(skeleton.clone()));
let (animated_geom, weights, skinning_uptodate, original_geom_buffer) = if model.skeleton().is_some() || shapekey.is_some(){
let (geom, weights) = to_animated_geometry(scene.trimodel(model.name()).unwrap());
let usage = if shapekey.is_some() {
gl::STREAM_DRAW
}else{
gl::STATIC_DRAW
};
let original_geom_buffer = world.resource_thread_local::<gl::Renderer>().unwrap()
.new_shared_buffer()
.from_data_target(&geom.geom, usage, gl::SHADER_STORAGE_BUFFER)
.unwrap();
let original_geom_buffer = OriginalGeometryBuffer(original_geom_buffer);
(Some(geom), Some(weights), Some(SkinningUpToDate(false)), Some(original_geom_buffer))
}else{
(None, None, None, None)
};
let selectable = components::Selectable(model.is_selectable());
let rigidbody_shape_and_offset = model.rigid_body()
.and_then(|rb| {
let shape_ty = RigidBodyShape::from_blender(&rb.shape);
let mesh = world
.component_for::<Geometry<Vertex>>(mesh)
.unwrap();
let shape_offset = rigidbody::geometry_shape_and_offset(&mesh, shape_ty);
if shape_offset.is_none() {
log::error!(
"Couldnt create rigid body shape of type {:?} for {}",
shape_ty,
model.name()
);
}
shape_offset
});
let (rigidbody_shape, rigidbody_offset) = match rigidbody_shape_and_offset{
Some((shape, offset)) => (Some(shape), Some(offset)),
None => (None, None)
};
let builder = common_entity(
world,
blender_obj,
node,
ty,
parent,
dynamic || has_skeleton,
prefix
);
builder
.add(VertexGroups::from_blender(model))
.add(Visible::new(model.is_visible()))
.add_slice(materials)
.add(GeometryRef::new(*mesh))
.add_option(rigidbody_shape)
.add_option(rigidbody_offset)
.add_option(shapekey)
.add_tag_if::<DynamicTransformation>(has_skeleton)
.add_option(skeleton_name)
.add_option(animated_geom)
.add_option(weights)
.add_option(skinning_uptodate)
.add_thread_local_option(original_geom_buffer)
.add(selectable)
}else{
log::trace!("Couldn't find materials for model {} in preloaded meshes. Adding as an empty", model.name());
common_entity(world, blender_obj, node, ty, parent, dynamic, prefix)
}
}else{
log::trace!("Couldn't find mesh {} for model {} in preloaded meshes. Probably empty.", model.mesh(), model.name());
common_entity(world, blender_obj, node, ty, parent, dynamic, 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) => {
log::trace!("Loading light {}", light.name);
let shadow_params = if light.shadowmap_type != rinblender::ShadowMapType::No {
Some(shadow::Parameters::from_blender(light))
}else{
None
};
#[cfg(feature="debug_geometry")]
let debug_geom;
#[cfg(feature="debug_geometry")]
let material;
let mut directional = None;
let mut point = None;
let mut spot = None;
match light.ty {
rinblender::LightType::Directional => {
let radius = clamp((light.size.atan() / Deg(8.).to_rad().value()).powf(2.) * 1.5, 0.01, 1.5);
let light = DirectionalLight::from_components(
light.color.c,
radius,
light.strength,
);
#[cfg(feature="debug_geometry")]
{
let static_shadow_params = shadow_params.as_ref().map(|s| vec![s]);
let shadow_params = shadow_params.as_ref().map(|s| vec![s]);
debug_geom = Some(light.debug_geometry(shadow_params, static_shadow_params));
material = Some(DirectionalLight::debug_material(world));
}
directional = Some(light);
}
rinblender::LightType::Spot => {
#[cfg(feature="debug_geometry")]
{
debug_geom = None;
material = None;
}
spot = Some(SpotLight::from_components(
light.size,
light.color.c,
light.strength,
light.angle / 2.,
light.spot_blend,
));
}
rinblender::LightType::Point => {
#[cfg(feature="debug_geometry")]
{
debug_geom = None;
material = None;
}
point = Some(PointLight::from_components(
light.color.c,
light.size,
light.strength,
));
}
_ => {
log::error!("Scene contains a not supported light type {:?}", ty);
#[cfg(feature="debug_geometry")]
{
debug_geom = None;
material = None;
}
}
}
#[cfg(feature="debug_geometry")]
let debug_geom = debug_geom.map(|debug_geom| {
let debug_geom = world.register_mesh(debug_geom);
DebugGeometryRef::new(debug_geom, material.unwrap())
});
let mut builder = common_entity(
world,
blender_obj,
node.clone(),
ty,
parent,
dynamic,
prefix
);
builder = builder.add(Light)
.add(Visible::new(light.visible))
.add_option(directional)
.add_option(spot)
.add_option(point);
let shadow_type = if light.shadow_contact{
shadow::Type::PCSS
}else{
shadow::Type::Gaussian5
};
builder = builder
.add_option(shadow_params.as_ref().map(|_| shadow_type))
.add_option(shadow_params.as_ref().map(|params| shadow::Map::new(params.clone())))
.add_option(shadow_params.map(|params| shadow::StaticMap::new(params)));
#[cfg(feature="debug_geometry")]
{
builder = builder.add_option(debug_geom);
}
builder.build()
}
rinblender::BlenderObject::Empty(ref empty) => {
log::trace!("Loading empty {}", prefix);
common_entity(world, blender_obj, node, ty, parent, dynamic, 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(node: idtree::NodeId,
scene: &rinblender::SceneData,
geometries_index: &HashMap<&ObjectId, Entity>,
materials_index: &HashMap<&ObjectId, MaterialRef>,
doublesided_materials_index: &HashMap<&ObjectId, MaterialRef>,
world: &mut CreationProxy,
parent: rinecs::Entity,
dynamic: bool,
skeletons: &mut Vec<Entity>,
models: &mut Vec<Entity>,
lights: &mut Vec<Entity>,
empties: &mut Vec<Entity>,
prefix: &str)
{
for child in node.children(&scene.arena){
let (e, ty) = build_enitity(
child,
scene,
geometries_index,
materials_index,
doublesided_materials_index,
world,
Some(parent),
dynamic,
prefix);
match ty{
Ty::Armature => skeletons.push(e),
Ty::Model => models.push(e),
Ty::Light => lights.push(e),
Ty::Empty => empties.push(e),
_ => unreachable!()
}
parse_children(
child,
scene,
geometries_index,
materials_index,
doublesided_materials_index,
world,
e,
dynamic,
skeletons,
models,
lights,
empties,
prefix);
}
}
pub struct BlenderMaterials<'a> {
alpha_type: AlphaType,
world: CreationProxy<'a>,
use_post_fragment_materials: bool,
}
impl<'a> crate::Builder<'a> for BlenderMaterials<'a> {
type Settings = ();
type IdType = ();
type Depends = ();
fn register(_: &mut World){
}
fn new(world: CreationProxy<'a>, _: ()) -> BlenderMaterials<'a> {
BlenderMaterials{
world,
alpha_type: AlphaType::None,
use_post_fragment_materials: false,
}
}
}
impl<'a> BlenderMaterials<'a> {
pub fn alpha_type(&mut self, alpha_type: AlphaType) -> &mut BlenderMaterials<'a> {
self.alpha_type = alpha_type;
self
}
pub fn post_fragment_materials(&mut self) -> &mut BlenderMaterials<'a> {
self.use_post_fragment_materials = true;
self
}
pub fn from_scene_data<P: AsRef<Path>>(&mut self, scene: &rinblender::SceneData, path: P) -> Result<(HashMap<ObjectId, MaterialRef>, HashMap<ObjectId, MaterialRef>)>{
let mut blender = Blender::new(self.world.clone(), ());
let mut blender = blender.alpha_type(self.alpha_type);
if self.use_post_fragment_materials {
blender = blender.post_fragment_materials()
}
let textures = blender.load_textures(&scene, path.as_ref());
let (materials, doublesided_materials) = blender.load_materials(&scene, path, &textures);
let materials = materials.into_iter()
.map(|(n,m)| (n.to_owned(), m))
.collect();
let doublesided_materials = doublesided_materials.into_iter()
.map(|(n,m)| (n.to_owned(), m))
.collect();
Ok((materials, doublesided_materials))
}
pub fn load<P: AsRef<Path>>(&mut self, path: P) -> Result<(HashMap<ObjectId, MaterialRef>, HashMap<ObjectId, MaterialRef>)>{
let file = rinblender::File::load(path.as_ref())
.map_err(|err| Error::with_cause("Error loading blender file", err))?;
let scene = rinblender::SceneData::load(&file);
self.from_scene_data(&scene, path)
}
}
use rinecs::ReadRef;
use rin_scene::physics::{Shape, Offset};
use rin_math::{Vec4, Mat4, ToPnt, pnt3, Pnt3, vec3, Isometry3};
use ncollide3d::shape::*;
use ncollide3d::bounding_volume::{AABB, BoundingSphere, BoundingVolume};
use rin_math::Deg;
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
use rin_math::vec4;
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline]
unsafe fn to_mm_ps(v: &Vec4) -> __m128 {
_mm_set_ps(v.w, v.z, v.y, v.x)
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline]
fn components_min4(a: &Vec4, b: &Vec4) -> Vec4{
unsafe{
let r = _mm_min_ps(to_mm_ps(a), to_mm_ps(b));
*(&r as *const __m128 as *const Vec4)
}
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
#[inline]
fn components_min4(a: &Vec4, b: &Vec4) -> Vec4{
vec4!(a.x.min(b.x), a.y.min(b.y), a.z.min(b.z), a.w.max(b.w))
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline]
fn components_max4(a: &Vec4, b: &Vec4) -> Vec4{
unsafe{
let r = _mm_max_ps(to_mm_ps(a), to_mm_ps(b));
*(&r as *const __m128 as *const Vec4)
}
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
#[inline]
fn components_max4(a: &Vec4, b: &Vec4) -> Vec4{
vec4!(a.x.max(b.x), a.y.max(b.y), a.z.max(b.z), a.w.max(b.w))
}
fn affine_transform_aabb_by(aabb: &AABB<f32>, m: &Mat4) -> AABB<f32>{
let xa = m.column(0) * aabb.mins.x;
let xb = m.column(0) * aabb.maxs.x;
let ya = m.column(1) * aabb.mins.y;
let yb = m.column(1) * aabb.maxs.y;
let za = m.column(2) * aabb.mins.z;
let zb = m.column(2) * aabb.maxs.z;
AABB::new(
(components_min4(&xa, &xb)
+ components_min4(&ya, &yb)
+ components_min4(&za, &zb)
+ m.column(3)).xyz().to_pnt(),
(components_max4(&xa, &xb)
+ components_max4(&ya, &yb)
+ components_max4(&za, &zb)
+ m.column(3)).xyz().to_pnt(),
)
}
fn affine_transform_bounding_sphere_by(bounding_sphere: &BoundingSphere<f32>, m: &Mat4) -> BoundingSphere<f32>{
let scale = vec3(
m.column(0).norm(),
m.column(1).norm(),
m.column(2).norm(),
);
let max_scale = scale.x.max(scale.y).max(scale.z);
let radius = max_scale * bounding_sphere.radius();
let center = (m * bounding_sphere.center().to_homogeneous()).xyz().to_pnt();
BoundingSphere::new(center, radius)
}
fn merge_aabbs(aabb1: &AABB<f32>, aabb2: &AABB<f32>) -> AABB<f32> {
let mins = pnt3(
aabb1.mins.x.min(aabb2.mins.x),
aabb1.mins.y.min(aabb2.mins.y),
aabb1.mins.z.min(aabb2.mins.z),
);
let maxs = pnt3(
aabb1.maxs.x.max(aabb2.maxs.x),
aabb1.maxs.y.max(aabb2.maxs.y),
aabb1.maxs.z.max(aabb2.maxs.z),
);
AABB::new(mins, maxs)
}
fn scene_index_aabb<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<AABB<f32>>
where
M: IntoIterator<Item = &'e Entity>
{
let inverse = trafo.inv_global_transformation();
let geom_aabb = |trafo: &Node, visible: &Visible, geom: &Geometry<Vertex>|{
if (visibles_only && !visible.is_visible()) || geom.vertices().is_empty(){
return None
}
let trafo = inverse * trafo.global_transformation();
let aabb = rigidbody::geometry_aabb(geom.vertices().iter())?;
Some(affine_transform_aabb_by(&aabb, &trafo))
};
let models = entities.iter_for_entities::<(
Read<Node>,
Read<Visible>,
ReadRef<GeometryRef, Geometry<Vertex>>), _>(models)
.filter_map(|(trafo, visible, geom)| geom_aabb(trafo, visible, geom));
let mut total_aabb: Option<AABB<f32>> = None;
for aabb in models {
if let Some(total_aabb) = total_aabb.as_mut() {
*total_aabb = total_aabb.merged(&aabb)
}else{
total_aabb = Some(aabb)
}
}
total_aabb
}
fn scene_index_bounding_sphere<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<BoundingSphere<f32>>
where
M: IntoIterator<Item = &'e Entity>
{
let inverse = trafo.inv_global_transformation();
let geom_bounding_sphere = |trafo: &Node, visible: &Visible, geom: &Geometry<Vertex>|{
if (visibles_only && !visible.is_visible()) || geom.vertices().is_empty(){
return None
}
let trafo = inverse * trafo.global_transformation();
let bounding_sphere = rigidbody::geometry_bounding_sphere(geom)?;
Some(affine_transform_bounding_sphere_by(&bounding_sphere, &trafo))
};
let models = entities.iter_for_entities::<(
Read<Node>,
Read<Visible>,
ReadRef<GeometryRef, Geometry<Vertex>>), _>(models.into_iter())
.filter_map(|(trafo, visible, geom)| geom_bounding_sphere(trafo, visible, geom));
let mut total_bounding_sphere: Option<BoundingSphere<f32>> = None;
for bounding_sphere in models {
if let Some(total_bounding_sphere) = total_bounding_sphere.as_mut() {
*total_bounding_sphere = total_bounding_sphere.merged(&bounding_sphere)
}else{
total_bounding_sphere = Some(bounding_sphere)
}
}
total_bounding_sphere
}
fn scene_index_box_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
let aabb = scene_index_aabb(entities, trafo, models, visibles_only)?;
Some(rigidbody::aabb_to_box_and_offset(&aabb))
}
fn scene_index_capsule_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
let aabb = scene_index_aabb(entities, trafo, models, visibles_only)?;
Some(rigidbody::aabb_to_capsule_and_offset(&aabb))
}
fn scene_index_sphere_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
let bounding_sphere = scene_index_bounding_sphere(entities, trafo, models, visibles_only)?;
let radius = bounding_sphere.radius();
let ball = Ball::new(radius);
let center = bounding_sphere.center();
let offset = Isometry3::new(
center.to_vec(),
vec3(0., 0.,0.)
);
Some((Shape::new(ball), Offset(offset)))
}
fn scene_index_trimesh<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> (Vec<Pnt3>, Vec<usize>)
where
M: IntoIterator<Item = &'e Entity>
{
let inverse = trafo.inv_global_transformation();
let mut points = vec![];
let mut indices = vec![];
for(trafo, visible, geom) in entities.iter_for_entities::<(
Read<Node>,
Read<Visible>,
ReadRef<GeometryRef, Geometry<Vertex>>), _>(models.into_iter())
{
if (visibles_only && !visible.is_visible()) || geom.vertices().is_empty(){
continue
}
let trafo = inverse * trafo.global_transformation();
let index_offset = points.len();
points.extend(geom.vertices()
.iter()
.map(|v| (trafo * v.position).xyz().to_pnt()));
if geom.indices().is_empty(){
indices.extend((0..geom.vertices().len()).map(|i| i + index_offset));
}else{
indices.extend(geom.indices().iter().map(|i| *i as usize + index_offset));
}
}
(points, indices)
}
fn scene_index_convexhull_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
let (points, indices) = scene_index_trimesh(
entities,
trafo,
models,
visibles_only
);
let convex_hull = ConvexHull::try_new(points, &indices)?;
Some((Shape::new(convex_hull), Offset(one())))
}
fn scene_index_trimesh_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
let (points, indices) = scene_index_trimesh(
entities,
trafo,
models,
visibles_only
);
let indices = indices
.chunks(3)
.map(|face| pnt3(face[0], face[1], face[2]))
.collect();
let trimesh = TriMesh::new(points, indices, None);
Some((Shape::new(trimesh), Offset(one())))
}
fn scene_index_shape_and_offset<'a, 'e, M>(
entities: &CreationProxy<'a>,
trafo: &Node,
models: M,
visibles_only: bool,
shape: RigidBodyShape) -> Option<(Shape, Offset)>
where
M: IntoIterator<Item = &'e Entity>
{
match shape {
RigidBodyShape::Capsule => scene_index_capsule_and_offset(entities, trafo, models, visibles_only),
RigidBodyShape::ConvexHull => scene_index_convexhull_and_offset(entities, trafo, models, visibles_only),
RigidBodyShape::Cuboid => scene_index_box_and_offset(entities, trafo, models, visibles_only),
RigidBodyShape::Mesh => scene_index_trimesh_and_offset(entities, trafo, models, visibles_only),
RigidBodyShape::Sphere => scene_index_sphere_and_offset(entities, trafo, models, visibles_only),
RigidBodyShape::Cone => unimplemented!("Cone shape not supported yet"),
RigidBodyShape::Cylinder => unimplemented!("Cylinder shape not supported yet"),
}
}