Blackbody colors are now parsed, and PsychoBlend has support as well.

This commit is contained in:
Nathan Vegdahl 2018-12-28 00:23:02 -08:00
parent caa4ea3e44
commit 53754b956c
7 changed files with 161 additions and 74 deletions

View File

@ -77,6 +77,19 @@ class PsychopathCamera(bpy.types.PropertyGroup):
min=0.0, max=10000.0, soft_min=0.0, soft_max=2.0, default=0.0
)
# Psychopath material
class PsychopathLight(bpy.types.PropertyGroup):
color_type = EnumProperty(
name="Color Type", description="",
items=[('Rec709', 'Rec709', ""), ('Blackbody', 'Blackbody', "")],
default="Rec709"
)
color_blackbody_temp = FloatProperty(
name="Temperature", description="Blackbody temperature in kelvin",
min=0.0, max=32000.0, soft_min=800.0, soft_max=6500.0, default=1200.0
)
# Custom Mesh properties
class PsychopathMesh(bpy.types.PropertyGroup):
is_subdivision_surface = BoolProperty(
@ -88,10 +101,16 @@ class PsychopathMesh(bpy.types.PropertyGroup):
class PsychopathMaterial(bpy.types.PropertyGroup):
surface_shader_type = EnumProperty(
name="Surface Shader Type", description="",
items=[('Emit', 'Emit', ""), ('Lambert', 'Lambert', ""), ('GTR', 'GTR', ""), ('GGX', 'GGX', "")],
items=[('Emit', 'Emit', ""), ('Lambert', 'Lambert', ""), ('GGX', 'GGX', "")],
default="Lambert"
)
color_type = EnumProperty(
name="Color Type", description="",
items=[('Rec709', 'Rec709', ""), ('Blackbody', 'Blackbody', "")],
default="Rec709"
)
color = FloatVectorProperty(
name="Color", description="",
subtype='COLOR',
@ -99,6 +118,11 @@ class PsychopathMaterial(bpy.types.PropertyGroup):
default=[0.8,0.8,0.8]
)
color_blackbody_temp = FloatProperty(
name="Temperature", description="Blackbody temperature in kelvin",
min=0.0, max=32000.0, soft_min=800.0, soft_max=6500.0, default=1200.0
)
roughness = FloatProperty(
name="Roughness", description="",
min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.1
@ -135,10 +159,12 @@ def register():
bpy.utils.register_class(PsychopathPreferences)
bpy.utils.register_class(RenderPsychopathSettingsScene)
bpy.utils.register_class(PsychopathCamera)
bpy.utils.register_class(PsychopathLight)
bpy.utils.register_class(PsychopathMesh)
bpy.utils.register_class(PsychopathMaterial)
bpy.types.Scene.psychopath = PointerProperty(type=RenderPsychopathSettingsScene)
bpy.types.Camera.psychopath = PointerProperty(type=PsychopathCamera)
bpy.types.Lamp.psychopath = PointerProperty(type=PsychopathLight)
bpy.types.Mesh.psychopath = PointerProperty(type=PsychopathMesh)
bpy.types.Material.psychopath = PointerProperty(type=PsychopathMaterial)
render.register()
@ -149,10 +175,12 @@ def unregister():
bpy.utils.unregister_class(PsychopathPreferences)
bpy.utils.unregister_class(RenderPsychopathSettingsScene)
bpy.utils.unregister_class(PsychopathCamera)
bpy.utils.unregister_class(PsychopathLight)
bpy.utils.unregister_class(PsychopathMesh)
bpy.utils.unregister_class(PsychopathMaterial)
del bpy.types.Scene.psychopath
del bpy.types.Camera.psychopath
del bpy.types.Lamp.psychopath
del bpy.types.Mesh.psychopath
del bpy.types.Material.psychopath
render.unregister()

View File

@ -213,7 +213,12 @@ class SphereLamp:
def take_sample(self, render_engine, scene, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
self.time_col += [self.ob.data.color * self.ob.data.energy]
if self.ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
elif self.ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
self.time_rad += [self.ob.data.shadow_soft_size]
def cleanup(self):
@ -225,7 +230,10 @@ class SphereLamp:
w.write("SphereLight $%s {\n" % self.name)
w.indent()
for col in self.time_col:
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
for rad in self.time_rad:
w.write("Radius [%f]\n" % rad)
@ -244,7 +252,12 @@ class RectLamp:
def take_sample(self, render_engine, scene, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
self.time_col += [self.ob.data.color * self.ob.data.energy]
if self.ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
elif self.ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
if self.ob.data.shape == 'RECTANGLE':
self.time_dim += [(self.ob.data.size, self.ob.data.size_y)]
else:
@ -259,7 +272,10 @@ class RectLamp:
w.write("RectangleLight $%s {\n" % self.name)
w.indent()
for col in self.time_col:
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
for dim in self.time_dim:
w.write("Dimensions [%f %f]\n" % dim)
@ -314,23 +330,40 @@ class Material:
w.indent()
if self.mat.psychopath.surface_shader_type == 'Emit':
w.write("Type [Emit]\n")
color = self.mat.psychopath.color
w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2]))
if self.mat.psychopath.color_type == 'Rec709':
col = self.mat.psychopath.color
w.write("Color [rec709, %f %f %f]\n" % (
col[0], col[1], col[2],
))
elif self.mat.psychopath.color_type == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (
self.mat.psychopath.color_blackbody_temp,
1.0,
))
elif self.mat.psychopath.surface_shader_type == 'Lambert':
w.write("Type [Lambert]\n")
color = self.mat.psychopath.color
w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2]))
elif self.mat.psychopath.surface_shader_type == 'GTR':
w.write("Type [GTR]\n")
color = self.mat.psychopath.color
w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2]))
w.write("Roughness [%f]\n" % self.mat.psychopath.roughness)
w.write("TailShape [%f]\n" % self.mat.psychopath.tail_shape)
w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
if self.mat.psychopath.color_type == 'Rec709':
col = self.mat.psychopath.color
w.write("Color [rec709, %f %f %f]\n" % (
col[0], col[1], col[2],
))
elif self.mat.psychopath.color_type == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (
self.mat.psychopath.color_blackbody_temp,
1.0,
))
elif self.mat.psychopath.surface_shader_type == 'GGX':
w.write("Type [GGX]\n")
color = self.mat.psychopath.color
w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2]))
if self.mat.psychopath.color_type == 'Rec709':
col = self.mat.psychopath.color
w.write("Color [rec709, %f %f %f]\n" % (
col[0], col[1], col[2],
))
elif self.mat.psychopath.color_type == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (
self.mat.psychopath.color_blackbody_temp,
1.0,
))
w.write("Roughness [%f]\n" % self.mat.psychopath.roughness)
w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
else:

