Blender 4.3 - Grease Pencil v3 Feedback

Both of these are planned, yes.

1 Like

Also, you might want to wait a bit until all changes for the API have been merged and itā€™s a bit more stable. Otherwise you might have to update your addons multiple times! The documentation for the release notes will also get a lot more info soon, so you donā€™t have to dig into the code yourself to find out how the new API works :slight_smile:

Well as I said i always update to nightly buildsā€¦so my scripts are broken anyway :smiley:
As long as I can get a keyframeā€™s frame number and set a keyframe numberā€¦thatā€™s pretty much 99% of my scripts.
In any case Iā€™ll wait until these pop up back. And update the scripts whenever necessary.

1 Like

There is now a PR with the ā€œhigh-levelā€ python API: #125599 - GPv3: High level python API - blender - Blender Projects

Builds available here: Blender Builds - blender.org

Let me know if you encounter issues! Thanks.

3 Likes

@filedescriptor

Hi Falk!

Have been playing with gp3. Itā€™s awesome! Thanks you so much! Well done!

But here are some questions about geo-nodes. Iā€™ve found that after few strokes drawing grease becomes laggy with geo-nodes modifier. As far as I understand itā€™s because node tree recalculates by every ā€œinput tickā€ I guess - donā€™t know how to call it right.

So after very few strokes every newly drawn stroke/point forces to update every already created point. And this is heavy.

Just to know - are there any plans for caching system like autobake already created strokes, or maybe even rasterization (if it possible)? Or maybe going towards GN Tools system for input? Or nodes are like post-drawing system with such limits? Like gp is another way to create curves in compatibility with geo-nodes?

Anyway thanks a lot for gp3!

Hi @SebVerkhozin. Yes, the drawing tool, especially in combination with GN, hasnā€™t been optimized yet. It doesnā€™t seem like the modifier evaluation is currently our bottleneck. It has to do with the inefficient triangulation of strokes. Anyway, I want to focus on performance after we reach feature parity. Hopefully at the start of September.

9 Likes

Hi, the 4.3 Main Branch has already GP3 by default?

Yes, the 4.3 Alpha (current main) doesnā€™t expose the legacy grease pencil anymore. Itā€™s GPv3 by default. Weā€™re slowly removing the legacy code.

Ok Thnx.
in GP3 is way easier than GP2 to work with a different unit scale that is not meters.
In GP2 wasnā€™t enough to change the radius (especially because it was clamped) and you had to use some hacks and make many tweaking to work with e.g. millimeters.
Now you just have to set the radius (1000x if you work with millimeters) and youā€™re good to go. Fortunately it keeps the same radius and offset from surface for all next objects.
It would be nice if, just like modelling, all parameters that are ā€œunit scale sentitiveā€ (e.g. radius) would automatically adjust based on the unit scale. See, if you set unit scale e.g. 0.001 (to work with millimeters) if you add a cube, its dimensions are 1000x1000x1000 units.
Having the same auto-adjustable working space in grease pencil would be really helpful

Hello @filedescriptor!

Not all of material attributes are exposed in geonodesā€”mostly fill material attributes. What about line material attributes? By guessing Iā€™ve found that ā€˜rotationā€™ works for dots and squares brushes, but I would love to see a UV factor for line textures to make the textures match the stroke length.

Right, unfortunately, this is a problem that wonā€™t be fixed in 4.3. Itā€™s just the way Grease Pencil materials work and we canā€™t really expose that to Geometry Nodes.

Rather than exposing material settings in Geometry Nodes, a better approach would be to move data out of the material and onto the geometry. Like, for example, if a stroke is filled or not. Or the UV data like you want. This is something that I want to work on after 4.3.

3 Likes

Been adapting the code to a custom build. Iā€™ve been noticing a few gaps in feature parity.

Edit mode

  • half the clean up operators are missing
  • no interpolate sequence operator
  • no merge operator
  • no join operators
  • focus (view) to selected doesnā€™t work

Sculpt mode

  • The clone brush is hard crashing after using it a little

API

  • if an addon or legacy code calls a create a grease pencil object operator, it will create a GP but sculpting on it will hard crash. GP2 from legacy code is somewhat there still, but not forward compatible.

GUI

  • menu order is different to legacy in some areas, making it slightly unfamiliar
  • animation operators and menu are missing from header

I know itā€™s wip, power to you to speed run it and polish before 4.3! My studio work runs on a rolling release made from main, so it kinda did throw some spanners in when GP2 was pulled before GP3 is mature enough to be used.

Wish there was a ā€œslow ringā€ of developement in main! To branch and develop and deploy in a stable future proof way.

Keep up the awesome work, GN and GP is a very fine marriage.

Hotkeys

Another observation, grease pencil hotkeys are still listing but grease pencil 3 hotkeys donā€™t list, you can Search for them, but you can not browse for them. It seems they are unexposed in the hotkey editorā€¦

