Light Field Tools: Update panel Rename, cleanup, fixes
Bumped version to 0.3.1 As a part of the task T50726: Update the Panel rename code to more generic one Pep8 cleanup Replace star imports Consistent property definitions Add the missing property deletion on unregister Fix crashes with missing brackets around CANCELLED Add an name to the generated Empty handler Update wiki link
This commit is contained in:
parent
70529875c6
commit
65741d7430
|
@ -21,11 +21,11 @@ bl_info = {
|
|||
"name": "Light Field Tools",
|
||||
"author": "Aurel Wildfellner",
|
||||
"description": "Tools to create a light field camera and projector",
|
||||
"version": (0, 3, 0),
|
||||
"version": (0, 3, 1),
|
||||
"blender": (2, 64, 0),
|
||||
"location": "View3D > Tool Shelf > Light Field Tools",
|
||||
"url": "http://www.jku.at/cg/",
|
||||
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
|
||||
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
|
||||
"Scripts/Render/Light_Field_Tools",
|
||||
"category": "Render"
|
||||
}
|
||||
|
@ -39,11 +39,21 @@ else:
|
|||
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import (
|
||||
AddonPreferences,
|
||||
PropertyGroup,
|
||||
)
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
PointerProperty,
|
||||
)
|
||||
|
||||
|
||||
# global properties for the script, mainly for UI
|
||||
class LightFieldPropertyGroup(bpy.types.PropertyGroup):
|
||||
class LightFieldPropertyGroup(PropertyGroup):
|
||||
angle = FloatProperty(
|
||||
name="Angle",
|
||||
# 40 degrees
|
||||
|
@ -52,91 +62,120 @@ class LightFieldPropertyGroup(bpy.types.PropertyGroup):
|
|||
# 172 degrees
|
||||
max=3.001966313430247,
|
||||
precision=2,
|
||||
subtype = 'ANGLE',
|
||||
description="Field of view of camera and angle of beam for spotlights")
|
||||
subtype='ANGLE',
|
||||
description="Field of view of camera and angle of beam for spotlights"
|
||||
)
|
||||
row_length = IntProperty(
|
||||
name="Row Length",
|
||||
default=1,
|
||||
min=1,
|
||||
description="The number of cameras/lights in one row")
|
||||
description="The number of cameras/lights in one row"
|
||||
)
|
||||
create_handler = BoolProperty(
|
||||
name="Handler",
|
||||
default=True,
|
||||
description="Creates an empty object, to which the cameras and spotlights are parented to")
|
||||
description="Creates an empty object, to which the cameras and spotlights are parented to"
|
||||
)
|
||||
do_camera = BoolProperty(
|
||||
name="Create Camera",
|
||||
default=True,
|
||||
description="A light field camera is created")
|
||||
description="A light field camera is created"
|
||||
)
|
||||
animate_camera = BoolProperty(
|
||||
name="Animate Camera",
|
||||
default=True,
|
||||
description="Animates a single camera, so not multiple cameras get created")
|
||||
description="Animates a single camera, so not multiple cameras get created"
|
||||
)
|
||||
do_projection = BoolProperty(
|
||||
name="Create Projector",
|
||||
default=False,
|
||||
description="A light field projector is created")
|
||||
description="A light field projector is created"
|
||||
)
|
||||
texture_path = StringProperty(
|
||||
name="Texture Path",
|
||||
description="From this path textures for the spotlights will be loaded",
|
||||
subtype='DIR_PATH')
|
||||
subtype='DIR_PATH'
|
||||
)
|
||||
light_intensity = FloatProperty(
|
||||
name="Light Intensity",
|
||||
default=2,
|
||||
min=0,
|
||||
precision=3,
|
||||
description="Total intensity of all lamps")
|
||||
description="Total intensity of all lamps"
|
||||
)
|
||||
# blending of the spotlights
|
||||
spot_blend = FloatProperty(
|
||||
spot_blend = FloatProperty(
|
||||
name="Blend",
|
||||
default=0,
|
||||
min=0,
|
||||
max=1,
|
||||
precision=3,
|
||||
description="Blending of the spotlights")
|
||||
description="Blending of the spotlights"
|
||||
)
|
||||
# spacing in pixels on the focal plane
|
||||
spacing = IntProperty(
|
||||
name="Spacing",
|
||||
default=10,
|
||||
min=0,
|
||||
description="The spacing in pixels between two cameras on the focal plane")
|
||||
description="The spacing in pixels between two cameras on the focal plane"
|
||||
)
|
||||
|
||||
|
||||
# Add-ons Preferences Update Panel
|
||||
|
||||
# Define Panel classes for updating
|
||||
panels = (
|
||||
light_field_tools.VIEW3D_OT_lightfield_tools,
|
||||
)
|
||||
|
||||
|
||||
## Addons Preferences Update Panel
|
||||
def update_panel(self, context):
|
||||
message = "Light Field Tools: Updating Panel locations has failed"
|
||||
try:
|
||||
bpy.utils.unregister_class(light_field_tools.VIEW3D_OT_lightfield_tools)
|
||||
except:
|
||||
pass
|
||||
light_field_tools.VIEW3D_OT_lightfield_tools.bl_category = context.user_preferences.addons[__name__].preferences.category
|
||||
bpy.utils.register_class(light_field_tools.VIEW3D_OT_lightfield_tools)
|
||||
for panel in panels:
|
||||
if "bl_rna" in panel.__dict__:
|
||||
bpy.utils.unregister_class(panel)
|
||||
|
||||
class LFTPreferences(bpy.types.AddonPreferences):
|
||||
for panel in panels:
|
||||
panel.bl_category = context.user_preferences.addons[__name__].preferences.category
|
||||
bpy.utils.register_class(panel)
|
||||
|
||||
except Exception as e:
|
||||
print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
|
||||
pass
|
||||
|
||||
|
||||
class LFTPreferences(AddonPreferences):
|
||||
# this must match the addon name, use '__package__'
|
||||
# when defining this in a submodule of a python package.
|
||||
bl_idname = __name__
|
||||
|
||||
category = bpy.props.StringProperty(
|
||||
category = StringProperty(
|
||||
name="Tab Category",
|
||||
description="Choose a name for the category of the panel",
|
||||
default="Tools",
|
||||
update=update_panel)
|
||||
update=update_panel
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.label(text="Tab Category:")
|
||||
col.prop(self, "category", text="")
|
||||
|
||||
|
||||
def register():
|
||||
# register properties
|
||||
bpy.utils.register_class(LightFieldPropertyGroup)
|
||||
bpy.types.Scene.lightfield = bpy.props.PointerProperty(type=LightFieldPropertyGroup)
|
||||
# bpy.utils.register_class(LightFieldPropertyGroup)
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.Scene.lightfield = PointerProperty(type=LightFieldPropertyGroup)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
del bpy.types.Scene.lightfield
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
import os
|
||||
import math
|
||||
|
||||
import mathutils
|
||||
from math import (
|
||||
degrees, tan,
|
||||
radians,
|
||||
)
|
||||
from mathutils import Vector
|
||||
|
||||
__bpydoc__ = """
|
||||
Light Field Tools
|
||||
|
@ -82,17 +86,17 @@ TODO:
|
|||
* Restore view after primary camera is changed.
|
||||
* Apply object matrix to normals.
|
||||
* Allign to normals, somehow,....
|
||||
* StringPropertie with PATH tag, for proper ui.
|
||||
* StringProperties with PATH tag, for proper ui.
|
||||
"""
|
||||
|
||||
|
||||
class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
||||
"""Create a lightfield rig based on the active object/mesh"""
|
||||
bl_idname="object.create_lightfield_rig"
|
||||
bl_label="Create a light field rig based on the active object/mesh"
|
||||
class OBJECT_OT_create_lightfield_rig(Operator):
|
||||
bl_idname = "object.create_lightfield_rig"
|
||||
bl_label = "Create a light field rig"
|
||||
bl_description = "Create a lightfield rig based on the active object/mesh"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
layer0 = [True] + [False]*19
|
||||
layer0 = [True] + [False] * 19
|
||||
|
||||
numSamples = 0
|
||||
baseObject = None
|
||||
|
@ -100,10 +104,9 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
verts = []
|
||||
imagePaths = []
|
||||
|
||||
|
||||
def arrangeVerts(self):
|
||||
"""Sorts the vertices as described in the usage part of the doc."""
|
||||
#FIXME get mesh with applied modifer stack
|
||||
# FIXME get mesh with applied modifer stack
|
||||
scene = bpy.context.scene
|
||||
mesh = self.baseObject.data
|
||||
verts = []
|
||||
|
@ -118,18 +121,19 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
|
||||
def key_x(v):
|
||||
return v[0][0]
|
||||
|
||||
def key_y(v):
|
||||
return v[0][1]
|
||||
|
||||
verts.sort(key=key_y)
|
||||
sorted_verts = []
|
||||
for i in range(0, len(verts), row_length):
|
||||
row = verts[i:i+row_length]
|
||||
row = verts[i: i + row_length]
|
||||
row.sort(key=key_x)
|
||||
sorted_verts.extend(row)
|
||||
|
||||
return sorted_verts
|
||||
|
||||
|
||||
def createCameraAnimated(self):
|
||||
scene = bpy.context.scene
|
||||
|
||||
|
@ -150,7 +154,7 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
# set as primary camera
|
||||
scene.camera = cam
|
||||
|
||||
### animate ###
|
||||
# animate
|
||||
scene.frame_current = 0
|
||||
|
||||
for frame, vert in enumerate(self.verts):
|
||||
|
@ -165,8 +169,7 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
# set anim render props
|
||||
scene.frame_current = 0
|
||||
scene.frame_start = 0
|
||||
scene.frame_end = self.numSamples-1
|
||||
|
||||
scene.frame_end = self.numSamples - 1
|
||||
|
||||
def createCameraMultiple(self):
|
||||
scene = bpy.context.scene
|
||||
|
@ -193,14 +196,12 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
if scene.lightfield.create_handler:
|
||||
cam.parent = self.handler
|
||||
|
||||
|
||||
def createCamera(self):
|
||||
if bpy.context.scene.lightfield.animate_camera:
|
||||
self.createCameraAnimated()
|
||||
else:
|
||||
self.createCameraMultiple()
|
||||
|
||||
|
||||
def getImagePaths(self):
|
||||
path = bpy.context.scene.lightfield.texture_path
|
||||
if not os.path.isdir(path):
|
||||
|
@ -209,16 +210,15 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
if not len(files) == self.numSamples:
|
||||
return False
|
||||
files.sort()
|
||||
self.imagePaths = list(map(lambda f : os.path.join(path, f), files))
|
||||
self.imagePaths = list(map(lambda f: os.path.join(path, f), files))
|
||||
return True
|
||||
|
||||
|
||||
def createTexture(self, index):
|
||||
name = "light_field_spot_tex_" + str(index)
|
||||
tex = bpy.data.textures.new(name, type='IMAGE')
|
||||
|
||||
# load and set the image
|
||||
#FIXME width, height. not necessary to set in the past.
|
||||
# FIXME width, height. not necessary to set in the past.
|
||||
img = bpy.data.images.new("lfe_str_" + str(index), width=5, height=5)
|
||||
img.filepath = self.imagePaths[index]
|
||||
img.source = 'FILE'
|
||||
|
@ -226,7 +226,6 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
|
||||
return tex
|
||||
|
||||
|
||||
def createSpot(self, index, textured=False):
|
||||
scene = bpy.context.scene
|
||||
bpy.ops.object.lamp_add(
|
||||
|
@ -259,30 +258,29 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
|
||||
return spot
|
||||
|
||||
|
||||
def createLightfieldEmitter(self, textured=False):
|
||||
for i, vert in enumerate(self.verts):
|
||||
spot = self.createSpot(i, textured)
|
||||
spot.location = vert[0]
|
||||
spot.rotation_euler = self.baseObject.rotation_euler
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
|
||||
obj = self.baseObject = context.active_object
|
||||
if not obj or obj.type != 'MESH':
|
||||
self.report({'ERROR'}, "No selected mesh object!")
|
||||
return 'CANCELLED'
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.verts = self.arrangeVerts()
|
||||
self.numSamples = len(self.verts)
|
||||
|
||||
if scene.lightfield.create_handler:
|
||||
#create an empty
|
||||
# create an empty
|
||||
bpy.ops.object.add(type='EMPTY')
|
||||
empty = bpy.context.active_object
|
||||
empty.location = self.baseObject.location
|
||||
empty.name = "light_field_handler"
|
||||
empty.rotation_euler = self.baseObject.rotation_euler
|
||||
self.handler = empty
|
||||
|
||||
|
@ -298,32 +296,27 @@ class OBJECT_OT_create_lightfield_rig(bpy.types.Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
|
||||
class OBJECT_OT_create_lightfield_basemesh(bpy.types.Operator):
|
||||
"""Creates a basemsh from the selected focal plane"""
|
||||
bl_idname="object.create_lightfield_basemesh"
|
||||
bl_label="Create a basemesh from the selected focal plane"
|
||||
class OBJECT_OT_create_lightfield_basemesh(Operator):
|
||||
bl_idname = "object.create_lightfield_basemesh"
|
||||
bl_label = "Create a basemesh from the selected focal plane"
|
||||
bl_description = "Creates a basemesh from the selected focal plane"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
objName = "lf_basemesh"
|
||||
|
||||
|
||||
def getWidth(self, obj):
|
||||
mat = obj.matrix_local
|
||||
mesh = obj.data
|
||||
v0 = mat * mesh.vertices[mesh.edges[0].vertices[0]].co
|
||||
v1 = mat * mesh.vertices[mesh.edges[0].vertices[1]].co
|
||||
return (v0-v1).length
|
||||
|
||||
return (v0 - v1).length
|
||||
|
||||
def getCamVec(self, obj, angle):
|
||||
width = self.getWidth(obj)
|
||||
itmat = obj.matrix_local.inverted().transposed()
|
||||
normal = itmat * obj.data.polygons[0].normal.normalized()
|
||||
vl = (width/2) * (1/math.tan(math.radians(angle/2)))
|
||||
return normal*vl
|
||||
|
||||
vl = (width / 2) * (1 / tan(radians(angle / 2)))
|
||||
return normal * vl
|
||||
|
||||
def addMeshObj(self, mesh):
|
||||
scene = bpy.context.scene
|
||||
|
@ -339,50 +332,49 @@ class OBJECT_OT_create_lightfield_basemesh(bpy.types.Operator):
|
|||
if scene.objects.active is None or scene.objects.active.mode == 'OBJECT':
|
||||
scene.objects.active = nobj
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
obj = context.active_object
|
||||
# check if active object is a mesh object
|
||||
if not obj or obj.type != 'MESH':
|
||||
self.report({'ERROR'}, "No selected mesh object!")
|
||||
return 'CANCELLED'
|
||||
return {'CANCELLED'}
|
||||
|
||||
# check if it has one single face
|
||||
if len(obj.data.polygons) != 1:
|
||||
self.report({'ERROR'}, "The selected mesh object has to have exactly one quad!")
|
||||
return 'CANCELLED'
|
||||
return {'CANCELLED'}
|
||||
|
||||
rl = scene.lightfield.row_length
|
||||
# use a degree angle here
|
||||
angle = math.degrees(scene.lightfield.angle)
|
||||
angle = degrees(scene.lightfield.angle)
|
||||
spacing = scene.lightfield.spacing
|
||||
# resolution of final renderings
|
||||
res = round(scene.render.resolution_x * (scene.render.resolution_percentage/100.))
|
||||
res = round(scene.render.resolution_x * (scene.render.resolution_percentage / 100.))
|
||||
width = self.getWidth(obj)
|
||||
|
||||
# the offset between n pixels on the focal plane
|
||||
fplane_offset = (width/res) * spacing
|
||||
fplane_offset = (width / res) * spacing
|
||||
|
||||
# vertices for the basemesh
|
||||
verts = []
|
||||
# the offset vector
|
||||
vec = self.getCamVec(obj, angle)
|
||||
# lower left coordinates of the grid
|
||||
sx = obj.location[0] - fplane_offset * int(rl/2)
|
||||
sy = obj.location[1] - fplane_offset * int(rl/2)
|
||||
sx = obj.location[0] - fplane_offset * int(rl / 2)
|
||||
sy = obj.location[1] - fplane_offset * int(rl / 2)
|
||||
z = obj.location[2]
|
||||
# position on the focal plane
|
||||
fplane_pos = mathutils.Vector()
|
||||
for x in [sx + fplane_offset*i for i in range(rl)]:
|
||||
for y in [sy + fplane_offset*i for i in range(rl)]:
|
||||
fplane_pos = Vector()
|
||||
for x in [sx + fplane_offset * i for i in range(rl)]:
|
||||
for y in [sy + fplane_offset * i for i in range(rl)]:
|
||||
fplane_pos.x = x
|
||||
fplane_pos.y = y
|
||||
fplane_pos.z = z
|
||||
# position of a vertex in a basemesh
|
||||
pos = fplane_pos + vec
|
||||
# pack coordinates flat into the vert list
|
||||
verts.append( (pos.x, pos.y, pos.z) )
|
||||
verts.append((pos.x, pos.y, pos.z))
|
||||
|
||||
# setup the basemesh and add verts
|
||||
mesh = bpy.data.meshes.new(self.objName)
|
||||
|
@ -392,9 +384,7 @@ class OBJECT_OT_create_lightfield_basemesh(bpy.types.Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
|
||||
class VIEW3D_OT_lightfield_tools(bpy.types.Panel):
|
||||
class VIEW3D_OT_lightfield_tools(Panel):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOLS"
|
||||
bl_context = "objectmode"
|
||||
|
@ -403,7 +393,6 @@ class VIEW3D_OT_lightfield_tools(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
|
||||
col = layout.column()
|
||||
|
@ -424,8 +413,7 @@ class VIEW3D_OT_lightfield_tools(bpy.types.Panel):
|
|||
|
||||
# create a basemesh
|
||||
col = layout.column(align=True)
|
||||
col.operator("object.create_lightfield_basemesh", "Create Base Grid")
|
||||
col.operator("object.create_lightfield_basemesh", text="Create Base Grid")
|
||||
col.prop(scene.lightfield, "spacing")
|
||||
|
||||
layout.operator("object.create_lightfield_rig", "Create Rig")
|
||||
|
||||
layout.operator("object.create_lightfield_rig", text="Create Rig")
|
||||
|
|
Loading…
Reference in New Issue