Blender 4.3 - Grease Pencil v3 Feedback

Yes that would be good. Also, add the settings of your draw brush please.

2 Likes

The issue has been submitted, the schedule is waiting for repair, this is #128680 - GPv3: Vertex color by default in paint mods - blender - Blender Projects

I just added a section on the migration page on how to get the ā€œScreen Spaceā€ effect from 4.2 in 4.3: https://developer.blender.org/docs/release_notes/4.3/grease_pencil_migration/#screen-space-stroke-thickness
Let me know if that works.

2 Likes

I’m not sure if this is the correct thread to post this, it may be more related to the viewport EEVEE render engine, but since the issue has to do with GP, here it goes:

At our studio we have encountered an issue in production where we need to draw some GP lines over the geometry, but keep them as close as they can be to the underlying geo, so the lines don’t appear to float when looked at up-close at an angle. So artists have worked with a tiny offset.
Problem is, they did it while in render/material view mode, and the viewport result is not the same as it would be when doing an f12 render. The actual offset, is what can be seen in solid mode, but they didn’t know this, so all the GP lines appeared inset into the geometry when rendered. You can see the problem in the video below:

Should I report it as a bug? We have encountered in 4.0, so it’s not a regression, but I think it should be considered a bug.

As I say, it may have to do more with the viewport render engine than GP per se, if that’s the case let me know.

If this is an issue in 4.2 LTS you could report is as a bug. I’m not entirely sure if this is expected or not. Maybe @fclem can comment.
In any case, this seems unrelated to the topic of this thread which is GPv3 (Blender 4.3).

Thanks, done! I checked and it is present in latest 4.2 LTS (the video recorded is using latest 4.3)

In any case, this seems unrelated to the topic of this thread which is GPv3 (Blender 4.3).

I thought as much, but I wasn’t sure if it was somehow related to GP itself. Sorry

No problem! Better to ask anyway :slight_smile:

Hello. Thank you very much for recent updates of the migration document. During testing new high-level APIs, I found them behave differently from GPv2. Each time I get access to a stroke, it returns a different reference. I cannot tell if two variables represent the same stroke, and the reference does not reflect later changes of the stroke:

(Logs from 4.3.0 Beta, branch: blender-v4.3-release, commit date: 2024-10-11 22:23, hash: `bcf6524ca1ee`)
>>> f = bpy.context.object.data.layers[1].current_frame()
>>> s1 = f.drawing.strokes[0]
>>> s2 = f.drawing.strokes[0]
>>> s1 == s2
False

>>> len(s1.points)
32
>>> f.drawing.resize_strokes([15], indices=[0])
>>> len(s1.points)
32

May I ask if it is intentional or will be improved later? I failed to migrate my add-on, because in many cases I need to revisit a stroke after performing some operations that may change the stroke indices and point counts. I did not figure out how to achieve this with the new API.

Yes this is expected and won’t change.

The stroke class is only a higher level API on top of the drawing.attributes API. When you access drawing.strokes it creates new instances of the stroke class every time. The stroke class is just a reference to the drawing + stroke index + point indices.
This is not explained well on the migration page yet. I’m planning on adding this. In the meantime: The strokes API is implemented in python, so you can read the implementation here: drawing.strokes, GreasePencilStroke.

To solve your problem you probably want to refer to strokes by their index instead of holding onto a reference.

1 Like

Thank you for posting the source code. Now I have a better understanding of how these slices work.

I prefer a persistent reference because of the following concerns:

  • In the 2D display mode, it is common for a user to rearrange the strokes frequently.
  • There may be memory safety issues if an old slice is called after attributes are updated. If the API user is not careful enough, Blender may crash instantly. I do not know if it would be better to have some protective measures.

To provide persistent stroke references, I tried to create my own wrapper class by assigning a random hash value to each stroke as an internal attribute:

import random

class TraceableStroke:
    def __init__(self, drawing, identifier, initial_index):
        self._drawing = drawing
        self._hash = identifier
        self._index = initial_index

    def update_index(self):
        if self._index >= len(self._drawing.attributes['.hash'].data) or self._drawing.attributes['.hash'].data[self._index] != self._hash:
            # May need optimization / error handling here
            for i, attr in enumerate(self._drawing.attributes['.hash'].data):
                if attr.value == self._hash:
                    self._index = i

    def __eq__(self, other):
        return (self._drawing == other._drawing) and (self._hash == other._hash)

    def __getattr__(self, name):
        self.update_index()
        return getattr(self._drawing.strokes[self._index], name)
        
