This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/sculpt_paint/paint_utils.c

419 lines
11 KiB
C
Raw Normal View History

#include <math.h>
#include <stdlib.h>
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_brush_types.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_DerivedMesh.h"
#include "BKE_paint.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "BIF_gl.h"
/* TODO: remove once projectf goes away */
#include "BIF_glutil.h"
#include "RE_shader_ext.h"
#include "ED_view3d.h"
#include "ED_screen.h"
#include "BLO_sys_types.h"
#include "ED_mesh.h" /* for face mask functions */
#include "WM_api.h"
#include "WM_types.h"
#include "paint_intern.h"
/* convert a point in model coordinates to 2D screen coordinates */
/* TODO: can be deleted once all calls are replaced with
view3d_project_float() */
void projectf(bglMats *mats, const float v[3], float p[2])
{
double ux, uy, uz;
gluProject(v[0],v[1],v[2], mats->modelview, mats->projection,
(GLint *)mats->viewport, &ux, &uy, &uz);
p[0]= ux;
p[1]= uy;
}
float paint_calc_object_space_radius(ViewContext *vc, float center[3],
float pixel_radius)
{
Object *ob = vc->obact;
float delta[3], scale, loc[3];
mul_v3_m4v3(loc, ob->obmat, center);
initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
window_to_3d_delta(vc->ar, delta, pixel_radius, 0);
scale= fabsf(mat4_to_scale(ob->obmat));
scale= (scale == 0.0f)? 1.0f: scale;
return len_v3(delta)/scale;
}
float paint_get_tex_pixel(Brush* br, float u, float v)
{
TexResult texres;
float co[3];
int hasrgb;
co[0] = u;
co[1] = v;
co[2] = 0;
memset(&texres, 0, sizeof(TexResult));
hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 0, &texres);
if (hasrgb & TEX_RGB)
texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta;
return texres.tin;
}
/* 3D Paint */
static void imapaint_project(Object *ob, float *model, float *proj, float *co, float *pco)
{
VECCOPY(pco, co);
pco[3]= 1.0f;
mul_m4_v3(ob->obmat, pco);
mul_m4_v3((float(*)[4])model, pco);
mul_m4_v4((float(*)[4])proj, pco);
}
static void imapaint_tri_weights(Object *ob, float *v1, float *v2, float *v3, float *co, float *w)
{
float pv1[4], pv2[4], pv3[4], h[3], divw;
float model[16], proj[16], wmat[3][3], invwmat[3][3];
GLint view[4];
/* compute barycentric coordinates */
/* get the needed opengl matrices */
glGetIntegerv(GL_VIEWPORT, view);
glGetFloatv(GL_MODELVIEW_MATRIX, model);
glGetFloatv(GL_PROJECTION_MATRIX, proj);
view[0] = view[1] = 0;
/* project the verts */
imapaint_project(ob, model, proj, v1, pv1);
imapaint_project(ob, model, proj, v2, pv2);
imapaint_project(ob, model, proj, v3, pv3);
/* do inverse view mapping, see gluProject man page */
h[0]= (co[0] - view[0])*2.0f/view[2] - 1;
h[1]= (co[1] - view[1])*2.0f/view[3] - 1;
h[2]= 1.0f;
/* solve for(w1,w2,w3)/perspdiv in:
h*perspdiv = Project*Model*(w1*v1 + w2*v2 + w3*v3) */
wmat[0][0]= pv1[0]; wmat[1][0]= pv2[0]; wmat[2][0]= pv3[0];
wmat[0][1]= pv1[1]; wmat[1][1]= pv2[1]; wmat[2][1]= pv3[1];
wmat[0][2]= pv1[3]; wmat[1][2]= pv2[3]; wmat[2][2]= pv3[3];
invert_m3_m3(invwmat, wmat);
mul_m3_v3(invwmat, h);
VECCOPY(w, h);
/* w is still divided by perspdiv, make it sum to one */
divw= w[0] + w[1] + w[2];
if(divw != 0.0f)
mul_v3_fl(w, 1.0f/divw);
}
/* compute uv coordinates of mouse in face */
void imapaint_pick_uv(Scene *scene, Object *ob, unsigned int faceindex, int *xy, float *uv)
{
DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
int *index = dm->getFaceDataArray(dm, CD_ORIGINDEX);
MTFace *tface = dm->getFaceDataArray(dm, CD_MTFACE), *tf;
int numfaces = dm->getNumFaces(dm), a, findex;
float p[2], w[3], absw, minabsw;
MFace mf;
MVert mv[4];
minabsw = 1e10;
uv[0] = uv[1] = 0.0;
/* test all faces in the derivedmesh with the original index of the picked face */
for(a = 0; a < numfaces; a++) {
findex= (index)? index[a]: a;
if(findex == faceindex) {
dm->getFace(dm, a, &mf);
dm->getVert(dm, mf.v1, &mv[0]);
dm->getVert(dm, mf.v2, &mv[1]);
dm->getVert(dm, mf.v3, &mv[2]);
if(mf.v4)
dm->getVert(dm, mf.v4, &mv[3]);
tf= &tface[a];
p[0]= xy[0];
p[1]= xy[1];
if(mf.v4) {
/* the triangle with the largest absolute values is the one
with the most negative weights */
imapaint_tri_weights(ob, mv[0].co, mv[1].co, mv[3].co, p, w);
absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
if(absw < minabsw) {
uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[3][0]*w[2];
uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[3][1]*w[2];
minabsw = absw;
}
imapaint_tri_weights(ob, mv[1].co, mv[2].co, mv[3].co, p, w);
absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
if(absw < minabsw) {
uv[0]= tf->uv[1][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
uv[1]= tf->uv[1][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
minabsw = absw;
}
}
else {
imapaint_tri_weights(ob, mv[0].co, mv[1].co, mv[2].co, p, w);
absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
if(absw < minabsw) {
uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
minabsw = absw;
}
}
}
}
dm->release(dm);
}
///* returns 0 if not found, otherwise 1 */
int imapaint_pick_face(ViewContext *vc, Mesh *me, int *mval, unsigned int *index)
{
if(!me || me->totface==0)
return 0;
/* sample only on the exact position */
*index = view3d_sample_backbuf(vc, mval[0], mval[1]);
if((*index)<=0 || (*index)>(unsigned int)me->totface)
return 0;
(*index)--;
return 1;
}
/* used for both 3d view and image window */
void paint_sample_color(Scene *scene, ARegion *ar, int x, int y) /* frontbuf */
{
Brush *br = paint_brush(paint_get_active(scene));
unsigned int col;
char *cp;
CLAMP(x, 0, ar->winx);
CLAMP(y, 0, ar->winy);
glReadBuffer(GL_FRONT);
glReadPixels(x+ar->winrct.xmin, y+ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
glReadBuffer(GL_BACK);
cp = (char *)&col;
if(br) {
br->rgb[0]= cp[0]/255.0f;
br->rgb[1]= cp[1]/255.0f;
br->rgb[2]= cp[2]/255.0f;
}
}
static int brush_curve_preset_exec(bContext *C, wmOperator *op)
{
Brush *br = paint_brush(paint_get_active(CTX_data_scene(C)));
brush_curve_preset(br, RNA_enum_get(op->ptr, "shape"));
return OPERATOR_FINISHED;
}
static int brush_curve_preset_poll(bContext *C)
{
Brush *br = paint_brush(paint_get_active(CTX_data_scene(C)));
return br && br->curve;
}
void BRUSH_OT_curve_preset(wmOperatorType *ot)
{
static EnumPropertyItem prop_shape_items[] = {
{CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""},
{CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""},
{CURVE_PRESET_MAX, "MAX", 0, "Max", ""},
{CURVE_PRESET_LINE, "LINE", 0, "Line", ""},
{CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""},
{CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""},
{0, NULL, 0, NULL, NULL}};
ot->name= "Preset";
ot->description= "Set brush shape";
ot->idname= "BRUSH_OT_curve_preset";
ot->exec= brush_curve_preset_exec;
ot->poll= brush_curve_preset_poll;
== Sculpt/Paint Fixes == * Fix: unify strength and size did work consistently with other paint modes * Fix: If [ and ] keys were used to resize a brush it was not possible to increase the size of the brush if it went under 10 pixels * Fix: Made interpretation of brush size consistent across all modes, Texture/Image paint interpreted brush size as the diameter while all the other modes interpret it as radius * Fix: The default spacing for vertex paint brushes was 3%, should be 10% * Fix: due to fixes to unified strength, re-enabled 'Unify Size' by default * Fix: Unified size and strength were stored in UserPrefs, moved this to ToolSettings * Fix: The setting of pressure sensitivity was not unified when strength or size were unified. Now the appropriate pressure sensitivity setting is also unified across all brushes when corresponding unification option is selected * Fix: When using [ and ] to resize the brush it didn't immediately redraw * Fix: fkey resizing/"re-strength-ing" was not working consistently accross all paint modes due to only sculpt mode having full support for unified size and strength, now it works properly. * Fix: other paint modes did expose the ability to have a custom brush colors, so I added the small bit of code to allow it. Note: I made all of the other paint mode brushes white. Note2: Actually, probably want to make the paint modes use the selected color for painting instead of a constant brush color. * I had removed OPTYPE_REGISTER from some Sculpt/Paint operators but in this commit I add them back. I'm not completely sure what this option does so I don't want to disturb it for now.
2010-07-22 18:56:46 +00:00
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", "");
}
/* face-select ops */
static int paint_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
{
paintface_select_linked(C, CTX_data_active_object(C), NULL, 2);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_linked(wmOperatorType *ot)
{
ot->name= "Select Linked";
ot->description= "Select linked faces";
ot->idname= "PAINT_OT_face_select_linked";
ot->exec= paint_select_linked_exec;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
static int paint_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
int mode= RNA_boolean_get(op->ptr, "extend") ? 1:0;
paintface_select_linked(C, CTX_data_active_object(C), event->mval, mode);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_linked_pick(wmOperatorType *ot)
{
ot->name= "Select Linked Pick";
ot->description= "Select linked faces";
ot->idname= "PAINT_OT_face_select_linked_pick";
ot->invoke= paint_select_linked_pick_invoke;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the existing selection");
}
static int face_select_all_exec(bContext *C, wmOperator *op)
{
Object *ob= CTX_data_active_object(C);
paintface_deselect_all_visible(ob, RNA_enum_get(op->ptr, "action"), TRUE);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_all(wmOperatorType *ot)
{
ot->name= "Face Selection";
ot->description= "Change selection for all faces";
ot->idname= "PAINT_OT_face_select_all";
ot->exec= face_select_all_exec;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
WM_operator_properties_select_all(ot);
}
static int face_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob= CTX_data_active_object(C);
paintface_deselect_all_visible(ob, SEL_INVERT, TRUE);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_inverse(wmOperatorType *ot)
{
ot->name= "Face Select Invert";
ot->description= "Invert selection of faces";
ot->idname= "PAINT_OT_face_select_inverse";
ot->exec= face_select_inverse_exec;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
static int face_select_hide_exec(bContext *C, wmOperator *op)
{
const int unselected= RNA_boolean_get(op->ptr, "unselected");
Object *ob= CTX_data_active_object(C);
paintface_hide(ob, unselected);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_hide(wmOperatorType *ot)
{
ot->name= "Face Select Hide";
ot->description= "Hide selected faces";
ot->idname= "PAINT_OT_face_select_hide";
ot->exec= face_select_hide_exec;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects.");
}
static int face_select_reveal_exec(bContext *C, wmOperator *op)
{
Object *ob= CTX_data_active_object(C);
paintface_reveal(ob);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_reveal(wmOperatorType *ot)
{
ot->name= "Face Select Reveal";
ot->description= "Reveal hidden faces";
ot->idname= "PAINT_OT_face_select_reveal";
ot->exec= face_select_reveal_exec;
ot->poll= facemask_paint_poll;
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects.");
}