This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
2021-03-12 22:29:37 +01:00

434 lines
13 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
void ED_sculpt_init_transform(struct bContext *C, Object *ob)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
copy_v3_v3(ss->init_pivot_pos, ss->pivot_pos);
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale);
copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
SCULPT_undo_push_begin(ob, "Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL;
}
static void sculpt_transform_matrices_init(SculptSession *ss,
const char symm,
const SculptTransformDisplacementMode t_mode,
float r_transform_mats[8][4][4])
{
float final_pivot_pos[3], d_t[3], d_r[4], d_s[3];
float t_mat[4][4], r_mat[4][4], s_mat[4][4], pivot_mat[4][4], pivot_imat[4][4],
transform_mat[4][4];
float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
switch (t_mode) {
case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
break;
case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos);
copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot);
copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale);
break;
}
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
ePaintSymmetryAreas v_symm = i;
copy_v3_v3(final_pivot_pos, ss->pivot_pos);
unit_m4(pivot_mat);
unit_m4(t_mat);
unit_m4(r_mat);
unit_m4(s_mat);
/* Translation matrix. */
sub_v3_v3v3(d_t, ss->pivot_pos, start_pivot_pos);
SCULPT_flip_v3_by_symm_area(d_t, symm, v_symm, ss->init_pivot_pos);
translate_m4(t_mat, d_t[0], d_t[1], d_t[2]);
/* Rotation matrix. */
sub_qt_qtqt(d_r, ss->pivot_rot, start_pivot_rot);
normalize_qt(d_r);
SCULPT_flip_quat_by_symm_area(d_r, symm, v_symm, ss->init_pivot_pos);
quat_to_mat4(r_mat, d_r);
/* Scale matrix. */
sub_v3_v3v3(d_s, ss->pivot_scale, start_pivot_scale);
add_v3_fl(d_s, 1.0f);
size_to_mat4(s_mat, d_s);
/* Pivot matrix. */
SCULPT_flip_v3_by_symm_area(final_pivot_pos, symm, v_symm, start_pivot_pos);
translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]);
invert_m4_m4(pivot_imat, pivot_mat);
/* Final transform matrix. */
mul_m4_m4m4(transform_mat, r_mat, t_mat);
mul_m4_m4m4(transform_mat, transform_mat, s_mat);
mul_m4_m4m4(r_transform_mats[i], transform_mat, pivot_imat);
mul_m4_m4m4(r_transform_mats[i], pivot_mat, r_transform_mats[i]);
}
}
static void sculpt_transform_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
PBVHVertexIter vd;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
float transformed_co[3], orig_co[3], disp[3];
float *start_co;
float fade = vd.mask ? *vd.mask : 0.0f;
copy_v3_v3(orig_co, orig_data.co);
char symm_area = SCULPT_get_vertex_symm_area(orig_co);
switch (ss->filter_cache->transform_displacement_mode) {
case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
start_co = orig_co;
break;
case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
start_co = vd.co;
break;
}
copy_v3_v3(transformed_co, start_co);
mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co);
sub_v3_v3v3(disp, transformed_co, start_co);
mul_v3_fl(disp, 1.0f - fade);
add_v3_v3v3(vd.co, start_co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(node);
}
static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
};
sculpt_transform_matrices_init(
ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
}
void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
sculpt_transform_all_vertices(sd, ob);
copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
if (ss->deform_modifiers_active || ss->shapekey_active) {
SCULPT_flush_stroke_deform(sd, ob, true);
}
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
}
void ED_sculpt_end_transform(struct bContext *C, Object *ob)
{
SculptSession *ss = ob->sculpt;
if (ss->filter_cache) {
SCULPT_filter_cache_free(ss);
}
/* Force undo push to happen even inside transform operator, since the sculpt
* undo system works separate from regular undo and this is require to properly
* finish an undo step also when canceling. */
const bool use_nested_undo = true;
SCULPT_undo_push_end_ex(use_nested_undo);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
typedef enum eSculptPivotPositionModes {
SCULPT_PIVOT_POSITION_ORIGIN = 0,
SCULPT_PIVOT_POSITION_UNMASKED = 1,
SCULPT_PIVOT_POSITION_MASK_BORDER = 2,
SCULPT_PIVOT_POSITION_ACTIVE_VERTEX = 3,
SCULPT_PIVOT_POSITION_CURSOR_SURFACE = 4,
} eSculptPivotPositionModes;
static EnumPropertyItem prop_sculpt_pivot_position_types[] = {
{SCULPT_PIVOT_POSITION_ORIGIN,
"ORIGIN",
0,
"Origin",
"Sets the pivot to the origin of the sculpt"},
{SCULPT_PIVOT_POSITION_UNMASKED,
"UNMASKED",
0,
"Unmasked",
"Sets the pivot position to the average position of the unmasked vertices"},
{SCULPT_PIVOT_POSITION_MASK_BORDER,
"BORDER",
0,
"Mask Border",
"Sets the pivot position to the center of the border of the mask"},
{SCULPT_PIVOT_POSITION_ACTIVE_VERTEX,
"ACTIVE",
0,
"Active Vertex",
"Sets the pivot position to the active vertex position"},
{SCULPT_PIVOT_POSITION_CURSOR_SURFACE,
"SURFACE",
0,
"Surface",
"Sets the pivot position to the surface under the cursor"},
{0, NULL, 0, NULL, NULL},
};
static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
ARegion *region = CTX_wm_region(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
int mode = RNA_enum_get(op->ptr, "mode");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
/* Pivot to center. */
if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {
zero_v3(ss->pivot_pos);
}
/* Pivot to active vertex. */
else if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) {
copy_v3_v3(ss->pivot_pos, SCULPT_active_vertex_co_get(ss));
}
/* Pivot to raycast surface. */
else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) {
float stroke_location[3];
float mouse[2];
mouse[0] = RNA_float_get(op->ptr, "mouse_x");
mouse[1] = RNA_float_get(op->ptr, "mouse_y");
if (SCULPT_stroke_get_location(C, stroke_location, mouse)) {
copy_v3_v3(ss->pivot_pos, stroke_location);
}
}
else {
PBVHNode **nodes;
int totnode;
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
float avg[3];
int total = 0;
zero_v3(avg);
/* Pivot to unmasked. */
if (mode == SCULPT_PIVOT_POSITION_UNMASKED) {
for (int n = 0; n < totnode; n++) {
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
const float mask = (vd.mask) ? *vd.mask : 0.0f;
if (mask < 1.0f) {
if (SCULPT_check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) {
add_v3_v3(avg, vd.co);
total++;
}
}
}
BKE_pbvh_vertex_iter_end;
}
}
/* Pivot to mask border. */
else if (mode == SCULPT_PIVOT_POSITION_MASK_BORDER) {
const float threshold = 0.2f;
for (int n = 0; n < totnode; n++) {
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
const float mask = (vd.mask) ? *vd.mask : 0.0f;
if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) {
if (SCULPT_check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) {
add_v3_v3(avg, vd.co);
total++;
}
}
}
BKE_pbvh_vertex_iter_end;
}
}
if (total > 0) {
mul_v3_fl(avg, 1.0f / total);
copy_v3_v3(ss->pivot_pos, avg);
}
MEM_SAFE_FREE(nodes);
}
/* Update the viewport navigation rotation origin. */
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos);
ups->average_stroke_counter = 1;
ups->last_stroke_valid = true;
ED_region_tag_redraw(region);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
}
static int sculpt_set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
RNA_float_set(op->ptr, "mouse_x", event->mval[0]);
RNA_float_set(op->ptr, "mouse_y", event->mval[1]);
return sculpt_set_pivot_position_exec(C, op);
}
void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Set Pivot Position";
ot->idname = "SCULPT_OT_set_pivot_position";
ot->description = "Sets the sculpt transform pivot position";
/* API callbacks. */
ot->invoke = sculpt_set_pivot_position_invoke;
ot->exec = sculpt_set_pivot_position_exec;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna,
"mode",
prop_sculpt_pivot_position_types,
SCULPT_PIVOT_POSITION_UNMASKED,
"Mode",
"");
RNA_def_float(ot->srna,
"mouse_x",
0.0f,
0.0f,
FLT_MAX,
"Mouse Position X",
"Position of the mouse used for \"Surface\" mode",
0.0f,
10000.0f);
RNA_def_float(ot->srna,
"mouse_y",
0.0f,
0.0f,
FLT_MAX,
"Mouse Position Y",
"Position of the mouse used for \"Surface\" mode",
0.0f,
10000.0f);
}