1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078 |
- # ##### 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 compliant>
- import bpy
- import os
- from bpy.types import Operator
- from bpy.props import FloatProperty
- from mathutils import (
- Vector,
- Matrix,
- )
- def CLIP_spaces_walk(context, all_screens, tarea, tspace, callback, *args):
- screens = bpy.data.screens if all_screens else [context.screen]
- for screen in screens:
- for area in screen.areas:
- if area.type == tarea:
- for space in area.spaces:
- if space.type == tspace:
- callback(space, *args)
- def CLIP_set_viewport_background(context, clip, clip_user):
- def set_background(cam, clip, user):
- bgpic = None
- for x in cam.background_images:
- if x.source == 'MOVIE_CLIP':
- bgpic = x
- break
- if not bgpic:
- bgpic = cam.background_images.new()
- bgpic.source = 'MOVIE_CLIP'
- bgpic.clip = clip
- bgpic.clip_user.proxy_render_size = user.proxy_render_size
- bgpic.clip_user.use_render_undistorted = True
- bgpic.use_camera_clip = False
- cam.show_background_images = True
- scene_camera = context.scene.camera
- if (not scene_camera) or (scene_camera.type != 'CAMERA'):
- return
- set_background(scene_camera.data, clip, clip_user)
- def CLIP_camera_for_clip(context, clip):
- scene = context.scene
- camera = scene.camera
- for ob in scene.objects:
- if ob.type == 'CAMERA':
- for con in ob.constraints:
- if con.type == 'CAMERA_SOLVER':
- cur_clip = scene.active_clip if con.use_active_clip else con.clip
- if cur_clip == clip:
- return ob
- return camera
- def CLIP_track_view_selected(sc, track):
- if track.select_anchor:
- return True
- if sc.show_marker_pattern and track.select_pattern:
- return True
- if sc.show_marker_search and track.select_search:
- return True
- return False
- def CLIP_default_settings_from_track(clip, track, framenr):
- settings = clip.tracking.settings
- width = clip.size[0]
- height = clip.size[1]
- marker = track.markers.find_frame(framenr, exact=False)
- pattern_bb = marker.pattern_bound_box
- pattern = Vector(pattern_bb[1]) - Vector(pattern_bb[0])
- search = marker.search_max - marker.search_min
- pattern[0] = pattern[0] * width
- pattern[1] = pattern[1] * height
- search[0] = search[0] * width
- search[1] = search[1] * height
- settings.default_correlation_min = track.correlation_min
- settings.default_pattern_size = max(pattern[0], pattern[1])
- settings.default_search_size = max(search[0], search[1])
- settings.default_frames_limit = track.frames_limit
- settings.default_pattern_match = track.pattern_match
- settings.default_margin = track.margin
- settings.default_motion_model = track.motion_model
- settings.use_default_brute = track.use_brute
- settings.use_default_normalization = track.use_normalization
- settings.use_default_mask = track.use_mask
- settings.use_default_red_channel = track.use_red_channel
- settings.use_default_green_channel = track.use_green_channel
- settings.use_default_blue_channel = track.use_blue_channel
- settings.default_weight = track.weight
- class CLIP_OT_filter_tracks(bpy.types.Operator):
- """Filter tracks which has weirdly looking spikes in motion curves"""
- bl_label = "Filter Tracks"
- bl_idname = "clip.filter_tracks"
- bl_options = {'UNDO', 'REGISTER'}
- track_threshold: FloatProperty(
- name="Track Threshold",
- description="Filter Threshold to select problematic tracks",
- default=5.0,
- )
- @staticmethod
- def _filter_values(context, threshold):
- def get_marker_coordinates_in_pixels(clip_size, track, frame_number):
- marker = track.markers.find_frame(frame_number)
- return Vector((marker.co[0] * clip_size[0], marker.co[1] * clip_size[1]))
- def marker_velocity(clip_size, track, frame):
- marker_a = get_marker_coordinates_in_pixels(clip_size, track, frame)
- marker_b = get_marker_coordinates_in_pixels(clip_size, track, frame - 1)
- return marker_a - marker_b
- scene = context.scene
- frame_start = scene.frame_start
- frame_end = scene.frame_end
- clip = context.space_data.clip
- clip_size = clip.size[:]
- bpy.ops.clip.clean_tracks(frames=10, action='DELETE_TRACK')
- tracks_to_clean = set()
- for frame in range(frame_start, frame_end + 1):
- # Find tracks with markers in both this frame and the previous one.
- relevant_tracks = [
- track for track in clip.tracking.tracks
- if (track.markers.find_frame(frame) and
- track.markers.find_frame(frame - 1))
- ]
- if not relevant_tracks:
- continue
- # Get average velocity and deselect track.
- average_velocity = Vector((0.0, 0.0))
- for track in relevant_tracks:
- track.select = False
- average_velocity += marker_velocity(clip_size, track, frame)
- if len(relevant_tracks) >= 1:
- average_velocity = average_velocity / len(relevant_tracks)
- # Then find all markers that behave differently than the average.
- for track in relevant_tracks:
- track_velocity = marker_velocity(clip_size, track, frame)
- distance = (average_velocity - track_velocity).length
- if distance > threshold:
- tracks_to_clean.add(track)
- for track in tracks_to_clean:
- track.select = True
- return len(tracks_to_clean)
- @classmethod
- def poll(cls, context):
- space = context.space_data
- return (space.type == 'CLIP_EDITOR') and space.clip
- def execute(self, context):
- num_tracks = self._filter_values(context, self.track_threshold)
- self.report({'INFO'}, "Identified %d problematic tracks" % num_tracks)
- return {'FINISHED'}
- class CLIP_OT_set_active_clip(bpy.types.Operator):
- bl_label = "Set Active Clip"
- bl_idname = "clip.set_active_clip"
- @classmethod
- def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
- def execute(self, context):
- clip = context.space_data.clip
- scene = context.scene
- scene.active_clip = clip
- scene.render.resolution_x = clip.size[0]
- scene.render.resolution_y = clip.size[1]
- return {'FINISHED'}
- class CLIP_OT_track_to_empty(Operator):
- """Create an Empty object which will be copying movement of active track"""
- bl_idname = "clip.track_to_empty"
- bl_label = "Link Empty to Track"
- bl_options = {'UNDO', 'REGISTER'}
- @staticmethod
- def _link_track(context, clip, tracking_object, track):
- sc = context.space_data
- constraint = None
- ob = None
- ob = bpy.data.objects.new(name=track.name, object_data=None)
- context.collection.objects.link(ob)
- ob.select_set(True)
- context.view_layer.objects.active = ob
- for con in ob.constraints:
- if con.type == 'FOLLOW_TRACK':
- constraint = con
- break
- if constraint is None:
- constraint = ob.constraints.new(type='FOLLOW_TRACK')
- constraint.use_active_clip = False
- constraint.clip = sc.clip
- constraint.track = track.name
- constraint.use_3d_position = False
- constraint.object = tracking_object.name
- constraint.camera = CLIP_camera_for_clip(context, clip)
- @classmethod
- def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
- def execute(self, context):
- sc = context.space_data
- clip = sc.clip
- tracking_object = clip.tracking.objects.active
- for track in tracking_object.tracks:
- if CLIP_track_view_selected(sc, track):
- self._link_track(context, clip, tracking_object, track)
- return {'FINISHED'}
- class CLIP_OT_bundles_to_mesh(Operator):
- """Create vertex cloud using coordinates of reconstructed tracks"""
- bl_idname = "clip.bundles_to_mesh"
- bl_label = "3D Markers to Mesh"
- bl_options = {'UNDO', 'REGISTER'}
- @classmethod
- def poll(cls, context):
- sc = context.space_data
- return (sc.type == 'CLIP_EDITOR') and sc.clip
- def execute(self, context):
- from bpy_extras.io_utils import unpack_list
- sc = context.space_data
- clip = sc.clip
- tracking_object = clip.tracking.objects.active
- new_verts = []
- scene = context.scene
- camera = scene.camera
- matrix = Matrix.Identity(4)
- if camera:
- reconstruction = tracking_object.reconstruction
- framenr = scene.frame_current - clip.frame_start + 1
- reconstructed_matrix = reconstruction.cameras.matrix_from_frame(frame=framenr)
- matrix = camera.matrix_world @ reconstructed_matrix.inverted()
- for track in tracking_object.tracks:
- if track.has_bundle and track.select:
- new_verts.append(track.bundle)
- if new_verts:
- mesh = bpy.data.meshes.new(name="Tracks")
- mesh.vertices.add(len(new_verts))
- mesh.vertices.foreach_set("co", unpack_list(new_verts))
- ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
- ob.matrix_world = matrix
- context.collection.objects.link(ob)
- ob.select_set(True)
- context.view_layer.objects.active = ob
- else:
- self.report({'WARNING'}, "No usable tracks selected")
- return {'FINISHED'}
- class CLIP_OT_delete_proxy(Operator):
- """Delete movie clip proxy files from the hard drive"""
- bl_idname = "clip.delete_proxy"
- bl_label = "Delete Proxy"
- bl_options = {'REGISTER'}
- @classmethod
- def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
- sc = context.space_data
- return sc.clip
- def invoke(self, context, event):
- wm = context.window_manager
- return wm.invoke_confirm(self, event)
- @staticmethod
- def _rmproxy(abspath):
- import shutil
- if not os.path.exists(abspath):
- return
- if os.path.isdir(abspath):
- shutil.rmtree(abspath)
- else:
- os.remove(abspath)
- def execute(self, context):
- sc = context.space_data
- clip = sc.clip
- if clip.use_proxy_custom_directory:
- proxydir = clip.proxy.directory
- else:
- clipdir = os.path.dirname(clip.filepath)
- proxydir = os.path.join(clipdir, "BL_proxy")
- clipfile = os.path.basename(clip.filepath)
- proxy = os.path.join(proxydir, clipfile)
- absproxy = bpy.path.abspath(proxy)
- # proxy_<quality>[_undistorted]
- for x in (25, 50, 75, 100):
- d = os.path.join(absproxy, "proxy_%d" % x)
- self._rmproxy(d)
- self._rmproxy(d + "_undistorted")
- self._rmproxy(os.path.join(absproxy, "proxy_%d.avi" % x))
- tc = ("free_run.blen_tc",
- "interp_free_run.blen_tc",
- "record_run.blen_tc")
- for x in tc:
- self._rmproxy(os.path.join(absproxy, x))
- # Remove proxy per-clip directory.
- try:
- os.rmdir(absproxy)
- except OSError:
- pass
- # Remove [custom] proxy directory if empty.
- try:
- absdir = bpy.path.abspath(proxydir)
- os.rmdir(absdir)
- except OSError:
- pass
- return {'FINISHED'}
- class CLIP_OT_set_viewport_background(Operator):
- """Set current movie clip as a camera background in 3D view-port """ \
- """(works only when a 3D view-port is visible)"""
- bl_idname = "clip.set_viewport_background"
- bl_label = "Set as Background"
- bl_options = {'REGISTER'}
- @classmethod
- def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
- sc = context.space_data
- return sc.clip
- def execute(self, context):
- sc = context.space_data
- CLIP_set_viewport_background(context, sc.clip, sc.clip_user)
- return {'FINISHED'}
- class CLIP_OT_constraint_to_fcurve(Operator):
- """Create F-Curves for object which will copy """ \
- """object's movement caused by this constraint"""
- bl_idname = "clip.constraint_to_fcurve"
- bl_label = "Constraint to F-Curve"
- bl_options = {'UNDO', 'REGISTER'}
- def _bake_object(self, scene, ob):
- con = None
- clip = None
- sfra = None
- efra = None
- frame_current = scene.frame_current
- matrices = []
- # Find constraint which would be converting
- # TODO: several camera solvers and track followers would fail,
- # but can't think about real work-flow where it'll be useful
- for x in ob.constraints:
- if x.type in {'CAMERA_SOLVER', 'FOLLOW_TRACK', 'OBJECT_SOLVER'}:
- con = x
- if not con:
- self.report({'ERROR'},
- "Motion Tracking constraint to be converted not found")
- return {'CANCELLED'}
- # Get clip used for parenting.
- if con.use_active_clip:
- clip = scene.active_clip
- else:
- clip = con.clip
- if not clip:
- self.report({'ERROR'},
- "Movie clip to use tracking data from isn't set")
- return {'CANCELLED'}
- if con.type == 'FOLLOW_TRACK' and con.use_3d_position:
- mat = ob.matrix_world.copy()
- ob.constraints.remove(con)
- ob.matrix_world = mat
- return {'FINISHED'}
- # Find start and end frames.
- for track in clip.tracking.tracks:
- if sfra is None:
- sfra = track.markers[0].frame
- else:
- sfra = min(sfra, track.markers[0].frame)
- if efra is None:
- efra = track.markers[-1].frame
- else:
- efra = max(efra, track.markers[-1].frame)
- if sfra is None or efra is None:
- return
- # Store object matrices.
- for x in range(sfra, efra + 1):
- scene.frame_set(x)
- matrices.append(ob.matrix_world.copy())
- ob.animation_data_create()
- # Apply matrices on object and insert key-frames.
- i = 0
- for x in range(sfra, efra + 1):
- scene.frame_set(x)
- ob.matrix_world = matrices[i]
- ob.keyframe_insert("location")
- if ob.rotation_mode == 'QUATERNION':
- ob.keyframe_insert("rotation_quaternion")
- else:
- ob.keyframe_insert("rotation_euler")
- i += 1
- ob.constraints.remove(con)
- scene.frame_set(frame_current)
- def execute(self, context):
- scene = context.scene
- # XXX, should probably use context.selected_editable_objects
- # since selected objects can be from a lib or in hidden layer!
- for ob in scene.objects:
- if ob.select_get():
- self._bake_object(scene, ob)
- return {'FINISHED'}
- class CLIP_OT_setup_tracking_scene(Operator):
- """Prepare scene for compositing 3D objects into this footage"""
- # TODO: it will be great to integrate with other engines (other than Cycles)
- bl_idname = "clip.setup_tracking_scene"
- bl_label = "Setup Tracking Scene"
- bl_options = {'UNDO', 'REGISTER'}
- @classmethod
- def poll(cls, context):
- sc = context.space_data
- if sc.type != 'CLIP_EDITOR':
- return False
- clip = sc.clip
- return clip and clip.tracking.reconstruction.is_valid
- @staticmethod
- def _setupScene(context):
- scene = context.scene
- scene.active_clip = context.space_data.clip
- scene.render.use_motion_blur = True
- @staticmethod
- def _setupWorld(context):
- scene = context.scene
- world = scene.world
- if not world:
- world = bpy.data.worlds.new(name="World")
- scene.world = world
- # Having AO enabled is nice for shadow catcher.
- world.light_settings.use_ambient_occlusion = True
- world.light_settings.distance = 1.0
- if hasattr(scene, "cycles"):
- world.light_settings.ao_factor = 0.05
- @staticmethod
- def _findOrCreateCamera(context):
- scene = context.scene
- if scene.camera:
- return scene.camera
- cam = bpy.data.cameras.new(name="Camera")
- camob = bpy.data.objects.new(name="Camera", object_data=cam)
- scene.collection.objects.link(camob)
- scene.camera = camob
- camob.matrix_local = (
- Matrix.Translation((7.481, -6.508, 5.344)) @
- Matrix.Rotation(0.815, 4, 'Z') @
- Matrix.Rotation(0.011, 4, 'Y') @
- Matrix.Rotation(1.109, 4, 'X')
- )
- return camob
- @staticmethod
- def _setupCamera(context):
- sc = context.space_data
- clip = sc.clip
- tracking = clip.tracking
- camob = CLIP_OT_setup_tracking_scene._findOrCreateCamera(context)
- cam = camob.data
- # Remove all constraints to be sure motion is fine.
- camob.constraints.clear()
- # Append camera solver constraint.
- con = camob.constraints.new(type='CAMERA_SOLVER')
- con.use_active_clip = True
- con.influence = 1.0
- cam.sensor_width = tracking.camera.sensor_width
- cam.lens = tracking.camera.focal_length
- @staticmethod
- def _setupViewport(context):
- sc = context.space_data
- CLIP_set_viewport_background(context, sc.clip, sc.clip_user)
- @staticmethod
- def _setupViewLayers(context):
- scene = context.scene
- view_layers = scene.view_layers
- if not view_layers.get("Foreground"):
- if len(view_layers) == 1:
- fg = view_layers[0]
- fg.name = 'Foreground'
- else:
- fg = view_layers.new("Foreground")
- fg.use_sky = True
- if not view_layers.get("Background"):
- _bg = view_layers.new("Background")
- @staticmethod
- def createCollection(context, collection_name):
- def collection_in_collection(collection, collection_to_query):
- """Return true if collection is in any of the children or """
- """grandchildren of collection_to_query"""
- for child in collection_to_query.children:
- if collection == child:
- return True
- if collection_in_collection(collection, child):
- return True
- master_collection = context.scene.collection
- collection = bpy.data.collections.get(collection_name)
- if collection and collection.library:
- # We need a local collection instead.
- collection = None
- if not collection:
- collection = bpy.data.collections.new(name=collection_name)
- master_collection.children.link(collection)
- else:
- # see if collection is in the scene
- if not collection_in_collection(collection, master_collection):
- master_collection.children.link(collection)
- def _setupCollections(self, context):
- def setup_collection_recursively(collections, collection_name, attr_name):
- for collection in collections:
- if collection.collection.name == collection_name:
- setattr(collection, attr_name, True)
- break
- else:
- setup_collection_recursively(collection.children, collection_name, attr_name)
- collections = context.scene.collection.children
- vlayers = context.scene.view_layers
- if len(collections) == 1:
- collections[0].name = "foreground"
- self.createCollection(context, "foreground")
- self.createCollection(context, "background")
- # rendersettings
- setup_collection_recursively(
- vlayers["Foreground"].layer_collection.children,
- "background",
- "holdout",
- )
- setup_collection_recursively(
- vlayers["Background"].layer_collection.children,
- "foreground",
- "indirect_only",
- )
- @staticmethod
- def _wipeDefaultNodes(tree):
- if len(tree.nodes) != 2:
- return False
- types = [node.type for node in tree.nodes]
- types.sort()
- if types[0] == 'COMPOSITE' and types[1] == 'R_LAYERS':
- while tree.nodes:
- tree.nodes.remove(tree.nodes[0])
- @staticmethod
- def _findNode(tree, type):
- for node in tree.nodes:
- if node.type == type:
- return node
- return None
- @staticmethod
- def _findOrCreateNode(tree, type):
- node = CLIP_OT_setup_tracking_scene._findNode(tree, type)
- if not node:
- node = tree.nodes.new(type=type)
- return node
- @staticmethod
- def _needSetupNodes(context):
- scene = context.scene
- tree = scene.node_tree
- if not tree:
- # No compositor node tree found, time to create it!
- return True
- for node in tree.nodes:
- if node.type in {'MOVIECLIP', 'MOVIEDISTORTION'}:
- return False
- return True
- @staticmethod
- def _offsetNodes(tree):
- for a in tree.nodes:
- for b in tree.nodes:
- if a != b and a.location == b.location:
- b.location += Vector((40.0, 20.0))
- def _setupNodes(self, context):
- if not self._needSetupNodes(context):
- # Compositor nodes were already setup or even changes already
- # do nothing to prevent nodes damage.
- return
- # Enable backdrop for all compositor spaces.
- def setup_space(space):
- space.show_backdrop = True
- CLIP_spaces_walk(context, True, 'NODE_EDITOR', 'NODE_EDITOR',
- setup_space)
- sc = context.space_data
- scene = context.scene
- scene.use_nodes = True
- tree = scene.node_tree
- clip = sc.clip
- need_stabilization = False
- # Remove all the nodes if they came from default node setup.
- # This is simplest way to make it so final node setup is correct.
- self._wipeDefaultNodes(tree)
- # Create nodes.
- rlayer_fg = self._findOrCreateNode(tree, 'CompositorNodeRLayers')
- rlayer_bg = tree.nodes.new(type='CompositorNodeRLayers')
- composite = self._findOrCreateNode(tree, 'CompositorNodeComposite')
- movieclip = tree.nodes.new(type='CompositorNodeMovieClip')
- distortion = tree.nodes.new(type='CompositorNodeMovieDistortion')
- if need_stabilization:
- stabilize = tree.nodes.new(type='CompositorNodeStabilize2D')
- scale = tree.nodes.new(type='CompositorNodeScale')
- shadowcatcher = tree.nodes.new(type='CompositorNodeAlphaOver')
- alphaover = tree.nodes.new(type='CompositorNodeAlphaOver')
- viewer = tree.nodes.new(type='CompositorNodeViewer')
- # Setup nodes.
- movieclip.clip = clip
- distortion.clip = clip
- distortion.distortion_type = 'UNDISTORT'
- if need_stabilization:
- stabilize.clip = clip
- scale.space = 'RENDER_SIZE'
- rlayer_bg.scene = scene
- rlayer_bg.layer = "Background"
- rlayer_fg.scene = scene
- rlayer_fg.layer = "Foreground"
- # Create links.
- tree.links.new(movieclip.outputs["Image"], distortion.inputs["Image"])
- if need_stabilization:
- tree.links.new(distortion.outputs["Image"],
- stabilize.inputs["Image"])
- tree.links.new(stabilize.outputs["Image"], scale.inputs["Image"])
- else:
- tree.links.new(distortion.outputs["Image"], scale.inputs["Image"])
- tree.links.new(scale.outputs["Image"], shadowcatcher.inputs[1])
- tree.links.new(rlayer_bg.outputs["Image"], shadowcatcher.inputs[2])
- tree.links.new(rlayer_fg.outputs["Image"], alphaover.inputs[2])
- tree.links.new(shadowcatcher.outputs["Image"], alphaover.inputs[1])
- tree.links.new(alphaover.outputs["Image"], composite.inputs["Image"])
- tree.links.new(alphaover.outputs["Image"], viewer.inputs["Image"])
- # Place nodes.
- movieclip.location = Vector((-300.0, 350.0))
- distortion.location = movieclip.location
- distortion.location += Vector((200.0, 0.0))
- if need_stabilization:
- stabilize.location = distortion.location
- stabilize.location += Vector((200.0, 0.0))
- scale.location = stabilize.location
- scale.location += Vector((200.0, 0.0))
- else:
- scale.location = distortion.location
- scale.location += Vector((200.0, 0.0))
- rlayer_bg.location = movieclip.location
- rlayer_bg.location -= Vector((0.0, 350.0))
- rlayer_fg.location = rlayer_bg.location
- rlayer_fg.location -= Vector((0.0, 500.0))
- shadowcatcher.location = scale.location
- shadowcatcher.location += Vector((250.0, 0.0))
- alphaover.location = shadowcatcher.location
- alphaover.location += Vector((250.0, -250.0))
- composite.location = alphaover.location
- composite.location += Vector((300.0, -100.0))
- viewer.location = composite.location
- composite.location += Vector((0.0, 200.0))
- # Ensure no nodes were created on the position of existing node.
- self._offsetNodes(tree)
- @staticmethod
- def _createMesh(collection, name, vertices, faces):
- from bpy_extras.io_utils import unpack_list
- mesh = bpy.data.meshes.new(name=name)
- mesh.vertices.add(len(vertices))
- mesh.vertices.foreach_set("co", unpack_list(vertices))
- nbr_loops = len(faces)
- nbr_polys = nbr_loops // 4
- mesh.loops.add(nbr_loops)
- mesh.polygons.add(nbr_polys)
- mesh.polygons.foreach_set("loop_start", range(0, nbr_loops, 4))
- mesh.polygons.foreach_set("loop_total", (4,) * nbr_polys)
- mesh.loops.foreach_set("vertex_index", faces)
- mesh.update()
- ob = bpy.data.objects.new(name=name, object_data=mesh)
- collection.objects.link(ob)
- return ob
- @staticmethod
- def _getPlaneVertices(half_size, z):
- return [(-half_size, -half_size, z),
- (half_size, -half_size, z),
- (half_size, half_size, z),
- (-half_size, half_size, z)]
- def _createGround(self, collection):
- vertices = self._getPlaneVertices(4.0, 0.0)
- faces = [0, 1, 2, 3]
- ob = self._createMesh(collection, "Ground", vertices, faces)
- ob["is_ground"] = True
- return ob
- @staticmethod
- def _findGround(context):
- scene = context.scene
- for ob in scene.objects:
- if ob.type == 'MESH' and "is_ground" in ob:
- return ob
- return None
- @staticmethod
- def _createLight():
- light = bpy.data.lights.new(name="Light", type='POINT')
- lightob = bpy.data.objects.new(name="Light", object_data=light)
- lightob.matrix_local = Matrix.Translation((4.076, 1.005, 5.904))
- return lightob
- def _createSampleObject(self, collection):
- vertices = self._getPlaneVertices(1.0, -1.0) + \
- self._getPlaneVertices(1.0, 1.0)
- faces = (0, 1, 2, 3,
- 4, 7, 6, 5,
- 0, 4, 5, 1,
- 1, 5, 6, 2,
- 2, 6, 7, 3,
- 3, 7, 4, 0)
- return self._createMesh(collection, "Cube", vertices, faces)
- def _setupObjects(self, context):
- def setup_shadow_catcher_objects(collection):
- """Make all the newly created and the old objects of a collection """ \
- """to be properly setup for shadow catch"""
- for ob in collection.objects:
- ob.cycles.is_shadow_catcher = True
- for child in collection.children:
- setup_shadow_catcher_collection(child)
- scene = context.scene
- fg_coll = bpy.data.collections["foreground", None]
- bg_coll = bpy.data.collections["background", None]
- # Ensure all lights are active on foreground and background.
- has_light = False
- has_mesh = False
- for ob in scene.objects:
- if ob.type == 'LIGHT':
- has_light = True
- elif ob.type == 'MESH' and "is_ground" not in ob:
- has_mesh = True
- # Create sample light if there is no lights in the scene.
- if not has_light:
- light = self._createLight()
- fg_coll.objects.link(light)
- bg_coll.objects.link(light)
- # Create sample object if there's no meshes in the scene.
- if not has_mesh:
- ob = self._createSampleObject(fg_coll)
- # Create ground object if needed.
- ground = self._findGround(context)
- if not ground:
- ground = self._createGround(bg_coll)
- # And set everything on background layer to shadow catcher.
- if hasattr(scene, "cycles"):
- setup_shadow_catcher_objects(bg_coll)
- def execute(self, context):
- self._setupScene(context)
- self._setupWorld(context)
- self._setupCamera(context)
- self._setupViewport(context)
- self._setupViewLayers(context)
- self._setupCollections(context)
- self._setupNodes(context)
- self._setupObjects(context)
- return {'FINISHED'}
- class CLIP_OT_track_settings_as_default(Operator):
- """Copy tracking settings from active track to default settings"""
- bl_idname = "clip.track_settings_as_default"
- bl_label = "Track Settings As Default"
- bl_options = {'UNDO', 'REGISTER'}
- @classmethod
- def poll(cls, context):
- sc = context.space_data
- if sc.type != 'CLIP_EDITOR':
- return False
- clip = sc.clip
- return clip and clip.tracking.tracks.active
- def execute(self, context):
- sc = context.space_data
- clip = sc.clip
- track = clip.tracking.tracks.active
- framenr = context.scene.frame_current - clip.frame_start + 1
- CLIP_default_settings_from_track(clip, track, framenr)
- return {'FINISHED'}
- class CLIP_OT_track_settings_to_track(bpy.types.Operator):
- """Copy tracking settings from active track to selected tracks"""
- bl_label = "Copy Track Settings"
- bl_idname = "clip.track_settings_to_track"
- bl_options = {'UNDO', 'REGISTER'}
- _attrs_track = (
- "correlation_min",
- "frames_limit",
- "pattern_match",
- "margin",
- "motion_model",
- "use_brute",
- "use_normalization",
- "use_mask",
- "use_red_channel",
- "use_green_channel",
- "use_blue_channel",
- "weight"
- )
- _attrs_marker = (
- "pattern_corners",
- "search_min",
- "search_max",
- )
- @classmethod
- def poll(cls, context):
- space = context.space_data
- if space.type != 'CLIP_EDITOR':
- return False
- clip = space.clip
- return clip and clip.tracking.tracks.active
- def execute(self, context):
- space = context.space_data
- clip = space.clip
- track = clip.tracking.tracks.active
- framenr = context.scene.frame_current - clip.frame_start + 1
- marker = track.markers.find_frame(framenr, exact=False)
- for t in clip.tracking.tracks:
- if t.select and t != track:
- marker_selected = t.markers.find_frame(framenr, exact=False)
- for attr in self._attrs_track:
- setattr(t, attr, getattr(track, attr))
- for attr in self._attrs_marker:
- setattr(marker_selected, attr, getattr(marker, attr))
- return {'FINISHED'}
- classes = (
- CLIP_OT_bundles_to_mesh,
- CLIP_OT_constraint_to_fcurve,
- CLIP_OT_delete_proxy,
- CLIP_OT_filter_tracks,
- CLIP_OT_set_active_clip,
- CLIP_OT_set_viewport_background,
- CLIP_OT_setup_tracking_scene,
- CLIP_OT_track_settings_as_default,
- CLIP_OT_track_settings_to_track,
- CLIP_OT_track_to_empty,
- )
|