Page MenuHome

Python-made grease pencil strokes not updating until manual stroke added
Closed, ResolvedPublic

Description

System Information
Operating system: Arch Linux
Graphics card: GTX 1060

Blender Version
Broken: 2.80 cef41d0144e
Worked: 2.79b

Short description of error
Grease pencil strokes created with python require manually creating a stroke before the pythonically-generated stroke will appear/update. This was a problem with Blender 2.78 as well (according to the comments section for this stack exchange answer) but it was later fixed in blender 2.79. I have confirmed that the following script works as intended in blender 2.79:

import bpy

gp = bpy.data.grease_pencil.new("My GPencil")
scene = bpy.context.scene
scene.grease_pencil = gp
scene.frame_set(5) 

layer = gp.layers.new("Name for new layer", set_active=True)

frame = layer.frames.new(5)
stroke = frame.strokes.new()
stroke.draw_mode = '3DSPACE'

stroke.points.add(4) # add 4 points
stroke.points.foreach_set("co", (0,0,0,0,0,4,0,6,4,8,6,4)) # set all at once efficiently

However, my analogous script for blender 2.80 shows the error:

import bpy
from mathutils import Vector

gpencil = bpy.data.grease_pencil.new('gpencil')

layer = gpencil.layers.new("alayer", set_active=True)
frame = layer.frames.new(bpy.context.scene.frame_current)
stroke = frame.strokes.new()
stroke.line_width = 300
stroke.display_mode = '3DSPACE'

points = [
    [0, 0],
    [10, 0]
]

for point in points:
    stroke.points.add(1)
    stroke.points[-1].co.x = point[0]
    stroke.points[-1].co.y = point[1]

obj = bpy.data.objects.new('agobject', gpencil)
bpy.context.scene.collection.objects.link(obj)

Exact steps for others to reproduce the error

  1. Open Blender and go to the scripting layout, delete the camera, cube and light in the 3D view
  2. Paste my 2.80 script given above and run it, you should see a display marker indicating a grease pencil object has been created
  3. With the new grease pencil object selected, go to draw mode. You should see a new stroke immediately appear. If you do the above steps multiple times however, you'll need to start drawing a new stroke for the generated stroke to appear.

Revisions and Commits

Event Timeline

I think the problem is you are not updating the depsgraph data, but not sure how to do that using python.

@Sergey Sharybin (sergey) Could you give us any clue here?

@BKE_object_handle_data_update, In most cases you shouldn't be explicitly updating dependency graph from a script (whatever that means). When running the script i do see DEG_graph_tag_relations_update:(), BKE_gpencil_eval_geometry() and BKE_object_handle_data_update() being run. So as far as dependency graph tags are concerned i don't see issues. So you tell me what it is what drawing code is missing :)

I have seen the problem is not the update, but the material assigned to stroke.

I have not tested yet in python how creates the material, but doing debug the information of the grease pencil data is not available in the material, so the stroke is bypassed when draw the strokes.

@Daniel Oakey (doakey3) Try this:

import bpy
from mathutils import Vector

gpencil = bpy.data.grease_pencil.new('gpencil')

layer = gpencil.layers.new("alayer", set_active=True)
frame = layer.frames.new(bpy.context.scene.frame_current)
stroke = frame.strokes.new()
stroke.line_width = 300
stroke.display_mode = '3DSPACE'
# assign material
stroke.material_index = 0

points = [
    [0, 0],
    [10, 0]
]

for point in points:
    stroke.points.add(1)
    stroke.points[-1].co.x = point[0]
    stroke.points[-1].co.y = point[1]  

obj = bpy.data.objects.new('agobject', gpencil)
bpy.context.scene.collection.objects.link(obj)

# create material
mat = bpy.data.materials.new('mymaterial')
obj.data.materials.append(mat)

Still we have some python API missing to manage graese pencil materials.

Nice! Adding a material appears to fix the main issue of the stroke not appearing at all. I noticed something though... The stroke endpoint is not rounded when it is generated, but the rounded endpoint appears when I go into edit mode and move the endpoint vertex as shown below. I'm not sure if this is a related issue or not.

I found the api for making a grease pencil material. The code below allows me to add color to the grease pencil object. I still have not found a way to make the endpoint update with python, but I'll keep tinkering. Thanks again for your assistance.

import bpy
from mathutils import Vector

gpencil = bpy.data.grease_pencil.new('gpencil')

layer = gpencil.layers.new("alayer", set_active=True)
frame = layer.frames.new(bpy.context.scene.frame_current)
stroke = frame.strokes.new()
stroke.line_width = 300
stroke.display_mode = '3DSPACE'
# assign material
stroke.material_index = 0


points = [
    [0, 0],
    [10, 0]
]

for point in points:
    stroke.points.add(1)
    stroke.points[-1].co.x = point[0]
    stroke.points[-1].co.y = point[1]  

obj = bpy.data.objects.new('agobject', gpencil)
bpy.context.scene.collection.objects.link(obj)

# create material
mat = bpy.data.materials.new('test')
bpy.data.materials.create_gpencil_data(mat)
mat.grease_pencil.color = (0, 0.5, 0, 1)
obj.data.materials.append(mat)

@Daniel Oakey (doakey3) Yes, I had missed the create_gpencil_data API.

About the end points, I need add a new python function to force the update and internal recalculation.

I have added new APIs to update internal data.

frame.strokes.update(stroke)
gpencil.update()

Last lines of python must be like these:

obj = bpy.data.objects.new('agobject', gpencil)
bpy.context.scene.collection.objects.link(obj)
mat = bpy.data.materials.new('mymaterial')
bpy.data.materials.create_gpencil_data(mat)
obj.data.materials.append(mat)
frame.strokes.update(stroke)
gpencil.update()

@Sergey Sharybin (sergey) I have done the change in this commit: e79f401ffa7d

There is a problem with the update of the datablock. If I run both commands in the same python, the gpencil.update() does not work, but if I run the first script without gpencil.update() and then in a new script add the following code works.

import bpy
gpencil = bpy.data.grease_pencil['gpencil']
gpencil.update()

What am I missing?

I have updated internally the API to recalc the data after adding points. There is a change in the points.add() api, now requires a datablock parameter. This parameter is necessary to tag the corresponding datablock. We could remove this parameter if we add a new API at datablock level to tag the datablock, but this was rejected in a previous commit.

The final code is this:

import bpy
from mathutils import Vector

gpencil = bpy.data.grease_pencil.new('gpencil')

layer = gpencil.layers.new("alayer", set_active=True)
frame = layer.frames.new(bpy.context.scene.frame_current)
stroke = frame.strokes.new()
stroke.line_width = 300
stroke.display_mode = '3DSPACE'
stroke.material_index = 0


points = [
    [2, 0],
    [8, 5]
]

for point in points:
    stroke.points.add(gpencil, 1)
    stroke.points[-1].co.x = point[0]
    stroke.points[-1].co.y = point[1]  

obj = bpy.data.objects.new('agobject', gpencil)
bpy.context.scene.collection.objects.link(obj)
mat = bpy.data.materials.new('mymaterial')
bpy.data.materials.create_gpencil_data(mat)
obj.data.materials.append(mat)
Antonio Vazquez (antoniov) lowered the priority of this task from 90 to 50.Dec 20 2018, 4:55 PM