forked from bartvdbraak/blender
24ce60cfe4
This commit includes all the changes made for plane tracker in tomato branch. Movie clip editor changes: - Artist might create a plane track out of multiple point tracks which belongs to the same track (minimum amount of point tracks is 4, maximum is not actually limited). When new plane track is added, it's getting "tracked" across all point tracks, which makes it stick to the same plane point tracks belong to. - After plane track was added, it need to be manually adjusted in a way it covers feature one might to mask/replace. General transform tools (G, R, S) or sliding corners with a mouse could be sued for this. Plane corner which corresponds to left bottom image corner has got X/Y axis on it (red is for X axis, green for Y). - Re-adjusting plane corners makes plane to be "re-tracked" for the frames sequence between current frame and next and previous keyframes. - Kayframes might be removed from the plane, using Shit-X (Marker Delete) operator. However, currently manual re-adjustment or "re-track" trigger is needed. Compositor changes: - Added new node called Plane Track Deform. - User selects which plane track to use (for this he need to select movie clip datablock, object and track names). - Node gets an image input, which need to be warped into the plane. - Node outputs: * Input image warped into the plane. * Plane, rasterized to a mask. Masking changes: - Mask points might be parented to a plane track, which makes this point deforming in a way as if it belongs to the tracked plane. Some video tutorials are available: - Coder video: http://www.youtube.com/watch?v=vISEwqNHqe4 - Artist video: https://vimeo.com/71727578 This is mine and Keir's holiday code project :)
357 lines
10 KiB
Python
357 lines
10 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8-80 compliant>
|
|
|
|
# panels get subclassed (not registered directly)
|
|
# menus are referenced `as is`
|
|
|
|
import bpy
|
|
from bpy.types import Menu, UIList
|
|
|
|
|
|
class MASK_UL_layers(UIList):
|
|
def draw_item(self, context, layout, data, item, icon,
|
|
active_data, active_propname, index):
|
|
# assert(isinstance(item, bpy.types.MaskLayer)
|
|
mask = item
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
split = layout.split()
|
|
split.label(text=mask.name, translate=False, icon_value=icon)
|
|
row = split.row(align=True)
|
|
row.prop(mask, "alpha", text="", emboss=False)
|
|
row.prop(mask, "hide", text="", emboss=False)
|
|
row.prop(mask, "hide_select", text="", emboss=False)
|
|
row.prop(mask, "hide_render", text="", emboss=False)
|
|
elif self.layout_type in {'GRID'}:
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
|
|
class MASK_PT_mask:
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'UI'
|
|
bl_label = "Mask Settings"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(mask, "frame_start")
|
|
col.prop(mask, "frame_end")
|
|
|
|
|
|
class MASK_PT_layers:
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'UI'
|
|
bl_label = "Mask Layers"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
active_layer = mask.layers.active
|
|
|
|
rows = 5 if active_layer else 2
|
|
|
|
row = layout.row()
|
|
row.template_list("MASK_UL_layers", "", mask, "layers",
|
|
mask, "active_layer_index", rows=rows)
|
|
|
|
sub = row.column(align=True)
|
|
|
|
sub.operator("mask.layer_new", icon='ZOOMIN', text="")
|
|
sub.operator("mask.layer_remove", icon='ZOOMOUT', text="")
|
|
|
|
if active_layer:
|
|
sub.separator()
|
|
|
|
props = sub.operator("mask.layer_move", icon='TRIA_UP', text="")
|
|
props.direction = 'UP'
|
|
|
|
props = sub.operator("mask.layer_move", icon='TRIA_DOWN', text="")
|
|
props.direction = 'DOWN'
|
|
|
|
layout.prop(active_layer, "name")
|
|
|
|
# blending
|
|
row = layout.row(align=True)
|
|
row.prop(active_layer, "alpha")
|
|
row.prop(active_layer, "invert", text="", icon='IMAGE_ALPHA')
|
|
|
|
layout.prop(active_layer, "blend")
|
|
layout.prop(active_layer, "falloff")
|
|
|
|
|
|
class MASK_PT_spline():
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'UI'
|
|
bl_label = "Active Spline"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
if mask and sc.mode == 'MASK':
|
|
return mask.layers.active and mask.layers.active.splines.active
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
spline = mask.layers.active.splines.active
|
|
|
|
col = layout.column()
|
|
col.prop(spline, "offset_mode")
|
|
col.prop(spline, "weight_interpolation")
|
|
|
|
row = col.row()
|
|
row.prop(spline, "use_cyclic")
|
|
row.prop(spline, "use_fill")
|
|
|
|
col.prop(spline, "use_self_intersection_check")
|
|
|
|
|
|
class MASK_PT_point():
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'UI'
|
|
bl_label = "Active Point"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
if mask and sc.mode == 'MASK':
|
|
mask_layer_active = mask.layers.active
|
|
return (mask_layer_active and
|
|
mask_layer_active.splines.active_point)
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
point = mask.layers.active.splines.active_point
|
|
parent = point.parent
|
|
|
|
col = layout.column()
|
|
col.prop(point, "handle_type")
|
|
|
|
col = layout.column()
|
|
# Currently only parenting yo movie clip is allowed, so do not
|
|
# ver-oplicate things for now and use single template_ID
|
|
#col.template_any_ID(parent, "id", "id_type", text="")
|
|
|
|
col.label("Parent:")
|
|
col.prop(parent, "id", text="")
|
|
|
|
if parent.id_type == 'MOVIECLIP' and parent.id:
|
|
clip = parent.id
|
|
tracking = clip.tracking
|
|
|
|
row = col.row()
|
|
row.prop(parent, "type", expand=True)
|
|
|
|
col.prop_search(parent, "parent", tracking,
|
|
"objects", icon='OBJECT_DATA', text="Object:")
|
|
|
|
tracks_list = "tracks" if parent.type == 'POINT_TRACK' else 'plane_tracks'
|
|
|
|
if parent.parent in tracking.objects:
|
|
object = tracking.objects[parent.parent]
|
|
col.prop_search(parent, "sub_parent", object,
|
|
tracks_list, icon='ANIM_DATA', text="Track:")
|
|
else:
|
|
col.prop_search(parent, "sub_parent", tracking,
|
|
tracks_list, icon='ANIM_DATA', text="Track:")
|
|
|
|
|
|
class MASK_PT_display():
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'UI'
|
|
bl_label = "Mask Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
space_data = context.space_data
|
|
|
|
layout.prop(space_data, "mask_draw_type", text="")
|
|
layout.prop(space_data, "show_mask_smooth")
|
|
|
|
|
|
class MASK_PT_tools():
|
|
# subclasses must define...
|
|
#~ bl_space_type = 'CLIP_EDITOR'
|
|
#~ bl_region_type = 'TOOLS'
|
|
bl_label = "Mask Tools"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Transform:")
|
|
col.operator("transform.translate")
|
|
col.operator("transform.rotate")
|
|
col.operator("transform.resize", text="Scale")
|
|
props = col.operator("transform.transform", text="Scale Feather")
|
|
props.mode = 'MASK_SHRINKFATTEN'
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Spline:")
|
|
col.operator("mask.delete")
|
|
col.operator("mask.cyclic_toggle")
|
|
col.operator("mask.switch_direction")
|
|
col.operator("mask.handle_type_set")
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Parenting:")
|
|
col.operator("mask.parent_set")
|
|
col.operator("mask.parent_clear")
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Animation:")
|
|
col.operator("mask.shape_key_clear")
|
|
col.operator("mask.shape_key_insert")
|
|
col.operator("mask.shape_key_feather_reset")
|
|
col.operator("mask.shape_key_rekey")
|
|
|
|
|
|
class MASK_MT_mask(Menu):
|
|
bl_label = "Mask"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.delete")
|
|
|
|
layout.separator()
|
|
layout.operator("mask.cyclic_toggle")
|
|
layout.operator("mask.switch_direction")
|
|
layout.operator("mask.normals_make_consistent")
|
|
layout.operator("mask.feather_weight_clear") # TODO, better place?
|
|
|
|
layout.separator()
|
|
layout.operator("mask.parent_clear")
|
|
layout.operator("mask.parent_set")
|
|
|
|
layout.separator()
|
|
layout.menu("MASK_MT_visibility")
|
|
layout.menu("MASK_MT_transform")
|
|
layout.menu("MASK_MT_animation")
|
|
|
|
|
|
class MASK_MT_visibility(Menu):
|
|
bl_label = "Show/Hide"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.hide_view_clear", text="Show Hidden")
|
|
layout.operator("mask.hide_view_set", text="Hide Selected")
|
|
|
|
props = layout.operator("mask.hide_view_set", text="Hide Unselected")
|
|
props.unselected = True
|
|
|
|
|
|
class MASK_MT_transform(Menu):
|
|
bl_label = "Transform"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator("transform.translate")
|
|
layout.operator("transform.rotate")
|
|
layout.operator("transform.resize")
|
|
props = layout.operator("transform.transform", text="Scale Feather")
|
|
props.mode = 'MASK_SHRINKFATTEN'
|
|
|
|
|
|
class MASK_MT_animation(Menu):
|
|
bl_label = "Animation"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.shape_key_clear")
|
|
layout.operator("mask.shape_key_insert")
|
|
layout.operator("mask.shape_key_feather_reset")
|
|
layout.operator("mask.shape_key_rekey")
|
|
|
|
|
|
class MASK_MT_select(Menu):
|
|
bl_label = "Select"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
sc = context.space_data
|
|
|
|
layout.operator("mask.select_border")
|
|
layout.operator("mask.select_circle")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.select_more")
|
|
layout.operator("mask.select_less")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.select_all").action = 'TOGGLE'
|
|
layout.operator("mask.select_all", text="Inverse").action = 'INVERT'
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
bpy.utils.register_module(__name__)
|