In general the API change from ā€œgpencilā€ to ā€œgreasepencilā€ is causing some legacy overlap and issues when it was pulled.

Info for addon developers:

The Python API is ready for testing and feedback

The high-level Python API landed in main, this means that most of the functionality from 4.2 is now ready for testing.

What is the ā€œhigh-level Python APIā€ ?

In 4.2 to access stroke/point data you would have to get the frame, e.g. frame = layer.frames[0] then get access to the strokes with frame.strokes (and then the points using stroke.points).

In 4.3 all the data is now attributes. Each frame points to a drawing. drawing = frame.drawing. To get access to the attributes, you simply use drawing.attributes (see Attribute(bpy_struct) - Blender Python API for how to use them).

Since this is a very different API, there is a high-level API (that still uses the attributes system under the hood) that makes it a bit easier to port addons.

The high-level API gives access to drawing.strokes. Each stroke has (almost) the same properties as before. You can also get access to the points with stroke.points.

Note: The high-level API can be slow. It is recommended to use the attributes API for performance critical code.

TL;DR: In 4.2 you used the frame to get access to the frame.strokes. In 4.3 you can use the high-level API with frame.drawing.strokes.

Testing and feedback

If you want to help test the API in 4.3, download the latest build from builder.blender.org and try to port your addon to this version. If you run into issues/crashes or encounter missing functionality, please report it here in the thread! Thank you!

Disclaimer

Weā€™re still in alpha, so the API might still (slightly) change. It is advised to wait till 4.3 is relased to publish your addons.

4 Likes

Been working with the updated GUI code for operators, panels and such, most operators Iā€™ve found used to be gpencil.operator but now are grease_pencil.operator

Same goes with menus with VIEW3D_MT_gpencil_menu is now VIEW3D_MT_grease_pencil_menu. I have had some addons and callbacks to operators, menus and panels using the former - was this slide in and replacement of GP3 with the new nomenclature intentional?

I know itā€™s a quick update to register to the new locations and use the new operator equivalents - but making the addon backwards compatible has proven a little tricky to work aroundā€¦

But nice work, itā€™s feeling much more substantial and near feature parity with most operators.

Hello!
First, I am trying to learn about GreasePencilStroke and GreasePencilStrokePoints in the Python API. Currently, the API documentation for these is incomplete. I have created a bug here requesting this.

Second, I would also like to suggest something like the attached code as a Python Template for Grease Pencil v3 in Blender 4.3 (run from Text Editor):

import bpy
import random
from bpy.types import Operator, Panel
from bpy.props import IntProperty, FloatVectorProperty
from mathutils import Vector

# Operator to add a Grease Pencil object
class GPENCIL_OT_add_rectangle(Operator):
    bl_idname = "grease_pencil.add_gpencil_rectangle"
    bl_label = "Add Grease Pencil Rectangle"
    bl_description = "Add a Grease Pencil rectangle if not already present"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        context = bpy.context
        gpencil_data = bpy.data.grease_pencils_v3.new(name="Rectangle")
        # Create a new Grease Pencil object and link it to the current collection
        gpencil_object = bpy.data.objects.new(name="Rectangle", object_data=gpencil_data)
        context.collection.objects.link(gpencil_object)

        # Set the new object as the active object in the view layer and select it
        context.view_layer.objects.active = gpencil_object
        gpencil_object.select_set(True)

        # Set the stroke depth order to 3D to allow proper depth sorting
        gpencil_data.stroke_depth_order = '3D'

        layer = gpencil_data.layers.new(name="Lines", set_active=True)
        frame = layer.frames.new(frame_number=1)

        # Create a drawing and add a stroke with 5 points
        drawing = frame.drawing
        drawing.add_strokes(sizes=[5])

        # Generate a random fill color
        random_color = (random.random(), random.random(), random.random(), 1)

        # Create a material for the rectangle
        rect_material = bpy.data.materials.new(name="Rectangle Material")
        # Enable Grease Pencil data for the material
        bpy.data.materials.create_gpencil_data(rect_material)
        rect_material.grease_pencil.fill_color = random_color
        rect_material.grease_pencil.show_stroke = False
        rect_material.grease_pencil.show_fill = True
        # Append the material to the Grease Pencil object's material list
        gpencil_object.data.materials.append(rect_material)

        stroke = drawing.strokes[0]

        # Assign the created material to the stroke
        stroke.material_index = gpencil_object.data.materials.find(rect_material.name)

        # Generate random width and height
        width = random.randint(5, 20)
        height = random.randint(5, 20)

        # Define the coordinates of the rectangle's points
        points = [
            (-width, -height, 0.0),  # Bottom left
            (-width,  height, 0.0),  # Top left
            ( width,  height, 0.0),  # Top right
            ( width, -height, 0.0),  # Bottom right
            (-width, -height, 0.0)   # Close the rectangle (same as first point)
        ]

        # Set the position of each point in the stroke
        for i, pt_pos in enumerate(points):
            point = stroke.points[i]
            point.position = pt_pos

        for window in context.window_manager.windows:
            screen = window.screen
            for area in screen.areas:
                print(area.type)
                if area.type == 'VIEW_3D':
                    with context.temp_override(window=window, area=area):
                        current_mode = gpencil_object.mode
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.grease_pencil.set_curve_type(type="POLY", use_handles=False)
                        bpy.ops.object.mode_set(mode=current_mode)
                    break

        return {'FINISHED'}

