예제 #1
0
    def process(self):
        if not self.outputs['Quaternions'].is_linked:
            return

        inputs = self.inputs

        quaternion_list = []

        if self.mode == "WXYZ":
            I = [inputs[n].sv_get()[0] for n in "WXYZ"]
            params = match_long_repeat(I)
            for wxyz in zip(*params):
                q = Quaternion(wxyz)
                if self.normalize:
                    q.normalize()
                quaternion_list.append(q)

        elif self.mode == "SCALARVECTOR":
            I = [inputs[n].sv_get()[0] for n in ["Scalar", "Vector"]]
            params = match_long_repeat(I)
            for scalar, vector in zip(*params):
                q = Quaternion([scalar, *vector])
                if self.normalize:
                    q.normalize()
                quaternion_list.append(q)

        elif self.mode == "EULER":
            I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"]
            params = match_long_repeat(I)
            # conversion factor from the current angle units to radians
            au = self.radians_conversion_factor()
            for angleX, angleY, angleZ in zip(*params):
                euler = Euler((angleX * au, angleY * au, angleZ * au),
                              self.euler_order)
                q = euler.to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternion_list.append(q)

        elif self.mode == "AXISANGLE":
            I = [inputs[n].sv_get()[0] for n in ["Axis", "Angle"]]
            params = match_long_repeat(I)
            # conversion factor from the current angle units to radians
            au = self.radians_conversion_factor()
            for axis, angle in zip(*params):
                q = Quaternion(axis, angle * au)
                if self.normalize:
                    q.normalize()
                quaternion_list.append(q)

        elif self.mode == "MATRIX":
            input_M = inputs["Matrix"].sv_get(default=id_mat)
            for m in input_M:
                q = Matrix(m).to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternion_list.append(q)

        self.outputs['Quaternions'].sv_set(quaternion_list)
예제 #2
0
def mapping_node_order_flip(orientation):
    """
    Flip euler order of mapping shader node
    see: Blender #a1ffb49
    """
    rot = Euler(orientation)
    rot.order = 'ZYX'
    quat = rot.to_quaternion()
    return quat.to_euler('XYZ')
예제 #3
0
    def process(self):
        if not self.outputs['Quaternions'].is_linked:
            return

        inputs = self.inputs

        quaternionList = []

        if self.mode == "WXYZ":
            I = [inputs[n].sv_get()[0] for n in "WXYZ"]
            params = match_long_repeat(I)
            for wxyz in zip(*params):
                q = Quaternion(wxyz)
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "SCALARVECTOR":
            I = [inputs[n].sv_get()[0] for n in ["Scalar", "Vector"]]
            params = match_long_repeat(I)
            for scalar, vector in zip(*params):
                q = Quaternion([scalar, *vector])
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "EULER":
            I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"]
            params = match_long_repeat(I)
            au = angleConversion[self.angleUnits]
            for angleX, angleY, angleZ in zip(*params):
                euler = Euler((angleX * au, angleY * au, angleZ * au), self.eulerOrder)
                q = euler.to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "AXISANGLE":
            I = [inputs[n].sv_get()[0] for n in ["Axis", "Angle"]]
            params = match_long_repeat(I)
            au = angleConversion[self.angleUnits]
            for axis, angle in zip(*params):
                q = Quaternion(axis, angle * au)
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "MATRIX":
            input_M = inputs["Matrix"].sv_get(default=idMat)
            for m in input_M:
                q = Matrix(m).to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        self.outputs['Quaternions'].sv_set([quaternionList])
예제 #4
0
        def process(self):
            if not any(socket.is_linked for socket in self.outputs):
                return

            location_s = self.inputs['Location'].sv_get()
            month_s = self.inputs['Month'].sv_get()
            day_s = self.inputs['Day'].sv_get()
            hour_s = self.inputs['Hour'].sv_get()

            altitude_s, azimuth_s = [], []
            sun_pos = []
            for location, months, days, hours in zip_long_repeat(
                    location_s, month_s, day_s, hour_s):
                sp = Sunpath.from_location(location)

                # sp = Sunpath.from_location(location, north_angle=40)
                altitude, azimuth = [], []
                for mo, day, ho in zip_long_repeat(months, days, hours):

                    sun = sp.calculate_sun(month=mo, day=day, hour=ho)
                    alt_l = sun.altitude
                    azi_l = sun.azimuth
                    altitude.append(sun.altitude)
                    azimuth.append(sun.azimuth)
                    angles = (alt_l * pi / 180, 0,
                              -azi_l * pi / 180 + self.north_angle * pi / 180)
                    euler = Euler(angles, 'XYZ')
                    mat_r = euler.to_quaternion().to_matrix().to_4x4()
                    pos = mat_r @ Vector((0, self.sun_dist, 0))
                    mat_t = Matrix.Translation(pos)
                    angles = ((90 - alt_l) * pi / 180, 0,
                              (180 - azi_l) * pi / 180 +
                              self.north_angle * pi / 180)
                    euler = Euler(angles, 'XYZ')
                    mat_r = euler.to_quaternion().to_matrix().to_4x4()
                    m = mat_t @ mat_r
                    sun_pos.append(m)
                altitude_s.append(altitude)
                azimuth_s.append(azimuth)
            self.outputs['Altitude'].sv_set(altitude_s)
            self.outputs['Azimuth'].sv_set(azimuth_s)
            self.outputs['Sun Position'].sv_set(sun_pos)
예제 #5
0
			def main(_self, entity_object, plugin_data):               
				# Restore Euler structure
				new_orientation = [0, 0, 0]
				for value in plugin_data:
					new_orientation[value[1]] = value[0]
				euler_orientation = Euler(new_orientation)
				
				# Interpolate between data and use for extrapolation
				interpolator = setdefaultdict(entity_object, "interpolate_orientation", interpolate([entity_object.worldOrientation.to_quaternion(), 0.0]))     
				orientation = [euler_orientation.to_quaternion(), time.time()]
				interpolator.update(orientation)            
					
				entity_object.worldOrientation = euler_orientation.to_matrix()
예제 #6
0
def update_rotations_to_quat(obj, path_check, new_data_path_prefix=""):

    obj.rotation_mode = "QUATERNION"
    actions = [
        action for action in bpy.data.actions
        if action.name.startswith(obj.name)
    ]

    for action in actions:

        data_paths = set([
            fcurve.data_path for fcurve in action.fcurves
            if path_check(fcurve.data_path)
        ])

        for data_path in data_paths:

            fcurves = [
                fcurve for fcurve in action.fcurves
                if fcurve.data_path == data_path
            ]
            group = next((fcurve.group
                          for fcurve in fcurves if fcurve.group is not None),
                         None)

            if not fcurves:
                continue

            frames = fcurves.frames_matching(action, data_path)

            euler = Euler((0.0, 0.0, 0.0), "XYZ")
            degrees = [0.0, 0.0, 0.0]

            for fr in frames:
                for fc in fcurves:
                    euler[fc.array_index] = fc.evaluate(fr)
                    degrees[fc.array_index] = math.degrees(
                        euler[fc.array_index])

                quat = euler.to_quaternion()

                fcurves.add_keyframe_quat(
                    action,
                    quat,
                    fr,
                    "{0}rotation_quaternion".format(new_data_path_prefix),
                    group,
                )

            for fcurve in fcurves:
                action.fcurves.remove(fcurve)
예제 #7
0
    def test_loc_rot_scale(self):
        euler = Euler((math.radians(90), 0, math.radians(90)), 'ZYX')
        expected = Matrix(
            ((0, -5, 0, 1), (0, 0, -6, 2), (4, 0, 0, 3), (0, 0, 0, 1)))

        result = Matrix.LocRotScale((1, 2, 3), euler, (4, 5, 6))
        self.assertAlmostEqualMatrix(result, expected, 4)

        result = Matrix.LocRotScale((1, 2, 3), euler.to_quaternion(),
                                    (4, 5, 6))
        self.assertAlmostEqualMatrix(result, expected, 4)

        result = Matrix.LocRotScale((1, 2, 3), euler.to_matrix(), (4, 5, 6))
        self.assertAlmostEqualMatrix(result, expected, 4)
예제 #8
0
    def process(self):
        if not self.outputs['Quaternions'].is_linked:
            return

        inputs = self.inputs

        quaternionList = []

        if self.mode == "WXYZ":
            I = [inputs[n].sv_get()[0] for n in "WXYZ"]
            params = match_long_repeat(I)
            for wxyz in zip(*params):
                q = Quaternion(wxyz)
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "EULER":
            I = [inputs["Angle " + n].sv_get()[0] for n in "XYZ"]
            params = match_long_repeat(I)
            au = angleConversion[self.angleUnits]
            for angleX, angleY, angleZ in zip(*params):
                euler = Euler((angleX * au, angleY * au, angleZ * au),
                              self.eulerOrder)
                q = euler.to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "AXISANGLE":
            I = [inputs[n].sv_get()[0] for n in {"Axis", "Angle"}]
            params = match_long_repeat(I)
            au = angleConversion[self.angleUnits]
            for axis, angle in zip(*params):
                q = Quaternion(axis, angle * au)
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        elif self.mode == "MATRIX":
            input_M = inputs["Matrix"].sv_get(default=idMat)
            for m in input_M:
                q = Matrix(m).to_quaternion()
                if self.normalize:
                    q.normalize()
                quaternionList.append(q)

        self.outputs['Quaternions'].sv_set([quaternionList])
