2.5/Paint:

* Some initial work on a new paint abstraction, PaintStroke. For now, most of the code just pulls out stroke-related stuff from sculpt mode, next step is to integrate the other paint modes to use this. It'll enable stuff like smooth stroke for all the paint modes with less code duplication.
This commit is contained in:
2009-08-19 21:24:52 +00:00
parent 8a5a7d3d28
commit c21627e31b
6 changed files with 415 additions and 270 deletions

View File

@@ -70,6 +70,7 @@ float *give_cursor(struct Scene *scene, struct View3D *v3d);
void initgrabz(struct RegionView3D *rv3d, float x, float y, float z);
void window_to_3d(struct ARegion *ar, float *vec, short mx, short my);
void window_to_3d_delta(struct ARegion *ar, float *vec, short mx, short my);
void view3d_unproject(struct bglMats *mats, float out[3], const short x, const short y, const float z);
/* Depth buffer */
float read_cached_depth(struct ViewContext *vc, int x, int y);

View File

@@ -29,13 +29,28 @@
#ifndef ED_PAINT_INTERN_H
#define ED_PAINT_INTERN_H
struct bContext;
struct Scene;
struct Object;
struct Mesh;
struct PaintStroke;
struct PointerRNA;
struct ViewContext;
struct wmEvent;
struct wmOperator;
struct wmOperatorType;
struct ARegion;
/* paint_stroke.c */
typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, struct wmEvent *event);
typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr);
typedef void (*StrokeDone)(struct bContext *C, struct PaintStroke *stroke);
struct PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start,
StrokeUpdateStep update_step, StrokeDone done);
int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event);
struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
/* paint_vertex.c */
void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot);
void PAINT_OT_weight_paint_radial_control(struct wmOperatorType *ot);

View File

@@ -0,0 +1,224 @@
/*
* ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2009 by Nicholas Bishop
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "RNA_access.h"
#include "BKE_context.h"
#include "BKE_paint.h"
#include "WM_api.h"
#include "WM_types.h"
#include "BLI_arithb.h"
#include "BIF_glutil.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "paint_intern.h"
#include <float.h>
#include <math.h>
typedef struct PaintStroke {
/* Cached values */
ViewContext vc;
bglMats mats;
Brush *brush;
float last_mouse_position[2];
/* Set whether any stroke step has yet occured
e.g. in sculpt mode, stroke doesn't start until cursor
passes over the mesh */
int stroke_started;
StrokeTestStart test_start;
StrokeUpdateStep update_step;
StrokeDone done;
} PaintStroke;
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
{
PointerRNA itemptr;
float cur_depth, pressure = 1;
float center[3];
PaintStroke *stroke = op->customdata;
cur_depth = read_cached_depth(&stroke->vc, mouse[0], mouse[1]);
view3d_unproject(&stroke->mats, center, mouse[0], mouse[1], cur_depth);
/* Tablet */
if(event->custom == EVT_DATA_TABLET) {
wmTabletData *wmtab= event->customdata;
if(wmtab->Active != EVT_TABLET_NONE)
pressure= wmtab->Pressure;
}
/* Add to stroke */
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "location", center);
RNA_float_set_array(&itemptr, "mouse", mouse);
RNA_boolean_set(&itemptr, "flip", event->shift);
RNA_float_set(&itemptr, "pressure", pressure);
stroke->last_mouse_position[0] = mouse[0];
stroke->last_mouse_position[1] = mouse[1];
stroke->update_step(C, stroke, &itemptr);
}
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
{
output[0] = event->x;
output[1] = event->y;
if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
/* If the mouse is moving within the radius of the last move,
don't update the mouse position. This allows sharp turns. */
if(dx*dx + dy*dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius)
return 0;
output[0] = event->x * v + stroke->last_mouse_position[0] * u;
output[1] = event->y * v + stroke->last_mouse_position[1] * u;
}
return 1;
}
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
static int paint_space_stroke_enabled(Brush *br)
{
return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
}
/* For brushes with stroke spacing enabled, moves mouse in steps
towards the final mouse location. */
static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
{
PaintStroke *stroke = op->customdata;
int cnt = 0;
if(paint_space_stroke_enabled(stroke->brush)) {
float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
float length, scale;
int steps = 0, i;
/* Normalize the vector between the last stroke dot and the goal */
length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
if(length > FLT_EPSILON) {
scale = stroke->brush->spacing / length;
vec[0] *= scale;
vec[1] *= scale;
steps = (int)(length / stroke->brush->spacing);
for(i = 0; i < steps; ++i, ++cnt) {
mouse[0] += vec[0];
mouse[1] += vec[1];
paint_brush_stroke_add_step(C, op, event, mouse);
}
}
}
return cnt;
}
/**** Public API ****/
PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start,
StrokeUpdateStep update_step, StrokeDone done)
{
PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C)));
view3d_set_viewcontext(C, &stroke->vc);
view3d_get_transformation(&stroke->vc, stroke->vc.obact, &stroke->mats);
stroke->test_start = test_start;
stroke->update_step = update_step;
stroke->done = done;
return stroke;
}
int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
PaintStroke *stroke = op->customdata;
float mouse[2];
if(!stroke->stroke_started) {
stroke->last_mouse_position[0] = event->x;
stroke->last_mouse_position[1] = event->y;
stroke->stroke_started = stroke->test_start(C, op, event);
ED_region_tag_redraw(ar);
}
if(stroke->stroke_started) {
if(paint_smooth_stroke(stroke, mouse, event)) {
if(paint_space_stroke_enabled(stroke->brush)) {
if(!paint_space_stroke(C, op, event, mouse))
ED_region_tag_redraw(ar);
}
else
paint_brush_stroke_add_step(C, op, event, mouse);
}
else
ED_region_tag_redraw(ar);
}
/* TODO: fix hardcoded event here */
if(event->type == LEFTMOUSE && event->val == 0) {
stroke->done(C, stroke);
MEM_freeN(stroke);
return OPERATOR_FINISHED;
}
else
return OPERATOR_RUNNING_MODAL;
}
ViewContext *paint_stroke_view_context(PaintStroke *stroke)
{
return &stroke->vc;
}

