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
blender-archive/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
Pablo Dobarro 4818ed1c76 Cleanup: Unindent if statements in sculpt tools code
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
2021-02-10 18:06:56 +01:00

475 lines
16 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_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_multires.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
typedef struct MultiplaneScrapeSampleData {
float area_cos[2][3];
float area_nos[2][3];
int area_count[2];
} MultiplaneScrapeSampleData;
static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
MultiplaneScrapeSampleData *mssd = tls->userdata_chunk;
float(*mat)[4] = data->mat;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
/* Apply the brush normal radius to the test before sampling. */
float test_radius = sqrtf(test.radius_squared);
test_radius *= brush->normal_radius_factor;
test.radius_squared = test_radius * test_radius;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
float local_co[3];
float normal[3];
if (vd.no) {
normal_short_to_float_v3(normal, vd.no);
}
else {
copy_v3_v3(normal, vd.fno);
}
mul_v3_m4v3(local_co, mat, vd.co);
/* Use the brush falloff to weight the sampled normals. */
const float fade = SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
/* Sample the normal and area of the +X and -X axis individually. */
if (local_co[0] > 0.0f) {
madd_v3_v3fl(mssd->area_nos[0], normal, fade);
add_v3_v3(mssd->area_cos[0], vd.co);
mssd->area_count[0]++;
}
else {
madd_v3_v3fl(mssd->area_nos[1], normal, fade);
add_v3_v3(mssd->area_cos[1], vd.co);
mssd->area_count[1]++;
}
BKE_pbvh_vertex_iter_end;
}
}
static void calc_multiplane_scrape_surface_reduce(const void *__restrict UNUSED(userdata),
void *__restrict chunk_join,
void *__restrict chunk)
{
MultiplaneScrapeSampleData *join = chunk_join;
MultiplaneScrapeSampleData *mssd = chunk;
add_v3_v3(join->area_cos[0], mssd->area_cos[0]);
add_v3_v3(join->area_cos[1], mssd->area_cos[1]);
add_v3_v3(join->area_nos[0], mssd->area_nos[0]);
add_v3_v3(join->area_nos[1], mssd->area_nos[1]);
join->area_count[0] += mssd->area_count[0];
join->area_count[1] += mssd->area_count[1];
}
static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*mat)[4] = data->mat;
float(*scrape_planes)[4] = data->multiplane_scrape_planes;
float angle = data->multiplane_scrape_angle;
PBVHVertexIter vd;
float(*proxy)[3];
const float bstrength = fabsf(ss->cache->bstrength);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
float local_co[3];
bool deform = false;
mul_v3_m4v3(local_co, mat, vd.co);
if (local_co[0] > 0.0f) {
deform = !SCULPT_plane_point_side(vd.co, scrape_planes[0]);
}
else {
deform = !SCULPT_plane_point_side(vd.co, scrape_planes[1]);
}
if (angle < 0.0f) {
deform = true;
}
if (!deform) {
continue;
}
float intr[3];
float val[3];
if (local_co[0] > 0.0f) {
closest_to_plane_normalized_v3(intr, scrape_planes[0], vd.co);
}
else {
closest_to_plane_normalized_v3(intr, scrape_planes[1], vd.co);
}
sub_v3_v3v3(val, intr, vd.co);
if (!SCULPT_plane_trim(ss->cache, brush, val)) {
continue;
}
/* Deform the local space along the Y axis to avoid artifacts on curved strokes. */
/* This produces a not round brush tip. */
local_co[1] *= 2.0f;
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
len_v3(local_co),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
}
/* Public functions. */
/* Main Brush Function. */
void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
const bool flip = (ss->cache->bstrength < 0.0f);
const float radius = flip ? -ss->cache->radius : ss->cache->radius;
const float offset = SCULPT_brush_plane_offset_get(sd, ss);
const float displace = -radius * offset;
/* The sculpt-plane normal (whatever its set to) */
float area_no_sp[3];
/* Geometry normal. */
float area_no[3];
float area_co[3];
float temp[3];
float mat[4][4];
SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no);
}
else {
copy_v3_v3(area_no, area_no_sp);
}
/* Delay the first daub because grab delta is not setup. */
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
ss->cache->multiplane_scrape_angle = 0.0f;
return;
}
if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
return;
}
mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp);
/* Init brush local space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
cross_v3_v3v3(mat[1], area_no, mat[0]);
mat[1][3] = 0.0f;
copy_v3_v3(mat[2], area_no);
mat[2][3] = 0.0f;
copy_v3_v3(mat[3], ss->cache->location);
mat[3][3] = 1.0f;
normalize_m4(mat);
invert_m4(mat);
/* Update matrix for the cursor preview. */
if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0) {
copy_m4_m4(ss->cache->stroke_local_mat, mat);
}
/* Dynamic mode. */
if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) {
/* Sample the individual normal and area center of the two areas at both sides of the cursor.
*/
SculptThreadedTaskData sample_data = {
.sd = NULL,
.ob = ob,
.brush = brush,
.nodes = nodes,
.totnode = totnode,
.mat = mat,
};
MultiplaneScrapeSampleData mssd = {{{0}}};
TaskParallelSettings sample_settings;
BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce;
sample_settings.userdata_chunk = &mssd;
sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData);
BLI_task_parallel_range(
0, totnode, &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings);
float sampled_plane_normals[2][3];
float sampled_plane_co[2][3];
float sampled_cv[2][3];
float mid_co[3];
/* Use the area center of both planes to detect if we are sculpting along a concave or convex
* edge. */
mul_v3_v3fl(sampled_plane_co[0], mssd.area_cos[0], 1.0f / (float)mssd.area_count[0]);
mul_v3_v3fl(sampled_plane_co[1], mssd.area_cos[1], 1.0f / (float)mssd.area_count[1]);
mid_v3_v3v3(mid_co, sampled_plane_co[0], sampled_plane_co[1]);
/* Calculate the scrape planes angle based on the sampled normals. */
mul_v3_v3fl(sampled_plane_normals[0], mssd.area_nos[0], 1.0f / (float)mssd.area_count[0]);
mul_v3_v3fl(sampled_plane_normals[1], mssd.area_nos[1], 1.0f / (float)mssd.area_count[1]);
normalize_v3(sampled_plane_normals[0]);
normalize_v3(sampled_plane_normals[1]);
float sampled_angle = angle_v3v3(sampled_plane_normals[0], sampled_plane_normals[1]);
copy_v3_v3(sampled_cv[0], area_no);
sub_v3_v3v3(sampled_cv[1], ss->cache->location, mid_co);
sampled_angle += DEG2RADF(brush->multiplane_scrape_angle) * ss->cache->pressure;
/* Invert the angle if we are sculpting along a concave edge. */
if (dot_v3v3(sampled_cv[0], sampled_cv[1]) < 0.0f) {
sampled_angle = -sampled_angle;
}
/* In dynamic mode, set the angle to 0 when inverting the brush, so you can trim plane
* surfaces without changing the brush. */
if (flip) {
sampled_angle = 0.0f;
}
else {
copy_v3_v3(area_co, ss->cache->location);
}
/* Interpolate between the previous and new sampled angles to avoid artifacts when if angle
* difference between two samples is too big. */
ss->cache->multiplane_scrape_angle = interpf(
RAD2DEGF(sampled_angle), ss->cache->multiplane_scrape_angle, 0.2f);
}
else {
/* Standard mode: Scrape with the brush property fixed angle. */
copy_v3_v3(area_co, ss->cache->location);
ss->cache->multiplane_scrape_angle = brush->multiplane_scrape_angle;
if (flip) {
ss->cache->multiplane_scrape_angle *= -1.0f;
}
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.mat = mat,
.multiplane_scrape_angle = ss->cache->multiplane_scrape_angle,
};
/* Calculate the final left and right scrape planes. */
float plane_no[3];
float plane_no_rot[3];
const float y_axis[3] = {0.0f, 1.0f, 0.0f};
float mat_inv[4][4];
invert_m4_m4(mat_inv, mat);
mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[1], area_co, plane_no);
mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings);
}
void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
Brush *brush,
SculptSession *ss,
const float outline_col[3],
const float outline_alpha)
{
if (!(brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW)) {
return;
}
float local_mat_inv[4][4];
invert_m4_m4(local_mat_inv, ss->cache->stroke_local_mat);
GPU_matrix_mul(local_mat_inv);
float angle = ss->cache->multiplane_scrape_angle;
if (ss->cache->pen_flip || ss->cache->invert) {
angle = -angle;
}
float offset = ss->cache->radius * 0.25f;
const float p[3] = {0.0f, 0.0f, ss->cache->radius};
const float y_axis[3] = {0.0f, 1.0f, 0.0f};
float p_l[3];
float p_r[3];
const float area_center[3] = {0.0f, 0.0f, 0.0f};
rotate_v3_v3v3fl(p_r, p, y_axis, DEG2RADF((angle + 180) * 0.5f));
rotate_v3_v3v3fl(p_l, p, y_axis, DEG2RADF(-(angle + 180) * 0.5f));
immBegin(GPU_PRIM_LINES, 14);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]);
immEnd();
immUniformColor3fvAlpha(outline_col, outline_alpha * 0.1f);
immBegin(GPU_PRIM_TRIS, 12);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]);
immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]);
immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]);
immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]);
immEnd();
}