2.5/Paint:

* Converted vertex paint to use the new stroke system. Now supports the same smooth stroke and stroke spacing as sculpt mode.
* Refactored the paint cursor a bit, just sculpt for now but other modes soon.
* A couple warning fixes
This commit is contained in:
Nicholas Bishop
2009-08-20 05:13:07 +00:00
parent 2878eed1c1
commit 7dfc1317ac
15 changed files with 207 additions and 143 deletions

View File

@@ -33,7 +33,7 @@ struct Object;
struct Paint;
struct Scene;
void paint_init(struct Paint *p, const char *brush_name);
void paint_init(struct Paint *p, const char *col);
void free_paint(struct Paint *p);
void copy_paint(struct Paint *orig, struct Paint *new);

View File

@@ -154,14 +154,23 @@ int paint_facesel_test(Object *ob)
}
void paint_init(Paint *p, const char *name)
void paint_init(Paint *p, const char *col)
{
Brush *brush;
/* If there's no brush, create one */
brush = paint_brush(p);
brush_check_exists(&brush, name);
brush_check_exists(&brush, "Brush");
paint_brush_set(p, brush);
if(col)
memcpy(p->paint_cursor_col, col, 3);
else {
p->paint_cursor_col[0] = 255;
p->paint_cursor_col[1] = 255;
p->paint_cursor_col[2] = 255;
}
p->paint_cursor_col[3] = 128;
}
void free_paint(Paint *paint)

View File

@@ -7073,6 +7073,25 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *ptr, in
return item;
}
static const char *object_mode_op_string(int mode)
{
if(mode == OB_MODE_EDIT)
return "OBJECT_OT_editmode_toggle";
if(mode == OB_MODE_SCULPT)
return "SCULPT_OT_sculptmode_toggle";
if(mode == OB_MODE_VERTEX_PAINT)
return "PAINT_OT_vertex_paint_toggle";
if(mode == OB_MODE_WEIGHT_PAINT)
return "PAINT_OT_weight_paint_toggle";
if(mode == OB_MODE_TEXTURE_PAINT)
return "PAINT_OT_texture_paint_toggle";
if(mode == OB_MODE_PARTICLE_EDIT)
return "PARTICLE_OT_particle_edit_toggle";
if(mode == OB_MODE_POSE)
return "OBJECT_OT_posemode_toggle";
return NULL;
}
static int object_mode_set_exec(bContext *C, wmOperator *op)
{
Object *ob= CTX_data_active_object(C);
@@ -7081,20 +7100,13 @@ static int object_mode_set_exec(bContext *C, wmOperator *op)
if(!ob)
return OPERATOR_CANCELLED;
if((mode == OB_MODE_EDIT) == !(ob->mode & OB_MODE_EDIT))
WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_SCULPT) == !(ob->mode & OB_MODE_SCULPT))
WM_operator_name_call(C, "SCULPT_OT_sculptmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_VERTEX_PAINT) == !(ob->mode & OB_MODE_VERTEX_PAINT))
WM_operator_name_call(C, "PAINT_OT_vertex_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_WEIGHT_PAINT) == !(ob->mode & OB_MODE_WEIGHT_PAINT))
WM_operator_name_call(C, "PAINT_OT_weight_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_TEXTURE_PAINT) == !(ob->mode & OB_MODE_TEXTURE_PAINT))
WM_operator_name_call(C, "PAINT_OT_texture_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_PARTICLE_EDIT) == !(ob->mode & OB_MODE_PARTICLE_EDIT))
WM_operator_name_call(C, "PARTICLE_OT_particle_edit_toggle", WM_OP_EXEC_REGION_WIN, NULL);
if((mode == OB_MODE_POSE) == !(ob->mode & OB_MODE_POSE))
WM_operator_name_call(C, "OBJECT_OT_posemode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
/* Exit off current mode */
if(ob->mode != OB_MODE_OBJECT)
WM_operator_name_call(C, object_mode_op_string(ob->mode), WM_OP_EXEC_REGION_WIN, NULL);
/* Enter new mode */
if(mode != OB_MODE_OBJECT)
WM_operator_name_call(C, object_mode_op_string(mode), WM_OP_EXEC_REGION_WIN, NULL);
return OPERATOR_FINISHED;
}

View File

@@ -5174,7 +5174,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT,
NULL, me->totface);
paint_init(&scene->toolsettings->imapaint.paint, "Brush");
paint_init(&scene->toolsettings->imapaint.paint, NULL);
if(U.glreslimit != 0)
GPU_free_images();

View File

@@ -50,6 +50,10 @@ 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);
void *paint_stroke_mode_data(struct PaintStroke *stroke);
void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
int paint_poll(bContext *C);
void paint_cursor_start(struct bContext *C, int (*poll)(struct bContext *C));
/* paint_vertex.c */
void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot);

View File

@@ -77,12 +77,6 @@ void BRUSH_OT_add(wmOperatorType *ot)
RNA_def_enum(ot->srna, "type", brush_type_items, OB_MODE_VERTEX_PAINT, "Type", "Which paint mode to create the brush for.");
}
/* Paint operators */
static int paint_poll(bContext *C)
{
return !!paint_get_active(CTX_data_scene(C));
}
/**************************** registration **********************************/
void ED_operatortypes_paint(void)