예제 #9
0
def draw_callback_px(self, context):
    font_id = 0
    region = context.region
    UIColor = (0.992, 0.5518, 0.0, 1.0)

    # Cut Type
    RECTANGLE = 0
    LINE = 1
    CIRCLE = 2
    self.carver_prefs = context.preferences.addons[__package__].preferences

    # Color
    color1 = (1.0, 1.0, 1.0, 1.0)
    color2 = UIColor

    #The mouse is outside the active region
    if not self.in_view_3d:
        color1 = color2 = (1.0, 0.2, 0.1, 1.0)

    # Primitives type
    PrimitiveType = "Rectangle"
    if self.CutType == CIRCLE:
        PrimitiveType = "Circle"
    if self.CutType == LINE:
        PrimitiveType = "Line"

    # Width screen
    overlap = context.preferences.system.use_region_overlap

    t_panel_width = 0
    if overlap:
        for region in context.area.regions:
            if region.type == 'TOOLS':
                t_panel_width = region.width

    # Initial position
    region_width = int(region.width / 2.0)
    y_txt = 10

    # Draw the center command from bottom to top

    # Get the size of the text
    text_size = 18 if region.width >= 850 else 12
    blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale,
                          0)), 72)

    # Help Display
    if (self.ObjectMode is False) and (self.ProfileMode is False):

        # Depth Cursor
        TypeStr = "Cursor Depth [" + self.carver_prefs.Key_Depth + "]"
        BoolStr = "(ON)" if self.snapCursor else "(OFF)"
        help_txt = [[TypeStr, BoolStr]]

        # Close poygonal shape
        if self.CreateMode and self.CutType == LINE:
            TypeStr = "Close [" + self.carver_prefs.Key_Close + "]"
            BoolStr = "(ON)" if self.Closed else "(OFF)"
            help_txt += [[TypeStr, BoolStr]]

        if self.CreateMode is False:
            # Apply Booleans
            TypeStr = "Apply Operations [" + self.carver_prefs.Key_Apply + "]"
            BoolStr = "(OFF)" if self.dont_apply_boolean else "(ON)"
            help_txt += [[TypeStr, BoolStr]]

            #Auto update for bevel
            TypeStr = "Bevel Update [" + self.carver_prefs.Key_Update + "]"
            BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)"
            help_txt += [[TypeStr, BoolStr]]

        # Circle subdivisions
        if self.CutType == CIRCLE:
            TypeStr = "Subdivisions [" + self.carver_prefs.Key_Subrem + "][" + self.carver_prefs.Key_Subadd + "]"
            BoolStr = str((int(360 / self.stepAngle[self.step])))
            help_txt += [[TypeStr, BoolStr]]

        if self.CreateMode:
            help_txt += [["Type [Space]", PrimitiveType]]
        else:
            help_txt += [["Cut Type [Space]", PrimitiveType]]

    else:
        # Instantiate
        TypeStr = "Instantiate [" + self.carver_prefs.Key_Instant + "]"
        BoolStr = "(ON)" if self.Instantiate else "(OFF)"
        help_txt = [[TypeStr, BoolStr]]

        # Random rotation
        if self.alt:
            TypeStr = "Random Rotation [" + self.carver_prefs.Key_Randrot + "]"
            BoolStr = "(ON)" if self.RandomRotation else "(OFF)"
            help_txt += [[TypeStr, BoolStr]]

        # Thickness
        if self.BrushSolidify:
            TypeStr = "Thickness [" + self.carver_prefs.Key_Depth + "]"
            if self.ProfileMode:
                BoolStr = str(
                    round(self.ProfileBrush.modifiers["CT_SOLIDIFY"].thickness,
                          2))
            if self.ObjectMode:
                BoolStr = str(
                    round(self.ObjectBrush.modifiers["CT_SOLIDIFY"].thickness,
                          2))
            help_txt += [[TypeStr, BoolStr]]

        # Brush depth
        if (self.ObjectMode):
            TypeStr = "Carve Depth [" + self.carver_prefs.Key_Depth + "]"
            BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2))
            help_txt += [[TypeStr, BoolStr]]

            TypeStr = "Brush Depth [" + self.carver_prefs.Key_BrushDepth + "]"
            BoolStr = str(round(self.BrushDepthOffset, 2))
            help_txt += [[TypeStr, BoolStr]]

    help_txt, bloc_height, max_option, max_key, comma = get_text_info(
        self, context, help_txt)
    xCmd = region_width - (max_option + max_key + comma) / 2
    draw_string(self,
                color1,
                color2,
                xCmd,
                y_txt,
                help_txt,
                max_option,
                divide=2)

    # Separator (Line)
    LineWidth = (max_option + max_key + comma) / 2
    if region.width >= 850:
        LineWidth = 140

    LineWidth = (max_option + max_key + comma)
    coords = [(int(region_width - LineWidth/2), y_txt + bloc_height + 8), \
        (int(region_width + LineWidth/2), y_txt + bloc_height + 8)]
    draw_shader(self, UIColor, 1, 'LINES', coords, self.carver_prefs.LineWidth)

    # Command Display
    if self.CreateMode and ((self.ObjectMode is False) and
                            (self.ProfileMode is False)):
        BooleanMode = "Create"
    else:
        if self.ObjectMode or self.ProfileMode:
            BooleanType = "Difference) [T]" if self.BoolOps == self.difference else "Union) [T]"
            BooleanMode = \
             "Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType
        else:
            BooleanMode = \
             "Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool"

    # Display boolean mode
    text_size = 40 if region.width >= 850 else 20
    blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale,
                          0)), 72)

    draw_string(self, color2, color2, region_width - (blf.dimensions(0, BooleanMode)[0]) / 2, \
       y_txt + bloc_height + 16, BooleanMode, 0, divide = 2)

    if region.width >= 850:

        if self.AskHelp is False:
            # "H for Help" text
            blf.size(0,
                     int(round(13 * bpy.context.preferences.view.ui_scale, 0)),
                     72)
            help_txt = "[" + self.carver_prefs.Key_Help + "] for help"
            txt_width = blf.dimensions(0, help_txt)[0]
            txt_height = (blf.dimensions(0, "gM")[1] * 1.45)

            # Draw a rectangle and put the text "H for Help"
            xrect = 40
            yrect = 40
            rect_vertices = [(xrect - 5, yrect - 5), (xrect + txt_width + 5, yrect - 5), \
                 (xrect + txt_width + 5, yrect + txt_height + 5), (xrect - 5, yrect + txt_height + 5)]
            draw_shader(self, (0.0, 0.0, 0.0), 0.3, 'TRI_FAN', rect_vertices,
                        self.carver_prefs.LineWidth)
            draw_string(self, color1, color2, xrect, yrect, help_txt, 0)

        else:
            #Draw the help text
            xHelp = 30 + t_panel_width
            yHelp = 10

            if self.ObjectMode or self.ProfileMode:
                if self.ProfileMode:
                    help_txt = [["Object Mode", self.carver_prefs.Key_Brush]]
                else:
                    help_txt = [["Cut Mode", self.carver_prefs.Key_Brush]]

            else:
                help_txt =[
                   ["Profil Brush", self.carver_prefs.Key_Brush],\
                   ["Move Cursor", "Ctrl + LMB"]
                   ]

            if (self.ObjectMode is False) and (self.ProfileMode is False):
                if self.CreateMode is False:
                    help_txt +=[
                        ["Create geometry", self.carver_prefs.Key_Create],\
                        ]
                else:
                    help_txt +=[
                        ["Cut", self.carver_prefs.Key_Create],\
                        ]
                if self.CutMode == RECTANGLE:
                    help_txt +=[
                        ["Dimension", "MouseMove"],\
                        ["Move all", "Alt"],\
                        ["Validate", "LMB"],\
                        ["Rebool", "Shift"]
                        ]

                elif self.CutMode == CIRCLE:
                    help_txt +=[
                        ["Rotation and Radius", "MouseMove"],\
                        ["Move all", "Alt"],\
                        ["Subdivision", self.carver_prefs.Key_Subrem + " " + self.carver_prefs.Key_Subadd],\
                        ["Incremental rotation", "Ctrl"],\
                        ["Rebool", "Shift"]
                        ]

                elif self.CutMode == LINE:
                    help_txt +=[
                        ["Dimension", "MouseMove"],\
                        ["Move all", "Alt"],\
                        ["Validate", "Space"],\
                        ["Rebool", "Shift"],\
                        ["Snap", "Ctrl"],\
                        ["Scale Snap", "WheelMouse"],\
                        ]
            else:
                # ObjectMode
                help_txt +=[
                    ["Difference", "Space"],\
                    ["Rebool", "Shift + Space"],\
                    ["Duplicate", "Alt + Space"],\
                    ["Scale", self.carver_prefs.Key_Scale],\
                    ["Rotation", "LMB + Move"],\
                    ["Step Angle", "CTRL + LMB + Move"],\
                    ]

                if self.ProfileMode:
                    help_txt += [[
                        "Previous or Next Profile",
                        self.carver_prefs.Key_Subadd + " " +
                        self.carver_prefs.Key_Subrem
                    ]]

                help_txt +=[
                    ["Create / Delete rows",  chr(8597)],\
                    ["Create / Delete cols",  chr(8596)],\
                    ["Gap for rows or columns",  self.carver_prefs.Key_Gapy + " " + self.carver_prefs.Key_Gapx]
                    ]

            blf.size(0,
                     int(round(15 * bpy.context.preferences.view.ui_scale, 0)),
                     72)
            help_txt, bloc_height, max_option, max_key, comma = get_text_info(
                self, context, help_txt)
            draw_string(self, color1, color2, xHelp, yHelp, help_txt,
                        max_option)

    if self.ProfileMode:
        xrect = region.width - t_panel_width - 80
        yrect = 80
        coords = [(xrect, yrect), (xrect + 60, yrect),
                  (xrect + 60, yrect - 60), (xrect, yrect - 60)]

        # Draw rectangle background in the lower right
        draw_shader(self, (0.0, 0.0, 0.0),
                    0.3,
                    'TRI_FAN',
                    coords,
                    size=self.carver_prefs.LineWidth)

        # Use numpy to get the vertices and indices of the profile object to draw
        WidthProfil = 50
        location = Vector((region.width - t_panel_width - WidthProfil, 50, 0))
        ProfilScale = 20.0
        coords = []
        mesh = bpy.data.meshes[self.Profils[self.nProfil][0]]
        mesh.calc_loop_triangles()
        vertices = np.empty((len(mesh.vertices), 3), 'f')
        indices = np.empty((len(mesh.loop_triangles), 3), 'i')
        mesh.vertices.foreach_get("co",
                                  np.reshape(vertices,
                                             len(mesh.vertices) * 3))
        mesh.loop_triangles.foreach_get(
            "vertices", np.reshape(indices,
                                   len(mesh.loop_triangles) * 3))

        for idx, vals in enumerate(vertices):
            coords.append([
                vals[0] * ProfilScale + location.x,
                vals[1] * ProfilScale + location.y,
                vals[2] * ProfilScale + location.z
            ])

        #Draw the silhouette of the mesh
        draw_shader(self,
                    UIColor,
                    0.5,
                    'TRIS',
                    coords,
                    size=self.carver_prefs.LineWidth,
                    indices=indices)

    if self.CutMode:

        if len(self.mouse_path) > 1:
            x0 = self.mouse_path[0][0]
            y0 = self.mouse_path[0][1]
            x1 = self.mouse_path[1][0]
            y1 = self.mouse_path[1][1]

        # Cut rectangle
        if self.CutType == RECTANGLE:
            coords = [
            (x0 + self.xpos, y0 + self.ypos), (x1 + self.xpos, y0 + self.ypos), \
            (x1 + self.xpos, y1 + self.ypos), (x0 + self.xpos, y1 + self.ypos)
            ]
            indices = ((0, 1, 2), (2, 0, 3))

            self.rectangle_coord = coords

            draw_shader(self,
                        UIColor,
                        1,
                        'LINE_LOOP',
                        coords,
                        size=self.carver_prefs.LineWidth)

            #Draw points
            draw_shader(self, UIColor, 1, 'POINTS', coords, size=3)

            if self.shift or self.CreateMode:
                draw_shader(self,
                            UIColor,
                            0.5,
                            'TRIS',
                            coords,
                            size=self.carver_prefs.LineWidth,
                            indices=indices)

            # Draw grid (based on the overlay options) to show the incremental snapping
            if self.ctrl:
                mini_grid(self, context, UIColor)

        # Cut Line
        elif self.CutType == LINE:
            coords = []
            indices = []
            top_grid = False

            for idx, vals in enumerate(self.mouse_path):
                coords.append([vals[0] + self.xpos, vals[1] + self.ypos])
                indices.append([idx])

            # Draw lines
            if self.Closed:
                draw_shader(self,
                            UIColor,
                            1.0,
                            'LINE_LOOP',
                            coords,
                            size=self.carver_prefs.LineWidth)
            else:
                draw_shader(self,
                            UIColor,
                            1.0,
                            'LINE_STRIP',
                            coords,
                            size=self.carver_prefs.LineWidth)

            # Draw points
            draw_shader(self, UIColor, 1.0, 'POINTS', coords, size=3)

            # Draw polygon
            if (self.shift) or (self.CreateMode and self.Closed):
                draw_shader(self,
                            UIColor,
                            0.5,
                            'TRI_FAN',
                            coords,
                            size=self.carver_prefs.LineWidth)

            # Draw grid (based on the overlay options) to show the incremental snapping
            if self.ctrl:
                mini_grid(self, context, UIColor)

        # Circle Cut
        elif self.CutType == CIRCLE:
            # Create a circle using a tri fan
            tris_coords, indices = draw_circle(self, x0, y0)

            # Remove the vertex in the center to get the outer line of the circle
            line_coords = tris_coords[1:]
            draw_shader(self,
                        UIColor,
                        1.0,
                        'LINE_LOOP',
                        line_coords,
                        size=self.carver_prefs.LineWidth)

            if self.shift or self.CreateMode:
                draw_shader(self,
                            UIColor,
                            0.5,
                            'TRIS',
                            tris_coords,
                            size=self.carver_prefs.LineWidth,
                            indices=indices)

    if (self.ObjectMode
            or self.ProfileMode) and len(self.CurrentSelection) > 0:
        if self.ShowCursor:
            region = context.region
            rv3d = context.space_data.region_3d

            if self.ObjectMode:
                ob = self.ObjectBrush
            if self.ProfileMode:
                ob = self.ProfileBrush
            mat = ob.matrix_world

            # 50% alpha, 2 pixel width line
            bgl.glEnable(bgl.GL_BLEND)

            bbox = [mat @ Vector(b) for b in ob.bound_box]
            objBBDiagonal = objDiagonal(self.CurrentSelection[0])

            if self.shift:
                gl_size = 4
                UIColor = (0.5, 1.0, 0.0, 1.0)
            else:
                gl_size = 2
                UIColor = (1.0, 0.8, 0.0, 1.0)

            line_coords = []
            idx = 0
            CRadius = ((bbox[7] - bbox[0]).length) / 2
            for i in range(int(len(self.CLR_C) / 3)):
                vector3d = (self.CLR_C[idx * 3] * CRadius + self.CurLoc.x, \
                   self.CLR_C[idx * 3 + 1] * CRadius + self.CurLoc.y, \
                   self.CLR_C[idx * 3 + 2] * CRadius + self.CurLoc.z)
                vector2d = bpy_extras.view3d_utils.location_3d_to_region_2d(
                    region, rv3d, vector3d)
                if vector2d is not None:
                    line_coords.append((vector2d[0], vector2d[1]))
                idx += 1
            if len(line_coords) > 0:
                draw_shader(self,
                            UIColor,
                            1.0,
                            'LINE_LOOP',
                            line_coords,
                            size=gl_size)

            # Object display
            if self.quat_rot is not None:
                ob.location = self.CurLoc
                v = Vector()
                v.x = v.y = 0.0
                v.z = self.BrushDepthOffset
                ob.location += self.quat_rot @ v

                e = Euler()
                e.x = 0.0
                e.y = 0.0
                e.z = self.aRotZ / 25.0

                qe = e.to_quaternion()
                qRot = self.quat_rot @ qe
                ob.rotation_mode = 'QUATERNION'
                ob.rotation_quaternion = qRot
                ob.rotation_mode = 'XYZ'

                if self.ProfileMode:
                    if self.ProfileBrush is not None:
                        self.ProfileBrush.location = self.CurLoc
                        self.ProfileBrush.rotation_mode = 'QUATERNION'
                        self.ProfileBrush.rotation_quaternion = qRot
                        self.ProfileBrush.rotation_mode = 'XYZ'

    # Opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)
