50enum eBrushUVSculptTool {
51 UV_SCULPT_BRUSH_TYPE_GRAB = 0,
52 UV_SCULPT_BRUSH_TYPE_RELAX = 1,
53 UV_SCULPT_BRUSH_TYPE_PINCH = 2,
57 UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN = 0,
58 UV_SCULPT_BRUSH_TYPE_RELAX_HC = 1,
59 UV_SCULPT_BRUSH_TYPE_RELAX_COTAN = 2,
65#define MARK_BOUNDARY 1
67struct UvAdjacencyElement {
85struct UVInitialStrokeElement {
96struct UVInitialStroke {
98 UVInitialStrokeElement *initialSelection;
101 int totalInitialSelected;
113 UvAdjacencyElement *uv;
125 UVInitialStroke *initial_stroke;
131 UvElementMap *elementMap;
143 bool constrain_to_bounds;
146 float uv_base_offset[2];
153 if (!sculptdata->constrain_to_bounds) {
156 float u = sculptdata->uv_base_offset[0];
157 float v = sculptdata->uv_base_offset[1];
158 uv[0] =
clamp_f(uv[0], u, u + 1.0f);
170 CLAMP(strength, 0.0f, 1.0f);
186 const int cd_loop_uv_offset,
187 const float mouse_coord[2],
189 const float radius_sq,
190 const float aspectRatio)
194 const float radius =
sqrtf(radius_sq);
200 for (
i = 0;
i < sculptdata->totalUvEdges;
i++) {
201 UvEdge *tmpedge = sculptdata->uvedges +
i;
202 tmp_uvdata[tmpedge->uv1].
ncounter++;
203 tmp_uvdata[tmpedge->uv2].
ncounter++;
205 add_v2_v2(tmp_uvdata[tmpedge->uv2].
sum_co, sculptdata->uv[tmpedge->uv1].uv);
206 add_v2_v2(tmp_uvdata[tmpedge->uv1].
sum_co, sculptdata->uv[tmpedge->uv2].uv);
209 for (
i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
214 tmp_uvdata[
i].
b[0] =
diff[0] - sculptdata->uv[
i].uv[0];
215 tmp_uvdata[
i].
b[1] =
diff[1] - sculptdata->uv[
i].uv[1];
218 for (
i = 0;
i < sculptdata->totalUvEdges;
i++) {
219 UvEdge *tmpedge = sculptdata->uvedges +
i;
224 for (
i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
225 if (sculptdata->uv[
i].is_locked) {
230 diff[1] /= aspectRatio;
232 if (dist <= radius_sq) {
237 sculptdata->uv[
i].uv[0] = (1.0f - strength) * sculptdata->uv[
i].uv[0] +
239 (tmp_uvdata[
i].
p[0] -
240 0.5f * (tmp_uvdata[
i].
b[0] +
242 sculptdata->uv[
i].uv[1] = (1.0f - strength) * sculptdata->uv[
i].uv[1] +
244 (tmp_uvdata[
i].
p[1] -
245 0.5f * (tmp_uvdata[
i].
b[1] +
271 const int cd_loop_uv_offset,
272 const float mouse_coord[2],
274 const float radius_sq,
275 const float aspectRatio)
279 const float radius =
sqrtf(radius_sq);
285 for (
i = 0;
i < sculptdata->totalUvEdges;
i++) {
286 UvEdge *tmpedge = sculptdata->uvedges +
i;
287 bool code1 = sculptdata->uv[sculptdata->uvedges[
i].uv1].is_boundary;
288 bool code2 = sculptdata->uv[sculptdata->uvedges[
i].uv2].is_boundary;
289 if (code1 || (code1 == code2)) {
290 tmp_uvdata[tmpedge->uv2].
ncounter++;
291 add_v2_v2(tmp_uvdata[tmpedge->uv2].
sum_co, sculptdata->uv[tmpedge->uv1].uv);
293 if (code2 || (code1 == code2)) {
294 tmp_uvdata[tmpedge->uv1].
ncounter++;
295 add_v2_v2(tmp_uvdata[tmpedge->uv1].
sum_co, sculptdata->uv[tmpedge->uv2].uv);
301 for (
i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
303 mul_v2_fl(tmp_uvdata[
i].p, 1.0f / tmp_uvdata[
i].ncounter);
306 for (
i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
307 if (sculptdata->uv[
i].is_locked) {
312 diff[1] /= aspectRatio;
314 if (dist <= radius_sq) {
319 sculptdata->uv[
i].uv[0] = (1.0f - strength) * sculptdata->uv[
i].uv[0] +
320 strength * tmp_uvdata[
i].
p[0];
321 sculptdata->uv[
i].uv[1] = (1.0f - strength) * sculptdata->uv[
i].uv[1] +
322 strength * tmp_uvdata[
i].
p[1];
344 const float luv_next[2],
345 const float luv_prev[2],
353 if (code1 || (code1 == code2)) {
354 int index_next = ele_next - storage;
355 delta_buf[index_next][0] -= delta[0] * weight;
356 delta_buf[index_next][1] -= delta[1] * weight;
357 delta_buf[index_next][2] +=
fabsf(weight);
359 if (code2 || (code1 == code2)) {
360 int index_prev = ele_prev - storage;
361 delta_buf[index_prev][0] += delta[0] * weight;
362 delta_buf[index_prev][1] += delta[1] * weight;
363 delta_buf[index_prev][2] +=
fabsf(weight);
367static float tri_weight_v3(
int method,
const float *v1,
const float *
v2,
const float *v3)
370 case UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN:
371 case UV_SCULPT_BRUSH_TYPE_RELAX_HC:
373 case UV_SCULPT_BRUSH_TYPE_RELAX_COTAN:
382 const int cd_loop_uv_offset,
383 const float mouse_coord[2],
385 const float radius_sq,
386 const float aspect_ratio,
389 if (method == UV_SCULPT_BRUSH_TYPE_RELAX_HC) {
391 sculptdata, cd_loop_uv_offset, mouse_coord, alpha, radius_sq, aspect_ratio);
394 if (method == UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN) {
396 sculptdata, cd_loop_uv_offset, mouse_coord, alpha, radius_sq, aspect_ratio);
402 const int total_uvs = sculptdata->elementMap->
total_uvs;
406 for (
int j = 0; j < total_uvs; j++) {
411 const float *v_curr_co = ele_curr->
l->
v->
co;
412 const float *v_prev_co = ele_prev->
l->
v->
co;
413 const float *v_next_co = ele_next->
l->
v->
co;
419 const UvElement *head_curr = head_table[ele_curr - sculptdata->elementMap->
storage];
420 const UvElement *head_next = head_table[ele_next - sculptdata->elementMap->
storage];
421 const UvElement *head_prev = head_table[ele_prev - sculptdata->elementMap->
storage];
424 const float weight_curr =
tri_weight_v3(method, v_curr_co, v_prev_co, v_next_co);
425 add_weighted_edge(delta_buf, storage, head_next, head_prev, *luv_next, *luv_prev, weight_curr);
428 const float weight_prev =
tri_weight_v3(method, v_prev_co, v_curr_co, v_next_co);
429 add_weighted_edge(delta_buf, storage, head_next, head_curr, *luv_next, *luv_curr, weight_prev);
431 if (method == UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN) {
437 const float weight_next =
tri_weight_v3(method, v_next_co, v_curr_co, v_prev_co);
438 add_weighted_edge(delta_buf, storage, head_prev, head_curr, *luv_prev, *luv_curr, weight_next);
441 for (
int i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
442 UvAdjacencyElement *adj_el = &sculptdata->uv[
i];
443 if (adj_el->is_locked) {
450 diff[1] /= aspect_ratio;
452 if (dist_sq > radius_sq) {
457 const float *delta_sum = delta_buf[adj_el->element - storage];
462 adj_el->uv[0] = (*luv)[0] + strength *
safe_divide(delta_sum[0], delta_sum[2]);
463 adj_el->uv[1] = (*luv)[1] + strength *
safe_divide(delta_sum[1], delta_sum[2]);
489 UvSculptData *sculptdata = (UvSculptData *)op->
customdata;
490 eBrushUVSculptTool tool = eBrushUVSculptTool(sculptdata->tool);
491 int invert = sculptdata->invert ? -1 : 1;
492 float alpha = sculptdata->uvsculpt->
strength;
505 const float radius = (sculptdata->uvsculpt->
size / 2.0f) / (width * zoomx);
506 float aspectRatio = width /
float(height);
509 const float radius_sq = radius * radius;
514 case UV_SCULPT_BRUSH_TYPE_PINCH: {
517 for (
i = 0;
i < sculptdata->totalUniqueUvs;
i++) {
518 if (sculptdata->uv[
i].is_locked) {
524 diff[1] /= aspectRatio;
526 if (dist <= radius_sq) {
532 sculptdata->uv[
i].uv[0] -= strength *
diff[0] * 0.001f;
533 sculptdata->uv[
i].uv[1] -= strength *
diff[1] * 0.001f;
548 case UV_SCULPT_BRUSH_TYPE_RELAX: {
558 case UV_SCULPT_BRUSH_TYPE_GRAB: {
563 for (
i = 0;
i < sculptdata->initial_stroke->totalInitialSelected;
i++) {
565 int uvindex = sculptdata->initial_stroke->initialSelection[
i].uv;
566 float strength = sculptdata->initial_stroke->initialSelection[
i].strength;
567 sculptdata->uv[uvindex].uv[0] =
568 sculptdata->initial_stroke->initialSelection[
i].initial_uv[0] + strength *
diff[0];
569 sculptdata->uv[uvindex].uv[1] =
570 sculptdata->initial_stroke->initialSelection[
i].initial_uv[1] + strength *
diff[1];
575 if (
element->separate &&
element != sculptdata->uv[uvindex].element) {
601 data->elementMap =
nullptr;
604 if (
data->initial_stroke) {
616 const bool do_islands)
619 if (!
element || (do_islands &&
element->island != island_index)) {
627 const UvEdge *edge =
static_cast<const UvEdge *
>(key);
633 const UvEdge *edge1 =
static_cast<const UvEdge *
>(a);
634 const UvEdge *edge2 =
static_cast<const UvEdge *
>(
b);
636 if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) {
680 int island_index = 0;
682 data->tool = UV_SCULPT_BRUSH_TYPE_RELAX;
685 data->tool = UV_SCULPT_BRUSH_TYPE_GRAB;
688 data->tool = UV_SCULPT_BRUSH_TYPE_PINCH;
697 const bool use_winding =
false;
698 const bool use_seams =
true;
700 bm, scene,
false, use_winding, use_seams, do_island_optimization);
702 if (!
data->elementMap) {
711 if (do_island_optimization) {
717 island_index =
element->island;
722 int unique_uvs =
data->elementMap->total_unique_uvs;
723 if (do_island_optimization) {
724 unique_uvs =
data->elementMap->island_total_unique_uvs[island_index];
734 if (!
data->uv || !uniqueUv || !edgeHash || !edges) {
744 data->totalUniqueUvs = unique_uvs;
750 for (
int i = 0;
i <
bm->totvert;
i++) {
754 if (do_island_optimization && (
element->island != island_index)) {
766 data->uv[counter].uv = *luv;
767 if (
data->tool != UV_SCULPT_BRUSH_TYPE_GRAB) {
769 data->uv[counter].is_locked =
true;
774 uniqueUv[
element -
data->elementMap->storage] = counter;
784 data->elementMap,
l, island_index, do_island_optimization);
786 data->elementMap,
l->next, island_index, do_island_optimization);
789 if (itmp1 == -1 || itmp2 == -1) {
793 int offset1 = uniqueUv[itmp1];
794 int offset2 = uniqueUv[itmp2];
798 if (offset1 < offset2) {
799 edges[counter].uv1 = offset1;
800 edges[counter].uv2 = offset2;
803 edges[counter].uv1 = offset2;
804 edges[counter].uv2 = offset1;
806 UvEdge *prev_edge =
static_cast<UvEdge *
>(
BLI_ghash_lookup(edgeHash, &edges[counter]));
808 prev_edge->is_interior =
true;
809 edges[counter].is_interior =
true;
822 if (!
data->uvedges) {
843 for (
int i = 0;
i <
data->totalUvEdges;
i++) {
844 if (!
data->uvedges[
i].is_interior) {
845 data->uv[
data->uvedges[
i].uv1].is_boundary =
true;
846 data->uv[
data->uvedges[
i].uv2].is_boundary =
true;
848 data->uv[
data->uvedges[
i].uv1].is_locked =
true;
849 data->uv[
data->uvedges[
i].uv2].is_locked =
true;
861 if (
data->tool == UV_SCULPT_BRUSH_TYPE_GRAB) {
862 float alpha =
data->uvsculpt->strength;
863 float radius =
data->uvsculpt->size / 2.0f;
869 float aspectRatio = width /
float(height);
870 radius /= (width * zoomx);
871 const float radius_sq = radius * radius;
874 data->initial_stroke =
static_cast<UVInitialStroke *
>(
876 if (!
data->initial_stroke) {
880 data->totalUniqueUvs, __func__);
881 if (!
data->initial_stroke->initialSelection) {
888 for (
int i = 0;
i <
data->totalUniqueUvs;
i++) {
889 if (
data->uv[
i].is_locked) {
895 diff[1] /= aspectRatio;
897 if (dist <= radius_sq) {
901 data->initial_stroke->initialSelection[counter].uv =
i;
902 data->initial_stroke->initialSelection[counter].strength = strength;
908 data->initial_stroke->totalInitialSelected = counter;
915 return static_cast<UvSculptData *
>(op->
customdata);
945 switch (event->
type) {
976 ot->srna,
"use_invert",
false,
"Invert",
"Invert action for the duration of the stroke");
982 ot->name =
"Grab UVs";
983 ot->description =
"Grab UVs";
984 ot->idname =
"SCULPT_OT_uv_sculpt_grab";
997 ot->name =
"Relax UVs";
998 ot->description =
"Relax UVs";
999 ot->idname =
"SCULPT_OT_uv_sculpt_relax";
1010 {UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN,
1014 "Use Laplacian method for relaxation"},
1015 {UV_SCULPT_BRUSH_TYPE_RELAX_HC,
"HC", 0,
"HC",
"Use HC method for relaxation"},
1016 {UV_SCULPT_BRUSH_TYPE_RELAX_COTAN,
1020 "Use Geometry (cotangent) relaxation, making UVs follow the underlying 3D geometry"},
1021 {0,
nullptr, 0,
nullptr,
nullptr},
1027 UV_SCULPT_BRUSH_TYPE_RELAX_LAPLACIAN,
1029 "Algorithm used for UV relaxation");
1034 ot->name =
"Pinch UVs";
1035 ot->description =
"Pinch UVs";
1036 ot->idname =
"SCULPT_OT_uv_sculpt_pinch";
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
SpaceImage * CTX_wm_space_image(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) ATTR_NONNULL(2
#define BLI_assert_unreachable()
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghashutil_uinthash(unsigned int key)
#define GHASH_ITER(gh_iter_, ghash_)
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float safe_divide(float a, float b)
float cotangent_tri_weight_v3(const float v1[3], const float v2[3], const float v3[3])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
void DEG_id_tag_update(ID *id, unsigned int flags)
Object is a sort of wrapper for general info.
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
void ED_space_image_get_zoom(SpaceImage *sima, const ARegion *region, float *r_zoomx, float *r_zoomy)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
void BM_uv_element_map_free(UvElementMap *element_map)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
UvElement ** BM_uv_element_map_ensure_head_table(UvElementMap *element_map)
bool ED_operator_uvedit_space_image(bContext *C)
void ED_region_tag_redraw(ARegion *region)
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
void ED_uvedit_live_unwrap_re_solve()
void ED_uvedit_live_unwrap_end(bool cancel)
Read Guarded memory(de)allocation.
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_ELEM_CD_GET_FLOAT2_P(ele, offset)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
void * MEM_mallocN(size_t len, const char *str)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
void * MEM_callocN(size_t len, const char *str)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void SCULPT_OT_uv_sculpt_relax(wmOperatorType *ot)
static wmOperatorStatus uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void laplacian_relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspectRatio)
static void register_common_props(wmOperatorType *ot)
static UvSculptData * uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
static void relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspect_ratio, const int method)
static void add_weighted_edge(float(*delta_buf)[3], const UvElement *storage, const UvElement *ele_next, const UvElement *ele_prev, const float luv_next[2], const float luv_prev[2], const float weight)
static bool uv_edge_compare(const void *a, const void *b)
static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3)
static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2])
static int uv_element_offset_from_face_get(UvElementMap *map, BMLoop *l, int island_index, const bool do_islands)
static void HC_relaxation_iteration_uv(UvSculptData *sculptdata, const int cd_loop_uv_offset, const float mouse_coord[2], const float alpha, const float radius_sq, const float aspectRatio)
static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *event, Object *obedit)
static uint uv_edge_hash(const void *key)
static wmOperatorStatus uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_uv_sculpt_pinch(wmOperatorType *ot)
static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op)
static void set_element_flag(UvElement *element, const int flag)
void SCULPT_OT_uv_sculpt_grab(wmOperatorType *ot)
static float calc_strength(const UvSculptData *sculptdata, float p, const float len)
struct ToolSettings * toolsettings
struct CurveMapping * curve_distance_falloff
int8_t curve_distance_falloff_preset
struct wmOperatorType * type
bool uv_find_nearest_vert(Scene *scene, Object *obedit, const float co[2], float penalty_dist, UvNearestHit *hit)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)