This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/sculpt_paint/sculpt.c
2012-08-01 17:25:10 +00:00

4248 lines
112 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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) 2006 by Nicholas Bishop
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Jason Wilkins, Tom Musgrove.
*
* ***** END GPL LICENSE BLOCK *****
*
* Implements the Sculpt Mode tools
*
*/
/** \file blender/editors/sculpt_paint/sculpt.c
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_dynstr.h"
#include "BLI_ghash.h"
#include "BLI_pbvh.h"
#include "BLI_threads.h"
#include "BLI_rand.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_brush_types.h"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_lattice.h" /* for armature_deform_verts */
#include "BKE_node.h"
#include "BKE_subsurf.h"
#include "BIF_glutil.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_sculpt.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "ED_util.h" /* for crazyspace correction */
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RE_render_ext.h"
#include "GPU_buffers.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#ifdef _OPENMP
#include <omp.h>
#endif
void ED_sculpt_force_update(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob && (ob->mode & OB_MODE_SCULPT))
multires_force_update(ob);
}
float *ED_sculpt_get_last_stroke(struct Object *ob)
{
return (ob && ob->sculpt && ob->sculpt->last_stroke_valid) ? ob->sculpt->last_stroke : NULL;
}
int ED_sculpt_minmax(bContext *C, float min[3], float max[3])
{
Object *ob = CTX_data_active_object(C);
if (ob && ob->sculpt && ob->sculpt->last_stroke_valid) {
copy_v3_v3(min, ob->sculpt->last_stroke);
copy_v3_v3(max, ob->sculpt->last_stroke);
return 1;
}
else {
return 0;
}
}
/* Sculpt mode handles multires differently from regular meshes, but only if
* it's the last modifier on the stack and it is not on the first level */
MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob)
{
Mesh *me = (Mesh *)ob->data;
ModifierData *md;
if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) {
/* multires can't work without displacement layer */
return NULL;
}
for (md = modifiers_getVirtualModifierList(ob); md; md = md->next) {
if (md->type == eModifierType_Multires) {
MultiresModifierData *mmd = (MultiresModifierData *)md;
if (!modifier_isEnabled(scene, md, eModifierMode_Realtime))
continue;
if (mmd->sculptlvl > 0) return mmd;
else return NULL;
}
}
return NULL;
}
/* Check if there are any active modifiers in stack (used for flushing updates at enter/exit sculpt mode) */
static int sculpt_has_active_modifiers(Scene *scene, Object *ob)
{
ModifierData *md;
md = modifiers_getVirtualModifierList(ob);
/* exception for shape keys because we can edit those */
for (; md; md = md->next) {
if (modifier_isEnabled(scene, md, eModifierMode_Realtime))
return 1;
}
return 0;
}
/* Checks if there are any supported deformation modifiers active */
static int sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
{
ModifierData *md;
Mesh *me = (Mesh *)ob->data;
MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
if (mmd) return 0;
/* non-locked shape keys could be handled in the same way as deformed mesh */
if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr)
return 1;
md = modifiers_getVirtualModifierList(ob);
/* exception for shape keys because we can edit those */
for (; md; md = md->next) {
ModifierTypeInfo *mti = modifierType_getInfo(md->type);
if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue;
if (md->type == eModifierType_ShapeKey) continue;
if (mti->type == eModifierTypeType_OnlyDeform) return 1;
else if ((sd->flags & SCULPT_ONLY_DEFORM) == 0) return 1;
}
return 0;
}
typedef enum StrokeFlags {
CLIP_X = 1,
CLIP_Y = 2,
CLIP_Z = 4
} StrokeFlags;
/* Cache stroke properties. Used because
* RNA property lookup isn't particularly fast.
*
* For descriptions of these settings, check the operator properties.
*/
typedef struct StrokeCache {
/* Invariants */
float initial_radius;
float scale[3];
int flag;
float clip_tolerance[3];
float initial_mouse[2];
/* Pre-allocated temporary storage used during smoothing */
int num_threads;
float (**tmpgrid_co)[3], (**tmprow_co)[3];
float **tmpgrid_mask, **tmprow_mask;
/* Variants */
float radius;
float radius_squared;
float true_location[3];
float location[3];
float pen_flip;
float invert;
float pressure;
float mouse[2];
float bstrength;
float tex_mouse[2];
/* The rest is temporary storage that isn't saved as a property */
int first_time; /* Beginning of stroke may do some things special */
/* from ED_view3d_ob_project_mat_get() */
float projection_mat[4][4];
/* Clean this up! */
ViewContext *vc;
Brush *brush;
float (*face_norms)[3]; /* Copy of the mesh faces' normals */
float special_rotation; /* Texture rotation (radians) for anchored and rake modes */
int pixel_radius, previous_pixel_radius;
float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[3];
int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
* 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
float true_view_normal[3];
float view_normal[3];
/* sculpt_normal gets calculated by calc_sculpt_normal(), then the
* sculpt_normal_symm gets updated quickly with the usual symmetry
* transforms */
float sculpt_normal[3];
float sculpt_normal_symm[3];
/* Used for wrap texture mode, local_mat gets calculated by
* calc_brush_local_mat() and used in tex_strength(). */
float brush_local_mat[4][4];
float last_center[3];
int radial_symmetry_pass;
float symm_rot_mat[4][4];
float symm_rot_mat_inv[4][4];
float last_rake[2]; /* Last location of updating rake rotation */
int original;
float vertex_rotation;
char saved_active_brush_name[MAX_ID_NAME];
char saved_mask_brush_tool;
int alt_smooth;
float plane_trim_squared;
rcti previous_r; /* previous redraw rectangle */
} StrokeCache;
/*** paint mesh ***/
static void paint_mesh_restore_co(Sculpt *sd, SculptSession *ss)
{
StrokeCache *cache = ss->cache;
int i;
PBVHNode **nodes;
int n, totnode;
#ifndef _OPENMP
(void)sd; /* quied unused warning */
#endif
BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
SculptUndoNode *unode;
unode = sculpt_undo_get_node(nodes[n]);
if (unode) {
PBVHVertexIter vd;
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (unode->type == SCULPT_UNDO_COORDS) {
copy_v3_v3(vd.co, unode->co[vd.i]);
if (vd.no) copy_v3_v3_short(vd.no, unode->no[vd.i]);
else normal_short_to_float_v3(vd.fno, unode->no[vd.i]);
}
else if (unode->type == SCULPT_UNDO_MASK) {
*vd.mask = unode->mask[vd.i];
}
if (vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
BLI_pbvh_vertex_iter_end;
BLI_pbvh_node_mark_update(nodes[n]);
}
}
if (ss->face_normals) {
float *fn = ss->face_normals;
for (i = 0; i < ss->totpoly; ++i, fn += 3)
copy_v3_v3(fn, cache->face_norms[i]);
}
if (nodes)
MEM_freeN(nodes);
}
/*** BVH Tree ***/
/* Get a screen-space rectangle of the modified area */
static int sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
Object *ob, rcti *rect)
{
SculptSession *ss;
PBVH *pbvh = ob->sculpt->pbvh;
float bb_min[3], bb_max[3];
if (!pbvh)
return 0;
BLI_pbvh_redraw_BB(pbvh, bb_min, bb_max);
/* convert 3D bounding box to screen space */
if (!paint_convert_bb_to_rect(rect,
bb_min,
bb_max,
ar,
rv3d,
ob))
{
return 0;
}
/* expand redraw rect with redraw rect from previous step to
* prevent partial-redraw issues caused by fast strokes. This is
* needed here (not in sculpt_flush_update) as it was before
* because redraw rectangle should be the same in both of
* optimized PBVH draw function and 3d view redraw (if not -- some
* mesh parts could disappear from screen (sergey) */
ss = ob->sculpt;
if (ss->cache) {
if (!BLI_rcti_is_empty(&ss->cache->previous_r))
BLI_rcti_union(rect, &ss->cache->previous_r);
}
return 1;
}
void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
RegionView3D *rv3d, Object *ob)
{
PBVH *pbvh = ob->sculpt->pbvh;
rcti rect;
sculpt_get_redraw_rect(ar, rv3d, ob, &rect);
paint_calc_redraw_planes(planes, ar, rv3d, ob, &rect);
/* clear redraw flag from nodes */
if (pbvh)
BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL);
}
/************************ Brush Testing *******************/
typedef struct SculptBrushTest {
float radius_squared;
float location[3];
float dist;
} SculptBrushTest;
static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
{
test->radius_squared = ss->cache->radius_squared;
copy_v3_v3(test->location, ss->cache->location);
test->dist = 0.0f; /* just for initialize */
}
static int sculpt_brush_test(SculptBrushTest *test, float co[3])
{
float distsq = len_squared_v3v3(co, test->location);
if (distsq <= test->radius_squared) {
test->dist = sqrt(distsq);
return 1;
}
else {
return 0;
}
}
static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3])
{
float distsq = len_squared_v3v3(co, test->location);
if (distsq <= test->radius_squared) {
test->dist = distsq;
return 1;
}
else {
return 0;
}
}
static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3])
{
return len_squared_v3v3(co, test->location) <= test->radius_squared;
}
static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4])
{
float side = M_SQRT1_2;
float local_co[3];
mul_v3_m4v3(local_co, local, co);
local_co[0] = fabs(local_co[0]);
local_co[1] = fabs(local_co[1]);
local_co[2] = fabs(local_co[2]);
if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
float p = 4.0f;
test->dist = ((powf(local_co[0], p) +
powf(local_co[1], p) +
powf(local_co[2], p)) / powf(side, p));
return 1;
}
else {
return 0;
}
}
static float frontface(Brush *brush, const float sculpt_normal[3],
const short no[3], const float fno[3])
{
if (brush->flag & BRUSH_FRONTFACE) {
float dot;
if (no) {
float tmp[3];
normal_short_to_float_v3(tmp, no);
dot = dot_v3v3(tmp, sculpt_normal);
}
else {
dot = dot_v3v3(fno, sculpt_normal);
}
return dot > 0 ? dot : 0;
}
else {
return 1;
}
}
#if 0
static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3])
{
if (sculpt_brush_test_fast(test, co)) {
float t1[3], t2[3], t3[3], dist;
sub_v3_v3v3(t1, location, co);
sub_v3_v3v3(t2, x2, location);
cross_v3_v3v3(t3, an, t1);
dist = len_v3(t3) / len_v3(t2);
test->dist = dist;
return 1;
}
return 0;
}
#endif
/* ===== Sculpting =====
*
*/
static float overlapped_curve(Brush *br, float x)
{
int i;
const int n = 100 / br->spacing;
const float h = br->spacing / 50.0f;
const float x0 = x - 1;
float sum;
sum = 0;
for (i = 0; i < n; i++) {
float xx;
xx = fabs(x0 + i * h);
if (xx < 1.0f)
sum += BKE_brush_curve_strength(br, xx, 1);
}
return sum;
}
static float integrate_overlap(Brush *br)
{
int i;
int m = 10;
float g = 1.0f / m;
float max;
max = 0;
for (i = 0; i < m; i++) {
float overlap = overlapped_curve(br, i * g);
if (overlap > max)
max = overlap;
}
return max;
}
/* Uses symm to selectively flip any axis of a coordinate. */
static void flip_v3_v3(float out[3], const float in[3], const char symm)
{
if (symm & SCULPT_SYMM_X)
out[0] = -in[0];
else
out[0] = in[0];
if (symm & SCULPT_SYMM_Y)
out[1] = -in[1];
else
out[1] = in[1];
if (symm & SCULPT_SYMM_Z)
out[2] = -in[2];
else
out[2] = in[2];
}
static void flip_v3(float v[3], const char symm)
{
flip_v3_v3(v, v, symm);
}
static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
{
float mirror[3];
float distsq;
/* flip_v3_v3(mirror, cache->traced_location, symm); */
flip_v3_v3(mirror, cache->true_location, symm);
if (axis != 0) {
float mat[4][4] = MAT4_UNITY;
rotate_m4(mat, axis, angle);
mul_m4_v3(mat, mirror);
}
/* distsq = len_squared_v3v3(mirror, cache->traced_location); */
distsq = len_squared_v3v3(mirror, cache->true_location);
if (distsq <= 4.0f * (cache->radius_squared))
return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius));
else
return 0;
}
static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis)
{
int i;
float overlap;
overlap = 0;
for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) {
const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X'];
overlap += calc_overlap(cache, symm, axis, angle);
}
return overlap;
}
static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache)
{
if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
float overlap;
int symm = cache->symmetry;
int i;
overlap = 0;
for (i = 0; i <= symm; i++) {
if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
overlap += calc_overlap(cache, i, 0, 0);
overlap += calc_radial_symmetry_feather(sd, cache, i, 'X');
overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y');
overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z');
}
}
return 1 / overlap;
}
else {
return 1;
}
}
/* Return modified brush strength. Includes the direction of the brush, positive
* values pull vertices, negative values push. Uses tablet pressure and a
* special multiplier found experimentally to scale the strength factor. */
static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather)
{
const Scene *scene = cache->vc->scene;
Brush *brush = paint_brush(&sd->paint);
/* Primary strength input; square it to make lower values more sensitive */
const float root_alpha = BKE_brush_alpha_get(scene, brush);
float alpha = root_alpha * root_alpha;
float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1;
float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1;
float pen_flip = cache->pen_flip ? -1 : 1;
float invert = cache->invert ? -1 : 1;
float accum = integrate_overlap(brush);
/* spacing is integer percentage of radius, divide by 50 to get
* normalized diameter */
float overlap = (brush->flag & BRUSH_SPACE_ATTEN &&
brush->flag & BRUSH_SPACE &&
!(brush->flag & BRUSH_ANCHORED) &&
(brush->spacing < 100)) ? 1.0f / accum : 1;
float flip = dir * invert * pen_flip;
switch (brush->sculpt_tool) {
case SCULPT_TOOL_CLAY:
case SCULPT_TOOL_CLAY_STRIPS:
case SCULPT_TOOL_DRAW:
case SCULPT_TOOL_LAYER:
return alpha * flip * pressure * overlap * feather;
case SCULPT_TOOL_MASK:
overlap = (1 + overlap) / 2;
switch ((BrushMaskTool)brush->mask_tool) {
case BRUSH_MASK_DRAW:
return alpha * flip * pressure * overlap * feather;
case BRUSH_MASK_SMOOTH:
return alpha * pressure * feather;
}
case SCULPT_TOOL_CREASE:
case SCULPT_TOOL_BLOB:
return alpha * flip * pressure * overlap * feather;
case SCULPT_TOOL_INFLATE:
if (flip > 0) {
return 0.250f * alpha * flip * pressure * overlap * feather;
}
else {
return 0.125f * alpha * flip * pressure * overlap * feather;
}
case SCULPT_TOOL_FILL:
case SCULPT_TOOL_SCRAPE:
case SCULPT_TOOL_FLATTEN:
if (flip > 0) {
overlap = (1 + overlap) / 2;
return alpha * flip * pressure * overlap * feather;
}
else {
/* reduce strength for DEEPEN, PEAKS, and CONTRAST */
return 0.5f * alpha * flip * pressure * overlap * feather;
}
case SCULPT_TOOL_SMOOTH:
return alpha * pressure * feather;
case SCULPT_TOOL_PINCH:
if (flip > 0) {
return alpha * flip * pressure * overlap * feather;
}
else {
return 0.25f * alpha * flip * pressure * overlap * feather;
}
case SCULPT_TOOL_NUDGE:
overlap = (1 + overlap) / 2;
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_THUMB:
return alpha * pressure * feather;
case SCULPT_TOOL_SNAKE_HOOK:
return feather;
case SCULPT_TOOL_GRAB:
return feather;
case SCULPT_TOOL_ROTATE:
return alpha * pressure * feather;
default:
return 0;
}
}
/* Return a multiplier for brush strength on a particular vertex. */
static float tex_strength(SculptSession *ss, Brush *br, float point[3],
const float len,
const float sculpt_normal[3],
const short vno[3],
const float fno[3],
const float mask)
{
MTex *mtex = &br->mtex;
float avg = 1;
if (!mtex->tex) {
avg = 1;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
float jnk;
/* Get strength by feeding the vertex
* location directly into a texture */
externtex(mtex, point, &avg,
&jnk, &jnk, &jnk, &jnk, 0);
}
else if (ss->texcache) {
float rotation = -mtex->rot;
float symm_point[3], point_2d[2];
float x = 0.0f, y = 0.0f; /* Quite warnings */
float radius = 1.0f; /* Quite warnings */
/* if the active area is being applied for symmetry, flip it
* across the symmetry axis and rotate it back to the original
* position in order to project it. This insures that the
* brush texture will be oriented correctly. */
flip_v3_v3(symm_point, point, ss->cache->mirror_symmetry_pass);
if (ss->cache->radial_symmetry_pass)
mul_m4_v3(ss->cache->symm_rot_mat_inv, symm_point);
ED_view3d_project_float_v2(ss->cache->vc->ar, symm_point, point_2d, ss->cache->projection_mat);
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* keep coordinates relative to mouse */
rotation += ss->cache->special_rotation;
point_2d[0] -= ss->cache->tex_mouse[0];
point_2d[1] -= ss->cache->tex_mouse[1];
/* use pressure adjusted size for fixed mode */
radius = ss->cache->pixel_radius;
x = point_2d[0] + ss->cache->vc->ar->winrct.xmin;
y = point_2d[1] + ss->cache->vc->ar->winrct.ymin;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
/* leave the coordinates relative to the screen */
/* use unadjusted size for tiled mode */
radius = BKE_brush_size_get(ss->cache->vc->scene, br);
x = point_2d[0];
y = point_2d[1];
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) {
/* Similar to fixed mode, but projects from brush angle
* rather than view direction */
/* Rotation is handled by the brush_local_mat */
rotation = 0;
mul_m4_v3(ss->cache->brush_local_mat, symm_point);
x = symm_point[0];
y = symm_point[1];
}
if (mtex->brush_map_mode != MTEX_MAP_MODE_AREA) {
x /= ss->cache->vc->ar->winx;
y /= ss->cache->vc->ar->winy;
if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
x -= 0.5f;
y -= 0.5f;
}
x *= ss->cache->vc->ar->winx / radius;
y *= ss->cache->vc->ar->winy / radius;
}
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
x *= br->mtex.size[0];
y *= br->mtex.size[1];
x += br->mtex.ofs[0];
y += br->mtex.ofs[1];
avg = paint_get_tex_pixel(br, x, y);
}
avg += br->texture_sample_bias;
/* Falloff curve */
avg *= BKE_brush_curve_strength(br, len, ss->cache->radius);
avg *= frontface(br, sculpt_normal, vno, fno);
/* Paint mask */
avg *= 1.0f - mask;
return avg;
}
typedef struct {
Sculpt *sd;
SculptSession *ss;
float radius_squared;
int original;
} SculptSearchSphereData;
/* Test AABB against sphere */
static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
{
SculptSearchSphereData *data = data_v;
float *center = data->ss->cache->location, nearest[3];
float t[3], bb_min[3], bb_max[3];
int i;
if (data->original)
BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
else
BLI_pbvh_node_get_BB(node, bb_min, bb_max);
for (i = 0; i < 3; ++i) {
if (bb_min[i] > center[i])
nearest[i] = bb_min[i];
else if (bb_max[i] < center[i])
nearest[i] = bb_max[i];
else
nearest[i] = center[i];
}
sub_v3_v3v3(t, center, nearest);
return dot_v3v3(t, t) < data->radius_squared;
}
/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3])
{
int i;
for (i = 0; i < 3; ++i) {
if (sd->flags & (SCULPT_LOCK_X << i))
continue;
if ((ss->cache->flag & (CLIP_X << i)) && (fabsf(co[i]) <= ss->cache->clip_tolerance[i]))
co[i] = 0.0f;
else
co[i] = val[i];
}
}
static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3])
{
if ((dot_v3v3(view_vec, fno)) > 0) {
add_v3_v3(out, fno);
}
else {
add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */
}
}
static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
float out_flip[3] = {0.0f, 0.0f, 0.0f};
int n, original;
/* Grab brush requires to test on original data (see r33888 and
* bug #25371) */
original = (paint_brush(&sd->paint)->sculpt_tool == SCULPT_TOOL_GRAB ?
TRUE : ss->cache->original);
(void)sd; /* unused w/o openmp */
zero_v3(an);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
float private_an[3] = {0.0f, 0.0f, 0.0f};
float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
sculpt_brush_test_init(ss, &test);
if (original) {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, unode->co[vd.i])) {
float fno[3];
normal_short_to_float_v3(fno, unode->no[vd.i]);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
}
BLI_pbvh_vertex_iter_end;
}
else {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, vd.co)) {
if (vd.no) {
float fno[3];
normal_short_to_float_v3(fno, vd.no);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
else {
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
}
}
}
BLI_pbvh_vertex_iter_end;
}
#pragma omp critical
{
add_v3_v3(an, private_an);
add_v3_v3(out_flip, private_out_flip);
}
}
if (is_zero_v3(an))
copy_v3_v3(an, out_flip);
normalize_v3(an);
}
/* Calculate primary direction of movement for many brushes */
static void calc_sculpt_normal(Sculpt *sd, Object *ob,
PBVHNode **nodes, int totnode,
float an[3])
{
const Brush *brush = paint_brush(&sd->paint);
const SculptSession *ss = ob->sculpt;
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
ED_view3d_global_to_vector(ss->cache->vc->rv3d,
ss->cache->vc->rv3d->twmat[3],
an);
break;
case SCULPT_DISP_DIR_X:
an[1] = 0.0;
an[2] = 0.0;
an[0] = 1.0;
break;
case SCULPT_DISP_DIR_Y:
an[0] = 0.0;
an[2] = 0.0;
an[1] = 1.0;
break;
case SCULPT_DISP_DIR_Z:
an[0] = 0.0;
an[1] = 0.0;
an[2] = 1.0;
break;
case SCULPT_DISP_DIR_AREA:
calc_area_normal(sd, ob, an, nodes, totnode);
default:
break;
}
}
static void update_sculpt_normal(Sculpt *sd, Object *ob,
PBVHNode **nodes, int totnode)
{
const Brush *brush = paint_brush(&sd->paint);
StrokeCache *cache = ob->sculpt->cache;
if (cache->mirror_symmetry_pass == 0 &&
cache->radial_symmetry_pass == 0 &&
(cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
{
calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal);
copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal);
}
else {
copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal);
flip_v3(cache->sculpt_normal_symm, cache->mirror_symmetry_pass);
mul_m4_v3(cache->symm_rot_mat, cache->sculpt_normal_symm);
}
}
static void calc_local_y(ViewContext *vc, const float center[3], float y[3])
{
Object *ob = vc->obact;
float loc[3], mval_f[2] = {0.0f, 1.0f};
mul_v3_m4v3(loc, ob->imat, center);
initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
ED_view3d_win_to_delta(vc->ar, mval_f, y);
normalize_v3(y);
add_v3_v3(y, ob->loc);
mul_m4_v3(ob->imat, y);
}
static void calc_brush_local_mat(const Brush *brush, Object *ob,
float local_mat[4][4])
{
const StrokeCache *cache = ob->sculpt->cache;
float tmat[4][4];
float mat[4][4];
float scale[4][4];
float angle, v[3];
float up[3];
/* Ensure ob->imat is up to date */
invert_m4_m4(ob->imat, ob->obmat);
/* Initialize last column of matrix */
mat[0][3] = 0;
mat[1][3] = 0;
mat[2][3] = 0;
mat[3][3] = 1;
/* Get view's up vector in object-space */
calc_local_y(cache->vc, cache->location, up);
/* Calculate the X axis of the local matrix */
cross_v3_v3v3(v, up, cache->sculpt_normal);
/* Apply rotation (user angle, rake, etc.) to X axis */
angle = brush->mtex.rot - cache->special_rotation;
rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle);
/* Get other axes */
cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]);
copy_v3_v3(mat[2], cache->sculpt_normal);
/* Set location */
copy_v3_v3(mat[3], cache->location);
/* Scale by brush radius */
normalize_m4(mat);
scale_m4_fl(scale, cache->radius);
mult_m4_m4m4(tmat, mat, scale);
/* Return inverse (for converting from modelspace coords to local
* area coords) */
invert_m4_m4(local_mat, tmat);
}
static void update_brush_local_mat(Sculpt *sd, Object *ob)
{
StrokeCache *cache = ob->sculpt->cache;
if (cache->mirror_symmetry_pass == 0 &&
cache->radial_symmetry_pass == 0)
{
calc_brush_local_mat(paint_brush(&sd->paint), ob,
cache->brush_local_mat);
}
}
/* Test whether the StrokeCache.sculpt_normal needs update in
* do_brush_action() */
static int brush_needs_sculpt_normal(const Brush *brush)
{
return ((ELEM(brush->sculpt_tool,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_SNAKE_HOOK) &&
((brush->normal_weight > 0) ||
(brush->flag & BRUSH_FRONTFACE))) ||
ELEM7(brush->sculpt_tool,
SCULPT_TOOL_BLOB,
SCULPT_TOOL_CREASE,
SCULPT_TOOL_DRAW,
SCULPT_TOOL_LAYER,
SCULPT_TOOL_NUDGE,
SCULPT_TOOL_ROTATE,
SCULPT_TOOL_THUMB) ||
(brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA));
}
/* For the smooth brush, uses the neighboring vertices around vert to calculate
* a smoothed location for vert. Skips corner vertices (used by only one
* polygon.) */
static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert)
{
const MeshElemMap *vert_map = &ss->pmap[vert];
const MVert *mvert = ss->mvert;
float (*deform_co)[3] = ss->deform_cos;
zero_v3(avg);
/* Don't modify corner vertices */
if (vert_map->count > 1) {
int i, total = 0;
for (i = 0; i < vert_map->count; i++) {
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
unsigned f_adj_v[3];
if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) {
int j;
for (j = 0; j < 3; j++) {
if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) {
add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] :
mvert[f_adj_v[j]].co);
total++;
}
}
}
}
if (total > 0) {
mul_v3_fl(avg, 1.0f / total);
return;
}
}
copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co);
}
/* Similar to neighbor_average(), but returns an averaged mask value
* instead of coordinate. Also does not restrict based on border or
* corner vertices. */
static float neighbor_average_mask(SculptSession *ss, unsigned vert)
{
const float *vmask = ss->vmask;
float avg = 0;
int i, total = 0;
for (i = 0; i < ss->pmap[vert].count; i++) {
const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]];
unsigned f_adj_v[3];
if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) {
int j;
for (j = 0; j < 3; j++) {
avg += vmask[f_adj_v[j]];
total++;
}
}
}
if (total > 0)
return avg / (float)total;
else
return vmask[vert];
}
static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength, int smooth_mask)
{
Brush *brush = paint_brush(&sd->paint);
PBVHVertexIter vd;
SculptBrushTest test;
CLAMP(bstrength, 0.0f, 1.0f);
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->view_normal, vd.no, vd.fno,
smooth_mask ? 0 : *vd.mask);
if (smooth_mask) {
float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask;
val *= fade * bstrength;
*vd.mask += val;
CLAMP(*vd.mask, 0, 1);
}
else {
float avg[3], val[3];
neighbor_average(ss, avg, vd.vert_indices[vd.i]);
sub_v3_v3v3(val, avg, vd.co);
mul_v3_fl(val, fade);
add_v3_v3(val, vd.co);
sculpt_clip(sd, ss, vd.co, val);
}
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node,
float bstrength, int smooth_mask)
{
Brush *brush = paint_brush(&sd->paint);
SculptBrushTest test;
CCGElem **griddata, *data;
CCGKey key;
DMGridAdjacency *gridadj, *adj;
float (*tmpgrid_co)[3], (*tmprow_co)[3];
float *tmpgrid_mask, *tmprow_mask;
int v1, v2, v3, v4;
int thread_num;
int *grid_indices, totgrid, gridsize, i, x, y;
sculpt_brush_test_init(ss, &test);
CLAMP(bstrength, 0.0f, 1.0f);
BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid,
NULL, &gridsize, &griddata, &gridadj);
BLI_pbvh_get_grid_key(ss->pbvh, &key);
thread_num = 0;
#ifdef _OPENMP
if (sd->flags & SCULPT_USE_OPENMP)
thread_num = omp_get_thread_num();
#endif
tmpgrid_co = ss->cache->tmpgrid_co[thread_num];
tmprow_co = ss->cache->tmprow_co[thread_num];
tmpgrid_mask = ss->cache->tmpgrid_mask[thread_num];
tmprow_mask = ss->cache->tmprow_mask[thread_num];
for (i = 0; i < totgrid; ++i) {
data = griddata[grid_indices[i]];
adj = &gridadj[grid_indices[i]];
if (smooth_mask)
memset(tmpgrid_mask, 0, sizeof(float) * gridsize * gridsize);
else
memset(tmpgrid_co, 0, sizeof(float) * 3 * gridsize * gridsize);
for (y = 0; y < gridsize - 1; y++) {
v1 = y * gridsize;
if (smooth_mask) {
tmprow_mask[0] = (*CCG_elem_offset_mask(&key, data, v1) +
*CCG_elem_offset_mask(&key, data, v1 + gridsize));
}
else {
add_v3_v3v3(tmprow_co[0],
CCG_elem_offset_co(&key, data, v1),
CCG_elem_offset_co(&key, data, v1 + gridsize));
}
for (x = 0; x < gridsize - 1; x++) {
v1 = x + y * gridsize;
v2 = v1 + 1;
v3 = v1 + gridsize;
v4 = v3 + 1;
if (smooth_mask) {
float tmp;
tmprow_mask[x + 1] = (*CCG_elem_offset_mask(&key, data, v2) +
*CCG_elem_offset_mask(&key, data, v4));
tmp = tmprow_mask[x + 1] + tmprow_mask[x];
tmpgrid_mask[v1] += tmp;
tmpgrid_mask[v2] += tmp;
tmpgrid_mask[v3] += tmp;
tmpgrid_mask[v4] += tmp;
}
else {
float tmp[3];
add_v3_v3v3(tmprow_co[x + 1],
CCG_elem_offset_co(&key, data, v2),
CCG_elem_offset_co(&key, data, v4));
add_v3_v3v3(tmp, tmprow_co[x + 1], tmprow_co[x]);
add_v3_v3(tmpgrid_co[v1], tmp);
add_v3_v3(tmpgrid_co[v2], tmp);
add_v3_v3(tmpgrid_co[v3], tmp);
add_v3_v3(tmpgrid_co[v4], tmp);
}
}
}
/* blend with existing coordinates */
for (y = 0; y < gridsize; ++y) {
for (x = 0; x < gridsize; ++x) {
float *co;
float *fno;
float *mask;
int index;
if (x == 0 && adj->index[0] == -1)
continue;
if (x == gridsize - 1 && adj->index[2] == -1)
continue;
if (y == 0 && adj->index[3] == -1)
continue;
if (y == gridsize - 1 && adj->index[1] == -1)
continue;
index = x + y * gridsize;
co = CCG_elem_offset_co(&key, data, index);
fno = CCG_elem_offset_no(&key, data, index);
mask = CCG_elem_offset_mask(&key, data, index);
if (sculpt_brush_test(&test, co)) {
const float strength_mask = (smooth_mask ? 0 : *mask);
const float fade = bstrength * tex_strength(ss, brush, co, test.dist,
ss->cache->view_normal,
NULL, fno, strength_mask);
float n = 1.0f / 16.0f;
if (x == 0 || x == gridsize - 1)
n *= 2;
if (y == 0 || y == gridsize - 1)
n *= 2;
if (smooth_mask) {
*mask += ((tmpgrid_mask[x + y * gridsize] * n) - *mask) * fade;
}
else {
float *avg, val[3];
avg = tmpgrid_co[x + y * gridsize];
mul_v3_fl(avg, n);
sub_v3_v3v3(val, avg, co);
mul_v3_fl(val, fade);
add_v3_v3(val, co);
sculpt_clip(sd, ss, co, val);
}
}
}
}
}
}
static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode,
float bstrength, int smooth_mask)
{
SculptSession *ss = ob->sculpt;
const int max_iterations = 4;
const float fract = 1.0f / max_iterations;
PBVHType type = BLI_pbvh_type(ss->pbvh);
int iteration, n, count;
float last;
CLAMP(bstrength, 0, 1);
count = (int)(bstrength * max_iterations);
last = max_iterations * (bstrength - count * fract);
if (type == PBVH_FACES && !ss->pmap) {
BLI_assert(!"sculpt smooth: pmap missing");
return;
}
for (iteration = 0; iteration <= count; ++iteration) {
float strength = (iteration != count) ? 1.0f : last;
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
switch (type) {
case PBVH_GRIDS:
do_multires_smooth_brush(sd, ss, nodes[n], strength,
smooth_mask);
break;
case PBVH_FACES:
do_mesh_smooth_brush(sd, ss, nodes[n], strength,
smooth_mask);
break;
}
}
if (ss->multires)
multires_stitch_grids(ob);
}
}
static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
smooth(sd, ob, nodes, totnode, ss->cache->bstrength, FALSE);
}
static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
int n;
/* threaded loop over nodes */
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
float fade = tex_strength(ss, brush, vd.co, test.dist,
ss->cache->view_normal, vd.no, vd.fno, 0);
(*vd.mask) += fade * bstrength;
CLAMP(*vd.mask, 0, 1);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
BLI_pbvh_vertex_iter_end;
}
}
}
static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
switch ((BrushMaskTool)brush->mask_tool) {
case BRUSH_MASK_DRAW:
do_mask_brush_draw(sd, ob, nodes, totnode);
break;
case BRUSH_MASK_SMOOTH:
smooth(sd, ob, nodes, totnode, ss->cache->bstrength, TRUE);
break;
}
}
static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float offset[3];
float bstrength = ss->cache->bstrength;
int n;
/* offset with as much as possible factored in already */
mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius);
mul_v3_v3(offset, ss->cache->scale);
mul_v3_fl(offset, bstrength);
/* threaded loop over nodes */
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
/* offset vertex */
float fade = tex_strength(ss, brush, vd.co, test.dist,
ss->cache->sculpt_normal_symm, vd.no,
vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], offset, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
const Scene *scene = ss->cache->vc->scene;
Brush *brush = paint_brush(&sd->paint);
float offset[3];
float bstrength = ss->cache->bstrength;
float flippedbstrength, crease_correction;
float brush_alpha;
int n;
/* offset with as much as possible factored in already */
mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius);
mul_v3_v3(offset, ss->cache->scale);
mul_v3_fl(offset, bstrength);
/* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */
crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor;
brush_alpha = BKE_brush_alpha_get(scene, brush);
if (brush_alpha > 0.0f)
crease_correction /= brush_alpha * brush_alpha;
/* we always want crease to pinch or blob to relax even when draw is negative */
flippedbstrength = (bstrength < 0) ? -crease_correction * bstrength : crease_correction * bstrength;
if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f;
/* threaded loop over nodes */
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
/* offset vertex */
const float fade = tex_strength(ss, brush, vd.co, test.dist,
ss->cache->sculpt_normal_symm,
vd.no, vd.fno, *vd.mask);
float val1[3];
float val2[3];
/* first we pinch */
sub_v3_v3v3(val1, test.location, vd.co);
mul_v3_fl(val1, fade * flippedbstrength);
/* then we draw */
mul_v3_v3fl(val2, offset, fade);
add_v3_v3v3(proxy[vd.i], val1, val2);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
int n;
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->view_normal, vd.no,
vd.fno, *vd.mask);
float val[3];
sub_v3_v3v3(val, test.location, vd.co);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
int n;
float len;
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
len = len_v3(grab_delta);
if (brush->normal_weight > 0) {
mul_v3_fl(ss->cache->sculpt_normal_symm, len * brush->normal_weight);
mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
add_v3_v3(grab_delta, ss->cache->sculpt_normal_symm);
}
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptUndoNode *unode;
SculptBrushTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
origco = unode->co;
origno = unode->no;
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, origco[vd.i])) {
const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist,
ss->cache->sculpt_normal_symm, origno[vd.i], NULL, *vd.mask);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
float tmp[3], cono[3];
int n;
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta);
cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->sculpt_normal_symm,
vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], cono, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
int n;
float len;
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
len = len_v3(grab_delta);
if (bstrength < 0)
negate_v3(grab_delta);
if (brush->normal_weight > 0) {
mul_v3_fl(ss->cache->sculpt_normal_symm, len * brush->normal_weight);
mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
add_v3_v3(grab_delta, ss->cache->sculpt_normal_symm);
}
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->sculpt_normal_symm,
vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
float tmp[3], cono[3];
int n;
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta);
cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptUndoNode *unode;
SculptBrushTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
origco = unode->co;
origno = unode->no;
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, origco[vd.i])) {
const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist,
ss->cache->sculpt_normal_symm,
origno[vd.i], NULL, *vd.mask);
mul_v3_v3fl(proxy[vd.i], cono, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
int n;
float m[4][4], rot[4][4], lmat[4][4], ilmat[4][4];
static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 };
float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass];
unit_m4(m);
unit_m4(lmat);
copy_v3_v3(lmat[3], ss->cache->location);
invert_m4_m4(ilmat, lmat);
axis_angle_to_mat4(rot, ss->cache->sculpt_normal_symm, angle);
mul_serie_m4(m, lmat, rot, ilmat, NULL, NULL, NULL, NULL, NULL);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptUndoNode *unode;
SculptBrushTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
origco = unode->co;
origno = unode->no;
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, origco[vd.i])) {
const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist,
ss->cache->sculpt_normal_symm,
origno[vd.i], NULL, *vd.mask);
mul_v3_m4v3(proxy[vd.i], m, origco[vd.i]);
sub_v3_v3(proxy[vd.i], origco[vd.i]);
mul_v3_fl(proxy[vd.i], fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float offset[3];
float lim = brush->height;
int n;
if (bstrength < 0)
lim = -lim;
mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
float (*origco)[3], *layer_disp;
/* XXX: layer brush needs conversion to proxy but its more complicated */
/* proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
origco = unode->co;
if (!unode->layer_disp) {
#pragma omp critical
unode->layer_disp = MEM_callocN(sizeof(float) * unode->totvert, "layer disp");
}
layer_disp = unode->layer_disp;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, origco[vd.i])) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->sculpt_normal_symm,
vd.no, vd.fno, *vd.mask);
float *disp = &layer_disp[vd.i];
float val[3];
*disp += fade;
/* Don't let the displacement go past the limit */
if ((lim < 0 && *disp < lim) || (lim >= 0 && *disp > lim))
*disp = lim;
mul_v3_v3fl(val, offset, *disp);
if (ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) {
int index = vd.vert_indices[vd.i];
/* persistent base */
add_v3_v3(val, ss->layer_co[index]);
}
else {
add_v3_v3(val, origco[vd.i]);
}
sculpt_clip(sd, ss, vd.co, val);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
int n;
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
ss->cache->view_normal, vd.no, vd.fno, *vd.mask);
float val[3];
if (vd.fno) copy_v3_v3(val, vd.fno);
else normal_short_to_float_v3(val, vd.no);
mul_v3_fl(val, fade * ss->cache->radius);
mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float fc[3])
{
SculptSession *ss = ob->sculpt;
int n;
float count = 0;
(void)sd; /* unused w/o openmp */
zero_v3(fc);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
float private_fc[3] = {0.0f, 0.0f, 0.0f};
int private_count = 0;
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
sculpt_brush_test_init(ss, &test);
if (ss->cache->original) {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, unode->co[vd.i])) {
add_v3_v3(private_fc, unode->co[vd.i]);
private_count++;
}
}
BLI_pbvh_vertex_iter_end;
}
else {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, vd.co)) {
add_v3_v3(private_fc, vd.co);
private_count++;
}
}
BLI_pbvh_vertex_iter_end;
}
#pragma omp critical
{
add_v3_v3(fc, private_fc);
count += private_count;
}
}
mul_v3_fl(fc, 1.0f / count);
}
/* this calculates flatten center and area normal together,
* amortizing the memory bandwidth and loop overhead to calculate both at the same time */
static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob,
PBVHNode **nodes, int totnode,
float an[3], float fc[3])
{
SculptSession *ss = ob->sculpt;
int n;
/* for area normal */
float out_flip[3] = {0.0f, 0.0f, 0.0f};
/* for flatten center */
float count = 0;
(void)sd; /* unused w/o openmp */
/* for area normal */
zero_v3(an);
/* for flatten center */
zero_v3(fc);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
float private_an[3] = {0.0f, 0.0f, 0.0f};
float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
float private_fc[3] = {0.0f, 0.0f, 0.0f};
int private_count = 0;
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
sculpt_brush_test_init(ss, &test);
if (ss->cache->original) {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, unode->co[vd.i])) {
/* for area normal */
float fno[3];
normal_short_to_float_v3(fno, unode->no[vd.i]);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
/* for flatten center */
add_v3_v3(private_fc, unode->co[vd.i]);
private_count++;
}
}
BLI_pbvh_vertex_iter_end;
}
else {
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, vd.co)) {
/* for area normal */
if (vd.no) {
float fno[3];
normal_short_to_float_v3(fno, vd.no);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
else {
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
}
/* for flatten center */
add_v3_v3(private_fc, vd.co);
private_count++;
}
}
BLI_pbvh_vertex_iter_end;
}
#pragma omp critical
{
/* for area normal */
add_v3_v3(an, private_an);
add_v3_v3(out_flip, private_out_flip);
/* for flatten center */
add_v3_v3(fc, private_fc);
count += private_count;
}
}
/* for area normal */
if (is_zero_v3(an))
copy_v3_v3(an, out_flip);
normalize_v3(an);
/* for flatten center */
if (count != 0) {
mul_v3_fl(fc, 1.0f / count);
}
else {
zero_v3(fc);
}
}
static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3])
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
if (ss->cache->mirror_symmetry_pass == 0 &&
ss->cache->radial_symmetry_pass == 0 &&
(ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
{
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
ED_view3d_global_to_vector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an);
break;
case SCULPT_DISP_DIR_X:
an[1] = 0.0;
an[2] = 0.0;
an[0] = 1.0;
break;
case SCULPT_DISP_DIR_Y:
an[0] = 0.0;
an[2] = 0.0;
an[1] = 1.0;
break;
case SCULPT_DISP_DIR_Z:
an[0] = 0.0;
an[1] = 0.0;
an[2] = 1.0;
break;
case SCULPT_DISP_DIR_AREA:
calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc);
default:
break;
}
/* for flatten center */
/* flatten center has not been calculated yet if we are not using the area normal */
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA)
calc_flatten_center(sd, ob, nodes, totnode, fc);
/* for area normal */
copy_v3_v3(ss->cache->sculpt_normal, an);
/* for flatten center */
copy_v3_v3(ss->cache->last_center, fc);
}
else {
/* for area normal */
copy_v3_v3(an, ss->cache->sculpt_normal);
/* for flatten center */
copy_v3_v3(fc, ss->cache->last_center);
/* for area normal */
flip_v3(an, ss->cache->mirror_symmetry_pass);
/* for flatten center */
flip_v3(fc, ss->cache->mirror_symmetry_pass);
/* for area normal */
mul_m4_v3(ss->cache->symm_rot_mat, an);
/* for flatten center */
mul_m4_v3(ss->cache->symm_rot_mat, fc);
}
}
/* Projects a point onto a plane along the plane's normal */
static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3])
{
sub_v3_v3v3(intr, co, plane_center);
mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr));
sub_v3_v3v3(intr, co, intr);
}
static int plane_trim(StrokeCache *cache, Brush *brush, float val[3])
{
return (!(brush->flag & BRUSH_PLANE_TRIM) ||
((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared)));
}
static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip)
{
float delta[3];
float d;
sub_v3_v3v3(delta, co, plane_center);
d = dot_v3v3(plane_normal, delta);
if (flip) d = -d;
return d <= 0.0f;
}
static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3])
{
float delta[3];
sub_v3_v3v3(delta, co, plane_center);
return dot_v3v3(plane_normal, delta) <= 0.0f;
}
static float get_offset(Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
float rv = brush->plane_offset;
if (brush->flag & BRUSH_OFFSET_PRESSURE) {
rv *= ss->cache->pressure;
}
return rv;
}
static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
const float radius = ss->cache->radius;
float an[3];
float fc[3];
float offset = get_offset(sd, ss);
float displace;
int n;
float temp[3];
calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = radius * offset;
mul_v3_v3v3(temp, an, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq(&test, vd.co)) {
float intr[3];
float val[3];
point_plane_project(intr, vd.co, an, fc);
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrt(test.dist),
an, vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float radius = ss->cache->radius;
float offset = get_offset(sd, ss);
float displace;
float an[3];
float fc[3];
int n;
float temp[3];
int flip;
calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
flip = bstrength < 0;
if (flip) {
bstrength = -bstrength;
radius = -radius;
}
displace = radius * (0.25f + offset);
mul_v3_v3v3(temp, an, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
/* add_v3_v3v3(p, ss->cache->location, an); */
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq(&test, vd.co)) {
if (plane_point_side_flip(vd.co, an, fc, flip)) {
float intr[3];
float val[3];
point_plane_project(intr, vd.co, an, fc);
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
sqrt(test.dist),
an, vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float radius = ss->cache->radius;
float offset = get_offset(sd, ss);
float displace;
float sn[3];
float an[3];
float fc[3];
int n;
float temp[3];
float mat[4][4];
float scale[4][4];
float tmat[4][4];
int flip;
calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc);
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL))
calc_area_normal(sd, ob, an, nodes, totnode);
else
copy_v3_v3(an, sn);
/* delay the first daub because grab delta is not setup */
if (ss->cache->first_time)
return;
flip = bstrength < 0;
if (flip) {
bstrength = -bstrength;
radius = -radius;
}
displace = radius * (0.25f + offset);
mul_v3_v3v3(temp, sn, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
/* init mat */
cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry);
mat[0][3] = 0;
cross_v3_v3v3(mat[1], an, mat[0]);
mat[1][3] = 0;
copy_v3_v3(mat[2], an);
mat[2][3] = 0;
copy_v3_v3(mat[3], ss->cache->location);
mat[3][3] = 1;
normalize_m4(mat);
/* scale mat */
scale_m4_fl(scale, ss->cache->radius);
mult_m4_m4m4(tmat, mat, scale);
invert_m4_m4(mat, tmat);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_cube(&test, vd.co, mat)) {
if (plane_point_side_flip(vd.co, sn, fc, flip)) {
float intr[3];
float val[3];
point_plane_project(intr, vd.co, sn, fc);
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
ss->cache->radius * test.dist,
an, vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
const float radius = ss->cache->radius;
float an[3];
float fc[3];
float offset = get_offset(sd, ss);
float displace;
int n;
float temp[3];
calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = radius * offset;
mul_v3_v3v3(temp, an, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq(&test, vd.co)) {
if (plane_point_side(vd.co, an, fc)) {
float intr[3];
float val[3];
point_plane_project(intr, vd.co, an, fc);
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
sqrt(test.dist),
an, vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
}
BLI_pbvh_vertex_iter_end;
}
}
static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
const float radius = ss->cache->radius;
float an[3];
float fc[3];
float offset = get_offset(sd, ss);
float displace;
int n;
float temp[3];
calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = -radius * offset;
mul_v3_v3v3(temp, an, ss->cache->scale);
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
float (*proxy)[3];
proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq(&test, vd.co)) {
if (!plane_point_side(vd.co, an, fc)) {
float intr[3];
float val[3];
point_plane_project(intr, vd.co, an, fc);
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
sqrt(test.dist),
an, vd.no, vd.fno, *vd.mask);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
}
BLI_pbvh_vertex_iter_end;
}
}
void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
{
Mesh *me = (Mesh *)ob->data;
float (*ofs)[3] = NULL;
int a, is_basis = 0;
KeyBlock *currkey;
/* for relative keys editing of base should update other keys */
if (me->key->type == KEY_RELATIVE)
for (currkey = me->key->block.first; currkey; currkey = currkey->next)
if (ob->shapenr - 1 == currkey->relative) {
is_basis = 1;
break;
}
if (is_basis) {
ofs = key_to_vertcos(ob, kb);
/* calculate key coord offsets (from previous location) */
for (a = 0; a < me->totvert; a++) {
sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]);
}
/* apply offsets on other keys */
currkey = me->key->block.first;
while (currkey) {
int apply_offset = ((currkey != kb) && (ob->shapenr - 1 == currkey->relative));
if (apply_offset)
offset_to_key(ob, currkey, ofs);
currkey = currkey->next;
}
MEM_freeN(ofs);
}
/* modifying of basis key should update mesh */
if (kb == me->key->refkey) {
MVert *mvert = me->mvert;
for (a = 0; a < me->totvert; a++, mvert++)
copy_v3_v3(mvert->co, vertCos[a]);
BKE_mesh_calc_normals_mapping(me->mvert, me->totvert, me->mloop,
me->mpoly, me->totloop, me->totpoly,
NULL, NULL, 0, NULL, NULL);
}
/* apply new coords on active key block */
vertcos_to_key(ob, kb, vertCos);
}
static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush)
{
SculptSession *ss = ob->sculpt;
SculptSearchSphereData data;
PBVHNode **nodes = NULL;
int n, totnode;
/* Build a list of all nodes that are potentially within the brush's area of influence */
data.ss = ss;
data.sd = sd;
data.radius_squared = ss->cache->radius_squared;
data.original = ELEM4(brush->sculpt_tool,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ROTATE,
SCULPT_TOOL_THUMB,
SCULPT_TOOL_LAYER);
BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
/* Only act if some verts are inside the brush area */
if (totnode) {
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
sculpt_undo_push_node(ob, nodes[n],
brush->sculpt_tool == SCULPT_TOOL_MASK ?
SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS);
BLI_pbvh_node_mark_update(nodes[n]);
}
if (brush_needs_sculpt_normal(brush))
update_sculpt_normal(sd, ob, nodes, totnode);
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)
update_brush_local_mat(sd, ob);
/* Apply one type of brush action */
switch (brush->sculpt_tool) {
case SCULPT_TOOL_DRAW:
do_draw_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SMOOTH:
do_smooth_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CREASE:
do_crease_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_BLOB:
do_crease_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PINCH:
do_pinch_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_INFLATE:
do_inflate_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_GRAB:
do_grab_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_ROTATE:
do_rotate_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SNAKE_HOOK:
do_snake_hook_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_NUDGE:
do_nudge_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_THUMB:
do_thumb_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_LAYER:
do_layer_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_FLATTEN:
do_flatten_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CLAY:
do_clay_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CLAY_STRIPS:
do_clay_strips_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_FILL:
do_fill_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SCRAPE:
do_scrape_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_MASK:
do_mask_brush(sd, ob, nodes, totnode);
break;
}
if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
brush->autosmooth_factor > 0)
{
if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
smooth(sd, ob, nodes, totnode, brush->autosmooth_factor * (1 - ss->cache->pressure), FALSE);
}
else {
smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, FALSE);
}
}
MEM_freeN(nodes);
}
}
/* flush displacement from deformed PBVH vertex to original mesh */
static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd)
{
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
float disp[3], newco[3];
int index = vd->vert_indices[vd->i];
sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]);
mul_m3_v3(ss->deform_imats[index], disp);
add_v3_v3v3(newco, disp, ss->orig_cos[index]);
copy_v3_v3(ss->deform_cos[index], vd->co);
copy_v3_v3(ss->orig_cos[index], newco);
if (!ss->kb)
copy_v3_v3(me->mvert[index].co, newco);
}
static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
PBVHNode **nodes;
int totnode, n;
BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) {
/* these brushes start from original coordinates */
const int use_orco = (ELEM3(brush->sculpt_tool, SCULPT_TOOL_GRAB,
SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB));
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
PBVHProxyNode *proxies;
int proxy_count;
float (*orco)[3];
if (use_orco)
orco = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS)->co;
BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
float val[3];
int p;
if (use_orco)
copy_v3_v3(val, orco[vd.i]);
else
copy_v3_v3(val, vd.co);
for (p = 0; p < proxy_count; p++)
add_v3_v3(val, proxies[p].co[vd.i]);
sculpt_clip(sd, ss, vd.co, val);
if (ss->modifiers_active)
sculpt_flush_pbvhvert_deform(ob, &vd);
}
BLI_pbvh_vertex_iter_end;
BLI_pbvh_node_free_proxies(nodes[n]);
}
}
if (nodes)
MEM_freeN(nodes);
}
/* copy the modified vertices from bvh to the active key */
static void sculpt_update_keyblock(Object *ob)
{
SculptSession *ss = ob->sculpt;
float (*vertCos)[3];
/* Keyblock update happens after handling deformation caused by modifiers,
* so ss->orig_cos would be updated with new stroke */
if (ss->orig_cos) vertCos = ss->orig_cos;
else vertCos = BLI_pbvh_get_vertCos(ss->pbvh);
if (vertCos) {
sculpt_vertcos_to_key(ob, ss->kb, vertCos);
if (vertCos != ss->orig_cos)
MEM_freeN(vertCos);
}
}
/* flush displacement from deformed PBVH to original layer */
static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) {
/* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't
* propagate needed deformation to original base */
int n, totnode;
Mesh *me = (Mesh *)ob->data;
PBVHNode **nodes;
float (*vertCos)[3] = NULL;
if (ss->kb)
vertCos = MEM_callocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts");
BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
sculpt_flush_pbvhvert_deform(ob, &vd);
if (vertCos) {
int index = vd.vert_indices[vd.i];
copy_v3_v3(vertCos[index], ss->orig_cos[index]);
}
}
BLI_pbvh_vertex_iter_end;
}
if (vertCos) {
sculpt_vertcos_to_key(ob, ss->kb, vertCos);
MEM_freeN(vertCos);
}
MEM_freeN(nodes);
/* Modifiers could depend on mesh normals, so we should update them/
* Note, then if sculpting happens on locked key, normals should be re-calculated
* after applying coords from keyblock on base mesh */
BKE_mesh_calc_normals(me->mvert, me->totvert, me->mloop, me->mpoly, me->totloop, me->totpoly, NULL);
}
else if (ss->kb) {
sculpt_update_keyblock(ob);
}
}
/* Flip all the editdata across the axis/axes specified by symm. Used to
* calculate multiple modifications to the mesh when symmetry is enabled. */
static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm,
const char axis, const float angle,
const float UNUSED(feather))
{
(void)sd; /* unused */
flip_v3_v3(cache->location, cache->true_location, symm);
flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm);
flip_v3_v3(cache->view_normal, cache->true_view_normal, symm);
/* XXX This reduces the length of the grab delta if it approaches the line of symmetry
* XXX However, a different approach appears to be needed */
#if 0
if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
float frac = 1.0f / max_overlap_count(sd);
float reduce = (feather - frac) / (1 - frac);
printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce);
if (frac < 1)
mul_v3_fl(cache->grab_delta_symmetry, reduce);
}
#endif
unit_m4(cache->symm_rot_mat);
unit_m4(cache->symm_rot_mat_inv);
if (axis) { /* expects XYZ */
rotate_m4(cache->symm_rot_mat, axis, angle);
rotate_m4(cache->symm_rot_mat_inv, axis, -angle);
}
mul_m4_v3(cache->symm_rot_mat, cache->location);
mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry);
}
static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush,
const char symm, const int axis,
const float feather)
{
SculptSession *ss = ob->sculpt;
int i;
for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) {
const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X'];
ss->cache->radial_symmetry_pass = i;
calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather);
do_brush_action(sd, ob, brush);
}
}
/* noise texture gives different values for the same input coord; this
* can tear a multires mesh during sculpting so do a stitch in this
* case */
static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = paint_brush(&sd->paint);
MTex *mtex = &brush->mtex;
if (ss->multires && mtex->tex && mtex->tex->type == TEX_NOISE)
multires_stitch_grids(ob);
}
static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob)
{
Brush *brush = paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
const char symm = sd->flags & 7;
int i;
float feather = calc_symmetry_feather(sd, ss->cache);
cache->bstrength = brush_strength(sd, cache, feather);
cache->symmetry = symm;
/* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 0; i <= symm; ++i) {
if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
cache->mirror_symmetry_pass = i;
cache->radial_symmetry_pass = 0;
calc_brushdata_symm(sd, cache, i, 0, 0, feather);
do_brush_action(sd, ob, brush);
do_radial_symmetry(sd, ob, brush, i, 'X', feather);
do_radial_symmetry(sd, ob, brush, i, 'Y', feather);
do_radial_symmetry(sd, ob, brush, i, 'Z', feather);
}
}
sculpt_combine_proxies(sd, ob);
/* hack to fix noise texture tearing mesh */
sculpt_fix_noise_tear(sd, ob);
if (ss->modifiers_active)
sculpt_flush_stroke_deform(sd, ob);
cache->first_time = 0;
}
static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
const int radius = BKE_brush_size_get(scene, brush);
if (ss->texcache) {
MEM_freeN(ss->texcache);
ss->texcache = NULL;
}
/* Need to allocate a bigger buffer for bigger brush size */
ss->texcache_side = 2 * radius;
if (!ss->texcache || ss->texcache_side > ss->texcache_actual) {
ss->texcache = BKE_brush_gen_texture_cache(brush, radius);
ss->texcache_actual = ss->texcache_side;
}
}
void sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, int need_pmap)
{
DerivedMesh *dm;
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
ss->modifiers_active = sculpt_modifiers_active(scene, sd, ob);
/* BMESH ONLY --- at some point we should move sculpt code to use polygons only - but for now it needs tessfaces */
BKE_mesh_tessface_ensure(me);
/* needs to be called after we ensure tessface */
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
if (!mmd) ss->kb = ob_get_keyblock(ob);
else ss->kb = NULL;
if (mmd) {
ss->multires = mmd;
ss->totvert = dm->getNumVerts(dm);
ss->totpoly = dm->getNumPolys(dm);
ss->mvert = NULL;
ss->mpoly = NULL;
ss->mloop = NULL;
ss->face_normals = NULL;
}
else {
ss->totvert = me->totvert;
ss->totpoly = me->totpoly;
ss->mvert = me->mvert;
ss->mpoly = me->mpoly;
ss->mloop = me->mloop;
ss->face_normals = NULL;
ss->multires = NULL;
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
}
ss->pbvh = dm->getPBVH(ob, dm);
ss->pmap = (need_pmap && dm->getPolyMap) ? dm->getPolyMap(ob, dm) : NULL;
if (ss->modifiers_active) {
if (!ss->orig_cos) {
int a;
free_sculptsession_deformMats(ss);
ss->orig_cos = (ss->kb) ? key_to_vertcos(ob, ss->kb) : mesh_getVertexCos(me, NULL);
crazyspace_build_sculpt(scene, ob, &ss->deform_imats, &ss->deform_cos);
BLI_pbvh_apply_vertCos(ss->pbvh, ss->deform_cos);
for (a = 0; a < me->totvert; ++a) {
invert_m3(ss->deform_imats[a]);
}
}
}
else free_sculptsession_deformMats(ss);
/* if pbvh is deformed, key block is already applied to it */
if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) {
float (*vertCos)[3] = key_to_vertcos(ob, ss->kb);
if (vertCos) {
/* apply shape keys coordinates to PBVH */
BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
MEM_freeN(vertCos);
}
}
}
int sculpt_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
return ob && ob->mode & OB_MODE_SCULPT;
}
int sculpt_poll(bContext *C)
{
return sculpt_mode_poll(C) && paint_poll(C);
}
static const char *sculpt_tool_name(Sculpt *sd)
{
Brush *brush = paint_brush(&sd->paint);
switch ((BrushSculptTool)brush->sculpt_tool) {
case SCULPT_TOOL_DRAW:
return "Draw Brush";
case SCULPT_TOOL_SMOOTH:
return "Smooth Brush";
case SCULPT_TOOL_CREASE:
return "Crease Brush";
case SCULPT_TOOL_BLOB:
return "Blob Brush";
case SCULPT_TOOL_PINCH:
return "Pinch Brush";
case SCULPT_TOOL_INFLATE:
return "Inflate Brush";
case SCULPT_TOOL_GRAB:
return "Grab Brush";
case SCULPT_TOOL_NUDGE:
return "Nudge Brush";
case SCULPT_TOOL_THUMB:
return "Thumb Brush";
case SCULPT_TOOL_LAYER:
return "Layer Brush";
case SCULPT_TOOL_FLATTEN:
return "Flatten Brush";
case SCULPT_TOOL_CLAY:
return "Clay Brush";
case SCULPT_TOOL_CLAY_STRIPS:
return "Clay Strips Brush";
case SCULPT_TOOL_FILL:
return "Fill Brush";
case SCULPT_TOOL_SCRAPE:
return "Scrape Brush";
case SCULPT_TOOL_SNAKE_HOOK:
return "Snake Hook Brush";
case SCULPT_TOOL_ROTATE:
return "Rotate Brush";
case SCULPT_TOOL_MASK:
return "Mask Brush";
}
return "Sculpting";
}
/**
* Operator for applying a stroke (various attributes including mouse path)
* using the current brush. */
static void sculpt_cache_free(StrokeCache *cache)
{
if (cache->face_norms)
MEM_freeN(cache->face_norms);
MEM_freeN(cache);
}
/* Initialize mirror modifier clipping */
static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss)
{
ModifierData *md;
int i;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Mirror &&
(md->mode & eModifierMode_Realtime))
{
MirrorModifierData *mmd = (MirrorModifierData *)md;
if (mmd->flag & MOD_MIR_CLIPPING) {
/* check each axis for mirroring */
for (i = 0; i < 3; ++i) {
if (mmd->flag & (MOD_MIR_AXIS_X << i)) {
/* enable sculpt clipping */
ss->cache->flag |= CLIP_X << i;
/* update the clip tolerance */
if (mmd->tolerance >
ss->cache->clip_tolerance[i])
{
ss->cache->clip_tolerance[i] =
mmd->tolerance;
}
}
}
}
}
}
}
static void sculpt_omp_start(Sculpt *sd, SculptSession *ss)
{
StrokeCache *cache = ss->cache;
#ifdef _OPENMP
/* If using OpenMP then create a number of threads two times the
* number of processor cores.
* Justification: Empirically I've found that two threads per
* processor gives higher throughput. */
if (sd->flags & SCULPT_USE_OPENMP) {
cache->num_threads = 2 * omp_get_num_procs();
omp_set_num_threads(cache->num_threads);
}
else
#endif
{
(void)sd;
cache->num_threads = 1;
}
if (ss->multires) {
int i, gridsize, array_mem_size;
BLI_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL,
&gridsize, NULL, NULL);
array_mem_size = cache->num_threads * sizeof(void *);
cache->tmpgrid_co = MEM_mallocN(array_mem_size, "tmpgrid_co array");
cache->tmprow_co = MEM_mallocN(array_mem_size, "tmprow_co array");
cache->tmpgrid_mask = MEM_mallocN(array_mem_size, "tmpgrid_mask array");
cache->tmprow_mask = MEM_mallocN(array_mem_size, "tmprow_mask array");
for (i = 0; i < cache->num_threads; i++) {
const size_t row_size = sizeof(float) * gridsize;
const size_t co_row_size = 3 * row_size;
cache->tmprow_co[i] = MEM_mallocN(co_row_size, "tmprow_co");
cache->tmpgrid_co[i] = MEM_mallocN(co_row_size * gridsize, "tmpgrid_co");
cache->tmprow_mask[i] = MEM_mallocN(row_size, "tmprow_mask");
cache->tmpgrid_mask[i] = MEM_mallocN(row_size * gridsize, "tmpgrid_mask");
}
}
}
static void sculpt_omp_done(SculptSession *ss)
{
if (ss->multires) {
int i;
for (i = 0; i < ss->cache->num_threads; i++) {
MEM_freeN(ss->cache->tmpgrid_co[i]);
MEM_freeN(ss->cache->tmprow_co[i]);
MEM_freeN(ss->cache->tmpgrid_mask[i]);
MEM_freeN(ss->cache->tmprow_mask[i]);
}
MEM_freeN(ss->cache->tmpgrid_co);
MEM_freeN(ss->cache->tmprow_co);
MEM_freeN(ss->cache->tmpgrid_mask);
MEM_freeN(ss->cache->tmprow_mask);
}
}
/* Initialize the stroke cache invariants from operator properties */
static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2])
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
Brush *brush = paint_brush(&sd->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata);
Object *ob = CTX_data_active_object(C);
int i;
int mode;
ss->cache = cache;
/* Set scaling adjustment */
ss->cache->scale[0] = 1.0f / ob->size[0];
ss->cache->scale[1] = 1.0f / ob->size[1];
ss->cache->scale[2] = 1.0f / ob->size[2];
ss->cache->plane_trim_squared = brush->plane_trim * brush->plane_trim;
ss->cache->flag = 0;
sculpt_init_mirror_clipping(ob, ss);
/* Initial mouse location */
copy_v2_v2(ss->cache->initial_mouse, mouse);
mode = RNA_enum_get(op->ptr, "mode");
cache->invert = mode == BRUSH_STROKE_INVERT;
cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH;
/* not very nice, but with current events system implementation
* we can't handle brush appearance inversion hotkey separately (sergey) */
if (cache->invert) brush->flag |= BRUSH_INVERTED;
else brush->flag &= ~BRUSH_INVERTED;
/* Alt-Smooth */
if (ss->cache->alt_smooth) {
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
cache->saved_mask_brush_tool = brush->mask_tool;
brush->mask_tool = BRUSH_MASK_SMOOTH;
}
else {
Paint *p = &sd->paint;
Brush *br;
BLI_strncpy(cache->saved_active_brush_name, brush->id.name + 2,
sizeof(cache->saved_active_brush_name));
br = (Brush *)BKE_libblock_find_name(ID_BR, "Smooth");
if (br) {
paint_brush_set(p, br);
brush = br;
}
}
}
copy_v2_v2(cache->mouse, cache->initial_mouse);
copy_v2_v2(cache->tex_mouse, cache->initial_mouse);
/* Truly temporary data that isn't stored in properties */
cache->vc = vc;
cache->brush = brush;
/* cache projection matrix */
ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat);
ED_view3d_global_to_vector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal);
/* Initialize layer brush displacements and persistent coords */
if (brush->sculpt_tool == SCULPT_TOOL_LAYER) {
/* not supported yet for multires */
if (!ss->multires && !ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) {
if (!ss->layer_co)
ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert,
"sculpt mesh vertices copy");
if (ss->deform_cos) memcpy(ss->layer_co, ss->deform_cos, ss->totvert);
else {
for (i = 0; i < ss->totvert; ++i) {
copy_v3_v3(ss->layer_co[i], ss->mvert[i].co);
}
}
}
}
/* Make copies of the mesh vertex locations and normals for some tools */
if (brush->flag & BRUSH_ANCHORED) {
if (ss->face_normals) {
float *fn = ss->face_normals;
cache->face_norms = MEM_mallocN(sizeof(float) * 3 * ss->totpoly, "Sculpt face norms");
for (i = 0; i < ss->totpoly; ++i, fn += 3)
copy_v3_v3(cache->face_norms[i], fn);
}
cache->original = 1;
}
if (ELEM8(brush->sculpt_tool,
SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB,
SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY,
SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_ROTATE))
{
if (!(brush->flag & BRUSH_ACCUMULATE)) {
cache->original = 1;
}
}
cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0;
cache->first_time = 1;
cache->vertex_rotation = 0;
sculpt_omp_start(sd, ss);
}
static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush)
{
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
float mouse[2] = {
cache->mouse[0] - cache->vc->ar->winrct.xmin,
cache->mouse[1] - cache->vc->ar->winrct.ymin
};
int tool = brush->sculpt_tool;
if (ELEM5(tool,
SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE,
SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_THUMB))
{
float grab_location[3], imat[4][4], delta[3], loc[3];
if (cache->first_time) {
copy_v3_v3(cache->orig_grab_location,
cache->true_location);
}
else if (tool == SCULPT_TOOL_SNAKE_HOOK)
add_v3_v3(cache->true_location, cache->grab_delta);
/* compute 3d coordinate at same z from original location + mouse */
mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location);
initgrabz(cache->vc->rv3d, loc[0], loc[1], loc[2]);
ED_view3d_win_to_3d(cache->vc->ar, loc, mouse, grab_location);
/* compute delta to move verts by */
if (!cache->first_time) {
switch (tool) {
case SCULPT_TOOL_GRAB:
case SCULPT_TOOL_THUMB:
sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, delta);
add_v3_v3(cache->grab_delta, delta);
break;
case SCULPT_TOOL_CLAY_STRIPS:
case SCULPT_TOOL_NUDGE:
case SCULPT_TOOL_SNAKE_HOOK:
if (brush->flag & BRUSH_ANCHORED) {
float orig[3];
mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location);
sub_v3_v3v3(cache->grab_delta, grab_location, orig);
}
else {
sub_v3_v3v3(cache->grab_delta, grab_location,
cache->old_grab_location);
}
invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, cache->grab_delta);
break;
}
}
else {
zero_v3(cache->grab_delta);
}
copy_v3_v3(cache->old_grab_location, grab_location);
if (tool == SCULPT_TOOL_GRAB)
copy_v3_v3(sd->anchored_location, cache->true_location);
else if (tool == SCULPT_TOOL_THUMB)
copy_v3_v3(sd->anchored_location, cache->orig_grab_location);
if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
/* location stays the same for finding vertices in brush radius */
copy_v3_v3(cache->true_location, cache->orig_grab_location);
sd->draw_anchored = 1;
copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
sd->anchored_size = cache->pixel_radius;
}
}
}
/* Initialize the stroke cache variants from operator properties */
static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob,
struct PaintStroke *stroke,
PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
Brush *brush = paint_brush(&sd->paint);
int dx, dy;
/* RNA_float_get_array(ptr, "location", cache->traced_location); */
if (cache->first_time ||
!((brush->flag & BRUSH_ANCHORED) ||
(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) ||
(brush->sculpt_tool == SCULPT_TOOL_ROTATE))
)
{
RNA_float_get_array(ptr, "location", cache->true_location);
}
cache->pen_flip = RNA_boolean_get(ptr, "pen_flip");
RNA_float_get_array(ptr, "mouse", cache->mouse);
/* XXX: Use pressure value from first brush step for brushes which don't
* support strokes (grab, thumb). They depends on initial state and
* brush coord/pressure/etc.
* It's more an events design issue, which doesn't split coordinate/pressure/angle
* changing events. We should avoid this after events system re-design */
if (paint_space_stroke_enabled(brush) || cache->first_time)
cache->pressure = RNA_float_get(ptr, "pressure");
/* Truly temporary data that isn't stored in properties */
sd->draw_pressure = 1;
sd->pressure_value = cache->pressure;
cache->previous_pixel_radius = cache->pixel_radius;
cache->pixel_radius = BKE_brush_size_get(scene, brush);
if (cache->first_time) {
if (!BKE_brush_use_locked_size(scene, brush)) {
cache->initial_radius = paint_calc_object_space_radius(cache->vc,
cache->true_location,
BKE_brush_size_get(scene, brush));
BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius);
}
else {
cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush);
}
}
if (BKE_brush_use_size_pressure(scene, brush)) {
cache->pixel_radius *= cache->pressure;
cache->radius = cache->initial_radius * cache->pressure;
}
else
cache->radius = cache->initial_radius;
cache->radius_squared = cache->radius * cache->radius;
if (!(brush->flag & BRUSH_ANCHORED ||
ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)))
{
copy_v2_v2(cache->tex_mouse, cache->mouse);
if ((brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) &&
(brush->flag & BRUSH_RANDOM_ROTATION) &&
!(brush->flag & BRUSH_RAKE))
{
cache->special_rotation = 2.0f * (float)M_PI * BLI_frand();
}
}
if (brush->flag & BRUSH_ANCHORED) {
int hit = 0;
dx = cache->mouse[0] - cache->initial_mouse[0];
dy = cache->mouse[1] - cache->initial_mouse[1];
sd->anchored_size = cache->pixel_radius = sqrt(dx * dx + dy * dy);
cache->special_rotation = atan2(dx, dy) + M_PI;
if (brush->flag & BRUSH_EDGE_TO_EDGE) {
float halfway[2];
float out[3];
halfway[0] = (float)dx * 0.5f + cache->initial_mouse[0];
halfway[1] = (float)dy * 0.5f + cache->initial_mouse[1];
if (sculpt_stroke_get_location(C, out, halfway)) {
copy_v3_v3(sd->anchored_location, out);
copy_v2_v2(sd->anchored_initial_mouse, halfway);
copy_v2_v2(cache->tex_mouse, halfway);
copy_v3_v3(cache->true_location, sd->anchored_location);
sd->anchored_size /= 2.0f;
cache->pixel_radius /= 2.0f;
hit = 1;
}
}
if (!hit)
copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
cache->radius = paint_calc_object_space_radius(paint_stroke_view_context(stroke),
cache->true_location,
cache->pixel_radius);
cache->radius_squared = cache->radius * cache->radius;
copy_v3_v3(sd->anchored_location, cache->true_location);
sd->draw_anchored = 1;
}
else if (brush->flag & BRUSH_RAKE) {
const float u = 0.5f;
const float v = 1 - u;
const float r = 20;
const float dx = cache->last_rake[0] - cache->mouse[0];
const float dy = cache->last_rake[1] - cache->mouse[1];
if (cache->first_time) {
copy_v2_v2(cache->last_rake, cache->mouse);
}
else if (dx * dx + dy * dy >= r * r) {
cache->special_rotation = atan2(dx, dy);
cache->last_rake[0] = u * cache->last_rake[0] + v * cache->mouse[0];
cache->last_rake[1] = u * cache->last_rake[1] + v * cache->mouse[1];
}
}
sculpt_update_brush_delta(sd, ob, brush);
if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) {
dx = cache->mouse[0] - cache->initial_mouse[0];
dy = cache->mouse[1] - cache->initial_mouse[1];
cache->vertex_rotation = -atan2f(dx, dy) * cache->bstrength;
sd->draw_anchored = 1;
copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
copy_v3_v3(sd->anchored_location, cache->true_location);
sd->anchored_size = cache->pixel_radius;
}
sd->special_rotation = cache->special_rotation;
}
/* Returns true iff any of the smoothing modes are active (currently
* one of smooth brush, autosmooth, mask smooth, or shift-key
* smooth) */
static int sculpt_any_smooth_mode(const Brush *brush,
StrokeCache *cache,
int stroke_mode)
{
return ((stroke_mode == BRUSH_STROKE_SMOOTH) ||
(cache && cache->alt_smooth) ||
(brush->sculpt_tool == SCULPT_TOOL_SMOOTH) ||
(brush->autosmooth_factor > 0) ||
((brush->sculpt_tool == SCULPT_TOOL_MASK) &&
(brush->mask_tool == BRUSH_MASK_SMOOTH)));
}
static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob)
{
SculptSession *ss = ob->sculpt;
if (ss->modifiers_active) {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
sculpt_update_mesh_elements(CTX_data_scene(C), sd, ob,
sculpt_any_smooth_mode(brush, ss->cache, 0));
}
}
typedef struct {
SculptSession *ss;
float *ray_start, *ray_normal;
int hit;
float dist;
int original;
} SculptRaycastData;
static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
{
if (BLI_pbvh_node_get_tmin(node) < *tmin) {
SculptRaycastData *srd = data_v;
float (*origco)[3] = NULL;
if (srd->original && srd->ss->cache) {
/* intersect with coordinates from before we started stroke */
SculptUndoNode *unode = sculpt_undo_get_node(node);
origco = (unode) ? unode->co : NULL;
}
if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) {
srd->hit = 1;
*tmin = srd->dist;
}
}
}
/* Do a raycast in the tree to find the 3d brush location
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise
*/
int sculpt_stroke_get_location(bContext *C, float out[3], float mouse[2])
{
ViewContext vc;
Object *ob;
SculptSession *ss;
StrokeCache *cache;
float ray_start[3], ray_end[3], ray_normal[3], dist;
float obimat[4][4];
float mval[2];
SculptRaycastData srd;
view3d_set_viewcontext(C, &vc);
ob = vc.obact;
ss = ob->sculpt;
cache = ss->cache;
sculpt_stroke_modifiers_check(C, ob);
mval[0] = mouse[0] - vc.ar->winrct.xmin;
mval[1] = mouse[1] - vc.ar->winrct.ymin;
ED_view3d_win_to_segment_clip(vc.ar, vc.v3d, mval, ray_start, ray_end);
invert_m4_m4(obimat, ob->obmat);
mul_m4_v3(obimat, ray_start);
mul_m4_v3(obimat, ray_end);
sub_v3_v3v3(ray_normal, ray_end, ray_start);
dist = normalize_v3(ray_normal);
srd.ss = vc.obact->sculpt;
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
srd.dist = dist;
srd.hit = 0;
srd.original = (cache) ? cache->original : 0;
BLI_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd,
ray_start, ray_normal, srd.original);
copy_v3_v3(out, ray_normal);
mul_v3_fl(out, srd.dist);
add_v3_v3(out, ray_start);
return srd.hit;
}
static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
MTex *mtex = &brush->mtex;
/* init mtex nodes */
if (mtex->tex && mtex->tex->nodetree)
ntreeTexBeginExecTree(mtex->tex->nodetree, 1); /* has internal flag to detect it only does it once */
/* TODO: Shouldn't really have to do this at the start of every
* stroke, but sculpt would need some sort of notification when
* changes are made to the texture. */
sculpt_update_tex(scene, sd, ss);
}
static int sculpt_brush_stroke_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
int mode = RNA_enum_get(op->ptr, "mode");
int is_smooth = 0;
view3d_operator_needs_opengl(C);
sculpt_brush_init_tex(scene, sd, ss);
is_smooth = sculpt_any_smooth_mode(brush, NULL, mode);
sculpt_update_mesh_elements(scene, sd, ob, is_smooth);
return 1;
}
static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
/* Restore the mesh before continuing with anchored stroke */
if ((brush->flag & BRUSH_ANCHORED) ||
(brush->sculpt_tool == SCULPT_TOOL_GRAB &&
BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) ||
(brush->flag & BRUSH_RESTORE_MESH))
{
paint_mesh_restore_co(sd, ss);
}
}
static void sculpt_flush_update(bContext *C)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
ARegion *ar = CTX_wm_region(C);
MultiresModifierData *mmd = ss->multires;
if (mmd)
multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
if (ob->derivedFinal) /* VBO no longer valid */
GPU_drawobject_free(ob->derivedFinal);
if (ss->modifiers_active) {
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
ED_region_tag_redraw(ar);
}
else {
rcti r;
BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL);
if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) {
if (ss->cache)
ss->cache->previous_r = r;
r.xmin += ar->winrct.xmin + 1;
r.xmax += ar->winrct.xmin - 1;
r.ymin += ar->winrct.ymin + 1;
r.ymax += ar->winrct.ymin - 1;
ss->partial_redraw = 1;
ED_region_tag_redraw_partial(ar, &r);
}
}
}
/* Returns whether the mouse/stylus is over the mesh (1)
* or over the background (0) */
static int over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y)
{
float mouse[2], co[3];
mouse[0] = x;
mouse[1] = y;
return sculpt_stroke_get_location(C, co, mouse);
}
static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
const float mouse[2])
{
/* Don't start the stroke until mouse goes over the mesh.
* note: event will only be null when re-executing the saved stroke. */
if (over_mesh(C, op, mouse[0], mouse[1])) {
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
sculpt_update_cache_invariants(C, sd, ss, op, mouse);
sculpt_undo_push_begin(sculpt_tool_name(sd));
return 1;
}
else
return 0;
}
static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
sculpt_stroke_modifiers_check(C, ob);
sculpt_update_cache_variants(C, sd, ob, stroke, itemptr);
sculpt_restore_mesh(sd, ss);
do_symmetrical_brush_actions(sd, ob);
/* Cleanup */
sculpt_flush_update(C);
}
static void sculpt_brush_exit_tex(Sculpt *sd)
{
Brush *brush = paint_brush(&sd->paint);
MTex *mtex = &brush->mtex;
if (mtex->tex && mtex->tex->nodetree)
ntreeTexEndExecTree(mtex->tex->nodetree->execdata, 1);
}
static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(stroke))
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
sculpt_omp_done(ss);
/* reset values used to draw brush after completing the stroke */
sd->draw_anchored = 0;
sd->draw_pressure = 0;
sd->special_rotation = 0;
/* Finished */
if (ss->cache) {
Brush *brush = paint_brush(&sd->paint);
brush->flag &= ~BRUSH_INVERTED;
sculpt_stroke_modifiers_check(C, ob);
/* Alt-Smooth */
if (ss->cache->alt_smooth) {
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
brush->mask_tool = ss->cache->saved_mask_brush_tool;
}
else {
Paint *p = &sd->paint;
brush = (Brush *)BKE_libblock_find_name(ID_BR, ss->cache->saved_active_brush_name);
if (brush) {
paint_brush_set(p, brush);
}
}
}
/* update last stroke position */
ob->sculpt->last_stroke_valid = 1;
copy_v3_v3(ob->sculpt->last_stroke, ss->cache->true_location);
mul_m4_v3(ob->obmat, ob->sculpt->last_stroke);
sculpt_cache_free(ss->cache);
ss->cache = NULL;
sculpt_undo_push_end();
BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL);
/* optimization: if there is locked key and active modifiers present in */
/* the stack, keyblock is updating at each step. otherwise we could update */
/* keyblock only when stroke is finished */
if (ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ob);
ss->partial_redraw = 0;
/* try to avoid calling this, only for e.g. linked duplicates now */
if (((Mesh *)ob->data)->id.us > 1)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
}
sculpt_brush_exit_tex(sd);
}
static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
struct PaintStroke *stroke;
int ignore_background_click;
if (!sculpt_brush_stroke_init(C, op))
return OPERATOR_CANCELLED;
stroke = paint_stroke_new(C, sculpt_stroke_get_location,
sculpt_stroke_test_start,
sculpt_stroke_update_step,
sculpt_stroke_done, event->type);
op->customdata = stroke;
/* For tablet rotation */
ignore_background_click = RNA_boolean_get(op->ptr,
"ignore_background_click");
if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) {
paint_stroke_data_free(op);
return OPERATOR_PASS_THROUGH;
}
/* add modal handler */
WM_event_add_modal_handler(C, op);
op->type->modal(C, op, event);
return OPERATOR_RUNNING_MODAL;
}
static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
{
if (!sculpt_brush_stroke_init(C, op))
return OPERATOR_CANCELLED;
op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start,
sculpt_stroke_update_step, sculpt_stroke_done, 0);
/* frees op->customdata */
paint_stroke_exec(C, op);
return OPERATOR_FINISHED;
}
static int sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
if (ss->cache) {
paint_mesh_restore_co(sd, ss);
}
paint_stroke_cancel(C, op);
if (ss->cache) {
sculpt_cache_free(ss->cache);
ss->cache = NULL;
}
sculpt_brush_exit_tex(sd);
return OPERATOR_CANCELLED;
}
static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
{
static EnumPropertyItem stroke_mode_items[] = {
{BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally"},
{BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"},
{BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke"},
{0}
};
/* identifiers */
ot->name = "Sculpt";
ot->idname = "SCULPT_OT_brush_stroke";
ot->description = "Sculpt a stroke into the geometry";
/* api callbacks */
ot->invoke = sculpt_brush_stroke_invoke;
ot->modal = paint_stroke_modal;
ot->exec = sculpt_brush_stroke_exec;
ot->poll = sculpt_poll;
ot->cancel = sculpt_brush_stroke_cancel;
/* flags (sculpt does own undo? (ton) */
ot->flag = OPTYPE_BLOCKING;
/* properties */
RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement,
"Stroke", "");
RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL,
"Sculpt Stroke Mode",
"Action taken when a sculpt stroke is made");
RNA_def_boolean(ot->srna, "ignore_background_click", 0,
"Ignore Background Click",
"Clicks on the background do not start the stroke");
}
/**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/
static int sculpt_set_persistent_base(bContext *C, wmOperator *UNUSED(op))
{
SculptSession *ss = CTX_data_active_object(C)->sculpt;
if (ss) {
if (ss->layer_co)
MEM_freeN(ss->layer_co);
ss->layer_co = NULL;
}
return OPERATOR_FINISHED;
}
static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Persistent Base";
ot->idname = "SCULPT_OT_set_persistent_base";
ot->description = "Reset the copy of the mesh that is being sculpted on";
/* api callbacks */
ot->exec = sculpt_set_persistent_base;
ot->poll = sculpt_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/**** Toggle operator for turning sculpt mode on or off ****/
static void sculpt_init_session(Scene *scene, Object *ob)
{
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
sculpt_update_mesh_elements(scene, scene->toolsettings->sculpt, ob, 0);
}
void ED_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
{
float *paint_mask;
Mesh *me = ob->data;
paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
/* if multires is active, create a grid paint mask layer if there
* isn't one already */
if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) {
GridPaintMask *gmask;
int level = MAX2(1, mmd->sculptlvl);
int gridsize = ccg_gridsize(level);
int gridarea = gridsize * gridsize;
int i, j;
gmask = CustomData_add_layer(&me->ldata, CD_GRID_PAINT_MASK,
CD_CALLOC, NULL, me->totloop);
for (i = 0; i < me->totloop; i++) {
GridPaintMask *gpm = &gmask[i];
gpm->level = level;
gpm->data = MEM_callocN(sizeof(float) * gridarea,
"GridPaintMask.data");
}
/* if vertices already have mask, copy into multires data */
if (paint_mask) {
for (i = 0; i < me->totpoly; i++) {
const MPoly *p = &me->mpoly[i];
float avg = 0;
/* mask center */
for (j = 0; j < p->totloop; j++) {
const MLoop *l = &me->mloop[p->loopstart + j];
avg += paint_mask[l->v];
}
avg /= (float)p->totloop;
/* fill in multires mask corner */
for (j = 0; j < p->totloop; j++) {
GridPaintMask *gpm = &gmask[p->loopstart + j];
const MLoop *l = &me->mloop[p->loopstart + j];
const MLoop *prev = ME_POLY_LOOP_PREV(me->mloop, p, j);
const MLoop *next = ME_POLY_LOOP_NEXT(me->mloop, p, j);
gpm->data[0] = avg;
gpm->data[1] = (paint_mask[l->v] +
paint_mask[next->v]) * 0.5f;
gpm->data[2] = (paint_mask[l->v] +
paint_mask[prev->v]) * 0.5f;
gpm->data[3] = paint_mask[l->v];
}
}
}
}
/* create vertex paint mask layer if there isn't one already */
if (!paint_mask) {
CustomData_add_layer(&me->vdata, CD_PAINT_MASK,
CD_CALLOC, NULL, me->totvert);
}
}
static int sculpt_toggle_mode(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
int flush_recalc = 0;
/* multires in sculpt mode could have different from object mode subdivision level */
flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl;
/* if object has got active modifiers, it's dm could be different in sculpt mode */
flush_recalc |= sculpt_has_active_modifiers(scene, ob);
if (ob->mode & OB_MODE_SCULPT) {
if (mmd)
multires_force_update(ob);
if (flush_recalc)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
/* Leave sculptmode */
ob->mode &= ~OB_MODE_SCULPT;
free_sculptsession(ob);
}
else {
/* Enter sculptmode */
ob->mode |= OB_MODE_SCULPT;
if (flush_recalc)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
/* Create persistent sculpt mode data */
if (!ts->sculpt) {
ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data");
/* Turn on X plane mirror symmetry by default */
ts->sculpt->flags |= SCULPT_SYMM_X;
}
/* Create sculpt mode session data */
if (ob->sculpt)
free_sculptsession(ob);
sculpt_init_session(scene, ob);
/* Mask layer is required */
ED_sculpt_mask_layers_ensure(ob, mmd);
BKE_paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT);
paint_cursor_start(C, sculpt_poll);
}
WM_event_add_notifier(C, NC_SCENE | ND_MODE, CTX_data_scene(C));
return OPERATOR_FINISHED;
}
static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Sculpt Mode";
ot->idname = "SCULPT_OT_sculptmode_toggle";
ot->description = "Toggle sculpt mode in 3D view";
/* api callbacks */
ot->exec = sculpt_toggle_mode;
ot->poll = ED_operator_object_active_editable_mesh;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
WM_operatortype_append(SCULPT_OT_sculptmode_toggle);
WM_operatortype_append(SCULPT_OT_set_persistent_base);
}