예제 #10
0
def duplicateObject(self):
    if self.Instantiate:
        bpy.ops.object.duplicate_move_linked(
            OBJECT_OT_duplicate={
                "linked": True,
                "mode": 'TRANSLATION',
            },
            TRANSFORM_OT_translate={
                "value": (0, 0, 0),
            },
        )
    else:
        bpy.ops.object.duplicate_move(
            OBJECT_OT_duplicate={
                "linked": False,
                "mode": 'TRANSLATION',
            },
            TRANSFORM_OT_translate={
                "value": (0, 0, 0),
            },
        )

    ob_new = bpy.context.active_object

    ob_new.location = self.CurLoc
    v = Vector()
    v.x = v.y = 0.0
    v.z = self.BrushDepthOffset
    ob_new.location += self.qRot * v

    if self.ObjectMode:
        ob_new.scale = self.ObjectBrush.scale
    if self.ProfileMode:
        ob_new.scale = self.ProfileBrush.scale

    e = Euler()
    e.x = e.y = 0.0
    e.z = self.aRotZ / 25.0

    # If duplicate with a grid, no random rotation (each mesh in the grid is already rotated randomly)
    if (self.alt is True) and ((self.nbcol + self.nbrow) < 3):
        if self.RandomRotation:
            e.z += random.random()

    qe = e.to_quaternion()
    qRot = self.qRot * qe
    ob_new.rotation_mode = 'QUATERNION'
    ob_new.rotation_quaternion = qRot
    ob_new.rotation_mode = 'XYZ'

    if (ob_new.display_type == "WIRE") and (self.BrushSolidify is False):
        ob_new.hide_viewport = True

    if self.BrushSolidify:
        ob_new.display_type = "SOLID"
        ob_new.show_in_front = False

    for o in bpy.context.selected_objects:
        UndoAdd(self, "DUPLICATE", o)

    if len(bpy.context.selected_objects) > 0:
        bpy.ops.object.select_all(action='TOGGLE')
    for o in self.all_sel_obj_list:
        o.select_set(True)

    bpy.context.view_layer.objects.active = self.OpsObj