View File

@ -125,7 +125,14 @@ class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
if ob.data.type != 'HEMI' and ob.data.type != 'AREA':
col.prop(ob.data, "shadow_soft_size")
col.prop(ob.data.psychopath, "color_type")
if ob.data.psychopath.color_type == 'Rec709':
col.prop(ob.data, "color")
elif ob.data.psychopath.color_type == 'Blackbody':
col.prop(ob.data.psychopath, "color_blackbody_temp")
col.prop(ob.data, "energy")
@ -239,10 +246,16 @@ class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
def draw(self, context):
layout = self.layout
col = layout.column()
mat = context.material
layout.prop(mat.psychopath, "surface_shader_type")
layout.prop(mat.psychopath, "color")
col.prop(mat.psychopath, "surface_shader_type")
col.prop(mat.psychopath, "color_type")
if mat.psychopath.color_type == 'Rec709':
col.prop(mat.psychopath, "color")
elif mat.psychopath.color_type == 'Blackbody':
col.prop(mat.psychopath, "color_blackbody_temp")
if mat.psychopath.surface_shader_type == 'GTR':
layout.prop(mat.psychopath, "roughness")

View File

@ -116,7 +116,7 @@ class BackgroundShader:
w.write("BackgroundShader {\n")
w.indent();
w.write("Type [Color]\n")
w.write("Color [%f %f %f]\n" % self.color)
w.write("Color [rec709, %f %f %f]\n" % self.color)
w.unindent()
w.write("}\n")
@ -132,7 +132,12 @@ class DistantDiskLamp:
def take_sample(self, render_engine, scene, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
self.time_dir += [tuple(self.ob.matrix_world.to_3x3() * Vector((0, 0, -1)))]
self.time_col += [self.ob.data.color * self.ob.data.energy]
if self.ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
elif self.ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
self.time_rad += [self.ob.data.shadow_soft_size]
def export(self, render_engine, w):
@ -142,7 +147,10 @@ class DistantDiskLamp:
for direc in self.time_dir:
w.write("Direction [%f %f %f]\n" % (direc[0], direc[1], direc[2]))
for col in self.time_col:
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
for rad in self.time_rad:
w.write("Radius [%f]\n" % rad)

View File

@ -499,12 +499,8 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
..
}) = bgs.iter_children_with_type("Color").nth(0)
{
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.trim().as_bytes())
{
// TODO: proper color space management, not just assuming
// rec.709.
background_color = Color::new_xyz(rec709_e_to_xyz(color));
if let Ok(color) = parse_color(contents) {
background_color = color;
} else {
return Err(PsyParseError::IncorrectLeafData(
byte_offset,
@ -582,3 +578,34 @@ pub fn make_transform_format_error(byte_offset: usize) -> PsyParseError {
the form '[# # # # # # # # # # # # # # # #]'.",
)
}
pub fn parse_color(contents: &str) -> Result<Color, PsyParseError> {
let items: Vec<_> = contents.split(",").map(|s| s.trim()).collect();
if items.len() != 2 {
return Err(PsyParseError::UnknownError(0));
}
match items[0] {
"rec709" => {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(items[1].as_bytes())
{
return Ok(Color::new_xyz(rec709_e_to_xyz(color)));
} else {
return Err(PsyParseError::UnknownError(0));
}
}
"blackbody" => {
if let IResult::Done(_, (temperature, factor)) =
closure!(tuple!(ws_f32, ws_f32))(items[1].as_bytes())
{
return Ok(Color::new_blackbody(temperature, factor));
} else {
return Err(PsyParseError::UnknownError(0));
}
}
_ => return Err(PsyParseError::UnknownError(0)),
}
}

View File

@ -12,7 +12,11 @@ use crate::{
math::Vector,
};
use super::{basics::ws_f32, psy::PsyParseError, DataTree};
use super::{
basics::ws_f32,
psy::{parse_color, PsyParseError},
DataTree,
};
pub fn parse_distant_disk_light<'a>(
arena: &'a MemArena,
@ -62,13 +66,8 @@ pub fn parse_distant_disk_light<'a>(
contents,
byte_offset,
} if type_name == "Color" => {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
colors.push(Color::new_xyz(rec709_e_to_xyz(color)));
if let Ok(color) = parse_color(contents) {
colors.push(color);
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));
@ -116,13 +115,8 @@ pub fn parse_sphere_light<'a>(
contents,
byte_offset,
} if type_name == "Color" => {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
colors.push(Color::new_xyz(rec709_e_to_xyz(color)));
if let Ok(color) = parse_color(contents) {
colors.push(color);
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));
@ -172,13 +166,8 @@ pub fn parse_rectangle_light<'a>(
contents,
byte_offset,
} if type_name == "Color" => {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
colors.push(Color::new_xyz(rec709_e_to_xyz(color)));
if let Ok(color) = parse_color(contents) {
colors.push(color);
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));

