Added PsychoBlend exporter to repo.
This commit is contained in:
parent
e8ee371423
commit
43f2a77264
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,6 +1,10 @@
|
||||||
target
|
target
|
||||||
*.rs.bk
|
*.rs.bk
|
||||||
|
|
||||||
|
# Python Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
.zedstate
|
.zedstate
|
||||||
test_renders
|
test_renders
|
||||||
perf.data*
|
perf.data*
|
||||||
|
|
154
psychoblend/__init__.py
Normal file
154
psychoblend/__init__.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
bl_info = {
|
||||||
|
"name": "PsychoBlend",
|
||||||
|
"version": (0, 1),
|
||||||
|
"author": "Nathan Vegdahl",
|
||||||
|
"blender": (2, 70, 0),
|
||||||
|
"description": "Psychopath renderer integration",
|
||||||
|
"location": "",
|
||||||
|
"wiki_url": "https://github.com/cessen/psychopath/wiki",
|
||||||
|
"tracker_url": "https://github.com/cessen/psychopath/issues",
|
||||||
|
"category": "Render"}
|
||||||
|
|
||||||
|
|
||||||
|
if "bpy" in locals():
|
||||||
|
import imp
|
||||||
|
imp.reload(ui)
|
||||||
|
imp.reload(psy_export)
|
||||||
|
imp.reload(render)
|
||||||
|
else:
|
||||||
|
from . import ui, psy_export, render
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy.types import (AddonPreferences,
|
||||||
|
PropertyGroup,
|
||||||
|
Operator,
|
||||||
|
)
|
||||||
|
from bpy.props import (StringProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
EnumProperty,
|
||||||
|
PointerProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Custom Scene settings
|
||||||
|
class RenderPsychopathSettingsScene(PropertyGroup):
|
||||||
|
spp = IntProperty(
|
||||||
|
name="Samples Per Pixel", description="Total number of samples to take per pixel",
|
||||||
|
min=1, max=65536, default=16
|
||||||
|
)
|
||||||
|
|
||||||
|
dicing_rate = FloatProperty(
|
||||||
|
name="Dicing Rate", description="The target microgeometry width in pixels",
|
||||||
|
min=0.0001, max=100.0, soft_min=0.125, soft_max=1.0, default=0.25
|
||||||
|
)
|
||||||
|
|
||||||
|
motion_blur_segments = IntProperty(
|
||||||
|
name="Motion Segments", description="The number of segments to use in motion blur. Zero means no motion blur. Will be rounded down to the nearest power of two.",
|
||||||
|
min=0, max=256, default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
shutter_start = FloatProperty(
|
||||||
|
name="Shutter Open", description="The time during the frame that the shutter opens, for motion blur",
|
||||||
|
min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
shutter_end = FloatProperty(
|
||||||
|
name="Shutter Close", description="The time during the frame that the shutter closes, for motion blur",
|
||||||
|
min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.5
|
||||||
|
)
|
||||||
|
|
||||||
|
export_path = StringProperty(
|
||||||
|
name="Export Path", description="The path to where the .psy files should be exported when rendering. If left blank, /tmp or the equivalent is used.",
|
||||||
|
subtype='FILE_PATH'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Custom Camera properties
|
||||||
|
class PsychopathCamera(bpy.types.PropertyGroup):
|
||||||
|
aperture_radius = FloatProperty(
|
||||||
|
name="Aperture Radius", description="Size of the camera's aperture, for DoF",
|
||||||
|
min=0.0, max=10000.0, soft_min=0.0, soft_max=2.0, default=0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Custom Mesh properties
|
||||||
|
class PsychopathMesh(bpy.types.PropertyGroup):
|
||||||
|
is_subdivision_surface = BoolProperty(
|
||||||
|
name="Is Subdivision Surface", description="Whether this is a sibdivision surface or just a normal mesh",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Psychopath material
|
||||||
|
class PsychopathMaterial(bpy.types.PropertyGroup):
|
||||||
|
surface_shader_type = EnumProperty(
|
||||||
|
name="Surface Shader Type", description="",
|
||||||
|
items=[('Emit', 'Emit', ""), ('Lambert', 'Lambert', ""), ('GTR', 'GTR', "")],
|
||||||
|
default="Lambert"
|
||||||
|
)
|
||||||
|
|
||||||
|
color = FloatVectorProperty(
|
||||||
|
name="Color", description="",
|
||||||
|
subtype='COLOR',
|
||||||
|
min=0.0, soft_min=0.0, soft_max = 1.0,
|
||||||
|
default=[0.8,0.8,0.8]
|
||||||
|
)
|
||||||
|
|
||||||
|
roughness = FloatProperty(
|
||||||
|
name="Roughness", description="",
|
||||||
|
min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
tail_shape = FloatProperty(
|
||||||
|
name="Tail Shape", description="",
|
||||||
|
min=0.0, max=8.0, soft_min=1.0, soft_max=3.0, default=2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
fresnel = FloatProperty(
|
||||||
|
name="Fresnel", description="",
|
||||||
|
min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.9
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Addon Preferences
|
||||||
|
class PsychopathPreferences(AddonPreferences):
|
||||||
|
bl_idname = __name__
|
||||||
|
|
||||||
|
filepath_psychopath = StringProperty(
|
||||||
|
name="Psychopath Location",
|
||||||
|
description="Path to renderer executable",
|
||||||
|
subtype='DIR_PATH',
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "filepath_psychopath")
|
||||||
|
|
||||||
|
|
||||||
|
##### REGISTER #####
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(PsychopathPreferences)
|
||||||
|
bpy.utils.register_class(RenderPsychopathSettingsScene)
|
||||||
|
bpy.utils.register_class(PsychopathCamera)
|
||||||
|
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.Mesh.psychopath = PointerProperty(type=PsychopathMesh)
|
||||||
|
bpy.types.Material.psychopath = PointerProperty(type=PsychopathMaterial)
|
||||||
|
render.register()
|
||||||
|
ui.register()
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(PsychopathPreferences)
|
||||||
|
bpy.utils.unregister_class(RenderPsychopathSettingsScene)
|
||||||
|
bpy.utils.unregister_class(PsychopathCamera)
|
||||||
|
bpy.utils.unregister_class(PsychopathMesh)
|
||||||
|
bpy.utils.unregister_class(PsychopathMaterial)
|
||||||
|
del bpy.types.Scene.psychopath
|
||||||
|
del bpy.types.Camera.psychopath
|
||||||
|
del bpy.types.Mesh.psychopath
|
||||||
|
del bpy.types.Material.psychopath
|
||||||
|
render.unregister()
|
||||||
|
ui.unregister()
|
465
psychoblend/psy_export.py
Normal file
465
psychoblend/psy_export.py
Normal file
|
@ -0,0 +1,465 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from math import degrees, pi, log
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
|
|
||||||
|
def mat2str(m):
|
||||||
|
""" Converts a matrix into a single-line string of values.
|
||||||
|
"""
|
||||||
|
s = ""
|
||||||
|
for j in range(4):
|
||||||
|
for i in range(4):
|
||||||
|
s += (" %f" % m[i][j])
|
||||||
|
return s[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def needs_def_mb(ob):
|
||||||
|
""" Determines if the given object needs to be exported with
|
||||||
|
deformation motion blur or not.
|
||||||
|
"""
|
||||||
|
for mod in ob.modifiers:
|
||||||
|
if mod.type == 'SUBSURF':
|
||||||
|
pass
|
||||||
|
elif mod.type == 'MIRROR':
|
||||||
|
if mod.mirror_object == None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if ob.type == 'MESH':
|
||||||
|
if ob.data.shape_keys == None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def escape_name(name):
|
||||||
|
name = name.replace("\\", "\\\\")
|
||||||
|
name = name.replace(" ", "\\ ")
|
||||||
|
name = name.replace("$", "\\$")
|
||||||
|
name = name.replace("[", "\\[")
|
||||||
|
name = name.replace("]", "\\]")
|
||||||
|
name = name.replace("{", "\\{")
|
||||||
|
name = name.replace("}", "\\}")
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def needs_xform_mb(ob):
|
||||||
|
""" Determines if the given object needs to be exported with
|
||||||
|
transformation motion blur or not.
|
||||||
|
"""
|
||||||
|
if ob.animation_data != None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if len(ob.constraints) > 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if ob.parent != None:
|
||||||
|
return needs_xform_mb(ob.parent)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class IndentedWriter:
|
||||||
|
def __init__(self, file_handle):
|
||||||
|
self.f = file_handle
|
||||||
|
self.indent_level = 0
|
||||||
|
self.indent_size = 4
|
||||||
|
|
||||||
|
def indent(self):
|
||||||
|
self.indent_level += self.indent_size
|
||||||
|
|
||||||
|
def unindent(self):
|
||||||
|
self.indent_level -= self.indent_size
|
||||||
|
if self.indent_level < 0:
|
||||||
|
self.indent_level = 0
|
||||||
|
|
||||||
|
def write(self, text, do_indent=True):
|
||||||
|
if do_indent:
|
||||||
|
self.f.write(' '*self.indent_level + text)
|
||||||
|
else:
|
||||||
|
self.f.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PsychoExporter:
|
||||||
|
def __init__(self, scene):
|
||||||
|
self.scene = scene
|
||||||
|
|
||||||
|
self.mesh_names = {}
|
||||||
|
self.group_names = {}
|
||||||
|
|
||||||
|
# Motion blur segments are rounded down to a power of two
|
||||||
|
if scene.psychopath.motion_blur_segments > 0:
|
||||||
|
self.time_samples = (2**int(log(scene.psychopath.motion_blur_segments, 2))) + 1
|
||||||
|
else:
|
||||||
|
self.time_samples = 1
|
||||||
|
|
||||||
|
# pre-calculate useful values for exporting motion blur
|
||||||
|
self.shutter_start = scene.psychopath.shutter_start
|
||||||
|
self.shutter_diff = (scene.psychopath.shutter_end - scene.psychopath.shutter_start) / max(1, (self.time_samples-1))
|
||||||
|
|
||||||
|
self.fr = scene.frame_current
|
||||||
|
|
||||||
|
|
||||||
|
def set_frame(self, frame, fraction):
|
||||||
|
if fraction >= 0:
|
||||||
|
self.scene.frame_set(frame, fraction)
|
||||||
|
else:
|
||||||
|
self.scene.frame_set(frame-1, 1.0+fraction)
|
||||||
|
|
||||||
|
|
||||||
|
def export_psy(self, export_path, render_image_path):
|
||||||
|
f = open(export_path, 'w')
|
||||||
|
self.w = IndentedWriter(f)
|
||||||
|
|
||||||
|
# Info
|
||||||
|
self.w.write("# Exported from Blender 2.7x\n")
|
||||||
|
|
||||||
|
# Scene begin
|
||||||
|
self.w.write("\n\nScene $%s_fr%d {\n" % (escape_name(self.scene.name), self.fr))
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Output section begin
|
||||||
|
self.w.write("Output {\n")
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
self.w.write('Path ["%s"]\n' % render_image_path)
|
||||||
|
|
||||||
|
# Output section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# RenderSettings section begin
|
||||||
|
self.w.write("RenderSettings {\n")
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
res_x = int(self.scene.render.resolution_x * (self.scene.render.resolution_percentage / 100))
|
||||||
|
res_y = int(self.scene.render.resolution_y * (self.scene.render.resolution_percentage / 100))
|
||||||
|
self.w.write('Resolution [%d %d]\n' % (res_x, res_y))
|
||||||
|
self.w.write("SamplesPerPixel [%d]\n" % self.scene.psychopath.spp)
|
||||||
|
self.w.write("DicingRate [%f]\n" % self.scene.psychopath.dicing_rate)
|
||||||
|
self.w.write('Seed [%d]\n' % self.fr)
|
||||||
|
|
||||||
|
# RenderSettings section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Camera section begin
|
||||||
|
self.w.write("Camera {\n")
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
cam = self.scene.camera
|
||||||
|
|
||||||
|
if cam.data.dof_object == None:
|
||||||
|
dof_distance = cam.data.dof_distance
|
||||||
|
else:
|
||||||
|
# TODO: implement DoF object tracking here
|
||||||
|
dof_distance = 0.0
|
||||||
|
print("WARNING: DoF object tracking not yet implemented.")
|
||||||
|
|
||||||
|
matz = Matrix()
|
||||||
|
matz[2][2] = -1
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
if res_x >= res_y:
|
||||||
|
self.w.write("Fov [%f]\n" % degrees(cam.data.angle))
|
||||||
|
else:
|
||||||
|
self.w.write("Fov [%f]\n" % (degrees(cam.data.angle) * res_x / res_y))
|
||||||
|
self.w.write("FocalDistance [%f]\n" % dof_distance)
|
||||||
|
self.w.write("ApertureRadius [%f]\n" % (cam.data.psychopath.aperture_radius))
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
mat = cam.matrix_world.copy()
|
||||||
|
mat = mat * matz
|
||||||
|
self.w.write("Transform [%s]\n" % mat2str(mat))
|
||||||
|
|
||||||
|
# Camera section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# World section begin
|
||||||
|
self.w.write("World {\n")
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
world = self.scene.world
|
||||||
|
|
||||||
|
if world != None:
|
||||||
|
self.w.write("BackgroundShader {\n")
|
||||||
|
self.w.indent();
|
||||||
|
self.w.write("Type [Color]\n")
|
||||||
|
self.w.write("Color [%f %f %f]\n" % (world.horizon_color[0], world.horizon_color[1], world.horizon_color[2]))
|
||||||
|
self.w.unindent();
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
# World section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Export objects and materials
|
||||||
|
# TODO: handle materials from linked files (as used in group
|
||||||
|
# instances) properly.
|
||||||
|
self.w.write("Assembly {\n")
|
||||||
|
self.w.indent()
|
||||||
|
self.export_materials(bpy.data.materials)
|
||||||
|
self.export_objects(self.scene.objects, self.scene.layers)
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
# Scene end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
f.close()
|
||||||
|
self.scene.frame_set(self.fr)
|
||||||
|
|
||||||
|
|
||||||
|
def export_materials(self, materials):
|
||||||
|
for m in materials:
|
||||||
|
self.w.write("SurfaceShader $%s {\n" % escape_name(m.name))
|
||||||
|
self.w.indent()
|
||||||
|
self.w.write("Type [%s]\n" % m.psychopath.surface_shader_type)
|
||||||
|
self.w.write("Color [%f %f %f]\n" % (m.psychopath.color[0], m.psychopath.color[1], m.psychopath.color[2]))
|
||||||
|
if m.psychopath.surface_shader_type == 'GTR':
|
||||||
|
self.w.write("Roughness [%f]\n" % m.psychopath.roughness)
|
||||||
|
self.w.write("TailShape [%f]\n" % m.psychopath.tail_shape)
|
||||||
|
self.w.write("Fresnel [%f]\n" % m.psychopath.fresnel)
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def export_objects(self, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)):
|
||||||
|
for ob in objects:
|
||||||
|
# Check if the object is visible for rendering
|
||||||
|
vis_layer = False
|
||||||
|
for i in range(len(ob.layers)):
|
||||||
|
vis_layer = vis_layer or (ob.layers[i] and visible_layers[i])
|
||||||
|
if ob.hide_render or not vis_layer:
|
||||||
|
continue
|
||||||
|
|
||||||
|
name = None
|
||||||
|
|
||||||
|
# Write object data
|
||||||
|
if ob.type == 'EMPTY':
|
||||||
|
if ob.dupli_type == 'GROUP':
|
||||||
|
name = group_prefix + "__" + escape_name(ob.dupli_group.name)
|
||||||
|
if name not in self.group_names:
|
||||||
|
self.group_names[name] = True
|
||||||
|
self.w.write("Assembly $%s {\n" % name)
|
||||||
|
self.w.indent()
|
||||||
|
self.export_objects(ob.dupli_group.objects, ob.dupli_group.layers, name, ob.dupli_group.dupli_offset*-1)
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
elif ob.type == 'MESH':
|
||||||
|
name = self.export_mesh_object(ob, group_prefix)
|
||||||
|
elif ob.type == 'SURFACE':
|
||||||
|
name = self.export_surface_object(ob, group_prefix)
|
||||||
|
elif ob.type == 'LAMP' and ob.data.type == 'POINT':
|
||||||
|
name = self.export_sphere_lamp(ob, group_prefix)
|
||||||
|
elif ob.type == 'LAMP' and ob.data.type == 'AREA':
|
||||||
|
name = self.export_area_lamp(ob, group_prefix)
|
||||||
|
|
||||||
|
# Write object instance, with transforms
|
||||||
|
if name != None:
|
||||||
|
time_mats = []
|
||||||
|
|
||||||
|
if needs_xform_mb(ob):
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
mat = ob.matrix_world.copy()
|
||||||
|
mat[0][3] += translation_offset[0]
|
||||||
|
mat[1][3] += translation_offset[1]
|
||||||
|
mat[2][3] += translation_offset[2]
|
||||||
|
time_mats += [mat]
|
||||||
|
else:
|
||||||
|
mat = ob.matrix_world.copy()
|
||||||
|
mat[0][3] += translation_offset[0]
|
||||||
|
mat[1][3] += translation_offset[1]
|
||||||
|
mat[2][3] += translation_offset[2]
|
||||||
|
time_mats += [mat]
|
||||||
|
|
||||||
|
self.w.write("Instance {\n")
|
||||||
|
self.w.indent()
|
||||||
|
self.w.write("Data [$%s]\n" % name)
|
||||||
|
if len(ob.material_slots) > 0 and ob.material_slots[0].material != None:
|
||||||
|
self.w.write("SurfaceShaderBind [$%s]\n" % escape_name(ob.material_slots[0].material.name))
|
||||||
|
for i in range(len(time_mats)):
|
||||||
|
mat = time_mats[i].inverted()
|
||||||
|
self.w.write("Transform [%s]\n" % mat2str(mat))
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def export_mesh_object(self, ob, group_prefix):
|
||||||
|
# Determine if and how to export the mesh data
|
||||||
|
has_modifiers = len(ob.modifiers) > 0
|
||||||
|
deform_mb = needs_def_mb(ob)
|
||||||
|
if has_modifiers or deform_mb:
|
||||||
|
mesh_name = group_prefix + "__" + ob.name + "__" + ob.data.name + "_"
|
||||||
|
else:
|
||||||
|
mesh_name = group_prefix + "__" + ob.data.name + "_"
|
||||||
|
export_mesh = (mesh_name not in self.mesh_names) or has_modifiers or deform_mb
|
||||||
|
|
||||||
|
# Collect time samples
|
||||||
|
time_meshes = []
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
if export_mesh and (deform_mb or i == 0):
|
||||||
|
time_meshes += [ob.to_mesh(self.scene, True, 'RENDER')]
|
||||||
|
|
||||||
|
# Export mesh data if necessary
|
||||||
|
if export_mesh and ob.data.psychopath.is_subdivision_surface == False:
|
||||||
|
# Exporting normal mesh
|
||||||
|
self.mesh_names[mesh_name] = True
|
||||||
|
self.w.write("Assembly $%s {\n" % escape_name(mesh_name))
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
# Write patches
|
||||||
|
polys = time_meshes[0].polygons
|
||||||
|
face_count = 0
|
||||||
|
for poly in polys:
|
||||||
|
face_count += 1
|
||||||
|
if len(poly.vertices) == 4:
|
||||||
|
# Object
|
||||||
|
self.w.write("BilinearPatch $%s.%d {\n" % (escape_name(mesh_name), face_count))
|
||||||
|
self.w.indent()
|
||||||
|
for i in range(len(time_meshes)):
|
||||||
|
verts = time_meshes[i].vertices
|
||||||
|
vstr = ""
|
||||||
|
for vi in [poly.vertices[0], poly.vertices[1], poly.vertices[3], poly.vertices[2]]:
|
||||||
|
v = verts[vi].co
|
||||||
|
vstr += ("%f %f %f " % (v[0], v[1], v[2]))
|
||||||
|
self.w.write("Vertices [%s]\n" % vstr[:-1])
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
# Instance
|
||||||
|
self.w.write("Instance {\n")
|
||||||
|
self.w.indent()
|
||||||
|
self.w.write("Data [$%s.%d]\n" % (escape_name(mesh_name), face_count))
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
for m in time_meshes:
|
||||||
|
bpy.data.meshes.remove(m)
|
||||||
|
|
||||||
|
# Assembly section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
elif export_mesh and ob.data.psychopath.is_subdivision_surface == True:
|
||||||
|
# Exporting subdivision surface cage
|
||||||
|
self.mesh_names[mesh_name] = True
|
||||||
|
self.w.write("SubdivisionSurface $%s {\n" % escape_name(mesh_name))
|
||||||
|
self.w.indent()
|
||||||
|
|
||||||
|
# Write vertices
|
||||||
|
for ti in range(len(time_meshes)):
|
||||||
|
self.w.write("Vertices [")
|
||||||
|
for v in time_meshes[ti].vertices:
|
||||||
|
self.w.write("%f %f %f " % (v.co[0], v.co[1], v.co[2]), False)
|
||||||
|
self.w.write("]\n", False)
|
||||||
|
|
||||||
|
# Write face vertex counts
|
||||||
|
self.w.write("FaceVertCounts [")
|
||||||
|
for p in time_meshes[0].polygons:
|
||||||
|
self.w.write("%d " % len(p.vertices), False)
|
||||||
|
self.w.write("]\n", False)
|
||||||
|
|
||||||
|
# Write face vertex indices
|
||||||
|
self.w.write("FaceVertIndices [")
|
||||||
|
for p in time_meshes[0].polygons:
|
||||||
|
for v in p.vertices:
|
||||||
|
self.w.write("%d " % v, False)
|
||||||
|
self.w.write("]\n", False)
|
||||||
|
|
||||||
|
# SubdivisionSurface section end
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
return mesh_name
|
||||||
|
|
||||||
|
|
||||||
|
def export_surface_object(self, ob, group_prefix):
|
||||||
|
name = group_prefix + "__" + escape_name(ob.name)
|
||||||
|
|
||||||
|
# Collect time samples
|
||||||
|
time_surfaces = []
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
time_surfaces += [ob.data.copy()]
|
||||||
|
|
||||||
|
# Write patch
|
||||||
|
self.w.write("BicubicPatch $" + name + " {\n")
|
||||||
|
self.w.indent()
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
verts = time_surfaces[i].splines[0].points
|
||||||
|
vstr = ""
|
||||||
|
for v in verts:
|
||||||
|
vstr += ("%f %f %f " % (v.co[0], v.co[1], v.co[2]))
|
||||||
|
self.w.write("Vertices [%s]\n" % vstr[:-1])
|
||||||
|
for s in time_surfaces:
|
||||||
|
bpy.data.curves.remove(s)
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def export_sphere_lamp(self, ob, group_prefix):
|
||||||
|
name = group_prefix + "__" + escape_name(ob.name)
|
||||||
|
|
||||||
|
# Collect data over time
|
||||||
|
time_col = []
|
||||||
|
time_rad = []
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
time_col += [ob.data.color * ob.data.energy]
|
||||||
|
time_rad += [ob.data.shadow_soft_size]
|
||||||
|
|
||||||
|
# Write out sphere light
|
||||||
|
self.w.write("SphereLight $%s {\n" % name)
|
||||||
|
self.w.indent()
|
||||||
|
for col in time_col:
|
||||||
|
self.w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||||
|
for rad in time_rad:
|
||||||
|
self.w.write("Radius [%f]\n" % rad)
|
||||||
|
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
def export_area_lamp(self, ob, group_prefix):
|
||||||
|
name = group_prefix + "__" + escape_name(ob.name)
|
||||||
|
|
||||||
|
# Collect data over time
|
||||||
|
time_col = []
|
||||||
|
time_dim = []
|
||||||
|
for i in range(self.time_samples):
|
||||||
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
|
time_col += [ob.data.color * ob.data.energy]
|
||||||
|
if ob.data.shape == 'RECTANGLE':
|
||||||
|
time_dim += [(ob.data.size, ob.data.size_y)]
|
||||||
|
else:
|
||||||
|
time_dim += [(ob.data.size, ob.data.size)]
|
||||||
|
|
||||||
|
|
||||||
|
# Write out sphere light
|
||||||
|
self.w.write("RectangleLight $%s {\n" % name)
|
||||||
|
self.w.indent()
|
||||||
|
for col in time_col:
|
||||||
|
self.w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||||
|
for dim in time_dim:
|
||||||
|
self.w.write("Dimensions [%f %f]\n" % dim)
|
||||||
|
|
||||||
|
self.w.unindent()
|
||||||
|
self.w.write("}\n")
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
153
psychoblend/render.py
Normal file
153
psychoblend/render.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
import bpy
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from . import psy_export
|
||||||
|
|
||||||
|
def get_temp_filename(suffix=""):
|
||||||
|
tmpf = tempfile.mkstemp(suffix=suffix, prefix='tmp')
|
||||||
|
os.close(tmpf[0])
|
||||||
|
return(tmpf[1])
|
||||||
|
|
||||||
|
class PsychopathRender(bpy.types.RenderEngine):
|
||||||
|
bl_idname = 'PSYCHOPATH_RENDER'
|
||||||
|
bl_label = "Psychopath"
|
||||||
|
DELAY = 1.0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _locate_binary():
|
||||||
|
addon_prefs = bpy.context.user_preferences.addons[__package__].preferences
|
||||||
|
|
||||||
|
# Use the system preference if its set.
|
||||||
|
psy_binary = addon_prefs.filepath_psychopath
|
||||||
|
if psy_binary:
|
||||||
|
if os.path.exists(psy_binary):
|
||||||
|
return psy_binary
|
||||||
|
else:
|
||||||
|
print("User Preference to psychopath %r NOT FOUND, checking $PATH" % psy_binary)
|
||||||
|
|
||||||
|
# search the path all os's
|
||||||
|
psy_binary_default = "psychopath"
|
||||||
|
|
||||||
|
os_path_ls = os.getenv("PATH").split(':') + [""]
|
||||||
|
|
||||||
|
for dir_name in os_path_ls:
|
||||||
|
psy_binary = os.path.join(dir_name, psy_binary_default)
|
||||||
|
if os.path.exists(psy_binary):
|
||||||
|
return psy_binary
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _export(self, scene, export_path, render_image_path):
|
||||||
|
exporter = psy_export.PsychoExporter(scene)
|
||||||
|
exporter.export_psy(export_path, render_image_path)
|
||||||
|
|
||||||
|
def _render(self, scene, psy_filepath):
|
||||||
|
psy_binary = PsychopathRender._locate_binary()
|
||||||
|
if not psy_binary:
|
||||||
|
print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# TODO: figure out command line options
|
||||||
|
args = ["-i", psy_filepath]
|
||||||
|
|
||||||
|
# Start Rendering!
|
||||||
|
try:
|
||||||
|
self._process = subprocess.Popen([psy_binary] + args,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
except OSError:
|
||||||
|
# TODO, report api
|
||||||
|
print("Psychopath: could not execute '%s'" % psy_binary)
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
print ("***-DONE-***")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup(self):
|
||||||
|
# for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out):
|
||||||
|
# for i in range(5):
|
||||||
|
# try:
|
||||||
|
# os.unlink(f)
|
||||||
|
# break
|
||||||
|
# except OSError:
|
||||||
|
# # Wait a bit before retrying file might be still in use by Blender,
|
||||||
|
# # and Windows does not know how to delete a file in use!
|
||||||
|
# time.sleep(self.DELAY)
|
||||||
|
# for i in unpacked_images:
|
||||||
|
# for c in range(5):
|
||||||
|
# try:
|
||||||
|
# os.unlink(i)
|
||||||
|
# break
|
||||||
|
# except OSError:
|
||||||
|
# # Wait a bit before retrying file might be still in use by Blender,
|
||||||
|
# # and Windows does not know how to delete a file in use!
|
||||||
|
# time.sleep(self.DELAY)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def render(self, scene):
|
||||||
|
# has to be called to update the frame on exporting animations
|
||||||
|
scene.frame_set(scene.frame_current)
|
||||||
|
|
||||||
|
export_path = scene.psychopath.export_path
|
||||||
|
if export_path != "":
|
||||||
|
export_path += "_%d.psy" % scene.frame_current
|
||||||
|
else:
|
||||||
|
# Create a temporary file for exporting
|
||||||
|
export_path = get_temp_filename('.psy')
|
||||||
|
|
||||||
|
# Create a temporary file to render into
|
||||||
|
render_image_path = get_temp_filename('.png')
|
||||||
|
|
||||||
|
# start export
|
||||||
|
self.update_stats("", "Psychopath: Exporting data from Blender")
|
||||||
|
self._export(scene, export_path, render_image_path)
|
||||||
|
|
||||||
|
# Start rendering
|
||||||
|
self.update_stats("", "Psychopath: Rendering from exported file")
|
||||||
|
if not self._render(scene, export_path):
|
||||||
|
self.update_stats("", "Psychopath: Not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
r = scene.render
|
||||||
|
# compute resolution
|
||||||
|
x = int(r.resolution_x * r.resolution_percentage)
|
||||||
|
y = int(r.resolution_y * r.resolution_percentage)
|
||||||
|
|
||||||
|
result = self.begin_result(0, 0, x, y)
|
||||||
|
lay = result.layers[0]
|
||||||
|
|
||||||
|
# TODO: Update viewport with render result while rendering
|
||||||
|
while self._process.poll() == None:
|
||||||
|
# Wait for self.DELAY seconds, but check for render cancels
|
||||||
|
# while waiting.
|
||||||
|
t = 0.0
|
||||||
|
while t < self.DELAY:
|
||||||
|
if self.test_break():
|
||||||
|
self._process.terminate()
|
||||||
|
break
|
||||||
|
time.sleep(0.05)
|
||||||
|
t += 0.05
|
||||||
|
# # Update viewport image with latest render output
|
||||||
|
# if os.path.exists(render_image_path):
|
||||||
|
# # This assumes the file has been fully written We wait a bit, just in case!
|
||||||
|
# try:
|
||||||
|
# lay.load_from_file(render_image_path)
|
||||||
|
# self.update_result(result)
|
||||||
|
# except RuntimeError:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# Load final image
|
||||||
|
lay.load_from_file(render_image_path)
|
||||||
|
self.end_result(result)
|
||||||
|
|
||||||
|
# Delete temporary image file
|
||||||
|
os.remove(render_image_path)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(PsychopathRender)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(PsychopathRender)
|
270
psychoblend/ui.py
Normal file
270
psychoblend/ui.py
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
# Use some of the existing buttons.
|
||||||
|
from bl_ui import properties_render
|
||||||
|
properties_render.RENDER_PT_render.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
properties_render.RENDER_PT_output.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
del properties_render
|
||||||
|
|
||||||
|
from bl_ui import properties_data_camera
|
||||||
|
properties_data_camera.DATA_PT_lens.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
properties_data_camera.DATA_PT_camera.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
properties_data_camera.DATA_PT_camera_display.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
properties_data_camera.DATA_PT_custom_props_camera.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
|
||||||
|
del properties_data_camera
|
||||||
|
|
||||||
|
class PsychopathPanel():
|
||||||
|
COMPAT_ENGINES = {'PSYCHOPATH_RENDER'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
rd = context.scene.render
|
||||||
|
return (rd.use_game_engine is False) and (rd.engine in cls.COMPAT_ENGINES)
|
||||||
|
|
||||||
|
|
||||||
|
class RENDER_PT_psychopath_render_settings(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Render Settings"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "render"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
scene = context.scene
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
|
||||||
|
col.label(text="Sampling")
|
||||||
|
col.prop(scene.psychopath, "spp")
|
||||||
|
|
||||||
|
col.label(text="Dicing")
|
||||||
|
col.prop(scene.psychopath, "dicing_rate")
|
||||||
|
|
||||||
|
col.label(text="Motion Blur")
|
||||||
|
col.prop(scene.psychopath, "motion_blur_segments")
|
||||||
|
col.prop(scene.psychopath, "shutter_start")
|
||||||
|
col.prop(scene.psychopath, "shutter_end")
|
||||||
|
|
||||||
|
|
||||||
|
class RENDER_PT_psychopath_export_settings(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Export Settings"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "render"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
scene = context.scene
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
col.prop(scene.psychopath, "export_path")
|
||||||
|
|
||||||
|
|
||||||
|
class WORLD_PT_psychopath_background(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Background"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "world"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.world and PsychopathPanel.poll(context)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
world = context.world
|
||||||
|
layout.prop(world, "horizon_color", text="Color")
|
||||||
|
|
||||||
|
|
||||||
|
class DATA_PT_psychopath_camera_dof(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Depth of Field"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
engine = context.scene.render.engine
|
||||||
|
return context.camera and PsychopathPanel.poll(context)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.active_object
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
|
||||||
|
col.prop(ob.data, "dof_object")
|
||||||
|
col.prop(ob.data, "dof_distance")
|
||||||
|
col.prop(ob.data.psychopath, "aperture_radius")
|
||||||
|
|
||||||
|
|
||||||
|
class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Lamp"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
engine = context.scene.render.engine
|
||||||
|
return context.lamp and PsychopathPanel.poll(context)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.active_object
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
|
||||||
|
row = col.row()
|
||||||
|
row.prop(ob.data, "type", expand=True)
|
||||||
|
|
||||||
|
if ob.data.type != 'HEMI' and ob.data.type != 'AREA':
|
||||||
|
col.prop(ob.data, "shadow_soft_size")
|
||||||
|
col.prop(ob.data, "color")
|
||||||
|
col.prop(ob.data, "energy")
|
||||||
|
|
||||||
|
|
||||||
|
class DATA_PT_psychopath_area_lamp(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Area Shape"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
lamp = context.lamp
|
||||||
|
engine = context.scene.render.engine
|
||||||
|
return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
lamp = context.lamp
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
col.row().prop(lamp, "shape", expand=True)
|
||||||
|
sub = col.row(align=True)
|
||||||
|
|
||||||
|
if lamp.shape == 'SQUARE':
|
||||||
|
sub.prop(lamp, "size")
|
||||||
|
elif lamp.shape == 'RECTANGLE':
|
||||||
|
sub.prop(lamp, "size", text="Size X")
|
||||||
|
sub.prop(lamp, "size_y", text="Size Y")
|
||||||
|
|
||||||
|
|
||||||
|
class DATA_PT_psychopath_mesh(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Psychopath Mesh Properties"
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "data"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
engine = context.scene.render.engine
|
||||||
|
return context.mesh and (engine in cls.COMPAT_ENGINES)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
mesh = context.mesh
|
||||||
|
|
||||||
|
layout.row().prop(mesh.psychopath, "is_subdivision_surface")
|
||||||
|
|
||||||
|
|
||||||
|
class MATERIAL_PT_psychopath_context_material(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = ""
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "material"
|
||||||
|
bl_options = {'HIDE_HEADER'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.material or context.object) and PsychopathPanel.poll(context)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
mat = context.material
|
||||||
|
ob = context.object
|
||||||
|
slot = context.material_slot
|
||||||
|
space = context.space_data
|
||||||
|
|
||||||
|
if ob:
|
||||||
|
row = layout.row()
|
||||||
|
|
||||||
|
row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=1)
|
||||||
|
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("object.material_slot_add", icon='ZOOMIN', text="")
|
||||||
|
col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
|
||||||
|
|
||||||
|
col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")
|
||||||
|
|
||||||
|
if ob.mode == 'EDIT':
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.operator("object.material_slot_assign", text="Assign")
|
||||||
|
row.operator("object.material_slot_select", text="Select")
|
||||||
|
row.operator("object.material_slot_deselect", text="Deselect")
|
||||||
|
|
||||||
|
split = layout.split(percentage=0.65)
|
||||||
|
|
||||||
|
if ob:
|
||||||
|
split.template_ID(ob, "active_material", new="material.new")
|
||||||
|
row = split.row()
|
||||||
|
|
||||||
|
if slot:
|
||||||
|
row.prop(slot, "link", text="")
|
||||||
|
else:
|
||||||
|
row.label()
|
||||||
|
elif mat:
|
||||||
|
split.template_ID(space, "pin_id")
|
||||||
|
split.separator()
|
||||||
|
|
||||||
|
|
||||||
|
class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
|
||||||
|
bl_label = "Surface"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "material"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.material and PsychopathPanel.poll(context)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
mat = context.material
|
||||||
|
layout.prop(mat.psychopath, "surface_shader_type")
|
||||||
|
layout.prop(mat.psychopath, "color")
|
||||||
|
|
||||||
|
if mat.psychopath.surface_shader_type == 'GTR':
|
||||||
|
layout.prop(mat.psychopath, "roughness")
|
||||||
|
layout.prop(mat.psychopath, "tail_shape")
|
||||||
|
layout.prop(mat.psychopath, "fresnel")
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(RENDER_PT_psychopath_render_settings)
|
||||||
|
bpy.utils.register_class(RENDER_PT_psychopath_export_settings)
|
||||||
|
bpy.utils.register_class(WORLD_PT_psychopath_background)
|
||||||
|
bpy.utils.register_class(DATA_PT_psychopath_camera_dof)
|
||||||
|
bpy.utils.register_class(DATA_PT_psychopath_mesh)
|
||||||
|
bpy.utils.register_class(DATA_PT_psychopath_lamp)
|
||||||
|
bpy.utils.register_class(DATA_PT_psychopath_area_lamp)
|
||||||
|
bpy.utils.register_class(MATERIAL_PT_psychopath_context_material)
|
||||||
|
bpy.utils.register_class(MATERIAL_PT_psychopath_surface)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(RENDER_PT_psychopath_render_settings)
|
||||||
|
bpy.utils.unregister_class(RENDER_PT_psychopath_export_settings)
|
||||||
|
bpy.utils.unregister_class(WORLD_PT_psychopath_background)
|
||||||
|
bpy.utils.unregister_class(DATA_PT_psychopath_camera_dof)
|
||||||
|
bpy.utils.register_class(DATA_PT_psychopath_mesh)
|
||||||
|
bpy.utils.unregister_class(DATA_PT_psychopath_lamp)
|
||||||
|
bpy.utils.unregister_class(DATA_PT_psychopath_area_lamp)
|
||||||
|
bpy.utils.unregister_class(MATERIAL_PT_psychopath_context_material)
|
||||||
|
bpy.utils.unregister_class(MATERIAL_PT_psychopath_surface)
|
Loading…
Reference in New Issue
Block a user