예제 #11
0
def bvh_node_dict2armature(context,
                           bvh_name,
                           bvh_nodes,
                           rotate_mode='XYZ',
                           frame_start=1,
                           IMPORT_LOOP=False,
                           global_matrix=None,
                           ):

    if frame_start < 1:
        frame_start = 1

    # Add the new armature,
    scene = context.scene
    for obj in scene.objects:
        obj.select = False

    arm_data = bpy.data.armatures.new(bvh_name)
    arm_ob = bpy.data.objects.new(bvh_name, arm_data)

    scene.objects.link(arm_ob)

    arm_ob.select = True
    scene.objects.active = arm_ob

    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)

    # Get the average bone length for zero length bones, we may not use this.
    average_bone_length = 0.0
    nonzero_count = 0
    for bvh_node in bvh_nodes.values():
        l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length
        if l:
            average_bone_length += l
            nonzero_count += 1

    # Very rare cases all bones couldbe zero length???
    if not average_bone_length:
        average_bone_length = 0.1
    else:
        # Normal operation
        average_bone_length = average_bone_length / nonzero_count

    # XXX, annoying, remove bone.
    while arm_data.edit_bones:
        arm_ob.edit_bones.remove(arm_data.edit_bones[-1])

    ZERO_AREA_BONES = []
    for name, bvh_node in bvh_nodes.items():
        # New editbone
        bone = bvh_node.temp = arm_data.edit_bones.new(name)

        bone.head = bvh_node.rest_head_world
        bone.tail = bvh_node.rest_tail_world

        # Zero Length Bones! (an exceptional case)
        if (bone.head - bone.tail).length < 0.001:
            print("\tzero length bone found:", bone.name)
            if bvh_node.parent:
                ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local
                if ofs.length:  # is our parent zero length also?? unlikely
                    bone.tail = bone.tail - ofs
                else:
                    bone.tail.y = bone.tail.y + average_bone_length
            else:
                bone.tail.y = bone.tail.y + average_bone_length

            ZERO_AREA_BONES.append(bone.name)

    for bvh_node in bvh_nodes.values():
        if bvh_node.parent:
            # bvh_node.temp is the Editbone

            # Set the bone parent
            bvh_node.temp.parent = bvh_node.parent.temp

            # Set the connection state
            if not bvh_node.has_loc and\
            bvh_node.parent and\
            bvh_node.parent.temp.name not in ZERO_AREA_BONES and\
            bvh_node.parent.rest_tail_local == bvh_node.rest_head_local:
                bvh_node.temp.use_connect = True

    # Replace the editbone with the editbone name,
    # to avoid memory errors accessing the editbone outside editmode
    for bvh_node in bvh_nodes.values():
        bvh_node.temp = bvh_node.temp.name

    # Now Apply the animation to the armature

    # Get armature animation data
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

    pose = arm_ob.pose
    pose_bones = pose.bones

    if rotate_mode == 'NATIVE':
        for bvh_node in bvh_nodes.values():
            bone_name = bvh_node.temp  # may not be the same name as the bvh_node, could have been shortened.
            pose_bone = pose_bones[bone_name]
            pose_bone.rotation_mode = bvh_node.rot_order_str

    elif rotate_mode != 'QUATERNION':
        for pose_bone in pose_bones:
            pose_bone.rotation_mode = rotate_mode
    else:
        # Quats default
        pass

    context.scene.update()

    arm_ob.animation_data_create()
    action = bpy.data.actions.new(name=bvh_name)
    arm_ob.animation_data.action = action

    # Replace the bvh_node.temp (currently an editbone)
    # With a tuple  (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
    for bvh_node in bvh_nodes.values():
        bone_name = bvh_node.temp  # may not be the same name as the bvh_node, could have been shortened.
        pose_bone = pose_bones[bone_name]
        rest_bone = arm_data.bones[bone_name]
        bone_rest_matrix = rest_bone.matrix_local.to_3x3()

        bone_rest_matrix_inv = Matrix(bone_rest_matrix)
        bone_rest_matrix_inv.invert()

        bone_rest_matrix_inv.resize_4x4()
        bone_rest_matrix.resize_4x4()
        bvh_node.temp = (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv)

    # Make a dict for fast access without rebuilding a list all the time.

    # KEYFRAME METHOD, SLOW, USE IPOS DIRECT
    # TODO: use f-point samples instead (Aligorith)
    if rotate_mode != 'QUATERNION':
        prev_euler = [Euler() for i in range(len(bvh_nodes))]

    # Animate the data, the last used bvh_node will do since they all have the same number of frames
    for frame_current in range(len(bvh_node.anim_data) - 1):  # skip the first frame (rest frame)
        # print frame_current

        # if frame_current==40: # debugging
        # 	break

        scene.frame_set(frame_start + frame_current)

        # Dont neet to set the current frame
        for i, bvh_node in enumerate(bvh_nodes.values()):
            pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp
            lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1]

            if bvh_node.has_rot:
                # apply rotation order and convert to XYZ
                # note that the rot_order_str is reversed.
                bone_rotation_matrix = Euler((rx, ry, rz), bvh_node.rot_order_str[::-1]).to_matrix().to_4x4()
                bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix

                if rotate_mode == 'QUATERNION':
                    pose_bone.rotation_quaternion = bone_rotation_matrix.to_quaternion()
                else:
                    euler = bone_rotation_matrix.to_euler(pose_bone.rotation_mode, prev_euler[i])
                    pose_bone.rotation_euler = euler
                    prev_euler[i] = euler

            if bvh_node.has_loc:
                pose_bone.location = (bone_rest_matrix_inv * Matrix.Translation(Vector((lx, ly, lz)) - bvh_node.rest_head_local)).to_translation()

            if bvh_node.has_loc:
                pose_bone.keyframe_insert("location")
            if bvh_node.has_rot:
                if rotate_mode == 'QUATERNION':
                    pose_bone.keyframe_insert("rotation_quaternion")
                else:
                    pose_bone.keyframe_insert("rotation_euler")

    for cu in action.fcurves:
        if IMPORT_LOOP:
            pass  # 2.5 doenst have cyclic now?

        for bez in cu.keyframe_points:
            bez.interpolation = 'LINEAR'

    # finally apply matrix
    arm_ob.matrix_world = global_matrix
    bpy.ops.object.transform_apply(rotation=True)

    return arm_ob