View File

@ -11,7 +11,11 @@ use crate::{
shading::{SimpleSurfaceShader, SurfaceShader},
};
use super::{basics::ws_f32, psy::PsyParseError, DataTree};
use super::{
basics::ws_f32,
psy::{parse_color, PsyParseError},
DataTree,
};
// pub struct TriangleMesh {
// time_samples: usize,
@ -38,13 +42,8 @@ pub fn parse_surface_shader<'a>(
let color = if let Some((_, contents, byte_offset)) =
tree.iter_leaf_children_with_type("Color").nth(0)
{
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
Color::new_xyz(rec709_e_to_xyz(color))
if let Ok(color) = parse_color(contents) {
color
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));
@ -64,13 +63,8 @@ pub fn parse_surface_shader<'a>(
let color = if let Some((_, contents, byte_offset)) =
tree.iter_leaf_children_with_type("Color").nth(0)
{
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
Color::new_xyz(rec709_e_to_xyz(color))
if let Ok(color) = parse_color(contents) {
color
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));
@ -125,13 +119,8 @@ pub fn parse_surface_shader<'a>(
let color = if let Some((_, contents, byte_offset)) =
tree.iter_leaf_children_with_type("Color").nth(0)
{
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
Color::new_xyz(rec709_e_to_xyz(color))
if let Ok(color) = parse_color(contents) {
color
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError(byte_offset));