class TraceableStrokeCollection:
    def __init__(self, drawing):
        # Would be helpful if this part can be automatically done
        # Also, need to allocate new hash to duplicated strokes
        self._drawing = drawing
        self._hash_attr = drawing.attributes.new(".hash", 'INT', 'CURVE') if '.hash' not in drawing.attributes else drawing.attributes['.hash']
        for attr in self._hash_attr.data:
            if attr.value == 0:
                attr.value = random.randint(1, 2**24)

    def __getitem__(self, key):
        return TraceableStroke(self._drawing, self._hash_attr.data[key].value, key)
    
    
# Example of using this class
import bpy
f = bpy.context.object.data.layers[-1].current_frame()
strokes = TraceableStrokeCollection(f.drawing)
s1 = strokes[1]
print(s1.points[0].position)
f.drawing.remove_strokes([0])
print(s1.points[0].position)    # Should get the same result

Do you think this is a feasible workaround to adopt?


Besides the reference issue, I also have some other questions about the new API:

  1. Using drawing.attributes API, is there a way to get the point count / the first point index of each stroke?
  2. Is there an equivalent of the GPv2 operator bpy.ops.gpencil.stroke_sample?
  3. I got errors when adding new points to a stroke. In this line, should resize_curves be resize_strokes?

If you want to keep a more permanent reference (that stays even when the user closes Blender) I can recommend saving the identifier (I’d probably still use the original index) as an attribute. During operations like reordering strokes or subdividing a stroke, etc., attributes are propagated correctly. Meaning if you store an attribute for a stroke once, it will stay there permanently unless the attribute can’t be propagated (e.g. when the stroke is removed).
See Attribute(bpy_struct) - Blender Python API.

  1. Yes, using drawing.curve_offsets.
  2. I can’t find anything in the API reference. We might have missed this one. Bit of a shame because it would have been easy to port. It will be in 4.4 then.
  3. Yep! That looks like a bug! EDIT: Fixed!
2 Likes

After more tests, I got some new issues about attributes:

  1. Each time I use high-level API to assign an attribute value, a redundant attribute is created. I think the problem might be in the AttributeGetterSetter, which always calls the new method. Although not visible, I think .selection setter may have the similar issue.

  1. If I first draw strokes using a material without fill, then enable the fill, the fill appears fully opaque. However, the attribute fill_opacity is not created. In this case, if I use the high-level API stroke.fill_opacity, it returns 0.0 rather than 1.

  2. The modifier TextureMapping creates a new attribute u_scale. If I apply the modifier, then draw new strokes, all the new strokes have zero u_scale, which prevents the line texture from showing.

Would be good to create bug reports for these issues, so it’s easier for us to track them!

By the way @filedescriptor thanks for adding the move function to the frames class. It’s cool that it fails on collisions, although it takes some preprocessing to calculate the direction a batch of frames should be moved, but it’s not a show stopper.
So big thanks!

1 Like
  1. and 3. have been fixed in todays build.

I think 2. is sort of expected at the moment unfortunately. I’m working on a proposal that would change the way this part of the materials would work. This would also solve this issue. Not for 4.3 though.

1 Like

This has been fixed in the latest beta builds.

@Maurits_Valk Have you reported the drawing performance issue somewhere?
It would be great if I could replicate the setup, specifically the file and the settings of the draw tool.

One of the issue might be a very low Advanced > Spacing setting. Anything below 50% might result in too many points being generated.
image

You can check the point count by enabling the Statistics overlay here:
image

1 Like

Thanks a lot for the continuous updates to the Python APIs!

May I ask how to interact with nested layer groups via Python? I figured out to use layer.parent_group to get the group that a layer belongs to, but I do not know how to handle the case where multiple levels of layer groups exist, since there is no API like layer_group.parent_group.

I updated the page with more info about this: Migration Process - Blender Developer Documentation

Hello there , I was testing out rigging grease 3.0 , And i noticed there’s a issue that persist from
Grease pencil 2. I was just wondering if people were aware of it because it’s a bit niche, But it’s important for doing cut out/puppet/bone animation. I made a explanation video of the issue. After you weight paint the gp to a bone, if you move the bone on another frame, and key it you lose the ability to sculpt the grease pencil. Edit: I realized you can kind of edit it, But sculpting it would make this process alot easier. If the gp could be sculpted into any shape no mater where the bone it’s attached to is moved and keyed, It opens up alot of options.

Video: https://files.catbox.moe/mu9sx8.mp4

1 Like