예제 #12
0
def bvh_node_dict2armature(
    context,
    bvh_name,
    bvh_nodes,
    rotate_mode='XYZ',
    frame_start=1,
    IMPORT_LOOP=False,
    global_matrix=None,
):

    if frame_start < 1:
        frame_start = 1

    # Add the new armature,
    scene = context.scene
    for obj in scene.objects:
        obj.select = False

    arm_data = bpy.data.armatures.new(bvh_name)
    arm_ob = bpy.data.objects.new(bvh_name, arm_data)

    scene.objects.link(arm_ob)

    arm_ob.select = True
    scene.objects.active = arm_ob

    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)

    bvh_nodes_list = sorted_nodes(bvh_nodes)

    # Get the average bone length for zero length bones, we may not use this.
    average_bone_length = 0.0
    nonzero_count = 0
    for bvh_node in bvh_nodes_list:
        l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length
        if l:
            average_bone_length += l
            nonzero_count += 1

    # Very rare cases all bones could be zero length???
    if not average_bone_length:
        average_bone_length = 0.1
    else:
        # Normal operation
        average_bone_length = average_bone_length / nonzero_count

    # XXX, annoying, remove bone.
    while arm_data.edit_bones:
        arm_ob.edit_bones.remove(arm_data.edit_bones[-1])

    ZERO_AREA_BONES = []
    for bvh_node in bvh_nodes_list:

        # New editbone
        bone = bvh_node.temp = arm_data.edit_bones.new(bvh_node.name)

        bone.head = bvh_node.rest_head_world
        bone.tail = bvh_node.rest_tail_world

        # Zero Length Bones! (an exceptional case)
        if (bone.head - bone.tail).length < 0.001:
            print("\tzero length bone found:", bone.name)
            if bvh_node.parent:
                ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local
                if ofs.length:  # is our parent zero length also?? unlikely
                    bone.tail = bone.tail - ofs
                else:
                    bone.tail.y = bone.tail.y + average_bone_length
            else:
                bone.tail.y = bone.tail.y + average_bone_length

            ZERO_AREA_BONES.append(bone.name)

    for bvh_node in bvh_nodes_list:
        if bvh_node.parent:
            # bvh_node.temp is the Editbone

            # Set the bone parent
            bvh_node.temp.parent = bvh_node.parent.temp

            # Set the connection state
            if ((not bvh_node.has_loc)
                    and (bvh_node.parent.temp.name not in ZERO_AREA_BONES) and
                (bvh_node.parent.rest_tail_local == bvh_node.rest_head_local)):

                bvh_node.temp.use_connect = True

    # Replace the editbone with the editbone name,
    # to avoid memory errors accessing the editbone outside editmode
    for bvh_node in bvh_nodes_list:
        bvh_node.temp = bvh_node.temp.name

    # Now Apply the animation to the armature

    # Get armature animation data
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

    pose = arm_ob.pose
    pose_bones = pose.bones

    if rotate_mode == 'NATIVE':
        for bvh_node in bvh_nodes_list:
            bone_name = bvh_node.temp  # may not be the same name as the bvh_node, could have been shortened.
            pose_bone = pose_bones[bone_name]
            pose_bone.rotation_mode = bvh_node.rot_order_str

    elif rotate_mode != 'QUATERNION':
        for pose_bone in pose_bones:
            pose_bone.rotation_mode = rotate_mode
    else:
        # Quats default
        pass

    context.scene.update()

    arm_ob.animation_data_create()
    action = bpy.data.actions.new(name=bvh_name)
    arm_ob.animation_data.action = action

    # Replace the bvh_node.temp (currently an editbone)
    # With a tuple  (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
    for bvh_node in bvh_nodes_list:
        bone_name = bvh_node.temp  # may not be the same name as the bvh_node, could have been shortened.
        pose_bone = pose_bones[bone_name]
        rest_bone = arm_data.bones[bone_name]
        bone_rest_matrix = rest_bone.matrix_local.to_3x3()

        bone_rest_matrix_inv = Matrix(bone_rest_matrix)
        bone_rest_matrix_inv.invert()

        bone_rest_matrix_inv.resize_4x4()
        bone_rest_matrix.resize_4x4()
        bvh_node.temp = (pose_bone, bone, bone_rest_matrix,
                         bone_rest_matrix_inv)

    # Make a dict for fast access without rebuilding a list all the time.

    # KEYFRAME METHOD, SLOW, USE IPOS DIRECT
    # TODO: use f-point samples instead (Aligorith)
    if rotate_mode != 'QUATERNION':
        prev_euler = [Euler() for i in range(len(bvh_nodes))]

    # Animate the data, the last used bvh_node will do since they all have the same number of frames
    for frame_current in range(len(bvh_node.anim_data) -
                               1):  # skip the first frame (rest frame)
        # print frame_current

        # if frame_current==40: # debugging
        # 	break

        scene.frame_set(frame_start + frame_current)

        # Dont neet to set the current frame
        for i, bvh_node in enumerate(bvh_nodes_list):
            pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp
            lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1]

            if bvh_node.has_rot:
                # apply rotation order and convert to XYZ
                # note that the rot_order_str is reversed.
                bone_rotation_matrix = Euler(
                    (rx, ry, rz),
                    bvh_node.rot_order_str[::-1]).to_matrix().to_4x4()
                bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix

                if rotate_mode == 'QUATERNION':
                    pose_bone.rotation_quaternion = bone_rotation_matrix.to_quaternion(
                    )
                else:
                    euler = bone_rotation_matrix.to_euler(
                        pose_bone.rotation_mode, prev_euler[i])
                    pose_bone.rotation_euler = euler
                    prev_euler[i] = euler

            if bvh_node.has_loc:
                pose_bone.location = (
                    bone_rest_matrix_inv * Matrix.Translation(
                        Vector((lx, ly, lz)) -
                        bvh_node.rest_head_local)).to_translation()

            if bvh_node.has_loc:
                pose_bone.keyframe_insert("location")
            if bvh_node.has_rot:
                if rotate_mode == 'QUATERNION':
                    pose_bone.keyframe_insert("rotation_quaternion")
                else:
                    pose_bone.keyframe_insert("rotation_euler")

    for cu in action.fcurves:
        if IMPORT_LOOP:
            pass  # 2.5 doenst have cyclic now?

        for bez in cu.keyframe_points:
            bez.interpolation = 'LINEAR'

    # finally apply matrix
    arm_ob.matrix_world = global_matrix
    bpy.ops.object.transform_apply(rotation=True)

    return arm_ob
예제 #13
0
    def process(self):
        if not self.outputs['Matrices'].is_linked:
            return

        inputs = self.inputs

        matrix_list = []
        add_matrix = matrix_list.extend if self.flat_output else matrix_list.append

        if self.mode == "QUATERNION":
            input_l = inputs["Location"].sv_get(deepcopy=False)
            input_s = inputs["Scale"].sv_get(deepcopy=False)
            input_q = inputs["Quaternion"].sv_get(deepcopy=False)
            if inputs["Quaternion"].is_linked:
                input_q = [input_q]
            else:
                input_q = [[Quaternion(input_q[0][0])]]
            I = [input_l, input_q, input_s]
            params1 = match_long_repeat(I)
            for ll, ql, sl in zip(*params1):
                params2 = match_long_repeat([ll, ql, sl])
                matrices = []
                for location, quaternion, scale in zip(*params2):
                    # translation
                    mat_t[0][3] = location[0]
                    mat_t[1][3] = location[1]
                    mat_t[2][3] = location[2]
                    # rotation
                    mat_r = quaternion.to_matrix().to_4x4()
                    # scale
                    mat_s[0][0] = scale[0]
                    mat_s[1][1] = scale[1]
                    mat_s[2][2] = scale[2]
                    # composite matrix
                    m = mat_t @ mat_r @ mat_s
                    matrices.append(m)
                add_matrix(matrices)

        elif self.mode == "EULER":
            socket_names = [
                "Location", "Angle X", "Angle Y", "Angle Z", "Scale"
            ]
            I = [inputs[name].sv_get(deepcopy=False) for name in socket_names]
            params1 = match_long_repeat(I)
            auc = angle_unit_conversion[self.angle_units][
                "RAD"]  # convert to radians
            for ll, axl, ayl, azl, sl in zip(*params1):
                params2 = match_long_repeat([ll, axl, ayl, azl, sl])
                matrices = []
                for location, angleX, angleY, angleZ, scale in zip(*params2):
                    # translation
                    mat_t[0][3] = location[0]
                    mat_t[1][3] = location[1]
                    mat_t[2][3] = location[2]
                    # rotation
                    angles = (angleX * auc, angleY * auc, angleZ * auc)
                    euler = Euler(angles, self.euler_order)
                    mat_r = euler.to_quaternion().to_matrix().to_4x4()
                    # scale
                    mat_s[0][0] = scale[0]
                    mat_s[1][1] = scale[1]
                    mat_s[2][2] = scale[2]
                    # composite matrix
                    m = mat_t @ mat_r @ mat_s
                    matrices.append(m)
                add_matrix(matrices)

        elif self.mode == "AXISANGLE":
            socket_names = ["Location", "Axis", "Angle", "Scale"]
            I = [inputs[name].sv_get(deepcopy=False) for name in socket_names]
            params1 = match_long_repeat(I)
            auc = angle_unit_conversion[self.angle_units][
                "RAD"]  # convert to radians
            for ll, xl, al, sl in zip(*params1):
                params2 = match_long_repeat([ll, xl, al, sl])
                matrices = []
                for location, axis, angle, scale in zip(*params2):
                    # translation
                    mat_t[0][3] = location[0]
                    mat_t[1][3] = location[1]
                    mat_t[2][3] = location[2]
                    # rotation
                    mat_r = Quaternion(axis, angle * auc).to_matrix().to_4x4()
                    # scale
                    mat_s[0][0] = scale[0]
                    mat_s[1][1] = scale[1]
                    mat_s[2][2] = scale[2]
                    # composite matrix
                    m = mat_t @ mat_r @ mat_s
                    matrices.append(m)
                add_matrix(matrices)

        self.outputs['Matrices'].sv_set(matrix_list)