View File

@@ -43,6 +43,7 @@
#include "BLI_arithb.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "ED_screen.h"
@@ -54,6 +55,9 @@
#include <math.h>
typedef struct PaintStroke {
void *mode_data;
void *smooth_stroke_cursor;
/* Cached values */
ViewContext vc;
bglMats mats;
@@ -71,6 +75,42 @@ typedef struct PaintStroke {
StrokeDone done;
} PaintStroke;
/*** Cursor ***/
static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata)
{
Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C)));
PaintStroke *stroke = customdata;
glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
if(stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) {
ARegion *ar = CTX_wm_region(C);
sdrawline(x, y, (int)stroke->last_mouse_position[0] - ar->winrct.xmin,
(int)stroke->last_mouse_position[1] - ar->winrct.ymin);
}
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
static void paint_draw_cursor(bContext *C, int x, int y, void *customdata)
{
Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C)));
glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glTranslatef((float)x, (float)y, 0.0f);
glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
glTranslatef((float)-x, (float)-y, 0.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
/* 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])
{
@@ -191,6 +231,11 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
stroke->last_mouse_position[0] = event->x;
stroke->last_mouse_position[1] = event->y;
stroke->stroke_started = stroke->test_start(C, op, event);
if(stroke->stroke_started)
stroke->smooth_stroke_cursor =
WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke);
ED_region_tag_redraw(ar);
}
@@ -209,6 +254,9 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
/* TODO: fix hardcoded event here */
if(event->type == LEFTMOUSE && event->val == 0) {
if(stroke->smooth_stroke_cursor)
WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor);
stroke->done(C, stroke);
MEM_freeN(stroke);
return OPERATOR_FINISHED;
@@ -222,3 +270,31 @@ ViewContext *paint_stroke_view_context(PaintStroke *stroke)
return &stroke->vc;
}
void *paint_stroke_mode_data(struct PaintStroke *stroke)
{
return stroke->mode_data;
}
void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
{
stroke->mode_data = mode_data;
}
int paint_poll(bContext *C)
{
Paint *p = paint_get_active(CTX_data_scene(C));
Object *ob = CTX_data_active_object(C);
return p && ob && paint_brush(p) &&
CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
}
void paint_cursor_start(bContext *C, int (*poll)(bContext *C))
{
Paint *p = paint_get_active(CTX_data_scene(C));
if(p && !p->paint_cursor)
p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL);
}

View File

