blender/release/scripts/startup/bl_ui/properties_data_curve.py
Hans Goudey b9febb54a4 Geometry Nodes: Support modifier on curve objects
With this commit, curve objects support the geometry nodes modifier.

Curves objects now evaluate to `CurveEval` unless there was a previous
implicit conversion (tessellating modifiers, mesh modifiers, or the
settings in the curve "Geometry" panel). In the new code, curves are
only considered to be the wire edges-- any generated surface is a mesh
instead, stored in the evaluated geometry set.

The consolidation of concepts mentioned above allows remove a lot of
code that had to do with maintaining the `DispList` type temporarily
for modifiers and rendering. Instead, render engines see a separate
object for the mesh from the mesh geometry component, and when the
curve object evaluates to a curve, the `CurveEval` is always used for
drawing wire edges.

However, currently the `DispList` type is still maintained and used as
an intermediate step in implicit mesh conversion. In the future, more
uses of it could be changed to use `CurveEval` and `Mesh` instead.

This is mostly not changed behavior, it is just a formalization of
existing logic after recent fixes for 2.8 versions last year and two
years ago. Also, in the future more functionality can be converted
to nodes, removing cases of implicit conversions. For more discussion
on that topic, see T89676.

The `use_fill_deform` option is removed. It has not worked properly
since 2.62, and the choice for filling a curve before or after
deformation will work much better and be clearer with a node system.

Applying the geometry nodes modifier to generate a curve is not
implemented with this commit, so applying the modifier won't work
at all. This is a separate technical challenge, and should be solved
in a separate step.

Differential Revision: https://developer.blender.org/D11597
2021-09-11 13:54:40 -05:00

514 lines
15 KiB
Python