def add_rotation_keyframe(object, group, translations, pivot, global_matrix):
    from mathutils import Vector
    from mathutils import Euler
    
    c1 = group.channels[0] # x
    c2 = group.channels[1] # y
    c3 = group.channels[2] # z
    
    totalTime = 0.0
    type = 1
    
    prevX = 0.0
    prevY = 0.0
    prevZ = 0.0
    keys = setup_keyframe_list([c1, c2, c3]) 
    #print(len(keys))
    #print(keys)
    
    # be sure to offset by the first frame's rotation
    for key in keys:
        if key['frame'] == 1.0:
            prevX = key['data'][0]
            prevY = key['data'][1]
            prevZ = key['data'][2]
            break

    for key in keys:
        x = key['data'][0]
        y = key['data'][1]
        z = key['data'][2]
        
        rot = Euler((x - prevX, y - prevY, z - prevZ))
        
        #rotDelta = rot
        #rotDelta.x -= prevRotation.x
        #rotDelta.y -= prevRotation.y
        #rotDelta.z -= prevRotation.z
        
        quat = rot.to_quaternion()
        axisAngle = quat.to_axis_angle()
        angle = -axisAngle[1]
        axis = axisAngle[0]
        
        start = key['frame']
        
        axis = Vector(global_matrix * axis)
        axis.normalize()
        axis.z = -axis.z
        
        translations.append(
        {
            "start": totalTime / 60.0,
            "duration": (start - totalTime) / 60.0,
            "type": type,
            "axis": axis,
            "angle": angle,
            "origin": Vector(global_matrix * pivot),
            "local": 0
        })
        
        prevX = x
        prevY = y
        prevZ = z
        totalTime = start
def grow_spline(tree_settings: TreeSettings, lvl, stem, num_split, spline_list,
                spline_to_bone, close_tip, kp, bone_step):
    out_att = tree_settings.attractOut[lvl]
    handle_type = tree_settings.handles

    # Curv at base
    stem_curv = stem.curv
    if (lvl == 0) and (kp <= tree_settings.splitHeight):
        stem_curv = 0.0

    curve_angle = stem_curv + (uniform(0, stem.curvV) * kp * stem.curvSignx)
    curve_var = uniform(0, stem.curvV) * kp * stem.curvSigny
    stem.curvSignx *= -1
    stem.curvSigny *= -1

    curve_var_mat = Matrix.Rotation(curve_var, 3, 'Y')

    # First find the current direction of the stem
    current_direction = stem.quat()

    # Length taperCrown
    if lvl == 0:
        dec = declination(current_direction) / 180
        dec = dec**2
        tf = 1 - (dec * tree_settings.taperCrown * 30)
        tf = max(.1, tf)
    else:
        tf = 1.0
    tf = 1.0  # disabled

    # Outward attraction
    if (lvl >= 0) and (kp > 0) and (out_att > 0):
        p = stem.p.co.copy()
        d = atan2(p[0], -p[1])  # + tau
        e_dir = current_direction.to_euler('XYZ', Euler((0, 0, d), 'XYZ'))
        d = angle_mean(e_dir[2], d, (kp * out_att))
        dir_v = Euler((e_dir[0], e_dir[1], d), 'XYZ')
        current_direction = dir_v.to_quaternion()

    if lvl == 0:
        current_direction = convert_quat(current_direction)

    if lvl != 0:
        split_length = 0

    # If the stem splits, we need to add new splines etc
    if num_split > 0:
        dir_vec, split_r2 = add_splines_on_split(bone_step, close_tip,
                                                 curve_angle, curve_var_mat,
                                                 current_direction, lvl,
                                                 num_split, spline_list,
                                                 spline_to_bone, stem, tf,
                                                 tree_settings)

    else:
        curve_var_mat = Matrix.Rotation(curve_var, 3, 'Y')
        # If there are no splits then generate the growth direction without accounting for spreading of stems
        dir_vec = z_axis.copy()
        div_rot_mat = Matrix.Rotation(-curve_angle, 3, 'X')
        dir_vec.rotate(div_rot_mat)

        # Horizontal curvature variation
        dir_vec.rotate(curve_var_mat)

        dir_vec.rotate(current_direction)

        stem.splitlast = 0  # numSplit #keep track of numSplit for next stem

    # Introduce upward curvature
    up_rot_axis = x_axis.copy()
    up_rot_axis.rotate(dir_vec.to_track_quat('Z', 'Y'))
    curve_up_ang = curve_up(stem.vertAtt, dir_vec.to_track_quat('Z', 'Y'),
                            stem.segMax)
    up_rot_mat = Matrix.Rotation(-curve_up_ang, 3, up_rot_axis)
    dir_vec.rotate(up_rot_mat)

    dir_vec.normalize()
    dir_vec *= stem.segL * tf

    # Get the end point position
    end_co = stem.p.co.copy() + dir_vec

    stem.spline.bezier_points.add(1)
    new_point = stem.spline.bezier_points[-1]
    (new_point.co, new_point.handle_left_type,
     new_point.handle_right_type) = (end_co, handle_type, handle_type)

    new_radius = stem.radS * (1 - (stem.seg + 1) / stem.segMax) + stem.radE * (
        (stem.seg + 1) / stem.segMax)
    if num_split > 0:
        new_radius = max(new_radius * split_r2, tree_settings.minRadius)
        stem.radS = max(stem.radS * split_r2, tree_settings.minRadius)
        stem.radE = max(stem.radE * split_r2, tree_settings.minRadius)
    new_radius = max(new_radius, stem.radE)
    if (stem.seg == stem.segMax - 1) and close_tip:
        new_radius = 0.0
    new_point.radius = new_radius

    # Set bezier handles for first point.
    if len(stem.spline.bezier_points) == 2:
        temp_point = stem.spline.bezier_points[0]
        if handle_type is 'AUTO':
            dir_vec = z_axis.copy()
            dir_vec.rotate(current_direction)
            dir_vec = dir_vec * stem.segL * 0.33
            (temp_point.handle_left_type,
             temp_point.handle_right_type) = ('ALIGNED', 'ALIGNED')
            temp_point.handle_right = temp_point.co + dir_vec
            temp_point.handle_left = temp_point.co - dir_vec
        elif handle_type is 'VECTOR':
            (temp_point.handle_left_type,
             temp_point.handle_right_type) = ('VECTOR', 'VECTOR')

    # Update the last point in the spline to be the newly added one
    stem.updateEnd()
예제 #16
0
def write_action_channels(writebuf, action):
    # Set of frame indices
    frame_set = set()

    # Set of unique bone names
    bone_set = []

    # Scan through all fcurves to build animated bone set
    for fcurve in action.fcurves:
        data_path = fcurve.data_path
        scale_match = scale_matcher.match(data_path)
        rotation_match = rotation_matcher.match(data_path)
        location_match = location_matcher.match(data_path)

        if scale_match:
            if scale_match.group(1) not in bone_set:
                bone_set.append(scale_match.group(1))
        elif rotation_match:
            if rotation_match.group(1) not in bone_set:
                bone_set.append(rotation_match.group(1))
        elif location_match:
            if location_match.group(1) not in bone_set:
                bone_set.append(location_match.group(1))
        else:
            continue

        # Count unified keyframes for interleaving channel data
        for key in fcurve.keyframe_points:
            frame_set.add(int(key.co[0]))

    # Build bone table
    bone_list = []
    for bone in bone_set:
        fc_dict = dict()
        rotation_mode = None
        property_bits = 0
        for fcurve in action.fcurves:
            if fcurve.data_path == 'pose.bones["' + bone + '"].scale':
                if 'scale' not in fc_dict:
                    fc_dict['scale'] = [None, None, None]
                    property_bits |= 4
                fc_dict['scale'][fcurve.array_index] = fcurve
            elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_euler':
                if 'rotation_euler' not in fc_dict:
                    fc_dict['rotation_euler'] = [None, None, None]
                    rotation_mode = 'rotation_euler'
                    property_bits |= 1
                fc_dict['rotation_euler'][fcurve.array_index] = fcurve
            elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_quaternion':
                if 'rotation_quaternion' not in fc_dict:
                    fc_dict['rotation_quaternion'] = [None, None, None, None]
                    rotation_mode = 'rotation_quaternion'
                    property_bits |= 1
                fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
            elif fcurve.data_path == 'pose.bones["' + bone + '"].rotation_axis_angle':
                if 'rotation_axis_angle' not in fc_dict:
                    fc_dict['rotation_axis_angle'] = [None, None, None, None]
                    rotation_mode = 'rotation_axis_angle'
                    property_bits |= 1
                fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
            elif fcurve.data_path == 'pose.bones["' + bone + '"].location':
                if 'location' not in fc_dict:
                    fc_dict['location'] = [None, None, None]
                    property_bits |= 2
                fc_dict['location'][fcurve.array_index] = fcurve
        bone_list.append((bone, rotation_mode, fc_dict, property_bits))

    # Write out frame indices
    sorted_frames = sorted(frame_set)
    writebuf(struct.pack('I', len(sorted_frames)))
    for frame in sorted_frames:
        writebuf(struct.pack('i', frame))

    # Interleave / interpolate keyframe data
    writebuf(struct.pack('I', len(bone_list)))
    for bone in bone_list:

        bone_name = bone[0]
        rotation_mode = bone[1]
        fc_dict = bone[2]
        property_bits = bone[3]

        writebuf(struct.pack('I', len(bone_name)))
        writebuf(bone_name.encode())

        writebuf(struct.pack('I', property_bits))

        writebuf(struct.pack('I', len(sorted_frames)))
        for frame in sorted_frames:

            # Rotation curves
            if rotation_mode == 'rotation_quaternion':
                quat = [0.0] * 4
                for comp in range(4):
                    if fc_dict['rotation_quaternion'][comp]:
                        quat[comp] = fc_dict['rotation_quaternion'][
                            comp].evaluate(frame)
                quat = Quaternion(quat).normalized()
                writebuf(
                    struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))

            elif rotation_mode == 'rotation_euler':
                euler = [0.0] * 3
                for comp in range(3):
                    if fc_dict['rotation_euler'][comp]:
                        euler[comp] = fc_dict['rotation_euler'][comp].evaluate(
                            frame)
                euler_o = Euler(euler, 'XYZ')
                quat = euler_o.to_quaternion()
                writebuf(
                    struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))

            elif rotation_mode == 'rotation_axis_angle':
                axis_angle = [0.0] * 4
                for comp in range(4):
                    if fc_dict['rotation_axis_angle'][comp]:
                        axis_angle[comp] = fc_dict['rotation_axis_angle'][
                            comp].evaluate(frame)
                quat = Quaternion(axis_angle[1:4], axis_angle[0])
                writebuf(
                    struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))

            # Location curves
            if 'location' in fc_dict:
                writevec = [0.0] * 3
                for comp in range(3):
                    if fc_dict['location'][comp]:
                        writevec[comp] = fc_dict['location'][comp].evaluate(
                            frame)
                writebuf(
                    struct.pack('fff', writevec[0], writevec[1], writevec[2]))

            # Scale curves
            if 'scale' in fc_dict:
                writevec = [1.0] * 3
                for comp in range(3):
                    if fc_dict['scale'][comp]:
                        writevec[comp] = fc_dict['scale'][comp].evaluate(frame)
                writebuf(
                    struct.pack('fff', writevec[0], writevec[1], writevec[2]))
