Created
September 22, 2019 21:48
-
-
Save jmpinit/919f88b02ad5338f6ac73959418f0028 to your computer and use it in GitHub Desktop.
Starting point for using Python to draw with the Grease Pencil in Blender 2.8.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://towardsdatascience.com/blender-2-8-grease-pencil-scripting-and-generative-art-cbbfd3967590 | |
import bpy | |
import math | |
import numpy as np | |
def get_grease_pencil(gpencil_obj_name='GPencil') -> bpy.types.GreasePencil: | |
""" | |
Return the grease-pencil object with the given name. Initialize one if not already present. | |
:param gpencil_obj_name: name/key of the grease pencil object in the scene | |
""" | |
# If not present already, create grease pencil object | |
if gpencil_obj_name not in bpy.context.scene.objects: | |
bpy.ops.object.gpencil_add(location=(0, 0, 0), type='EMPTY') | |
# rename grease pencil | |
bpy.context.scene.objects[-1].name = gpencil_obj_name | |
# Get grease pencil object | |
gpencil = bpy.context.scene.objects[gpencil_obj_name] | |
return gpencil | |
def get_grease_pencil_layer(gpencil: bpy.types.GreasePencil, gpencil_layer_name='GP_Layer', | |
clear_layer=False) -> bpy.types.GPencilLayer: | |
""" | |
Return the grease-pencil layer with the given name. Create one if not already present. | |
:param gpencil: grease-pencil object for the layer data | |
:param gpencil_layer_name: name/key of the grease pencil layer | |
:param clear_layer: whether to clear all previous layer data | |
""" | |
# Get grease pencil layer or create one if none exists | |
if gpencil.data.layers and gpencil_layer_name in gpencil.data.layers: | |
gpencil_layer = gpencil.data.layers[gpencil_layer_name] | |
else: | |
gpencil_layer = gpencil.data.layers.new(gpencil_layer_name, set_active=True) | |
if clear_layer: | |
gpencil_layer.clear() # clear all previous layer data | |
# bpy.ops.gpencil.paintmode_toggle() # need to trigger otherwise there is no frame | |
return gpencil_layer | |
# Util for default behavior merging previous two methods | |
def init_grease_pencil(gpencil_obj_name='GPencil', gpencil_layer_name='GP_Layer', | |
clear_layer=True) -> bpy.types.GPencilLayer: | |
gpencil = get_grease_pencil(gpencil_obj_name) | |
gpencil_layer = get_grease_pencil_layer(gpencil, gpencil_layer_name, clear_layer=clear_layer) | |
return gpencil_layer | |
def draw_line(gp_frame, p0: tuple, p1: tuple): | |
# Init new stroke | |
gp_stroke = gp_frame.strokes.new() | |
gp_stroke.display_mode = '3DSPACE' # allows for editing | |
# Define stroke geometry | |
gp_stroke.points.add(count=2) | |
gp_stroke.points[0].co = p0 | |
gp_stroke.points[1].co = p1 | |
return gp_stroke | |
def draw_circle(gp_frame, center: tuple, radius: float, segments: int): | |
# Init new stroke | |
gp_stroke = gp_frame.strokes.new() | |
gp_stroke.display_mode = '3DSPACE' # allows for editing | |
gp_stroke.draw_cyclic = True # closes the stroke | |
#gp_stroke.line_width = 100 | |
#gp_stroke.material_index = 1 | |
# Define stroke geometry | |
angle = 2*math.pi/segments # angle in radians | |
gp_stroke.points.add(count=segments) | |
for i in range(segments): | |
x = center[0] + radius*math.cos(angle*i) | |
y = center[1] + radius*math.sin(angle*i) | |
z = center[2] | |
gp_stroke.points[i].co = (x, y, z) | |
return gp_stroke | |
def rotate_stroke(stroke, angle, axis='z'): | |
# Define rotation matrix based on axis | |
if axis.lower() == 'x': | |
transform_matrix = np.array([[1, 0, 0], | |
[0, math.cos(angle), -math.sin(angle)], | |
[0, math.sin(angle), math.cos(angle)]]) | |
elif axis.lower() == 'y': | |
transform_matrix = np.array([[math.cos(angle), 0, -math.sin(angle)], | |
[0, 1, 0], | |
[math.sin(angle), 0, math.cos(angle)]]) | |
# default on z | |
else: | |
transform_matrix = np.array([[cos(angle), -math.sin(angle), 0], | |
[sin(angle), math.cos(angle), 0], | |
[0, 0, 1]]) | |
# Apply rotation matrix to each point | |
for i, p in enumerate(stroke.points): | |
p.co = transform_matrix @ np.array(p.co).reshape(3, 1) | |
def draw_sphere(gp_frame, center: tuple, radius: int, circles: int): | |
angle = math.pi / circles | |
for i in range(circles): | |
circle = draw_circle(gp_frame, center, radius, 32) | |
rotate_stroke(circle, angle*i, 'x') | |
print(angle * i) | |
gp_layer = init_grease_pencil() | |
for frame in range(0, 100): | |
gp_frame = gp_layer.frames.new(frame) | |
draw_sphere(gp_frame, (0, 0, frame / 100 * 2), 1, 20) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment