diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_utils.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_utils.c | 296 |
1 files changed, 286 insertions, 10 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 25308f6595e..eaa4885572e 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -35,23 +35,28 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_material_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "BLI_math.h" +#include "BLI_math_color.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLF_translation.h" +#include "BKE_scene.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" +#include "BKE_material.h" #include "BKE_image.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_image.h" #include "RNA_access.h" #include "RNA_define.h" @@ -67,6 +72,10 @@ #include "ED_view3d.h" #include "ED_screen.h" +#include "ED_uvedit.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" #include "BLI_sys_types.h" #include "ED_mesh.h" /* for face mask functions */ @@ -205,6 +214,175 @@ void paint_get_tex_pixel_col(MTex *mtex, float u, float v, float rgba[4], struct CLAMP(rgba[3], 0.0f, 1.0f); } +/* 3D Paint */ + +static void imapaint_project(float matrix[4][4], const float co[3], float pco[4]) +{ + copy_v3_v3(pco, co); + pco[3] = 1.0f; + + mul_m4_v4(matrix, pco); +} + +static void imapaint_tri_weights(float matrix[4][4], GLint view[4], + const float v1[3], const float v2[3], const float v3[3], + const float co[2], float w[3]) +{ + float pv1[4], pv2[4], pv3[4], h[3], divw; + float wmat[3][3], invwmat[3][3]; + + /* compute barycentric coordinates */ + + /* project the verts */ + imapaint_project(matrix, v1, pv1); + imapaint_project(matrix, v2, pv2); + imapaint_project(matrix, v3, pv3); + + /* do inverse view mapping, see gluProject man page */ + h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f; + h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f; + 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); + + copy_v3_v3(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 */ +static void imapaint_pick_uv(Scene *scene, Object *ob, unsigned int faceindex, const int xy[2], float uv[2]) +{ + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + MTFace *tf_base, *tf; + Material *ma; + TexPaintSlot *slot; + int numfaces = dm->getNumTessFaces(dm), a, findex; + float p[2], w[3], absw, minabsw; + MFace mf; + MVert mv[4]; + float matrix[4][4], proj[4][4]; + GLint view[4]; + + /* compute barycentric coordinates */ + + /* double lookup */ + const int *index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + if (index_mf_to_mpoly == NULL) { + index_mp_to_orig = NULL; + } + + /* get the needed opengl matrices */ + glGetIntegerv(GL_VIEWPORT, view); + glGetFloatv(GL_MODELVIEW_MATRIX, (float *)matrix); + glGetFloatv(GL_PROJECTION_MATRIX, (float *)proj); + view[0] = view[1] = 0; + mul_m4_m4m4(matrix, matrix, ob->obmat); + mul_m4_m4m4(matrix, proj, matrix); + + 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_mf_to_mpoly ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a; + + if (findex == faceindex) { + dm->getTessFace(dm, a, &mf); + + ma = dm->mat[mf.mat_nr]; + slot = &ma->texpaintslot[ma->paint_active_slot]; + + 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]); + + if (!slot->uvname[0] || !(tf_base = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, slot->uvname))) + tf_base = CustomData_get_layer(&dm->faceData, CD_MTFACE); + + tf = &tf_base[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(matrix, view, mv[0].co, mv[1].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(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(matrix, view, mv[1].co, mv[2].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(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(matrix, view, mv[0].co, mv[1].co, mv[2].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(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 */ +static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int *r_index, unsigned int totface) +{ + if (totface == 0) + return 0; + + /* sample only on the exact position */ + *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); + + if ((*r_index) == 0 || (*r_index) > (unsigned int)totface) { + return 0; + } + + (*r_index)--; + + return 1; +} + + +static Image *imapaint_face_image(DerivedMesh *dm, int face_index) +{ + Image *ima; + MFace *mf = dm->getTessFaceArray(dm) + face_index; + Material *ma = dm->mat[mf->mat_nr]; + ima = ma->texpaintslot[ma->paint_active_slot].ima; + + return ima; +} + /* Uses symm to selectively flip any axis of a coordinate. */ void flip_v3_v3(float out[3], const float in[3], const char symm) { @@ -223,25 +401,123 @@ void flip_v3_v3(float out[3], const float in[3], const char symm) } /* used for both 3d view and image window */ -void paint_sample_color(const bContext *C, ARegion *ar, int x, int y) /* frontbuf */ +void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) { + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = BKE_paint_palette(paint); + PaletteColor *color; Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); unsigned int col; - const char *cp; + const unsigned 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); + if (use_palette) { + if (!palette) { + palette = BKE_palette_add(CTX_data_main(C), "Palette"); + BKE_paint_palette_set(paint, palette); + } + + color = BKE_palette_color_add(palette); + } + + + if (CTX_wm_view3d(C) && texpaint_proj) { + /* first try getting a colour directly from the mesh faces if possible */ + Object *ob = OBACT; + bool sample_success = false; + + if (ob) { + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + + ViewContext vc; + const int mval[2] = {x, y}; + unsigned int faceindex; + unsigned int totface = dm->getNumTessFaces(dm); + MTFace *dm_mtface = dm->getTessFaceDataArray(dm, CD_MTFACE); + + DM_update_materials(dm, ob); + + if (dm_mtface) { + view3d_set_viewcontext(C, &vc); + + view3d_operator_needs_opengl(C); + + if (imapaint_pick_face(&vc, mval, &faceindex, totface)) { + Image *image = imapaint_face_image(dm, faceindex); + + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (ibuf && ibuf->rect) { + float uv[2]; + float u, v; + imapaint_pick_uv(scene, ob, faceindex, mval, uv); + sample_success = true; + + u = fmodf(uv[0], 1.0f); + v = fmodf(uv[1], 1.0f); + + if (u < 0.0f) u += 1.0f; + if (v < 0.0f) v += 1.0f; + + u = u * ibuf->x - 0.5f; + v = v * ibuf->y - 0.5f; + + if (ibuf->rect_float) { + float rgba_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + straight_to_premul_v4(rgba_f); + if (use_palette) { + linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); + } + else { + linearrgb_to_srgb_v3_v3(rgba_f, rgba_f); + BKE_brush_color_set(scene, br, rgba_f); + } + } + else { + unsigned char rgba[4]; + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (use_palette) { + rgb_uchar_to_float(color->rgb, rgba); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, rgba); + BKE_brush_color_set(scene, br, rgba_f); + } + } + } + + BKE_image_release_ibuf(image, ibuf, NULL); + } + } + dm->release(dm); + } - cp = (char *)&col; + if (!sample_success) { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + else + return; + } + else { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + cp = (unsigned 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; + if (use_palette) { + rgb_uchar_to_float(color->rgb, cp); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, cp); + BKE_brush_color_set(scene, br, rgba_f); } } |