예제 #17
0
    def execute(self, context):
        """SETTINGS"""
        keep_breasts = True
        keep_twist_weights = True
        belly_locators = True

        arm_bend = 45
        finger_bend = -15

        scene = bpy.context.scene
        obj = bpy.context.object
        meshes = obj.children

        #gyaz stamp
        obj.data['GYAZ_rig'] = True

        #remove all modifiers except for armature modifier
        for mesh in meshes:
            for m in mesh.modifiers:
                if m.type != 'ARMATURE':
                    mesh.modifiers.remove(m)

        #################################################################################
        #raycast function
        my_tree = BVHTree.FromObject(scene.objects[meshes[0].name],
                                     bpy.context.depsgraph)

        rig = obj

        def cast_ray_from_bone(start_bone, head_tail, ebone_pbone, direction,
                               distance):
            #set ray start and direction
            if ebone_pbone == 'ebone':
                bpy.ops.object.mode_set(mode='EDIT')
                if head_tail == 'head':
                    ray_start = rig.data.edit_bones[start_bone].head
                elif head_tail == 'tail':
                    ray_start = rig.data.edit_bones[start_bone].tail
            elif ebone_pbone == 'pbone':
                bpy.ops.object.mode_set(mode='POSE')
                if head_tail == 'head':
                    ray_start = rig.pose.bones[start_bone].head
                elif head_tail == 'tail':
                    ray_start = rig.pose.bones[start_bone].tail
            ray_direction = direction
            ray_distance = 10

            #cast ray
            hit_loc, hit_nor, hit_index, hit_dist = my_tree.ray_cast(
                ray_start, ray_direction, ray_distance)

            return (hit_loc, hit_nor, hit_index, hit_dist)

        #################################################################################

        if obj.type == 'ARMATURE':
            rig = obj

            sides = [['_L', '_l'], ['_R', '_r']]

            names_central = [['root', 'root'], ['pelvis', 'hips'],
                             ['spine01', 'spine_1'], ['spine02', 'spine_2'],
                             ['spine03', 'spine_3'], ['neck', 'neck'],
                             ['head', 'head']]

            names_side = [
                ['clavicle', 'shoulder'],
                ['upperarm', 'upperarm'],
                ['lowerarm', 'forearm'],
                ['hand', 'hand'],
                ['thigh', 'thigh'],
                ['calf', 'shin'],
                ['foot', 'foot'],
                ['toes', 'toes'],
                #        ['breast', 'spring_chest']
            ]

            finger_names = [['thumb', 'thumb'], ['index', 'pointer'],
                            ['middle', 'middle'], ['ring', 'ring'],
                            ['pinky', 'pinky']]

            counters = [['01', '_1'], ['02', '_2'], ['03', '_3']]

            twist_names = ['upperarm', 'lowerarm', 'thigh', 'calf']
            new_twist_names = ['upperarm', 'forearm', 'thigh', 'shin']

            metacarpal_names = ['index', 'middle', 'ring', 'pinky']

            extra_names = ['root']

            breast_names = ['breast', 'spring_chest']  # old name, new name

            bpy.ops.object.mode_set(mode='OBJECT')
            """WEIGHTS"""
            def merge_and_remove_weight(weight_to_merge, weight_to_merge_to):
                for mesh in meshes:
                    bpy.ops.object.select_all(action='DESELECT')
                    mesh.select_set(True)
                    bpy.context.view_layer.objects.active = mesh
                    #mix weights if mesh has those weights
                    vgroups = mesh.vertex_groups
                    if vgroups.get(weight_to_merge) != None:
                        if vgroups.get(weight_to_merge_to) == None:
                            vgroups.new(name=weight_to_merge_to)
                        m = mesh.modifiers.new(type='VERTEX_WEIGHT_MIX',
                                               name="Mix Twist Weight")
                        m.mix_mode = 'ADD'
                        m.mix_set = 'ALL'
                        m.vertex_group_a = weight_to_merge_to
                        m.vertex_group_b = weight_to_merge
                        bpy.ops.object.modifier_apply(
                            apply_as='DATA', modifier="Mix Twist Weight")
                        #delete surplus weights
                        vgroups = mesh.vertex_groups
                        vgroups.remove(vgroups[weight_to_merge])

            if keep_twist_weights == False:
                for old_side, new_side in sides:
                    for name in twist_names:
                        weight_to_merge = name + '_twist' + old_side
                        weight_to_merge_to = name + old_side

                        merge_and_remove_weight(weight_to_merge,
                                                weight_to_merge_to)

            for old_side, new_side in sides:
                for name in metacarpal_names:
                    weight_to_merge = name + '00' + old_side
                    weight_to_merge_to = 'hand' + old_side

                    merge_and_remove_weight(weight_to_merge,
                                            weight_to_merge_to)

            if keep_breasts == False:
                for old_side, new_side in sides:
                    weight_to_merge = breast_names[0] + old_side
                    weight_to_merge_to = 'spine03'

                    merge_and_remove_weight(weight_to_merge,
                                            weight_to_merge_to)

            for mesh in meshes:
                mesh.data.update()

            bpy.ops.object.select_all(action='DESELECT')
            rig.select_set(True)
            bpy.context.view_layer.objects.active = rig
            bpy.ops.object.mode_set(mode='EDIT')
            ebones = rig.data.edit_bones
            """RENAME"""

            for old_name, new_name in names_central:
                ebones[old_name].name = new_name

            for old_side, new_side in sides:
                for old_name, new_name in names_side:
                    ebones[old_name + old_side].name = new_name + new_side

            for old_side, new_side in sides:
                for old_finger, new_finger in finger_names:
                    for old_counter, new_counter in counters:
                        ebones[
                            old_finger + old_counter +
                            old_side].name = new_finger + new_counter + new_side

            for old_side, new_side in sides:
                ebone = ebones[breast_names[0] +
                               old_side].name = breast_names[1] + new_side

            for old_side, new_side in sides:
                for index, name in enumerate(twist_names):
                    ebone = ebones[name + '_twist' + old_side]
                    ebone.name = 'twist_1_' + new_twist_names[index] + new_side
            """REMOVE BONES"""

            for old_side, new_side in sides:
                for name in new_twist_names:
                    ebone = ebones['twist_1_' + name + new_side]
                    ebones.remove(ebone)

            for old_side, new_side in sides:
                for name in metacarpal_names:
                    ebone = ebones[name + '00' + old_side]
                    ebones.remove(ebone)

            for name in extra_names:
                ebone = ebones[name]
                ebones.remove(ebone)

            if keep_breasts == False:
                for old_side, new_side in sides:
                    ebone = ebones[breast_names[1] + new_side]
                    ebones.remove(ebone)
            """POSITION BONES"""

            ebones = rig.data.edit_bones

            for ebone in ebones:
                ebone.use_connect = False

