Skip to content

Instantly share code, notes, and snippets.

@jmpinit
Created September 22, 2019 21:48
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmpinit/919f88b02ad5338f6ac73959418f0028 to your computer and use it in GitHub Desktop.
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.
# 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