Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py48
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_curveprofile.h76
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c1011
-rw-r--r--source/blender/blenkernel/intern/scene.c11
-rw-r--r--source/blender/blenloader/intern/readfile.c29
-rw-r--r--source/blender/blenloader/intern/versioning_280.c27
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c7
-rw-r--r--source/blender/blenloader/intern/writefile.c17
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c20
-rw-r--r--source/blender/bmesh/intern/bmesh_operator_api.h5
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.h6
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c10
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c2011
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h7
-rw-r--r--source/blender/editors/include/UI_interface.h3
-rw-r--r--source/blender/editors/interface/interface_draw.c232
-rw-r--r--source/blender/editors/interface/interface_handlers.c335
-rw-r--r--source/blender/editors/interface/interface_intern.h5
-rw-r--r--source/blender/editors/interface/interface_query.c3
-rw-r--r--source/blender/editors/interface/interface_templates.c608
-rw-r--r--source/blender/editors/interface/interface_widgets.c4
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c263
-rw-r--r--source/blender/makesdna/DNA_curveprofile_types.h91
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h19
-rw-r--r--source/blender/makesdna/DNA_scene_types.h9
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_enum_types.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c322
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c65
-rw-r--r--source/blender/makesrna/intern/rna_scene.c6
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c4
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c35
39 files changed, 4577 insertions, 726 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index b2ce3c8e608..f86b017806f 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -4680,6 +4680,8 @@ def km_bevel_modal_map(_params):
("MARK_SHARP_TOGGLE", {"type": 'K', "value": 'PRESS', "any": True}, None),
("OUTER_MITER_CHANGE", {"type": 'O', "value": 'PRESS', "any": True}, None),
("INNER_MITER_CHANGE", {"type": 'I', "value": 'PRESS', "any": True}, None),
+ ("CUSTOM_PROFILE_TOGGLE", {"type": 'Z', "value": 'PRESS', "any": True}, None),
+ ("VERTEX_MESH_CHANGE", {"type": 'N', "value": 'PRESS', "any": True}, None),
])
return keymap
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 05e8f0fce38..f576a29a783 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -133,43 +133,55 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
layout.prop(md, "end_cap")
def BEVEL(self, layout, ob, md):
- split = layout.split()
-
- col = split.column()
- if md.offset_type == 'PERCENT':
- col.prop(md, "width_pct")
+ offset_type = md.offset_type
+ if offset_type == 'PERCENT':
+ layout.prop(md, "width_pct")
else:
- col.prop(md, "width")
- col.prop(md, "segments")
- col.prop(md, "profile")
- col.prop(md, "material")
+ offset_text = "Width"
+ if offset_type == 'DEPTH':
+ offset_text = "Depth"
+ elif offset_type == 'OFFSET':
+ offset_text = "Offset"
+ layout.prop(md, "width", text=offset_text)
+ layout.row().prop(md, "offset_type", expand=True)
+ split = layout.split()
col = split.column()
col.prop(md, "use_only_vertices")
col.prop(md, "use_clamp_overlap")
col.prop(md, "loop_slide")
+ col = split.column()
col.prop(md, "mark_seam")
col.prop(md, "mark_sharp")
col.prop(md, "harden_normals")
+ layout.row().prop(md, "segments")
+ layout.row().prop(md, "profile")
+ layout.row().prop(md, "material")
+
+ layout.label(text="Miter Type:")
+ layout.row().prop(md, "miter_outer", text="Outer")
+ layout.row().prop(md, "miter_inner", text="Inner")
+ if md.miter_inner in {'MITER_PATCH', 'MITER_ARC'}:
+ layout.row().prop(md, "spread")
+
layout.label(text="Limit Method:")
layout.row().prop(md, "limit_method", expand=True)
if md.limit_method == 'ANGLE':
layout.prop(md, "angle_limit")
elif md.limit_method == 'VGROUP':
- layout.label(text="Vertex Group:")
layout.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
- layout.label(text="Width Method:")
- layout.row().prop(md, "offset_type", expand=True)
-
- layout.label(text="Set Face Strength Mode")
+ layout.label(text="Face Strength Mode:")
layout.row().prop(md, "face_strength_mode", expand=True)
- layout.label(text="Miter Patterns")
- layout.row().prop(md, "miter_outer")
- layout.row().prop(md, "miter_inner")
- layout.row().prop(md, "spread")
+ layout.label(text="Intersection Type:")
+ layout.row().prop(md, "vmesh_method", expand=True)
+ layout.row().prop(md, "use_custom_profile")
+ row = layout.row()
+ row.enabled = md.use_custom_profile
+ if md.use_custom_profile:
+ layout.template_curveprofile(md, "custom_profile")
def BOOLEAN(self, layout, _ob, md):
split = layout.split()
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 203543b0ef0..62923d18b70 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -68,6 +68,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_outliner_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_packedFile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_particle_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curveprofile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_rigidbody_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_scene_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_screen_types.h
diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h
new file mode 100644
index 00000000000..1f6659d785a
--- /dev/null
+++ b/source/blender/blenkernel/BKE_curveprofile.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+#ifndef __BKE_CURVEPROFILE_H__
+#define __BKE_CURVEPROFILE_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+struct CurveProfile;
+struct CurveProfilePoint;
+
+void BKE_curveprofile_set_defaults(struct CurveProfile *profile);
+
+struct CurveProfile *BKE_curveprofile_add(int preset);
+
+void BKE_curveprofile_free_data(struct CurveProfile *profile);
+
+void BKE_curveprofile_free(struct CurveProfile *profile);
+
+void BKE_curveprofile_copy_data(struct CurveProfile *target, const struct CurveProfile *profile);
+
+struct CurveProfile *BKE_curveprofile_copy(const struct CurveProfile *profile);
+
+bool BKE_curveprofile_remove_point(struct CurveProfile *profile, struct CurveProfilePoint *point);
+
+void BKE_curveprofile_remove_by_flag(struct CurveProfile *profile, const short flag);
+
+struct CurveProfilePoint *BKE_curveprofile_insert(struct CurveProfile *profile, float x, float y);
+
+void BKE_curveprofile_selected_handle_set(struct CurveProfile *profile, int type_1, int type_2);
+
+void BKE_curveprofile_reverse(struct CurveProfile *profile);
+
+void BKE_curveprofile_reset(struct CurveProfile *profile);
+
+void BKE_curveprofile_create_samples(struct CurveProfile *profile,
+ int segments_len,
+ bool sample_straight_edges,
+ struct CurveProfilePoint *r_samples);
+
+void BKE_curveprofile_initialize(struct CurveProfile *profile, short segments_len);
+
+/* Called for a complete update of the widget after modifications */
+void BKE_curveprofile_update(struct CurveProfile *profile, const bool rem_doubles);
+
+/* Need to find the total length of the curve to sample a portion of it */
+float BKE_curveprofile_total_length(const struct CurveProfile *profile);
+
+void BKE_curveprofile_create_samples_even_spacing(struct CurveProfile *profile,
+ int segments_len,
+ struct CurveProfilePoint *r_samples);
+
+/* Length portion is the fraction of the total path length where we want the location */
+void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile,
+ float length_portion,
+ float *x_out,
+ float *y_out);
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 023980292fa..49cead5c003 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -185,6 +185,7 @@ set(SRC
intern/pbvh_bmesh.c
intern/pbvh_parallel.cc
intern/pointcache.c
+ intern/curveprofile.c
intern/report.c
intern/rigidbody.c
intern/scene.c
@@ -330,6 +331,7 @@ set(SRC
BKE_particle.h
BKE_pbvh.h
BKE_pointcache.h
+ BKE_curveprofile.h
BKE_report.h
BKE_rigidbody.h
BKE_scene.h
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
new file mode 100644
index 00000000000..6689eca132d
--- /dev/null
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -0,0 +1,1011 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curveprofile_types.h"
+#include "DNA_curve_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
+
+#include "BKE_curveprofile.h"
+#include "BKE_curve.h"
+#include "BKE_fcurve.h"
+
+void BKE_curveprofile_free_data(CurveProfile *profile)
+{
+ MEM_SAFE_FREE(profile->path);
+ MEM_SAFE_FREE(profile->table);
+ MEM_SAFE_FREE(profile->segments);
+}
+
+void BKE_curveprofile_free(CurveProfile *profile)
+{
+ if (profile) {
+ BKE_curveprofile_free_data(profile);
+ MEM_freeN(profile);
+ }
+}
+
+void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profile)
+{
+ *target = *profile;
+
+ target->path = MEM_dupallocN(profile->path);
+ target->table = MEM_dupallocN(profile->table);
+ target->segments = MEM_dupallocN(profile->segments);
+}
+
+CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile)
+{
+ if (profile) {
+ CurveProfile *new_prdgt = MEM_dupallocN(profile);
+ BKE_curveprofile_copy_data(new_prdgt, profile);
+ return new_prdgt;
+ }
+ return NULL;
+}
+
+/** Removes a specific point from the path of control points.
+ * \note: Requires curveprofile_update call after. */
+bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *point)
+{
+ CurveProfilePoint *pts;
+
+ /* Must have 2 points minimum. */
+ if (profile->path_len <= 2) {
+ return false;
+ }
+
+ /* Input point must be within the array. */
+ if (!(point > profile->path && point < profile->path + profile->path_len)) {
+ return false;
+ }
+
+ pts = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, "path points");
+
+ uint i_delete = (uint)(point - profile->path);
+
+ /* Copy the before and after the deleted point. */
+ memcpy(pts, profile->path, i_delete);
+ memcpy(pts + i_delete, profile->path + i_delete + 1, (size_t)profile->path_len - i_delete - 1);
+
+ MEM_freeN(profile->path);
+ profile->path = pts;
+ profile->path_len -= 1;
+ return true;
+}
+
+/** Removes every point in the widget with the supplied flag set, except for the first and last.
+ * \param flag: CurveProfilePoint->flag.
+ * \note: Requires curveprofile_update call after. */
+void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag)
+{
+ int i_old, i_new, n_removed = 0;
+
+ /* Copy every point without the flag into the new path. */
+ CurveProfilePoint *new_pts = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
+ "profile path");
+
+ /* Build the new list without any of the points with the flag. Keep the first and last points. */
+ new_pts[0] = profile->path[0];
+ for (i_old = 1, i_new = 1; i_old < profile->path_len - 1; i_old++) {
+ if (!(profile->path[i_old].flag & flag)) {
+ new_pts[i_new] = profile->path[i_old];
+ i_new++;
+ }
+ else {
+ n_removed++;
+ }
+ }
+ new_pts[i_new] = profile->path[i_old];
+
+ MEM_freeN(profile->path);
+ profile->path = new_pts;
+ profile->path_len -= n_removed;
+}
+
+/** Adds a new point at the specified location. The choice for which points to place the new vertex
+ * between is made by checking which control point line segment is closest to the new point and
+ * placing the new vertex in between that segment's points.
+ * \note: Requires curveprofile_update call after. */
+CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float y)
+{
+ CurveProfilePoint *new_pt = NULL;
+ float new_loc[2] = {x, y};
+
+ /* Don't add more control points than the maximum size of the higher resolution table. */
+ if (profile->path_len == PROF_TABLE_MAX - 1) {
+ return NULL;
+ }
+
+ /* Find the index at the line segment that's closest to the new position. */
+ float distance;
+ float min_distance = FLT_MAX;
+ int i_insert = 0;
+ for (int i = 0; i < profile->path_len - 1; i++) {
+ float loc1[2] = {profile->path[i].x, profile->path[i].y};
+ float loc2[2] = {profile->path[i + 1].x, profile->path[i + 1].y};
+
+ distance = dist_squared_to_line_segment_v2(new_loc, loc1, loc2);
+ if (distance < min_distance) {
+ min_distance = distance;
+ i_insert = i + 1;
+ }
+ }
+
+ /* Insert the new point at the location we found and copy all of the old points in as well. */
+ profile->path_len++;
+ CurveProfilePoint *new_pts = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
+ "profile path");
+ for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) {
+ if (i_new != i_insert) {
+ /* Insert old points */
+ new_pts[i_new].x = profile->path[i_old].x;
+ new_pts[i_new].y = profile->path[i_old].y;
+ new_pts[i_new].flag = profile->path[i_old].flag & ~PROF_SELECT; /* Deselect old points. */
+ new_pts[i_new].h1 = profile->path[i_old].h1;
+ new_pts[i_new].h2 = profile->path[i_old].h2;
+ i_old++;
+ }
+ else {
+ /* Insert new point. */
+ new_pts[i_new].x = x;
+ new_pts[i_new].y = y;
+ new_pts[i_new].flag = PROF_SELECT;
+ new_pt = &new_pts[i_new];
+ /* Set handles of new point based on its neighbors. */
+ if (new_pts[i_new - 1].h2 == HD_VECT && profile->path[i_insert].h1 == HD_VECT) {
+ new_pt->h1 = new_pt->h2 = HD_VECT;
+ }
+ else {
+ new_pt->h1 = new_pt->h2 = HD_AUTO;
+ }
+ }
+ }
+
+ /* Free the old path and use the new one. */
+ MEM_freeN(profile->path);
+ profile->path = new_pts;
+ return new_pt;
+}
+
+/** Sets the handle type of the selected control points.
+ * \param type_*: Either HD_VECT or HD_AUTO. Handle types for the first and second handles.
+ * \note: Requires curveprofile_update call after. */
+void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
+{
+ for (int i = 0; i < profile->path_len; i++) {
+ if (profile->path[i].flag & PROF_SELECT) {
+ switch (type_1) {
+ case HD_AUTO:
+ profile->path[i].h1 = HD_AUTO;
+ break;
+ case HD_VECT:
+ profile->path[i].h1 = HD_VECT;
+ break;
+ default:
+ profile->path[i].h1 = HD_AUTO;
+ break;
+ }
+ switch (type_2) {
+ case HD_AUTO:
+ profile->path[i].h2 = HD_AUTO;
+ break;
+ case HD_VECT:
+ profile->path[i].h2 = HD_VECT;
+ break;
+ default:
+ profile->path[i].h1 = HD_AUTO;
+ break;
+ }
+ }
+ }
+}
+
+/** Flips the profile across the diagonal so that its orientation is reversed.
+ * \note: Requires curveprofile_update call after. */
+void BKE_curveprofile_reverse(CurveProfile *profile)
+{
+ /* When there are only two points, reversing shouldn't do anything. */
+ if (profile->path_len == 2) {
+ return;
+ }
+ CurveProfilePoint *new_pts = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
+ "profile path");
+ /* Mirror the new points across the y = x line */
+ for (int i = 0; i < profile->path_len; i++) {
+ new_pts[profile->path_len - i - 1].x = profile->path[i].y;
+ new_pts[profile->path_len - i - 1].y = profile->path[i].x;
+ new_pts[profile->path_len - i - 1].flag = profile->path[i].flag;
+ new_pts[profile->path_len - i - 1].h1 = profile->path[i].h1;
+ new_pts[profile->path_len - i - 1].h2 = profile->path[i].h2;
+ }
+
+ /* Free the old points and use the new ones */
+ MEM_freeN(profile->path);
+ profile->path = new_pts;
+}
+
+/** Builds a quarter circle profile with space on each side for 'support loops.' */
+static void CurveProfile_build_supports(CurveProfile *profile)
+{
+ int n = profile->path_len;
+
+ profile->path[0].x = 1.0;
+ profile->path[0].y = 0.0;
+ profile->path[0].flag = 0;
+ profile->path[0].h1 = HD_VECT;
+ profile->path[0].h2 = HD_VECT;
+ profile->path[1].x = 1.0;
+ profile->path[1].y = 0.5;
+ profile->path[1].flag = 0;
+ profile->path[1].h1 = HD_VECT;
+ profile->path[1].h2 = HD_VECT;
+ for (int i = 1; i < n - 2; i++) {
+ profile->path[i + 1].x = 1.0f - (0.5f * (1.0f - cosf((float)((i / (float)(n - 3))) * M_PI_2)));
+ profile->path[i + 1].y = 0.5f + 0.5f * sinf((float)((i / (float)(n - 3)) * M_PI_2));
+ profile->path[i + 1].flag = 0;
+ profile->path[i + 1].h1 = HD_AUTO;
+ profile->path[i + 1].h2 = HD_AUTO;
+ }
+ profile->path[n - 2].x = 0.5;
+ profile->path[n - 2].y = 1.0;
+ profile->path[n - 2].flag = 0;
+ profile->path[n - 2].h1 = HD_VECT;
+ profile->path[n - 2].h2 = HD_VECT;
+ profile->path[n - 1].x = 0.0;
+ profile->path[n - 1].y = 1.0;
+ profile->path[n - 1].flag = 0;
+ profile->path[n - 1].h1 = HD_VECT;
+ profile->path[n - 1].h2 = HD_VECT;
+}
+
+/** Puts the widget's control points in a step pattern. Uses vector handles for each point. */
+static void CurveProfile_build_steps(CurveProfile *profile)
+{
+ int n, step_x, step_y;
+ float n_steps_x, n_steps_y;
+
+ n = profile->path_len;
+
+ /* Special case for two points to avoid dividing by zero later. */
+ if (n == 2) {
+ profile->path[0].x = 1.0f;
+ profile->path[0].y = 0.0f;
+ profile->path[0].flag = 0;
+ profile->path[0].h1 = HD_VECT;
+ profile->path[0].h2 = HD_VECT;
+ profile->path[1].x = 0.0f;
+ profile->path[1].y = 1.0f;
+ profile->path[1].flag = 0;
+ profile->path[1].h1 = HD_VECT;
+ profile->path[1].h2 = HD_VECT;
+ return;
+ }
+
+ n_steps_x = (n % 2 == 0) ? n : (n - 1);
+ n_steps_y = (n % 2 == 0) ? (n - 2) : (n - 1);
+
+ for (int i = 0; i < n; i++) {
+ step_x = (i + 1) / 2;
+ step_y = i / 2;
+ profile->path[i].x = 1.0f - ((float)(2 * step_x) / n_steps_x);
+ profile->path[i].y = (float)(2 * step_y) / n_steps_y;
+ profile->path[i].flag = 0;
+ profile->path[i].h1 = HD_VECT;
+ profile->path[i].h2 = HD_VECT;
+ }
+}
+
+/** Shorthand helper function for setting location and interpolation of a point. */
+static void point_init(CurveProfilePoint *point, float x, float y, short flag, char h1, char h2)
+{
+ point->x = x;
+ point->y = y;
+ point->flag = flag;
+ point->h1 = h1;
+ point->h2 = h2;
+}
+
+/** Resets the profile to the current preset.
+ * \note: Requires curveprofile_update call after. */
+void BKE_curveprofile_reset(CurveProfile *profile)
+{
+ if (profile->path) {
+ MEM_freeN(profile->path);
+ }
+
+ int preset = profile->preset;
+ switch (preset) {
+ case PROF_PRESET_LINE:
+ profile->path_len = 2;
+ break;
+ case PROF_PRESET_SUPPORTS:
+ /* Use a dynamic number of control points for the widget's profile. */
+ if (profile->segments_len < 4) {
+ /* But always use enough points to at least build the support points. */
+ profile->path_len = 5;
+ }
+ else {
+ profile->path_len = profile->segments_len + 1;
+ }
+ break;
+ case PROF_PRESET_CORNICE:
+ profile->path_len = 13;
+ break;
+ case PROF_PRESET_CROWN:
+ profile->path_len = 11;
+ break;
+ case PROF_PRESET_STEPS:
+ /* Also use dynamic number of control points based on the set number of segments. */
+ if (profile->segments_len == 0) {
+ /* totsegments hasn't been set-- use the number of control points for 8 steps. */
+ profile->path_len = 17;
+ }
+ else {
+ profile->path_len = profile->segments_len + 1;
+ }
+ break;
+ }
+
+ profile->path = MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, "profile path");
+
+ switch (preset) {
+ case PROF_PRESET_LINE:
+ point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[1], 0.0f, 1.0f, 0, HD_AUTO, HD_AUTO);
+ break;
+ case PROF_PRESET_SUPPORTS:
+ CurveProfile_build_supports(profile);
+ break;
+ case PROF_PRESET_CORNICE:
+ point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[1], 1.0f, 0.125f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[2], 0.92f, 0.16f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[3], 0.875f, 0.25f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[4], 0.8f, 0.25f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[5], 0.733f, 0.433f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[6], 0.582f, 0.522f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[7], 0.4f, 0.6f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[8], 0.289f, 0.727f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[9], 0.25f, 0.925f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[10], 0.175f, 0.925f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[11], 0.175f, 1.0f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[12], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
+ break;
+ case PROF_PRESET_CROWN:
+ point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[1], 1.0f, 0.25f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[2], 0.75f, 0.25f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[3], 0.75f, 0.325f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[4], 0.925f, 0.4f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[5], 0.975f, 0.5f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[6], 0.94f, 0.65f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[7], 0.85f, 0.75f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[8], 0.75f, 0.875f, 0, HD_AUTO, HD_AUTO);
+ point_init(&profile->path[9], 0.7f, 1.0f, 0, HD_VECT, HD_VECT);
+ point_init(&profile->path[10], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
+ break;
+ case PROF_PRESET_STEPS:
+ CurveProfile_build_steps(profile);
+ break;
+ }
+
+ if (profile->table) {
+ MEM_freeN(profile->table);
+ profile->table = NULL;
+ }
+}
+
+/** Helper for 'curve_profile_create' samples. Returns whether both handles that make up the edge
+ * are vector handles. */
+static bool is_curved_edge(BezTriple *bezt, int i)
+{
+ return (bezt[i].h2 != HD_VECT || bezt[i + 1].h1 != HD_VECT);
+}
+
+/** Used to set bezier handle locations in the sample creation process. Reduced copy of
+ * #calchandleNurb_intern code in curve.c. */
+static void calchandle_profile(BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
+{
+#define point_handle1 ((point_loc)-3)
+#define point_handle2 ((point_loc) + 3)
+
+ const float *prev_loc, *next_loc;
+ float *point_loc;
+ float pt[3];
+ float len, len_a, len_b;
+ float dvec_a[2], dvec_b[2];
+
+ if (bezt->h1 == 0 && bezt->h2 == 0) {
+ return;
+ }
+
+ point_loc = bezt->vec[1];
+
+ if (prev == NULL) {
+ next_loc = next->vec[1];
+ pt[0] = 2.0f * point_loc[0] - next_loc[0];
+ pt[1] = 2.0f * point_loc[1] - next_loc[1];
+ prev_loc = pt;
+ }
+ else {
+ prev_loc = prev->vec[1];
+ }
+
+ if (next == NULL) {
+ prev_loc = prev->vec[1];
+ pt[0] = 2.0f * point_loc[0] - prev_loc[0];
+ pt[1] = 2.0f * point_loc[1] - prev_loc[1];
+ next_loc = pt;
+ }
+ else {
+ next_loc = next->vec[1];
+ }
+
+ sub_v2_v2v2(dvec_a, point_loc, prev_loc);
+ sub_v2_v2v2(dvec_b, next_loc, point_loc);
+
+ len_a = len_v2(dvec_a);
+ len_b = len_v2(dvec_b);
+
+ if (len_a == 0.0f) {
+ len_a = 1.0f;
+ }
+ if (len_b == 0.0f) {
+ len_b = 1.0f;
+ }
+
+ if (bezt->h1 == HD_AUTO || bezt->h2 == HD_AUTO) { /* auto */
+ float tvec[2];
+ tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
+ tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
+
+ len = len_v2(tvec) * 2.5614f;
+ if (len != 0.0f) {
+
+ if (bezt->h1 == HD_AUTO) {
+ len_a /= len;
+ madd_v2_v2v2fl(point_handle1, point_loc, tvec, -len_a);
+ }
+ if (bezt->h2 == HD_AUTO) {
+ len_b /= len;
+ madd_v2_v2v2fl(point_handle2, point_loc, tvec, len_b);
+ }
+ }
+ }
+
+ if (bezt->h1 == HD_VECT) { /* vector */
+ madd_v2_v2v2fl(point_handle1, point_loc, dvec_a, -1.0f / 3.0f);
+ }
+ if (bezt->h2 == HD_VECT) {
+ madd_v2_v2v2fl(point_handle2, point_loc, dvec_b, 1.0f / 3.0f);
+ }
+#undef point_handle1
+#undef point_handle2
+}
+
+/** Helper function for 'BKE_CurveProfile_create_samples.' Calculates the angle between the
+ * handles on the inside of the edge starting at index i. A larger angle means the edge is
+ * more curved.
+ * \param i_edge: The start index of the edge to calculate the angle for. */
+static float bezt_edge_handle_angle(const BezTriple *bezt, int i_edge)
+{
+ /* Find the direction of the handles that define this edge along the direction of the path. */
+ float start_handle_direction[2], end_handle_direction[2];
+ /* Handle 2 - point location. */
+ sub_v2_v2v2(start_handle_direction, bezt[i_edge].vec[2], bezt[i_edge].vec[1]);
+ /* Point location - handle 1. */
+ sub_v2_v2v2(end_handle_direction, bezt[i_edge + 1].vec[1], bezt[i_edge + 1].vec[0]);
+
+ float angle = angle_v2v2(start_handle_direction, end_handle_direction);
+ return angle;
+}
+
+/** Struct to sort curvature of control point edges. */
+typedef struct {
+ /** The index of the corresponding bezier point. */
+ int bezt_index;
+ /** The curvature of the edge with the above index. */
+ float bezt_curvature;
+} CurvatureSortPoint;
+
+/** Helper function for 'BKE_curveprofile_create_samples' for sorting edges based on curvature. */
+static int sort_points_curvature(const void *in_a, const void *in_b)
+{
+ const CurvatureSortPoint *a = (const CurvatureSortPoint *)in_a;
+ const CurvatureSortPoint *b = (const CurvatureSortPoint *)in_b;
+
+ if (a->bezt_curvature > b->bezt_curvature) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+}
+
+/** Used for sampling curves along the profile's path. Any points more than the number of user-
+ * defined points will be evenly distributed among the curved edges. Then the remainders will be
+ * distributed to the most curved edges.
+ * \param n_segments: The number of segments to sample along the path. It must be higher than the
+ * number of points used to define the profile (profile->path_len).
+ * \param sample_straight_edges: Whether to sample points between vector handle control points. If
+ * this is true and there are only vector edges the straight edges will still be sampled.
+ * \param r_samples: An array of points to put the sampled positions. Must have length n_segments.
+ * \return r_samples: Fill the array with the sampled locations and if the point corresponds
+ * to a control point, its handle type */
+void BKE_curveprofile_create_samples(CurveProfile *profile,
+ int n_segments,
+ bool sample_straight_edges,
+ CurveProfilePoint *r_samples)
+{
+ BezTriple *bezt;
+ int i, n_left, n_common, i_sample, n_curved_edges;
+ int *n_samples;
+ CurvatureSortPoint *curve_sorted;
+ int totpoints = profile->path_len;
+ int totedges = totpoints - 1;
+
+ BLI_assert(n_segments > 0);
+
+ /* Create Bezier points for calculating the higher resolution path. */
+ bezt = MEM_callocN(sizeof(BezTriple) * totpoints, "beztarr");
+ for (i = 0; i < totpoints; i++) {
+ bezt[i].vec[1][0] = profile->path[i].x;
+ bezt[i].vec[1][1] = profile->path[i].y;
+ bezt[i].h1 = (profile->path[i].h1 == HD_VECT) ? HD_VECT : HD_AUTO;
+ bezt[i].h2 = (profile->path[i].h2 == HD_VECT) ? HD_VECT : HD_AUTO;
+ }
+ /* Give the first and last bezier points the same handle type as their neighbors. */
+ if (totpoints > 2) {
+ bezt[0].h1 = bezt[0].h2 = bezt[1].h1;
+ bezt[totpoints - 1].h1 = bezt[totpoints - 1].h2 = bezt[totpoints - 2].h2;
+ }
+ /* Get handle positions for the bezier points. */
+ calchandle_profile(&bezt[0], NULL, &bezt[1]);
+ for (i = 1; i < totpoints - 1; i++) {
+ calchandle_profile(&bezt[i], &bezt[i - 1], &bezt[i + 1]);
+ }
+ calchandle_profile(&bezt[totpoints - 1], &bezt[totpoints - 2], NULL);
+
+ /* Create a list of edge indices with the most curved at the start, least curved at the end. */
+ curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges, "curve sorted");
+ for (i = 0; i < totedges; i++) {
+ curve_sorted[i].bezt_index = i;
+ /* Calculate the curvature of each edge once for use when sorting for curvature. */
+ curve_sorted[i].bezt_curvature = bezt_edge_handle_angle(bezt, i);
+ }
+ qsort(curve_sorted, (size_t)totedges, sizeof(CurvatureSortPoint), sort_points_curvature);
+
+ /* Assign the number of sampled points for each edge. */
+ n_samples = MEM_callocN(sizeof(int) * totedges, "create samples numbers");
+ int n_added = 0;
+ if (n_segments >= totedges) {
+ if (sample_straight_edges) {
+ /* Assign an even number to each edge if it’s possible, then add the remainder of sampled
+ * points starting with the most curved edges. */
+ n_common = n_segments / totedges;
+ n_left = n_segments % totedges;
+
+ /* Assign the points that fill fit evenly to the edges. */
+ if (n_common > 0) {
+ for (i = 0; i < totedges; i++) {
+ n_samples[i] = n_common;
+ n_added += n_common;
+ }
+ }
+ }
+ else {
+ /* Count the number of curved edges */
+ n_curved_edges = 0;
+ for (i = 0; i < totedges; i++) {
+ if (is_curved_edge(bezt, i)) {
+ n_curved_edges++;
+ }
+ }
+ /* Just sample all of the edges if there are no curved edges. */
+ n_curved_edges = (n_curved_edges == 0) ? totedges : n_curved_edges;
+
+ /* Give all of the curved edges the same number of points and straight edges one point. */
+ n_left = n_segments - (totedges - n_curved_edges); /* Left after 1 for each straight edge. */
+ n_common = n_left / n_curved_edges; /* Number assigned to all curved edges */
+ if (n_common > 0) {
+ for (i = 0; i < totedges; i++) {
+ /* Add the common number if it's a curved edge or if edges are curved. */
+ if (is_curved_edge(bezt, i) || n_curved_edges == totedges) {
+ n_samples[i] += n_common;
+ n_added += n_common;
+ }
+ else {
+ n_samples[i] = 1;
+ n_added++;
+ }
+ }
+ }
+ n_left -= n_common * n_curved_edges;
+ }
+ }
+ else {
+ /* Not enough segments to give one to each edge, so just give them to the most curved edges. */
+ n_left = n_segments;
+ }
+ /* Assign the remainder of the points that couldn't be spread out evenly. */
+ BLI_assert(n_left < totedges);
+ for (i = 0; i < n_left; i++) {
+ n_samples[curve_sorted[i].bezt_index]++;
+ n_added++;
+ }
+
+ BLI_assert(n_added == n_segments); /* n_added is just used for this assert, could remove it. */
+
+ /* Sample the points and add them to the locations table. */
+ for (i_sample = 0, i = 0; i < totedges; i++) {
+ if (n_samples[i] > 0) {
+ /* Carry over the handle types from the control point to its first corresponding sample. */
+ r_samples[i_sample].h1 = profile->path[i].h1;
+ r_samples[i_sample].h2 = profile->path[i].h2;
+ /* All extra sample points for this control point get "auto" handles. */
+ for (int j = i_sample + 1; j < i_sample + n_samples[i]; j++) {
+ r_samples[j].flag = 0;
+ r_samples[j].h1 = HD_AUTO;
+ r_samples[j].h2 = HD_AUTO;
+ BLI_assert(j < n_segments);
+ }
+
+ /* Do the sampling from bezier points, X values first, then Y values. */
+ BKE_curve_forward_diff_bezier(bezt[i].vec[1][0],
+ bezt[i].vec[2][0],
+ bezt[i + 1].vec[0][0],
+ bezt[i + 1].vec[1][0],
+ &r_samples[i_sample].x,
+ n_samples[i],
+ sizeof(CurveProfilePoint));
+ BKE_curve_forward_diff_bezier(bezt[i].vec[1][1],
+ bezt[i].vec[2][1],
+ bezt[i + 1].vec[0][1],
+ bezt[i + 1].vec[1][1],
+ &r_samples[i_sample].y,
+ n_samples[i],
+ sizeof(CurveProfilePoint));
+ }
+ i_sample += n_samples[i]; /* Add the next set of points after the ones we just added. */
+ BLI_assert(i_sample <= n_segments);
+ }
+
+#ifdef DEBUG_profile_TABLE
+ printf("CURVEPROFILE CREATE SAMPLES\n");
+ printf("n_segments: %d\n", n_segments);
+ printf("totedges: %d\n", totedges);
+ printf("n_common: %d\n", n_common);
+ printf("n_left: %d\n", n_left);
+ printf("n_samples: ");
+ for (i = 0; i < totedges; i++) {
+ printf("%d, ", n_samples[i]);
+ }
+ printf("\n");
+ printf("i_curved_sorted: ");
+ for (i = 0; i < totedges; i++) {
+ printf("(%d %.2f), ", curve_sorted[i].bezt_index, curve_sorted[i].bezt_curvature);
+ }
+ printf("\n");
+#endif
+ MEM_freeN(bezt);
+ MEM_freeN(curve_sorted);
+ MEM_freeN(n_samples);
+}
+
+/** Creates a higher resolution table by sampling the curved points. This table is used for display
+ * and evenly spaced evaluation. */
+static void curveprofile_make_table(CurveProfile *profile)
+{
+ int n_samples = PROF_N_TABLE(profile->path_len);
+ CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
+ "high-res table");
+
+ BKE_curveprofile_create_samples(profile, n_samples - 1, false, new_table);
+ /* Manually add last point at the end of the profile */
+ new_table[n_samples - 1].x = 0.0f;
+ new_table[n_samples - 1].y = 1.0f;
+
+ if (profile->table) {
+ MEM_freeN(profile->table);
+ }
+ profile->table = new_table;
+}
+
+/** Creates the table of points used for displaying a preview of the sampled segment locations on
+ * the widget itself. */
+static void CurveProfile_make_segments_table(CurveProfile *profile)
+{
+ int n_samples = profile->segments_len;
+ if (n_samples <= 0) {
+ return;
+ }
+ CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
+ "samples table");
+
+ if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) {
+ /* Even length sampling incompatible with only straight edge sampling for now. */
+ BKE_curveprofile_create_samples_even_spacing(profile, n_samples, new_table);
+ }
+ else {
+ BKE_curveprofile_create_samples(
+ profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table);
+ }
+
+ if (profile->segments) {
+ MEM_freeN(profile->segments);
+ }
+ profile->segments = new_table;
+}
+
+/** Sets the default settings and clip range for the profile widget. Does not generate either
+ * table. */
+void BKE_curveprofile_set_defaults(CurveProfile *profile)
+{
+ profile->flag = PROF_USE_CLIP;
+
+ BLI_rctf_init(&profile->view_rect, 0.0f, 1.0f, 0.0f, 1.0f);
+ profile->clip_rect = profile->view_rect;
+
+ profile->path_len = 2;
+ profile->path = MEM_callocN(2 * sizeof(CurveProfilePoint), "path points");
+
+ profile->path[0].x = 1.0f;
+ profile->path[0].y = 0.0f;
+ profile->path[1].x = 1.0f;
+ profile->path[1].y = 1.0f;
+
+ profile->changed_timestamp = 0;
+}
+
+/** Returns a pointer to a newly allocated curve profile, using the given preset.
+ \param preset: Value in eCurveProfilePresets. */
+struct CurveProfile *BKE_curveprofile_add(int preset)
+{
+ CurveProfile *profile = MEM_callocN(sizeof(CurveProfile), "curve profile");
+
+ BKE_curveprofile_set_defaults(profile);
+ profile->preset = preset;
+ BKE_curveprofile_reset(profile);
+ curveprofile_make_table(profile);
+
+ return profile;
+}
+
+/** Should be called after the widget is changed. Does profile and remove double checks and more
+ * importantly, recreates the display / evaluation and segments tables. */
+void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double)
+{
+ CurveProfilePoint *points = profile->path;
+ rctf *clipr = &profile->clip_rect;
+ float thresh;
+ float dx, dy;
+ int i;
+
+ profile->changed_timestamp++;
+
+ /* Clamp with the clipping rect in case something got past. */
+ if (profile->flag & PROF_USE_CLIP) {
+ /* Move points inside the clip rectangle. */
+ for (i = 0; i < profile->path_len; i++) {
+ points[i].x = max_ff(points[i].x, clipr->xmin);
+ points[i].x = min_ff(points[i].x, clipr->xmax);
+ points[i].y = max_ff(points[i].y, clipr->ymin);
+ points[i].y = min_ff(points[i].y, clipr->ymax);
+ }
+ /* Ensure zoom-level respects clipping. */
+ if (BLI_rctf_size_x(&profile->view_rect) > BLI_rctf_size_x(&profile->clip_rect)) {
+ profile->view_rect.xmin = profile->clip_rect.xmin;
+ profile->view_rect.xmax = profile->clip_rect.xmax;
+ }
+ if (BLI_rctf_size_y(&profile->view_rect) > BLI_rctf_size_y(&profile->clip_rect)) {
+ profile->view_rect.ymin = profile->clip_rect.ymin;
+ profile->view_rect.ymax = profile->clip_rect.ymax;
+ }
+ }
+
+ /* Remove doubles with a threshold set at 1% of default range. */
+ thresh = 0.01f * BLI_rctf_size_x(clipr);
+ if (remove_double && profile->path_len > 2) {
+ for (i = 0; i < profile->path_len - 1; i++) {
+ dx = points[i].x - points[i + 1].x;
+ dy = points[i].y - points[i + 1].y;
+ if (sqrtf(dx * dx + dy * dy) < thresh) {
+ if (i == 0) {
+ points[i + 1].flag |= HD_VECT;
+ if (points[i + 1].flag & PROF_SELECT) {
+ points[i].flag |= PROF_SELECT;
+ }
+ }
+ else {
+ points[i].flag |= HD_VECT;
+ if (points[i].flag & PROF_SELECT) {
+ points[i + 1].flag |= PROF_SELECT;
+ }
+ }
+ break; /* Assumes 1 deletion per edit is ok. */
+ }
+ }
+ if (i != profile->path_len - 1) {
+ BKE_curveprofile_remove_by_flag(profile, 2);
+ }
+ }
+
+ /* Create the high resolution table for drawing and some evaluation functions. */
+ curveprofile_make_table(profile);
+
+ /* Store a table of samples for the segment locations for a preview and the table's user. */
+ if (profile->segments_len > 0) {
+ CurveProfile_make_segments_table(profile);
+ }
+}
+
+/** Refreshes the higher resolution table sampled from the input points. A call to this or
+ * curveprofile_update is needed before evaluation functions that use the table. Also sets the
+ * number of segments used for the display preview of the locations of the sampled points. */
+void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len)
+{
+ profile->segments_len = segments_len;
+
+ /* Calculate the higher resolution / segments tables for display and evaluation. */
+ BKE_curveprofile_update(profile, false);
+}
+
+/** Gives the distance to the next point in the widget's sampled table, in other words the length
+ * of the ith edge of the table.
+ * \note Requires curveprofile_initialize or curveprofile_update call before to fill table. */
+static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
+{
+ BLI_assert(i < PROF_N_TABLE(profile->path_len));
+
+ return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
+}
+
+/** Calculates the total length of the profile from the curves sampled in the table.
+ * \note Requires curveprofile_initialize or curveprofile_update call before to fill table. */
+float BKE_curveprofile_total_length(const CurveProfile *profile)
+{
+ float total_length = 0;
+ for (int i = 0; i < PROF_N_TABLE(profile->path_len) - 1; i++) {
+ total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
+ }
+ return total_length;
+}
+
+/** Samples evenly spaced positions along the curve profile's table (generated from path). Fills
+ * an entire table at once for a speedup if all of the results are going to be used anyway.
+ * \note Requires curveprofile_initialize or curveprofile_update call before to fill table.
+ * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for
+ * now. */
+void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
+ int n_segments,
+ CurveProfilePoint *r_samples)
+{
+ const float total_length = BKE_curveprofile_total_length(profile);
+ const float segment_length = total_length / n_segments;
+ float length_travelled = 0.0f;
+ float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
+ float distance_to_previous_table_point = 0.0f;
+ float segment_left, factor;
+ int i_table = 0;
+
+ /* Set the location for the first point. */
+ r_samples[0].x = profile->table[0].x;
+ r_samples[0].y = profile->table[0].y;
+
+ /* Travel along the path, recording the locations of segments as we pass them. */
+ segment_left = segment_length;
+ for (int i = 1; i < n_segments; i++) {
+ /* Travel over all of the points that fit inside this segment. */
+ while (distance_to_next_table_point < segment_left) {
+ length_travelled += distance_to_next_table_point;
+ segment_left -= distance_to_next_table_point;
+ i_table++;
+ distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
+ distance_to_previous_table_point = 0.0f;
+ }
+ /* We're at the last table point that fits inside the current segment, use interpolation. */
+ factor = (distance_to_previous_table_point + segment_left) /
+ (distance_to_previous_table_point + distance_to_next_table_point);
+ r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor);
+ r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor);
+#ifdef DEBUG_CURVEPROFILE_EVALUATE
+ BLI_assert(factor <= 1.0f && factor >= 0.0f);
+ printf("segment_left: %.3f\n", segment_left);
+ printf("i_table: %d\n", i_table);
+ printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point);
+ printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point);
+ printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n",
+ factor,
+ profile->table[i_table].x,
+ profile->table[i_table].y,
+ profile->table[i_table + 1].x,
+ profile->table[i_table + 1].y);
+#endif
+
+ /* We sampled in between this table point and the next, so the next travel step is smaller. */
+ distance_to_next_table_point -= segment_left;
+ distance_to_previous_table_point += segment_left;
+ length_travelled += segment_left;
+ segment_left = segment_length;
+ }
+}
+
+/** Does a single evaluation along the profile's path. Travels down (length_portion * path) length
+ * and returns the position at that point.
+ * \param length_portion: The portion (0 to 1) of the path's full length to sample at.
+ * \note Requires curveprofile_initialize or curveprofile_update call before to fill table */
+void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
+ float length_portion,
+ float *x_out,
+ float *y_out)
+{
+ const float total_length = BKE_curveprofile_total_length(profile);
+ const float requested_length = length_portion * total_length;
+
+ /* Find the last point along the path with a lower length portion than the input. */
+ int i = 0;
+ float length_travelled = 0.0f;
+ while (length_travelled < requested_length) {
+ /* Check if we reached the last point before the final one. */
+ if (i == PROF_N_TABLE(profile->path_len) - 2) {
+ break;
+ }
+ float new_length = curveprofile_distance_to_next_table_point(profile, i);
+ if (length_travelled + new_length >= requested_length) {
+ break;
+ }
+ length_travelled += new_length;
+ i++;
+ }
+
+ /* Now travel the remaining distance of length portion down the path to the next point and
+ * find the location where we stop. */
+ float distance_to_next_point = curveprofile_distance_to_next_table_point(profile, i);
+ float lerp_factor = (requested_length - length_travelled) / distance_to_next_point;
+
+#ifdef DEBUG_CURVEPROFILE_EVALUATE
+ printf("CURVEPROFILE EVALUATE\n");
+ printf(" length portion input: %f\n", (double)length_portion);
+ printf(" requested path length: %f\n", (double)requested_length);
+ printf(" distance to next point: %f\n", (double)distance_to_next_point);
+ printf(" length travelled: %f\n", (double)length_travelled);
+ printf(" lerp-factor: %f\n", (double)lerp_factor);
+ printf(" ith point (%f, %f)\n", (double)profile->path[i].x, (double)profile->path[i].y);
+ printf(" next point(%f, %f)\n", (double)profile->path[i + 1].x, (double)profile->path[i + 1].y);
+#endif
+
+ *x_out = interpf(profile->table[i].x, profile->table[i + 1].x, lerp_factor);
+ *y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor);
+}
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 53e5f1fdfe5..2753cbd9be2 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -29,6 +29,7 @@
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
+#include "DNA_curveprofile_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
@@ -80,6 +81,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_curveprofile.h"
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -182,6 +184,8 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
/* duplicate Grease Pencil multiframe fallof */
ts->gp_sculpt.cur_falloff = BKE_curvemapping_copy(ts->gp_sculpt.cur_falloff);
ts->gp_sculpt.cur_primitive = BKE_curvemapping_copy(ts->gp_sculpt.cur_primitive);
+
+ ts->custom_bevel_profile_preset = BKE_curveprofile_copy(ts->custom_bevel_profile_preset);
return ts;
}
@@ -224,6 +228,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_curvemapping_free(toolsettings->gp_sculpt.cur_primitive);
}
+ if (toolsettings->custom_bevel_profile_preset) {
+ BKE_curveprofile_free(toolsettings->custom_bevel_profile_preset);
+ }
+
MEM_freeN(toolsettings);
}
@@ -729,6 +737,9 @@ void BKE_scene_init(Scene *sce)
copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
}
+ /* Curve Profile */
+ sce->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
+
for (int i = 0; i < ARRAY_SIZE(sce->orientation_slots); i++) {
sce->orientation_slots[i].index_custom = -1;
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 167917f7b6a..f6c1cd0380a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -74,6 +74,7 @@
#include "DNA_object_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_particle_types.h"
+#include "DNA_curveprofile_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_text_types.h"
@@ -132,6 +133,7 @@
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
+#include "BKE_curveprofile.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -2698,6 +2700,19 @@ static void direct_link_curvemapping(FileData *fd, CurveMapping *cumap)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Read CurveProfile
+ * \{ */
+
+static void direct_link_curveprofile(FileData *fd, CurveProfile *profile)
+{
+ profile->path = newdataadr(fd, profile->path);
+ profile->table = NULL;
+ profile->segments = NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Read ID: Brush
* \{ */
@@ -5802,6 +5817,13 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
}
}
}
+ else if (md->type == eModifierType_Bevel) {
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ bmd->custom_profile = newdataadr(fd, bmd->custom_profile);
+ if (bmd->custom_profile) {
+ direct_link_curveprofile(fd, bmd->custom_profile);
+ }
+ }
}
}
@@ -6758,6 +6780,13 @@ static void direct_link_scene(FileData *fd, Scene *sce)
if (sce->toolsettings->gp_sculpt.cur_primitive) {
direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_primitive);
}
+
+ /* Relink toolsettings curve profile */
+ sce->toolsettings->custom_bevel_profile_preset = newdataadr(
+ fd, sce->toolsettings->custom_bevel_profile_preset);
+ if (sce->toolsettings->custom_bevel_profile_preset) {
+ direct_link_curveprofile(fd, sce->toolsettings->custom_bevel_profile_preset);
+ }
}
if (sce->ed) {
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 50363e3f42a..761424a5879 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -36,6 +36,7 @@
#include "DNA_cloth_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_curveprofile_types.h"
#include "DNA_gpu_types.h"
#include "DNA_light_types.h"
#include "DNA_layer_types.h"
@@ -74,6 +75,7 @@
#include "BKE_node.h"
#include "BKE_paint.h"
#include "BKE_pointcache.h"
+#include "BKE_curveprofile.h"
#include "BKE_report.h"
#include "BKE_rigidbody.h"
#include "BKE_screen.h"
@@ -3932,10 +3934,35 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Versioning code until next subversion bump goes here. */
+
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
sa->flag &= ~AREA_FLAG_UNUSED_6;
}
}
+
+ /* Add custom curve profile to toolsettings for bevel tool */
+ if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "CurveProfile", "custom_profile")) {
+ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ ToolSettings *ts = scene->toolsettings;
+ if ((ts) && (ts->custom_bevel_profile_preset == NULL)) {
+ ts->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
+ }
+ }
+ }
+
+ /* Add custom curve profile to bevel modifier */
+ if (!DNA_struct_elem_find(fd->filesdna, "BevelModifier", "CurveProfile", "custom_profile")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ for (ModifierData *md = object->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Bevel) {
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ if (!bmd->custom_profile) {
+ bmd->custom_profile = BKE_curveprofile_add(PROF_PRESET_LINE);
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 45fb62a4ac0..05758b446ad 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -27,6 +27,7 @@
#include "BLI_system.h"
#include "DNA_camera_types.h"
+#include "DNA_curveprofile_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
@@ -51,6 +52,7 @@
#include "BKE_paint.h"
#include "BKE_screen.h"
#include "BKE_workspace.h"
+#include "BKE_curveprofile.h"
#include "BLO_readfile.h"
@@ -324,6 +326,11 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
copy_v2_v2(me->mloopuv[i].uv, uv_values[i]);
}
}
+
+ /* Make sure that the curve profile is initialized */
+ if (ts->custom_bevel_profile_preset == NULL) {
+ ts->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
+ }
}
/**
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 2966a030f63..3390d30ad5d 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -139,6 +139,7 @@
#include "DNA_workspace_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
+#include "DNA_curveprofile_types.h"
#include "MEM_guardedalloc.h" // MEM_freeN
#include "BLI_bitmap.h"
@@ -953,6 +954,12 @@ static void write_curvemapping(WriteData *wd, CurveMapping *cumap)
write_curvemapping_curves(wd, cumap);
}
+static void write_CurveProfile(WriteData *wd, CurveProfile *profile)
+{
+ writestruct(wd, DATA, CurveProfile, 1, profile);
+ writestruct(wd, DATA, CurveProfilePoint, profile->path_len, profile->path);
+}
+
static void write_node_socket(WriteData *wd, bNodeSocket *sock)
{
/* actual socket writing */
@@ -1759,6 +1766,12 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
}
}
}
+ else if (md->type == eModifierType_Bevel) {
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ if (bmd->custom_profile) {
+ write_CurveProfile(wd, bmd->custom_profile);
+ }
+ }
}
}
@@ -2533,6 +2546,10 @@ static void write_scene(WriteData *wd, Scene *sce)
if (tos->gp_sculpt.cur_primitive) {
write_curvemapping(wd, tos->gp_sculpt.cur_primitive);
}
+ /* Write the curve profile to the file. */
+ if (tos->custom_bevel_profile_preset) {
+ write_CurveProfile(wd, tos->custom_bevel_profile_preset);
+ }
write_paint(wd, &tos->imapaint.paint);
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 7086cea1ace..74d01dca66a 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1725,6 +1725,12 @@ static BMO_FlagSet bmo_enum_bevel_miter_type[] = {
{0, NULL},
};
+static BMO_FlagSet bmo_enum_bevel_vmesh_method[] = {
+ {BEVEL_VMESH_ADJ, "ADJ"},
+ {BEVEL_VMESH_CUTOFF, "CUTOFF"},
+ {0, NULL},
+};
+
/*
* Bevel.
*
@@ -1735,7 +1741,8 @@ static BMOpDefine bmo_bevel_def = {
/* slots_in */
{{"geom", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* input edges and vertices */
{"offset", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
- {"offset_type", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM}, bmo_enum_bevel_offset_type}, /* how to measure the offset */
+ {"offset_type", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+ bmo_enum_bevel_offset_type}, /* how to measure the offset */
{"segments", BMO_OP_SLOT_INT}, /* number of segments in bevel */
{"profile", BMO_OP_SLOT_FLT}, /* profile shape, 0->1 (.5=>round) */
{"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */
@@ -1746,13 +1753,18 @@ static BMOpDefine bmo_bevel_def = {
{"mark_sharp", BMO_OP_SLOT_BOOL}, /* extend edge data to allow sharp edges to run across bevels */
{"harden_normals", BMO_OP_SLOT_BOOL}, /* harden normals */
{"face_strength_mode", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
- bmo_enum_bevel_face_strength_type}, /* whether to set face strength, and which faces to set if so */
+ bmo_enum_bevel_face_strength_type}, /* whether to set face strength, and which faces to set if so */
{"miter_outer", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
- bmo_enum_bevel_miter_type}, /* outer miter kind */
+ bmo_enum_bevel_miter_type}, /* outer miter kind */
{"miter_inner", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
- bmo_enum_bevel_miter_type}, /* outer miter kind */
+ bmo_enum_bevel_miter_type}, /* outer miter kind */
{"spread", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
{"smoothresh", BMO_OP_SLOT_FLT}, /* for passing mesh's smoothresh, used in hardening */
+ {"use_custom_profile", BMO_OP_SLOT_BOOL}, /* Whether to use custom profile feature */
+ /* the ProfileWiget struct for the custom profile shape */
+ {"custom_profile", BMO_OP_SLOT_PTR, {(int)BMO_OP_SLOT_SUBTYPE_PTR_STRUCT}},
+ {"vmesh_method", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+ bmo_enum_bevel_vmesh_method},
{{'\0'}},
},
/* slots_out */
diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h
index 2039289dcd7..dbd2bf076c6 100644
--- a/source/blender/bmesh/intern/bmesh_operator_api.h
+++ b/source/blender/bmesh/intern/bmesh_operator_api.h
@@ -237,6 +237,7 @@ typedef enum eBMOpSlotSubType_Ptr {
BMO_OP_SLOT_SUBTYPE_PTR_SCENE = 101,
BMO_OP_SLOT_SUBTYPE_PTR_OBJECT = 102,
BMO_OP_SLOT_SUBTYPE_PTR_MESH = 103,
+ BMO_OP_SLOT_SUBTYPE_PTR_STRUCT = 104,
} eBMOpSlotSubType_Ptr;
typedef enum eBMOpSlotSubType_Int {
BMO_OP_SLOT_SUBTYPE_INT_ENUM = 200,
@@ -294,8 +295,8 @@ typedef struct BMOpSlot {
BLI_assert(((slot >= (op)->slots_in) && (slot < &(op)->slots_in[BMO_OP_MAX_SLOTS])) || \
((slot >= (op)->slots_out) && (slot < &(op)->slots_out[BMO_OP_MAX_SLOTS])))
-/* way more than probably needed, compiler complains if limit hit */
-#define BMO_OP_MAX_SLOTS 20
+/* Limit hit, so expanded for bevel operator. Compiler complains if limit is hit. */
+#define BMO_OP_MAX_SLOTS 21
/* BMOpDefine->type_flag */
typedef enum {
diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h
index 78e8ce04115..9f0107db693 100644
--- a/source/blender/bmesh/intern/bmesh_operators.h
+++ b/source/blender/bmesh/intern/bmesh_operators.h
@@ -126,6 +126,12 @@ enum {
BEVEL_MITER_ARC,
};
+/* Bevel vertex mesh creation methods */
+enum {
+ BEVEL_VMESH_ADJ,
+ BEVEL_VMESH_CUTOFF,
+};
+
/* Normal Face Strength values */
enum {
FACE_STRENGTH_WEAK = -16384,
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 33be9559db3..1d5bcf8b160 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -24,6 +24,8 @@
#include "bmesh.h"
#include "bmesh_tools.h"
+#include "BKE_curveprofile.h"
+#include "DNA_curveprofile_types.h"
#include "intern/bmesh_operators_private.h" /* own include */
@@ -45,6 +47,9 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
const int miter_inner = BMO_slot_int_get(op->slots_in, "miter_inner");
const float spread = BMO_slot_float_get(op->slots_in, "spread");
const float smoothresh = BMO_slot_float_get(op->slots_in, "smoothresh");
+ const bool use_custom_profile = BMO_slot_bool_get(op->slots_in, "use_custom_profile");
+ const CurveProfile *custom_profile = BMO_slot_ptr_get(op->slots_in, "custom_profile");
+ const int vmesh_method = BMO_slot_int_get(op->slots_in, "vmesh_method");
if (offset > 0) {
BMOIter siter;
@@ -87,7 +92,10 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
miter_outer,
miter_inner,
spread,
- smoothresh);
+ smoothresh,
+ use_custom_profile,
+ custom_profile,
+ vmesh_method);
BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG);
BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "edges.out", BM_EDGE, BM_ELEM_TAG);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 797e2ca864e..6989946147e 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -38,6 +38,9 @@
#include "eigen_capi.h"
+#include "BKE_curveprofile.h"
+#include "DNA_curveprofile_types.h"
+
#include "bmesh.h"
#include "bmesh_bevel.h" /* own include */
@@ -50,10 +53,22 @@
#define BEVEL_EPSILON_BIG_SQ 1e-8f
#define BEVEL_EPSILON_ANG DEG2RADF(2.0f)
#define BEVEL_SMALL_ANG DEG2RADF(10.0f)
+/** Difference in dot products that corresponds to 10 degree difference between vectors. */
+#define BEVEL_SMALL_ANG_DOT 1 - cosf(BEVEL_SMALL_ANG)
#define BEVEL_MAX_ADJUST_PCT 10.0f
#define BEVEL_MAX_AUTO_ADJUST_PCT 300.0f
#define BEVEL_MATCH_SPEC_WEIGHT 0.2
+//#define DEBUG_CUSTOM_PROFILE_CUTOFF
+//#define DEBUG_CUSTOM_PROFILE_SAMPLE
+
+#if defined(DEBUG_PROFILE_ORIENTATION_DRAW) || defined(DEBUG_CUSTOM_PROFILE_PIPE)
+static float debug_color_red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+static float debug_color_blue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+extern void DRW_debug_sphere(const float center[3], const float radius, const float color[4]);
+extern void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]);
+#endif
+
/* happens far too often, uncomment for development */
// #define BEVEL_ASSERT_PROJECT
@@ -64,72 +79,108 @@
typedef struct NewVert {
BMVert *v;
float co[3];
- // int _pad;
+ char _pad[4];
} NewVert;
struct BoundVert;
/* Data for one end of an edge involved in a bevel */
typedef struct EdgeHalf {
- struct EdgeHalf *next, *prev; /* in CCW order */
- BMEdge *e; /* original mesh edge */
- BMFace *fprev; /* face between this edge and previous, if any */
- BMFace *fnext; /* face between this edge and next, if any */
- struct BoundVert *leftv; /* left boundary vert (looking along edge to end) */
- struct BoundVert *rightv; /* right boundary vert, if beveled */
- int profile_index; /* offset into profile to attach non-beveled edge */
- int seg; /* how many segments for the bevel */
- float offset_l; /* offset for this edge, on left side */
- float offset_r; /* offset for this edge, on right side */
- float offset_l_spec; /* user specification for offset_l */
- float offset_r_spec; /* user specification for offset_r */
- bool is_bev; /* is this edge beveled? */
- bool is_rev; /* is e->v2 the vertex at this end? */
- bool is_seam; /* is e a seam for custom loopdata (e.g., UVs)? */
- // int _pad;
+ /** Other EdgeHalves connected to the same BevVert, in CCW order. */
+ struct EdgeHalf *next, *prev;
+ /** Original mesh edge */
+ BMEdge *e;
+ /** Face between this edge and previous, if any */
+ BMFace *fprev;
+ /** Face between this edge and next, if any */
+ BMFace *fnext;
+ /** Left boundary vert (looking along edge to end) */
+ struct BoundVert *leftv;
+ /** Right boundary vert, if beveled */
+ struct BoundVert *rightv;
+ /** Offset into profile to attach non-beveled edge */
+ int profile_index;
+ /** How many segments for the bevel */
+ int seg;
+ /** Offset for this edge, on left side */
+ float offset_l;
+ /** Offset for this edge, on right side */
+ float offset_r;
+ /** User specification for offset_l */
+ float offset_l_spec;
+ /** User specification for offset_r */
+ float offset_r_spec;
+ /** Is this edge beveled? */
+ bool is_bev;
+ /** Is e->v2 the vertex at this end? */
+ bool is_rev;
+ /** Is e a seam for custom loopdata (e.g., UVs)? */
+ bool is_seam;
+ /** Used during the custom profile orientation pass */
+ bool visited_rpo;
+ char _pad[4];
} EdgeHalf;
-/* Profile specification.
+/* Profile specification:
* Many interesting profiles are in family of superellipses:
* (abs(x/a))^r + abs(y/b))^r = 1
* r==2 => ellipse; r==1 => line; r < 1 => concave; r > 1 => bulging out.
* Special cases: let r==0 mean straight-inward, and r==4 mean straight outward.
- * The profile is an arc with control points coa, midco,
+ * The profile is a path defined with start, middle, and end control points
* projected onto a plane (plane_no is normal, plane_co is a point on it)
* via lines in a given direction (proj_dir).
* After the parameters are all set, the actual profile points are calculated
- * and point in prof_co. We also may need profile points for a higher resolution
- * number of segments, in order to make the vertex mesh pattern, and that goes
- * in prof_co_2.
+ * and pointed to by prof_co. We also may need profile points for a higher resolution
+ * number of segments for the subdivision while making the ADJ vertex mesh pattern,
+ * and that goes in prof_co_2.
*/
typedef struct Profile {
- float super_r; /* superellipse r parameter */
- float coa[3]; /* start control point for profile */
- float midco[3]; /* mid control point for profile */
- float cob[3]; /* end control point for profile */
- float plane_no[3]; /* normal of plane to project to */
- float plane_co[3]; /* coordinate on plane to project to */
- float proj_dir[3]; /* direction of projection line */
- float *prof_co; /* seg+1 profile coordinates (triples of floats) */
- float *prof_co_2; /* like prof_co, but for seg power of 2 >= seg */
+ /** Superellipse r parameter */
+ float super_r;
+ /** Height for profile cutoff face sides */
+ float height;
+ /** Start control point for profile */
+ float start[3];
+ /** Mid control point for profile */
+ float middle[3];
+ /** End control point for profile */
+ float end[3];
+ /** Normal of plane to project to */
+ float plane_no[3];
+ /** Coordinate on plane to project to */
+ float plane_co[3];
+ /** Direction of projection line */
+ float proj_dir[3];
+ /** seg+1 profile coordinates (triples of floats) */
+ float *prof_co;
+ /** Like prof_co, but for seg power of 2 >= seg */
+ float *prof_co_2;
} Profile;
#define PRO_SQUARE_R 1e4f
#define PRO_CIRCLE_R 2.0f
#define PRO_LINE_R 1.0f
#define PRO_SQUARE_IN_R 0.0f
-/* Cache result of expensive calculation of u parameter values to
+/** The un-transformed 2D storage of profile vertex locations. Also for non-custom profiles
+ * this serves as a cache for the results of the expensive calculation of u parameter values to
* get even spacing on superellipse for current BevelParams seg
* and pro_super_r. */
typedef struct ProfileSpacing {
- double *xvals; /* seg+1 x values */
- double *xvals_2; /* seg_2+1 x values, seg_2 = power of 2 >= seg */
- double *yvals; /* seg+1 y values */
- double *yvals_2; /* seg_2+1 y values, seg_2 = power of 2 >= seg */
- int seg_2; /* the seg_2 value */
+ /** The profile's seg+1 x values */
+ double *xvals;
+ /** The profile's seg+1 y values */
+ double *yvals;
+ /** The profile's seg_2+1 x values, (seg_2 = power of 2 >= seg) */
+ double *xvals_2;
+ /** The profile's seg_2+1 y values, (seg_2 = power of 2 >= seg) */
+ double *yvals_2;
+ /** The power of two greater than or equal to the number of segments. */
+ int seg_2;
+ /** How far "out" the profile is, used at the start of subdivision */
+ float fullness;
} ProfileSpacing;
-/* An element in a cyclic boundary of a Vertex Mesh (VMesh) */
+/** An element in a cyclic boundary of a Vertex Mesh (VMesh) */
typedef struct BoundVert {
/** In CCW order. */
struct BoundVert *next, *prev;
@@ -137,7 +188,7 @@ typedef struct BoundVert {
/** First of edges attached here: in CCW order. */
EdgeHalf *efirst;
EdgeHalf *elast;
- /** The "edge between" that this is on, in offset_on_edge_between case. */
+ /** The "edge between" that this boundvert on, in offset_on_edge_between case. */
EdgeHalf *eon;
/** Beveled edge whose left side is attached here, if any. */
EdgeHalf *ebev;
@@ -157,26 +208,35 @@ typedef struct BoundVert {
bool is_arc_start;
/** This boundvert begins a patch profile */
bool is_patch_start;
+ /** Is this boundvert the side of the custom profile's start */
+ bool is_profile_start;
+ char _pad[3];
/** Length of seam starting from current boundvert to next boundvert with ccw ordering */
int seam_len;
/** Same as seam_len but defines length of sharp edges */
int sharp_len;
- // int _pad;
} BoundVert;
-/* Mesh structure replacing a vertex */
+/** Mesh structure replacing a vertex */
typedef struct VMesh {
- NewVert *mesh; /* allocated array - size and structure depends on kind */
- BoundVert *boundstart; /* start of boundary double-linked list */
- int count; /* number of vertices in the boundary */
- int seg; /* common # of segments for segmented edges */
+ /** Allocated array - size and structure depends on kind */
+ NewVert *mesh;
+ /** Start of boundary double-linked list */
+ BoundVert *boundstart;
+ /** Number of vertices in the boundary */
+ int count;
+ /** Common number of segments for segmented edges (same as bp->seg) */
+ int seg;
+ /** The kind of mesh to build at the corner vertex meshes */
enum {
M_NONE, /* no polygon mesh needed */
M_POLY, /* a simple polygon */
M_ADJ, /* "adjacent edges" mesh pattern */
M_TRI_FAN, /* a simple polygon - fan filled */
+ M_CUTOFF, /* A triangulated face at the end of each profile */
} mesh_kind;
- // int _pad;
+
+ int _pad;
} VMesh;
/* Data for a vertex involved in a bevel */
@@ -196,6 +256,7 @@ typedef struct BevVert {
/** Used in graph traversal */
bool visited;
/** Array of size edgecount; CCW order from vertex normal side */
+ char _pad[6];
EdgeHalf *edges;
/** Array of size wirecount of wire edges */
BMEdge **wire_edges;
@@ -209,7 +270,7 @@ typedef enum {
F_NONE,
/** Original face, not touched */
F_ORIG,
- /** Face for construction aroun a vert */
+ /** Face for construction around a vert */
F_VERT,
/** Face for a beveled edge */
F_EDGE,
@@ -217,11 +278,21 @@ typedef enum {
F_RECON,
} FKind;
+/** Helper for keeping track of angle kind. */
+enum {
+ /** Angle less than 180 degrees */
+ ANGLE_SMALLER = -1,
+ /** 180 degree angle */
+ ANGLE_STRAIGHT = 0,
+ /** Angle greater than 180 degrees */
+ ANGLE_LARGER = 1,
+};
+
#if 0
static const char* fkind_names[] = {"F_NONE", "F_ORIG", "F_VERT", "F_EDGE", "F_RECON"}; /* DEBUG */
#endif
-/* Bevel parameters and state */
+/** Bevel parameters and state */
typedef struct BevelParams {
/** Records BevVerts made: key BMVert*, value BevVert* */
GHash *vert_hash;
@@ -229,9 +300,10 @@ typedef struct BevelParams {
GHash *face_hash;
/** Use for all allocs while bevel runs, if we need to free we can switch to mempool. */
MemArena *mem_arena;
- /** Parameter values for evenly spaced profiles. */
+ /** Profile vertex location and spacings */
ProfileSpacing pro_spacing;
-
+ /** Parameter values for evenly spaced profile points for the miter profiles */
+ ProfileSpacing pro_spacing_miter;
/** Blender units to offset each side of a beveled edge. */
float offset;
/** How offset is measured; enum defined in bmesh_operators.h */
@@ -258,6 +330,11 @@ typedef struct BevelParams {
bool mark_sharp;
/** Should we harden normals? */
bool harden_normals;
+ /** Should we use the custom profiles feature? */
+ bool use_custom_profile;
+ char _pad[3];
+ /** The struct used to store the custom profile input */
+ const struct CurveProfile *custom_profile;
/** Vertex group array, maybe set if vertex_only. */
const struct MDeformVert *dvert;
/** Vertex group index, maybe set if vertex_only. */
@@ -270,6 +347,8 @@ typedef struct BevelParams {
int miter_outer;
/** What kind of miter pattern to use on non-reflex angles. */
int miter_inner;
+ /** The method to use for vertex mesh creation */
+ int vmesh_method;
/** Amount to spread when doing inside miter. */
float spread;
/** Mesh's smoothresh, used if hardening. */
@@ -278,8 +357,6 @@ typedef struct BevelParams {
// #pragma GCC diagnostic ignored "-Wpadded"
-// #include "bevdebug.c"
-
/* Some flags to re-enable old behavior for a while,
* in case fixes broke things not caught by regression tests. */
static int bev_debug_flags = 0;
@@ -367,6 +444,7 @@ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float
ans->any_seam = false;
ans->is_arc_start = false;
ans->is_patch_start = false;
+ ans->is_profile_start = false;
vm->count++;
return ans;
}
@@ -628,7 +706,7 @@ static BMFace *bev_create_ngon(BMesh *bm,
}
if (mat_nr >= 0) {
- f->mat_nr = mat_nr;
+ f->mat_nr = (short)mat_nr;
}
return f;
}
@@ -812,7 +890,7 @@ static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v)
}
/* Calculate coordinates of a point a distance d from v on e->e and return it in slideco */
-static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
+static void slide_dist(EdgeHalf *e, BMVert *v, float d, float r_slideco[3])
{
float dir[3], len;
@@ -821,8 +899,8 @@ static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
if (d > len) {
d = len - (float)(50.0 * BEVEL_EPSILON_D);
}
- copy_v3_v3(slideco, v->co);
- madd_v3_v3fl(slideco, dir, -d);
+ copy_v3_v3(r_slideco, v->co);
+ madd_v3_v3fl(r_slideco, dir, -d);
}
/* Is co not on the edge e? if not, return the closer end of e in ret_closer_v */
@@ -873,13 +951,13 @@ static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v)
}
dot = dot_v3v3(cross, no);
if (fabsf(dot) < BEVEL_EPSILON_BIG) {
- return 0;
+ return ANGLE_STRAIGHT;
}
else if (dot < 0.0f) {
- return 1;
+ return ANGLE_LARGER;
}
else {
- return -1;
+ return ANGLE_SMALLER;
}
}
@@ -917,7 +995,7 @@ static bool point_between_edges(float co[3], BMVert *v, BMFace *f, EdgeHalf *e1,
/*
* Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco.
* e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of
- * the bevel vertex, e1 precedes e2 in CCW order.
+ * the bevel vertex, e1 precedes e2 in CCW order.
* Except: if edges_between is true, there are edges between e1 and e2 in CCW order so they
* don't share a common face. We want the meeting point to be on an existing face so it
* should be dropped onto one of the intermediate faces, if possible.
@@ -978,7 +1056,7 @@ static void offset_meet(
normalize_v3(norm_perp1);
copy_v3_v3(off1a, v->co);
d = max_ff(e1->offset_r, e2->offset_l);
- d = d / cos(ang / 2.0f);
+ d = d / cosf(ang / 2.0f);
madd_v3_v3fl(off1a, norm_perp1, d);
copy_v3_v3(meetco, off1a);
}
@@ -1260,37 +1338,37 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
negate_v3(pro->proj_dir);
}
normalize_v3(pro->proj_dir);
- project_to_edge(e->e, co1, co2, pro->midco);
+ project_to_edge(e->e, co1, co2, pro->middle);
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
- /* put arc endpoints on plane with normal proj_dir, containing midco */
+ /* put arc endpoints on plane with normal proj_dir, containing middle */
add_v3_v3v3(co3, co1, pro->proj_dir);
- if (!isect_line_plane_v3(pro->coa, co1, co3, pro->midco, pro->proj_dir)) {
+ if (!isect_line_plane_v3(pro->start, co1, co3, pro->middle, pro->proj_dir)) {
/* shouldn't happen */
- copy_v3_v3(pro->coa, co1);
+ copy_v3_v3(pro->start, co1);
}
add_v3_v3v3(co3, co2, pro->proj_dir);
- if (!isect_line_plane_v3(pro->cob, co2, co3, pro->midco, pro->proj_dir)) {
+ if (!isect_line_plane_v3(pro->end, co2, co3, pro->middle, pro->proj_dir)) {
/* shouldn't happen */
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->end, co2);
}
}
else {
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
}
- /* default plane to project onto is the one with triangle co1 - midco - co2 in it */
- sub_v3_v3v3(d1, pro->midco, co1);
- sub_v3_v3v3(d2, pro->midco, co2);
+ /* default plane to project onto is the one with triangle co1 - middle - co2 in it */
+ sub_v3_v3v3(d1, pro->middle, co1);
+ sub_v3_v3v3(d2, pro->middle, co2);
normalize_v3(d1);
normalize_v3(d2);
cross_v3_v3v3(pro->plane_no, d1, d2);
normalize_v3(pro->plane_no);
if (nearly_parallel(d1, d2)) {
- /* co1 - midco -co2 are collinear.
+ /* co1 - middle -co2 are collinear.
* Should be case that beveled edge is coplanar with two boundary verts.
* We want to move the profile to that common plane, if possible.
* That makes the multi-segment bevels curve nicely in that plane, as users expect.
- * The new midco should be either v (when neighbor edges are unbeveled)
+ * The new middle should be either v (when neighbor edges are unbeveled)
* or the intersection of the offset lines (if they are).
* If the profile is going to lead into unbeveled edges on each side
* (that is, both BoundVerts are "on-edge" points on non-beveled edges)
@@ -1300,14 +1378,14 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
else {
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
}
if (DEBUG_OLD_FLAT_MID) {
- copy_v3_v3(pro->midco, bv->v->co);
+ copy_v3_v3(pro->middle, bv->v->co);
}
else {
- copy_v3_v3(pro->midco, bv->v->co);
+ copy_v3_v3(pro->middle, bv->v->co);
if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) {
/* want mid at the meet point of next and prev offset edges */
float d3[3], d4[3], co4[3], meetco[3], isect2[3];
@@ -1319,7 +1397,7 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
normalize_v3(d4);
if (nearly_parallel(d3, d4)) {
/* offset lines are collinear - want linear interpolation */
- mid_v3_v3v3(pro->midco, co1, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
do_linear_interp = true;
}
else {
@@ -1327,20 +1405,20 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
add_v3_v3v3(co4, co2, d4);
isect_kind = isect_line_line_v3(co1, co3, co2, co4, meetco, isect2);
if (isect_kind != 0) {
- copy_v3_v3(pro->midco, meetco);
+ copy_v3_v3(pro->middle, meetco);
}
else {
/* offset lines don't intersect - want linear interpolation */
- mid_v3_v3v3(pro->midco, co1, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
do_linear_interp = true;
}
}
}
}
- copy_v3_v3(pro->cob, co2);
- sub_v3_v3v3(d1, pro->midco, co1);
+ copy_v3_v3(pro->end, co2);
+ sub_v3_v3v3(d1, pro->middle, co1);
normalize_v3(d1);
- sub_v3_v3v3(d2, pro->midco, co2);
+ sub_v3_v3v3(d2, pro->middle, co2);
normalize_v3(d2);
cross_v3_v3v3(pro->plane_no, d1, d2);
normalize_v3(pro->plane_no);
@@ -1357,9 +1435,9 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
copy_v3_v3(pro->plane_co, co1);
}
else if (bndv->is_arc_start) {
- /* assume pro->midco was already set */
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ /* assume pro->middle was alredy set */
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
pro->super_r = PRO_CIRCLE_R;
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
@@ -1368,9 +1446,9 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
if (do_linear_interp) {
pro->super_r = PRO_LINE_R;
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
- mid_v3_v3v3(pro->midco, co1, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
/* won't use projection for this line profile */
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
@@ -1378,22 +1456,23 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
}
-/* Maybe move the profile plane for bndv->ebev to the plane its profile's coa, cob and the
+/* Maybe move the profile plane for bndv->ebev to the plane its profile's start, and the
* original beveled vert, bmv. This will usually be the plane containing its adjacent
- * non-beveled edges, but sometimes coa and cob are not on those edges.
- */
-static void move_profile_plane(BoundVert *bndv, BMVert *bmv)
+ * non-beveled edges, but sometimes the start and the end are not on those edges.
+ *
+ * Currently just used in build boundary terminal edge */
+static void move_profile_plane(BoundVert *bndv, BMVert *bmvert)
{
float d1[3], d2[3], no[3], no2[3], no3[3], dot2, dot3;
Profile *pro = &bndv->profile;
- /* only do this if projecting, and coa, cob, and proj_dir are not coplanar */
+ /* only do this if projecting, and start, end, and proj_dir are not coplanar */
if (is_zero_v3(pro->proj_dir)) {
return;
}
- sub_v3_v3v3(d1, bmv->co, pro->coa);
+ sub_v3_v3v3(d1, bmvert->co, pro->start);
normalize_v3(d1);
- sub_v3_v3v3(d2, bmv->co, pro->cob);
+ sub_v3_v3v3(d2, bmvert->co, pro->end);
normalize_v3(d2);
cross_v3_v3v3(no, d1, d2);
cross_v3_v3v3(no2, d1, pro->proj_dir);
@@ -1584,35 +1663,35 @@ static double superellipse_co(double x, float r, bool rbig)
}
/* Find the point on given profile at parameter i which goes from 0 to n as
- * the profile is moved from pro->coa to pro->cob.
+ * the profile is moved from pro->start to pro->end.
* We assume that n is either the global seg number or a power of 2 less than
* or equal to the power of 2 >= seg.
* In the latter case, we subsample the profile for seg_2, which will not necessarily
* give equal spaced chords, but is in fact more what is desired by the cubic subdivision
* method used to make the vmesh pattern. */
-static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int n, float r_co[3])
+static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int nseg, float r_co[3])
{
- int d;
+ int subsample_spacing;
if (bp->seg == 1) {
if (i == 0) {
- copy_v3_v3(r_co, pro->coa);
+ copy_v3_v3(r_co, pro->start);
}
else {
- copy_v3_v3(r_co, pro->cob);
+ copy_v3_v3(r_co, pro->end);
}
}
else {
- if (n == bp->seg) {
+ if (nseg == bp->seg) {
BLI_assert(pro->prof_co != NULL);
copy_v3_v3(r_co, pro->prof_co + 3 * i);
}
else {
- BLI_assert(is_power_of_2_i(n) && n <= bp->pro_spacing.seg_2);
- /* set d to spacing in prof_co_2 between subsamples */
- d = bp->pro_spacing.seg_2 / n;
- copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * d);
+ BLI_assert(is_power_of_2_i(nseg) && nseg <= bp->pro_spacing.seg_2);
+ /* Find spacing between subsamples in prof_co_2 */
+ subsample_spacing = bp->pro_spacing.seg_2 / nseg;
+ copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * subsample_spacing);
}
}
}
@@ -1624,15 +1703,16 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int n,
* the coordinate values for the power of 2 >= bp->seg,
* because the ADJ pattern needs power-of-2 boundaries
* during construction. */
-static void calculate_profile(BevelParams *bp, BoundVert *bndv)
+static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter)
{
int i, k, ns;
const double *xvals, *yvals;
- float co[3], co2[3], p[3], m[4][4];
+ float co[3], co2[3], p[3], map[4][4], bottom_corner[3], top_corner[3];
float *prof_co, *prof_co_k;
float r;
bool need_2, map_ok;
Profile *pro = &bndv->profile;
+ ProfileSpacing *pro_spacing = (miter) ? &bp->pro_spacing_miter : &bp->pro_spacing;
if (bp->seg == 1) {
return;
@@ -1640,27 +1720,39 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
need_2 = bp->seg != bp->pro_spacing.seg_2;
if (!pro->prof_co) {
- pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, (bp->seg + 1) * 3 * sizeof(float));
+ pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena,
+ ((size_t)bp->seg + 1) * 3 * sizeof(float));
if (need_2) {
pro->prof_co_2 = (float *)BLI_memarena_alloc(
- bp->mem_arena, (bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float));
+ bp->mem_arena, ((size_t)bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float));
}
else {
pro->prof_co_2 = pro->prof_co;
}
}
r = pro->super_r;
- if (r == PRO_LINE_R) {
+ if (!bp->use_custom_profile && r == PRO_LINE_R) {
map_ok = false;
}
else {
- map_ok = make_unit_square_map(pro->coa, pro->midco, pro->cob, m);
- }
+ map_ok = make_unit_square_map(pro->start, pro->middle, pro->end, map);
+ }
+ if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) {
+ /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the
+ * un-transformed profile throught the 2D->3D map and calculating the distance between them */
+ zero_v3(p);
+ mul_v3_m4v3(bottom_corner, map, p);
+ p[0] = 1.0f;
+ p[1] = 1.0f;
+ mul_v3_m4v3(top_corner, map, p);
+ pro->height = len_v3v3(bottom_corner, top_corner);
+ }
+ /* The first iteration is the nseg case, the second is the seg_2 case (if it's needed) */
for (i = 0; i < 2; i++) {
if (i == 0) {
ns = bp->seg;
- xvals = bp->pro_spacing.xvals;
- yvals = bp->pro_spacing.yvals;
+ xvals = pro_spacing->xvals;
+ yvals = pro_spacing->yvals;
prof_co = pro->prof_co;
}
else {
@@ -1668,33 +1760,42 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
break; /* shares coords with pro->prof_co */
}
ns = bp->pro_spacing.seg_2;
- xvals = bp->pro_spacing.xvals_2;
- yvals = bp->pro_spacing.yvals_2;
+ xvals = pro_spacing->xvals_2;
+ yvals = pro_spacing->yvals_2;
prof_co = pro->prof_co_2;
}
- BLI_assert((r == PRO_LINE_R || (xvals != NULL && yvals != NULL)) && prof_co != NULL);
+
+ /* Iterate over the vertices along the boundary arc */
for (k = 0; k <= ns; k++) {
if (k == 0) {
- copy_v3_v3(co, pro->coa);
+ copy_v3_v3(co, pro->start);
}
else if (k == ns) {
- copy_v3_v3(co, pro->cob);
+ copy_v3_v3(co, pro->end);
}
else {
if (map_ok) {
- p[0] = xvals[k];
- p[1] = yvals[k];
+ if (reversed) {
+ p[0] = (float)yvals[ns - k];
+ p[1] = (float)xvals[ns - k];
+ }
+ else {
+ p[0] = (float)xvals[k];
+ p[1] = (float)yvals[k];
+ }
p[2] = 0.0f;
- mul_v3_m4v3(co, m, p);
+ /* Do the 2D->3D transformation of the profile coordinates */
+ mul_v3_m4v3(co, map, p);
}
else {
- interp_v3_v3v3(co, pro->coa, pro->cob, (float)k / (float)ns);
+ interp_v3_v3v3(co, pro->start, pro->end, (float)k / (float)ns);
}
}
- /* project co onto final profile plane */
- prof_co_k = prof_co + 3 * k;
+ /* Finish the 2D->3D transformation by projecting onto the final profile plane */
+ prof_co_k = prof_co + 3 * k; /* Each coord takes up 3 spaces */
if (!is_zero_v3(pro->proj_dir)) {
add_v3_v3v3(co2, co, pro->proj_dir);
+ /* pro->plane_co and pro->plane_no are filled in "set_profile_params" */
if (!isect_line_plane_v3(prof_co_k, co, co2, pro->plane_co, pro->plane_no)) {
/* shouldn't happen */
copy_v3_v3(prof_co_k, co);
@@ -1708,11 +1809,12 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
}
/* Snap a direction co to a superellipsoid with parameter super_r.
- * For square profiles, midline says whether or not to snap to both planes. */
+ * For square profiles, midline says whether or not to snap to both planes.
+ *
+ * Only currently used for the pipe and cube corner special cases */
static void snap_to_superellipsoid(float co[3], const float super_r, bool midline)
{
float a, b, c, x, y, z, r, rinv, dx, dy;
-
r = super_r;
if (r == PRO_CIRCLE_R) {
normalize_v3(co);
@@ -1936,7 +2038,7 @@ static void bevel_extend_edge_data(BevVert *bv)
} while (bcur != start);
}
-/* Mark edges as sharp if they are between a smooth recon face and a new face. */
+/* Mark edges as sharp if they are between a smooth reconstructed face and a new face. */
static void bevel_edges_sharp_boundary(BMesh *bm, BevelParams *bp)
{
BMIter fiter, liter;
@@ -1972,7 +2074,7 @@ static void bevel_edges_sharp_boundary(BMesh *bm, BevelParams *bp)
* And at boundaries between #F_EDGE and #F_VERT faces, the normals should match the #F_EDGE ones.
* Assumes custom loop normals are in use.
*/
-static void bevel_harden_normals(BMesh *bm, BevelParams *bp)
+static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
{
BMIter liter, fiter;
BMFace *f;
@@ -2207,13 +2309,17 @@ static bool eh_on_plane(EdgeHalf *e)
/* Calculate the profiles for all the BoundVerts of VMesh vm */
static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm)
{
- BoundVert *v;
+ BoundVert *bndv;
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- set_profile_params(bp, bv, v);
- calculate_profile(bp, v);
- } while ((v = v->next) != vm->boundstart);
+ set_profile_params(bp, bv, bndv);
+ /* Use the miter profile spacing struct if the default is filled with the custom profile. */
+ bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start);
+ /* Don't bother reversing the profile if it's a miter profile */
+ bool reverse_profile = !bndv->is_profile_start && !miter_profile;
+ calculate_profile(bp, bndv, reverse_profile, miter_profile);
+ } while ((bndv = bndv->next) != vm->boundstart);
}
/* Implements build_boundary for vertex-only case */
@@ -2263,14 +2369,15 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr
static void build_boundary_terminal_edge(BevelParams *bp,
BevVert *bv,
EdgeHalf *efirst,
- bool construct)
+ const bool construct)
{
MemArena *mem_arena = bp->mem_arena;
VMesh *vm = bv->vmesh;
- BoundVert *v;
+ BoundVert *bndv;
EdgeHalf *e;
const float *no;
float co[3], d;
+ bool use_tri_fan;
e = efirst;
if (bv->edgecount == 2) {
@@ -2278,9 +2385,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
no = e->fprev ? e->fprev->no : (e->fnext ? e->fnext->no : NULL);
offset_in_plane(e, no, true, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = v->ebev = e;
- e->leftv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = bndv->ebev = e;
+ e->leftv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2288,9 +2395,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
no = e->fnext ? e->fnext->no : (e->fprev ? e->fprev->no : NULL);
offset_in_plane(e, no, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e;
- e->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e;
+ e->rightv = bndv;
}
else {
adjust_bound_vert(e->rightv, co);
@@ -2298,11 +2405,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
/* make artificial extra point along unbeveled edge, and form triangle */
slide_dist(e->next, bv->v, e->offset_l, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e->next;
- e->next->leftv = e->next->rightv = v;
- /* could use M_POLY too, but tri-fan looks nicer)*/
- vm->mesh_kind = M_TRI_FAN;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e->next;
+ e->next->leftv = e->next->rightv = bndv;
set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
}
else {
@@ -2316,11 +2421,11 @@ static void build_boundary_terminal_edge(BevelParams *bp,
/* TODO: should do something else if angle between e and e->prev > 180 */
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = e->prev;
- v->elast = v->ebev = e;
- e->leftv = v;
- e->prev->leftv = e->prev->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = e->prev;
+ bndv->elast = bndv->ebev = e;
+ e->leftv = bndv;
+ e->prev->leftv = e->prev->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2328,23 +2433,26 @@ static void build_boundary_terminal_edge(BevelParams *bp,
e = e->next;
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = e->prev;
- v->elast = e;
- e->leftv = e->rightv = v;
- e->prev->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = e->prev;
+ bndv->elast = e;
+ e->leftv = e->rightv = bndv;
+ e->prev->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
}
/* For the edges not adjacent to the beveled edge, slide the bevel amount along. */
d = efirst->offset_l_spec;
+ if (bp->use_custom_profile || bp->profile < 0.25f) {
+ d *= sqrtf(2.0f); /* Need to go further along the edge to make room for full profile area. */
+ }
for (e = e->next; e->next != efirst; e = e->next) {
slide_dist(e, bv->v, d, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e;
- e->leftv = e->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e;
+ e->leftv = e->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2354,11 +2462,12 @@ static void build_boundary_terminal_edge(BevelParams *bp,
calculate_vm_profiles(bp, bv, vm);
if (bv->edgecount >= 3) {
- /* special case: snap profile to plane of adjacent two edges */
- v = vm->boundstart;
- BLI_assert(v->ebev != NULL);
- move_profile_plane(v, bv->v);
- calculate_profile(bp, v);
+ /* Special case: snap profile to plane of adjacent two edges. */
+ bndv = vm->boundstart;
+ BLI_assert(bndv->ebev != NULL);
+ move_profile_plane(bndv, bv->v);
+ /* This step happens before the profile orientation pass so don't reverse the profile. */
+ calculate_profile(bp, bndv, false, false);
}
if (construct) {
@@ -2368,12 +2477,34 @@ static void build_boundary_terminal_edge(BevelParams *bp,
vm->mesh_kind = M_NONE;
}
else if (vm->count == 3) {
- vm->mesh_kind = M_TRI_FAN;
+ use_tri_fan = true;
+ if (bp->use_custom_profile) {
+ /* Use M_POLY if the extra point is planar with the profile to prevent overhanging edges */
+ bndv = efirst->leftv;
+ float profile_plane[4];
+ plane_from_point_normal_v3(profile_plane, bndv->profile.plane_co, bndv->profile.plane_no);
+ bndv = efirst->rightv->next; /* The added boundvert placed along the non-adjacent edge */
+ if (dist_squared_to_plane_v3(bndv->nv.co, profile_plane) < BEVEL_EPSILON_BIG) {
+ use_tri_fan = false;
+ }
+ }
+ vm->mesh_kind = (use_tri_fan) ? M_TRI_FAN : M_POLY;
}
else {
vm->mesh_kind = M_POLY;
}
}
+#ifdef DEBUG_CUSTOM_PROFILE_WELD
+ if (bp->seg > 1) {
+ printf("Terminal Edge Profile Coordinates:\n");
+ for (int k = 0; k < bp->seg; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)vm->boundstart->profile.prof_co[3 * k],
+ (double)vm->boundstart->profile.prof_co[3 * k + 1],
+ (double)vm->boundstart->profile.prof_co[3 * k + 2]);
+ }
+ }
+#endif
}
/* Helper for build_boundary to handle special miters */
@@ -2399,7 +2530,7 @@ static void adjust_miter_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
v3next = v3->next;
copy_v3_v3(co2, v1->nv.co);
if (v1->is_arc_start) {
- copy_v3_v3(v1->profile.midco, co2);
+ copy_v3_v3(v1->profile.middle, co2);
}
/* co1 is intersection of line through co2 in dir of emiter->e
@@ -2477,7 +2608,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
BoundVert *v, *v1, *v2, *v3;
VMesh *vm;
float co[3], r;
- int nip, nnip, miter_outer, miter_inner;
+ int in_plane, not_in_plane, miter_outer, miter_inner;
int ang_kind;
/* Current bevel does nothing if only one edge into a vertex */
@@ -2497,7 +2628,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
BLI_assert(e->is_bev);
if (bv->selcount == 1) {
- /* special case: only one beveled edge in */
+ /* Special case: only one beveled edge in */
build_boundary_terminal_edge(bp, bv, efirst, construct);
return;
}
@@ -2509,7 +2640,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
/* keep track of the first beveled edge of an outside miter (there can be at most 1 per bv */
emiter = NULL;
- /* Here: there is more than one beveled edge.
+ /* There is more than one beveled edge.
* We make BoundVerts to connect the sides of the beveled edges.
* Non-beveled edges in between will just join to the appropriate juncture point. */
e = efirst;
@@ -2519,26 +2650,26 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
/* Make the BoundVert for the right side of e; other side will be made
* when the beveled edge to the left of e is handled.
* Analyze edges until next beveled edge.
- * They are either "in plane" (preceding and subsequent faces are coplanar)
- * or not. The "non-in-plane" edges effect silhouette and we prefer to slide
- * along one of those if possible. */
- nip = nnip = 0; /* counts of in-plane / not-in-plane */
- enip = eip = NULL; /* representatives of each */
+ * They are either "in plane" (preceding and subsequent faces are coplanar) or not.
+ * The "non-in-plane" edges affect the silhouette and we prefer to slide along one of those if
+ * possible. */
+ in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane */
+ enip = eip = NULL; /* representatives of each */
for (e2 = e->next; !e2->is_bev; e2 = e2->next) {
if (eh_on_plane(e2)) {
- nip++;
+ in_plane++;
eip = e2;
}
else {
- nnip++;
+ not_in_plane++;
enip = e2;
}
}
- if (nip == 0 && nnip == 0) {
+ if (in_plane == 0 && not_in_plane == 0) {
offset_meet(e, e2, bv->v, e->fnext, false, co);
}
- else if (nnip > 0) {
- if (bp->loop_slide && nnip == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
+ else if (not_in_plane > 0) {
+ if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
if (offset_on_edge_between(e, e2, enip, bv->v, co, &r)) {
eon = enip;
}
@@ -2548,8 +2679,8 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
}
else {
- /* nip > 0 and nnip == 0 */
- if (bp->loop_slide && nip == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) {
+ /* n_in_plane > 0 and n_not_in_plane == 0 */
+ if (bp->loop_slide && in_plane == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) {
if (offset_on_edge_between(e, e2, eip, bv->v, co, &r)) {
eon = eip;
}
@@ -2579,16 +2710,15 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
* There can only be one outer reflex angle, so only one outer miter,
* and emiter will be set to the first edge of such an edge.
* A miter kind of BEVEL_MITER_SHARP means no special miter */
-
- if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
- (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1)) {
- if (ang_kind == 1) {
+ if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
+ (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
+ if (ang_kind == ANGLE_LARGER) {
emiter = e;
}
/* make one or two more boundverts; for now all will have same co */
v1 = v;
v1->ebev = NULL;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v2 = add_new_bound_vert(mem_arena, vm, co);
}
else {
@@ -2600,7 +2730,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
v3->elast = e2;
v3->eon = NULL;
e2->leftv = v3;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v1->is_patch_start = true;
v2->eon = v1->eon;
v2->sinratio = v1->sinratio;
@@ -2622,17 +2752,16 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
else {
v1->is_arc_start = true;
- copy_v3_v3(v1->profile.midco, co);
+ copy_v3_v3(v1->profile.middle, co);
if (e->next == e2) {
v1->elast = v1->efirst;
}
else {
- int between = nip + nnip;
+ int between = in_plane + not_in_plane;
int bet2 = between / 2;
bool betodd = (between % 2) == 1;
int i = 0;
- /* Put first half of in-between edges at index 0,
- * second half at index bp->seg.
+ /* Put first half of in-between edges at index 0, second half at index bp->seg.
* If between is odd, put middle one at midindex */
for (e3 = e->next; e3 != e2; e3 = e3->next) {
v1->elast = e3;
@@ -2651,15 +2780,15 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
}
}
- else {
+ else { /* construct == false */
ang_kind = edges_angle_kind(e, e2, bv->v);
- if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
- (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1)) {
- if (ang_kind == 1) {
+ if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
+ (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
+ if (ang_kind == ANGLE_LARGER) {
emiter = e;
}
v1 = e->rightv;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v2 = v1->next;
v3 = v2->next;
}
@@ -2699,7 +2828,14 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
vm->mesh_kind = M_POLY;
}
else {
- vm->mesh_kind = M_ADJ;
+ switch (bp->vmesh_method) {
+ case BEVEL_VMESH_ADJ:
+ vm->mesh_kind = M_ADJ;
+ break;
+ case BEVEL_VMESH_CUTOFF:
+ vm->mesh_kind = M_CUTOFF;
+ break;
+ }
}
}
}
@@ -2885,6 +3021,194 @@ static bool adjust_the_cycle_or_chain_fast(BoundVert *vstart, int np, bool iscyc
}
#endif
+/** Helper function to return the next Beveled EdgeHalf along a path.
+ * \param toward_bv Whether the direction to travel points toward or away from the BevVert
+ * connected to the current EdgeHalf
+ * \param r_bv The BevVert conencted to the EdgeHalf-- updated if we're travelling to the other
+ * EdgeHalf of an original edge
+ * \note This only returns the most parallel edge if it's the most parallel by
+ * at least 10 degrees. This is a somewhat arbitrary choice, but it makes sure that consistent
+ * orientation paths only continue in obvious ways. */
+static EdgeHalf *next_edgehalf_bev(BevelParams *bp,
+ EdgeHalf *start_edge,
+ bool toward_bv,
+ BevVert **r_bv)
+{
+ EdgeHalf *new_edge;
+ EdgeHalf *next_edge = NULL;
+ float dir_start_edge[3], dir_new_edge[3];
+ float second_best_dot = 0.0f, best_dot = 0.0f;
+ float new_dot;
+
+ /* Case 1: The next EdgeHalf is across a BevVert from the current EdgeHalf. */
+ if (toward_bv) {
+ /* Skip all the logic if there's only one beveled edge at the vertex, we're at an end. */
+ if ((*r_bv)->selcount == 1) {
+ return NULL; /* No other edges to go to. */
+ }
+
+ /* The case with only one other edge connected to the vertex is special too. */
+ if ((*r_bv)->selcount == 2) {
+ /* Just find the next beveled edge, that's the only other option. */
+ new_edge = start_edge;
+ do {
+ new_edge = new_edge->next;
+ } while (!new_edge->is_bev);
+
+ return new_edge;
+ }
+
+ /* Find the direction vector of the current edge (pointing INTO the BevVert).
+ * v1 and v2 don't necessarily have an order, so we need to check which is closer to bv. */
+ if (start_edge->e->v1 == (*r_bv)->v) {
+ sub_v3_v3v3(dir_start_edge, start_edge->e->v1->co, start_edge->e->v2->co);
+ }
+ else {
+ sub_v3_v3v3(dir_start_edge, start_edge->e->v2->co, start_edge->e->v1->co);
+ }
+ normalize_v3(dir_start_edge);
+
+ /* Find the beveled edge coming out of the BevVert that's most parallel to the current edge. */
+ new_edge = start_edge->next;
+ while (new_edge != start_edge) {
+ if (!new_edge->is_bev) {
+ new_edge = new_edge->next;
+ continue;
+ }
+ /* Find direction vector of the possible next edge (pointing OUT of the BevVert). */
+ if (new_edge->e->v2 == (*r_bv)->v) {
+ sub_v3_v3v3(dir_new_edge, new_edge->e->v1->co, new_edge->e->v2->co);
+ }
+ else {
+ sub_v3_v3v3(dir_new_edge, new_edge->e->v2->co, new_edge->e->v1->co);
+ }
+ normalize_v3(dir_new_edge);
+
+ /* Use this edge if it is the most parallel to the orignial so far */
+ new_dot = dot_v3v3(dir_new_edge, dir_start_edge);
+ if (new_dot > best_dot) {
+ second_best_dot = best_dot; /* For remembering if the choice was too close. */
+ best_dot = new_dot;
+ next_edge = new_edge;
+ }
+ else if (new_dot > second_best_dot) {
+ second_best_dot = new_dot;
+ }
+
+ new_edge = new_edge->next;
+ }
+
+ /* Only return a new Edge if one was found and if the choice of next edge was not too close. */
+ if ((next_edge != NULL) && compare_ff(best_dot, second_best_dot, BEVEL_SMALL_ANG_DOT)) {
+ return NULL;
+ }
+ else {
+ return next_edge;
+ }
+ }
+ else {
+ /* Case 2: The next EdgeHalf is the other side of the BMEdge.
+ * It's part of the same BMEdge, so we know the other EdgeHalf is also beveled. */
+ next_edge = find_other_end_edge_half(bp, start_edge, r_bv);
+ return next_edge;
+ }
+}
+
+/** Starting along any beveled edge, travel along the chain / cycle of beveled edges including that
+ * edge, marking consistent profile orientations along the way. Orientations are marked by setting
+ * whether the BoundVert that contains each profile's information is the side of the profile's
+ * start or not. */
+static void regularize_profile_orientation(BevelParams *bp, BMEdge *bme)
+{
+ BevVert *start_bv;
+ BevVert *bv;
+ EdgeHalf *start_edgehalf, *edgehalf;
+ bool toward_bv;
+
+ start_bv = find_bevvert(bp, bme->v1);
+ start_edgehalf = find_edge_half(start_bv, bme);
+ if (!start_edgehalf->is_bev || start_edgehalf->visited_rpo) {
+ return;
+ }
+
+ /* Pick a BoundVert on one side of the profile to use for the start of the profile. */
+ start_edgehalf->leftv->is_profile_start = false;
+ start_edgehalf->visited_rpo = true;
+
+ /* First loop starts in the away from BevVert direction and the second starts toward it. */
+ for (int i = 0; i < 2; i++) {
+ edgehalf = start_edgehalf;
+ bv = start_bv;
+ toward_bv = (i == 0);
+ edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
+
+ /* Keep traveling until there is no unvisited beveled edgehalf to visit next. */
+ while (edgehalf && !edgehalf->visited_rpo) {
+ /* Mark the correct BoundVert as the start of the newly visited profile.
+ * The direction relative to the BevVert switches every step, so also switch
+ * the orientation every step. */
+ if (i == 0) {
+ edgehalf->leftv->is_profile_start = toward_bv;
+ }
+ else {
+ /* The opposite side as the first direction because we're moving the other way. */
+ edgehalf->leftv->is_profile_start = !toward_bv;
+ }
+
+ /* The next jump will in the opposite direction relative to the BevVert. */
+ toward_bv = !toward_bv;
+
+ edgehalf->visited_rpo = true;
+ edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
+ }
+ }
+}
+
+#ifdef DEBUG_PROFILE_ORIENTATION_DRAW
+/** Draws markers on beveled edges showing the side that the profile starts on. A sphere shows
+ * the start side of the profile where it starts, and the lines connected to the sphere show which
+ * edge the orientation corresponds to.
+ * \note Only drawn while bevel is calculating, the debug geometry is not persistent. */
+static void debug_draw_profile_orientation(BevelParams *bp, BMesh *bm)
+{
+ BMIter iter;
+ BMEdge *bmedge;
+ float middle[3];
+
+ BM_ITER_MESH (bmedge, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(bmedge, BM_ELEM_TAG)) {
+ mid_v3_v3v3(middle, bmedge->v1->co, bmedge->v2->co);
+
+ /* Draw the orientation for the first side of the edge. */
+ EdgeHalf *edge_half = find_edge_half(find_bevvert(bp, bmedge->v1), bmedge);
+ if (edge_half->leftv->is_profile_start) { /* The left boundvert defines the profiles. */
+ DRW_debug_sphere(edge_half->leftv->nv.co, 0.04f, debug_color_red);
+ DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_red);
+ DRW_debug_line_v3v3(bmedge->v1->co, edge_half->leftv->nv.co, debug_color_red);
+ }
+ else {
+ DRW_debug_sphere(edge_half->rightv->nv.co, 0.04f, debug_color_red);
+ DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_red);
+ DRW_debug_line_v3v3(bmedge->v1->co, edge_half->rightv->nv.co, debug_color_red);
+ }
+
+ /* Draw the orientation for the second side of the edge. */
+ edge_half = find_edge_half(find_bevvert(bp, bmedge->v2), bmedge);
+ if (edge_half->leftv->is_profile_start) {
+ DRW_debug_sphere(edge_half->leftv->nv.co, 0.05f, debug_color_blue);
+ DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_blue);
+ DRW_debug_line_v3v3(bmedge->v2->co, edge_half->leftv->nv.co, debug_color_blue);
+ }
+ else {
+ DRW_debug_sphere(edge_half->rightv->nv.co, 0.05f, debug_color_blue);
+ DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_blue);
+ DRW_debug_line_v3v3(bmedge->v2->co, edge_half->rightv->nv.co, debug_color_blue);
+ }
+ }
+ }
+}
+#endif
+
/* Adjust the offsets for a single cycle or chain.
* For chains and some cycles, a fast solution exists.
* Otherwise, we set up and solve a linear least squares problem
@@ -3218,8 +3542,8 @@ static VMesh *new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *
vm->count = count;
vm->seg = seg;
vm->boundstart = bounds;
- vm->mesh = (NewVert *)BLI_memarena_alloc(mem_arena,
- count * (1 + seg / 2) * (1 + seg) * sizeof(NewVert));
+ vm->mesh = (NewVert *)BLI_memarena_alloc(
+ mem_arena, (size_t)(count * (1 + seg / 2) * (1 + seg)) * sizeof(NewVert));
vm->mesh_kind = M_ADJ;
return vm;
}
@@ -3261,10 +3585,10 @@ static NewVert *mesh_vert_canon(VMesh *vm, int i, int j, int k)
static bool is_canon(VMesh *vm, int i, int j, int k)
{
int ns2 = vm->seg / 2;
- if (vm->seg % 2 == 1) {
+ if (vm->seg % 2 == 1) { /* odd */
return (j <= ns2 && k <= ns2);
}
- else {
+ else { /* even */
return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
}
}
@@ -3296,6 +3620,9 @@ static void vmesh_copy_equiv_verts(VMesh *vm)
/* Calculate and return in r_cent the centroid of the center poly */
static void vmesh_center(VMesh *vm, float r_cent[3])
{
+#ifdef DEBUG_CUSTOM_PROFILE_ADJ
+ printf("VMESH CENTER\n");
+#endif
int n, ns2, i;
n = vm->count;
@@ -3432,182 +3759,193 @@ static int interp_range(const float *frac, int n, const float f, float *r_rest)
}
/* Interpolate given vmesh to make one with target nseg border vertices on the profiles */
-static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm0, int nseg)
+/* HANS-TODO: This puts the center mesh vert at a slightly off location sometimes, which seems to
+ * be associated with the rest of that ring being shifted or connected slightly incorrectly to its
+ * neighbors */
+static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm_in, int nseg)
{
- int n, ns0, nseg2, odd, i, j, k, j0, k0, k0prev, j0inc, k0inc;
+ int n_bndv, ns_in, nseg2, odd, i, j, k, j_in, k_in, k_in_prev, j0inc, k0inc;
float *prev_frac, *frac, *new_frac, *prev_new_frac;
- float f, restj, restk, restkprev;
+ float fraction, restj, restk, restkprev;
float quad[4][3], co[3], center[3];
- VMesh *vm1;
+ VMesh *vm_out;
BoundVert *bndv;
- n = vm0->count;
- ns0 = vm0->seg;
+ n_bndv = vm_in->count;
+ ns_in = vm_in->seg;
nseg2 = nseg / 2;
odd = nseg % 2;
- vm1 = new_adj_vmesh(bp->mem_arena, n, nseg, vm0->boundstart);
+ vm_out = new_adj_vmesh(bp->mem_arena, n_bndv, nseg, vm_in->boundstart);
- prev_frac = BLI_array_alloca(prev_frac, (ns0 + 1));
- frac = BLI_array_alloca(frac, (ns0 + 1));
+ prev_frac = BLI_array_alloca(prev_frac, (ns_in + 1));
+ frac = BLI_array_alloca(frac, (ns_in + 1));
new_frac = BLI_array_alloca(new_frac, (nseg + 1));
prev_new_frac = BLI_array_alloca(prev_new_frac, (nseg + 1));
- fill_vmesh_fracs(vm0, prev_frac, n - 1);
- bndv = vm0->boundstart;
+ fill_vmesh_fracs(vm_in, prev_frac, n_bndv - 1);
+ bndv = vm_in->boundstart;
fill_profile_fracs(bp, bndv->prev, prev_new_frac, nseg);
- for (i = 0; i < n; i++) {
- fill_vmesh_fracs(vm0, frac, i);
+ for (i = 0; i < n_bndv; i++) {
+ fill_vmesh_fracs(vm_in, frac, i);
fill_profile_fracs(bp, bndv, new_frac, nseg);
for (j = 0; j <= nseg2 - 1 + odd; j++) {
for (k = 0; k <= nseg2; k++) {
- f = new_frac[k];
- k0 = interp_range(frac, ns0, f, &restk);
- f = prev_new_frac[nseg - j];
- k0prev = interp_range(prev_frac, ns0, f, &restkprev);
- j0 = ns0 - k0prev;
+ /* Finding the locations where "fraction" fits into previous and current "frac" */
+ fraction = new_frac[k];
+ k_in = interp_range(frac, ns_in, fraction, &restk);
+ fraction = prev_new_frac[nseg - j];
+ k_in_prev = interp_range(prev_frac, ns_in, fraction, &restkprev);
+ j_in = ns_in - k_in_prev;
restj = -restkprev;
if (restj > -BEVEL_EPSILON) {
restj = 0.0f;
}
else {
- j0 = j0 - 1;
+ j_in = j_in - 1;
restj = 1.0f + restj;
}
/* Use bilinear interpolation within the source quad; could be smarter here */
if (restj < BEVEL_EPSILON && restk < BEVEL_EPSILON) {
- copy_v3_v3(co, mesh_vert_canon(vm0, i, j0, k0)->co);
+ copy_v3_v3(co, mesh_vert_canon(vm_in, i, j_in, k_in)->co);
}
else {
- j0inc = (restj < BEVEL_EPSILON || j0 == ns0) ? 0 : 1;
- k0inc = (restk < BEVEL_EPSILON || k0 == ns0) ? 0 : 1;
- copy_v3_v3(quad[0], mesh_vert_canon(vm0, i, j0, k0)->co);
- copy_v3_v3(quad[1], mesh_vert_canon(vm0, i, j0, k0 + k0inc)->co);
- copy_v3_v3(quad[2], mesh_vert_canon(vm0, i, j0 + j0inc, k0 + k0inc)->co);
- copy_v3_v3(quad[3], mesh_vert_canon(vm0, i, j0 + j0inc, k0)->co);
+ j0inc = (restj < BEVEL_EPSILON || j_in == ns_in) ? 0 : 1;
+ k0inc = (restk < BEVEL_EPSILON || k_in == ns_in) ? 0 : 1;
+ copy_v3_v3(quad[0], mesh_vert_canon(vm_in, i, j_in, k_in)->co);
+ copy_v3_v3(quad[1], mesh_vert_canon(vm_in, i, j_in, k_in + k0inc)->co);
+ copy_v3_v3(quad[2], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in + k0inc)->co);
+ copy_v3_v3(quad[3], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in)->co);
interp_bilinear_quad_v3(quad, restk, restj, co);
}
- copy_v3_v3(mesh_vert(vm1, i, j, k)->co, co);
+ copy_v3_v3(mesh_vert(vm_out, i, j, k)->co, co);
}
}
bndv = bndv->next;
- memcpy(prev_frac, frac, (ns0 + 1) * sizeof(float));
- memcpy(prev_new_frac, new_frac, (nseg + 1) * sizeof(float));
+ memcpy(prev_frac, frac, (size_t)(ns_in + 1) * sizeof(float));
+ memcpy(prev_new_frac, new_frac, (size_t)(nseg + 1) * sizeof(float));
}
if (!odd) {
- vmesh_center(vm0, center);
- copy_v3_v3(mesh_vert(vm1, 0, nseg2, nseg2)->co, center);
+ vmesh_center(vm_in, center);
+ copy_v3_v3(mesh_vert(vm_out, 0, nseg2, nseg2)->co, center);
}
- vmesh_copy_equiv_verts(vm1);
- return vm1;
+ vmesh_copy_equiv_verts(vm_out);
+ return vm_out;
}
/* Do one step of cubic subdivision (Catmull-Clark), with special rules at boundaries.
* For now, this is written assuming vm0->nseg is even and > 0.
- * We are allowed to modify vm0, as it will not be used after this call.
+ * We are allowed to modify vm_in, as it will not be used after this call.
* See Levin 1999 paper: "Filling an N-sided hole using combined subdivision schemes". */
-static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm0)
+static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
{
- int n, ns0, ns20, ns1;
+ int n_boundary, ns_in, ns_in2, ns_out;
int i, j, k, inext;
float co[3], co1[3], co2[3], acc[3];
float beta, gamma;
- VMesh *vm1;
+ VMesh *vm_out;
BoundVert *bndv;
- n = vm0->count;
- ns0 = vm0->seg;
- ns20 = ns0 / 2;
- BLI_assert(ns0 % 2 == 0);
- ns1 = 2 * ns0;
- vm1 = new_adj_vmesh(bp->mem_arena, n, ns1, vm0->boundstart);
+ n_boundary = vm_in->count;
+ ns_in = vm_in->seg;
+ ns_in2 = ns_in / 2;
+ BLI_assert(ns_in % 2 == 0);
+ ns_out = 2 * ns_in;
+ vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart);
- /* First we adjust the boundary vertices of the input mesh, storing in output mesh */
- for (i = 0; i < n; i++) {
- copy_v3_v3(mesh_vert(vm1, i, 0, 0)->co, mesh_vert(vm0, i, 0, 0)->co);
- for (k = 1; k < ns0; k++) {
- /* smooth boundary rule */
- copy_v3_v3(co, mesh_vert(vm0, i, 0, k)->co);
- copy_v3_v3(co1, mesh_vert(vm0, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert(vm0, i, 0, k + 1)->co);
+ /* First we adjust the boundary vertices of the input mesh, storing in output mesh. */
+ for (i = 0; i < n_boundary; i++) {
+ copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co);
+ for (k = 1; k < ns_in; k++) {
+ copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
+ if (!bp->use_custom_profile) {
+ copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
- copy_v3_v3(mesh_vert_canon(vm1, i, 0, 2 * k)->co, co);
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
+
+ copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co);
}
}
- /* now do odd ones in output mesh, based on even ones */
- bndv = vm1->boundstart;
- for (i = 0; i < n; i++) {
- for (k = 1; k < ns1; k += 2) {
- get_profile_point(bp, &bndv->profile, k, ns1, co);
- copy_v3_v3(co1, mesh_vert_canon(vm1, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert_canon(vm1, i, 0, k + 1)->co);
+ /* Now adjust odd boundary vertices in output mesh, based on even ones. */
+ bndv = vm_out->boundstart;
+ for (i = 0; i < n_boundary; i++) {
+ for (k = 1; k < ns_out; k += 2) {
+ get_profile_point(bp, &bndv->profile, k, ns_out, co);
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ /* Smooth if using a non-custom profile. */
+ if (!bp->use_custom_profile) {
+ copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
- copy_v3_v3(mesh_vert_canon(vm1, i, 0, k)->co, co);
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
+
+ copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co);
}
bndv = bndv->next;
}
- vmesh_copy_equiv_verts(vm1);
+ vmesh_copy_equiv_verts(vm_out);
- /* Copy adjusted verts back into vm0 */
- for (i = 0; i < n; i++) {
- for (k = 0; k < ns0; k++) {
- copy_v3_v3(mesh_vert(vm0, i, 0, k)->co, mesh_vert(vm1, i, 0, 2 * k)->co);
+ /* Copy adjusted verts back into vm_in. */
+ for (i = 0; i < n_boundary; i++) {
+ for (k = 0; k < ns_in; k++) {
+ copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co);
}
}
- vmesh_copy_equiv_verts(vm0);
+ vmesh_copy_equiv_verts(vm_in);
/* Now we do the internal vertices, using standard Catmull-Clark
* and assuming all boundary vertices have valence 4 */
/* The new face vertices */
- for (i = 0; i < n; i++) {
- for (j = 0; j < ns20; j++) {
- for (k = 0; k < ns20; k++) {
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 0; j < ns_in2; j++) {
+ for (k = 0; k < ns_in2; k++) {
/* face up and right from (j, k) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j, k + 1),
- mesh_vert(vm0, i, j + 1, k),
- mesh_vert(vm0, i, j + 1, k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j + 1, 2 * k + 1)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j, k + 1),
+ mesh_vert(vm_in, i, j + 1, k),
+ mesh_vert(vm_in, i, j + 1, k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k + 1)->co, co);
}
}
}
- /* The new vertical edge vertices */
- for (i = 0; i < n; i++) {
- for (j = 0; j < ns20; j++) {
- for (k = 1; k <= ns20; k++) {
+ /* The new vertical edge vertices */
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 0; j < ns_in2; j++) {
+ for (k = 1; k <= ns_in2; k++) {
/* vertical edge between (j, k) and (j+1, k) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j + 1, k),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j + 1, 2 * k)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j + 1, k),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k)->co, co);
}
}
}
- /* The new horizontal edge vertices */
- for (i = 0; i < n; i++) {
- for (j = 1; j < ns20; j++) {
- for (k = 0; k < ns20; k++) {
+ /* The new horizontal edge vertices */
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 1; j < ns_in2; j++) {
+ for (k = 0; k < ns_in2; k++) {
/* horizontal edge between (j, k) and (j, k+1) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j, k + 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j, 2 * k + 1)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j, k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k + 1)->co, co);
}
}
}
@@ -3615,66 +3953,66 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm0)
/* The new vertices, not on border */
gamma = 0.25f;
beta = -gamma;
- for (i = 0; i < n; i++) {
- for (j = 1; j < ns20; j++) {
- for (k = 1; k <= ns20; k++) {
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 1; j < ns_in2; j++) {
+ for (k = 1; k <= ns_in2; k++) {
/* co1 = centroid of adjacent new edge verts */
avg4(co1,
- mesh_vert_canon(vm1, i, 2 * j, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k));
+ mesh_vert_canon(vm_out, i, 2 * j, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k));
/* co2 = centroid of adjacent new face verts */
avg4(co2,
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
/* combine with original vert with alpha, beta, gamma factors */
copy_v3_v3(co, co1); /* alpha = 1.0 */
madd_v3_v3fl(co, co2, beta);
- madd_v3_v3fl(co, mesh_vert(vm0, i, j, k)->co, gamma);
- copy_v3_v3(mesh_vert(vm1, i, 2 * j, 2 * k)->co, co);
+ madd_v3_v3fl(co, mesh_vert(vm_in, i, j, k)->co, gamma);
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k)->co, co);
}
}
}
- vmesh_copy_equiv_verts(vm1);
+ vmesh_copy_equiv_verts(vm_out);
/* The center vertex is special */
- gamma = sabin_gamma(n);
+ gamma = sabin_gamma(n_boundary);
beta = -gamma;
/* accumulate edge verts in co1, face verts in co2 */
zero_v3(co1);
zero_v3(co2);
- for (i = 0; i < n; i++) {
- add_v3_v3(co1, mesh_vert(vm1, i, ns0, ns0 - 1)->co);
- add_v3_v3(co2, mesh_vert(vm1, i, ns0 - 1, ns0 - 1)->co);
- add_v3_v3(co2, mesh_vert(vm1, i, ns0 - 1, ns0 + 1)->co);
+ for (i = 0; i < n_boundary; i++) {
+ add_v3_v3(co1, mesh_vert(vm_out, i, ns_in, ns_in - 1)->co);
+ add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in - 1)->co);
+ add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in + 1)->co);
}
copy_v3_v3(co, co1);
- mul_v3_fl(co, 1.0f / (float)n);
- madd_v3_v3fl(co, co2, beta / (2.0f * (float)n));
- madd_v3_v3fl(co, mesh_vert(vm0, 0, ns20, ns20)->co, gamma);
- for (i = 0; i < n; i++) {
- copy_v3_v3(mesh_vert(vm1, i, ns0, ns0)->co, co);
+ mul_v3_fl(co, 1.0f / (float)n_boundary);
+ madd_v3_v3fl(co, co2, beta / (2.0f * (float)n_boundary));
+ madd_v3_v3fl(co, mesh_vert(vm_in, 0, ns_in2, ns_in2)->co, gamma);
+ for (i = 0; i < n_boundary; i++) {
+ copy_v3_v3(mesh_vert(vm_out, i, ns_in, ns_in)->co, co);
}
- /* Final step: sample the boundary vertices at even parameter spacing */
- bndv = vm1->boundstart;
- for (i = 0; i < n; i++) {
- inext = (i + 1) % n;
- for (k = 0; k <= ns1; k++) {
- get_profile_point(bp, &bndv->profile, k, ns1, co);
- copy_v3_v3(mesh_vert(vm1, i, 0, k)->co, co);
- if (k >= ns0 && k < ns1) {
- copy_v3_v3(mesh_vert(vm1, inext, ns1 - k, 0)->co, co);
+ /* Final step: Copy the profile vertices to the VMesh's boundary */
+ bndv = vm_out->boundstart;
+ for (i = 0; i < n_boundary; i++) {
+ inext = (i + 1) % n_boundary;
+ for (k = 0; k <= ns_out; k++) {
+ get_profile_point(bp, &bndv->profile, k, ns_out, co);
+ copy_v3_v3(mesh_vert(vm_out, i, 0, k)->co, co);
+ if (k >= ns_in && k < ns_out) {
+ copy_v3_v3(mesh_vert(vm_out, inext, ns_out - k, 0)->co, co);
}
}
bndv = bndv->next;
}
- return vm1;
+ return vm_out;
}
/* Special case for cube corner, when r is PRO_SQUARE_R, meaning straight sides */
@@ -3686,7 +4024,7 @@ static VMesh *make_cube_corner_square(MemArena *mem_arena, int nseg)
ns2 = nseg / 2;
vm = new_adj_vmesh(mem_arena, 3, nseg, NULL);
- vm->count = 0; // reset, so following loop will end up with correct count
+ vm->count = 0; /* Reset, so the following loop will end up with correct count. */
for (i = 0; i < 3; i++) {
zero_v3(co);
co[i] = 1.0f;
@@ -3765,16 +4103,18 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
int i, j, k, ns2;
float co[3], coc[3];
- if (r == PRO_SQUARE_R) {
- return make_cube_corner_square(mem_arena, nseg);
- }
- else if (r == PRO_SQUARE_IN_R) {
- return make_cube_corner_square_in(mem_arena, nseg);
+ if (!bp->use_custom_profile) {
+ if (r == PRO_SQUARE_R) {
+ return make_cube_corner_square(mem_arena, nseg);
+ }
+ else if (r == PRO_SQUARE_IN_R) {
+ return make_cube_corner_square_in(mem_arena, nseg);
+ }
}
- /* initial mesh has 3 sides, 2 segments */
+ /* Initial mesh has 3 sides and 2 segments on each side */
vm0 = new_adj_vmesh(mem_arena, 3, 2, NULL);
- vm0->count = 0; // reset, so following loop will end up with correct count
+ vm0->count = 0; /* Reset, so the following loop will end up with correct count. */
for (i = 0; i < 3; i++) {
zero_v3(co);
co[i] = 1.0f;
@@ -3787,20 +4127,24 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
coc[(i + 1) % 3] = 1.0f;
coc[(i + 2) % 3] = 0.0f;
bndv->profile.super_r = r;
- copy_v3_v3(bndv->profile.coa, bndv->nv.co);
- copy_v3_v3(bndv->profile.cob, bndv->next->nv.co);
- copy_v3_v3(bndv->profile.midco, coc);
- copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->profile.coa);
- copy_v3_v3(bndv->profile.plane_co, bndv->profile.coa);
- cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.coa, bndv->profile.cob);
+ copy_v3_v3(bndv->profile.start, bndv->nv.co);
+ copy_v3_v3(bndv->profile.end, bndv->next->nv.co);
+ copy_v3_v3(bndv->profile.middle, coc);
+ copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->profile.start);
+ copy_v3_v3(bndv->profile.plane_co, bndv->profile.start);
+ cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.start, bndv->profile.end);
copy_v3_v3(bndv->profile.proj_dir, bndv->profile.plane_no);
- calculate_profile(bp, bndv);
+ /* No need to reverse the profile or use the miter profile spacing struct because this case
+ * isn't used with custom profiles. */
+ calculate_profile(bp, bndv, false, false);
+
+ /* Just building the boundaries here, so sample the profile halfway through */
get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
bndv = bndv->next;
}
/* center vertex */
- copy_v3_fl(co, M_SQRT1_3);
+ copy_v3_fl(co, (float)M_SQRT1_3);
if (nseg > 2) {
if (r > 1.5f) {
@@ -3843,7 +4187,8 @@ static int tri_corner_test(BevelParams *bp, BevVert *bv)
int i;
int in_plane_e = 0;
- if (bp->vertex_only) {
+ /* The superellipse snapping of this case isn't helpful with custom profiles enabled. */
+ if (bp->vertex_only || bp->use_custom_profile) {
return -1;
}
if (bv->vmesh->count != 3) {
@@ -3909,94 +4254,71 @@ static VMesh *tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
return vm;
}
+/* Makes the mesh that replaces the original vertex, bounded by the profiles on the sides */
static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
{
- int n, ns, i;
+ int n_bndv, nseg, i;
VMesh *vm0, *vm1;
- float co[3], coa[3], cob[3], dir[3];
+ float boundverts_center[3], original_vertex[3], negative_fullest[3], center_direction[3];
BoundVert *bndv;
MemArena *mem_arena = bp->mem_arena;
- float r, p, fullness;
- /* best fullness for circles, segs = 2,4,6,8,10 */
-#define CIRCLE_FULLNESS_SEGS 11
- static const float circle_fullness[CIRCLE_FULLNESS_SEGS] = {
- 0.0f, /* nsegs ==1 */
- 0.559f, /* 2 */
- 0.642f, /* 3 */
- 0.551f, /* 4 */
- 0.646f, /* 5 */
- 0.624f, /* 6 */
- 0.646f, /* 7 */
- 0.619f, /* 8 */
- 0.647f, /* 9 */
- 0.639f, /* 10 */
- 0.647f, /* 11 */
- };
+ float fullness;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
/* Same bevel as that of 3 edges of vert in a cube */
- if (n == 3 && tri_corner_test(bp, bv) != -1 && bp->pro_super_r != PRO_SQUARE_IN_R) {
+ if (n_bndv == 3 && tri_corner_test(bp, bv) != -1 && bp->pro_super_r != PRO_SQUARE_IN_R) {
return tri_corner_adj_vmesh(bp, bv);
}
- /* First construct an initial control mesh, with nseg==2 */
- ns = bv->vmesh->seg;
- vm0 = new_adj_vmesh(mem_arena, n, 2, bv->vmesh->boundstart);
+ /* First construct an initial control mesh, with nseg == 2. */
+ nseg = bv->vmesh->seg;
+ vm0 = new_adj_vmesh(mem_arena, n_bndv, 2, bv->vmesh->boundstart);
+ /* Find the center of the boundverts that make up the vmesh. */
bndv = vm0->boundstart;
- zero_v3(co);
- for (i = 0; i < n; i++) {
- /* Boundaries just divide input polygon edges into 2 even segments */
+ zero_v3(boundverts_center);
+ for (i = 0; i < n_bndv; i++) {
+ /* Boundaries just divide input polygon edges into 2 even segments. */
copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->nv.co);
get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
- add_v3_v3(co, bndv->nv.co);
+ add_v3_v3(boundverts_center, bndv->nv.co);
bndv = bndv->next;
}
- /* To place center vertex:
- * coa is original vertex
- * co is centroid of boundary corners
- * cob is reflection of coa in across co.
- * Calculate 'fullness' = fraction of way
- * from co to coa (if positive) or to cob (if negative).
- */
- copy_v3_v3(coa, bv->v->co);
- mul_v3_fl(co, 1.0f / (float)n);
- sub_v3_v3v3(cob, co, coa);
- add_v3_v3(cob, co);
-
- /* An offline optimization process found fullness that let to closest fit to sphere as
- * a function of r and ns (for case of cube corner) */
- r = bp->pro_super_r;
- p = bp->profile;
- if (r == PRO_LINE_R) {
- fullness = 0.0f;
- }
- else if (r == PRO_CIRCLE_R && ns > 0 && ns <= CIRCLE_FULLNESS_SEGS) {
- fullness = circle_fullness[ns - 1];
- }
- else {
- /* linear regression fit found best linear function, separately for even/odd segs */
- if (ns % 2 == 0) {
- fullness = 2.4506f * p - 0.00000300f * ns - 0.6266f;
+ mul_v3_fl(boundverts_center, 1.0f / (float)n_bndv);
+
+ /* To place the center vertex:
+ * 'negative_fullest' is the reflection of the original vertex across the boundverts' center.
+ * 'fullness' is the fraction of the way from the boundvert's centroid to to the original vertex
+ * (if positive) or to negative_fullest (if negative). */
+ copy_v3_v3(original_vertex, bv->v->co);
+ sub_v3_v3v3(negative_fullest, boundverts_center, original_vertex);
+ add_v3_v3(negative_fullest, boundverts_center);
+
+ /* Find the vertex mesh's start center with the profile's fullness */
+ fullness = bp->pro_spacing.fullness;
+ sub_v3_v3v3(center_direction, original_vertex, boundverts_center);
+ if (len_squared_v3(center_direction) > BEVEL_EPSILON_SQ) {
+ if (bp->use_custom_profile) {
+ fullness *= 2.0f;
+ madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, negative_fullest, center_direction, fullness);
}
else {
- fullness = 2.3635f * p + 0.000152f * ns - 0.6060f;
+ madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center, center_direction, fullness);
}
}
- sub_v3_v3v3(dir, coa, co);
- if (len_squared_v3(dir) > BEVEL_EPSILON_SQ) {
- madd_v3_v3fl(co, dir, fullness);
+ else {
+ copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center);
}
- copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, co);
vmesh_copy_equiv_verts(vm0);
+ /* Do the subdivision process to go from the two segment start mesh to the final vertex mesh. */
vm1 = vm0;
do {
vm1 = cubic_subdiv(bp, vm1);
- } while (vm1->seg < ns);
- if (vm1->seg != ns) {
- vm1 = interp_vmesh(bp, vm1, ns);
+ } while (vm1->seg < nseg);
+ if (vm1->seg != nseg) {
+ vm1 = interp_vmesh(bp, vm1, nseg);
}
return vm1;
}
@@ -4012,15 +4334,16 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
Profile *pro = &vpipe->profile;
EdgeHalf *e = vpipe->ebev;
- copy_v3_v3(va, pro->coa);
- copy_v3_v3(vb, pro->cob);
+ copy_v3_v3(va, pro->start);
+ copy_v3_v3(vb, pro->end);
+ /* Get a plane with the normal pointing along the beveled edge */
sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co);
-
plane_from_point_normal_v3(plane, co, edir);
+
closest_to_plane_v3(va0, plane, va);
closest_to_plane_v3(vb0, plane, vb);
- closest_to_plane_v3(vmid0, plane, pro->midco);
+ closest_to_plane_v3(vmid0, plane, pro->middle);
if (make_unit_square_map(va0, vmid0, vb0, m)) {
/* Transform co and project it onto superellipse */
if (!invert_m4_m4(minv, m)) {
@@ -4030,6 +4353,7 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
}
mul_v3_m4v3(p, minv, co);
snap_to_superellipsoid(p, pro->super_r, midline);
+
mul_v3_m4v3(snap, m, p);
copy_v3_v3(co, snap);
}
@@ -4040,38 +4364,123 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
}
}
-/* See pipe_test for conditions that make 'pipe'; vpipe is the return value from that.
+/** See pipe_test for conditions that make 'pipe'; vpipe is the return value from that.
* We want to make an ADJ mesh but then snap the vertices to the profile in a plane
- * perpendicular to the pipes.
- * A tricky case is for the 'square' profiles and an even nseg: we want certain vertices
- * to snap to the midline on the pipe, not just to one plane or the other. */
+ * perpendicular to the pipes. */
static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
{
- int i, j, k, n, ns, ns2, ipipe1, ipipe2;
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("PIPE ADJ VMESH\n");
+ float green[4] = {0.0f, 1.0f, 0.0f, 1.0f};
+ float blue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+ float red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+ float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ float *color;
+#endif
+ int i, j, k, n_bndv, ns, half_ns, ipipe1, ipipe2, ring;
VMesh *vm;
bool even, midline;
+ float *profile_point_pipe1, *profile_point_pipe2, f;
+ /* Some unecessary overhead running this subdivision with custom profile snapping later on. */
vm = adj_vmesh(bp, bv);
- /* Now snap all interior coordinates to be on the epipe profile */
- n = bv->vmesh->count;
+ /* Now snap all interior coordinates to be on the epipe profile. */
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
- ns2 = ns / 2;
+ half_ns = ns / 2;
even = (ns % 2) == 0;
ipipe1 = vpipe->index;
ipipe2 = vpipe->next->next->index;
- for (i = 0; i < n; i++) {
- for (j = 1; j <= ns2; j++) {
- for (k = 0; k <= ns2; k++) {
+
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("ipipe1: %d\n", ipipe1);
+ printf("ipipe2: %d\n", ipipe2);
+#endif
+
+ for (i = 0; i < n_bndv; i++) {
+ for (j = 1; j <= half_ns; j++) {
+ for (k = 0; k <= half_ns; k++) {
if (!is_canon(vm, i, j, k)) {
continue;
}
- midline = even && k == ns2 && ((i == 0 && j == ns2) || (i == ipipe1 || i == ipipe2));
- snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co);
+ if (bp->use_custom_profile) {
+ /* Find both profile vertices that correspond to this point. */
+ if (i == ipipe1 || i == ipipe2) {
+ if (n_bndv == 3 && i == ipipe1) {
+ /* This part of the vmesh is the triangular corner between the two pipe profiles. */
+ ring = max_ii(j, k);
+ profile_point_pipe2 = mesh_vert(vm, i, 0, ring)->co;
+ profile_point_pipe1 = mesh_vert(vm, i, ring, 0)->co;
+ /* End profile index increases with k on one side and j on the other. */
+ f = ((k < j) ? min_ff(j, k) : ((2.0f * ring) - j)) / (2.0f * ring);
+ }
+ else {
+ /* This is part of either pipe profile boundvert area in the 4-way intersection. */
+ profile_point_pipe1 = mesh_vert(vm, i, 0, k)->co;
+ profile_point_pipe2 = mesh_vert(vm, (i == ipipe1) ? ipipe2 : ipipe1, 0, ns - k)->co;
+ f = (float)j / (float)ns; /* The ring index brings us closer to the other side. */
+ }
+ }
+ else {
+ /* The profile vertices are on both ends of each of the side profile's rings. */
+ profile_point_pipe1 = mesh_vert(vm, i, j, 0)->co;
+ profile_point_pipe2 = mesh_vert(vm, i, j, ns)->co;
+ f = (float)k / (float)ns; /* Ring runs along the pipe, so segment is used here. */
+ }
+
+ /* Place the vertex by interpolatin between the two profile points using the factor. */
+ interp_v3_v3v3(mesh_vert(vm, i, j, k)->co, profile_point_pipe1, profile_point_pipe2, f);
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("(%d, %d, %d)\n", i, j, k);
+ printf("f: %.3f\n", f);
+ printf("point 1: (%.3f, %.3f, %.3f)\n",
+ profile_point_pipe1[0],
+ profile_point_pipe1[1],
+ profile_point_pipe1[2]);
+ printf("point 2: (%.3f, %.3f, %.3f)\n",
+ profile_point_pipe2[0],
+ profile_point_pipe2[1],
+ profile_point_pipe2[2]);
+#endif
+ }
+ else {
+ /* A tricky case is for the 'square' profiles and an even nseg: we want certain
+ * vertices to snap to the midline on the pipe, not just to one plane or the other. */
+ midline = even && k == half_ns &&
+ ((i == 0 && j == half_ns) || (i == ipipe1 || i == ipipe2));
+ snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co);
+ }
}
}
}
-
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ /* Draw the locations of all the vertices after the "snapping" process */
+ for (i = 0; i < n_bndv; i++) {
+ for (j = 1; j <= half_ns; j++) {
+ for (k = 1; k <= ns; k++) {
+ if (!is_canon(vm, i, j, k)) {
+ continue;
+ }
+ switch (i) {
+ case 0:
+ color = red;
+ break;
+ case 1:
+ color = green;
+ break;
+ case 2:
+ color = blue;
+ break;
+ case 3:
+ color = white;
+ break;
+ }
+ DRW_debug_sphere(mesh_vert(vm, i, j, k)->co, 0.01f, color);
+ }
+ }
+ }
+#endif
return vm;
}
@@ -4123,7 +4532,7 @@ static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, flo
BMEdge *e;
float closest[3];
- beste_d2 = 1e20;
+ beste_d2 = 1e20f;
BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
closest_to_line_segment_v3(closest, co, e->v1->co, e->v2->co);
d2 = len_squared_v3v3(closest, co);
@@ -4243,7 +4652,7 @@ static void closer_v3_v3v3v3(float r[3], float a[3], float b[3], float v[3])
*/
static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
{
- int n, ns, ns2, odd, i, j, k, ikind, im1, clstride, iprev, akind;
+ int n_bndv, ns, ns2, odd, i, j, k, ikind, im1, clstride, iprev, ang_kind;
float bndco[3], dir1[3], dir2[3], co1[3], co2[3], meet1[3], meet2[3], v1co[3], v2co[3];
float *on_edge_cur, *on_edge_prev, *p;
float ns2inv, finalfrac, ang;
@@ -4253,26 +4662,26 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
float *centerline;
bool *cset, v1set, v2set;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
ns2 = ns / 2;
odd = ns % 2;
ns2inv = 1.0f / (float)ns2;
- vm = new_adj_vmesh(bp->mem_arena, n, ns, bv->vmesh->boundstart);
+ vm = new_adj_vmesh(bp->mem_arena, n_bndv, ns, bv->vmesh->boundstart);
clstride = 3 * (ns2 + 1);
- centerline = MEM_mallocN(clstride * n * sizeof(float), "bevel");
- cset = MEM_callocN(n * sizeof(bool), "bevel");
+ centerline = MEM_mallocN((size_t)(clstride * n_bndv) * sizeof(float), "bevel");
+ cset = MEM_callocN((size_t)n_bndv * sizeof(bool), "bevel");
/* find on_edge, place on bndv[i]'s elast where offset line would meet,
* taking min-distance-to bv->v with position where next sector's offset line would meet */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
copy_v3_v3(bndco, bndv->nv.co);
e1 = bndv->efirst;
e2 = bndv->elast;
- akind = 0;
+ ang_kind = ANGLE_STRAIGHT;
if (e1 && e2) {
- akind = edges_angle_kind(e1, e2, bv->v);
+ ang_kind = edges_angle_kind(e1, e2, bv->v);
}
if (bndv->is_patch_start) {
mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
@@ -4288,13 +4697,13 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
else if (bndv->is_arc_start) {
e1 = bndv->efirst;
e2 = bndv->next->efirst;
- copy_v3_v3(centerline + clstride * i, bndv->profile.midco);
+ copy_v3_v3(centerline + clstride * i, bndv->profile.middle);
bndv = bndv->next;
cset[i] = true;
i++;
/* leave cset[i] where it was - probably false, unless i == n - 1 */
}
- else if (akind < 0) {
+ else if (ang_kind == ANGLE_SMALLER) {
sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
add_v3_v3v3(co1, bndco, dir1);
@@ -4321,7 +4730,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration */
on_edge_cur = centerline + clstride * i;
- iprev = (i == 0) ? n - 1 : i - 1;
+ iprev = (i == 0) ? n_bndv - 1 : i - 1;
on_edge_prev = centerline + clstride * iprev;
if (v2set) {
if (cset[i]) {
@@ -4346,7 +4755,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
}
/* Maybe not everything was set by the previous loop */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
if (!cset[i]) {
on_edge_cur = centerline + clstride * i;
e1 = bndv->next->efirst;
@@ -4381,7 +4790,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* fill in rest of centerlines by interpolation */
copy_v3_v3(co2, bv->v->co);
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
if (odd) {
ang = 0.5f * angle_v3v3v3(bndv->nv.co, co1, bndv->next->nv.co);
if (ang > BEVEL_SMALL_ANG) {
@@ -4389,7 +4798,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
* such that the base of the triangle is 1.
* This is used in interpolation along centerline in odd case.
* To avoid too big a drop from bv, cap finalfrac a 0.8 arbitrarily */
- finalfrac = 0.5f / sin(ang);
+ finalfrac = 0.5f / sinf(ang);
if (finalfrac > 0.8f) {
finalfrac = 0.8f;
}
@@ -4412,9 +4821,9 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* coords of edges and mid or near-mid line */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
copy_v3_v3(co1, bndv->nv.co);
- copy_v3_v3(co2, centerline + clstride * (i == 0 ? n - 1 : i - 1));
+ copy_v3_v3(co2, centerline + clstride * (i == 0 ? n_bndv - 1 : i - 1));
for (j = 0; j < ns2 + odd; j++) {
interp_v3_v3v3(mesh_vert(vm, i, j, 0)->co, co1, co2, j * ns2inv);
}
@@ -4431,8 +4840,8 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* fill in interior points by interpolation from edges to centerlines */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
- im1 = (i == 0) ? n - 1 : i - 1;
+ for (i = 0; i < n_bndv; i++) {
+ im1 = (i == 0) ? n_bndv - 1 : i - 1;
for (j = 1; j < ns2 + odd; j++) {
for (k = 1; k <= ns2; k++) {
ikind = isect_line_line_v3(mesh_vert(vm, i, 0, k)->co,
@@ -4466,43 +4875,43 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
return vm;
}
-/*
+/**
* Given that the boundary is built and the boundary BMVerts have been made,
* calculate the positions of the interior mesh points for the M_ADJ pattern,
- * using cubic subdivision, then make the BMVerts and the new faces. */
-static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
+ * using cubic subdivision, then make the BMVerts and the new faces.
+ */
+static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert *vpipe)
{
- int n, ns, ns2, odd, i, j, k, ring;
+ int n_bndv, ns, ns2, odd, i, j, k, ring;
VMesh *vm1, *vm;
- BoundVert *v;
+ BoundVert *bndv;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
BMFace *f, *f2, *r_f;
BMEdge *bme, *bme1, *bme2, *bme3;
EdgeHalf *e;
- BoundVert *vpipe;
int mat_nr = bp->mat_nr;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
ns2 = ns / 2;
odd = ns % 2;
- BLI_assert(n >= 3 && ns > 1);
+ BLI_assert(n_bndv >= 3 && ns > 1);
- /* Add support for profiles in vertex only in-plane bevels */
+ /* Add support for profiles in vertex only in-plane bevels. */
if (bp->vertex_only) {
- v = bv->vmesh->boundstart;
+ bndv = bv->vmesh->boundstart;
do {
- Profile *pro = &v->profile;
+ Profile *pro = &bndv->profile;
+ copy_v3_v3(pro->middle, bv->v->co);
pro->super_r = bp->pro_super_r;
- copy_v3_v3(pro->midco, bv->v->co);
- calculate_profile(bp, v);
- v = v->next;
- } while (v != bv->vmesh->boundstart);
+ bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start);
+ /* Orientation doesn't matter when only beveling vertices */
+ calculate_profile(bp, bndv, false, miter_profile);
+ bndv = bndv->next;
+ } while (bndv != bv->vmesh->boundstart);
}
- vpipe = pipe_test(bv);
-
- if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd) {
+ if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd && !bp->use_custom_profile) {
vm1 = square_out_adj_vmesh(bp, bv);
}
else if (vpipe) {
@@ -4512,7 +4921,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
vm1 = tri_corner_adj_vmesh(bp, bv);
/* the PRO_SQUARE_IN_R profile has boundary edges that merge
* and no internal ring polys except possibly center ngon */
- if (bp->pro_super_r == PRO_SQUARE_IN_R) {
+ if (bp->pro_super_r == PRO_SQUARE_IN_R && !bp->use_custom_profile) {
build_square_in_vmesh(bp, bm, bv, vm1);
return;
}
@@ -4523,7 +4932,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
/* copy final vmesh into bv->vmesh, make BMVerts and BMFaces */
vm = bv->vmesh;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
for (j = 0; j <= ns2; j++) {
for (k = 0; k <= ns; k++) {
if (j == 0 && (k == 0 || k == ns)) {
@@ -4539,16 +4948,16 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
vmesh_copy_equiv_verts(vm);
/* make the polygons */
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- i = v->index;
- f = boundvert_rep_face(v, NULL);
- f2 = boundvert_rep_face(v->next, NULL);
+ i = bndv->index;
+ f = boundvert_rep_face(bndv, NULL);
+ f2 = boundvert_rep_face(bndv->next, NULL);
if (bp->vertex_only) {
- e = v->efirst;
+ e = bndv->efirst;
}
else {
- e = v->ebev;
+ e = bndv->ebev;
}
bme = e ? e->e : NULL;
/* For odd ns, make polys with lower left corner at (i,j,k) for
@@ -4576,7 +4985,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
f2,
NULL,
NULL,
- v->next->efirst->e,
+ bndv->next->efirst->e,
bme,
mat_nr);
}
@@ -4617,8 +5026,8 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
else {
bme1 = k == ns2 - 1 ? bme : NULL;
bme3 = NULL;
- if (j == ns2 - 1 && v->prev->ebev) {
- bme3 = v->prev->ebev->e;
+ if (j == ns2 - 1 && bndv->prev->ebev) {
+ bme3 = bndv->prev->ebev->e;
}
bme2 = bme1 != NULL ? bme1 : bme3;
r_f = bev_create_quad_ex(
@@ -4628,14 +5037,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
record_face_kind(bp, r_f, F_VERT);
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
/* Fix UVs along center lines if even number of segments */
if (!odd) {
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- i = v->index;
- if (!v->any_seam) {
+ i = bndv->index;
+ if (!bndv->any_seam) {
for (ring = 1; ring < ns2; ring++) {
BMVert *v_uv = mesh_vert(vm, i, ring, ns2)->v;
if (v_uv) {
@@ -4643,7 +5052,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
bmv1 = mesh_vert(vm, 0, ns2, ns2)->v;
if (bp->vertex_only || count_bound_vert_seams(bv) <= 1) {
bev_merge_uvs(bm, bmv1);
@@ -4656,6 +5065,179 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
+/** Builds the vertex mesh when the vertex mesh type is set to "cut off" with a face closing
+ * off each incoming edge's profile */
+/* HANS-TODO: Make cutoff VMesh work with outer miter != sharp. This should be possible but there
+ * are two problems currently:
+ * - Miter profiles don't have plane_no filled, so down direction is incorrect.
+ * - Indexing profile points of miters with (i, 0, k) seems to return zero except for the first
+ * and last profile point. */
+/* HANS-TODO: Use repface / edge arrays for UV interpolation properly. */
+static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
+{
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("BEVEL BUILD CUTOFF\n");
+ int j;
+#endif
+ int i;
+ int n_bndv = bv->vmesh->count;
+ BoundVert *bndv;
+ float length;
+ float down_direction[3], new_vert[3];
+ bool build_center_face;
+ /* BMFace *repface; */
+ BMVert **face_bmverts = NULL;
+ BMEdge **bmedges = NULL;
+ BMFace **bmfaces = NULL;
+
+ /* Find the locations for the corner vertices at the bottom of the cutoff faces. */
+ bndv = bv->vmesh->boundstart;
+ do {
+ i = bndv->index;
+
+ /* Find the "down" direction for this side of the cutoff face. */
+ /* Find the direction along the intersection of the two adjecent profile normals. */
+ cross_v3_v3v3(down_direction, bndv->profile.plane_no, bndv->prev->profile.plane_no);
+ if (dot_v3v3(down_direction, bv->v->no) > 0.0f) {
+ negate_v3(down_direction);
+ }
+
+ /* Move down from the boundvert by average profile height from the two adjacent profiles. */
+ length = (bndv->profile.height / sqrtf(2.0f) + bndv->prev->profile.height / sqrtf(2.0f)) / 2;
+ madd_v3_v3v3fl(new_vert, bndv->nv.co, down_direction, length);
+
+ /* Use this location for this profile's first corner vert and the last profile's second. */
+ copy_v3_v3(mesh_vert(bv->vmesh, i, 1, 0)->co, new_vert);
+ copy_v3_v3(mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->co, new_vert);
+
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Corner vertices:\n");
+ for (j = 0; j < n_bndv; j++) {
+ printf(" (%.3f, %.3f, %.3f)\n",
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[0],
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[1],
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[2]);
+ }
+#endif
+
+ /* Disable the center face if the corner vertices share the same location. */
+ build_center_face = true;
+ if (n_bndv == 3) { /* Vertices only collapse with a 3-way VMesh. */
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
+ mesh_vert(bv->vmesh, 1, 1, 0)->co) > BEVEL_EPSILON;
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
+ mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 1, 1, 0)->co,
+ mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
+ }
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("build_center_face: %d\n", build_center_face);
+#endif
+
+ /* Create the corner vertex BMVerts. */
+ if (build_center_face) {
+ do {
+ i = bndv->index;
+ create_mesh_bmvert(bm, bv->vmesh, i, 1, 0, bv->v);
+ /* The second corner vertex for the previous profile shares this BMVert. */
+ mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->v = mesh_vert(bv->vmesh, i, 1, 0)->v;
+
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+ }
+ else {
+ /* Use the same BMVert for all of the corner vertices. */
+ create_mesh_bmvert(bm, bv->vmesh, 0, 1, 0, bv->v);
+ for (i = 1; i < n_bndv; i++) {
+ mesh_vert(bv->vmesh, i, 1, 0)->v = mesh_vert(bv->vmesh, 0, 1, 0)->v;
+ }
+ }
+
+ /* Build the profile cutoff faces. */
+ /* Extra one or two for corner vertices and one for last point along profile, or the size of the
+ * center face array if it's bigger. */
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Building profile cutoff faces.\n");
+#endif
+ face_bmverts = BLI_memarena_alloc(
+ bp->mem_arena, ((size_t)max_ii(bp->seg + 2 + build_center_face, n_bndv) * sizeof(BMVert *)));
+ bndv = bv->vmesh->boundstart;
+ do {
+ i = bndv->index;
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
+
+ /* Add the first corner vertex under this boundvert */
+ face_bmverts[0] = mesh_vert(bv->vmesh, i, 1, 0)->v;
+
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Profile Number %d:\n", i);
+ if (bndv->is_patch_start || bndv->is_arc_start) {
+ printf(" Miter profile\n");
+ }
+ printf(" Corner 1: (%0.3f, %0.3f, %0.3f)\n",
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[2]);
+#endif
+
+ /* Add profile point vertices to the face, including the last one. */
+ for (int k = 0; k < bp->seg + 1; k++) {
+ face_bmverts[k + 1] = mesh_vert(bv->vmesh, i, 0, k)->v; /* Leave room for first vert. */
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf(" Profile %d: (%0.3f, %0.3f, %0.3f)\n",
+ k,
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[2]);
+#endif
+ }
+
+ /* Add the second corner vert to complete the bottom of the face. */
+ if (build_center_face) {
+ face_bmverts[bp->seg + 2] = mesh_vert(bv->vmesh, i, 1, 1)->v;
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf(" Corner 2: (%0.3f, %0.3f, %0.3f)\n",
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[2]);
+#endif
+ }
+
+ /* Create the profile cutoff face for this boundvert. */
+ /* repface = boundvert_rep_face(bndv, NULL); */
+ bev_create_ngon(bm,
+ face_bmverts,
+ bp->seg + 2 + build_center_face,
+ bmfaces,
+ NULL,
+ bmedges,
+ bp->mat_nr,
+ true);
+
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+
+ /* Create the bottom face if it should be built, reusing previous face_bmverts allocation. */
+ if (build_center_face) {
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
+
+ /* Add all of the corner vertices to this face. */
+ for (i = 0; i < n_bndv; i++) {
+ /* Add verts from each cutoff face. */
+ face_bmverts[i] = mesh_vert(bv->vmesh, i, 1, 0)->v;
+ }
+ /* BLI_array_append(bmfaces, repface); */
+ bev_create_ngon(bm, face_bmverts, n_bndv, bmfaces, NULL, bmedges, bp->mat_nr, true);
+
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
+ }
+}
+
/* If we make a poly out of verts around bv, snapping to rep frep, will uv poly have zero area?
* The uv poly is made by snapping all outside-of-frep vertices to the closest edge in frep.
* Assume that this function is called when the only inside-of-frep vertex is vm->boundstart.
@@ -4690,71 +5272,72 @@ static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
{
- BMFace *f, *frep, *frep2;
+ BMFace *f, *repface, *frep2;
int n, k;
VMesh *vm = bv->vmesh;
- BoundVert *v;
- BMEdge *frep_e1, *frep_e2, *frep_e;
- BMVert **vv = NULL;
- BMFace **vf = NULL;
- BMEdge **ve = NULL;
- BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
- BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE);
- BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE);
+ BoundVert *bndv;
+ BMEdge *repface_e1, *repface_e2, *frep_e;
+ BMVert **bmverts = NULL;
+ BMEdge **bmedges = NULL;
+ BMFace **bmfaces = NULL;
+ BLI_array_staticdeclare(bmverts, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
if (bv->any_seam) {
- frep = boundvert_rep_face(vm->boundstart, &frep2);
- if (frep2 && frep && is_bad_uv_poly(bv, frep)) {
- frep = frep2;
+ repface = boundvert_rep_face(vm->boundstart, &frep2);
+ if (frep2 && repface && is_bad_uv_poly(bv, repface)) {
+ repface = frep2;
}
- get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
+ get_incident_edges(repface, bv->v, &repface_e1, &repface_e2);
}
else {
- frep = NULL;
- frep_e1 = frep_e2 = NULL;
+ repface = NULL;
+ repface_e1 = repface_e2 = NULL;
}
- v = vm->boundstart;
+ bndv = vm->boundstart;
n = 0;
do {
/* accumulate vertices for vertex ngon */
/* also accumulate faces in which uv interpolation is to happen for each */
- BLI_array_append(vv, v->nv.v);
- if (frep) {
- BLI_array_append(vf, frep);
- frep_e = find_closer_edge(v->nv.v->co, frep_e1, frep_e2);
- BLI_array_append(ve, n > 0 ? frep_e : NULL);
+ BLI_array_append(bmverts, bndv->nv.v);
+ if (repface) {
+ BLI_array_append(bmfaces, repface);
+ frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2);
+ BLI_array_append(bmedges, n > 0 ? frep_e : NULL);
}
else {
- BLI_array_append(vf, boundvert_rep_face(v, NULL));
- BLI_array_append(ve, NULL);
+ BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
+ BLI_array_append(bmedges, NULL);
}
n++;
- if (v->ebev && v->ebev->seg > 1) {
- for (k = 1; k < v->ebev->seg; k++) {
- BLI_array_append(vv, mesh_vert(vm, v->index, 0, k)->v);
- if (frep) {
- BLI_array_append(vf, frep);
- frep_e = find_closer_edge(mesh_vert(vm, v->index, 0, k)->v->co, frep_e1, frep_e2);
- BLI_array_append(ve, k < v->ebev->seg / 2 ? NULL : frep_e);
+ if (bndv->ebev && bndv->ebev->seg > 1) {
+ for (k = 1; k < bndv->ebev->seg; k++) {
+ BLI_array_append(bmverts, mesh_vert(vm, bndv->index, 0, k)->v);
+ if (repface) {
+ BLI_array_append(bmfaces, repface);
+ frep_e = find_closer_edge(
+ mesh_vert(vm, bndv->index, 0, k)->v->co, repface_e1, repface_e2);
+ BLI_array_append(bmedges, k < bndv->ebev->seg / 2 ? NULL : frep_e);
}
else {
- BLI_array_append(vf, boundvert_rep_face(v, NULL));
- BLI_array_append(ve, NULL);
+ BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
+ BLI_array_append(bmedges, NULL);
}
n++;
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
if (n > 2) {
- f = bev_create_ngon(bm, vv, n, vf, frep, ve, bp->mat_nr, true);
+ f = bev_create_ngon(bm, bmverts, n, bmfaces, repface, bmedges, bp->mat_nr, true);
record_face_kind(bp, f, F_VERT);
}
else {
f = NULL;
}
- BLI_array_free(vv);
- BLI_array_free(vf);
- BLI_array_free(ve);
+ BLI_array_free(bmverts);
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
return f;
}
@@ -4817,6 +5400,7 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
* we have to make it here. */
static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
{
+
VMesh *vm = bv->vmesh;
BMVert *v1, *v2;
BMEdge *e_eg, *bme;
@@ -4832,18 +5416,20 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
ns = vm->seg;
if (ns > 1) {
- /* Set up profile parameters */
+ /* Set up profile parameters. */
bndv = vm->boundstart;
pro = &bndv->profile;
pro->super_r = bp->pro_super_r;
- copy_v3_v3(pro->coa, v1->co);
- copy_v3_v3(pro->cob, v2->co);
- copy_v3_v3(pro->midco, bv->v->co);
- /* don't use projection */
+ copy_v3_v3(pro->start, v1->co);
+ copy_v3_v3(pro->end, v2->co);
+ copy_v3_v3(pro->middle, bv->v->co);
+ /* Don't use projection. */
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
zero_v3(pro->proj_dir);
- calculate_profile(bp, bndv);
+ /* there's no orientation chain to continue so the orientation of the bevel doesn't matter. */
+ calculate_profile(bp, bndv, false, false);
+
for (k = 1; k < ns; k++) {
get_profile_point(bp, pro, k, ns, co);
copy_v3_v3(mesh_vert(vm, 0, 0, k)->co, co);
@@ -4874,84 +5460,129 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
* for the boundary and the interior of the vertex mesh. */
static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
{
- MemArena *mem_arena = bp->mem_arena;
VMesh *vm = bv->vmesh;
- BoundVert *v, *weld1, *weld2;
+ BoundVert *bndv, *weld1, *weld2, *vpipe;
int n, ns, ns2, i, k, weld;
- float *va, *vb, co[3];
+ float *v_weld1, *v_weld2, co[3];
n = vm->count;
ns = vm->seg;
ns2 = ns / 2;
- vm->mesh = (NewVert *)BLI_memarena_alloc(mem_arena, n * (ns2 + 1) * (ns + 1) * sizeof(NewVert));
+ vm->mesh = (NewVert *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(n * (ns2 + 1) * (ns + 1)) * sizeof(NewVert));
- /* special case: two beveled ends welded together */
+ /* Special case: just two beveled edges welded together. */
weld = (bv->selcount == 2) && (vm->count == 2);
- weld1 = weld2 = NULL; /* will hold two BoundVerts involved in weld */
+ weld1 = weld2 = NULL; /* Will hold two BoundVerts involved in weld. */
- /* make (i, 0, 0) mesh verts for all i */
- v = vm->boundstart;
+ /* Make (i, 0, 0) mesh verts for all i boundverts. */
+ bndv = vm->boundstart;
do {
- i = v->index;
- copy_v3_v3(mesh_vert(vm, i, 0, 0)->co, v->nv.co);
- create_mesh_bmvert(bm, vm, i, 0, 0, bv->v);
- v->nv.v = mesh_vert(vm, i, 0, 0)->v;
- if (weld && v->ebev) {
+ i = bndv->index;
+ copy_v3_v3(mesh_vert(vm, i, 0, 0)->co, bndv->nv.co); /* Mesh NewVert to boundary NewVert. */
+ create_mesh_bmvert(bm, vm, i, 0, 0, bv->v); /* Create BMVert for that NewVert. */
+ bndv->nv.v = mesh_vert(vm, i, 0, 0)->v; /* Use the BMVert for the BoundVert's NewVert. */
+
+ /* Find boundverts and move profile planes if this is a weld case. */
+ if (weld && bndv->ebev) {
if (!weld1) {
- weld1 = v;
+ weld1 = bndv;
}
- else {
- weld2 = v;
- move_weld_profile_planes(bv, weld1, weld2);
- calculate_profile(bp, weld1);
- calculate_profile(bp, weld2);
+ else { /* Get the last of the two BoundVerts. */
+ weld2 = bndv;
+ move_weld_profile_planes(bv, weld1, weld2); /* Profile recalculated in next loop. */
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
- /* copy other ends to (i, 0, ns) for all i, and fill in profiles for edges */
- v = vm->boundstart;
+ /* Create new vertices and place them based on the profiles. */
+ /* Copy other ends to (i, 0, ns) for all i, and fill in profiles for edges. */
+ bndv = vm->boundstart;
do {
- i = v->index;
- copy_mesh_vert(vm, i, 0, ns, v->next->index, 0, 0);
- for (k = 1; k < ns; k++) {
- if (v->ebev && vm->mesh_kind != M_ADJ) {
- get_profile_point(bp, &v->profile, k, ns, co);
- copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
- if (!weld) {
- create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
+ i = bndv->index;
+ /* bndv's last vert along the boundary arc is the the first of the next BoundVert's arc. */
+ copy_mesh_vert(vm, i, 0, ns, bndv->next->index, 0, 0);
+
+ /* Fix the profile orientations if it's not a miter profile. */
+ if (bp->use_custom_profile && !bndv->is_arc_start && !bndv->is_patch_start) {
+ calculate_profile(bp, bndv, !bndv->is_profile_start, false);
+ }
+ if (vm->mesh_kind != M_ADJ) {
+ for (k = 1; k < ns; k++) {
+ if (bndv->ebev) {
+ get_profile_point(bp, &bndv->profile, k, ns, co);
+ copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co); /* Get NewVert location from profile coord */
+ if (!weld) {
+ /* This is done later with (possibly) better positions for the weld case. */
+ create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
+ }
+ }
+ else if (n == 2 && !bndv->ebev) {
+ /* case of one edge beveled and this is the v without ebev */
+ /* want to copy the verts from other v, in reverse order */
+ copy_mesh_vert(bv->vmesh, i, 0, k, 1 - i, 0, ns - k);
}
- }
- else if (n == 2 && !v->ebev && vm->mesh_kind != M_ADJ) {
- /* case of one edge beveled and this is the v without ebev */
- /* want to copy the verts from other v, in reverse order */
- copy_mesh_vert(vm, i, 0, k, 1 - i, 0, ns - k);
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
+ /* Build the profile for the weld case (just a connection between the two boundverts). */
if (weld) {
- vm->mesh_kind = M_NONE;
+ bv->vmesh->mesh_kind = M_NONE;
for (k = 1; k < ns; k++) {
- va = mesh_vert(vm, weld1->index, 0, k)->co;
- vb = mesh_vert(vm, weld2->index, 0, ns - k)->co;
- /* if one of the profiles is on a flat plane,
- * just use the boundary point of the other */
- if (weld1->profile.super_r == PRO_LINE_R && weld2->profile.super_r != PRO_LINE_R) {
- copy_v3_v3(co, vb);
- }
- else if (weld2->profile.super_r == PRO_LINE_R && weld1->profile.super_r != PRO_LINE_R) {
- copy_v3_v3(co, va);
+ v_weld1 = mesh_vert(bv->vmesh, weld1->index, 0, k)->co;
+ v_weld2 = mesh_vert(bv->vmesh, weld2->index, 0, ns - k)->co;
+ if (bp->use_custom_profile) {
+ /* Don't bother with special case profile check from below. */
+ mid_v3_v3v3(co, v_weld1, v_weld2);
}
else {
- mid_v3_v3v3(co, va, vb);
+ /* Use the point from the other profile if one is in a special case. */
+ if (weld1->profile.super_r == PRO_LINE_R && weld2->profile.super_r != PRO_LINE_R) {
+ copy_v3_v3(co, v_weld2);
+ }
+ else if (weld2->profile.super_r == PRO_LINE_R && weld1->profile.super_r != PRO_LINE_R) {
+ copy_v3_v3(co, v_weld1);
+ }
+ else {
+ /* In case the profiles aren't snapped to the same plane, use their midpoint. */
+ mid_v3_v3v3(co, v_weld1, v_weld2);
+ }
}
- copy_v3_v3(mesh_vert(vm, weld1->index, 0, k)->co, co);
- create_mesh_bmvert(bm, vm, weld1->index, 0, k, bv->v);
+ copy_v3_v3(mesh_vert(bv->vmesh, weld1->index, 0, k)->co, co);
+ create_mesh_bmvert(bm, bv->vmesh, weld1->index, 0, k, bv->v);
}
for (k = 1; k < ns; k++) {
- copy_mesh_vert(vm, weld2->index, 0, ns - k, weld1->index, 0, k);
+ copy_mesh_vert(bv->vmesh, weld2->index, 0, ns - k, weld1->index, 0, k);
+ }
+ }
+#ifdef DEBUG_CUSTOM_PROFILE_WELD
+ if (weld && ns > 1) {
+ printf("Weld1 profile coordinates:\n");
+ for (k = 0; k < ns; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)weld1->profile.prof_co[3 * k],
+ (double)weld1->profile.prof_co[3 * k + 1],
+ (double)weld1->profile.prof_co[3 * k + 2]);
+ }
+ printf("Weld2 profile coordinates\n");
+ for (k = 0; k < ns; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)weld2->profile.prof_co[3 * k],
+ (double)weld2->profile.prof_co[3 * k + 1],
+ (double)weld2->profile.prof_co[3 * k + 2]);
+ }
+ }
+#endif
+
+ /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */
+ vpipe = NULL;
+ if ((vm->count == 3 || vm->count == 4) && bp->seg > 1) {
+ /* Result is passed to bevel_build_rings to avoid overhead. */
+ vpipe = pipe_test(bv);
+ if (vpipe) {
+ vm->mesh_kind = M_ADJ;
}
}
@@ -4965,11 +5596,13 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
bevel_build_poly(bp, bm, bv);
break;
case M_ADJ:
- bevel_build_rings(bp, bm, bv);
+ bevel_build_rings(bp, bm, bv, vpipe);
break;
case M_TRI_FAN:
bevel_build_trifan(bp, bm, bv);
break;
+ case M_CUTOFF:
+ bevel_build_cutoff(bp, bm, bv);
}
}
@@ -5229,9 +5862,7 @@ static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme)
}
}
-/*
- * Construction around the vertex
- */
+/* Construction around the vertex */
static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
{
BMEdge *bme;
@@ -5504,7 +6135,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
return bv;
}
-/* Face f has at least one beveled vertex. Rebuild f */
+/* Face f has at least one beveled vertex. Rebuild f */
static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
{
BMIter liter, eiter, fiter;
@@ -5849,7 +6480,7 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex
}
BLI_assert(bme_prev && bme_next);
- /* want seams and sharp edges to cross only if that way on both sides */
+ /* Want seams and sharp edges to cross only if that way on both sides. */
disable_seam = BM_elem_flag_test(bme_prev, BM_ELEM_SEAM) !=
BM_elem_flag_test(bme_next, BM_ELEM_SEAM);
enable_smooth = BM_elem_flag_test(bme_prev, BM_ELEM_SMOOTH) !=
@@ -5869,8 +6500,8 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex
}
}
-/*
- * Build the polygons along the selected Edge
+/**
+ * Build the bevel polygons along the selected Edge.
*/
static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
{
@@ -6269,50 +6900,131 @@ static void find_even_superellipse_chords(int n, float r, double *xvals, double
find_even_superellipse_chords_general(n, r, xvals, yvals);
}
-/* The superellipse used for multisegment profiles does not
- * have a closed-form way to generate evenly spaced points
- * along an arc. We use an expensive search procedure to find
- * the parameter values that lead to bp->seg even chords.
- * We also want spacing for a number of segments that is
- * a power of 2 >= bp->seg (but at least 4).
- * Use doubles because otherwise we cannot come close to float
- * precision for final results. */
-static void set_profile_spacing(BevelParams *bp)
+/** Find the profile's "fullness," which is the fraction of the space it takes up way from the
+ * boundvert's centroid to to the original vertex for a non-custom profile, or in the case of a
+ * custom profile, the average "height" of the profile points along its centerline. */
+static float find_profile_fullness(BevelParams *bp)
+{
+ float fullness;
+ int nseg = bp->seg;
+
+ /* Precalculated fullness for circle profile radius and more common low seg values. */
+#define CIRCLE_FULLNESS_SEGS 11
+ static const float circle_fullness[CIRCLE_FULLNESS_SEGS] = {
+ 0.0f, /* nsegs == 1 */
+ 0.559f, /* 2 */
+ 0.642f, /* 3 */
+ 0.551f, /* 4 */
+ 0.646f, /* 5 */
+ 0.624f, /* 6 */
+ 0.646f, /* 7 */
+ 0.619f, /* 8 */
+ 0.647f, /* 9 */
+ 0.639f, /* 10 */
+ 0.647f, /* 11 */
+ };
+
+ if (bp->use_custom_profile) {
+ /* Set fullness to the average "height" of the profile's sampled points. */
+ fullness = 0.0f;
+ for (int i = 0; i < nseg; i++) { /* Don't use the end points. */
+ fullness += (float)(bp->pro_spacing.xvals[i] + bp->pro_spacing.yvals[i]) / (2.0f * nseg);
+ }
+ }
+ else {
+ /* An offline optimization process found fullness that led to closest fit to sphere as
+ * a function of r and ns (for case of cube corner). */
+ if (bp->pro_super_r == PRO_LINE_R) {
+ fullness = 0.0f;
+ }
+ else if (bp->pro_super_r == PRO_CIRCLE_R && nseg > 0 && nseg <= CIRCLE_FULLNESS_SEGS) {
+ fullness = circle_fullness[nseg - 1];
+ }
+ else {
+ /* Linear regression fit found best linear function, separately for even/odd segs. */
+ if (nseg % 2 == 0) {
+ fullness = 2.4506f * bp->profile - 0.00000300f * nseg - 0.6266f;
+ }
+ else {
+ fullness = 2.3635f * bp->profile + 0.000152f * nseg - 0.6060f;
+ }
+ }
+ }
+ return fullness;
+}
+
+/** Fills the ProfileSpacing struct with the 2D coordinates for the profile's vertices.
+ * The superellipse used for multisegment profiles does not have a closed-form way
+ * to generate evenly spaced points along an arc. We use an expensive search procedure
+ * to find the parameter values that lead to bp->seg even chords.
+ * We also want spacing for a number of segments that is a power of 2 >= bp->seg (but at least 4).
+ * Use doubles because otherwise we cannot come close to float precision for final results.
+ * \param pro_spacing: The struct to fill. Changes depending on whether there needs
+ to be a separate miter profile. */
+static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom)
{
int seg, seg_2;
seg = bp->seg;
+ seg_2 = power_of_2_max_i(bp->seg);
if (seg > 1) {
- bp->pro_spacing.xvals = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg + 1) * sizeof(double));
- bp->pro_spacing.yvals = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg + 1) * sizeof(double));
- find_even_superellipse_chords(
- seg, bp->pro_super_r, bp->pro_spacing.xvals, bp->pro_spacing.yvals);
- seg_2 = power_of_2_max_i(bp->seg);
+ /* Sample the input number of segments. */
+ pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg + 1) * sizeof(double));
+ pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg + 1) * sizeof(double));
+ if (custom) {
+ /* Make sure the curve profile's sample table is full. */
+ if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) {
+ BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg);
+ }
+
+ /* Copy segment locations into the profile spacing struct. */
+ for (int i = 0; i < seg + 1; i++) {
+ pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y;
+ pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x;
+ }
+ }
+ else {
+ find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals);
+ }
+
+ /* Sample the seg_2 segments used for subdividing the vertex meshes. */
if (seg_2 == 2) {
seg_2 = 4;
}
bp->pro_spacing.seg_2 = seg_2;
if (seg_2 == seg) {
- bp->pro_spacing.xvals_2 = bp->pro_spacing.xvals;
- bp->pro_spacing.yvals_2 = bp->pro_spacing.yvals;
+ pro_spacing->xvals_2 = pro_spacing->xvals;
+ pro_spacing->yvals_2 = pro_spacing->yvals;
}
else {
- bp->pro_spacing.xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg_2 + 1) * sizeof(double));
- bp->pro_spacing.yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg_2 + 1) * sizeof(double));
- find_even_superellipse_chords(
- seg_2, bp->pro_super_r, bp->pro_spacing.xvals_2, bp->pro_spacing.yvals_2);
+ pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg_2 + 1) * sizeof(double));
+ pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg_2 + 1) * sizeof(double));
+ if (custom) {
+ /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */
+ BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg_2);
+
+ /* Copy segment locations into the profile spacing struct. */
+ for (int i = 0; i < seg_2 + 1; i++) {
+ pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y;
+ pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x;
+ }
+ }
+ else {
+ find_even_superellipse_chords(
+ seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2);
+ }
}
}
- else {
- bp->pro_spacing.xvals = NULL;
- bp->pro_spacing.yvals = NULL;
- bp->pro_spacing.xvals_2 = NULL;
- bp->pro_spacing.yvals_2 = NULL;
- bp->pro_spacing.seg_2 = 0;
+ else { /* Only 1 segment, we don't need any profile information */
+ pro_spacing->xvals = NULL;
+ pro_spacing->yvals = NULL;
+ pro_spacing->xvals_2 = NULL;
+ pro_spacing->yvals_2 = NULL;
+ pro_spacing->seg_2 = 0;
}
}
@@ -6320,7 +7032,7 @@ static void set_profile_spacing(BevelParams *bp)
* Assume we have a situation like:
*
* a d
- * \ /
+ * \ /
* A \ / C
* \ th1 th2/
* b---------c
@@ -6566,7 +7278,10 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_outer,
const int miter_inner,
const float spread,
- const float smoothresh)
+ const float smoothresh,
+ const bool use_custom_profile,
+ const struct CurveProfile *custom_profile,
+ const int vmesh_method)
{
BMIter iter, liter;
BMVert *v, *v_next;
@@ -6578,9 +7293,9 @@ void BM_mesh_bevel(BMesh *bm,
bp.offset = offset;
bp.offset_type = offset_type;
- bp.seg = segments;
+ bp.seg = (int)segments;
bp.profile = profile;
- bp.pro_super_r = -log(2.0) / log(sqrt(profile)); /* convert to superellipse exponent */
+ bp.pro_super_r = -logf(2.0) / logf(sqrtf(profile)); /* convert to superellipse exponent */
bp.vertex_only = vertex_only;
bp.use_weights = use_weights;
bp.loop_slide = loop_slide;
@@ -6598,6 +7313,15 @@ void BM_mesh_bevel(BMesh *bm,
bp.spread = spread;
bp.smoothresh = smoothresh;
bp.face_hash = NULL;
+ bp.use_custom_profile = use_custom_profile;
+ bp.custom_profile = custom_profile;
+ bp.vmesh_method = vmesh_method;
+
+ /* Disable the miters with the cutoff vertex mesh method, this combination isn't useful anyway */
+ if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) {
+ bp.miter_outer = BEVEL_MITER_SHARP;
+ bp.miter_inner = BEVEL_MITER_SHARP;
+ }
if (profile >= 0.950f) { /* r ~ 692, so PRO_SQUARE_R is 1e4 */
bp.pro_super_r = PRO_SQUARE_R;
@@ -6608,7 +7332,18 @@ void BM_mesh_bevel(BMesh *bm,
bp.vert_hash = BLI_ghash_ptr_new(__func__);
bp.mem_arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__);
BLI_memarena_use_calloc(bp.mem_arena);
- set_profile_spacing(&bp);
+
+ /* Get the 2D profile point locations from either the superellipse or the custom profile */
+ set_profile_spacing(&bp, &bp.pro_spacing, bp.use_custom_profile);
+ if (bp.seg > 1) {
+ bp.pro_spacing.fullness = find_profile_fullness(&bp);
+ }
+
+ /* Get separate non-custom profile samples for the miter profiles if they are needed */
+ if (bp.use_custom_profile &&
+ (bp.miter_inner != BEVEL_MITER_SHARP || bp.miter_outer != BEVEL_MITER_SHARP)) {
+ set_profile_spacing(&bp, &bp.pro_spacing_miter, false);
+ }
bp.face_hash = BLI_ghash_ptr_new(__func__);
BLI_ghash_flag_set(bp.face_hash, GHASH_FLAG_ALLOW_DUPES);
@@ -6643,6 +7378,20 @@ void BM_mesh_bevel(BMesh *bm,
adjust_offsets(&bp, bm);
}
+ /* Maintain consistent orientations for the unsymmetrical custom profiles. */
+ if (bp.use_custom_profile) {
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ regularize_profile_orientation(&bp, e);
+ }
+ }
+ }
+#ifdef DEBUG_PROFILE_ORIENTATION_DRAW
+ if (bp.use_custom_profile) {
+ debug_draw_profile_orientation(&bp, bm);
+ }
+#endif
+
/* Build the meshes around vertices, now that positions are final */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
@@ -6688,14 +7437,14 @@ void BM_mesh_bevel(BMesh *bm,
}
if (bp.harden_normals) {
- bevel_harden_normals(bm, &bp);
+ bevel_harden_normals(&bp, bm);
}
if (bp.face_strength_mode != BEVEL_FACE_STRENGTH_NONE) {
bevel_set_weighted_normal_face_strength(bm, &bp);
}
/* When called from operator (as opposed to modifier), bm->use_toolflags
- * will be set, and we to transfer the oflags to BM_ELEM_TAGs */
+ * will be set, and we need to transfer the oflags to BM_ELEM_TAGs */
if (bm->use_toolflags) {
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_OUT)) {
@@ -6717,6 +7466,22 @@ void BM_mesh_bevel(BMesh *bm,
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l, BM_ELEM_LONG_TAG);
}
+
+#ifdef DEBUG_CUSTOM_PROFILE_SAMPLE
+ printf("Profile spacing:\n");
+ printf("Seg values:\n");
+ if (bp.pro_spacing.xvals != NULL) {
+ for (int i = 0; i < bp.seg; i++) {
+ printf("(%.3f, %.3f)\n", bp.pro_spacing.xvals[i], bp.pro_spacing.yvals[i]);
+ }
+ }
+ if (bp.pro_spacing.seg_2 != bp.seg && bp.pro_spacing.seg_2 != 0) {
+ printf("Seg_2 values:\n");
+ for (int i = 0; i < bp.pro_spacing.seg_2; i++) {
+ printf("(%0.2f, %0.2f)\n", bp.pro_spacing.xvals_2[i], bp.pro_spacing.yvals_2[i]);
+ }
+ }
+#endif
}
/* primary free */
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 496be9219b7..45bebbb33ce 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -21,6 +21,7 @@
* \ingroup bmesh
*/
+struct CurveProfile;
struct MDeformVert;
void BM_mesh_bevel(BMesh *bm,
@@ -42,6 +43,8 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_outer,
const int miter_inner,
const float spread,
- const float smoothresh);
-
+ const float smoothresh,
+ const bool use_custom_profile,
+ const struct CurveProfile *custom_profile,
+ const int vmesh_method);
#endif /* __BMESH_BEVEL_H__ */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 8f205173011..69229f25917 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -346,6 +346,8 @@ typedef enum {
/** sphere widget (used to input a unit-vector, aka normal) */
UI_BTYPE_UNITVEC = 31 << 9,
UI_BTYPE_CURVE = 32 << 9,
+ /** Profile editing widget */
+ UI_BTYPE_CURVEPROFILE = 33 << 9,
UI_BTYPE_LISTBOX = 36 << 9,
UI_BTYPE_LISTROW = 37 << 9,
UI_BTYPE_HSVCIRCLE = 38 << 9,
@@ -1973,6 +1975,7 @@ void uiTemplateCurveMapping(uiLayout *layout,
bool brush,
bool neg_slope,
bool tone);
+void uiTemplateCurveProfile(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
void uiTemplateColorPicker(uiLayout *layout,
struct PointerRNA *ptr,
const char *propname,
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 72c31c7b39e..499842a570b 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -27,16 +27,21 @@
#include "DNA_color_types.h"
#include "DNA_screen_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_curveprofile_types.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLI_polyfill_2d.h"
+
+#include "MEM_guardedalloc.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_node.h"
#include "BKE_tracking.h"
+#include "BKE_curveprofile.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -1814,6 +1819,8 @@ static void ui_draw_but_curve_grid(
immVertex2f(pos, rect->xmax, fy);
fy += dy;
}
+ /* Note: Assertion fails with here when the view is moved farther below the center.
+ * Missing two points from the number given with immBegin. */
immEnd();
}
@@ -2113,6 +2120,231 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, const uiWidgetColors *wcol, cons
immUnbindProgram();
}
+/** Used to draw a curve profile widget. Somewhat similar to ui_draw_but_CURVE */
+void ui_draw_but_CURVEPROFILE(ARegion *ar,
+ uiBut *but,
+ const uiWidgetColors *wcol,
+ const rcti *rect)
+{
+ uint i;
+ float fx, fy;
+ CurveProfile *profile;
+ if (but->editprofile) {
+ profile = but->editprofile;
+ }
+ else {
+ profile = (CurveProfile *)but->poin;
+ }
+
+ /* Calculate offset and zoom */
+ float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect);
+ float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect);
+ float offsx = profile->view_rect.xmin - (1.0f / zoomx);
+ float offsy = profile->view_rect.ymin - (1.0f / zoomy);
+
+ /* Exit early if too narrow */
+ if (zoomx == 0.0f) {
+ return;
+ }
+
+ /* Test needed because path can draw outside of boundary */
+ int scissor[4];
+ GPU_scissor_get_i(scissor);
+ rcti scissor_new = {
+ .xmin = rect->xmin,
+ .ymin = rect->ymin,
+ .xmax = rect->xmax,
+ .ymax = rect->ymax,
+ };
+ rcti scissor_region = {0, ar->winx, 0, ar->winy};
+ BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new);
+ GPU_scissor(scissor_new.xmin,
+ scissor_new.ymin,
+ BLI_rcti_size_x(&scissor_new),
+ BLI_rcti_size_y(&scissor_new));
+
+ GPU_line_width(1.0f);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ /* Backdrop */
+ float color_backdrop[4] = {0, 0, 0, 1};
+ if (profile->flag & PROF_USE_CLIP) {
+ gl_shaded_color_get_fl((uchar *)wcol->inner, -20, color_backdrop);
+ immUniformColor3fv(color_backdrop);
+ immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ immUniformColor3ubv((uchar *)wcol->inner);
+ immRectf(pos,
+ rect->xmin + zoomx * (profile->clip_rect.xmin - offsx),
+ rect->ymin + zoomy * (profile->clip_rect.ymin - offsy),
+ rect->xmin + zoomx * (profile->clip_rect.xmax - offsx),
+ rect->ymin + zoomy * (profile->clip_rect.ymax - offsy));
+ }
+ else {
+ rgb_uchar_to_float(color_backdrop, (uchar *)wcol->inner);
+ immUniformColor3fv(color_backdrop);
+ immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ }
+
+ /* 0.25 step grid */
+ gl_shaded_color((uchar *)wcol->inner, -16);
+ ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f);
+ /* 1.0 step grid */
+ gl_shaded_color((uchar *)wcol->inner, -24);
+ ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f);
+
+ /* Draw the path's fill */
+ if (profile->table == NULL) {
+ BKE_curveprofile_update(profile, false);
+ }
+ CurveProfilePoint *pts = profile->table;
+ /* Also add the last points on the right and bottom edges to close off the fill polygon */
+ bool add_left_tri = profile->view_rect.xmin < 0.0f;
+ bool add_bottom_tri = profile->view_rect.ymin < 0.0f;
+ uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri;
+ uint tot_triangles = tot_points - 2;
+
+ /* Create array of the positions of the table's points */
+ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords");
+ for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len);
+ i++) { /* Only add the points from the table here */
+ table_coords[i][0] = pts[i].x;
+ table_coords[i][1] = pts[i].y;
+ }
+ if (add_left_tri && add_bottom_tri) {
+ /* Add left side, bottom left corner, and bottom side points */
+ table_coords[tot_points - 3][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 3][1] = 1.0f;
+ table_coords[tot_points - 2][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 2][1] = profile->view_rect.ymin;
+ table_coords[tot_points - 1][0] = 1.0f;
+ table_coords[tot_points - 1][1] = profile->view_rect.ymin;
+ }
+ else if (add_left_tri) {
+ /* Add the left side and bottom left corner points */
+ table_coords[tot_points - 2][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 2][1] = 1.0f;
+ table_coords[tot_points - 1][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 1][1] = 0.0f;
+ }
+ else if (add_bottom_tri) {
+ /* Add the bottom side and bottom left corner points */
+ table_coords[tot_points - 2][0] = 0.0f;
+ table_coords[tot_points - 2][1] = profile->view_rect.ymin;
+ table_coords[tot_points - 1][0] = 1.0f;
+ table_coords[tot_points - 1][1] = profile->view_rect.ymin;
+ }
+ else {
+ /* Just add the bottom corner point. Side points would be redundant anyway */
+ table_coords[tot_points - 1][0] = 0.0f;
+ table_coords[tot_points - 1][1] = 0.0f;
+ }
+
+ /* Calculate the table point indices of the triangles for the profile's fill */
+ uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices");
+ BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices);
+
+ /* Draw the triangles for the profile fill */
+ immUniformColor3ubvAlpha((const uchar *)wcol->item, 128);
+ GPU_blend(true);
+ GPU_polygon_smooth(false);
+ immBegin(GPU_PRIM_TRIS, 3 * tot_triangles);
+ for (i = 0; i < tot_triangles; i++) {
+ for (uint j = 0; j < 3; j++) {
+ uint *tri = tri_indices[i];
+ fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx);
+ fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy);
+ immVertex2f(pos, fx, fy);
+ }
+ }
+ immEnd();
+ MEM_freeN(tri_indices);
+
+ /* Draw the profile's path so the edge stands out a bit */
+ tot_points -= (add_left_tri + add_left_tri);
+ GPU_line_width(1.0f);
+ immUniformColor3ubvAlpha((const uchar *)wcol->item, 255);
+ GPU_line_smooth(true);
+ immBegin(GPU_PRIM_LINE_STRIP, tot_points - 1);
+ for (i = 0; i < tot_points - 1; i++) {
+ fx = rect->xmin + zoomx * (table_coords[i][0] - offsx);
+ fy = rect->ymin + zoomy * (table_coords[i][1] - offsy);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+ immUnbindProgram();
+ MEM_freeN(table_coords);
+
+ /* New GPU instructions for control points and sampled points. */
+ format = immVertexFormat();
+ pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+
+ /* Calculate vertex colors based on text theme. */
+ float color_vert[4], color_vert_select[4], color_sample[4];
+ UI_GetThemeColor4fv(TH_TEXT_HI, color_vert);
+ UI_GetThemeColor4fv(TH_TEXT, color_vert_select);
+ color_sample[0] = (float)wcol->item[0] / 255.0f;
+ color_sample[1] = (float)wcol->item[1] / 255.0f;
+ color_sample[2] = (float)wcol->item[2] / 255.0f;
+ color_sample[3] = (float)wcol->item[3] / 255.0f;
+ if (len_squared_v3v3(color_vert, color_vert_select) < 0.1f) {
+ interp_v3_v3v3(color_vert, color_vert_select, color_backdrop, 0.75f);
+ }
+ if (len_squared_v3(color_vert) > len_squared_v3(color_vert_select)) {
+ /* Ensure brightest text color is used for selection. */
+ swap_v3_v3(color_vert, color_vert_select);
+ }
+
+ /* Draw the control points. */
+ pts = profile->path;
+ tot_points = (uint)profile->path_len;
+ GPU_line_smooth(false);
+ GPU_blend(false);
+ GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f)));
+ immBegin(GPU_PRIM_POINTS, tot_points);
+ for (i = 0; i < tot_points; i++) {
+ fx = rect->xmin + zoomx * (pts[i].x - offsx);
+ fy = rect->ymin + zoomy * (pts[i].y - offsy);
+ immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+
+ /* Draw the sampled points in addition to the control points if they have been created */
+ pts = profile->segments;
+ tot_points = (uint)profile->segments_len;
+ if (tot_points > 0 && pts) {
+ GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 3.0f, 3.0f)));
+ immBegin(GPU_PRIM_POINTS, tot_points);
+ for (i = 0; i < tot_points; i++) {
+ fx = rect->xmin + zoomx * (pts[i].x - offsx);
+ fy = rect->ymin + zoomy * (pts[i].y - offsy);
+ immAttr4fv(col, color_sample);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+ }
+
+ immUnbindProgram();
+
+ /* restore scissortest */
+ GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
+
+ /* Outline */
+ format = immVertexFormat();
+ pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ immUniformColor3ubv((const uchar *)wcol->outline);
+ imm_draw_box_wire_2d(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+
+ immUnbindProgram();
+}
+
void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(ar),
uiBut *but,
const uiWidgetColors *UNUSED(wcol),
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 806b5789df1..673e4c1a8da 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -32,7 +32,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
-
+#include "DNA_curveprofile_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -57,6 +57,7 @@
#include "BKE_tracking.h"
#include "BKE_unit.h"
#include "BKE_paint.h"
+#include "BKE_curveprofile.h"
#include "IMB_colormanagement.h"
@@ -442,6 +443,8 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut
static ColorBand but_copypaste_coba = {0};
static CurveMapping but_copypaste_curve = {0};
static bool but_copypaste_curve_alive = false;
+static CurveProfile but_copypaste_profile = {0};
+static bool but_copypaste_profile_alive = false;
/** \} */
@@ -1080,6 +1083,13 @@ static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data
data->applied = true;
}
+static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+ data->retval = but->retval;
+ data->applied = true;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1979,6 +1989,7 @@ static void ui_apply_but(
float *editvec;
ColorBand *editcoba;
CurveMapping *editcumap;
+ CurveProfile *editprofile;
data->retval = 0;
@@ -2036,11 +2047,13 @@ static void ui_apply_but(
editvec = but->editvec;
editcoba = but->editcoba;
editcumap = but->editcumap;
+ editprofile = but->editprofile;
but->editstr = NULL;
but->editval = NULL;
but->editvec = NULL;
but->editcoba = NULL;
but->editcumap = NULL;
+ but->editprofile = NULL;
/* handle different types */
switch (but->type) {
@@ -2100,6 +2113,9 @@ static void ui_apply_but(
case UI_BTYPE_CURVE:
ui_apply_but_CURVE(C, but, data);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_apply_but_CURVEPROFILE(C, but, data);
+ break;
case UI_BTYPE_KEY_EVENT:
case UI_BTYPE_HOTKEY_EVENT:
ui_apply_but_BUT(C, but, data);
@@ -2147,6 +2163,7 @@ static void ui_apply_but(
but->editvec = editvec;
but->editcoba = editcoba;
but->editcumap = editcumap;
+ but->editprofile = editprofile;
}
/** \} */
@@ -2446,6 +2463,29 @@ static void ui_but_paste_curvemapping(bContext *C, uiBut *but)
}
}
+static void ui_but_copy_CurveProfile(uiBut *but)
+{
+ if (but->poin != NULL) {
+ but_copypaste_profile_alive = true;
+ BKE_curveprofile_free_data(&but_copypaste_profile);
+ BKE_curveprofile_copy_data(&but_copypaste_profile, (CurveProfile *)but->poin);
+ }
+}
+
+static void ui_but_paste_CurveProfile(bContext *C, uiBut *but)
+{
+ if (but_copypaste_profile_alive) {
+ if (!but->poin) {
+ but->poin = MEM_callocN(sizeof(CurveProfile), "CurveProfile");
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ BKE_curveprofile_free_data((CurveProfile *)but->poin);
+ BKE_curveprofile_copy_data((CurveProfile *)but->poin, &but_copypaste_profile);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+}
+
static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
{
PointerRNA *opptr;
@@ -2540,6 +2580,10 @@ static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
ui_but_copy_curvemapping(but);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_but_copy_CurveProfile(but);
+ break;
+
case UI_BTYPE_BUT:
if (!but->optype) {
break;
@@ -2623,6 +2667,10 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons
ui_but_paste_curvemapping(C, but);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_but_paste_CurveProfile(C, but);
+ break;
+
default:
break;
}
@@ -2633,6 +2681,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons
void ui_but_clipboard_free(void)
{
BKE_curvemapping_free_data(&but_copypaste_curve);
+ BKE_curveprofile_free_data(&but_copypaste_profile);
}
/** \} */
@@ -3757,6 +3806,9 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
if (but->type == UI_BTYPE_CURVE) {
but->editcumap = (CurveMapping *)but->poin;
}
+ if (but->type == UI_BTYPE_CURVEPROFILE) {
+ but->editprofile = (CurveProfile *)but->poin;
+ }
else if (but->type == UI_BTYPE_COLORBAND) {
data->coba = (ColorBand *)but->poin;
but->editcoba = data->coba;
@@ -3847,6 +3899,7 @@ static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
but->editvec = NULL;
but->editcoba = NULL;
but->editcumap = NULL;
+ but->editprofile = NULL;
data->dragstartx = 0;
data->draglastx = 0;
@@ -6803,6 +6856,281 @@ static int ui_do_but_CURVE(
return WM_UI_HANDLER_CONTINUE;
}
+/* Same as ui_numedit_but_CURVE with some smaller changes. */
+static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
+ uiBut *but,
+ uiHandleButtonData *data,
+ int evtx,
+ int evty,
+ bool snap,
+ const bool shift)
+{
+ CurveProfile *profile = (CurveProfile *)but->poin;
+ CurveProfilePoint *pts = profile->path;
+ float fx, fy, zoomx, zoomy;
+ int mx, my, dragx, dragy;
+ int a;
+ bool changed = false;
+
+ /* evtx evty and drag coords are absolute mousecoords,
+ * prevents errors when editing when layout changes */
+ mx = evtx;
+ my = evty;
+ ui_window_to_block(data->region, block, &mx, &my);
+ dragx = data->draglastx;
+ dragy = data->draglasty;
+ ui_window_to_block(data->region, block, &dragx, &dragy);
+
+ zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect);
+ zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
+
+ if (snap) {
+ float d[2];
+
+ d[0] = mx - data->dragstartx;
+ d[1] = my - data->dragstarty;
+
+ if (len_squared_v2(d) < (3.0f * 3.0f)) {
+ snap = false;
+ }
+ }
+
+ fx = (mx - dragx) / zoomx;
+ fy = (my - dragy) / zoomy;
+
+ if (data->dragsel != -1) {
+ CurveProfilePoint *point_last = NULL;
+ const float mval_factor = ui_mouse_scale_warp_factor(shift);
+ bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
+
+ fx *= mval_factor;
+ fy *= mval_factor;
+
+ /* Move all the points that aren't the last or the first */
+ for (a = 1; a < profile->path_len - 1; a++) {
+ if (pts[a].flag & PROF_SELECT) {
+ float origx = pts[a].x, origy = pts[a].y;
+ pts[a].x += fx;
+ pts[a].y += fy;
+ if (snap) {
+ pts[a].x = 0.125f * roundf(8.0f * pts[a].x);
+ pts[a].y = 0.125f * roundf(8.0f * pts[a].y);
+ }
+ if (!moved_point && (pts[a].x != origx || pts[a].y != origy)) {
+ moved_point = true;
+ }
+
+ point_last = &pts[a];
+ }
+ }
+
+ BKE_curveprofile_update(profile, false);
+
+ if (moved_point) {
+ data->draglastx = evtx;
+ data->draglasty = evty;
+ changed = true;
+#ifdef USE_CONT_MOUSE_CORRECT
+ /* note: using 'cmp_last' is weak since there may be multiple points selected,
+ * but in practice this isnt really an issue */
+ if (ui_but_is_cursor_warp(but)) {
+ /* OK but can go outside bounds */
+ data->ungrab_mval[0] = but->rect.xmin + ((point_last->x - profile->view_rect.xmin) * zoomx);
+ data->ungrab_mval[1] = but->rect.ymin + ((point_last->y - profile->view_rect.ymin) * zoomy);
+ BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
+ }
+#endif
+ }
+ data->dragchange = true; /* mark for selection */
+ }
+ else {
+ /* clamp for clip */
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmin - fx < profile->clip_rect.xmin) {
+ fx = profile->view_rect.xmin - profile->clip_rect.xmin;
+ }
+ else if (profile->view_rect.xmax - fx > profile->clip_rect.xmax) {
+ fx = profile->view_rect.xmax - profile->clip_rect.xmax;
+ }
+ if (profile->view_rect.ymin - fy < profile->clip_rect.ymin) {
+ fy = profile->view_rect.ymin - profile->clip_rect.ymin;
+ }
+ else if (profile->view_rect.ymax - fy > profile->clip_rect.ymax) {
+ fy = profile->view_rect.ymax - profile->clip_rect.ymax;
+ }
+ }
+
+ profile->view_rect.xmin -= fx;
+ profile->view_rect.ymin -= fy;
+ profile->view_rect.xmax -= fx;
+ profile->view_rect.ymax -= fy;
+
+ data->draglastx = evtx;
+ data->draglasty = evty;
+
+ changed = true;
+ }
+
+ return changed;
+}
+
+/** Interaction for curve profile widget.
+ * \note Uses hardcoded keys rather than the keymap. */
+static int ui_do_but_CURVEPROFILE(
+ bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+{
+ int mx, my, i;
+
+ mx = event->x;
+ my = event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ /* Move selected control points. */
+ if (event->type == GKEY && event->val == KM_RELEASE) {
+ data->dragstartx = mx;
+ data->dragstarty = my;
+ data->draglastx = mx;
+ data->draglasty = my;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ CurveProfile *profile = (CurveProfile *)but->poin;
+
+ /* Delete selected control points. */
+ if (event->type == XKEY && event->val == KM_RELEASE) {
+ BKE_curveprofile_remove_by_flag(profile, PROF_SELECT);
+ BKE_curveprofile_update(profile, false);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ /* Selecting, adding, and starting point movements. */
+ if (data->state == BUTTON_STATE_HIGHLIGHT) {
+ if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
+ CurveProfilePoint *pts; /* Path or table. */
+ const float m_xy[2] = {mx, my};
+ float dist_min_sq;
+ int i_selected = -1;
+
+ if (event->ctrl) {
+ float f_xy[2];
+ BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
+
+ BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
+ BKE_curveprofile_update(profile, false);
+ }
+
+ /* Check for selecting of a point by finding closest point in radius. */
+ dist_min_sq = SQUARE(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */
+ pts = profile->path;
+ for (i = 0; i < profile->path_len; i++) {
+ float f_xy[2];
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
+ const float dist_sq = len_squared_v2v2(m_xy, f_xy);
+ if (dist_sq < dist_min_sq) {
+ i_selected = i;
+ dist_min_sq = dist_sq;
+ }
+ }
+
+ /* Add a point if the click was close to the path but not a control point. */
+ if (i_selected == -1) { /* No control point selected. */
+ float f_xy[2], f_xy_prev[2];
+ pts = profile->table;
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[0].x);
+
+ dist_min_sq = SQUARE(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */
+
+ /* Loop through the path's high resolution table and find what's near the click. */
+ for (i = 1; i <= PROF_N_TABLE(profile->path_len); i++) {
+ copy_v2_v2(f_xy_prev, f_xy);
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
+
+ if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) {
+ BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
+
+ CurveProfilePoint *new_pt = BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
+ BKE_curveprofile_update(profile, false);
+
+ /* reset pts back to the control points. */
+ pts = profile->path;
+
+ /* Get the index of the newly added point. */
+ for (i = 0; i < profile->path_len; i++) {
+ if (&pts[i] == new_pt) {
+ i_selected = i;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* Change the flag for the point(s) if one was selected. */
+ if (i_selected != -1) {
+ /* Deselect all if this one is deselected, except if we hold shift. */
+ if (!event->shift) {
+ for (i = 0; i < profile->path_len; i++) {
+ pts[i].flag &= ~PROF_SELECT;
+ }
+ pts[i_selected].flag |= PROF_SELECT;
+ }
+ else {
+ pts[i_selected].flag ^= PROF_SELECT;
+ }
+ }
+ else {
+ /* Move the view. */
+ data->cancel = true;
+ }
+
+ data->dragsel = i_selected;
+
+ data->dragstartx = mx;
+ data->dragstarty = my;
+ data->draglastx = mx;
+ data->draglasty = my;
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if (data->state == BUTTON_STATE_NUM_EDITING) { /* Do control point movement. */
+ if (event->type == MOUSEMOVE) {
+ if (mx != data->draglastx || my != data->draglasty) {
+ if (ui_numedit_but_CURVEPROFILE(
+ block, but, data, mx, my, event->ctrl != 0, event->shift != 0)) {
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ }
+ else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+ /* Finish move. */
+ if (data->dragsel != -1) {
+ CurveProfilePoint *pts = profile->path;
+
+ if (data->dragchange == false) {
+ /* Deselect all, select one. */
+ if (!event->shift) {
+ for (i = 0; i < profile->path_len; i++) {
+ pts[i].flag &= ~PROF_SELECT;
+ }
+ pts[data->dragsel].flag |= PROF_SELECT;
+ }
+ }
+ else {
+ BKE_curveprofile_update(profile, true); /* Remove doubles after move. */
+ }
+ }
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
{
Histogram *hist = (Histogram *)but->poin;
@@ -7208,6 +7536,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_CURVE:
retval = ui_do_but_CURVE(C, block, but, data, event);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ retval = ui_do_but_CURVEPROFILE(C, block, but, data, event);
+ break;
case UI_BTYPE_HSVCUBE:
retval = ui_do_but_HSVCUBE(C, block, but, data, event);
break;
@@ -7599,7 +7930,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA
copy_v2_fl(data->ungrab_mval, FLT_MAX);
#endif
- if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_SEARCH_MENU)) {
+ if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_CURVEPROFILE, UI_BTYPE_SEARCH_MENU)) {
/* XXX curve is temp */
}
else {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 81979b1fc8f..3fe2750e070 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -274,6 +274,7 @@ struct uiBut {
float *editvec;
void *editcoba;
void *editcumap;
+ void *editprofile;
uiButPushedStateFunc pushed_state_func;
void *pushed_state_arg;
@@ -740,6 +741,10 @@ void ui_draw_but_CURVE(ARegion *ar,
uiBut *but,
const struct uiWidgetColors *wcol,
const rcti *rect);
+void ui_draw_but_CURVEPROFILE(ARegion *ar,
+ uiBut *but,
+ const struct uiWidgetColors *wcol,
+ const rcti *rect);
void ui_draw_but_IMAGE(ARegion *ar,
uiBut *but,
const struct uiWidgetColors *wcol,
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 34b1070f8b4..ab20b5ac520 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -422,7 +422,8 @@ bool ui_but_is_cursor_warp(const uiBut *but)
UI_BTYPE_HSVCIRCLE,
UI_BTYPE_TRACK_PREVIEW,
UI_BTYPE_HSVCUBE,
- UI_BTYPE_CURVE)) {
+ UI_BTYPE_CURVE,
+ UI_BTYPE_CURVEPROFILE)) {
return true;
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 021d7733fae..b205572ba06 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -35,6 +35,7 @@
#include "DNA_texture_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_curveprofile_types.h"
#include "BLI_utildefines.h"
#include "BLI_alloca.h"
@@ -69,6 +70,7 @@
#include "BKE_packedFile.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "BKE_curveprofile.h"
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_shader_fx.h"
@@ -4505,6 +4507,612 @@ void uiTemplateCurveMapping(uiLayout *layout,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Curve Profile Template
+ * \{ */
+
+static void CurveProfile_presets_dofunc(bContext *C, void *profile_v, int event)
+{
+ CurveProfile *profile = profile_v;
+
+ profile->preset = event;
+ BKE_curveprofile_reset(profile);
+ BKE_curveprofile_update(profile, false);
+
+ ED_undo_push(C, "CurveProfile tools");
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static uiBlock *CurveProfile_presets_func(bContext *C, ARegion *ar, CurveProfile *profile)
+{
+ uiBlock *block;
+ short yco = 0;
+ short menuwidth = 12 * UI_UNIT_X;
+ menuwidth = 0;
+
+ block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
+ UI_block_func_butmenu_set(block, CurveProfile_presets_dofunc, profile);
+
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Default"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_LINE,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Support Loops"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_SUPPORTS,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Cornice Moulding"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_CORNICE,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Crown Moulding"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_CROWN,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Steps"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_STEPS,
+ "");
+
+ UI_block_direction_set(block, UI_DIR_DOWN);
+ UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
+
+ return block;
+}
+
+static uiBlock *CurveProfile_buttons_presets(bContext *C, ARegion *ar, void *profile_v)
+{
+ return CurveProfile_presets_func(C, ar, (CurveProfile *)profile_v);
+}
+
+/* Only for CurveProfile tools block */
+enum {
+ UIPROFILE_FUNC_RESET,
+ UIPROFILE_FUNC_RESET_VIEW,
+};
+
+static void CurveProfile_tools_dofunc(bContext *C, void *profile_v, int event)
+{
+ CurveProfile *profile = profile_v;
+
+ switch (event) {
+ case UIPROFILE_FUNC_RESET: /* reset */
+ BKE_curveprofile_reset(profile);
+ BKE_curveprofile_update(profile, false);
+ break;
+ case UIPROFILE_FUNC_RESET_VIEW: /* reset view to clipping rect */
+ profile->view_rect = profile->clip_rect;
+ break;
+ }
+ ED_undo_push(C, "CurveProfile tools");
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *ar, CurveProfile *profile)
+{
+ uiBlock *block;
+ short yco = 0;
+ short menuwidth = 10 * UI_UNIT_X;
+
+ block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
+ UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile);
+
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Reset View"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ UIPROFILE_FUNC_RESET_VIEW,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Reset Curve"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ UIPROFILE_FUNC_RESET,
+ "");
+
+ UI_block_direction_set(block, UI_DIR_DOWN);
+ UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
+
+ return block;
+}
+
+static uiBlock *CurveProfile_buttons_tools(bContext *C, ARegion *ar, void *profile_v)
+{
+ return CurveProfile_tools_func(C, ar, (CurveProfile *)profile_v);
+}
+
+static void CurveProfile_buttons_zoom_in(bContext *C, void *profile_v, void *UNUSED(arg))
+{
+ CurveProfile *profile = profile_v;
+ float d;
+
+ /* we allow 20 times zoom */
+ if (BLI_rctf_size_x(&profile->view_rect) > 0.04f * BLI_rctf_size_x(&profile->clip_rect)) {
+ d = 0.1154f * BLI_rctf_size_x(&profile->view_rect);
+ profile->view_rect.xmin += d;
+ profile->view_rect.xmax -= d;
+ d = 0.1154f * BLI_rctf_size_y(&profile->view_rect);
+ profile->view_rect.ymin += d;
+ profile->view_rect.ymax -= d;
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static void CurveProfile_buttons_zoom_out(bContext *C, void *profile_v, void *UNUSED(arg))
+{
+ CurveProfile *profile = profile_v;
+ float d, d1;
+
+ /* Allow 20 times zoom, but don't view outside clip */
+ if (BLI_rctf_size_x(&profile->view_rect) < 20.0f * BLI_rctf_size_x(&profile->clip_rect)) {
+ d = d1 = 0.15f * BLI_rctf_size_x(&profile->view_rect);
+
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmin - d < profile->clip_rect.xmin) {
+ d1 = profile->view_rect.xmin - profile->clip_rect.xmin;
+ }
+ }
+ profile->view_rect.xmin -= d1;
+
+ d1 = d;
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmax + d > profile->clip_rect.xmax) {
+ d1 = -profile->view_rect.xmax + profile->clip_rect.xmax;
+ }
+ }
+ profile->view_rect.xmax += d1;
+
+ d = d1 = 0.15f * BLI_rctf_size_y(&profile->view_rect);
+
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.ymin - d < profile->clip_rect.ymin) {
+ d1 = profile->view_rect.ymin - profile->clip_rect.ymin;
+ }
+ }
+ profile->view_rect.ymin -= d1;
+
+ d1 = d;
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.ymax + d > profile->clip_rect.ymax) {
+ d1 = -profile->view_rect.ymax + profile->clip_rect.ymax;
+ }
+ }
+ profile->view_rect.ymax += d1;
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static void CurveProfile_clipping_toggle(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ profile->flag ^= PROF_USE_CLIP;
+
+ BKE_curveprofile_update(profile, false);
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_reverse(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_reverse(profile);
+ BKE_curveprofile_update(profile, false);
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_delete(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_remove_by_flag(profile, SELECT);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_setsharp(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_selected_handle_set(profile, HD_VECT, HD_VECT);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_setcurved(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_selected_handle_set(profile, HD_AUTO, HD_AUTO);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+ BKE_curveprofile_update(profile, true);
+ rna_update_cb(C, arg1_v, NULL);
+}
+
+static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUpdateCb *cb)
+{
+ CurveProfile *profile = ptr->data;
+ CurveProfilePoint *point = NULL;
+ uiLayout *row, *sub;
+ uiBlock *block;
+ uiBut *bt;
+ int i, icon, path_width, path_height;
+ bool point_last_or_first = false;
+ rctf bounds;
+
+ block = uiLayoutGetBlock(layout);
+
+ UI_block_emboss_set(block, UI_EMBOSS);
+
+ uiLayoutRow(layout, false);
+
+ /* Preset selector */
+ /* There is probably potential to use simpler "uiItemR" functions here, but automatic updating
+ * after a preset is selected would be more complicated. */
+ bt = uiDefBlockBut(
+ block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, "");
+ UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ row = uiLayoutRow(layout, false);
+
+ /* (Left aligned) */
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
+
+ /* Zoom in */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ZOOM_IN,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Zoom in"));
+ UI_but_func_set(bt, CurveProfile_buttons_zoom_in, profile, NULL);
+
+ /* Zoom out */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ZOOM_OUT,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Zoom out"));
+ UI_but_func_set(bt, CurveProfile_buttons_zoom_out, profile, NULL);
+
+ /* (Right aligned) */
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
+
+ /* Reset view, reset curve */
+ bt = uiDefIconBlockBut(
+ block, CurveProfile_buttons_tools, profile, 0, 0, 0, 0, UI_UNIT_X, UI_UNIT_X, TIP_("Tools"));
+ UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ /* Flip path */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ARROW_LEFTRIGHT,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Reverse Path"));
+ UI_but_funcN_set(bt, CurveProfile_buttons_reverse, MEM_dupallocN(cb), profile);
+
+ /* Clipping toggle */
+ icon = (profile->flag & PROF_USE_CLIP) ? ICON_CLIPUV_HLT : ICON_CLIPUV_DEHLT;
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ icon,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Toggle Profile Clipping"));
+ UI_but_funcN_set(bt, CurveProfile_clipping_toggle, MEM_dupallocN(cb), profile);
+
+ UI_block_funcN_set(block, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ /* The path itself */
+ path_width = max_ii(uiLayoutGetWidth(layout), UI_UNIT_X);
+ path_width = min_ii(path_width, (int)(16.0f * UI_UNIT_X));
+ path_height = path_width;
+ uiLayoutRow(layout, false);
+ uiDefBut(block,
+ UI_BTYPE_CURVEPROFILE,
+ 0,
+ "",
+ 0,
+ 0,
+ (short)path_width,
+ (short)path_height,
+ profile,
+ 0.0f,
+ 1.0f,
+ -1,
+ 0,
+ "");
+
+ /* Position sliders for (first) selected point */
+ for (i = 0; i < profile->path_len; i++) {
+ if (profile->path[i].flag & PROF_SELECT) {
+ point = &profile->path[i];
+ break;
+ }
+ }
+ if (i == 0 || i == profile->path_len - 1) {
+ point_last_or_first = true;
+ }
+
+ /* Selected point data */
+ if (point) {
+ if (profile->flag & PROF_USE_CLIP) {
+ bounds = profile->clip_rect;
+ }
+ else {
+ bounds.xmin = bounds.ymin = -1000.0;
+ bounds.xmax = bounds.ymax = 1000.0;
+ }
+
+ uiLayoutRow(layout, true);
+ UI_block_funcN_set(block, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
+
+ /* Sharp / Smooth */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_LINCURVE,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Set the point's handle type to sharp."));
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+ UI_but_funcN_set(bt, CurveProfile_buttons_setsharp, MEM_dupallocN(cb), profile);
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_SMOOTHCURVE,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Set the point's handle type to sharp."));
+ UI_but_funcN_set(bt, CurveProfile_buttons_setcurved, MEM_dupallocN(cb), profile);
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ /* Position */
+ bt = uiDefButF(block,
+ UI_BTYPE_NUM,
+ 0,
+ "X:",
+ 0,
+ 2 * UI_UNIT_Y,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &point->x,
+ bounds.xmin,
+ bounds.xmax,
+ 1,
+ 5,
+ "");
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ bt = uiDefButF(block,
+ UI_BTYPE_NUM,
+ 0,
+ "Y:",
+ 0,
+ 1 * UI_UNIT_Y,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &point->y,
+ bounds.ymin,
+ bounds.ymax,
+ 1,
+ 5,
+ "");
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ /* Delete points */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_X,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Delete points"));
+ UI_but_funcN_set(bt, CurveProfile_buttons_delete, MEM_dupallocN(cb), profile);
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+ }
+
+ uiItemR(layout, ptr, "use_sample_straight_edges", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_sample_even_lengths", 0, NULL, ICON_NONE);
+
+ UI_block_funcN_set(block, NULL, NULL, NULL);
+}
+
+/** Template for a path creation widget intended for custom bevel profiles.
+ * This section is quite similar to uiTemplateCurveMapping, but with reduced complexity */
+void uiTemplateCurveProfile(uiLayout *layout, PointerRNA *ptr, const char *propname)
+{
+ RNAUpdateCb *cb;
+ PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
+ PointerRNA cptr;
+ ID *id;
+ uiBlock *block = uiLayoutGetBlock(layout);
+
+ if (!prop) {
+ RNA_warning(
+ "Curve Profile property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if (RNA_property_type(prop) != PROP_POINTER) {
+ RNA_warning(
+ "Curve Profile is not a pointer: %s.%s", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ cptr = RNA_property_pointer_get(ptr, prop);
+ if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_CurveProfile)) {
+ return;
+ }
+
+ /* Share update functionality with the CurveMapping widget template. */
+ cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
+ cb->ptr = *ptr;
+ cb->prop = prop;
+
+ id = cptr.owner_id;
+ UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
+
+ CurveProfile_buttons_layout(layout, &cptr, cb);
+
+ UI_block_lock_clear(block);
+
+ MEM_freeN(cb);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ColorPicker Template
* \{ */
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 4cef9ace66e..7f7352517c8 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -4774,6 +4774,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
ui_draw_but_CURVE(ar, but, &tui->wcol_regular, rect);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_draw_but_CURVEPROFILE(ar, but, &tui->wcol_regular, rect);
+ break;
+
case UI_BTYPE_PROGRESS_BAR:
wt = widget_type(UI_WTYPE_PROGRESSBAR);
fstyle = &style->widgetlabel;
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 7afd72f33c9..acdf667b410 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -33,8 +33,10 @@
#include "BKE_unit.h"
#include "BKE_layer.h"
#include "BKE_mesh.h"
+#include "BKE_curveprofile.h"
#include "DNA_mesh_types.h"
+#include "DNA_curveprofile_types.h"
#include "RNA_define.h"
#include "RNA_access.h"
@@ -43,6 +45,7 @@
#include "WM_types.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "ED_mesh.h"
#include "ED_numinput.h"
@@ -96,6 +99,8 @@ typedef struct {
short gizmo_flag;
short value_mode; /* Which value does mouse movement and numeric input affect? */
float segments; /* Segments as float so smooth mouse pan works in small increments */
+
+ CurveProfile *custom_profile;
} BevelData;
enum {
@@ -114,6 +119,8 @@ enum {
BEV_MODAL_MARK_SHARP_TOGGLE,
BEV_MODAL_OUTER_MITER_CHANGE,
BEV_MODAL_INNER_MITER_CHANGE,
+ BEV_MODAL_CUSTOM_PROFILE_TOGGLE,
+ BEV_MODAL_VERTEX_MESH_CHANGE,
};
static float get_bevel_offset(wmOperator *op)
@@ -129,15 +136,15 @@ static float get_bevel_offset(wmOperator *op)
return val;
}
-static void edbm_bevel_update_header(bContext *C, wmOperator *op)
+static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
{
- char header[UI_MAX_DRAW_STR];
+ char status_text[UI_MAX_DRAW_STR];
char buf[UI_MAX_DRAW_STR];
char *p = buf;
int available_len = sizeof(buf);
Scene *sce = CTX_data_scene(C);
char offset_str[NUM_STR_REP_LEN];
- const char *mode_str, *omiter_str, *imiter_str;
+ const char *mode_str, *omiter_str, *imiter_str, *vmesh_str;
PropertyRNA *prop;
#define WM_MODALKEY(_id) \
@@ -167,22 +174,27 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op)
prop = RNA_struct_find_property(op->ptr, "miter_inner");
RNA_property_enum_name_gettexted(
C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &imiter_str);
-
- BLI_snprintf(header,
- sizeof(header),
- TIP_("%s: confirm, "
- "%s: cancel, "
- "%s: mode (%s), "
- "%s: width (%s), "
- "%s: segments (%d), "
- "%s: profile (%.3f), "
- "%s: clamp overlap (%s), "
- "%s: vertex only (%s), "
- "%s: outer miter (%s), "
- "%s: inner miter (%s), "
- "%s: harden normals (%s), "
- "%s: mark seam (%s), "
- "%s: mark sharp (%s)"),
+ prop = RNA_struct_find_property(op->ptr, "vmesh_method");
+ RNA_property_enum_name_gettexted(
+ C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &vmesh_str);
+
+ BLI_snprintf(status_text,
+ sizeof(status_text),
+ TIP_("%s: Confirm, "
+ "%s: Cancel, "
+ "%s: Mode (%s), "
+ "%s: Width (%s), "
+ "%s: Segments (%d), "
+ "%s: Profile (%.3f), "
+ "%s: Clamp Overlap (%s), "
+ "%s: Vertex Only (%s), "
+ "%s: Outer Miter (%s), "
+ "%s: Inner Miter (%s), "
+ "%s: Harden Normals (%s), "
+ "%s: Mark Seam (%s), "
+ "%s: Mark Sharp (%s), "
+ "%s: Custom Profile (%s), "
+ "%s: Intersection (%s)"),
WM_MODALKEY(BEV_MODAL_CONFIRM),
WM_MODALKEY(BEV_MODAL_CANCEL),
WM_MODALKEY(BEV_MODAL_OFFSET_MODE_CHANGE),
@@ -206,16 +218,21 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op)
WM_MODALKEY(BEV_MODAL_MARK_SEAM_TOGGLE),
WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_seam")),
WM_MODALKEY(BEV_MODAL_MARK_SHARP_TOGGLE),
- WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_sharp")));
+ WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_sharp")),
+ WM_MODALKEY(BEV_MODAL_CUSTOM_PROFILE_TOGGLE),
+ WM_bool_as_string(RNA_boolean_get(op->ptr, "use_custom_profile")),
+ WM_MODALKEY(BEV_MODAL_VERTEX_MESH_CHANGE),
+ vmesh_str);
#undef WM_MODALKEY
- ED_workspace_status_text(C, header);
+ ED_workspace_status_text(C, status_text);
}
static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
{
Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
BevelData *opdata;
ViewLayer *view_layer = CTX_data_view_layer(C);
float pixels_per_inch;
@@ -230,6 +247,9 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
uint objects_used_len = 0;
opdata->max_obj_scale = FLT_MIN;
+ /* Put the Curve Profile from the toolsettings into the opdata struct */
+ opdata->custom_profile = ts->custom_bevel_profile_preset;
+
{
uint ob_store_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
@@ -318,6 +338,8 @@ static bool edbm_bevel_calc(wmOperator *op)
const int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
const int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
const float spread = RNA_float_get(op->ptr, "spread");
+ const bool use_custom_profile = RNA_boolean_get(op->ptr, "use_custom_profile");
+ const int vmesh_method = RNA_enum_get(op->ptr, "vmesh_method");
for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
em = opdata->ob_store[ob_index].em;
@@ -344,7 +366,8 @@ static bool edbm_bevel_calc(wmOperator *op)
"bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
"clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
"harden_normals=%b face_strength_mode=%i "
- "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f",
+ "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f use_custom_profile=%b "
+ "custom_profile=%p vmesh_method=%i",
BM_ELEM_SELECT,
offset,
segments,
@@ -361,7 +384,10 @@ static bool edbm_bevel_calc(wmOperator *op)
miter_outer,
miter_inner,
spread,
- me->smoothresh);
+ me->smoothresh,
+ use_custom_profile,
+ opdata->custom_profile,
+ vmesh_method);
BMO_op_exec(em->bm, &bmop);
@@ -389,7 +415,6 @@ static bool edbm_bevel_calc(wmOperator *op)
static void edbm_bevel_exit(bContext *C, wmOperator *op)
{
BevelData *opdata = op->customdata;
-
ScrArea *sa = CTX_wm_area(C);
if (sa) {
@@ -430,7 +455,7 @@ static void edbm_bevel_cancel(bContext *C, wmOperator *op)
ED_region_tag_redraw(CTX_wm_region(C));
}
-/* bevel! yay!!*/
+/* bevel! yay!! */
static int edbm_bevel_exec(bContext *C, wmOperator *op)
{
if (!edbm_bevel_init(C, op, false)) {
@@ -500,7 +525,7 @@ static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
edbm_bevel_calc_initial_length(op, event, false);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
if (!edbm_bevel_calc(op)) {
edbm_bevel_cancel(C, op);
@@ -598,13 +623,9 @@ wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
static const EnumPropertyItem modal_items[] = {
{BEV_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel bevel"},
{BEV_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", "Confirm bevel"},
- {BEV_MODAL_VALUE_OFFSET, "VALUE_OFFSET", 0, "Value is offset", "Value changes offset"},
- {BEV_MODAL_VALUE_PROFILE, "VALUE_PROFILE", 0, "Value is profile", "Value changes profile"},
- {BEV_MODAL_VALUE_SEGMENTS,
- "VALUE_SEGMENTS",
- 0,
- "Value is segments",
- "Value changes segments"},
+ {BEV_MODAL_VALUE_OFFSET, "VALUE_OFFSET", 0, "Change offset", "Value changes offset"},
+ {BEV_MODAL_VALUE_PROFILE, "VALUE_PROFILE", 0, "Change profile", "Value changes profile"},
+ {BEV_MODAL_VALUE_SEGMENTS, "VALUE_SEGMENTS", 0, "Change segments", "Value changes segments"},
{BEV_MODAL_SEGMENTS_UP, "SEGMENTS_UP", 0, "Increase segments", "Increase segments"},
{BEV_MODAL_SEGMENTS_DOWN, "SEGMENTS_DOWN", 0, "Decrease segments", "Decrease segments"},
{BEV_MODAL_OFFSET_MODE_CHANGE,
@@ -647,12 +668,18 @@ wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
0,
"Change inner miter",
"Cycle through inner miter kinds"},
+ {BEV_MODAL_CUSTOM_PROFILE_TOGGLE, "CUSTOM_PROFILE_TOGGLE", 0, "Toggle custom profile", ""},
+ {BEV_MODAL_VERTEX_MESH_CHANGE,
+ "VERTEX_MESH_CHANGE",
+ 0,
+ "Change intersection method",
+ "Cycle through intersection methods"},
{0, NULL, 0, NULL, NULL},
};
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Bevel Modal Map");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each spacetype, only needs to add map once */
if (keymap && keymap->modal_items) {
return NULL;
}
@@ -682,14 +709,14 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
edbm_bevel_numinput_set_value(op);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
return OPERATOR_RUNNING_MODAL;
}
else if (etype == MOUSEMOVE) {
if (!has_numinput) {
edbm_bevel_mouse_set_value(op, event);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
}
}
@@ -703,7 +730,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
RNA_int_set(op->ptr, "segments", (int)opdata->segments);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
}
else if (etype == EVT_MODAL_MAP) {
@@ -723,7 +750,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
opdata->segments = opdata->segments + 1;
RNA_int_set(op->ptr, "segments", (int)opdata->segments);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
@@ -731,7 +758,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
opdata->segments = max_ff(opdata->segments - 1, 1);
RNA_int_set(op->ptr, "segments", (int)opdata->segments);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
@@ -758,7 +785,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
edbm_bevel_mouse_set_value(op, event);
}
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
@@ -766,7 +793,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
RNA_boolean_set(op->ptr, "clamp_overlap", !clamp_overlap);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -790,7 +817,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
RNA_boolean_set(op->ptr, "vertex_only", !vertex_only);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -799,7 +826,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
RNA_boolean_set(op->ptr, "mark_seam", !mark_seam);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -808,7 +835,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -824,7 +851,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
RNA_enum_set(op->ptr, "miter_inner", miter_inner);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -837,7 +864,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
RNA_enum_set(op->ptr, "miter_outer", miter_outer);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -846,7 +873,29 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
RNA_boolean_set(op->ptr, "harden_normals", !harden_normals);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
+ handled = true;
+ break;
+ }
+
+ case BEV_MODAL_CUSTOM_PROFILE_TOGGLE: {
+ bool use_custom_profile = RNA_boolean_get(op->ptr, "use_custom_profile");
+ RNA_boolean_set(op->ptr, "use_custom_profile", !use_custom_profile);
+ edbm_bevel_calc(op);
+ edbm_bevel_update_status_text(C, op);
+ handled = true;
+ break;
+ }
+
+ case BEV_MODAL_VERTEX_MESH_CHANGE: {
+ int vmesh_method = RNA_enum_get(op->ptr, "vmesh_method");
+ vmesh_method++;
+ if (vmesh_method > BEVEL_VMESH_CUTOFF) {
+ vmesh_method = BEVEL_VMESH_ADJ;
+ }
+ RNA_enum_set(op->ptr, "vmesh_method", vmesh_method);
+ edbm_bevel_calc(op);
+ edbm_bevel_update_status_text(C, op);
handled = true;
break;
}
@@ -858,13 +907,84 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
edbm_bevel_numinput_set_value(op);
edbm_bevel_calc(op);
- edbm_bevel_update_header(C, op);
+ edbm_bevel_update_status_text(C, op);
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_RUNNING_MODAL;
}
+static void edbm_bevel_ui(bContext *C, wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row, *col, *split;
+ PointerRNA ptr, toolsettings_ptr;
+ PropertyRNA *prop;
+ const char *offset_name;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ if (RNA_enum_get(&ptr, "offset_type") == BEVEL_AMT_PERCENT) {
+ uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE);
+ }
+ else {
+ switch (RNA_enum_get(&ptr, "offset_type")) {
+ case BEVEL_AMT_DEPTH:
+ offset_name = "Depth";
+ break;
+ case BEVEL_AMT_WIDTH:
+ offset_name = "Width";
+ break;
+ case BEVEL_AMT_OFFSET:
+ offset_name = "Offset";
+ break;
+ }
+ prop = RNA_struct_find_property(op->ptr, "offset_type");
+ RNA_property_enum_name_gettexted(
+ C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &offset_name);
+ uiItemR(layout, &ptr, "offset", 0, offset_name, ICON_NONE);
+ }
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &ptr, "offset_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ split = uiLayoutSplit(layout, 0.5f, true);
+ col = uiLayoutColumn(split, true);
+ uiItemR(col, &ptr, "vertex_only", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(split, true);
+ uiItemR(col, &ptr, "mark_seam", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "mark_sharp", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "profile", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
+
+ uiItemL(layout, "Miter Type:", ICON_NONE);
+ uiItemR(layout, &ptr, "miter_outer", 0, "Outer", ICON_NONE);
+ uiItemR(layout, &ptr, "miter_inner", 0, "Inner", ICON_NONE);
+ if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
+ uiItemR(layout, &ptr, "spread", 0, NULL, ICON_NONE);
+ }
+
+ uiItemL(layout, "Face Strength Mode:", ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &ptr, "face_strength_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ uiItemL(layout, "Intersection Type:", ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, &ptr, "vmesh_method", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "use_custom_profile", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(&ptr, "use_custom_profile")) {
+ /* Get an RNA pointer to ToolSettings to give to the curve profile template code */
+ Scene *scene = CTX_data_scene(C);
+ RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
+ uiTemplateCurveProfile(layout, &toolsettings_ptr, "custom_bevel_profile_preset");
+ }
+}
+
void MESH_OT_bevel(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -906,10 +1026,19 @@ void MESH_OT_bevel(wmOperatorType *ot)
{0, NULL, 0, NULL, NULL},
};
+ static EnumPropertyItem vmesh_method_items[] = {
+ {BEVEL_VMESH_ADJ, "ADJ", 0, "Grid Fill", "Default patterned fill"},
+ {BEVEL_VMESH_CUTOFF,
+ "CUTOFF",
+ 0,
+ "Cutoff",
+ "A cut-off at each profile's end before the intersection"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Bevel";
- ot->description =
- "Cut into selected items at an angle to create flat or rounded bevel or chamfer";
+ ot->description = "Cut into selected items at an angle to create bevel or chamfer";
ot->idname = "MESH_OT_bevel";
/* api callbacks */
@@ -919,19 +1048,23 @@ void MESH_OT_bevel(wmOperatorType *ot)
ot->cancel = edbm_bevel_cancel;
ot->poll = ED_operator_editmesh;
ot->poll_property = edbm_bevel_poll_property;
+ ot->ui = edbm_bevel_ui;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
+ /* properties */
RNA_def_enum(
ot->srna, "offset_type", offset_type_items, 0, "Width Type", "What distance Width measures");
prop = RNA_def_property(ot->srna, "offset", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_range(prop, 0.0, 1e6);
- RNA_def_property_ui_range(prop, 0.0f, 100.0, 1, 3);
+ RNA_def_property_ui_range(prop, 0.0, 100.0, 1, 3);
RNA_def_property_ui_text(prop, "Width", "Bevel amount");
+
prop = RNA_def_property(ot->srna, "offset_pct", PROP_FLOAT, PROP_PERCENTAGE);
RNA_def_property_range(prop, 0.0, 100);
RNA_def_property_ui_text(prop, "Width Percent", "Bevel amount for percentage method");
+
RNA_def_int(ot->srna,
"segments",
1,
@@ -941,6 +1074,7 @@ void MESH_OT_bevel(wmOperatorType *ot)
"Segments for curved edge",
1,
100);
+
RNA_def_float(ot->srna,
"profile",
0.5f,
@@ -950,16 +1084,22 @@ void MESH_OT_bevel(wmOperatorType *ot)
"Controls profile shape (0.5 = round)",
PROFILE_HARD_MIN,
1.0f);
+
RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
+
RNA_def_boolean(ot->srna,
"clamp_overlap",
false,
"Clamp Overlap",
"Do not allow beveled edges/vertices to overlap each other");
+
RNA_def_boolean(
- ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
+ ot->srna, "loop_slide", true, "Loop Slide", "Prefer sliding along edges to even widths");
+
RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges");
+
RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
+
RNA_def_int(ot->srna,
"material",
-1,
@@ -969,29 +1109,34 @@ void MESH_OT_bevel(wmOperatorType *ot)
"Material for bevel faces (-1 means use adjacent faces)",
-1,
100);
+
RNA_def_boolean(ot->srna,
"harden_normals",
false,
"Harden Normals",
"Match normals of new faces to adjacent faces");
+
RNA_def_enum(ot->srna,
"face_strength_mode",
face_strength_mode_items,
BEVEL_FACE_STRENGTH_NONE,
"Face Strength Mode",
"Whether to set face strength, and which faces to set face strength on");
+
RNA_def_enum(ot->srna,
"miter_outer",
miter_outer_items,
BEVEL_MITER_SHARP,
"Outer Miter",
"Pattern to use for outside of miters");
+
RNA_def_enum(ot->srna,
"miter_inner",
miter_inner_items,
BEVEL_MITER_SHARP,
"Inner Miter",
"Pattern to use for inside of miters");
+
RNA_def_float(ot->srna,
"spread",
0.1f,
@@ -1001,6 +1146,20 @@ void MESH_OT_bevel(wmOperatorType *ot)
"Amount to spread arcs for arc inner miters",
0.0f,
100.0f);
+
+ RNA_def_boolean(ot->srna,
+ "use_custom_profile",
+ false,
+ "Custom Profile",
+ "Use a custom profile for the bevel");
+
+ RNA_def_enum(ot->srna,
+ "vmesh_method",
+ vmesh_method_items,
+ BEVEL_VMESH_ADJ,
+ "Vertex Mesh Method",
+ "The method to use to create meshes at intersections");
+
prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h
new file mode 100644
index 00000000000..d7e3591a9b8
--- /dev/null
+++ b/source/blender/makesdna/DNA_curveprofile_types.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#ifndef DNA_PROFILEPATH_TYPES_H
+#define DNA_PROFILEPATH_TYPES_H
+
+#include "DNA_vec_types.h"
+
+/** Number of points in high resolution table is dynamic up to a maximum. */
+#define PROF_TABLE_MAX 512
+/** Number of table points per control point. */
+#define PROF_RESOL 16
+/** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */
+#define PROF_N_TABLE(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1)
+
+/** Each control point that makes up the profile.
+ * \note The flags use the same enum as Bezier curves, but they aren't garanteed
+ * to have identical functionality, and all types aren't implemented. */
+typedef struct CurveProfilePoint {
+ /** Location of the point, keep together. */
+ float x, y;
+ /** Flag selection state and others. */
+ short flag;
+ /** Flags for both handle's type (eBezTriple_Handle). */
+ char h1, h2;
+} CurveProfilePoint;
+
+/** #CurveProfilePoint.flag */
+enum {
+ PROF_SELECT = (1 << 0),
+};
+
+/** Defines a profile */
+typedef struct CurveProfile {
+ /** Number of user-added points that define the profile. */
+ short path_len;
+ /** Number of sampled points. */
+ short segments_len;
+ /** Preset to use when reset. */
+ int preset;
+ /** Sequence of points defining the shape of the curve. */
+ CurveProfilePoint *path;
+ /** Display and evaluation table at higher resolution for curves. */
+ CurveProfilePoint *table;
+ /** The positions of the sampled points. Used to display a preview of where they will be. */
+ CurveProfilePoint *segments;
+ /** Flag for mode states, sampling options, etc... */
+ int flag;
+ /** Used for keeping track how many times the widget is changed. */
+ int changed_timestamp;
+ /** Widget's current view, and clipping rect (is default rect too). */
+ rctf view_rect, clip_rect;
+} CurveProfile;
+
+/** #CurveProfile.flag */
+enum {
+ PROF_USE_CLIP = (1 << 0), /* Keep control points inside bounding rectangle. */
+ PROF_SYMMETRY_MODE = (1 << 1), /* Unused for now. */
+ PROF_SAMPLE_STRAIGHT_EDGES = (1 << 2), /* Sample extra points on straight edges. */
+ PROF_SAMPLE_EVEN_LENGTHS = (1 << 3), /* Put segments evenly spaced along the path. */
+};
+
+typedef enum eCurveProfilePresets {
+ PROF_PRESET_LINE = 0, /* Default simple line between end points. */
+ PROF_PRESET_SUPPORTS = 1, /* Support loops for a regular curved profile. */
+ PROF_PRESET_CORNICE = 2, /* Moulding type example. */
+ PROF_PRESET_CROWN = 3, /* Second moulding example. */
+ PROF_PRESET_STEPS = 4, /* Dynamic number of steps defined by totsegments. */
+} eCurveProfilePresets;
+
+#endif
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 9c4d7bcd3b1..a8db46238d8 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -377,10 +377,11 @@ typedef struct BevelModifierData {
short mat;
short edge_flags;
short face_str_mode;
- /* patterns to use for mitering non-reflex and reflex miter edges */
+ /** Patterns to use for mitering non-reflex and reflex miter edges */
short miter_inner;
short miter_outer;
- char _pad0[2];
+ /** The method to use for creating >2-way intersections */
+ short vmesh_method;
/** Controls profile shape (0->1, .5 is round). */
float profile;
/** if the MOD_BEVEL_ANGLE is set,
@@ -390,6 +391,10 @@ typedef struct BevelModifierData {
/** if the MOD_BEVEL_VWEIGHT option is set,
* this will be the name of the vert group, MAX_VGROUP_NAME */
char defgrp_name[64];
+
+ /** Curve info for the custom profile */
+ struct CurveProfile *custom_profile;
+
} BevelModifierData;
/* BevelModifierData->flags and BevelModifierData->lim_flags */
@@ -399,8 +404,8 @@ enum {
MOD_BEVEL_ANGLE = (1 << 3),
MOD_BEVEL_WEIGHT = (1 << 4),
MOD_BEVEL_VGROUP = (1 << 5),
- /* unused = (1 << 7), */
- /* unused = (1 << 8), */
+ MOD_BEVEL_CUSTOM_PROFILE = (1 << 7),
+ MOD_BEVEL_SAMPLE_STRAIGHT = (1 << 8),
/* unused = (1 << 9), */
/* unused = (1 << 10), */
/* unused = (1 << 11), */
@@ -439,6 +444,12 @@ enum {
MOD_BEVEL_MITER_ARC,
};
+/* BevelModifier->vmesh_method */
+enum {
+ MOD_BEVEL_VMESH_ADJ,
+ MOD_BEVEL_VMESH_CUTOFF,
+};
+
typedef struct SmokeModifierData {
ModifierData modifier;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 2ca9e3b2ef7..e60b16453b2 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -33,7 +33,8 @@
extern "C" {
#endif
-#include "DNA_color_types.h" /* color management */
+#include "DNA_color_types.h" /* color management */
+#include "DNA_curveprofile_types.h"
#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */
#include "DNA_vec_types.h"
#include "DNA_listBase.h"
@@ -50,6 +51,7 @@ struct Brush;
struct Collection;
struct ColorSpace;
struct CurveMapping;
+struct CurveProfile;
struct CustomData_MeshMasks;
struct Editing;
struct Image;
@@ -1515,6 +1517,11 @@ typedef struct ToolSettings {
/* Normal Editing */
float normal_vector[3];
char _pad6[4];
+
+ /* Custom Curve Profile for bevel tool:
+ * Temporary until there is a proper preset system that stores the profiles or maybe stores
+ * entire bevel configurations. */
+ struct CurveProfile *custom_bevel_profile_preset;
} ToolSettings;
/* *************************************************************** */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 80d37d89f14..d6f52e48ebe 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -131,6 +131,7 @@ static const char *includefiles[] = {
"DNA_layer_types.h",
"DNA_workspace_types.h",
"DNA_lightprobe_types.h",
+ "DNA_curveprofile_types.h",
/* see comment above before editing! */
@@ -1590,6 +1591,7 @@ int main(int argc, char **argv)
#include "DNA_layer_types.h"
#include "DNA_workspace_types.h"
#include "DNA_lightprobe_types.h"
+#include "DNA_curveprofile_types.h"
/* end of list */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a1369d84786..5bf6fb40c6a 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -209,6 +209,8 @@ extern StructRNA RNA_CurveMapPoint;
extern StructRNA RNA_CurveMapping;
extern StructRNA RNA_CurveModifier;
extern StructRNA RNA_CurvePoint;
+extern StructRNA RNA_CurveProfile;
+extern StructRNA RNA_CurveProfilePoint;
extern StructRNA RNA_DampedTrackConstraint;
extern StructRNA RNA_DataTransferModifier;
extern StructRNA RNA_DecimateModifier;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 458f031ceae..9290b81f1af 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -216,6 +216,8 @@ extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[];
extern const EnumPropertyItem rna_enum_abc_compression_items[];
extern const EnumPropertyItem rna_enum_context_mode_items[];
+extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
+
/* API calls */
int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo);
int rna_node_tree_idname_to_enum(const char *idname);
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 4bb53404724..7c06582dd6a 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -68,6 +68,7 @@ set(DEFSRC
rna_palette.c
rna_particle.c
rna_pose.c
+ rna_curveprofile.c
rna_render.c
rna_rigidbody.c
rna_rna.c
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 2d4da942610..009a723551e 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4255,6 +4255,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
+ {"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
{"rna_render.c", NULL, RNA_def_render},
{"rna_rigidbody.c", NULL, RNA_def_rigidbody},
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
new file mode 100644
index 00000000000..71e1aac5aba
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "DNA_curveprofile_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_texture_types.h"
+
+#include "BLI_utildefines.h"
+
+#include "RNA_define.h"
+#include "rna_internal.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#ifdef RNA_RUNTIME
+
+# include "RNA_access.h"
+
+# include "DNA_image_types.h"
+# include "DNA_material_types.h"
+# include "DNA_movieclip_types.h"
+# include "DNA_node_types.h"
+# include "DNA_object_types.h"
+# include "DNA_particle_types.h"
+# include "DNA_sequence_types.h"
+
+# include "MEM_guardedalloc.h"
+
+# include "BKE_colorband.h"
+# include "BKE_curveprofile.h"
+# include "BKE_image.h"
+# include "BKE_movieclip.h"
+# include "BKE_node.h"
+# include "BKE_sequencer.h"
+# include "BKE_linestyle.h"
+
+# include "DEG_depsgraph.h"
+
+# include "ED_node.h"
+
+# include "IMB_colormanagement.h"
+# include "IMB_imbuf.h"
+
+static void rna_CurveProfile_clip_set(PointerRNA *ptr, bool value)
+{
+ CurveProfile *profile = (CurveProfile *)ptr->data;
+
+ if (value) {
+ profile->flag |= PROF_USE_CLIP;
+ }
+ else {
+ profile->flag &= ~PROF_USE_CLIP;
+ }
+
+ BKE_curveprofile_update(profile, false);
+}
+
+static void rna_CurveProfile_sample_straight_set(PointerRNA *ptr, bool value)
+{
+ CurveProfile *profile = (CurveProfile *)ptr->data;
+
+ if (value) {
+ profile->flag |= PROF_SAMPLE_STRAIGHT_EDGES;
+ }
+ else {
+ profile->flag &= ~PROF_SAMPLE_STRAIGHT_EDGES;
+ }
+
+ BKE_curveprofile_update(profile, false);
+}
+
+static void rna_CurveProfile_sample_even_set(PointerRNA *ptr, bool value)
+{
+ CurveProfile *profile = (CurveProfile *)ptr->data;
+
+ if (value) {
+ profile->flag |= PROF_SAMPLE_EVEN_LENGTHS;
+ }
+ else {
+ profile->flag &= ~PROF_SAMPLE_EVEN_LENGTHS;
+ }
+
+ BKE_curveprofile_update(profile, false);
+}
+
+static void rna_CurveProfile_remove_point(CurveProfile *profile,
+ ReportList *reports,
+ PointerRNA *point_ptr)
+{
+ CurveProfilePoint *point = point_ptr->data;
+ if (BKE_curveprofile_remove_point(profile, point) == false) {
+ BKE_report(reports, RPT_ERROR, "Unable to remove path point");
+ return;
+ }
+
+ RNA_POINTER_INVALIDATE(point_ptr);
+}
+
+static void rna_CurveProfile_evaluate(struct CurveProfile *profile,
+ ReportList *reports,
+ float length_portion,
+ float *location)
+{
+ if (!profile->table) {
+ BKE_report(reports, RPT_ERROR, "CurveProfile table not initialized, call initialize()");
+ }
+ BKE_curveprofile_evaluate_length_portion(profile, length_portion, &location[0], &location[1]);
+}
+
+static void rna_CurveProfile_initialize(struct CurveProfile *profile, int segments_len)
+{
+ BKE_curveprofile_initialize(profile, (short)segments_len);
+}
+
+static void rna_CurveProfile_update(struct CurveProfile *profile)
+{
+ BKE_curveprofile_update(profile, false);
+}
+
+#else
+
+static const EnumPropertyItem prop_handle_type_items[] = {
+ {HD_AUTO, "AUTO", 0, "Auto Handle", ""},
+ {HD_VECT, "VECTOR", 0, "Vector Handle", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void rna_def_curveprofilepoint(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CurveProfilePoint", NULL);
+ RNA_def_struct_ui_text(srna, "CurveProfilePoint", "Point of a path used to define a profile");
+
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "x");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "Location", "X/Y coordinates of the path point");
+
+ prop = RNA_def_property(srna, "handle_type_1", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "h1");
+ RNA_def_property_enum_items(prop, prop_handle_type_items);
+ RNA_def_property_ui_text(
+ prop, "First Handle Type", "Path interpolation at this point: Bezier or vector");
+
+ prop = RNA_def_property(srna, "handle_type_2", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "h2");
+ RNA_def_property_enum_items(prop, prop_handle_type_items);
+ RNA_def_property_ui_text(
+ prop, "Second Handle Type", "Path interpolation at this point: Bezier or vector");
+
+ prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_SELECT);
+ RNA_def_property_ui_text(prop, "Select", "Selection state of the path point");
+}
+
+static void rna_def_curveprofile_points_api(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *parm;
+ FunctionRNA *func;
+
+ RNA_def_property_srna(cprop, "CurveProfilePoints");
+ srna = RNA_def_struct(brna, "CurveProfilePoints", NULL);
+ RNA_def_struct_sdna(srna, "CurveProfile");
+ RNA_def_struct_ui_text(srna, "Profile Point", "Collection of Profile Points");
+
+ func = RNA_def_function(srna, "add", "BKE_curveprofile_insert");
+ RNA_def_function_ui_description(func, "Add point to the profile");
+ parm = RNA_def_float(func,
+ "x",
+ 0.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ "X Position",
+ "X Position for new point",
+ -FLT_MAX,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float(func,
+ "y",
+ 0.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Y Position",
+ "Y Position for new point",
+ -FLT_MAX,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "point", "CurveProfilePoint", "", "New point");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_CurveProfile_remove_point");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Delete point from the profile");
+ parm = RNA_def_pointer(func, "point", "CurveProfilePoint", "", "Point to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+}
+
+static void rna_def_curveprofile(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+ PropertyRNA *parm;
+ FunctionRNA *func;
+
+ static const EnumPropertyItem rna_enum_curveprofile_preset_items[] = {
+ {PROF_PRESET_LINE, "LINE", 0, "Line", "Default"},
+ {PROF_PRESET_SUPPORTS, "SUPPORTS", 0, "Support Loops", "Loops on each side of the profile"},
+ {PROF_PRESET_CORNICE, "CORNICE", 0, "Cornice Moulding", ""},
+ {PROF_PRESET_CROWN, "CROWN", 0, "Crown Moulding", ""},
+ {PROF_PRESET_STEPS, "STEPS", 0, "Steps", "A number of steps defined by the segments"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "CurveProfile", NULL);
+ RNA_def_struct_ui_text(srna, "CurveProfile", "Profile Path editor used to build a profile path");
+
+ prop = RNA_def_property(srna, "preset", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "preset");
+ RNA_def_property_enum_items(prop, rna_enum_curveprofile_preset_items);
+ RNA_def_property_ui_text(prop, "Preset", "");
+
+ prop = RNA_def_property(srna, "use_clip", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_USE_CLIP);
+ RNA_def_property_ui_text(prop, "Clip", "Force the path view to fit a defined boundary");
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_CurveProfile_clip_set");
+
+ prop = RNA_def_property(srna, "use_sample_straight_edges", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_SAMPLE_STRAIGHT_EDGES);
+ RNA_def_property_ui_text(prop, "Sample Straight Edges", "Sample edges with vector handles");
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_CurveProfile_sample_straight_set");
+
+ prop = RNA_def_property(srna, "use_sample_even_lengths", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_SAMPLE_EVEN_LENGTHS);
+ RNA_def_property_ui_text(prop, "Sample Even Lengths", "Sample edges with even lengths");
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_CurveProfile_sample_even_set");
+
+ func = RNA_def_function(srna, "update", "rna_CurveProfile_update");
+ RNA_def_function_ui_description(func, "Update the profile");
+
+ func = RNA_def_function(srna, "initialize", "rna_CurveProfile_initialize");
+ parm = RNA_def_int(func,
+ "totsegments",
+ 1,
+ 1,
+ 1000,
+ "",
+ "The number of segment values to"
+ " initialize the segments table with",
+ 1,
+ 100);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_function_ui_description(func, "Set the number of display segments and fill tables");
+
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "path", "path_len");
+ RNA_def_property_struct_type(prop, "CurveProfilePoint");
+ RNA_def_property_ui_text(prop, "Points", "Profile control points");
+ rna_def_curveprofile_points_api(brna, prop);
+
+ prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "segments", "segments_len");
+ RNA_def_property_struct_type(prop, "CurveProfilePoint");
+ RNA_def_property_ui_text(prop, "Segments", "Segments sampled from control points");
+
+ func = RNA_def_function(srna, "evaluate", "rna_CurveProfile_evaluate");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Evaluate the at the given portion of the path length");
+ parm = RNA_def_float(func,
+ "length_portion",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Length Portion",
+ "Portion of the path length to travel before evaluation",
+ 0.0f,
+ 1.0f);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float_vector(func,
+ "location",
+ 2,
+ NULL,
+ -100.0f,
+ 100.0f,
+ "Location",
+ "The location at the given portion of the profile",
+ -100.0f,
+ 100.0f);
+ RNA_def_function_output(func, parm);
+}
+
+void RNA_def_profile(BlenderRNA *brna)
+{
+ rna_def_curveprofilepoint(brna);
+ rna_def_curveprofile(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 04a59a48b63..94394a4826b 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -178,6 +178,7 @@ void RNA_def_packedfile(struct BlenderRNA *brna);
void RNA_def_palette(struct BlenderRNA *brna);
void RNA_def_particle(struct BlenderRNA *brna);
void RNA_def_pose(struct BlenderRNA *brna);
+void RNA_def_profile(struct BlenderRNA *brna);
void RNA_def_lightprobe(struct BlenderRNA *brna);
void RNA_def_render(struct BlenderRNA *brna);
void RNA_def_rigidbody(struct BlenderRNA *brna);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 6e81b1343f5..789946d3268 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -37,6 +37,7 @@
#include "BLT_translation.h"
#include "BKE_animsys.h"
+#include "BKE_curveprofile.h"
#include "BKE_data_transfer.h"
#include "BKE_dynamicpaint.h"
#include "BKE_effect.h"
@@ -433,7 +434,6 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
};
#ifdef RNA_RUNTIME
-
# include "DNA_particle_types.h"
# include "DNA_curve_types.h"
# include "DNA_smoke_types.h"
@@ -998,6 +998,18 @@ static PointerRNA rna_CollisionModifier_settings_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_CollisionSettings, ob->pd);
}
+/* Special update function for setting the number of segments of the modifier that also resamples
+ * the segments in the custom profile. */
+static void rna_BevelModifier_update_segments(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ BevelModifierData *bmd = (BevelModifierData *)ptr->data;
+ if (RNA_boolean_get(ptr, "use_custom_profile")) {
+ short segments = (short)RNA_int_get(ptr, "segments");
+ BKE_curveprofile_initialize(bmd->custom_profile, segments);
+ }
+ rna_Modifier_update(bmain, scene, ptr);
+}
+
static void rna_UVProjectModifier_num_projectors_set(PointerRNA *ptr, int value)
{
UVProjectModifierData *md = (UVProjectModifierData *)ptr->data;
@@ -3583,10 +3595,26 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
- static EnumPropertyItem prop_miter_items[] = {
- {MOD_BEVEL_MITER_SHARP, "MITER_SHARP", 0, "Sharp", "Default sharp miter"},
- {MOD_BEVEL_MITER_PATCH, "MITER_PATCH", 0, "Patch", "Miter with extra corner"},
- {MOD_BEVEL_MITER_ARC, "MITER_ARC", 0, "Arc", "Miter with curved arc"},
+ static const EnumPropertyItem prop_miter_outer_items[] = {
+ {MOD_BEVEL_MITER_SHARP, "MITER_SHARP", 0, "Sharp", "Outside of miter is sharp"},
+ {MOD_BEVEL_MITER_PATCH, "MITER_PATCH", 0, "Patch", "Outside of miter is squared-off patch"},
+ {MOD_BEVEL_MITER_ARC, "MITER_ARC", 0, "Arc", "Outside of miter is arc"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem prop_miter_inner_items[] = {
+ {MOD_BEVEL_MITER_SHARP, "MITER_SHARP", 0, "Sharp", "Inside of miter is sharp"},
+ {MOD_BEVEL_MITER_ARC, "MITER_ARC", 0, "Arc", "Inside of miter is arc"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static EnumPropertyItem prop_vmesh_method_items[] = {
+ {MOD_BEVEL_VMESH_ADJ, "ADJ", 0, "Grid Fill", "Default patterned fill"},
+ {MOD_BEVEL_VMESH_CUTOFF,
+ "CUTOFF",
+ 0,
+ "Cutoff",
+ "A cut-off at the end of each profile before the intersection"},
{0, NULL, 0, NULL, NULL},
};
@@ -3614,7 +3642,7 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "res");
RNA_def_property_range(prop, 1, 100);
RNA_def_property_ui_text(prop, "Segments", "Number of segments for round edges/verts");
- RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ RNA_def_property_update(prop, 0, "rna_BevelModifier_update_segments");
prop = RNA_def_property(srna, "use_only_vertices", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_BEVEL_VERT);
@@ -3648,7 +3676,7 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
prop = RNA_def_property(srna, "offset_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "val_flags");
RNA_def_property_enum_items(prop, prop_val_type_items);
- RNA_def_property_ui_text(prop, "Amount Type", "What distance Width measures");
+ RNA_def_property_ui_text(prop, "Width Type", "What distance Width measures");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "profile", PROP_FLOAT, PROP_FACTOR);
@@ -3693,13 +3721,13 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
prop = RNA_def_property(srna, "miter_outer", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "miter_outer");
- RNA_def_property_enum_items(prop, prop_miter_items);
+ RNA_def_property_enum_items(prop, prop_miter_outer_items);
RNA_def_property_ui_text(prop, "Outer Miter", "Pattern to use for outside of miters");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "miter_inner", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "miter_inner");
- RNA_def_property_enum_items(prop, prop_miter_items);
+ RNA_def_property_enum_items(prop, prop_miter_inner_items);
RNA_def_property_ui_text(prop, "Inner Miter", "Pattern to use for inside of miters");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -3709,6 +3737,25 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0.0f, 100.0f, 0.1, 4);
RNA_def_property_ui_text(prop, "Spread", "Spread distance for inner miter arcs");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_custom_profile", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_BEVEL_CUSTOM_PROFILE);
+ RNA_def_property_ui_text(
+ prop, "Custom Profile", "Whether to use a user inputed curve for the bevel's profile");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "custom_profile", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveProfile");
+ RNA_def_property_pointer_sdna(prop, NULL, "custom_profile");
+ RNA_def_property_ui_text(prop, "Custom Profile Path", "The path for the custom profile");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "vmesh_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "vmesh_method");
+ RNA_def_property_enum_items(prop, prop_vmesh_method_items);
+ RNA_def_property_ui_text(
+ prop, "Vertex Mesh Method", "The method to use to create the mesh at intersections");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_shrinkwrap(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index b9bc7b2bbf5..4d3de4ed6e6 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3307,6 +3307,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "MeshStatVis");
RNA_def_property_ui_text(prop, "Mesh Statistics Visualization", NULL);
+
+ /* CurveProfile */
+ prop = RNA_def_property(srna, "custom_bevel_profile_preset", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "custom_bevel_profile_preset");
+ RNA_def_property_struct_type(prop, "CurveProfile");
+ RNA_def_property_ui_text(prop, "Curve Profile Widget", "Used for defining a profile's path");
}
static void rna_def_unified_paint_settings(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index 94fe43a0a77..c734720fdcd 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1220,6 +1220,10 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_boolean(func, "use_negative_slope", false, "", "Use a negative slope by default");
RNA_def_boolean(func, "show_tone", false, "", "Show tone options");
+ func = RNA_def_function(srna, "template_curveprofile", "uiTemplateCurveProfile");
+ RNA_def_function_ui_description(func, "A profile path editor used for custom profiles");
+ api_ui_item_rna_common(func);
+
func = RNA_def_function(srna, "template_color_ramp", "uiTemplateColorRamp");
RNA_def_function_ui_description(func, "Item. A color ramp widget");
api_ui_item_rna_common(func);
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index 0c00bb572be..17384f133b1 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -31,6 +31,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_curveprofile_types.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
@@ -40,6 +41,7 @@
#include "bmesh.h"
#include "bmesh_tools.h"
+#include "BKE_curveprofile.h"
#include "DEG_depsgraph_query.h"
@@ -47,7 +49,7 @@ static void initData(ModifierData *md)
{
BevelModifierData *bmd = (BevelModifierData *)md;
- bmd->value = 0.1f;
+ bmd->value = 1.0f;
bmd->res = 1;
bmd->flags = 0;
bmd->val_flags = MOD_BEVEL_AMT_OFFSET;
@@ -62,11 +64,16 @@ static void initData(ModifierData *md)
bmd->profile = 0.5f;
bmd->bevel_angle = DEG2RADF(30.0f);
bmd->defgrp_name[0] = '\0';
+ bmd->custom_profile = BKE_curveprofile_add(PROF_PRESET_LINE);
}
static void copyData(const ModifierData *md_src, ModifierData *md_dst, const int flag)
{
+ const BevelModifierData *bmd_src = (const BevelModifierData *)md_src;
+ BevelModifierData *bmd_dst = (BevelModifierData *)md_dst;
+
modifier_copyData_generic(md_src, md_dst, flag);
+ bmd_dst->custom_profile = BKE_curveprofile_copy(bmd_src->custom_profile);
}
static void requiredDataMask(Object *UNUSED(ob),
@@ -109,6 +116,8 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
const int miter_outer = bmd->miter_outer;
const int miter_inner = bmd->miter_inner;
const float spread = bmd->spread;
+ const bool use_custom_profile = (bmd->flags & MOD_BEVEL_CUSTOM_PROFILE);
+ const int vmesh_method = bmd->vmesh_method;
bm = BKE_mesh_to_bmesh_ex(mesh,
&(struct BMeshCreateParams){0},
@@ -210,7 +219,10 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
miter_outer,
miter_inner,
spread,
- mesh->smoothresh);
+ mesh->smoothresh,
+ use_custom_profile,
+ bmd->custom_profile,
+ vmesh_method);
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
@@ -229,6 +241,18 @@ static bool dependsOnNormals(ModifierData *UNUSED(md))
return true;
}
+static void freeData(ModifierData *md)
+{
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ BKE_curveprofile_free(bmd->custom_profile);
+}
+
+static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(userRenderParams))
+{
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ return (bmd->value == 0.0f);
+}
+
ModifierTypeInfo modifierType_Bevel = {
/* name */ "Bevel",
/* structName */ "BevelModifierData",
@@ -236,19 +260,16 @@ ModifierTypeInfo modifierType_Bevel = {
/* type */ eModifierTypeType_Constructive,
/* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_AcceptsCVs,
-
/* copyData */ copyData,
-
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* applyModifier */ applyModifier,
-
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
- /* freeData */ NULL,
- /* isDisabled */ NULL,
+ /* freeData */ freeData,
+ /* isDisabled */ isDisabled,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ dependsOnNormals,