#            for old_side, new_side in sides:
#                foot = ebones['foot'+new_side]
#                foot.tail = foot.head[0], foot.tail[1], foot.head[2]
#
#                toes = ebones['toes'+new_side]
#                toes.head = foot.head[0], toes.head[1], toes.head[2]
#                toes.tail = foot.head[0], toes.tail[1], toes.head[2]
#
#                foot.roll = 0
#                toes.roll = 0

            ebones['hips'].head = (ebones['thigh_l'].head +
                                   ebones['thigh_r'].head) / 2
            ebones['spine_3'].tail = ebones['neck'].head

            for old_side, new_side in sides:
                ebone = ebones['shoulder' + new_side]
                ebone.head = ebone.head[0], ebone.tail[1], ebone.tail[2]

            ebone = ebones['head']
            ebone.tail = ebone.head[0], ebone.head[1], ebone.tail[2]

            spine_names = [
                'hips', 'spine_1', 'spine_2', 'spine_3', 'neck', 'head'
            ]

            for name in spine_names:
                ebones[name].roll = 0

            for old_side, new_side in sides:
                ebone = ebones['hand' + new_side]
                roll = ebone.roll
                print(degrees(roll))
                ebone.tail = (ebone.tail - ebone.head) + ebone.tail
                ebone.roll = roll
            """REST POSE"""

            bpy.ops.object.mode_set(mode='POSE')

            pbones = rig.pose.bones

            for old_side, side in sides:
                pbone = pbones['forearm' + side]

                eu = Euler((radians(arm_bend), 0, 0), 'XYZ')
                qu = eu.to_quaternion()
                pbone.rotation_quaternion = qu

                for old_name, name in finger_names:
                    for n in range(1, 4):
                        pbone = pbones[name + '_' + str(n) + side]
                        if 'thumb_1' not in pbone.name:

                            eu = Euler((radians(finger_bend), 0, 0), 'XYZ')
                            qu = eu.to_quaternion()
                            pbone.rotation_quaternion = qu

            if len(meshes) > 0:
                for mesh in meshes:
                    bpy.ops.object.mode_set(mode='OBJECT')
                    bpy.ops.object.select_all(action='DESELECT')
                    mesh.select_set(True)
                    bpy.context.view_layer.objects.active = mesh

                    #remove shape keys
                    try:
                        bpy.ops.object.shape_key_remove(all=True)
                    except:
                        'do nothing'

                    #apply armature modifier
                    old_mesh = mesh.data
                    new_mesh = mesh.to_mesh(bpy.context.depsgraph,
                                            apply_modifiers=True,
                                            calc_undeformed=False)
                    mesh.data = new_mesh
                    bpy.data.meshes.remove(old_mesh)

            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.select_all(action='DESELECT')
            rig.select_set(True)
            bpy.context.view_layer.objects.active = rig

            #apply pose
            bpy.ops.object.mode_set(mode='POSE')
            bpy.ops.pose.select_all(action='SELECT')
            bpy.ops.pose.armature_apply()

            #adjust spine

            #spine_1, spine_2
            bpy.ops.object.mode_set(mode='EDIT')
            ebones = rig.data.edit_bones
            loc = (ebones['spine_1'].head + ebones['spine_2'].tail) / 2
            ebones['spine_1'].tail = loc
            ebones['spine_2'].head = loc

            #spine chain
            def adjust_spine_point(lower_bone, upper_bone):
                forward = (0, -1, 0)
                backward = (0, 1, 0)
                hit_loc, hit_nor, hit_index, hit_dist = cast_ray_from_bone(
                    lower_bone, 'tail', 'ebone', forward, 10)
                front = hit_loc
                hit_loc, hit_nor, hit_index, hit_dist = cast_ray_from_bone(
                    lower_bone, 'tail', 'ebone', backward, 10)
                back = hit_loc
                middle = (front + back) / 2

                bpy.ops.object.mode_set(mode='EDIT')
                ebones = rig.data.edit_bones
                ebones[lower_bone].tail = middle
                ebones[upper_bone].head = middle

                return front

            loc_pelvis_front = adjust_spine_point('hips', 'spine_1')
            adjust_spine_point('spine_1', 'spine_2')
            loc_sternum_lower = adjust_spine_point('spine_2', 'spine_3')
            adjust_spine_point('spine_3', 'neck')

            #additional locator bones
            if belly_locators == True:
                ebone = rig.data.edit_bones.new(name='loc_pelvis_front')
                ebone.head = loc_pelvis_front
                ebone.tail = loc_pelvis_front + Vector((0, 0, 0.05))
                ebone.parent = rig.data.edit_bones['hips']

                ebone = rig.data.edit_bones.new(name='loc_sternum_lower')
                ebone.head = loc_sternum_lower
                ebone.tail = loc_sternum_lower + Vector((0, 0, 0.05))
                ebone.parent = rig.data.edit_bones['spine_3']

            #enforce roll values
            bpy.ops.object.mode_set(mode='EDIT')
            ebones = rig.data.edit_bones
            bpy.ops.armature.select_all(action='SELECT')
            bpy.ops.armature.symmetrize()

            #finalize
            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.select_all(action='DESELECT')
            rig.select_set(True)
            bpy.context.view_layer.objects.active = rig

            #GYAZ stamp
            rig.data['GYAZ_game_rig'] = True

        return {'FINISHED'}
예제 #18
0
    def execute(self, context):
        data = []

        objId = 0
        for ob in bpy.data.objects:
            if (ob.physacq.is_actor):
                print(ob.name)
                o = {}
                o['name'] = ob.name
                o['objId'] = objId
                o['shape'] = ob.physacq.shape
                o['size'] = {
                    "x": ob.dimensions[0],
                    "y": ob.dimensions[1],
                    "z": ob.dimensions[2]
                }
                o['mass'] = ob.physacq.mass

                log = {}

                if not ob.animation_data:
                    continue

                action = ob.animation_data.action
                for fcu in action.fcurves:
                    #print( "fcu: ", fcu )
                    #print( "prop: ", fcu.data_path )
                    #print( "propId: ", fcu.array_index )

                    if fcu.data_path == 'location':
                        logKey = 'pos'
                    elif fcu.data_path == 'rotation_euler':
                        logKey = 'pose'
                    elif fcu.data_path == 'rotation_quaternion':
                        logKey = 'pose'
                    elif fcu.data_path == 'scale':
                        continue
                    else:
                        print("Can't interpret keyframe type, attention!!!",
                              fcu.data_path)
                        continue

                    if fcu.array_index == 0:
                        subKey = 'x'
                    elif fcu.array_index == 1:
                        subKey = 'y'
                    elif fcu.array_index == 2:
                        subKey = 'z'
                    elif fcu.array_index == 3:
                        subKey = 'w'
                    else:
                        print("Unprepared!")

                    keyframe_points = fcu.keyframe_points
                    for entry in keyframe_points:
                        #print( entry.co )
                        #print( "key:", str(int(entry.co[0])) )
                        frameId = str(int(entry.co[0]))
                        if frameId not in log:
                            log[frameId] = {}
                        if logKey not in log[frameId]:
                            log[frameId][logKey] = {}
                        log[frameId][logKey][subKey] = entry.co[1]
                prevKey = None
                prevQ = None
                for key, frame in log.items():
                    if 'pos' in log[key]:
                        pos = Vector(
                            coordinatesUtil.jsonVector3ToTuple(
                                log[key]['pos']))
                        log[key]['pos'] = coordinatesUtil.vector3ToJson(
                            self.coordChangeM * pos)

                    if 'pose' not in log[key]:
                        continue

                    #print( 'ob.rotation_mode:', ob.rotation_mode)
                    if ob.rotation_mode == 'XYZ':
                        v = Vector(
                            coordinatesUtil.jsonVector3ToTuple(
                                log[key]['pose']))
                        eul = Euler(v, ob.rotation_mode)
                        q = eul.to_quaternion()
                    elif ob.rotation_mode == 'QUATERNION':
                        entry = log[key]['pose']
                        print(entry)
                        q = Quaternion((
                            entry['x'], entry['y'], entry['z'],
                            entry['w']))  # yes, array_index==3 was parsed to w
                    if prevQ:
                        dotProd = q.dot(prevQ)
                        if dotProd < 0:
                            print("dot: ", dotProd)
                            #q.negate()

                    #print( "q: %s" % (q.__repr__()) )
                    #log[ key ] ['pose'] = { 'x' : q[1], 'y' : -q[3], 'z': q[2], 'w' : q[0] }
                    log[key]['pose'] = coordinatesUtil.quatToJson(
                        coordinatesUtil.quatFromBlender(q))
                    if prevKey:
                        #print( "check: ", log[prevKey]['pose'], "\n",log[key]['pose'] )
                        q2 = Quaternion(q)
                        q2.negate()
                        #print( "q:", q, "negative: ", q2 )

                    prevKey = key
                    prevQ = q

                o['log'] = log
                data.append(o)
                objId += 1

        if not len(data):
            self.report({'WARNING'}, "No keyframes, not saving")
            return {'FINISHED'}

        fp = context.scene.physacq.savePath
        if len(bpy.path.basename(fp)) == 0:
            fp = "//cuboids.json"

        if fp.find("//") >= 0:
            fp = bpy.path.abspath(fp)
        self.report({'INFO'}, "Saving poses to %s" % (fp))

        f = open(fp, 'w')
        json.dump(data, f)
        f.close()

        return {'FINISHED'}