View File

@@ -1709,122 +1709,123 @@ static void vpaint_exit(bContext *C, wmOperator *op)
op->customdata= NULL;
}
static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
static void vpaint_dot(bContext *C, struct VPaintData *vpd, wmEvent *event)
{
ToolSettings *ts= CTX_data_tool_settings(C);
VPaint *vp= ts->vpaint;
Brush *brush = paint_brush(&vp->paint);
switch(event->type) {
case LEFTMOUSE:
if(event->val==0) { /* release */
vpaint_exit(C, op);
return OPERATOR_FINISHED;
}
/* pass on, first press gets painted too */
ViewContext *vc= &vpd->vc;
Object *ob= vc->obact;
Mesh *me= ob->data;
float mat[4][4];
int *indexar= vpd->indexar;
int totindex, index;
short mval[2];
case MOUSEMOVE:
{
struct VPaintData *vpd= op->customdata;
ViewContext *vc= &vpd->vc;
Object *ob= vc->obact;
Mesh *me= ob->data;
float mat[4][4];
int *indexar= vpd->indexar;
int totindex, index;
short mval[2];
view3d_operator_needs_opengl(C);
view3d_operator_needs_opengl(C);
/* load projection matrix */
wmMultMatrix(ob->obmat);
wmGetSingleMatrix(mat);
wmLoadMatrix(vc->rv3d->viewmat);
/* load projection matrix */
wmMultMatrix(ob->obmat);
wmGetSingleMatrix(mat);
wmLoadMatrix(vc->rv3d->viewmat);
mval[0]= event->x - vc->ar->winrct.xmin;
mval[1]= event->y - vc->ar->winrct.ymin;
mval[0]= event->x - vc->ar->winrct.xmin;
mval[1]= event->y - vc->ar->winrct.ymin;
/* which faces are involved */
if(vp->flag & VP_AREA) {
totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size);
}
else {
indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
if(indexar[0]) totindex= 1;
else totindex= 0;
}
/* which faces are involved */
if(vp->flag & VP_AREA) {
totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size);
}
else {
indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
if(indexar[0]) totindex= 1;
else totindex= 0;
}
MTC_Mat4SwapMat4(vc->rv3d->persmat, mat);
MTC_Mat4SwapMat4(vc->rv3d->persmat, mat);
if(vp->flag & VP_COLINDEX) {
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
if(vp->flag & VP_COLINDEX) {
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
if(mface->mat_nr!=ob->actcol-1) {
indexar[index]= 0;
}
}
if(mface->mat_nr!=ob->actcol-1) {
indexar[index]= 0;
}
}
if((G.f & G_FACESELECT) && me->mface) {
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
if((mface->flag & ME_FACE_SEL)==0)
indexar[index]= 0;
}
}
}
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
unsigned int *mcol= ( (unsigned int *)me->mcol) + 4*(indexar[index]-1);
unsigned int *mcolorig= ( (unsigned int *)vp->vpaint_prev) + 4*(indexar[index]-1);
int alpha;
if(vp->mode==VP_BLUR) {
unsigned int fcol1= mcol_blend( mcol[0], mcol[1], 128);
if(mface->v4) {
unsigned int fcol2= mcol_blend( mcol[2], mcol[3], 128);
vpd->paintcol= mcol_blend( fcol1, fcol2, 128);
}
else {
vpd->paintcol= mcol_blend( mcol[2], fcol1, 170);
}
}
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v1, mval);
if(alpha) vpaint_blend(vp, mcol, mcolorig, vpd->paintcol, alpha);
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v2, mval);
if(alpha) vpaint_blend(vp, mcol+1, mcolorig+1, vpd->paintcol, alpha);
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v3, mval);
if(alpha) vpaint_blend(vp, mcol+2, mcolorig+2, vpd->paintcol, alpha);
if(mface->v4) {
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v4, mval);
if(alpha) vpaint_blend(vp, mcol+3, mcolorig+3, vpd->paintcol, alpha);
}
}
}
MTC_Mat4SwapMat4(vc->rv3d->persmat, mat);
do_shared_vertexcol(me);
ED_region_tag_redraw(vc->ar);
DAG_object_flush_update(vc->scene, ob, OB_RECALC_DATA);
}
}
}
if((G.f & G_FACESELECT) && me->mface) {
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
if((mface->flag & ME_FACE_SEL)==0)
indexar[index]= 0;
}
}
}
for(index=0; index<totindex; index++) {
if(indexar[index] && indexar[index]<=me->totface) {
MFace *mface= ((MFace *)me->mface) + (indexar[index]-1);
unsigned int *mcol= ( (unsigned int *)me->mcol) + 4*(indexar[index]-1);
unsigned int *mcolorig= ( (unsigned int *)vp->vpaint_prev) + 4*(indexar[index]-1);
int alpha;
if(vp->mode==VP_BLUR) {
unsigned int fcol1= mcol_blend( mcol[0], mcol[1], 128);
if(mface->v4) {
unsigned int fcol2= mcol_blend( mcol[2], mcol[3], 128);
vpd->paintcol= mcol_blend( fcol1, fcol2, 128);
}
else {
vpd->paintcol= mcol_blend( mcol[2], fcol1, 170);
}
}
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v1, mval);
if(alpha) vpaint_blend(vp, mcol, mcolorig, vpd->paintcol, alpha);
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v2, mval);
if(alpha) vpaint_blend(vp, mcol+1, mcolorig+1, vpd->paintcol, alpha);
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v3, mval);
if(alpha) vpaint_blend(vp, mcol+2, mcolorig+2, vpd->paintcol, alpha);
if(mface->v4) {
alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v4, mval);
if(alpha) vpaint_blend(vp, mcol+3, mcolorig+3, vpd->paintcol, alpha);
}
}
}
MTC_Mat4SwapMat4(vc->rv3d->persmat, mat);
do_shared_vertexcol(me);
ED_region_tag_redraw(vc->ar);
DAG_object_flush_update(vc->scene, ob, OB_RECALC_DATA);
}
static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
{
switch(event->type) {
case LEFTMOUSE:
if(event->val==0) { /* release */
vpaint_exit(C, op);
return OPERATOR_FINISHED;
}
/* pass on, first press gets painted too */
case MOUSEMOVE:
vpaint_dot(C, op->customdata, event);
break;
}
return OPERATOR_RUNNING_MODAL;
}

