New freehand curve drawing tool
- Access with Shift-LMB or from the 'Create' toolbar tab. - Uses curve fitting for bezier curves, with error and corner angle options. - Optional tablet pressure to curve radius mapping. - Depth can use the cursor or optionally draw onto the surface, for the entire stroke or using the stroke start. - Stroke plane can optionally be perpendicular to, or aligned to the surface normal. - Optional radius tapering and for start/end points. - Supports operator redo and calling from Python.
This commit is contained in:
parent
e56e7bd1ec
commit
8ac662c77a
|
@ -55,7 +55,7 @@
|
|||
*
|
||||
* \returns zero on success, nonzero is reserved for error values.
|
||||
*/
|
||||
int curve_fit_cubic_from_points_db(
|
||||
int curve_fit_cubic_to_points_db(
|
||||
const double *points,
|
||||
const unsigned int points_len,
|
||||
const unsigned int dims,
|
||||
|
@ -67,7 +67,7 @@ int curve_fit_cubic_from_points_db(
|
|||
unsigned int **r_cubic_orig_index,
|
||||
unsigned int **r_corner_index_array, unsigned int *r_corner_index_len);
|
||||
|
||||
int curve_fit_cubic_from_points_fl(
|
||||
int curve_fit_cubic_to_points_fl(
|
||||
const float *points,
|
||||
const unsigned int points_len,
|
||||
const unsigned int dims,
|
||||
|
|
|
@ -846,7 +846,7 @@ static void fit_cubic_to_points(
|
|||
* Take an array of 3d points.
|
||||
* return the cubic splines
|
||||
*/
|
||||
int curve_fit_cubic_from_points_db(
|
||||
int curve_fit_cubic_to_points_db(
|
||||
const double *points,
|
||||
const uint points_len,
|
||||
const uint dims,
|
||||
|
@ -984,9 +984,9 @@ int curve_fit_cubic_from_points_db(
|
|||
}
|
||||
|
||||
/**
|
||||
* A version of #curve_fit_cubic_from_points_db to handle floats
|
||||
* A version of #curve_fit_cubic_to_points_db to handle floats
|
||||
*/
|
||||
int curve_fit_cubic_from_points_fl(
|
||||
int curve_fit_cubic_to_points_fl(
|
||||
const float *points,
|
||||
const uint points_len,
|
||||
const uint dims,
|
||||
|
@ -1009,7 +1009,7 @@ int curve_fit_cubic_from_points_fl(
|
|||
float *cubic_array_fl = NULL;
|
||||
uint cubic_array_len = 0;
|
||||
|
||||
int result = curve_fit_cubic_from_points_db(
|
||||
int result = curve_fit_cubic_to_points_db(
|
||||
points_db, points_len, dims, error_threshold, corners, corners_len,
|
||||
&cubic_array_db, &cubic_array_len,
|
||||
r_cubic_orig_index,
|
||||
|
|
|
@ -136,6 +136,7 @@ class VIEW3D_PT_tools_add_object(View3DPanel, Panel):
|
|||
|
||||
@staticmethod
|
||||
def draw_add_curve(layout, label=False):
|
||||
|
||||
if label:
|
||||
layout.label(text="Bezier:")
|
||||
layout.operator("curve.primitive_bezier_curve_add", text="Bezier", icon='CURVE_BEZCURVE')
|
||||
|
@ -149,6 +150,10 @@ class VIEW3D_PT_tools_add_object(View3DPanel, Panel):
|
|||
layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE')
|
||||
layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH')
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("curve.draw", icon='LINE_DATA')
|
||||
|
||||
@staticmethod
|
||||
def draw_add_surface(layout):
|
||||
layout.operator("surface.primitive_nurbs_surface_curve_add", text="Nurbs Curve", icon='SURFACE_NCURVE')
|
||||
|
@ -546,8 +551,61 @@ class VIEW3D_PT_tools_add_curve_edit(View3DPanel, Panel):
|
|||
|
||||
VIEW3D_PT_tools_add_object.draw_add_curve(col, label=True)
|
||||
|
||||
# ********** default tools for editmode_surface ****************
|
||||
|
||||
class VIEW3D_PT_tools_curveedit_options_stroke(View3DPanel, Panel):
|
||||
bl_category = "Options"
|
||||
bl_context = "curve_edit"
|
||||
bl_label = "Curve Stroke"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
tool_settings = context.tool_settings
|
||||
cps = tool_settings.curve_paint_settings
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.prop(cps, "curve_type")
|
||||
|
||||
if cps.curve_type == 'BEZIER':
|
||||
col.label("Bezier Options:")
|
||||
col.prop(cps, "error_threshold")
|
||||
col.prop(cps, "use_corners_detect")
|
||||
|
||||
col = layout.column()
|
||||
col.active = cps.use_corners_detect
|
||||
col.prop(cps, "corner_angle")
|
||||
|
||||
col.label("Pressure Radius:")
|
||||
row = layout.row(align=True)
|
||||
rowsub = row.row(align=True)
|
||||
rowsub.prop(cps, "radius_min", text="Min")
|
||||
rowsub.prop(cps, "radius_max", text="Max")
|
||||
|
||||
row.prop(cps, "use_pressure_radius", text="", icon_only=True)
|
||||
|
||||
col = layout.column()
|
||||
col.label("Taper Radius:")
|
||||
row = layout.row(align=True)
|
||||
row.prop(cps, "radius_taper_start", text="Start")
|
||||
row.prop(cps, "radius_taper_end", text="End")
|
||||
|
||||
col = layout.column()
|
||||
col.label("Projection Depth:")
|
||||
row = layout.row(align=True)
|
||||
row.prop(cps, "depth_mode", expand=True)
|
||||
|
||||
col = layout.column()
|
||||
if cps.depth_mode == 'SURFACE':
|
||||
col.prop(cps, "use_stroke_endpoints")
|
||||
if cps.use_stroke_endpoints:
|
||||
colsub = layout.column(align=True)
|
||||
colsub.prop(cps, "surface_plane", expand=True)
|
||||
else:
|
||||
col.prop(cps, "radius_offset")
|
||||
|
||||
|
||||
# ********** default tools for editmode_surface ****************
|
||||
|
||||
class VIEW3D_PT_tools_transform_surface(View3DPanel, Panel):
|
||||
bl_category = "Tools"
|
||||
|
|
|
@ -616,6 +616,12 @@ void BKE_scene_init(Scene *sce)
|
|||
sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH;
|
||||
sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE;
|
||||
|
||||
sce->toolsettings->curve_paint_settings.curve_type = CU_BEZIER;
|
||||
sce->toolsettings->curve_paint_settings.flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
|
||||
sce->toolsettings->curve_paint_settings.error_threshold = 8;
|
||||
sce->toolsettings->curve_paint_settings.radius_max = 1.0f;
|
||||
sce->toolsettings->curve_paint_settings.corner_angle = DEG2RADF(70.0f);
|
||||
|
||||
sce->toolsettings->statvis.overhang_axis = OB_NEGZ;
|
||||
sce->toolsettings->statvis.overhang_min = 0;
|
||||
sce->toolsettings->statvis.overhang_max = DEG2RADF(45.0f);
|
||||
|
|
|
@ -1072,5 +1072,17 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
|
||||
CurvePaintSettings *cps = &scene->toolsettings->curve_paint_settings;
|
||||
if (cps->error_threshold == 0) {
|
||||
cps->curve_type = CU_BEZIER;
|
||||
cps->flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
|
||||
cps->error_threshold = 8;
|
||||
cps->radius_max = 1.0f;
|
||||
cps->corner_angle = DEG2RADF(70.0f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,20 +23,24 @@ set(INC
|
|||
../../blenkernel
|
||||
../../blenlib
|
||||
../../blentranslation
|
||||
../../gpu
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/guardedalloc
|
||||
../../../../intern/glew-mx
|
||||
../../../../extern/curve_fit_nd
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
${GLEW_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
set(SRC
|
||||
curve_ops.c
|
||||
editcurve.c
|
||||
editcurve_add.c
|
||||
editcurve_paint.c
|
||||
editcurve_select.c
|
||||
editfont.c
|
||||
|
||||
|
@ -47,4 +51,6 @@ if(WITH_INTERNATIONAL)
|
|||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
|
|
@ -166,4 +166,7 @@ void SURFACE_OT_primitive_nurbs_surface_cylinder_add(struct wmOperatorType *ot);
|
|||
void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot);
|
||||
void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot);
|
||||
|
||||
/* editcurve_paint.c */
|
||||
void CURVE_OT_draw(struct wmOperatorType *ot);
|
||||
|
||||
#endif /* __CURVE_INTERN_H__ */
|
||||
|
|
|
@ -135,6 +135,7 @@ void ED_operatortypes_curve(void)
|
|||
WM_operatortype_append(CURVE_OT_make_segment);
|
||||
WM_operatortype_append(CURVE_OT_spin);
|
||||
WM_operatortype_append(CURVE_OT_vertex_add);
|
||||
WM_operatortype_append(CURVE_OT_draw);
|
||||
WM_operatortype_append(CURVE_OT_extrude);
|
||||
WM_operatortype_append(CURVE_OT_cyclic_toggle);
|
||||
|
||||
|
@ -234,6 +235,9 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
|
|||
|
||||
WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "CURVE_OT_draw", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
|
||||
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1266,6 +1266,40 @@ typedef enum {
|
|||
UNIFIED_PAINT_BRUSH_ALPHA_PRESSURE = (1 << 4)
|
||||
} UnifiedPaintSettingsFlags;
|
||||
|
||||
|
||||
typedef struct CurvePaintSettings {
|
||||
char curve_type;
|
||||
char flag;
|
||||
char depth_mode;
|
||||
char surface_plane;
|
||||
int error_threshold;
|
||||
float radius_min, radius_max;
|
||||
float radius_taper_start, radius_taper_end;
|
||||
float radius_offset;
|
||||
float corner_angle;
|
||||
} CurvePaintSettings;
|
||||
|
||||
/* CurvePaintSettings.flag */
|
||||
enum {
|
||||
CURVE_PAINT_FLAG_CORNERS_DETECT = (1 << 0),
|
||||
CURVE_PAINT_FLAG_PRESSURE_RADIUS = (1 << 1),
|
||||
CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS = (1 << 2),
|
||||
};
|
||||
|
||||
/* CurvePaintSettings.depth_mode */
|
||||
enum {
|
||||
CURVE_PAINT_PROJECT_CURSOR = 0,
|
||||
CURVE_PAINT_PROJECT_SURFACE = 1,
|
||||
};
|
||||
|
||||
/* CurvePaintSettings.surface_plane */
|
||||
enum {
|
||||
CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW = 0,
|
||||
CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE = 1,
|
||||
CURVE_PAINT_SURFACE_PLANE_VIEW = 2,
|
||||
};
|
||||
|
||||
|
||||
/* *************************************************************** */
|
||||
/* Stats */
|
||||
|
||||
|
@ -1417,6 +1451,8 @@ typedef struct ToolSettings {
|
|||
/* Unified Paint Settings */
|
||||
struct UnifiedPaintSettings unified_paint_settings;
|
||||
|
||||
struct CurvePaintSettings curve_paint_settings;
|
||||
|
||||
struct MeshStatVis statvis;
|
||||
} ToolSettings;
|
||||
|
||||
|
|
|
@ -1767,6 +1767,11 @@ static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr))
|
|||
return BLI_strdup("tool_settings.unified_paint_settings");
|
||||
}
|
||||
|
||||
static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.curve_paint_settings");
|
||||
}
|
||||
|
||||
/* generic function to recalc geometry */
|
||||
static void rna_EditMesh_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
|
@ -2497,6 +2502,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
|||
RNA_def_property_struct_type(prop, "UnifiedPaintSettings");
|
||||
RNA_def_property_ui_text(prop, "Unified Paint Settings", NULL);
|
||||
|
||||
/* Curve Paint Settings */
|
||||
prop = RNA_def_property(srna, "curve_paint_settings", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
RNA_def_property_struct_type(prop, "CurvePaintSettings");
|
||||
RNA_def_property_ui_text(prop, "Curve Paint Settings", NULL);
|
||||
|
||||
/* Mesh Statistics */
|
||||
prop = RNA_def_property(srna, "statvis", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_NEVER_NULL);
|
||||
|
@ -2595,6 +2606,96 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna)
|
|||
"when unlocked brush size is given in pixels");
|
||||
}
|
||||
|
||||
|
||||
static void rna_def_curve_paint_settings(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "CurvePaintSettings", NULL);
|
||||
RNA_def_struct_path_func(srna, "rna_CurvePaintSettings_path");
|
||||
RNA_def_struct_ui_text(srna, "Curve Paint Settings", "");
|
||||
|
||||
static EnumPropertyItem curve_type_items[] = {
|
||||
{CU_POLY, "POLY", 0, "Poly", ""},
|
||||
{CU_BEZIER, "BEZIER", 0, "Bezier", ""},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
prop = RNA_def_property(srna, "curve_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "curve_type");
|
||||
RNA_def_property_enum_items(prop, curve_type_items);
|
||||
RNA_def_property_ui_text(prop, "Type", "Type of curve to use for new strokes");
|
||||
|
||||
prop = RNA_def_property(srna, "use_corners_detect", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_CORNERS_DETECT);
|
||||
RNA_def_property_ui_text(prop, "Detect Corners", "Detect corners and use non-aligned handles");
|
||||
|
||||
prop = RNA_def_property(srna, "use_pressure_radius", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_PRESSURE_RADIUS);
|
||||
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
|
||||
RNA_def_property_ui_text(prop, "Use Pressure", "Map tablet pressure to curve radius");
|
||||
|
||||
prop = RNA_def_property(srna, "use_stroke_endpoints", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS);
|
||||
RNA_def_property_ui_text(prop, "Only First", "Use the start of the stroke for the depth");
|
||||
|
||||
prop = RNA_def_property(srna, "error_threshold", PROP_INT, PROP_PIXEL);
|
||||
RNA_def_property_range(prop, 1, 100);
|
||||
RNA_def_property_ui_text(prop, "Tolerance", "Allow deviation for a smoother, less preceise line");
|
||||
|
||||
prop = RNA_def_property(srna, "corner_angle", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_range(prop, 0, M_PI);
|
||||
RNA_def_property_ui_text(prop, "Corner Angle", "Angles above this are considered corners");
|
||||
|
||||
prop = RNA_def_property(srna, "radius_min", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0.0, 100.0);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
|
||||
RNA_def_property_ui_text(prop, "Radius Min",
|
||||
"Minimum radius when the minimum pressure is applied (also the minimum when tapering)");
|
||||
|
||||
prop = RNA_def_property(srna, "radius_max", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0.0, 100.0);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
|
||||
RNA_def_property_ui_text(prop, "Radius Max",
|
||||
"Radius to use when the maximum pressure is applied (or when a tablet isn't used)");
|
||||
|
||||
prop = RNA_def_property(srna, "radius_taper_start", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0.0, 1.0);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
|
||||
RNA_def_property_ui_text(prop, "Radius Min", "Taper factor for the radius of each point along the curve");
|
||||
|
||||
prop = RNA_def_property(srna, "radius_taper_end", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0.0, 10.0);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
|
||||
RNA_def_property_ui_text(prop, "Radius Max", "Taper factor for the radius of each point along the curve");
|
||||
|
||||
prop = RNA_def_property(srna, "radius_offset", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, -10.0, 10.0);
|
||||
RNA_def_property_ui_range(prop, -1.0f, 1.0, 1, 2);
|
||||
RNA_def_property_ui_text(prop, "Offset", "Offset the stroke from the surface");
|
||||
|
||||
static EnumPropertyItem depth_mode_items[] = {
|
||||
{CURVE_PAINT_PROJECT_CURSOR, "CURSOR", 0, "Cursor", ""},
|
||||
{CURVE_PAINT_PROJECT_SURFACE, "SURFACE", 0, "Surface", ""},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
prop = RNA_def_property(srna, "depth_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "depth_mode");
|
||||
RNA_def_property_enum_items(prop, depth_mode_items);
|
||||
RNA_def_property_ui_text(prop, "Depth", "Method of projecting depth");
|
||||
|
||||
static EnumPropertyItem surface_plane_items[] = {
|
||||
{CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, "NORMAL_VIEW", 0, "Normal/View", "Draw perpendicular to the surface"},
|
||||
{CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE, "NORMAL_SURFACE", 0, "Normal/Surface", "Draw aligned to the surface"},
|
||||
{CURVE_PAINT_SURFACE_PLANE_VIEW, "VIEW", 0, "View", "Draw aligned to the viewport"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
prop = RNA_def_property(srna, "surface_plane", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "surface_plane");
|
||||
RNA_def_property_enum_items(prop, surface_plane_items);
|
||||
RNA_def_property_ui_text(prop, "Plane", "Plane for projected stroke");
|
||||
}
|
||||
|
||||
static void rna_def_statvis(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
@ -6720,6 +6821,7 @@ void RNA_def_scene(BlenderRNA *brna)
|
|||
RNA_define_animate_sdna(false);
|
||||
rna_def_tool_settings(brna);
|
||||
rna_def_unified_paint_settings(brna);
|
||||
rna_def_curve_paint_settings(brna);
|
||||
rna_def_statvis(brna);
|
||||
rna_def_unit_settings(brna);
|
||||
rna_def_scene_image_format_data(brna);
|
||||
|
|
Loading…
Reference in New Issue