This implements layer parenting and layer transforms. * Adds a new "Transform" panel in the object-data properties with the (local) translation, rotation and scale. * Adds a new "Relations" panel with the parent property (and also bone name in case the parent is an armature). * When converting from GPv2 to GPv3, the parent and transforms are converted too. * Bone names are updated if they are renamed in the armature. Implementation details: * The positions in the drawings are always in layer space. During extraction, we transform the positions to object space. Note that this could be optimized further and done in the render engine itself. * This means that e.g. the selection code (which needs to know where the positions are on screen) now takes this transform into account. * The layer transform is calculated when accessed (from the location, rotation, scale properties). * Code that needs to know where the positions are on screen now takes this new transform into account. Pull Request: https://projects.blender.org/blender/blender/pulls/117247
141 lines
3.9 KiB
Python
141 lines
3.9 KiB
Python
# SPDX-FileCopyrightText: 2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
import bpy
|
|
from bpy.types import Panel, Menu
|
|
|
|
|
|
class DataButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return hasattr(context, "grease_pencil") and context.grease_pencil
|
|
|
|
|
|
class LayerDataButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
grease_pencil = context.grease_pencil
|
|
return grease_pencil and grease_pencil.layers.active
|
|
|
|
|
|
class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_options = {'HIDE_HEADER'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
ob = context.object
|
|
grease_pencil = context.grease_pencil
|
|
space = context.space_data
|
|
|
|
if ob:
|
|
layout.template_ID(ob, "data")
|
|
elif grease_pencil:
|
|
layout.template_ID(space, "pin_id")
|
|
|
|
|
|
class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu):
|
|
bl_label = "Add Extra"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator("grease_pencil.layer_group_add", text="Add Group")
|
|
|
|
|
|
class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
|
|
bl_label = "Layers"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
grease_pencil = context.grease_pencil
|
|
layer = grease_pencil.layers.active
|
|
|
|
row = layout.row()
|
|
row.template_grease_pencil_layer_tree()
|
|
|
|
col = row.column()
|
|
sub = col.column(align=True)
|
|
sub.operator_context = 'EXEC_DEFAULT'
|
|
sub.operator("grease_pencil.layer_add", icon='ADD', text="")
|
|
sub.menu("GREASE_PENCIL_MT_grease_pencil_add_layer_extra", icon='DOWNARROW_HLT', text="")
|
|
|
|
col.operator("grease_pencil.layer_remove", icon='REMOVE', text="")
|
|
|
|
# Layer main properties
|
|
if layer:
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = True
|
|
col = layout.column(align=True)
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(layer, "opacity", text="Opacity", slider=True)
|
|
|
|
|
|
class DATA_PT_grease_pencil_layer_transform(LayerDataButtonsPanel, Panel):
|
|
bl_label = "Transform"
|
|
bl_parent_id = "DATA_PT_grease_pencil_layers"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
grease_pencil = context.grease_pencil
|
|
layer = grease_pencil.layers.active
|
|
layout.active = not layer.lock
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(layer, "translation")
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(layer, "rotation")
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(layer, "scale")
|
|
|
|
|
|
class DATA_PT_grease_pencil_layer_relations(LayerDataButtonsPanel, Panel):
|
|
bl_label = "Relations"
|
|
bl_parent_id = "DATA_PT_grease_pencil_layers"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
grease_pencil = context.grease_pencil
|
|
layer = grease_pencil.layers.active
|
|
layout.active = not layer.lock
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(layer, "parent", text="Parent")
|
|
|
|
if layer.parent and layer.parent.type == 'ARMATURE':
|
|
row = layout.row(align=True)
|
|
row.prop_search(layer, "parent_bone", layer.parent.data, "bones", text="Bone")
|
|
|
|
|
|
classes = (
|
|
DATA_PT_context_grease_pencil,
|
|
DATA_PT_grease_pencil_layers,
|
|
DATA_PT_grease_pencil_layer_transform,
|
|
DATA_PT_grease_pencil_layer_relations,
|
|
GREASE_PENCIL_MT_grease_pencil_add_layer_extra,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|