@@ -62,6 +62,7 @@
#include "DNA_userdef_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "BKE_armature.h"
#include "BKE_brush.h"
@@ -91,6 +92,8 @@
#include "ED_util.h"
#include "ED_view3d.h"
#include "paint_intern.h"
/* vp->mode */
#define VP_MIX 0
#define VP_ADD 1
@@ -807,7 +810,7 @@ static int sample_backbuf_area(ViewContext *vc, int *indexar, int totface, int x
return tot;
}
static int calc_vp_alpha_dl(VPaint *vp, ViewContext *vc, float vpimat[][3], float *vert_nor, short *mval)
static int calc_vp_alpha_dl(VPaint *vp, ViewContext *vc, float vpimat[][3], float *vert_nor, float *mval)
{
Brush *brush = paint_brush(&vp->paint);
float fac, dx, dy;
@@ -1124,7 +1127,7 @@ static int set_wpaint(bContext *C, wmOperator *op) /* toggle */
if(wp==NULL)
wp= scene->toolsettings->wpaint= new_vpaint(1);
paint_init(&wp->paint, "Brush");
paint_init(&wp->paint, NULL);
toggle_paint_cursor(C, 1);
@@ -1329,7 +1332,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
float paintweight= ts->vgroup_weight;
int *indexar= wpd->indexar;
int totindex, index, alpha, totw;
short mval[2];
float mval[2];
view3d_operator_needs_opengl(C);
@@ -1634,7 +1637,7 @@ static int set_vpaint(bContext *C, wmOperator *op) /* toggle */
toggle_paint_cursor(C, 0);
paint_init(&vp->paint, "Brush");
paint_init(&vp->paint, NULL);
}
if (me)
@@ -1693,25 +1696,47 @@ struct VPaintData {
float vpimat[3][3];
};
static void vpaint_exit(bContext *C, wmOperator *op)
static int vpaint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event)
{
ToolSettings *ts= CTX_data_tool_settings(C);
struct VPaintData *vpd= op->customdata;
struct PaintStroke *stroke = op->customdata;
VPaint *vp= ts->vpaint;
struct VPaintData *vpd;
Object *ob= CTX_data_active_object(C);
Mesh *me;
float mat[4][4], imat[4][4];
/* context checks could be a poll() */
me= get_mesh(ob);
if(me==NULL || me->totface==0) return OPERATOR_PASS_THROUGH;
if(vpd->vertexcosnos)
MEM_freeN(vpd->vertexcosnos);
MEM_freeN(vpd->indexar);
if(me->mcol==NULL) make_vertexcol(CTX_data_scene(C), 0);
if(me->mcol==NULL) return OPERATOR_CANCELLED;
/* frees prev buffer */
copy_vpaint_prev(ts->vpaint, NULL, 0);
/* make mode data storage */
vpd= MEM_callocN(sizeof(struct VPaintData), "VPaintData");
paint_stroke_set_mode_data(stroke, vpd);
view3d_set_viewcontext(C, &vpd->vc);
MEM_freeN(vpd);
op->customdata= NULL;
vpd->vertexcosnos= mesh_get_mapped_verts_nors(vpd->vc.scene, ob);
vpd->indexar= get_indexarray();
vpd->paintcol= vpaint_get_current_col(vp);
/* for filtering */
copy_vpaint_prev(vp, (unsigned int *)me->mcol, me->totface);
/* some old cruft to sort out later */
Mat4MulMat4(mat, ob->obmat, vpd->vc.rv3d->viewmat);
Mat4Invert(imat, mat);
Mat3CpyMat4(vpd->vpimat, imat);
return 1;
}
static void vpaint_dot(bContext *C, struct VPaintData *vpd, wmEvent *event)
static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
ToolSettings *ts= CTX_data_tool_settings(C);
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
VPaint *vp= ts->vpaint;
Brush *brush = paint_brush(&vp->paint);
ViewContext *vc= &vpd->vc;
@@ -1720,7 +1745,9 @@ static void vpaint_dot(bContext *C, struct VPaintData *vpd, wmEvent *event)
float mat[4][4];
int *indexar= vpd->indexar;
int totindex, index;
short mval[2];
float mval[2];
RNA_float_get_array(itemptr, "mouse", mval);
view3d_operator_needs_opengl(C);
@@ -1728,10 +1755,11 @@ static void vpaint_dot(bContext *C, struct VPaintData *vpd, wmEvent *event)
wmMultMatrix(ob->obmat);
wmGetSingleMatrix(mat);
wmLoadMatrix(vc->rv3d->viewmat);
mval[0]-= vc->ar->winrct.xmin;
mval[1]-= 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);
@@ -1811,58 +1839,27 @@ static void vpaint_dot(bContext *C, struct VPaintData *vpd, wmEvent *event)
DAG_object_flush_update(vc->scene, ob, OB_RECALC_DATA);
}
static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
static void vpaint_stroke_done(bContext *C, struct PaintStroke *stroke)
{
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;
}
ToolSettings *ts= CTX_data_tool_settings(C);
struct VPaintData *vpd= paint_stroke_mode_data(stroke);
return OPERATOR_RUNNING_MODAL;
if(vpd->vertexcosnos)
MEM_freeN(vpd->vertexcosnos);
MEM_freeN(vpd->indexar);
/* frees prev buffer */
copy_vpaint_prev(ts->vpaint, NULL, 0);
MEM_freeN(vpd);
}
static int vpaint_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
ToolSettings *ts= CTX_data_tool_settings(C);
VPaint *vp= ts->vpaint;
struct VPaintData *vpd;
Object *ob= CTX_data_active_object(C);
Mesh *me;
float mat[4][4], imat[4][4];
/* context checks could be a poll() */
me= get_mesh(ob);
if(me==NULL || me->totface==0) return OPERATOR_PASS_THROUGH;
if(me->mcol==NULL) make_vertexcol(CTX_data_scene(C), 0);
if(me->mcol==NULL) return OPERATOR_CANCELLED;
/* make customdata storage */
op->customdata= vpd= MEM_callocN(sizeof(struct VPaintData), "VPaintData");
view3d_set_viewcontext(C, &vpd->vc);
vpd->vertexcosnos= mesh_get_mapped_verts_nors(vpd->vc.scene, ob);
vpd->indexar= get_indexarray();
vpd->paintcol= vpaint_get_current_col(vp);
/* for filtering */
copy_vpaint_prev(vp, (unsigned int *)me->mcol, me->totface);
/* some old cruft to sort out later */
Mat4MulMat4(mat, ob->obmat, vpd->vc.rv3d->viewmat);
Mat4Invert(imat, mat);
Mat3CpyMat4(vpd->vpimat, imat);
/* do paint once for click only paint */
vpaint_modal(C, op, event);
op->customdata = paint_stroke_new(C, vpaint_stroke_test_start,
vpaint_stroke_update_step,
vpaint_stroke_done);
/* add modal handler */
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
@@ -1878,11 +1875,13 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
/* api callbacks */
ot->invoke= vpaint_invoke;
ot->modal= vpaint_modal;
ot->modal= paint_stroke_modal;
/* ot->exec= vpaint_exec; <-- needs stroke property */
ot->poll= vp_poll;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
}

