Both of these are planned, yes.
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
Well as I said i always update to nightly buildsā¦so my scripts are broken anyway
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.
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.
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.
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.
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.
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()
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.
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