# ##### 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 rna_prop_ui import PropertyPanel
from bpy.types import Curve, SurfaceCurve, TextCurve
class CurveButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return (context.curve is not None)
class CurveButtonsPanelCurve(CurveButtonsPanel):
@classmethod
def poll(cls, context):
return (type(context.curve) is Curve)
class CurveButtonsPanelSurface(CurveButtonsPanel):
@classmethod
def poll(cls, context):
return (type(context.curve) is SurfaceCurve)
class CurveButtonsPanelText(CurveButtonsPanel):
@classmethod
def poll(cls, context):
return (type(context.curve) is TextCurve)
class CurveButtonsPanelActive(CurveButtonsPanel):
"""Same as above but for curves only"""
@classmethod
def poll(cls, context):
curve = context.curve
return (curve and type(curve) is not TextCurve and curve.splines.active)
class DATA_PT_context_curve(CurveButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
def draw(self, context):
layout = self.layout
obj = context.object
curve = context.curve
space = context.space_data
if obj:
layout.template_ID(obj, "data")
elif curve:
layout.template_ID(space, "pin_id")
class DATA_PT_shape_curve(CurveButtonsPanel, Panel):
bl_label = "Shape"
def draw(self, context):
layout = self.layout
curve = context.curve
is_surf = type(curve) is SurfaceCurve
is_curve = type(curve) is Curve
is_text = type(curve) is TextCurve
if is_curve:
row = layout.row()
row.prop(curve, "dimensions", expand=True)
layout.use_property_split = True
col = layout.column()
sub = col.column(align=True)
sub.prop(curve, "resolution_u", text="Resolution Preview U")
if is_surf:
sub.prop(curve, "resolution_v", text="V")
sub = col.column(align=True)
sub.prop(curve, "render_resolution_u", text="Render U")
if is_surf:
sub.prop(curve, "render_resolution_v", text="V")
col.separator()
if is_curve:
col.prop(curve, "twist_mode")
col.prop(curve, "twist_smooth", text="Smooth")
elif is_text:
col.prop(curve, "use_fast_edit", text="Fast Editing")
if is_curve or is_text:
col = layout.column()
col.separator()
sub = col.column()
sub.active = (curve.dimensions == '2D' or (curve.bevel_mode != 'OBJECT' and curve.dimensions == '3D'))
sub.prop(curve, "fill_mode")
if is_curve:
col = layout.column()
col.separator()
sub = col.column(heading="Curve Deform", align=True)
sub.prop(curve, "use_radius")
sub.prop(curve, "use_stretch")
sub.prop(curve, "use_deform_bounds")
class DATA_PT_curve_texture_space(CurveButtonsPanel, Panel):
bl_label = "Texture Space"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
curve = context.curve
col = layout.column()
col.prop(curve, "use_auto_texspace")
col = layout.column()
col.prop(curve, "texspace_location")
col.prop(curve, "texspace_size")
layout.operator("curve.match_texture_space")
class DATA_PT_geometry_curve(CurveButtonsPanelCurve, Panel):
bl_label = "Geometry"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return (type(context.curve) in {Curve, TextCurve})
def draw(self, context):
layout = self.layout
layout.use_property_split = True
curve = context.curve
col = layout.column()
col.prop(curve, "offset")
sub = col.column()
sub.active = (curve.bevel_mode != 'OBJECT')
sub.prop(curve, "extrude")
col.prop(curve, "taper_object")
col.prop(curve, "taper_radius_mode")
if type(curve) is not TextCurve:
# This setting makes no sense for texts, since we have no control over start/end of the bevel object curve.
sub = col.column()
sub.active = curve.taper_object is not None
sub.prop(curve, "use_map_taper")
class DATA_PT_geometry_curve_bevel(CurveButtonsPanelCurve, Panel):
bl_label = "Bevel"
bl_parent_id = "DATA_PT_geometry_curve"
@classmethod
def poll(cls, context):
return (type(context.curve) in {Curve, TextCurve})
def draw(self, context):
layout = self.layout
curve = context.curve
layout.prop(curve, "bevel_mode", expand=True)
layout.use_property_split = True
col = layout.column()
if curve.bevel_mode == 'OBJECT':
col.prop(curve, "bevel_object", text="Object")
else:
col.prop(curve, "bevel_depth", text="Depth")
col.prop(curve, "bevel_resolution", text="Resolution")
col.prop(curve, "use_fill_caps")
if curve.bevel_mode == 'PROFILE':
col.template_curveprofile(curve, "bevel_profile")
class DATA_PT_geometry_curve_start_end(CurveButtonsPanelCurve, Panel):
bl_label = "Start & End Mapping"
bl_parent_id = "DATA_PT_geometry_curve"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
# Text objects don't support these properties
return (type(context.curve) in {Curve})
def draw(self, context):
layout = self.layout
layout.use_property_split = True
curve = context.curve
col = layout.column()
col.active = (
((curve.bevel_depth > 0.0) or (curve.extrude > 0.0)) or
((curve.bevel_mode == 'OBJECT') and curve.bevel_object is not None)
)
sub = col.column(align=True)
sub.prop(curve, "bevel_factor_start", text="Factor Start")
sub.prop(curve, "bevel_factor_end", text="End")
sub = col.column(align=True)
sub.prop(curve, "bevel_factor_mapping_start", text="Mapping Start")
sub.prop(curve, "bevel_factor_mapping_end", text="End")
class DATA_PT_pathanim(CurveButtonsPanelCurve, Panel):
bl_label = "Path Animation"
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
curve = context.curve
self.layout.prop(curve, "use_path", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
curve = context.curve
layout.active = curve.use_path
col = layout.column()
col.prop(curve, "path_duration", text="Frames")
col.prop(curve, "eval_time")
# these are for paths only
col.separator()
col.prop(curve, "use_path_clamp")
col.prop(curve, "use_path_follow")
class DATA_PT_active_spline(CurveButtonsPanelActive, Panel):
bl_label = "Active Spline"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
curve = context.curve
act_spline = curve.splines.active
is_surf = type(curve) is SurfaceCurve
is_poly = (act_spline.type == 'POLY')
col = layout.column()
if is_poly:
# These settings are below but its easier to have
# polys set aside since they use so few settings
col.prop(act_spline, "use_cyclic_u")
col.prop(act_spline, "use_smooth")
else:
sub = col.column(heading="Cyclic", align=True)
sub.prop(act_spline, "use_cyclic_u", text="U")
if is_surf:
sub.prop(act_spline, "use_cyclic_v", text="V")
if act_spline.type == 'NURBS':
sub = col.column(heading="Bezier", align=True)
# sub.active = (not act_spline.use_cyclic_u)
sub.prop(act_spline, "use_bezier_u", text="U")
if is_surf:
subsub = sub.column()
subsub.active = (not act_spline.use_cyclic_v)
subsub.prop(act_spline, "use_bezier_v", text="V")
sub = col.column(heading="Endpoint", align=True)
sub.prop(act_spline, "use_endpoint_u", text="U")
if is_surf:
subsub = sub.column()
subsub.active = (not act_spline.use_cyclic_v)
subsub.prop(act_spline, "use_endpoint_v", text="V")
sub = col.column(align=True)
sub.prop(act_spline, "order_u", text="Order U")
if is_surf:
sub.prop(act_spline, "order_v", text="V")
sub = col.column(align=True)
sub.prop(act_spline, "resolution_u", text="Resolution U")
if is_surf:
sub.prop(act_spline, "resolution_v", text="V")
if act_spline.type == 'BEZIER':
col.separator()
sub = col.column()
sub.active = (curve.dimensions == '3D')
sub.prop(act_spline, "tilt_interpolation", text="Interpolation Tilt")
col.prop(act_spline, "radius_interpolation", text="Radius")
layout.prop(act_spline, "use_smooth")
class DATA_PT_font(CurveButtonsPanelText, Panel):
bl_label = "Font"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
text = context.curve
char = text.edit_format
mode = context.mode
row = layout.split(factor=0.25)
row.label(text="Regular")
row.template_ID(text, "font", open="font.open", unlink="font.unlink")
row = layout.split(factor=0.25)
row.label(text="Bold")
row.template_ID(text, "font_bold", open="font.open", unlink="font.unlink")
row = layout.split(factor=0.25)
row.label(text="Italic")
row.template_ID(text, "font_italic", open="font.open", unlink="font.unlink")
row = layout.split(factor=0.25)
row.label(text="Bold & Italic")
row.template_ID(text, "font_bold_italic", open="font.open", unlink="font.unlink")
if mode == 'EDIT_TEXT':
layout.separator()
row = layout.row(align=True)
row.prop(char, "use_bold", toggle=True)
row.prop(char, "use_italic", toggle=True)
row.prop(char, "use_underline", toggle=True)
row.prop(char, "use_small_caps", toggle=True)
class DATA_PT_font_transform(CurveButtonsPanelText, Panel):
bl_label = "Transform"
bl_parent_id = "DATA_PT_font"
def draw(self, context):
layout = self.layout
text = context.curve
layout.use_property_split = True
col = layout.column()
col.separator()
col.prop(text, "size", text="Size")
col.prop(text, "shear")
col.separator()
col.prop(text, "family")
col.prop(text, "follow_curve")
col.separator()
sub = col.column(align=True)
sub.prop(text, "underline_position", text="Underline Position")
sub.prop(text, "underline_height", text="Underline Thickness")
col.prop(text, "small_caps_scale", text="Small Caps Scale")
class DATA_PT_paragraph(CurveButtonsPanelText, Panel):
bl_label = "Paragraph"
def draw(self, context):
# Parent panel
pass
class DATA_PT_paragraph_alignment(CurveButtonsPanelText, Panel):
bl_parent_id = "DATA_PT_paragraph"
bl_label = "Alignment"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
text = context.curve
col = layout.column()
col.prop(text, "align_x", text="Horizontal")
col.prop(text, "align_y", text="Vertical")
class DATA_PT_paragraph_spacing(CurveButtonsPanelText, Panel):
bl_parent_id = "DATA_PT_paragraph"
bl_label = "Spacing"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
text = context.curve
col = layout.column(align=True)
col.prop(text, "space_character", text="Character Spacing")
col.prop(text, "space_word", text="Word Spacing")
col.prop(text, "space_line", text="Line Spacing")
layout.separator()
col = layout.column(align=True)
col.prop(text, "offset_x", text="Offset X")
col.prop(text, "offset_y", text="Y")
class DATA_PT_text_boxes(CurveButtonsPanelText, Panel):
bl_label = "Text Boxes"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
text = context.curve
layout.operator("font.textbox_add", icon='ADD')
layout.prop(text, "overflow", text="Overflow")
for i, box in enumerate(text.text_boxes):
boxy = layout.box()
row = boxy.row()
col = row.column()
col.use_property_split = True
sub = col.column(align=True)
sub.prop(box, "width", text="Size X")
sub.prop(box, "height", text="Y")
sub = col.column(align=True)
sub.prop(box, "x", text="Offset X")
sub.prop(box, "y", text="Y")
row.operator("font.textbox_remove", text="", icon='X', emboss=False).index = i
class DATA_PT_custom_props_curve(CurveButtonsPanel, PropertyPanel, Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
_context_path = "object.data"
_property_type = bpy.types.Curve
classes = (
DATA_PT_context_curve,
DATA_PT_shape_curve,
DATA_PT_curve_texture_space,
DATA_PT_geometry_curve,
DATA_PT_geometry_curve_bevel,
DATA_PT_geometry_curve_start_end,
DATA_PT_pathanim,
DATA_PT_active_spline,
DATA_PT_font,
DATA_PT_font_transform,
DATA_PT_paragraph,
DATA_PT_paragraph_alignment,
DATA_PT_paragraph_spacing,
DATA_PT_text_boxes,
DATA_PT_custom_props_curve,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)