Skip to content
Snippets Groups Projects
animation_motion_trail.py 77.2 KiB
Newer Older
# ##### 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 #####


bl_info = {
Campbell Barton's avatar
Campbell Barton committed
    "name": "Motion Trail",
    "author": "Bart Crouch",
    "version": (3, 1, 3),
    "blender": (2, 80, 0),
Campbell Barton's avatar
Campbell Barton committed
    "location": "View3D > Toolbar > Motion Trail tab",
    "warning": "Needs bgl draw update",
    "description": "Display and edit motion trails in the 3D View",
    "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
               "Scripts/Animation/Motion_Trail",
    "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",


import bgl
import blf
import bpy
from bpy_extras import view3d_utils
import math
import mathutils
from bpy.props import (
        BoolProperty,
        EnumProperty,
        FloatProperty,
        IntProperty,
        StringProperty,
        PointerProperty,
        )


# fake fcurve class, used if no fcurve is found for a path
class fake_fcurve():
    def __init__(self, object, index, rotation=False, scale=False):
        # location
        if not rotation and not scale:
            self.loc = object.location[index]
        # scale
        elif scale:
            self.loc = object.scale[index]
        # rotation
        elif rotation == 'QUATERNION':
            self.loc = object.rotation_quaternion[index]
        elif rotation == 'AXIS_ANGLE':
            self.loc = object.rotation_axis_angle[index]
        else:
            self.loc = object.rotation_euler[index]
        self.keyframe_points = []
    def evaluate(self, frame):
        return(self.loc)
    def range(self):
        return([])


# get location curves of the given object
def get_curves(object, child=False):
    if object.animation_data and object.animation_data.action:
        action = object.animation_data.action
        if child:
            # posebone
            curves = [
                    fc for fc in action.fcurves if len(fc.data_path) >= 14 and
                    fc.data_path[-9:] == '.location' and
                    child.name in fc.data_path.split("\"")
                    ]
        else:
            # normal object
            curves = [fc for fc in action.fcurves if fc.data_path == 'location']

    elif object.animation_data and object.animation_data.use_nla:
        curves = []
        strips = []
        for track in object.animation_data.nla_tracks:
            not_handled = [s for s in track.strips]
            while not_handled:
                current_strip = not_handled.pop(-1)
                if current_strip.action:
                    strips.append(current_strip)
                if current_strip.strips:
                    # meta strip
                    not_handled += [s for s in current_strip.strips]
        for strip in strips:
            if child:
                # posebone
                curves = [
                        fc for fc in strip.action.fcurves if
                        len(fc.data_path) >= 14 and fc.data_path[-9:] == '.location' and
                        child.name in fc.data_path.split("\"")
                        ]
            else:
                # normal object
                curves = [fc for fc in strip.action.fcurves if fc.data_path == 'location']
            if curves:
                # use first strip with location fcurves
                break
    else:
        # should not happen?
        curves = []
    # ensure we have three curves per object
    fcx = None
    fcy = None
    fcz = None
    for fc in curves:
        if fc.array_index == 0:
            fcx = fc
        elif fc.array_index == 1:
            fcy = fc
        elif fc.array_index == 2:
            fcz = fc
        fcx = fake_fcurve(object, 0)
        fcy = fake_fcurve(object, 1)
        fcz = fake_fcurve(object, 2)

    return([fcx, fcy, fcz])


# turn screen coordinates (x,y) into world coordinates vector
def screen_to_world(context, x, y):
    depth_vector = view3d_utils.region_2d_to_vector_3d(
                            context.region, context.region_data, [x, y]
                            )
    vector = view3d_utils.region_2d_to_location_3d(
                            context.region, context.region_data, [x, y],
                            depth_vector
                            )
    return(vector)


# turn 3d world coordinates vector into screen coordinate integers (x,y)
def world_to_screen(context, vector):
    prj = context.region_data.perspective_matrix * \
        mathutils.Vector((vector[0], vector[1], vector[2], 1.0))
    width_half = context.region.width / 2.0
    height_half = context.region.height / 2.0

    x = int(width_half + width_half * (prj.x / prj.w))
    y = int(height_half + height_half * (prj.y / prj.w))
    # correction for corner cases in perspective mode
    if prj.w < 0:
        if x < 0:
            x = context.region.width * 2
        else:
            x = context.region.width * -2
        if y < 0:
            y = context.region.height * 2
        else:
            y = context.region.height * -2
    return(x, y)


# calculate location of display_ob in worldspace
def get_location(frame, display_ob, offset_ob, curves):
    if offset_ob:
        bpy.context.scene.frame_set(frame)
        display_mat = getattr(display_ob, "matrix", False)
        if not display_mat:
            # posebones have "matrix", objects have "matrix_world"
            display_mat = display_ob.matrix_world
        if offset_ob:
            loc = display_mat.to_translation() + \
                offset_ob.matrix_world.to_translation()
        else:
            loc = display_mat.to_translation()
    else:
        fcx, fcy, fcz = curves
        locx = fcx.evaluate(frame)
        locy = fcy.evaluate(frame)
        locz = fcz.evaluate(frame)
        loc = mathutils.Vector([locx, locy, locz])
    return(loc)
Loading
Loading full blame...