View File

@@ -1042,47 +1042,7 @@ static int sculpt_mode_poll(bContext *C)
static int sculpt_poll(bContext *C)
{
return sculpt_mode_poll(C) && paint_brush(&CTX_data_tool_settings(C)->sculpt->paint) &&
CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
}
/*** Sculpt Cursor ***/
static void draw_paint_cursor(bContext *C, int x, int y, void *customdata)
{
Sculpt *sd= CTX_data_tool_settings(C)->sculpt;
SculptSession *ss= CTX_data_active_object(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
glColor4ub(255, 100, 100, 128);
glEnable( GL_LINE_SMOOTH );
glEnable(GL_BLEND);
glTranslatef((float)x, (float)y, 0.0f);
glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
glTranslatef((float)-x, (float)-y, 0.0f);
if(ss && ss->cache && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) {
ARegion *ar = CTX_wm_region(C);
sdrawline(x, y, (int)ss->cache->mouse[0] - ar->winrct.xmin, (int)ss->cache->mouse[1] - ar->winrct.ymin);
}
glDisable(GL_BLEND);
glDisable( GL_LINE_SMOOTH );
}
static void toggle_paint_cursor(bContext *C)
{
Sculpt *s = CTX_data_scene(C)->toolsettings->sculpt;
if(s->cursor) {
WM_paint_cursor_end(CTX_wm_manager(C), s->cursor);
s->cursor = NULL;
}
else {
s->cursor =
WM_paint_cursor_activate(CTX_wm_manager(C), sculpt_poll, draw_paint_cursor, NULL);
}
return sculpt_mode_poll(C) && paint_poll(C);
}
static void sculpt_undo_push(bContext *C, Sculpt *sd)
@@ -1112,8 +1072,11 @@ static void sculpt_undo_push(bContext *C, Sculpt *sd)
/**** Radial control ****/
static int sculpt_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
Brush *brush = paint_brush(&CTX_data_tool_settings(C)->sculpt->paint);
toggle_paint_cursor(C);
Paint *p = paint_get_active(CTX_data_scene(C));
Brush *brush = paint_brush(p);
WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor);
p->paint_cursor = NULL;
brush_radial_control_invoke(op, brush, 1);
return WM_radial_control_invoke(C, op, event);
}
@@ -1122,7 +1085,7 @@ static int sculpt_radial_control_modal(bContext *C, wmOperator *op, wmEvent *eve
{
int ret = WM_radial_control_modal(C, op, event);
if(ret != OPERATOR_RUNNING_MODAL)
toggle_paint_cursor(C);
paint_cursor_start(C, sculpt_poll);
return ret;
}
@@ -1592,6 +1555,8 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op)
free_sculptsession(&ob->sculpt);
}
else {
const char col[3] = {255, 100, 100};
/* Enter sculptmode */
ob->mode |= OB_MODE_SCULPT;
@@ -1605,10 +1570,9 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op)
free_sculptsession(&ob->sculpt);
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
if(!ts->sculpt->cursor)
toggle_paint_cursor(C);
paint_init(&ts->sculpt->paint, "Brush");
paint_init(&ts->sculpt->paint, col);
paint_cursor_start(C, sculpt_poll);
WM_event_add_notifier(C, NC_SCENE|ND_MODE, CTX_data_scene(C));
}

