This removes indentations from if statements by converting them to early returns and continue. Most of the code of brushes and tools has loops with a full indented body inside of an if, which was also copied into some of the new tools. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D10333
384 lines
11 KiB
C
384 lines
11 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_hash.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_task.h"
|
|
|
|
#include "DNA_brush_types.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>
|
|
|
|
AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
|
|
{
|
|
if (ss->cache) {
|
|
return ss->cache->automasking;
|
|
}
|
|
if (ss->filter_cache) {
|
|
return ss->filter_cache->automasking;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
|
|
const Brush *br,
|
|
const eAutomasking_flag mode)
|
|
{
|
|
if (br) {
|
|
return br->automasking_flags & mode || sd->automasking_flags & mode;
|
|
}
|
|
return sd->automasking_flags & mode;
|
|
}
|
|
|
|
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
|
|
{
|
|
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
|
|
return false;
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
|
|
return true;
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_FACE_SETS)) {
|
|
return true;
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
|
|
return true;
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
|
|
{
|
|
if (brush) {
|
|
return sculpt->automasking_flags | brush->automasking_flags;
|
|
}
|
|
return sculpt->automasking_flags;
|
|
}
|
|
|
|
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
|
|
{
|
|
|
|
const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
|
|
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
|
|
return true;
|
|
}
|
|
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
|
|
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
|
|
}
|
|
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
|
|
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
|
|
{
|
|
if (!automasking) {
|
|
return 1.0f;
|
|
}
|
|
/* If the cache is initialized with valid info, use the cache. This is used when the
|
|
* automasking information can't be computed in real time per vertex and needs to be
|
|
* initialized for the whole mesh when the stroke starts. */
|
|
if (automasking->factor) {
|
|
return automasking->factor[vert];
|
|
}
|
|
|
|
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
|
|
if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
|
|
if (SCULPT_vertex_is_boundary(ss, vert)) {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
|
|
if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
|
|
{
|
|
if (!automasking) {
|
|
return;
|
|
}
|
|
|
|
MEM_SAFE_FREE(automasking->factor);
|
|
MEM_SAFE_FREE(automasking);
|
|
}
|
|
|
|
static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
|
|
{
|
|
/* 2D falloff is not constrained by radius. */
|
|
if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
|
|
return false;
|
|
}
|
|
|
|
if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
typedef struct AutomaskFloodFillData {
|
|
float *automask_factor;
|
|
float radius;
|
|
bool use_radius;
|
|
float location[3];
|
|
char symm;
|
|
} AutomaskFloodFillData;
|
|
|
|
static bool automask_floodfill_cb(
|
|
SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
|
|
{
|
|
AutomaskFloodFillData *data = userdata;
|
|
|
|
data->automask_factor[to_v] = 1.0f;
|
|
data->automask_factor[from_v] = 1.0f;
|
|
return (!data->use_radius ||
|
|
SCULPT_is_vertex_inside_brush_radius_symm(
|
|
SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
|
|
}
|
|
|
|
static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
|
|
{
|
|
SculptSession *ss = ob->sculpt;
|
|
Brush *brush = BKE_paint_brush(&sd->paint);
|
|
|
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
|
|
BLI_assert(!"Topology masking: pmap missing");
|
|
return NULL;
|
|
}
|
|
|
|
const int totvert = SCULPT_vertex_count_get(ss);
|
|
for (int i = 0; i < totvert; i++) {
|
|
automask_factor[i] = 0.0f;
|
|
}
|
|
|
|
/* Flood fill automask to connected vertices. Limited to vertices inside
|
|
* the brush radius if the tool requires it. */
|
|
SculptFloodFill flood;
|
|
SCULPT_floodfill_init(ss, &flood);
|
|
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
|
|
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
|
|
|
|
AutomaskFloodFillData fdata = {
|
|
.automask_factor = automask_factor,
|
|
.radius = radius,
|
|
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
|
|
.symm = SCULPT_mesh_symmetry_xyz_get(ob),
|
|
};
|
|
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
|
|
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
|
|
SCULPT_floodfill_free(&flood);
|
|
|
|
return automask_factor;
|
|
}
|
|
|
|
static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
|
|
{
|
|
SculptSession *ss = ob->sculpt;
|
|
Brush *brush = BKE_paint_brush(&sd->paint);
|
|
|
|
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
|
|
BLI_assert(!"Face Sets automasking: pmap missing");
|
|
return NULL;
|
|
}
|
|
|
|
int tot_vert = SCULPT_vertex_count_get(ss);
|
|
int active_face_set = SCULPT_active_face_set_get(ss);
|
|
for (int i = 0; i < tot_vert; i++) {
|
|
if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
|
|
automask_factor[i] *= 0.0f;
|
|
}
|
|
}
|
|
|
|
return automask_factor;
|
|
}
|
|
|
|
#define EDGE_DISTANCE_INF -1
|
|
|
|
float *SCULPT_boundary_automasking_init(Object *ob,
|
|
eBoundaryAutomaskMode mode,
|
|
int propagation_steps,
|
|
float *automask_factor)
|
|
{
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
if (!ss->pmap) {
|
|
BLI_assert(!"Boundary Edges masking: pmap missing");
|
|
return NULL;
|
|
}
|
|
|
|
const int totvert = SCULPT_vertex_count_get(ss);
|
|
int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor");
|
|
|
|
for (int i = 0; i < totvert; i++) {
|
|
edge_distance[i] = EDGE_DISTANCE_INF;
|
|
switch (mode) {
|
|
case AUTOMASK_INIT_BOUNDARY_EDGES:
|
|
if (SCULPT_vertex_is_boundary(ss, i)) {
|
|
edge_distance[i] = 0;
|
|
}
|
|
break;
|
|
case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
|
|
if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
|
|
edge_distance[i] = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) {
|
|
for (int i = 0; i < totvert; i++) {
|
|
if (edge_distance[i] != EDGE_DISTANCE_INF) {
|
|
continue;
|
|
}
|
|
SculptVertexNeighborIter ni;
|
|
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
|
|
if (edge_distance[ni.index] == propagation_it) {
|
|
edge_distance[i] = propagation_it + 1;
|
|
}
|
|
}
|
|
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < totvert; i++) {
|
|
if (edge_distance[i] == EDGE_DISTANCE_INF) {
|
|
continue;
|
|
}
|
|
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
|
|
const float edge_boundary_automask = pow2f(p);
|
|
automask_factor[i] *= (1.0f - edge_boundary_automask);
|
|
}
|
|
|
|
MEM_SAFE_FREE(edge_distance);
|
|
return automask_factor;
|
|
}
|
|
|
|
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
|
|
SculptSession *ss,
|
|
Sculpt *sd,
|
|
Brush *brush)
|
|
{
|
|
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
|
|
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
|
|
}
|
|
|
|
AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
|
|
{
|
|
SculptSession *ss = ob->sculpt;
|
|
const int totvert = SCULPT_vertex_count_get(ss);
|
|
|
|
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
|
|
return NULL;
|
|
}
|
|
|
|
AutomaskingCache *automasking = MEM_callocN(sizeof(AutomaskingCache), "automasking cache");
|
|
SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
|
|
SCULPT_boundary_info_ensure(ob);
|
|
|
|
if (!SCULPT_automasking_needs_factors_cache(sd, brush)) {
|
|
return automasking;
|
|
}
|
|
|
|
automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
|
|
for (int i = 0; i < totvert; i++) {
|
|
automasking->factor[i] = 1.0f;
|
|
}
|
|
|
|
const int boundary_propagation_steps = brush ?
|
|
brush->automasking_boundary_edges_propagation_steps :
|
|
1;
|
|
|
|
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
|
|
SCULPT_vertex_random_access_ensure(ss);
|
|
SCULPT_topology_automasking_init(sd, ob, automasking->factor);
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
|
|
SCULPT_vertex_random_access_ensure(ss);
|
|
sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
|
|
}
|
|
|
|
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
|
|
SCULPT_vertex_random_access_ensure(ss);
|
|
SCULPT_boundary_automasking_init(
|
|
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
|
|
}
|
|
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
|
|
SCULPT_vertex_random_access_ensure(ss);
|
|
SCULPT_boundary_automasking_init(
|
|
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
|
|
}
|
|
|
|
return automasking;
|
|
}
|