View File

@@ -80,6 +80,7 @@
#include "ED_space_api.h"
#include "ED_util.h"
#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
@@ -144,7 +145,6 @@ typedef struct StrokeCache {
int first_time; /* Beginning of stroke may do some things special */
ViewContext vc;
bglMats *mats;
short (*orig_norms)[3]; /* Copy of the mesh vertices' normals */
@@ -178,19 +178,6 @@ typedef struct ProjVert {
* Simple functions to get data from the GL
*/
/* Uses window coordinates (x,y) and depth component z to find a point in
modelspace */
static void unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
{
double ux, uy, uz;
gluUnProject(x,y,z, mats->modelview, mats->projection,
(GLint *)mats->viewport, &ux, &uy, &uz );
out[0] = ux;
out[1] = uy;
out[2] = uz;
}
/* Convert a point in model coordinates to 2D screen coordinates. */
static void projectf(bglMats *mats, const float v[3], float p[2])
{
@@ -547,7 +534,7 @@ static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, const ListBase
ActiveData *node= active_verts->first;
/* area_normal and cntr define the plane towards which vertices are squashed */
float area_normal[3];
float cntr[3], cntr2[3], bstr;
float cntr[3], cntr2[3], bstr = 0;
int flip = 0;
calc_area_normal(sd, ss, area_normal, active_verts);
@@ -1169,7 +1156,7 @@ static float unproject_brush_radius(SculptSession *ss, float offset)
float brush_edge[3];
/* In anchored mode, brush size changes with mouse loc, otherwise it's fixed using the brush radius */
unproject(ss->cache->mats, brush_edge, ss->cache->initial_mouse[0] + offset,
view3d_unproject(ss->cache->mats, brush_edge, ss->cache->initial_mouse[0] + offset,
ss->cache->initial_mouse[1], ss->cache->depth);
return VecLenf(ss->cache->true_location, brush_edge);
@@ -1194,6 +1181,7 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
Brush *brush = paint_brush(&sd->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata);
int i;
ss->cache = cache;
@@ -1209,10 +1197,8 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
/* Truly temporary data that isn't stored in properties */
view3d_set_viewcontext(C, &cache->vc);
cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats");
view3d_get_transformation(&cache->vc, cache->vc.obact, cache->mats);
view3d_get_transformation(vc, vc->obact, cache->mats);
sculpt_update_mesh_elements(C);
@@ -1252,7 +1238,7 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
}
}
unproject(cache->mats, cache->true_location, cache->initial_mouse[0], cache->initial_mouse[1], cache->depth);
view3d_unproject(cache->mats, cache->true_location, cache->initial_mouse[0], cache->initial_mouse[1], cache->depth);
cache->initial_radius = unproject_brush_radius(ss, brush->size);
cache->rotation = 0;
cache->first_time = 1;
@@ -1312,7 +1298,7 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerR
/* Find the grab delta */
if(brush->sculpt_tool == SCULPT_TOOL_GRAB) {
unproject(cache->mats, grab_location, cache->mouse[0], cache->mouse[1], cache->depth);
view3d_unproject(cache->mats, grab_location, cache->mouse[0], cache->mouse[1], cache->depth);
if(!cache->first_time)
VecSubf(cache->grab_delta, grab_location, cache->old_grab_location);
VecCopyf(cache->old_grab_location, grab_location);
@@ -1325,7 +1311,6 @@ static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmE
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob= CTX_data_active_object(C);
ModifierData *md;
ViewContext vc;
float scale[3], clip_tolerance[3] = {0,0,0};
float mouse[2];
int flag = 0;
@@ -1358,13 +1343,12 @@ static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmE
RNA_float_set_array(op->ptr, "initial_mouse", mouse);
/* Initial screen depth under the mouse */
view3d_set_viewcontext(C, &vc);
RNA_float_set(op->ptr, "depth", read_cached_depth(&vc, event->x, event->y));
RNA_float_set(op->ptr, "depth", read_cached_depth(paint_stroke_view_context(op->customdata), event->x, event->y));
sculpt_update_cache_invariants(sd, ss, C, op);
}
static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event)
static void sculpt_brush_stroke_init(bContext *C)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
@@ -1376,10 +1360,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even
changes are made to the texture. */
sculpt_update_tex(sd, ss);
/* add modal handler */
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
return OPERATOR_RUNNING_MODAL;
sculpt_update_mesh_elements(C);
}
static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
@@ -1435,157 +1416,67 @@ static void sculpt_flush_update(bContext *C)
ED_region_tag_redraw(ar);
}
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
static int sculpt_smooth_stroke(Sculpt *s, SculptSession *ss, float output[2], wmEvent *event)
static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event)
{
Brush *brush = paint_brush(&s->paint);
output[0] = event->x;
output[1] = event->y;
if(brush->flag & BRUSH_SMOOTH_STROKE && brush->sculpt_tool != SCULPT_TOOL_GRAB) {
StrokeCache *cache = ss->cache;
float u = brush->smooth_stroke_factor, v = 1.0 - u;
float dx = cache->mouse[0] - event->x, dy = cache->mouse[1] - event->y;
/* If the mouse is moving within the radius of the last move,
don't update the mouse position. This allows sharp turns. */
if(dx*dx + dy*dy < brush->smooth_stroke_radius * brush->smooth_stroke_radius)
return 0;
output[0] = event->x * v + cache->mouse[0] * u;
output[1] = event->y * v + cache->mouse[1] * u;
}
return 1;
}
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
int sculpt_space_stroke_enabled(Sculpt *s)
{
Brush *br = paint_brush(&s->paint);
return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
}
/* Put the location of the next sculpt stroke dot into the stroke RNA and apply it to the mesh */
static void sculpt_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
StrokeCache *cache = ss->cache;
PointerRNA itemptr;
float cur_depth, pressure = 1;
float center[3];
cur_depth = read_cached_depth(&cache->vc, mouse[0], mouse[1]);
unproject(ss->cache->mats, center, mouse[0], mouse[1], cur_depth);
/* Tablet */
if(event->custom == EVT_DATA_TABLET) {
wmTabletData *wmtab= event->customdata;
if(wmtab->Active != EVT_TABLET_NONE)
pressure= wmtab->Pressure;
}
/* Add to stroke */
RNA_collection_add(op->ptr, "stroke", &itemptr);
RNA_float_set_array(&itemptr, "location", center);
RNA_float_set_array(&itemptr, "mouse", mouse);
RNA_boolean_set(&itemptr, "flip", event->shift);
RNA_float_set(&itemptr, "pressure", pressure);
sculpt_update_cache_variants(sd, ss, &itemptr);
sculpt_restore_mesh(sd, ss);
do_symmetrical_brush_actions(sd, ss);
}
/* For brushes with stroke spacing enabled, moves mouse in steps
towards the final mouse location. */
static int sculpt_space_stroke(bContext *C, wmOperator *op, wmEvent *event, Sculpt *s, SculptSession *ss, const float final_mouse[2])
{
StrokeCache *cache = ss->cache;
Brush *brush = paint_brush(&s->paint);
int cnt = 0;
if(sculpt_space_stroke_enabled(s)) {
float vec[2] = {final_mouse[0] - cache->mouse[0], final_mouse[1] - cache->mouse[1]};
float mouse[2] = {cache->mouse[0], cache->mouse[1]};
float length, scale;
int steps = 0, i;
/* Normalize the vector between the last stroke dot and the goal */
length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
if(length > FLT_EPSILON) {
scale = brush->spacing / length;
vec[0] *= scale;
vec[1] *= scale;
steps = (int)(length / brush->spacing);
for(i = 0; i < steps; ++i, ++cnt) {
mouse[0] += vec[0];
mouse[1] += vec[1];
sculpt_brush_stroke_add_step(C, op, event, mouse);
}
}
}
return cnt;
}
static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
ARegion *ar = CTX_wm_region(C);
ViewContext vc;
float cur_depth;
sculpt_update_mesh_elements(C);
view3d_set_viewcontext(C, &vc);
cur_depth = read_cached_depth(&vc, event->x, event->y);
/* Don't start the stroke until a valid depth is found */
if(cur_depth < 1.0 - FLT_EPSILON) {
SculptSession *ss = CTX_data_active_object(C)->sculpt;
if(!ss->cache) {
ViewContext vc;
view3d_set_viewcontext(C, &vc);
cur_depth = read_cached_depth(&vc, event->x, event->y);
sculpt_brush_stroke_init_properties(C, op, event, ss);
sculptmode_update_all_projverts(ss);
/* Don't start the stroke until a valid depth is found */
if(cur_depth < 1.0 - FLT_EPSILON) {
sculpt_brush_stroke_init_properties(C, op, event, ss);
sculptmode_update_all_projverts(ss);
}
ED_region_tag_redraw(ar);
return 1;
}
else
return 0;
}
if(ss->cache) {
float mouse[2];
static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
if(sculpt_smooth_stroke(sd, ss, mouse, event)) {
if(sculpt_space_stroke_enabled(sd)) {
if(!sculpt_space_stroke(C, op, event, sd, ss, mouse))
ED_region_tag_redraw(ar);
}
else
sculpt_brush_stroke_add_step(C, op, event, mouse);
sculpt_update_cache_variants(sd, ss, itemptr);
sculpt_restore_mesh(sd, ss);
do_symmetrical_brush_actions(sd, ss);
sculpt_flush_update(C);
sculpt_post_stroke_free(ss);
}
else
ED_region_tag_redraw(ar);
}
/* Cleanup */
sculpt_flush_update(C); // XXX: during exec, shouldn't do this every time
sculpt_post_stroke_free(ss);
}
static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke)
{
SculptSession *ss = CTX_data_active_object(C)->sculpt;
/* Finished */
if(event->type == LEFTMOUSE && event->val == 0) {
if(ss->cache) {
request_depth_update(ss->cache->vc.rv3d);
sculpt_cache_free(ss->cache);
ss->cache = NULL;
sculpt_undo_push(C, sd);
}
if(ss->cache) {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
return OPERATOR_FINISHED;
request_depth_update(paint_stroke_view_context(stroke)->rv3d);
sculpt_cache_free(ss->cache);
ss->cache = NULL;
sculpt_undo_push(C, sd);
}
}
static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
sculpt_brush_stroke_init(C);
op->customdata = paint_stroke_new(C, sculpt_stroke_test_start,
sculpt_stroke_update_step,
sculpt_stroke_done);
/* add modal handler */
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -1594,10 +1485,10 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = CTX_data_active_object(C)->sculpt;
view3d_operator_needs_opengl(C);
sculpt_brush_stroke_init(C);
sculpt_update_cache_invariants(sd, ss, C, op);
sculptmode_update_all_projverts(ss);
sculpt_update_tex(sd, ss);
RNA_BEGIN(op->ptr, itemptr, "stroke") {
sculpt_update_cache_variants(sd, ss, &itemptr);
@@ -1627,7 +1518,7 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
/* api callbacks */
ot->invoke= sculpt_brush_stroke_invoke;
ot->modal= sculpt_brush_stroke_modal;
ot->modal= paint_stroke_modal;
ot->exec= sculpt_brush_stroke_exec;
ot->poll= sculpt_poll;

View File

@@ -63,6 +63,7 @@
#include "RE_pipeline.h" // make_stars
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -543,6 +544,18 @@ void view3d_get_object_project_mat(RegionView3D *rv3d, Object *ob, float pmat[4]
Mat4MulMat4(pmat, vmat, rv3d->winmat);
}
/* Uses window coordinates (x,y) and depth component z to find a point in
modelspace */
void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
{
double ux, uy, uz;
gluUnProject(x,y,z, mats->modelview, mats->projection,
(GLint *)mats->viewport, &ux, &uy, &uz );
out[0] = ux;
out[1] = uy;
out[2] = uz;
}
/* use above call to get projecting mat */
void view3d_project_float(ARegion *ar, float *vec, float *adr, float mat[4][4])