# Operator to add points to Grease Pencil data
class GPENCIL_OT_add_points(Operator):
    bl_idname = "grease_pencil.add_gpencil_points"
    bl_label = "Add Grease Pencil Points"
    bl_description = "Add points to the end of Grease Pencil data"
    bl_options = {'REGISTER', 'UNDO'}
    
    count: IntProperty(name="Count", default=1, min=1)
    offset: FloatVectorProperty(
        name="Offset",
        default=Vector((0.001, 0.0, 0.0)),
        step=0.01,
        precision=3,
        subtype='TRANSLATION'
    )

    def execute(self, context):
        gpencil_object = context.active_object
        if gpencil_object and gpencil_object.type == 'GREASEPENCIL':
            frame = gpencil_object.data.layers.active.current_frame()
            if frame and frame.drawing and len(frame.drawing.strokes) > 0:
                stroke = frame.drawing.strokes[-1]
                
                # Resize the stroke to add new points
                size = len(stroke.points)
                frame.drawing.resize_strokes(sizes=[size + self.count], indices=[stroke._curve_index])

                # Re-access the stroke to make sure it's up-to-date after resizing
                stroke = frame.drawing.strokes[-1]
                
                # Set the position of the new points based on the last point's position plus the offset
                last_point_pos = stroke.points[size - 1].position if size > 0 else Vector((0.0, 0.0, 0.0))

                for i in range(size, size + self.count):
                    increment = self.offset * (i - size + 1)
                    stroke.points[i].position = last_point_pos + increment

                self.report({'INFO'}, f"Added {self.count} points to the Grease Pencil stroke.")
            else:
                self.report({'ERROR'}, "No active stroke found.")
        else:
            self.report({'ERROR'}, "No active Grease Pencil object found.")
        return {'FINISHED'}

# Operator to remove points from Grease Pencil data
class GPENCIL_OT_remove_points(Operator):
    bl_idname = "grease_pencil.remove_gpencil_points"
    bl_label = "Remove Grease Pencil Points"
    bl_description = "Remove points from the end of Grease Pencil data"
    bl_options = {'REGISTER', 'UNDO'}
    
    count: IntProperty(name="Count", default=1, min=1)
    
    def execute(self, context):
        gpencil_object = context.active_object
        if gpencil_object and gpencil_object.type == 'GREASEPENCIL':
            frame = gpencil_object.data.layers.active.current_frame()
            if frame and frame.drawing and len(frame.drawing.strokes) > 0:
                stroke = frame.drawing.strokes[-1]
                size = len(stroke.points)
                # Resize the stroke to remove points from the end
                frame.drawing.resize_strokes(sizes=[size-self.count], indices=[stroke._curve_index])
                self.report({'INFO'}, f"Removed {self.count} points from the Grease Pencil stroke.")
            else:
                self.report({'ERROR'}, "No active stroke found.")
        else:
            self.report({'ERROR'}, "No active Grease Pencil object found.")
        return {'FINISHED'}

# Panel in 3D view
class GPENCIL_PT_panel(Panel):
    bl_label = "Grease Pencil"
    bl_idname = "VIEW3D_PT_gpencil_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Grease Pencil v3'
    
    def draw(self, context):
        layout = self.layout
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator("grease_pencil.add_gpencil_rectangle", icon='OUTLINER_OB_GREASEPENCIL')
        col.operator("grease_pencil.add_gpencil_points", text="Add Points", icon='ADD')
        col.operator("grease_pencil.remove_gpencil_points", text="Remove Points", icon='REMOVE')

# Register classes
classes = [
    GPENCIL_OT_add_rectangle,
    GPENCIL_OT_add_points,
    GPENCIL_OT_remove_points,
    GPENCIL_PT_panel
]

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()

1 Like

This was done so that the GPv2 code could be left untouched. We could name them the same, but that might not even fix all the issues, because the content of some panels and menus is different.

2 Likes

Iā€™m looking into exposing the docs on the manual. For now you can use Pythons help() method to get the documentation:

>>> help(drawing.strokes[0])

It looks like the default new file 2D animation template still creating old GP brushes? This is what I get with the airbrush pen if I create a new 2D animation file

There is also some issue with how the spacing is controlled with the speed. See the video for how dotted the strokes are with the default Ink Pen. I am drawing with relatively slow to mid fast speed on Win 11 + Cintiq