use color::{Rgba, color_space::LinearRgb};
use blender;
use crate::utils::{self, ObjectId, LibraryId};
use angle::{Deg, Rad, Angle};
use crate::utils::Property;
#[repr(u16)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
pub enum ShadowMapType{
No = 3,
Simple = 0,
Variance = 1,
Exponential = 2,
}
#[repr(u16)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
pub enum LightType {
Point = 0,
Directional = 1,
Spot = 2,
Hemi = 3,
Area = 4,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Lamp{
pub name: ObjectId,
pub ty: LightType,
pub transformations: utils::Transformations,
pub color: Rgba<f32, LinearRgb>,
pub strength: f32,
pub att_constant: f32,
pub att_linear: f32,
pub att_quadratic: f32,
pub size: f32,
pub angle: Deg<f32>,
pub spot_blend: f32,
pub shadowmap_type: ShadowMapType,
pub shadow_frustum_size: Option<f32>,
pub shadow_bufsize: u16,
pub shadow_near_clip: f32,
pub shadow_far_clip: Option<f32>,
pub shadow_bias: f32,
pub shadow_contact: bool,
pub custom_properties: Vec<Property>,
pub visible: bool,
}
bitflags! {
struct LampMode: u32 {
const LA_SHAD_BUF = (1 << 0);
const LA_HALO = (1 << 1);
const LA_LAYER = (1 << 2);
const LA_QUAD = (1 << 3) ;
const LA_NEG = (1 << 4);
const LA_ONLYSHADOW = (1 << 5);
const LA_SPHERE = (1 << 6);
const LA_SQUARE = (1 << 7);
const LA_TEXTURE = (1 << 8);
const LA_OSATEX = (1 << 9);
const LA_NO_DIFF = (1 << 11);
const LA_NO_SPEC = (1 << 12);
const LA_SHAD_RAY = (1 << 13);
const LA_LAYER_SHADOW = (1 << 15);
const LA_SHAD_TEX = (1 << 16);
const LA_SHOW_CONE = (1 << 17);
const LA_SHOW_SHADOW_BOX = (1 << 18);
const LA_SHAD_CONTACT = (1 << 19);
}
}
pub fn parse(obj: &blender::Object, library_id: LibraryId, visible: bool, scene_eevee: Option<&blender::Object>) -> Option<Lamp>{
if let Ok(lightdata) = obj.get_object("data"){
let lighttype = *lightdata.get("type").unwrap();
let transformations = utils::transformations(&obj);
let name = obj.name().unwrap();
match lighttype{
LightType::Point => {
Some(parse_point_light_nodes(&lightdata, library_id, name, transformations, visible, scene_eevee))
}
LightType::Directional => {
Some(parse_directional_light_nodes(&lightdata, library_id, name, transformations, visible, scene_eevee))
}
LightType::Spot => {
Some(parse_spot_light_nodes(&lightdata, library_id, name, transformations, visible, scene_eevee))
}
_ => {
None
}
}
}else{
None
}
}
fn shadowmap_type(lightdata: &blender::Object, eevee: Option<&blender::Object>) -> ShadowMapType{
let mode: LampMode = *lightdata.get("mode").unwrap();
if mode.contains(LampMode::LA_SHAD_BUF)
|| mode.contains(LampMode::LA_SHAD_RAY)
|| mode.contains(LampMode::LA_SHAD_TEX)
{
if let Some(eevee) = eevee{
match *eevee.get::<i32>("shadow_method").unwrap(){
1 => ShadowMapType::Exponential,
2 => ShadowMapType::Variance,
3 => ShadowMapType::Simple,
_ => unreachable!("Unkown shadow map type")
}
}else{
*lightdata.get("shadowmap_type").unwrap()
}
}else{
ShadowMapType::No
}
}
fn parse_point_light_nodes(
lightdata: &blender::Object,
library_id: LibraryId,
name: &str,
transformations: utils::Transformations,
visible: bool,
eevee: Option<&blender::Object>) -> Lamp
{
let mut color = rgba_linear!(1.,1.,1.,1.);
let mut strength = 1.;
let mut att_constant = 1.;
let mut att_linear = 0.;
let mut att_quadratic = 0.1 / strength;
let custom_properties = utils::custom_properties(lightdata);
if let Ok(nodetree) = lightdata.get_object("nodetree"){
if let Some(emission) = nodetree.get_list("nodes").unwrap().iter().find(|node| node.name().unwrap() == "Emission"){
if let Some(strength_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Strength"){
if let Ok(link) = strength_obj.get_object("link"){
if let Ok(from) = link.get_object("fromnode"){
let name = from.name().unwrap();
if name.starts_with("Light Falloff"){
let out = link.get_object("fromsock").unwrap();
let strength_obj = from.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Strength").unwrap();
strength = *strength_obj.get_object("default_value").unwrap().cast_to("bNodeSocketValueFloat").unwrap().get::<f32>("value").unwrap();
if out.name().unwrap() == "Quadratic"{
att_constant = 1.0;
att_linear = 0.0;
att_quadratic = 0.1 / strength;
}else if out.name().unwrap() == "Linear"{
att_constant = 1.0;
att_linear = 0.1 / strength;
att_quadratic = 0.0;
}else if out.name().unwrap() == "Constant"{
att_constant = 0.1 / strength;
att_linear = 0.0;
att_quadratic = 0.0;
}
}
}
}else{
let strength_obj = strength_obj.get_object("default_value").unwrap();
let strength_socket = strength_obj.cast_to("bNodeSocketValueFloat").unwrap();
strength = *strength_socket.get::<f32>("value").unwrap();
att_constant = 1.0;
att_linear = 0.0;
att_quadratic = 0.1 / strength;
}
}
if let Some(color_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Color"){
if let Ok(link) = color_obj.get_object("link"){
if let Ok(from) = link.get_object("fromnode"){
let name = from.get_str("idname").unwrap();
if name == "ShaderNodeBlackbody"{
let temperature = from.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Temperature").unwrap();
let temperature = *temperature.get_object("default_value").unwrap().cast_to("bNodeSocketValueFloat").unwrap().get::<f32>("value").unwrap();
color = rgba_linear!(utils::blackbody(temperature), 1.0);
}
}
}else{
let color_obj = color_obj.get_object("default_value").unwrap();
let color_socket = color_obj.cast_to("bNodeSocketValueRGBA").unwrap();
let color_arr = color_socket.get_slice::<f32>("value").unwrap();
color = rgba_linear!(color_arr[0], color_arr[1], color_arr[2], color_arr[3]);
}
}
}
}else{
let r = *lightdata.get::<f32>("r").unwrap();
let g = *lightdata.get("g").unwrap();
let b = *lightdata.get("b").unwrap();
let a = *lightdata.get("k").unwrap();
if let Ok(energy) = lightdata.get("energy"){
strength = *energy;
}
color = rgba_linear!(r, g, b, a);
}
Lamp{
name: ObjectId::new(library_id, name),
ty: LightType::Point,
transformations,
color,
strength,
att_constant,
att_linear,
att_quadratic,
size: *lightdata.get("area_size").unwrap(),
angle: Deg(0.),
spot_blend: 0.,
shadowmap_type: shadowmap_type(lightdata, eevee),
shadow_frustum_size: lightdata.get::<f32>("shadow_frustum_size").map(|size| *size * 2.).ok(),
shadow_bufsize: eevee
.map(|eevee| *eevee.get::<i32>("shadow_cascade_size").unwrap() as u16)
.unwrap_or_else(|| *lightdata.get("bufsize").unwrap()),
shadow_near_clip: *lightdata.get("clipsta").unwrap(),
shadow_far_clip: None,
shadow_bias: *lightdata.get("bias").unwrap(),
shadow_contact: lightdata.get::<LampMode>("mode").unwrap().contains(LampMode::LA_SHAD_CONTACT),
custom_properties,
visible,
}
}
fn parse_spot_light_nodes(
lightdata: &blender::Object,
library_id: LibraryId,
name: &str,
transformations: utils::Transformations,
visible: bool,
eevee: Option<&blender::Object>) -> Lamp
{
let mut color = rgba_linear!(1.,1.,1.,1.);
let mut strength = 1.;
let mut att_constant = 1.;
let mut att_linear = 0.;
let mut att_quadratic = 0.1 / strength;
let custom_properties = utils::custom_properties(lightdata);
if let Ok(nodetree) = lightdata.get_object("nodetree"){
if let Some(emission) = nodetree.get_list("nodes").unwrap().iter().find(|node| node.name().unwrap() == "Emission"){
if let Some(strength_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Strength"){
if let Ok(link) = strength_obj.get_object("link"){
if let Ok(from) = link.get_object("fromnode"){
let name = from.name().unwrap();
if name.starts_with("Light Falloff"){
let out = link.get_object("fromsock").unwrap();
let strength_obj = from.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Strength").unwrap();
strength = *strength_obj.get_object("default_value").unwrap().cast_to("bNodeSocketValueFloat").unwrap().get::<f32>("value").unwrap();
if out.name().unwrap() == "Quadratic"{
att_constant = 1.0;
att_linear = 0.0;
att_quadratic = 0.1 / strength;
}else if out.name().unwrap() == "Linear"{
att_constant = 1.0;
att_linear = 0.1 / strength;
att_quadratic = 0.0;
}else if out.name().unwrap() == "Constant"{
att_constant = 0.1 / strength;
att_linear = 0.0;
att_quadratic = 0.0;
}
}
}
}else{
let strength_obj = strength_obj.get_object("default_value").unwrap();
let strength_socket = strength_obj.cast_to("bNodeSocketValueFloat").unwrap();
strength = *strength_socket.get::<f32>("value").unwrap();
att_constant = 1.0;
att_linear = 0.0;
att_quadratic = 0.1 / strength;
}
}
if let Some(color_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Color"){
if let Ok(link) = color_obj.get_object("link"){
if let Ok(from) = link.get_object("fromnode"){
let name = from.get_str("idname").unwrap();
if name == "ShaderNodeBlackbody"{
let temperature = from.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Temperature").unwrap();
let temperature = *temperature.get_object("default_value").unwrap().cast_to("bNodeSocketValueFloat").unwrap().get::<f32>("value").unwrap();
color = rgba_linear!(utils::blackbody(temperature), 1.0);
}
}
}else{
let color_obj = color_obj.get_object("default_value").unwrap();
let color_socket = color_obj.cast_to("bNodeSocketValueRGBA").unwrap();
let color_arr = color_socket.get_slice::<f32>("value").unwrap();
color = rgba_linear!(color_arr[0], color_arr[1], color_arr[2], color_arr[3]);
}
}
}
}else{
let r = *lightdata.get::<f32>("r").unwrap();
let g = *lightdata.get("g").unwrap();
let b = *lightdata.get("b").unwrap();
let a = *lightdata.get("k").unwrap();
if let Ok(energy) = lightdata.get("energy"){
strength = *energy;
}
color = rgba_linear!(r, g, b, a);
}
Lamp{
name: ObjectId::new(library_id, name),
ty: LightType::Spot,
transformations,
color,
strength,
att_constant,
att_linear,
att_quadratic,
size: *lightdata.get("area_size").unwrap(),
angle: Rad(*lightdata.get("spotsize").unwrap()).to_deg(),
spot_blend: *lightdata.get("spotblend").unwrap(),
shadowmap_type: shadowmap_type(lightdata, eevee),
shadow_frustum_size: lightdata.get::<f32>("shadow_frustum_size").map(|size| *size * 2.).ok(),
shadow_bufsize: eevee
.map(|eevee| *eevee.get::<i32>("shadow_cascade_size").unwrap() as u16)
.unwrap_or_else(|| *lightdata.get("bufsize").unwrap()),
shadow_near_clip: *lightdata.get("clipsta").unwrap(),
shadow_far_clip: None,
shadow_bias: *lightdata.get("bias").unwrap(),
shadow_contact: lightdata.get::<LampMode>("mode").unwrap().contains(LampMode::LA_SHAD_CONTACT),
custom_properties,
visible,
}
}
fn parse_directional_light_nodes(
lightdata: &blender::Object,
library_id: LibraryId,
name: &str,
transformations: utils::Transformations,
visible: bool,
eevee: Option<&blender::Object>) -> Lamp
{
let mut color = rgba_linear!(1.,1.,1.,1.);
let mut strength = 1.;
let att_constant = 1.;
let att_linear = 0.;
let att_quadratic = 0.1 / strength;
let custom_properties = utils::custom_properties(lightdata);
if let Ok(nodetree) = lightdata.get_object("nodetree"){
if let Some(emission) = nodetree.get_list("nodes").unwrap().iter().find(|node| node.name().unwrap() == "Emission"){
if let Some(strength_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Strength"){
let strength_obj = strength_obj.get_object("default_value").unwrap();
let strength_socket = strength_obj.cast_to("bNodeSocketValueFloat").unwrap();
strength = *strength_socket.get("value").unwrap();
}
if let Some(color_obj) = emission.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Color"){
if let Ok(link) = color_obj.get_object("link"){
if let Ok(from) = link.get_object("fromnode"){
let name = from.get_str("idname").unwrap();
if name == "ShaderNodeBlackbody"{
let temperature = from.get_list("inputs").unwrap().iter().find(|input| input.name().unwrap() == "Temperature").unwrap();
let temperature = *temperature.get_object("default_value").unwrap().cast_to("bNodeSocketValueFloat").unwrap().get::<f32>("value").unwrap();
color = rgba_linear!(utils::blackbody(temperature), 1.0);
}else{
trace!("found color node with unkown name {}", name);
}
}
}else{
let color_obj = color_obj.get_object("default_value").unwrap();
let color_socket = color_obj.cast_to("bNodeSocketValueRGBA").unwrap();
let color_arr = color_socket.get_slice::<f32>("value").unwrap();
color = rgba_linear!(color_arr[0], color_arr[1], color_arr[2], color_arr[3]);
}
}
}
}else{
let r = *lightdata.get::<f32>("r").unwrap();
let g = *lightdata.get("g").unwrap();
let b = *lightdata.get("b").unwrap();
let a = *lightdata.get("k").unwrap();
color = rgba_linear!(r, g, b, a);
}
Lamp{
name: ObjectId::new(library_id, name),
ty: LightType::Directional,
transformations,
color,
strength,
att_constant,
att_linear,
att_quadratic,
size: if let Ok(sun_angle) = lightdata.get::<f32>("sun_angle"){
(sun_angle.min(Deg(179.9).to_rad().value()) / 2.).tan().max(0.01)
}else{
*lightdata.get("area_size").unwrap()
},
angle: Deg(0.),
spot_blend: 0.,
shadowmap_type: shadowmap_type(lightdata, eevee),
shadow_frustum_size: lightdata.get::<f32>("shadow_frustum_size").map(|size| *size * 2.).ok(),
shadow_bufsize: eevee
.map(|eevee| *eevee.get::<i32>("shadow_cascade_size").unwrap() as u16)
.unwrap_or_else(|| *lightdata.get("bufsize").unwrap()),
shadow_near_clip: *lightdata.get("clipsta").unwrap(),
shadow_far_clip: Some(*lightdata.get("clipend").unwrap()),
shadow_bias: *lightdata.get("bias").unwrap(),
shadow_contact: lightdata.get::<LampMode>("mode").unwrap().contains(LampMode::LA_SHAD_CONTACT),
custom_properties,
visible
}
}