View File

@@ -43,6 +43,7 @@ struct Object;
struct Scene;
struct View3D;
struct RegionView3D;
struct SmokeModifierData;
/* OpenGL drawing functions related to shading. These are also
* shared with the game engine, where there were previously

View File

@@ -455,8 +455,11 @@ typedef struct Paint {
Brush **brushes;
int active_brush_index, brush_count;
/* WM handle */
/* WM Paint cursor */
void *paint_cursor;
unsigned char paint_cursor_col[4];
int pad;
} Paint;
typedef struct ImagePaintSettings {
@@ -498,9 +501,6 @@ typedef struct TransformOrientation {
typedef struct Sculpt {
Paint paint;
/* WM handle */
void *cursor;
/* For rotating around a pivot point */
float pivot[3];

View File

@@ -151,7 +151,7 @@ static void rna_SpaceImageEditor_paint_update(bContext *C, PointerRNA *ptr)
Scene *scene= CTX_data_scene(C);
if(scene)
paint_init(&scene->toolsettings->imapaint.paint, "Brush");
paint_init(&scene->toolsettings->imapaint.paint, NULL);
}
static int rna_SpaceImageEditor_show_render_get(PointerRNA *ptr)

View File

@@ -287,6 +287,11 @@ typedef struct wmTimer {
} wmTimer;
/* **************** Paint Cursor ******************* */
typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata);
/* ****************** Messages ********************* */
enum {

View File

@@ -1067,7 +1067,7 @@ static void WM_OT_exit_blender(wmOperatorType *ot)
*/
void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
void (*draw)(bContext *C, int, int, void *customdata), void *customdata)
wmPaintCursorDraw draw, void *customdata)
{
wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");