This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton.master
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 2.4 KiB |
@ -1 +1 @@
|
||||
Subproject commit c87ee4d46f16d60a2e1db7514c8d5ab42c5d93df
|
||||
Subproject commit 371960484a38fc64e0a2635170a41a0d8ab2f6bd
|
@ -1 +1 @@
|
||||
Subproject commit 15b25a42783d1e516b5298d70b582fae2559ae17
|
||||
Subproject commit 474702157831f1a58bb50f5240ab8b1b02b6ba37
|
@ -0,0 +1,402 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Menu, Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
from .properties_grease_pencil_common import (
|
||||
GreasePencilDataPanel,
|
||||
GreasePencilOnionPanel,
|
||||
)
|
||||
|
||||
###############################
|
||||
# Base-Classes (for shared stuff - e.g. poll, attributes, etc.)
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL'
|
||||
|
||||
|
||||
class LayerDataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.object and
|
||||
context.object.type == 'GPENCIL' and
|
||||
context.active_gpencil_layer)
|
||||
|
||||
|
||||
###############################
|
||||
# GP Object Properties Panels and Helper Classes
|
||||
|
||||
class DATA_PT_gpencil(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# Grease Pencil data selector
|
||||
gpd_owner = context.gpencil_data_owner
|
||||
gpd = context.gpencil_data
|
||||
|
||||
layout.template_ID(gpd_owner, "data")
|
||||
|
||||
|
||||
class GPENCIL_UL_layer(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
# assert(isinstance(item, bpy.types.GPencilLayer)
|
||||
gpl = item
|
||||
gpd = context.gpencil_data
|
||||
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
if gpl.lock:
|
||||
layout.active = False
|
||||
|
||||
row = layout.row(align=True)
|
||||
if gpl.is_parented:
|
||||
icon = 'BONE_DATA'
|
||||
else:
|
||||
icon = 'BLANK1'
|
||||
|
||||
row.label(text="", icon=icon)
|
||||
row.prop(gpl, "info", text="", emboss=False)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(gpl, "lock", text="", emboss=False)
|
||||
row.prop(gpl, "hide", text="", emboss=False)
|
||||
row.prop(gpl, "unlock_color", text="", emboss=False)
|
||||
if gpl.use_onion_skinning is False:
|
||||
icon = 'GHOST_DISABLED'
|
||||
else:
|
||||
icon = 'GHOST_ENABLED'
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(gpl, "use_onion_skinning", text="", icon=icon, emboss=False)
|
||||
subrow.active = gpd.use_onion_skinning
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class GPENCIL_MT_layer_specials(Menu):
|
||||
bl_label = "Layer"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
|
||||
layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
|
||||
layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down")
|
||||
|
||||
|
||||
class DATA_PT_gpencil_datapanel(Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Layers"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.gpencil_data is None:
|
||||
return False
|
||||
|
||||
ob = context.object
|
||||
if ob is not None and ob.type == 'GPENCIL':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
#layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
gpd = context.gpencil_data
|
||||
|
||||
# Grease Pencil data...
|
||||
if (gpd is None) or (not gpd.layers):
|
||||
layout.operator("gpencil.layer_add", text="New Layer")
|
||||
else:
|
||||
self.draw_layers(context, layout, gpd)
|
||||
|
||||
def draw_layers(self, context, layout, gpd):
|
||||
row = layout.row()
|
||||
|
||||
col = row.column()
|
||||
if len(gpd.layers) >= 2:
|
||||
layer_rows = 5
|
||||
else:
|
||||
layer_rows = 2
|
||||
col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
|
||||
|
||||
col = row.column()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
|
||||
sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
|
||||
|
||||
gpl = context.active_gpencil_layer
|
||||
if gpl:
|
||||
sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if len(gpd.layers) > 1:
|
||||
col.separator()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
|
||||
|
||||
col.separator()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
|
||||
sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
|
||||
|
||||
row = layout.row(align=True)
|
||||
if gpl:
|
||||
row.prop(gpl, "opacity", text="Opacity", slider=True)
|
||||
|
||||
|
||||
class DATA_PT_gpencil_layer_optionpanel(LayerDataButtonsPanel, Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Adjustments"
|
||||
bl_parent_id = 'DATA_PT_gpencil_datapanel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
gpl = context.active_gpencil_layer
|
||||
layout.active = not gpl.lock
|
||||
|
||||
# Layer options
|
||||
# Offsets - Color Tint
|
||||
layout.enabled = not gpl.lock
|
||||
col = layout.column(align=True)
|
||||
col.prop(gpl, "tint_color")
|
||||
col.prop(gpl, "tint_factor", slider=True)
|
||||
|
||||
# Offsets - Thickness
|
||||
col = layout.row(align=True)
|
||||
col.prop(gpl, "line_change", text="Stroke Thickness")
|
||||
|
||||
|
||||
class DATA_PT_gpencil_parentpanel(LayerDataButtonsPanel, Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Relations"
|
||||
bl_parent_id = 'DATA_PT_gpencil_datapanel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
gpl = context.active_gpencil_layer
|
||||
col = layout.column(align=True)
|
||||
col.active = not gpl.lock
|
||||
col.prop(gpl, "parent", text="Parent")
|
||||
col.prop(gpl, "parent_type", text="Parent Type")
|
||||
parent = gpl.parent
|
||||
|
||||
if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
|
||||
col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone")
|
||||
|
||||
|
||||
class DATA_PT_gpencil_onionpanel(Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Onion Skinning"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.active_gpencil_layer)
|
||||
|
||||
@staticmethod
|
||||
def draw_header(self, context):
|
||||
self.layout.prop(context.gpencil_data, "use_onion_skinning", text="")
|
||||
|
||||
def draw(self, context):
|
||||
gpd = context.gpencil_data
|
||||
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.enabled = gpd.use_onion_skinning
|
||||
|
||||
GreasePencilOnionPanel.draw_settings(layout, gpd)
|
||||
|
||||
|
||||
class GPENCIL_MT_gpencil_vertex_group(Menu):
|
||||
bl_label = "GP Vertex Groups"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator_context = 'EXEC_AREA'
|
||||
layout.operator("object.vertex_group_add")
|
||||
|
||||
ob = context.active_object
|
||||
if ob.vertex_groups.active:
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.vertex_group_assign", text="Assign to Active Group")
|
||||
layout.operator("gpencil.vertex_group_remove_from", text="Remove from Active Group")
|
||||
|
||||
layout.separator()
|
||||
layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group")
|
||||
layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False
|
||||
layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True
|
||||
|
||||
layout.separator()
|
||||
layout.operator("gpencil.vertex_group_select", text="Select Points")
|
||||
layout.operator("gpencil.vertex_group_deselect", text="Deselect Points")
|
||||
|
||||
|
||||
class GPENCIL_UL_vgroups(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
vgroup = item
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
|
||||
# icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
|
||||
# layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class DATA_PT_gpencil_vertexpanel(DataButtonsPanel, Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Vertex Groups"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
group = ob.vertex_groups.active
|
||||
|
||||
rows = 2
|
||||
if group:
|
||||
rows = 4
|
||||
|
||||
row = layout.row()
|
||||
row.template_list("GPENCIL_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows)
|
||||
|
||||
col = row.column(align=True)
|
||||
col.operator("object.vertex_group_add", icon='ZOOMIN', text="")
|
||||
col.operator("object.vertex_group_remove", icon='ZOOMOUT', text="").all = False
|
||||
|
||||
if ob.vertex_groups:
|
||||
row = layout.row()
|
||||
|
||||
sub = row.row(align=True)
|
||||
sub.operator("gpencil.vertex_group_assign", text="Assign")
|
||||
sub.operator("gpencil.vertex_group_remove_from", text="Remove")
|
||||
|
||||
sub = row.row(align=True)
|
||||
sub.operator("gpencil.vertex_group_select", text="Select")
|
||||
sub.operator("gpencil.vertex_group_deselect", text="Deselect")
|
||||
|
||||
layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
|
||||
|
||||
|
||||
class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
|
||||
bl_label = "Viewport Display"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ob = context.object
|
||||
|
||||
gpd = context.gpencil_data
|
||||
gpl = context.active_gpencil_layer
|
||||
|
||||
layout.prop(gpd, "xray_mode", text="Depth Ordering")
|
||||
layout.prop(gpd, "edit_line_color", text="Edit Line Color")
|
||||
layout.prop(ob, "empty_draw_size", text="Marker Size")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(gpd, "show_constant_thickness")
|
||||
sub = col.column()
|
||||
sub.active = not gpd.show_constant_thickness
|
||||
sub.prop(gpd, "pixfactor", text="Thickness Scale")
|
||||
|
||||
if gpl:
|
||||
layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions")
|
||||
|
||||
|
||||
class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel):
|
||||
_context_path = "object.data"
|
||||
_property_type = bpy.types.GreasePencil
|
||||
|
||||
###############################
|
||||
|
||||
classes = (
|
||||
DATA_PT_gpencil,
|
||||
DATA_PT_gpencil_datapanel,
|
||||
DATA_PT_gpencil_onionpanel,
|
||||
DATA_PT_gpencil_layer_optionpanel,
|
||||
DATA_PT_gpencil_parentpanel,
|
||||
DATA_PT_gpencil_vertexpanel,
|
||||
DATA_PT_gpencil_display,
|
||||
DATA_PT_custom_props_gpencil,
|
||||
|
||||
GPENCIL_UL_layer,
|
||||
GPENCIL_UL_vgroups,
|
||||
|
||||
GPENCIL_MT_layer_specials,
|
||||
GPENCIL_MT_gpencil_vertex_group,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@ -0,0 +1,134 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
|
||||
|
||||
class ShaderFxButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "shaderfx"
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel):
|
||||
bl_label = "Effects"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
ob = context.object
|
||||
return ob and ob.type == 'GPENCIL'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ob = context.object
|
||||
|
||||
layout.operator_menu_enum("object.shaderfx_add", "type")
|
||||
|
||||
for fx in ob.shader_effects:
|
||||
box = layout.template_shaderfx(fx)
|
||||
if box:
|
||||
# match enum type to our functions, avoids a lookup table.
|
||||
getattr(self, fx.type)(box, fx)
|
||||
|
||||
# the mt.type enum is (ab)used for a lookup on function names
|
||||
# ...to avoid lengthy if statements
|
||||
# so each type must have a function here.
|
||||
|
||||
def FX_BLUR(self, layout, fx):
|
||||
|
||||
layout.prop(fx, "factor", text="Factor")
|
||||
layout.prop(fx, "samples", text="Samples")
|
||||
|
||||
layout.separator()
|
||||
layout.prop(fx, "use_dof_mode")
|
||||
if fx.use_dof_mode:
|
||||
layout.prop(fx, "coc")
|
||||
|
||||
def FX_COLORIZE(self, layout, fx):
|
||||
layout.prop(fx, "mode", text="Mode")
|
||||
|
||||
if fx.mode == 'BITONE':
|
||||
layout.prop(fx, "low_color", text="Low Color")
|
||||
if fx.mode == 'CUSTOM':
|
||||
layout.prop(fx, "low_color", text="Color")
|
||||
|
||||
if fx.mode == 'BITONE':
|
||||
layout.prop(fx, "high_color", text="High Color")
|
||||
|
||||
if fx.mode in {'BITONE', 'CUSTOM', 'TRANSPARENT'}:
|
||||
layout.prop(fx, "factor")
|
||||
|
||||
def FX_WAVE(self, layout,fx):
|
||||
layout.prop(fx, "orientation", expand=True)
|
||||
|
||||
layout.separator()
|
||||
layout.prop(fx, "amplitude")
|
||||
layout.prop(fx, "period")
|
||||
layout.prop(fx, "phase")
|
||||
|
||||
def FX_PIXEL(self, layout, fx):
|
||||
layout.prop(fx, "size", text="Size")
|
||||
|
||||
layout.prop(fx, "use_lines", text="Display Lines")
|
||||
|
||||
col = layout.column()
|
||||
col.enabled = fx.use_lines
|
||||
col.prop(fx, "color")
|
||||
|
||||
def FX_RIM(self, layout, fx):
|
||||
layout.prop(fx, "offset", text="Offset")
|
||||
|
||||
layout.prop(fx, "rim_color")
|
||||
layout.prop(fx, "mask_color")
|
||||
layout.prop(fx, "mode")
|
||||
layout.prop(fx, "blur")
|
||||
layout.prop(fx, "samples")
|
||||
|
||||
def FX_SWIRL(self, layout, fx):
|
||||
layout.prop(fx, "object", text="Object")
|
||||
|
||||
layout.prop(fx, "radius")
|
||||
layout.prop(fx, "angle")
|
||||
|
||||
layout.prop(fx, "transparent")
|
||||
|
||||
def FX_FLIP(self, layout, fx):
|
||||
layout.prop(fx, "flip_horizontal")
|
||||
layout.prop(fx, "flip_vertical")
|
||||
|
||||
def FX_LIGHT(self, layout, fx):
|
||||
layout.prop(fx, "object", text="Object")
|
||||
|
||||
layout.prop(fx, "energy")
|
||||
layout.prop(fx, "ambient")
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_shader_fx,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@ -0,0 +1,322 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Menu, Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
||||
class GPENCIL_MT_color_specials(Menu):
|
||||
bl_label = "Layer"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("gpencil.color_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
|
||||
layout.operator("gpencil.color_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.color_lock_all", icon='LOCKED', text="Lock All")
|
||||
layout.operator("gpencil.color_unlock_all", icon='UNLOCKED', text="UnLock All")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="Lock Unselected")
|
||||
layout.operator("gpencil.lock_layer", icon='COLOR', text="Lock Unused")
|
||||
|
||||
|
||||
class GPENCIL_UL_matslots(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
slot = item
|
||||
ma = slot.material
|
||||
if (ma is not None) and (ma.grease_pencil is not None):
|
||||
gpcolor = ma.grease_pencil
|
||||
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
if gpcolor.lock:
|
||||
layout.active = False
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.enabled = not gpcolor.lock
|
||||
row.prop(ma, "name", text="", emboss=False, icon_value=icon)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(gpcolor, "lock", text="", emboss=False)
|
||||
row.prop(gpcolor, "hide", text="", emboss=False)
|
||||
if gpcolor.ghost is True:
|
||||
icon = 'GHOST_DISABLED'
|
||||
else:
|
||||
icon = 'GHOST_ENABLED'
|
||||
row.prop(gpcolor, "ghost", text="", icon=icon, emboss=False)
|
||||
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class GPMaterialButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "material"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return (ob and ob.type == 'GPENCIL' and
|
||||
ob.active_material and
|
||||
ob.active_material.grease_pencil)
|
||||
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_slots(Panel):
|
||||
bl_label = "Grease Pencil Material Slots"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "material"
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return ob and ob.type == 'GPENCIL'
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
gpd = context.gpencil_data
|
||||
|
||||
mat = context.object.active_material
|
||||
ob = context.object
|
||||
slot = context.material_slot
|
||||
space = context.space_data
|
||||
|
||||
if ob:
|
||||
is_sortable = len(ob.material_slots) > 1
|
||||
rows = 1
|
||||
if (is_sortable):
|
||||
rows = 4
|
||||
|
||||
row = layout.row()
|
||||
|
||||
row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
|
||||
|
||||
col = row.column(align=True)
|
||||
col.operator("object.material_slot_add", icon='ZOOMIN', text="")
|
||||
col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
|
||||
|
||||
col.menu("GPENCIL_MT_color_specials", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if is_sortable:
|
||||
col.separator()
|
||||
|
||||
col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
|
||||
col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
|
||||
|
||||
col.separator()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False
|
||||
sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
|
||||
|
||||
row = layout.row()
|
||||
|
||||
if ob:
|
||||
row.template_ID(ob, "active_material", new="material.new", live_icon=True)
|
||||
|
||||
if slot:
|
||||
icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
|
||||
row.prop(slot, "link", icon=icon_link, icon_only=True)
|
||||
|
||||
if gpd.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.operator("gpencil.stroke_change_color", text="Assign")
|
||||
row.operator("gpencil.color_select", text="Select")
|
||||
|
||||
elif mat:
|
||||
row.template_ID(space, "pin_id")
|
||||
|
||||
|
||||
# Used as parent for "Stroke" and "Fill" panels
|
||||
class MATERIAL_PT_gpencil_surface(GPMaterialButtonsPanel, Panel):
|
||||
bl_label = "Surface"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
ma = context.object.active_material
|
||||
if ma is None or ma.grease_pencil is None:
|
||||
return False
|
||||
|
||||
return ob and ob.type == 'GPENCIL'
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel):
|
||||
bl_label = "Stroke"
|
||||
bl_parent_id = 'MATERIAL_PT_gpencil_surface'
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ma = context.object.active_material
|
||||
if ma is not None and ma.grease_pencil is not None:
|
||||
gpcolor = ma.grease_pencil
|
||||
|
||||
col = layout.column()
|
||||
col.active = not gpcolor.lock
|
||||
|
||||
col.prop(gpcolor, "mode")
|
||||
|
||||
col.prop(gpcolor, "stroke_style", text="Style")
|
||||
|
||||
if gpcolor.stroke_style == 'TEXTURE':
|
||||
row = col.row()
|
||||
row.enabled = not gpcolor.lock
|
||||
col = row.column(align=True)
|
||||
col.template_ID(gpcolor, "stroke_image", open="image.open")
|
||||
col.prop(gpcolor, "pixel_size", text="UV Factor")
|
||||
col.prop(gpcolor, "use_stroke_pattern", text="Use As Pattern")
|
||||
|
||||
if gpcolor.stroke_style == 'SOLID' or gpcolor.use_stroke_pattern is True:
|
||||
col.prop(gpcolor, "color", text="Color")
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel):
|
||||
bl_label = "Fill"
|
||||
bl_parent_id = 'MATERIAL_PT_gpencil_surface'
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ma = context.object.active_material
|
||||
if ma is not None and ma.grease_pencil:
|
||||
gpcolor = ma.grease_pencil
|
||||
|
||||
# color settings
|
||||
col = layout.column()
|
||||
col.active = not gpcolor.lock
|
||||
col.prop(gpcolor, "fill_style", text="Style")
|
||||
|
||||
if gpcolor.fill_style == 'GRADIENT':
|
||||
col.prop(gpcolor, "gradient_type")
|
||||
|
||||
if gpcolor.fill_style != 'TEXTURE':
|
||||
col.prop(gpcolor, "fill_color", text="Color")
|
||||
|
||||
if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'):
|
||||
col.prop(gpcolor, "mix_color", text="Secondary Color")
|
||||
|
||||
if gpcolor.fill_style == 'GRADIENT':
|
||||
col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True)
|
||||
|
||||
if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'):
|
||||
col.prop(gpcolor, "flip", text="Flip Colors")
|
||||
|
||||
col.prop(gpcolor, "pattern_shift", text="Location")
|
||||
col.prop(gpcolor, "pattern_scale", text="Scale")
|
||||
|
||||
if gpcolor.gradient_type == 'RADIAL' and gpcolor.fill_style not in ('SOLID', 'CHESSBOARD'):
|
||||
col.prop(gpcolor, "pattern_radius", text="Radius")
|
||||
else:
|
||||
if gpcolor.fill_style != 'SOLID':
|
||||
col.prop(gpcolor, "pattern_angle", text="Angle")
|
||||
|
||||
if gpcolor.fill_style == 'CHESSBOARD':
|
||||
col.prop(gpcolor, "pattern_gridsize", text="Box Size")
|
||||
|
||||
# Texture
|
||||
if gpcolor.fill_style == 'TEXTURE' or (gpcolor.texture_mix is True and gpcolor.fill_style == 'SOLID'):
|
||||
col.template_ID(gpcolor, "fill_image", open="image.open")
|
||||
|
||||
if gpcolor.fill_style == 'TEXTURE':
|
||||
col.prop(gpcolor, "use_fill_pattern", text="Use As Pattern")
|
||||
if gpcolor.use_fill_pattern is True:
|
||||
col.prop(gpcolor, "fill_color", text="Color")
|
||||
|
||||
col.prop(gpcolor, "texture_offset", text="Offset")
|
||||
col.prop(gpcolor, "texture_scale", text="Scale")
|
||||
col.prop(gpcolor, "texture_angle")
|
||||
col.prop(gpcolor, "texture_opacity")
|
||||
col.prop(gpcolor, "texture_clamp", text="Clip Image")
|
||||
|
||||
if gpcolor.use_fill_pattern is False:
|
||||
col.prop(gpcolor, "texture_mix", text="Mix With Color")
|
||||
|
||||
if gpcolor.texture_mix is True:
|
||||
col.prop(gpcolor, "fill_color", text="Mix Color")
|
||||
col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True)
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_preview(GPMaterialButtonsPanel, Panel):
|
||||
bl_label = "Preview"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
ma = context.object.active_material
|
||||
self.layout.label(ma.name)
|
||||
self.layout.template_preview(ma)
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_custom_props(GPMaterialButtonsPanel, PropertyPanel, Panel):
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_OPENGL'}
|
||||
_context_path = "object.active_material"
|
||||
_property_type = bpy.types.Material
|
||||
|
||||
|
||||
class MATERIAL_PT_gpencil_options(GPMaterialButtonsPanel, Panel):
|
||||
bl_label = "Options"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ma = context.object.active_material
|
||||
if ma is not None and ma.grease_pencil is not None:
|
||||
gpcolor = ma.grease_pencil
|
||||
layout.prop(gpcolor, "pass_index")
|
||||
|
||||
|
||||
classes = (
|
||||
GPENCIL_UL_matslots,
|
||||
GPENCIL_MT_color_specials,
|
||||
MATERIAL_PT_gpencil_slots,
|
||||
MATERIAL_PT_gpencil_preview,
|
||||
MATERIAL_PT_gpencil_surface,
|
||||
MATERIAL_PT_gpencil_strokecolor,
|
||||
MATERIAL_PT_gpencil_fillcolor,
|
||||
MATERIAL_PT_gpencil_options,
|
||||
MATERIAL_PT_gpencil_custom_props,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
#ifndef __BKE_GPENCIL_MODIFIER_H__
|
||||
#define __BKE_GPENCIL_MODIFIER_H__
|
||||
|
||||
/** \file BKE_greasepencil_modifier.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "DNA_gpencil_modifier_types.h" /* needed for all enum typdefs */
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
struct ID;
|
||||
struct Depsgraph;
|
||||
struct DerivedMesh;
|
||||
struct bContext; /* NOTE: bakeModifier() - called from UI - needs to create new datablocks, hence the need for this */
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct ListBase;
|
||||
struct bArmature;
|
||||
struct Main;
|
||||
struct GpencilModifierData;
|
||||
struct BMEditMesh;
|
||||
struct DepsNodeHandle;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
struct bGPDstroke;
|
||||
struct ModifierUpdateDepsgraphContext;
|
||||
|
||||
#define GPENCIL_MODIFIER_ACTIVE(_md, _is_render) (((_md->mode & eGpencilModifierMode_Realtime) && (_is_render == false)) || \
|
||||
((_md->mode & eGpencilModifierMode_Render) && (_is_render == true)))
|
||||
#define GPENCIL_MODIFIER_EDIT(_md, _is_edit) (((_md->mode & eGpencilModifierMode_Editmode) == 0) && (_is_edit))
|
||||
|
||||
typedef enum {
|
||||
/* Should not be used, only for None modifier type */
|
||||
eGpencilModifierTypeType_None,
|
||||
|
||||
/* grease pencil modifiers */
|
||||
eGpencilModifierTypeType_Gpencil,
|
||||
} GpencilModifierTypeType;
|
||||
|
||||
typedef enum {
|
||||
eGpencilModifierTypeFlag_SupportsMapping = (1 << 0),
|
||||
eGpencilModifierTypeFlag_SupportsEditmode = (1 << 1),
|
||||
|
||||
/* For modifiers that support editmode this determines if the
|
||||
* modifier should be enabled by default in editmode. This should
|
||||
* only be used by modifiers that are relatively speedy and
|
||||
* also generally used in editmode, otherwise let the user enable
|
||||
* it by hand.
|
||||
*/
|
||||
eGpencilModifierTypeFlag_EnableInEditmode = (1 << 2),
|
||||
|
||||
/* For modifiers that require original data and so cannot
|
||||
* be placed after any non-deformative modifier.
|
||||
*/
|
||||
eGpencilModifierTypeFlag_RequiresOriginalData = (1 << 3),
|
||||
|
||||
/* max one per type */
|
||||
eGpencilModifierTypeFlag_Single = (1 << 4),
|
||||
|
||||
/* can't be added manually by user */
|
||||
eGpencilModifierTypeFlag_NoUserAdd = (1 << 5),
|
||||
} GpencilModifierTypeFlag;
|
||||
|
||||
/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */
|
||||
typedef void(*GreasePencilObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag);
|
||||
typedef void(*GreasePencilIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag);
|
||||
typedef void(*GreasePencilTexWalkFunc)(void *userData, struct Object *ob, struct GpencilModifierData *md, const char *propname);
|
||||
|
||||
typedef struct GpencilModifierTypeInfo {
|
||||
/* The user visible name for this modifier */
|
||||
char name[32];
|
||||
|
||||
/* The DNA struct name for the modifier data type, used to
|
||||
* write the DNA data out.
|
||||
*/
|
||||
char struct_name[32];
|
||||
|
||||
/* The size of the modifier data type, used by allocation. */
|
||||
int struct_size;
|
||||
|
||||
GpencilModifierType type;
|
||||
GpencilModifierTypeFlag flags;
|
||||
|
||||
|
||||
/********************* Non-optional functions *********************/
|
||||
|
||||
/* Copy instance data for this modifier type. Should copy all user
|
||||
* level settings to the target modifier.
|
||||
*/
|
||||
void (*copyData)(const struct GpencilModifierData *md, struct GpencilModifierData *target);
|
||||
|
||||
/* Callback for GP "stroke" modifiers that operate on the
|
||||
* shape and parameters of the provided strokes (e.g. Thickness, Noise, etc.)
|
||||
*
|
||||
* The gpl parameter contains the GP layer that the strokes come from.
|
||||
* While access is provided to this data, you should not directly access
|
||||
* the gpl->frames data from the modifier. Instead, use the gpf parameter
|
||||
* instead.
|
||||
*
|
||||
* The gps parameter contains the GP stroke to operate on. This is usually a copy
|
||||
* of the original (unmodified and saved to files) stroke data.
|
||||
*/
|
||||
void (*deformStroke)(struct GpencilModifierData *md, struct Depsgraph *depsgraph,
|
||||
struct Object *ob, struct bGPDlayer *gpl, struct bGPDstroke *gps);
|
||||
|
||||
/* Callback for GP "geometry" modifiers that create extra geometry
|
||||
* in the frame (e.g. Array)
|
||||
*
|
||||
* The gpf parameter contains the GP frame/strokes to operate on. This is
|
||||
* usually a copy of the original (unmodified and saved to files) stroke data.
|
||||
* Modifiers should only add any generated strokes to this frame (and not one accessed
|
||||
* via the gpl parameter).
|
||||
*
|
||||
* The modifier_index parameter indicates where the modifier is
|
||||
* in the modifier stack in relation to other modifiers.
|
||||
*/
|
||||
void (*generateStrokes)(struct GpencilModifierData *md, struct Depsgraph *depsgraph,
|
||||
struct Object *ob, struct bGPDlayer *gpl, struct bGPDframe *gpf);
|
||||
|
||||
/* Bake-down GP modifier's effects into the GP datablock.
|
||||
*
|
||||
* This gets called when the user clicks the "Apply" button in the UI.
|
||||
* As such, this callback needs to go through all layers/frames in the
|
||||
* datablock, mutating the geometry and/or creating new datablocks/objects
|
||||
*/
|
||||
void (*bakeModifier)(struct Main *bmain, struct Depsgraph *depsgraph,
|
||||
struct GpencilModifierData *md, struct Object *ob);
|
||||
|
||||
/********************* Optional functions *********************/
|
||||
|
||||
/* Initialize new instance data for this modifier type, this function
|
||||
* should set modifier variables to their default values.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*initData)(struct GpencilModifierData *md);
|
||||
|
||||
/* Free internal modifier data variables, this function should
|
||||
* not free the md variable itself.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*freeData)(struct GpencilModifierData *md);
|
||||
|
||||
/* Return a boolean value indicating if this modifier is able to be
|
||||
* calculated based on the modifier data. This is *not* regarding the
|
||||
* md->flag, that is tested by the system, this is just if the data
|
||||
* validates (for example, a lattice will return false if the lattice
|
||||
* object is not defined).
|
||||
*
|
||||
* This function is optional (assumes never disabled if not present).
|
||||
*/
|
||||
bool (*isDisabled)(struct GpencilModifierData *md, int userRenderParams);
|
||||
|
||||
/* Add the appropriate relations to the dependency graph.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*updateDepsgraph)(struct GpencilModifierData *md,
|
||||
const struct ModifierUpdateDepsgraphContext *ctx);
|
||||
|
||||
/* Should return true if the modifier needs to be recalculated on time
|
||||
* changes.
|
||||
*
|
||||
* This function is optional (assumes false if not present).
|
||||
*/
|
||||
bool (*dependsOnTime)(struct GpencilModifierData *md);
|
||||
|
||||
|
||||
/* Should call the given walk function on with a pointer to each Object
|
||||
* pointer that the modifier data stores. This is used for linking on file
|
||||
* load and for unlinking objects or forwarding object references.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*foreachObjectLink)(struct GpencilModifierData *md, struct Object *ob,
|
||||
GreasePencilObjectWalkFunc walk, void *userData);
|
||||
|
||||
/* Should call the given walk function with a pointer to each ID
|
||||
* pointer (i.e. each datablock pointer) that the modifier data
|
||||
* stores. This is used for linking on file load and for
|
||||
* unlinking datablocks or forwarding datablock references.
|
||||
*
|
||||
* This function is optional. If it is not present, foreachObjectLink
|
||||
* will be used.
|
||||
*/
|
||||
void (*foreachIDLink)(struct GpencilModifierData *md, struct Object *ob,
|
||||
GreasePencilIDWalkFunc walk, void *userData);
|
||||
|
||||
/* Should call the given walk function for each texture that the
|
||||
* modifier data stores. This is used for finding all textures in
|
||||
* the context for the UI.
|
||||
*
|
||||
* This function is optional. If it is not present, it will be
|
||||
* assumed the modifier has no textures.
|
||||
*/
|
||||
void (*foreachTexLink)(struct GpencilModifierData *md, struct Object *ob,
|
||||
GreasePencilTexWalkFunc walk, void *userData);
|
||||
} GpencilModifierTypeInfo;
|
||||
|
||||
void BKE_gpencil_instance_modifier_instance_tfm(struct InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]);
|
||||
|
||||
/* Initialize modifier's global data (type info and some common global storages). */
|
||||
void BKE_gpencil_modifier_init(void);
|
||||
|
||||
const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type);
|
||||
struct GpencilModifierData *BKE_gpencil_modifier_new(int type);
|
||||
void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, const int flag);
|
||||
void BKE_gpencil_modifier_free(struct GpencilModifierData *md);
|
||||
bool BKE_gpencil_modifier_unique_name(struct ListBase *modifiers, struct GpencilModifierData *gmd);
|
||||
bool BKE_gpencil_modifier_dependsOnTime(struct GpencilModifierData *md);
|
||||
struct GpencilModifierData *BKE_gpencil_modifiers_findByType(struct Object *ob, GpencilModifierType type);
|
||||
struct GpencilModifierData *BKE_gpencil_modifiers_findByName(struct Object *ob, const char *name);
|
||||
void BKE_gpencil_modifier_copyData_generic(const struct GpencilModifierData *md_src, struct GpencilModifierData *md_dst);
|
||||
void BKE_gpencil_modifier_copyData(struct GpencilModifierData *md, struct GpencilModifierData *target);
|
||||
void BKE_gpencil_modifier_copyData_ex(struct GpencilModifierData *md, struct GpencilModifierData *target, const int flag);
|
||||
void BKE_gpencil_modifiers_foreachIDLink(struct Object *ob, GreasePencilIDWalkFunc walk, void *userData);
|
||||
void BKE_gpencil_modifiers_foreachTexLink(struct Object *ob, GreasePencilTexWalkFunc walk, void *userData);
|
||||
|
||||
bool BKE_gpencil_has_geometry_modifiers(struct Object *ob);
|
||||
|
||||
void BKE_gpencil_stroke_modifiers(
|
||||
struct Depsgraph *depsgraph, struct Object *ob,
|
||||
struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, bool is_render);
|
||||
void BKE_gpencil_geometry_modifiers(
|
||||
struct Depsgraph *depsgraph, struct Object *ob,
|
||||
struct bGPDlayer *gpl, struct bGPDframe *gpf, bool is_render);
|
||||
|
||||
void BKE_gpencil_lattice_init(struct Object *ob);
|
||||
void BKE_gpencil_lattice_clear(struct Object *ob);
|
||||
|
||||
#endif /* __BKE_GPENCIL_MODIFIER_H__ */
|
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
#ifndef __BKE_SHADER_FX_H__
|
||||
#define __BKE_SHADER_FX_H__
|
||||
|
||||
/** \file BKE_shader_fx.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "DNA_shader_fx_types.h" /* needed for all enum typdefs */
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
struct ID;
|
||||
struct Depsgraph;
|
||||
struct DerivedMesh;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct ListBase;
|
||||
struct bArmature;
|
||||
struct Main;
|
||||
struct ShaderFxData;
|
||||
struct DepsNodeHandle;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
struct bGPDstroke;
|
||||
struct ModifierUpdateDepsgraphContext;
|
||||
|
||||
#define SHADER_FX_ACTIVE(_fx, _is_render) (((_fx->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \
|
||||
((_fx->mode & eShaderFxMode_Render) && (_is_render == true)))
|
||||
#define SHADER_FX_EDIT(_fx, _is_edit) (((_fx->mode & eShaderFxMode_Editmode) == 0) && (_is_edit))
|
||||
|
||||
typedef enum {
|
||||
/* Should not be used, only for None type */
|
||||
eShaderFxType_NoneType,
|
||||
|
||||
/* grease pencil effects */
|
||||
eShaderFxType_GpencilType,
|
||||
} ShaderFxTypeType;
|
||||
|
||||
typedef enum {
|
||||
eShaderFxTypeFlag_SupportsEditmode = (1 << 0),
|
||||
|
||||
/* For effects that support editmode this determines if the
|
||||
* effect should be enabled by default in editmode.
|
||||
*/
|
||||
eShaderFxTypeFlag_EnableInEditmode = (1 << 2),
|
||||
|
||||
/* max one per type */
|
||||
eShaderFxTypeFlag_Single = (1 << 4),
|
||||
|
||||
/* can't be added manually by user */
|
||||
eShaderFxTypeFlag_NoUserAdd = (1 << 5),
|
||||
} ShaderFxTypeFlag;
|
||||
|
||||
/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */
|
||||
typedef void(*ShaderFxObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag);
|
||||
typedef void(*ShaderFxIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag);
|
||||
typedef void(*ShaderFxTexWalkFunc)(void *userData, struct Object *ob, struct ShaderFxData *fx, const char *propname);
|
||||
|
||||
typedef struct ShaderFxTypeInfo {
|
||||
/* The user visible name for this effect */
|
||||
char name[32];
|
||||
|
||||
/* The DNA struct name for the effect data type, used to
|
||||
* write the DNA data out.
|
||||
*/
|
||||
char struct_name[32];
|
||||
|
||||
/* The size of the effect data type, used by allocation. */
|
||||
int struct_size;
|
||||
|
||||
ShaderFxTypeType type;
|
||||
ShaderFxTypeFlag flags;
|
||||
|
||||
/* Copy instance data for this effect type. Should copy all user
|
||||
* level settings to the target effect.
|
||||
*/
|
||||
void(*copyData)(const struct ShaderFxData *fx, struct ShaderFxData *target);
|
||||
|
||||
/* Initialize new instance data for this effect type, this function
|
||||
* should set effect variables to their default values.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*initData)(struct ShaderFxData *fx);
|
||||
|
||||
/* Free internal effect data variables, this function should
|
||||
* not free the fx variable itself.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*freeData)(struct ShaderFxData *fx);
|
||||
|
||||
/* Return a boolean value indicating if this effect is able to be
|
||||
* calculated based on the effect data. This is *not* regarding the
|
||||
* fx->flag, that is tested by the system, this is just if the data
|
||||
* validates (for example, a lattice will return false if the lattice
|
||||
* object is not defined).
|
||||
*
|
||||
* This function is optional (assumes never disabled if not present).
|
||||
*/
|
||||
bool (*isDisabled)(struct ShaderFxData *fx, int userRenderParams);
|
||||
|
||||
/* Add the appropriate relations to the dependency graph.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*updateDepsgraph)(struct ShaderFxData *fx,
|
||||
const struct ModifierUpdateDepsgraphContext *ctx);
|
||||
|
||||
/* Should return true if the effect needs to be recalculated on time
|
||||
* changes.
|
||||
*
|
||||
* This function is optional (assumes false if not present).
|
||||
*/
|
||||
bool (*dependsOnTime)(struct ShaderFxData *fx);
|
||||
|
||||
|
||||
/* Should call the given walk function on with a pointer to each Object
|
||||
* pointer that the effect data stores. This is used for linking on file
|
||||
* load and for unlinking objects or forwarding object references.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*foreachObjectLink)(struct ShaderFxData *fx, struct Object *ob,
|
||||
ShaderFxObjectWalkFunc walk, void *userData);
|
||||
|
||||
/* Should call the given walk function with a pointer to each ID
|
||||
* pointer (i.e. each datablock pointer) that the effect data
|
||||
* stores. This is used for linking on file load and for
|
||||
* unlinking datablocks or forwarding datablock references.
|
||||
*
|
||||
* This function is optional. If it is not present, foreachObjectLink
|
||||
* will be used.
|
||||
*/
|
||||
void (*foreachIDLink)(struct ShaderFxData *fx, struct Object *ob,
|
||||
ShaderFxIDWalkFunc walk, void *userData);
|
||||
} ShaderFxTypeInfo;
|
||||
|
||||
/* Initialize global data (type info and some common global storages). */
|
||||
void BKE_shaderfx_init(void);
|
||||
|
||||
const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type);
|
||||
struct ShaderFxData *BKE_shaderfx_new(int type);
|
||||
void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag);
|
||||
void BKE_shaderfx_free(struct ShaderFxData *fx);
|
||||
bool BKE_shaderfx_unique_name(struct ListBase *shaderfx, struct ShaderFxData *fx);
|
||||
bool BKE_shaderfx_dependsOnTime(struct ShaderFxData *fx);
|
||||
struct ShaderFxData *BKE_shaderfx_findByType(struct Object *ob, ShaderFxType type);
|
||||
struct ShaderFxData *BKE_shaderfx_findByName(struct Object *ob, const char *name);
|
||||
void BKE_shaderfx_copyData_generic(const struct ShaderFxData *fx_src, struct ShaderFxData *fx_dst);
|
||||
void BKE_shaderfx_copyData(struct ShaderFxData *fx, struct ShaderFxData *target);
|
||||
void BKE_shaderfx_copyData_ex(struct ShaderFxData *fx, struct ShaderFxData *target, const int flag);
|
||||
void BKE_shaderfx_foreachIDLink(struct Object *ob, ShaderFxIDWalkFunc walk, void *userData);
|
||||
|
||||
bool BKE_shaderfx_has_gpencil(struct Object *ob);
|
||||
|
||||
#endif /* __BKE_SHADER_FX_H__ */
|
@ -0,0 +1,679 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2017, Blender Foundation
|
||||
* This is a new part of Blender
|
||||
*
|
||||
* Contributor(s): Antonio Vazquez
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/blenkernel/intern/gpencil_modifier.c
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_gpencil_modifier_types.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_library_query.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_gpencil_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "MOD_gpencil_modifiertypes.h"
|
||||
|
||||
static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = { NULL };
|
||||
|
||||
/* *************************************************** */
|
||||
/* Geometry Utilities */
|
||||
|
||||
/* calculate stroke normal using some points */
|
||||
void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
|
||||
{
|
||||
if (gps->totpoints < 3) {
|
||||
zero_v3(r_normal);
|
||||
return;
|
||||
}
|
||||
|
||||
bGPDspoint *points = gps->points;
|
||||
int totpoints = gps->totpoints;
|
||||
|
||||
const bGPDspoint *pt0 = &points[0];
|
||||
const bGPDspoint *pt1 = &points[1];
|
||||
const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
|
||||
|
||||
float vec1[3];
|
||||
float vec2[3];
|
||||
|
||||
/* initial vector (p0 -> p1) */
|
||||
sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
|
||||
|
||||
/* point vector at 3/4 */
|
||||
sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
|
||||
|
||||
/* vector orthogonal to polygon plane */
|
||||
cross_v3_v3v3(r_normal, vec1, vec2);
|
||||
|
||||
/* Normalize vector */
|
||||
normalize_v3(r_normal);
|
||||
}
|
||||
|
||||
/* Get points of stroke always flat to view not affected by camera view or view position */
|
||||
static void gpencil_stroke_project_2d(const bGPDspoint *points, int totpoints, vec2f *points2d)
|
||||
{
|
||||
const bGPDspoint *pt0 = &points[0];
|
||||
const bGPDspoint *pt1 = &points[1];
|
||||
const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
|
||||
|
||||
float locx[3];
|
||||
float locy[3];
|
||||
float loc3[3];
|
||||
float normal[3];
|
||||
|
||||
/* local X axis (p0 -> p1) */
|
||||
sub_v3_v3v3(locx, &pt1->x, &pt0->x);
|
||||
|
||||
/* point vector at 3/4 */
|
||||
sub_v3_v3v3(loc3, &pt3->x, &pt0->x);
|
||||
|
||||
/* vector orthogonal to polygon plane */
|
||||
cross_v3_v3v3(normal, locx, loc3);
|
||||
|
||||
/* local Y axis (cross to normal/x axis) */
|
||||
cross_v3_v3v3(locy, normal, locx);
|
||||
|
||||
/* Normalize vectors */
|
||||
normalize_v3(locx);
|
||||
normalize_v3(locy);
|
||||
|
||||
/* Get all points in local space */
|
||||
for (int i = 0; i < totpoints; i++) {
|
||||
const bGPDspoint *pt = &points[i];
|
||||
float loc[3];
|
||||
|
||||
/* Get local space using first point as origin */
|
||||
sub_v3_v3v3(loc, &pt->x, &pt0->x);
|
||||
|
||||
vec2f *point = &points2d[i];
|
||||
point->x = dot_v3v3(loc, locx);
|
||||
point->y = dot_v3v3(loc, locy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Stroke Simplify ------------------------------------- */
|
||||
|
||||
/* Reduce a series of points to a simplified version, but
|
||||
* maintains the general shape of the series
|
||||
*
|
||||
* Ramer - Douglas - Peucker algorithm
|
||||
* by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
|
||||
*/
|
||||
static void gpencil_rdp_stroke(bGPDstroke *gps, vec2f *points2d, float epsilon)
|
||||
{
|
||||
vec2f *old_points2d = points2d;
|
||||
int totpoints = gps->totpoints;
|
||||
char *marked = NULL;
|
||||
char work;
|
||||
|
||||
int start = 1;
|
||||
int end = gps->totpoints - 2;
|
||||
|
||||
marked = MEM_callocN(totpoints, "GP marked array");
|
||||
marked[start] = 1;
|
||||
marked[end] = 1;
|
||||
|
||||
work = 1;
|
||||
int totmarked = 0;
|
||||
/* while still reducing */
|
||||
while (work) {
|
||||
int ls, le;
|
||||
work = 0;
|
||||
|
||||
ls = start;
|
||||
le = start + 1;
|
||||
|
||||
/* while not over interval */
|
||||
while (ls < end) {
|
||||
int max_i = 0;
|
||||
float v1[2];
|
||||
/* divided to get more control */
|
||||
float max_dist = epsilon / 10.0f;
|
||||
|
||||
/* find the next marked point */
|
||||
while (marked[le] == 0) {
|
||||
le++;
|
||||
}
|
||||
|
||||
/* perpendicular vector to ls-le */
|
||||
v1[1] = old_points2d[le].x - old_points2d[ls].x;
|
||||
v1[0] = old_points2d[ls].y - old_points2d[le].y;
|
||||
|
||||
for (int i = ls + 1; i < le; i++) {
|
||||
float mul;
|
||||
float dist;
|
||||
float v2[2];
|
||||
|
||||
v2[0] = old_points2d[i].x - old_points2d[ls].x;
|
||||
v2[1] = old_points2d[i].y - old_points2d[ls].y;
|
||||
|
||||
if (v2[0] == 0 && v2[1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mul = (float)(v1[0] * v2[0] + v1[1] * v2[1]) / (float)(v2[0] * v2[0] + v2[1] * v2[1]);
|
||||
|
||||
dist = mul * mul * (v2[0] * v2[0] + v2[1] * v2[1]);
|
||||
|
||||
if (dist > max_dist) {
|
||||
max_dist = dist;
|
||||
max_i = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_i != 0) {
|
||||
work = 1;
|
||||
marked[max_i] = 1;
|
||||
totmarked++;
|
||||
}
|
||||
|
||||
ls = le;
|
||||
le = ls + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* adding points marked */
|
||||
bGPDspoint *old_points = MEM_dupallocN(gps->points);
|
||||
MDeformVert *old_dvert = MEM_dupallocN(gps->dvert);
|
||||
|
||||
/* resize gps */
|
||||
gps->flag |= GP_STROKE_RECALC_CACHES;
|
||||
gps->tot_triangles = 0;
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < totpoints; i++) {
|
||||
bGPDspoint *pt_src = &old_points[i];
|
||||
bGPDspoint *pt = &gps->points[j];
|
||||
|
||||
MDeformVert *dvert_src = &old_dvert[i];
|
||||
MDeformVert *dvert = &gps->dvert[j];
|
||||
|
||||
if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
|
||||
memcpy(pt, pt_src, sizeof(bGPDspoint));
|
||||
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
BKE_gpencil_free_point_weights(dvert_src);
|
||||
}
|
||||
}
|
||||
|
||||
gps->totpoints = j;
|
||||
|
||||
MEM_SAFE_FREE(old_points);
|
||||
MEM_SAFE_FREE(old_dvert);
|
||||
MEM_SAFE_FREE(marked);
|
||||
}
|
||||
|
||||
/* Simplify stroke using Ramer-Douglas-Peucker algorithm */
|
||||
void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float factor)
|
||||
{
|
||||
/* first create temp data and convert points to 2D */
|
||||
vec2f *points2d = MEM_mallocN(sizeof(vec2f) * gps->totpoints, "GP Stroke temp 2d points");
|
||||
|
||||
gpencil_stroke_project_2d(gps->points, gps->totpoints, points2d);
|
||||
|
||||
gpencil_rdp_stroke(gps, points2d, factor);
|
||||
|
||||
MEM_SAFE_FREE(points2d);
|
||||
}
|
||||
|
||||
/* Simplify alternate vertex of stroke except extrems */
|
||||
void BKE_gpencil_simplify_fixed(bGPDstroke *gps)
|
||||
{
|
||||
if (gps->totpoints < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* save points */
|
||||
bGPDspoint *old_points = MEM_dupallocN(gps->points);
|
||||
MDeformVert *old_dvert = MEM_dupallocN(gps->dvert);
|
||||
|
||||
/* resize gps */
|
||||
int newtot = (gps->totpoints - 2) / 2;
|
||||
if (((gps->totpoints - 2) % 2) > 0) {
|
||||
newtot++;
|
||||
}
|
||||
newtot += 2;
|
||||
|
||||
gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
|
||||
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
|
||||
gps->flag |= GP_STROKE_RECALC_CACHES;
|
||||
gps->tot_triangles = 0;
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
bGPDspoint *pt_src = &old_points[i];
|
||||
bGPDspoint *pt = &gps->points[j];
|
||||
|
||||
MDeformVert *dvert_src = &old_dvert[i];
|
||||
MDeformVert *dvert = &gps->dvert[j];
|
||||
|
||||
if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
|
||||
memcpy(pt, pt_src, sizeof(bGPDspoint));
|
||||
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
BKE_gpencil_free_point_weights(dvert_src);
|
||||
}
|
||||
}
|
||||
|
||||
gps->totpoints = j;
|
||||
|
||||
MEM_SAFE_FREE(old_points);
|
||||
MEM_SAFE_FREE(old_dvert);
|
||||
}
|
||||
|
||||
/* *************************************************** */
|
||||
/* Modifier Utilities */
|
||||
|
||||
/* Lattice Modifier ---------------------------------- */
|
||||
/* Usually, evaluation of the lattice modifier is self-contained.
|
||||
* However, since GP's modifiers operate on a per-stroke basis,
|
||||
* we need to these two extra functions that called before/after
|
||||
* each loop over all the geometry being evaluated.
|
||||
*/
|
||||
|
||||
/* init lattice deform data */
|
||||
void BKE_gpencil_lattice_init(Object *ob)
|
||||
{
|
||||
GpencilModifierData *md;
|
||||
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
||||
if (md->type == eGpencilModifierType_Lattice) {
|
||||
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
||||
Object *latob = NULL;
|
||||
|
||||
latob = mmd->object;
|
||||
if ((!latob) || (latob->type != OB_LATTICE)) {
|
||||
return;
|
||||
}
|
||||
if (mmd->cache_data) {
|
||||
end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
|
||||
}
|
||||
|
||||
/* init deform data */
|
||||
mmd->cache_data = (struct LatticeDeformData *)init_latt_deform(latob, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clear lattice deform data */
|
||||
void BKE_gpencil_lattice_clear(Object *ob)
|
||||
{
|
||||
GpencilModifierData *md;
|
||||
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
||||
if (md->type == eGpencilModifierType_Lattice) {
|
||||
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
||||
if ((mmd) && (mmd->cache_data)) {
|
||||
end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
|
||||
mmd->cache_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* *************************************************** */
|
||||
/* Modifier Methods - Evaluation Loops, etc. */
|
||||
|
||||
/* check if exist geometry modifiers */
|
||||
bool BKE_gpencil_has_geometry_modifiers(Object *ob)
|
||||
{
|
||||
GpencilModifierData *md;
|
||||
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if (mti && mti->generateStrokes) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* apply stroke modifiers */
|
||||
void BKE_gpencil_stroke_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *UNUSED(gpf), bGPDstroke *gps, bool is_render)
|
||||
{
|
||||
GpencilModifierData *md;
|
||||
bGPdata *gpd = ob->data;
|
||||
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
||||
|
||||
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
||||
if (GPENCIL_MODIFIER_ACTIVE(md, is_render))
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if (GPENCIL_MODIFIER_EDIT(md, is_edit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mti && mti->deformStroke) {
|
||||
mti->deformStroke(md, depsgraph, ob, gpl, gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* apply stroke geometry modifiers */
|
||||
void BKE_gpencil_geometry_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bool is_render)
|
||||
{
|
||||
GpencilModifierData *md;
|
||||
bGPdata *gpd = ob->data;
|
||||
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
||||
|
||||
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
||||
if (GPENCIL_MODIFIER_ACTIVE(md, is_render))
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if (GPENCIL_MODIFIER_EDIT(md, is_edit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mti->generateStrokes) {
|
||||
mti->generateStrokes(md, depsgraph, ob, gpl, gpf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* *************************************************** */
|
||||
|
||||
void BKE_gpencil_eval_geometry(Depsgraph *depsgraph,
|
||||
bGPdata *gpd)
|
||||
{
|
||||
DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd);
|
||||
int ctime = (int)DEG_get_ctime(depsgraph);
|
||||
|
||||
/* update active frame */
|
||||
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV);
|
||||
}
|
||||
|
||||
/* TODO: Move "derived_gpf" logic here from DRW_gpencil_populate_datablock()?
|
||||
* This would be better than inventing our own logic for this stuff...
|
||||
*/
|
||||
|
||||
/* TODO: Move the following code to "BKE_gpencil_eval_done()" (marked as an exit node)
|
||||
* later when there's more happening here. For now, let's just keep this in here to avoid
|
||||
* needing to have one more node slowing down evaluation...
|
||||
*/
|
||||
if (DEG_is_active(depsgraph)) {
|
||||
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
|
||||
|
||||
/* sync "actframe" changes back to main-db too,
|
||||
* so that editing tools work with copy-on-write
|
||||
* when the current frame changes
|
||||
*/
|
||||
for (bGPDlayer *gpl = gpd_orig->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_init(void)
|
||||
{
|
||||
/* Initialize modifier types */
|
||||
gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */
|
||||
}
|
||||
|
||||
GpencilModifierData *BKE_gpencil_modifier_new(int type)
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type);
|
||||
GpencilModifierData *md = MEM_callocN(mti->struct_size, mti->struct_name);
|
||||
|
||||
/* note, this name must be made unique later */
|
||||
BLI_strncpy(md->name, DATA_(mti->name), sizeof(md->name));
|
||||
|
||||
md->type = type;
|
||||
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render | eGpencilModifierMode_Expanded;
|
||||
md->flag = eGpencilModifierFlag_StaticOverride_Local;
|
||||
|
||||
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode)
|
||||
md->mode |= eGpencilModifierMode_Editmode;
|
||||
|
||||
if (mti->initData) mti->initData(md);
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
static void modifier_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag)
|
||||
{
|
||||
ID *id = *idpoin;
|
||||
if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) {
|
||||
id_us_min(id);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag)
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
if (mti->foreachIDLink) {
|
||||
mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL);
|
||||
}
|
||||
else if (mti->foreachObjectLink) {
|
||||
mti->foreachObjectLink(md, NULL, (GreasePencilObjectWalkFunc)modifier_free_data_id_us_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (mti->freeData) mti->freeData(md);
|
||||
if (md->error) MEM_freeN(md->error);
|
||||
|
||||
MEM_freeN(md);
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_free(GpencilModifierData *md)
|
||||
{
|
||||
BKE_gpencil_modifier_free_ex(md, 0);
|
||||
}
|
||||
|
||||
/* check unique name */
|
||||
bool BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *gmd)
|
||||
{
|
||||
if (modifiers && gmd) {
|
||||
const GpencilModifierTypeInfo *gmti = BKE_gpencil_modifierType_getInfo(gmd->type);
|
||||
return BLI_uniquename(modifiers, gmd, DATA_(gmti->name), '.', offsetof(GpencilModifierData, name), sizeof(gmd->name));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_gpencil_modifier_dependsOnTime(GpencilModifierData *md)
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
return mti->dependsOnTime && mti->dependsOnTime(md);
|
||||
}
|
||||
|
||||
const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type)
|
||||
{
|
||||
/* type unsigned, no need to check < 0 */
|
||||
if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && modifier_gpencil_types[type]->name[0] != '\0') {
|
||||
return modifier_gpencil_types[type];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_copyData_generic(const GpencilModifierData *md_src, GpencilModifierData *md_dst)
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md_src->type);
|
||||
|
||||
/* md_dst may have alredy be fully initialized with some extra allocated data,
|
||||
* we need to free it now to avoid memleak. */
|
||||
if (mti->freeData) {
|
||||
mti->freeData(md_dst);
|
||||
}
|
||||
|
||||
const size_t data_size = sizeof(GpencilModifierData);
|
||||
const char *md_src_data = ((const char *)md_src) + data_size;
|
||||
char *md_dst_data = ((char *)md_dst) + data_size;
|
||||
BLI_assert(data_size <= (size_t)mti->struct_size);
|
||||
memcpy(md_dst_data, md_src_data, (size_t)mti->struct_size - data_size);
|
||||
}
|
||||
|
||||
static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag)
|
||||
{
|
||||
ID *id = *idpoin;
|
||||
if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) {
|
||||
id_us_plus(id);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_copyData_ex(GpencilModifierData *md, GpencilModifierData *target, const int flag)
|
||||
{
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
target->mode = md->mode;
|
||||
target->flag = md->flag;
|
||||
|
||||
if (mti->copyData) {
|
||||
mti->copyData(md, target);
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
if (mti->foreachIDLink) {
|
||||
mti->foreachIDLink(target, NULL, gpencil_modifier_copy_data_id_us_cb, NULL);
|
||||
}
|
||||
else if (mti->foreachObjectLink) {
|
||||
mti->foreachObjectLink(target, NULL, (GreasePencilObjectWalkFunc)gpencil_modifier_copy_data_id_us_cb, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifier_copyData(GpencilModifierData *md, GpencilModifierData *target)
|
||||
{
|
||||
BKE_gpencil_modifier_copyData_ex(md, target, 0);
|
||||
}
|
||||
|
||||
GpencilModifierData *BKE_gpencil_modifiers_findByType(Object *ob, GpencilModifierType type)
|
||||
{
|
||||
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
||||
|
||||
for (; md; md = md->next)
|
||||
if (md->type == type)
|
||||
break;
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifiers_foreachIDLink(Object *ob, GreasePencilIDWalkFunc walk, void *userData)
|
||||
{
|
||||
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
||||
|
||||
for (; md; md = md->next) {
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if (mti->foreachIDLink) mti->foreachIDLink(md, ob, walk, userData);
|
||||
else if (mti->foreachObjectLink) {
|
||||
/* each Object can masquerade as an ID, so this should be OK */
|
||||
GreasePencilObjectWalkFunc fp = (GreasePencilObjectWalkFunc)walk;
|
||||
mti->foreachObjectLink(md, ob, fp, userData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_gpencil_modifiers_foreachTexLink(Object *ob, GreasePencilTexWalkFunc walk, void *userData)
|
||||
{
|
||||
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
||||
|
||||
for (; md; md = md->next) {
|
||||
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
||||
|
||||
if (mti->foreachTexLink)
|
||||
mti->foreachTexLink(md, ob, walk, userData);
|
||||
}
|
||||
}
|
||||
|
||||
GpencilModifierData *BKE_gpencil_modifiers_findByName(Object *ob, const char *name)
|
||||
{
|
||||
return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name));
|
||||
}
|
||||
|
||||
/* helper function for per-instance positioning */
|
||||
void BKE_gpencil_instance_modifier_instance_tfm(InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4])
|
||||
{
|
||||
float offset[3], rot[3], scale[3];
|
||||
int ri = mmd->rnd[0];
|
||||
float factor;
|
||||
|
||||
offset[0] = mmd->offset[0] * elem_idx[0];
|
||||
offset[1] = mmd->offset[1] * elem_idx[1];
|
||||
offset[2] = mmd->offset[2] * elem_idx[2];
|
||||
|
||||
/* rotation */
|
||||
if (mmd->flag & GP_INSTANCE_RANDOM_ROT) {
|
||||
factor = mmd->rnd_rot * mmd->rnd[ri];
|
||||
mul_v3_v3fl(rot, mmd->rot, factor);
|
||||
add_v3_v3(rot, mmd->rot);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(rot, mmd->rot);
|
||||
}
|
||||
|
||||
/* scale */
|
||||
if (mmd->flag & GP_INSTANCE_RANDOM_SIZE) {
|
||||
factor = mmd->rnd_size * mmd->rnd[ri];
|
||||
mul_v3_v3fl(scale, mmd->scale, factor);
|
||||
add_v3_v3(scale, mmd->scale);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(scale, mmd->scale);
|
||||
}
|
||||
|
||||
/* advance random index */
|
||||
mmd->rnd[0]++;
|
||||
if (mmd->rnd[0] > 19) {
|
||||
mmd->rnd[0] = 1;
|
||||
}
|
||||
|
||||
/* calculate matrix */
|
||||
loc_eul_size_to_mat4(r_mat, offset, rot, scale);
|
||||
}
|