/** * $Id$ * * ***** 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) 2001-2002 by NaN Holding BV. * All rights reserved. * * Contributor(s): Blender Foundation, full update, glsl support * * ***** END GPL LICENSE BLOCK ***** */ #include #include #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_edgehash.h" #include "BLI_editVert.h" #include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_property_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "DNA_object_types.h" #include "BKE_DerivedMesh.h" #include "BKE_effect.h" #include "BKE_image.h" #include "BKE_material.h" #include "BKE_paint.h" #include "BKE_property.h" #include "BKE_utildefines.h" #include "BIF_gl.h" #include "BIF_glutil.h" #include "UI_resources.h" #include "GPU_buffers.h" #include "GPU_extensions.h" #include "GPU_draw.h" #include "ED_mesh.h" #include "view3d_intern.h" // own include /***/ /* Flags for marked edges */ enum { eEdge_Visible = (1<<0), eEdge_Select = (1<<1), }; /* Creates a hash of edges to flags indicating * adjacent tface select/active/etc flags. */ static void get_marked_edge_info__orFlags(EdgeHash *eh, int v0, int v1, int flags) { int *flags_p; if (!BLI_edgehash_haskey(eh, v0, v1)) { BLI_edgehash_insert(eh, v0, v1, 0); } flags_p = (int*) BLI_edgehash_lookup_p(eh, v0, v1); *flags_p |= flags; } static EdgeHash *get_tface_mesh_marked_edge_info(Mesh *me) { EdgeHash *eh = BLI_edgehash_new(); int i; MFace *mf; MTFace *tf = NULL; for (i=0; itotface; i++) { mf = &me->mface[i]; if (me->mtface) tf = &me->mtface[i]; if (mf->v3) { if (!(mf->flag&ME_HIDE)) { unsigned int flags = eEdge_Visible; if (mf->flag&ME_FACE_SEL) flags |= eEdge_Select; get_marked_edge_info__orFlags(eh, mf->v1, mf->v2, flags); get_marked_edge_info__orFlags(eh, mf->v2, mf->v3, flags); if (mf->v4) { get_marked_edge_info__orFlags(eh, mf->v3, mf->v4, flags); get_marked_edge_info__orFlags(eh, mf->v4, mf->v1, flags); } else { get_marked_edge_info__orFlags(eh, mf->v3, mf->v1, flags); } } } } return eh; } static int draw_tfaces3D__setHiddenOpts(void *userData, int index) { struct { Mesh *me; EdgeHash *eh; } *data = userData; Mesh *me= data->me; MEdge *med = &me->medge[index]; uintptr_t flags = (intptr_t) BLI_edgehash_lookup(data->eh, med->v1, med->v2); if((me->drawflag & ME_DRAWSEAMS) && (med->flag&ME_SEAM)) { return 0; } else if(me->drawflag & ME_DRAWEDGES){ if (me->drawflag & ME_HIDDENEDGES) { return 1; } else { return (flags & eEdge_Visible); } } else { return (flags & eEdge_Select); } } static int draw_tfaces3D__setSeamOpts(void *userData, int index) { struct { Mesh *me; EdgeHash *eh; } *data = userData; Mesh *me= data->me; MEdge *med = &data->me->medge[index]; uintptr_t flags = (intptr_t) BLI_edgehash_lookup(data->eh, med->v1, med->v2); if (med->flag & ME_SEAM) { if (me->drawflag & ME_HIDDENEDGES) { return 1; } else { return (flags & eEdge_Visible); } } else { return 0; } } static int draw_tfaces3D__setSelectOpts(void *userData, int index) { struct { Mesh *me; EdgeHash *eh; } *data = userData; MEdge *med = &data->me->medge[index]; uintptr_t flags = (intptr_t) BLI_edgehash_lookup(data->eh, med->v1, med->v2); return flags & eEdge_Select; } static int draw_tfaces3D__setActiveOpts(void *userData, int index) { struct { Mesh *me; EdgeHash *eh; } *data = userData; MEdge *med = &data->me->medge[index]; uintptr_t flags = (intptr_t) BLI_edgehash_lookup(data->eh, med->v1, med->v2); if (flags & eEdge_Select) { return 1; } else { return 0; } } static int draw_tfaces3D__drawFaceOpts(void *userData, int index) { Mesh *me = (Mesh*)userData; MFace *mface = &me->mface[index]; if (!(mface->flag&ME_HIDE) && (mface->flag&ME_FACE_SEL)) return 2; /* Don't set color */ else return 0; } static void draw_tfaces3D(RegionView3D *rv3d, Object *ob, Mesh *me, DerivedMesh *dm) { struct { Mesh *me; EdgeHash *eh; } data; data.me = me; data.eh = get_tface_mesh_marked_edge_info(me); glEnable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); bglPolygonOffset(rv3d->dist, 1.0); /* Draw (Hidden) Edges */ UI_ThemeColor(TH_EDGE_FACESEL); dm->drawMappedEdges(dm, draw_tfaces3D__setHiddenOpts, &data); /* Draw Seams */ if(me->drawflag & ME_DRAWSEAMS) { UI_ThemeColor(TH_EDGE_SEAM); glLineWidth(2); dm->drawMappedEdges(dm, draw_tfaces3D__setSeamOpts, &data); glLineWidth(1); } /* Draw Selected Faces */ if(me->drawflag & ME_DRAWFACES) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); UI_ThemeColor4(TH_FACE_SELECT); dm->drawMappedFacesTex(dm, draw_tfaces3D__drawFaceOpts, (void*)me); glDisable(GL_BLEND); } bglPolygonOffset(rv3d->dist, 1.0); /* Draw Stippled Outline for selected faces */ glColor3ub(255, 255, 255); setlinestyle(1); dm->drawMappedEdges(dm, draw_tfaces3D__setSelectOpts, &data); setlinestyle(0); dm->drawMappedEdges(dm, draw_tfaces3D__setActiveOpts, &data); bglPolygonOffset(rv3d->dist, 0.0); // resets correctly now, even after calling accumulated offsets BLI_edgehash_free(data.eh, NULL); } static Material *give_current_material_or_def(Object *ob, int matnr) { extern Material defmaterial; // render module abuse... Material *ma= give_current_material(ob, matnr); return ma?ma:&defmaterial; } static int set_draw_settings_cached(int clearcache, int textured, MTFace *texface, int lit, Object *litob, int litmatnr, int doublesided) { static int c_textured; static int c_lit; static int c_doublesided; static MTFace *c_texface; static Object *c_litob; static int c_litmatnr; static int c_badtex; if (clearcache) { c_textured= c_lit= c_doublesided= -1; c_texface= (MTFace*) -1; c_litob= (Object*) -1; c_litmatnr= -1; c_badtex= 0; } if (texface) { lit = lit && (lit==-1 || texface->mode&TF_LIGHT); textured = textured && (texface->mode&TF_TEX); doublesided = texface->mode&TF_TWOSIDE; } else { textured = 0; } if (doublesided!=c_doublesided) { if (doublesided) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); c_doublesided= doublesided; } if (textured!=c_textured || texface!=c_texface) { if (textured ) { c_badtex= !GPU_set_tpage(texface, !(litob->mode & OB_MODE_TEXTURE_PAINT)); } else { GPU_set_tpage(NULL, 0); c_badtex= 0; } c_textured= textured; c_texface= texface; } if (c_badtex) lit= 0; if (lit!=c_lit || litob!=c_litob || litmatnr!=c_litmatnr) { if (lit) { Material *ma= give_current_material_or_def(litob, litmatnr+1); float spec[4]; spec[0]= ma->spec*ma->specr; spec[1]= ma->spec*ma->specg; spec[2]= ma->spec*ma->specb; spec[3]= 1.0; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); } else { glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); } c_lit= lit; c_litob= litob; c_litmatnr= litmatnr; } return c_badtex; } /* Icky globals, fix with userdata parameter */ struct TextureDrawState { Object *ob; int islit, istex; int color_profile; unsigned char obcol[4]; } Gtexdraw = {NULL, 0, 0, 0, {0, 0, 0, 0}}; static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob) { unsigned char obcol[4]; int istex, solidtex= 0; // XXX scene->obedit warning if(v3d->drawtype==OB_SOLID || ((ob->mode & OB_MODE_EDIT) && v3d->drawtype!=OB_TEXTURE)) { /* draw with default lights in solid draw mode and edit mode */ solidtex= 1; Gtexdraw.islit= -1; } else { /* draw with lights in the scene otherwise */ Gtexdraw.islit= GPU_scene_object_lights(scene, ob, v3d->lay, rv3d->viewmat, get_view3d_ortho(v3d, rv3d)); } obcol[0]= CLAMPIS(ob->col[0]*255, 0, 255); obcol[1]= CLAMPIS(ob->col[1]*255, 0, 255); obcol[2]= CLAMPIS(ob->col[2]*255, 0, 255); obcol[3]= CLAMPIS(ob->col[3]*255, 0, 255); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); if(solidtex || v3d->drawtype==OB_TEXTURE) istex= 1; else istex= 0; Gtexdraw.ob = ob; Gtexdraw.istex = istex; Gtexdraw.color_profile = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT; memcpy(Gtexdraw.obcol, obcol, sizeof(obcol)); set_draw_settings_cached(1, 0, 0, Gtexdraw.islit, 0, 0, 0); glShadeModel(GL_SMOOTH); } static void draw_textured_end() { /* switch off textures */ GPU_set_tpage(NULL, 0); glShadeModel(GL_FLAT); glDisable(GL_CULL_FACE); /* XXX, bad patch - GPU_default_lights() calls * glLightfv(GL_LIGHT_POSITION, ...) which * is transformed by the current matrix... we * need to make sure that matrix is identity. * * It would be better if drawmesh.c kept track * of and restored the light settings it changed. * - zr */ glPushMatrix(); glLoadIdentity(); GPU_default_lights(); glPopMatrix(); } static int draw_tface__set_draw_legacy(MTFace *tface, MCol *mcol, int matnr) { if (tface && (tface->mode&TF_INVISIBLE)) return 0; if (tface && set_draw_settings_cached(0, Gtexdraw.istex, tface, Gtexdraw.islit, Gtexdraw.ob, matnr, TF_TWOSIDE)) { glColor3ub(0xFF, 0x00, 0xFF); return 2; /* Don't set color */ } else if (tface && tface->mode&TF_OBCOL) { glColor3ubv(Gtexdraw.obcol); return 2; /* Don't set color */ } else if (!mcol) { if (tface) glColor3f(1.0, 1.0, 1.0); else { Material *ma= give_current_material(Gtexdraw.ob, matnr+1); if(ma) { float col[3]; if(Gtexdraw.color_profile) linearrgb_to_srgb_v3_v3(col, &ma->r); else copy_v3_v3(col, &ma->r); glColor3fv(col); } else glColor3f(1.0, 1.0, 1.0); } return 2; /* Don't set color */ } else { return 1; /* Set color from mcol */ } } static int draw_tface__set_draw(MTFace *tface, MCol *mcol, int matnr) { if (tface && (tface->mode&TF_INVISIBLE)) return 0; if (tface && set_draw_settings_cached(0, Gtexdraw.istex, tface, Gtexdraw.islit, Gtexdraw.ob, matnr, TF_TWOSIDE)) { return 2; /* Don't set color */ } else if (tface && tface->mode&TF_OBCOL) { return 2; /* Don't set color */ } else if (!mcol) { return 1; /* Don't set color */ } else { return 1; /* Set color from mcol */ } } static void add_tface_color_layer(DerivedMesh *dm) { MTFace *tface = DM_get_face_data_layer(dm, CD_MTFACE); MFace *mface = DM_get_face_data_layer(dm, CD_MFACE); MCol *finalCol; int i,j; MCol *mcol = dm->getFaceDataArray(dm, CD_WEIGHT_MCOL); if(!mcol) mcol = dm->getFaceDataArray(dm, CD_MCOL); finalCol = MEM_mallocN(sizeof(MCol)*4*dm->getNumFaces(dm),"add_tface_color_layer"); for(i=0;igetNumFaces(dm);i++) { if (tface && (tface->mode&TF_INVISIBLE)) { if( mcol ) memcpy(&finalCol[i*4],&mcol[i*4],sizeof(MCol)*4); else for(j=0;j<4;j++) { finalCol[i*4+j].b = 255; finalCol[i*4+j].g = 255; finalCol[i*4+j].r = 255; } } else if (tface && mface && set_draw_settings_cached(0, Gtexdraw.istex, tface, Gtexdraw.islit, Gtexdraw.ob, mface[i].mat_nr, TF_TWOSIDE)) { for(j=0;j<4;j++) { finalCol[i*4+j].b = 255; finalCol[i*4+j].g = 0; finalCol[i*4+j].r = 255; } } else if (tface && tface->mode&TF_OBCOL) { for(j=0;j<4;j++) { finalCol[i*4+j].r = FTOCHAR(Gtexdraw.obcol[0]); finalCol[i*4+j].g = FTOCHAR(Gtexdraw.obcol[1]); finalCol[i*4+j].b = FTOCHAR(Gtexdraw.obcol[2]); } } else if (!mcol) { if (tface) { for(j=0;j<4;j++) { finalCol[i*4+j].b = 255; finalCol[i*4+j].g = 255; finalCol[i*4+j].r = 255; } } else { float col[3]; Material *ma= give_current_material(Gtexdraw.ob, mface[i].mat_nr+1); if(ma) { if(Gtexdraw.color_profile) linearrgb_to_srgb_v3_v3(col, &ma->r); else copy_v3_v3(col, &ma->r); for(j=0;j<4;j++) { finalCol[i*4+j].b = FTOCHAR(col[2]); finalCol[i*4+j].g = FTOCHAR(col[1]); finalCol[i*4+j].r = FTOCHAR(col[0]); } } else for(j=0;j<4;j++) { finalCol[i*4+j].b = 255; finalCol[i*4+j].g = 255; finalCol[i*4+j].r = 255; } } } else { for(j=0;j<4;j++) { finalCol[i*4+j].b = mcol[i*4+j].r; finalCol[i*4+j].g = mcol[i*4+j].g; finalCol[i*4+j].r = mcol[i*4+j].b; } } } CustomData_add_layer( &dm->faceData, CD_TEXTURE_MCOL, CD_ASSIGN, finalCol, dm->numFaceData ); } static int draw_tface_mapped__set_draw(void *userData, int index) { Mesh *me = (Mesh*)userData; MTFace *tface = (me->mtface)? &me->mtface[index]: NULL; MFace *mface = (me->mface)? &me->mface[index]: NULL; MCol *mcol = (me->mcol)? &me->mcol[index]: NULL; int matnr = me->mface[index].mat_nr; if (mface && mface->flag&ME_HIDE) return 0; return draw_tface__set_draw(tface, mcol, matnr); } static int draw_em_tf_mapped__set_draw(void *userData, int index) { EditMesh *em = userData; EditFace *efa= EM_get_face_for_index(index); MTFace *tface; MCol *mcol; int matnr; if (efa==NULL || efa->h) return 0; tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); matnr = efa->mat_nr; return draw_tface__set_draw_legacy(tface, mcol, matnr); } static int wpaint__setSolidDrawOptions(void *userData, int index, int *drawSmooth_r) { Mesh *me = (Mesh*)userData; MTFace *tface = (me->mtface)? &me->mtface[index]: NULL; MFace *mface = (me->mface)? &me->mface[index]: NULL; if ((mface->flag&ME_HIDE) || (tface && (tface->mode&TF_INVISIBLE))) return 0; *drawSmooth_r = 1; return 1; } void draw_mesh_text(Scene *scene, Object *ob, int glsl) { Mesh *me = ob->data; DerivedMesh *ddm; MFace *mf, *mface= me->mface; MTFace *tface= me->mtface; MCol *mcol= me->mcol; /* why does mcol exist? */ bProperty *prop = get_ob_property(ob, "Text"); GPUVertexAttribs gattribs; int a, totface= me->totface; /* don't draw without tfaces */ if(!tface) return; /* don't draw when editing */ if(ob->mode & OB_MODE_EDIT) return; else if(ob==OBACT) if(paint_facesel_test(ob)) return; ddm = mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH); for(a=0, mf=mface; amode; int matnr= mf->mat_nr; int mf_smooth= mf->flag & ME_SMOOTH; if (!(mf->flag&ME_HIDE) && !(mode&TF_INVISIBLE) && (mode&TF_BMFONT)) { float v1[3], v2[3], v3[3], v4[3]; char string[MAX_PROPSTRING]; int characters, i, glattrib= -1, badtex= 0; if(glsl) { GPU_enable_material(matnr+1, &gattribs); for(i=0; igetVertCo(ddm, mf->v1, v1); ddm->getVertCo(ddm, mf->v2, v2); ddm->getVertCo(ddm, mf->v3, v3); if (mf->v4) ddm->getVertCo(ddm, mf->v4, v4); // The BM_FONT handling is in the gpu module, shared with the // game engine, was duplicated previously set_property_valstr(prop, string); characters = strlen(string); if(!BKE_image_get_ibuf(tface->tpage, NULL)) characters = 0; if (!mf_smooth) { float nor[3]; normal_tri_v3( nor,v1, v2, v3); glNormal3fv(nor); } GPU_render_text(tface, tface->mode, string, characters, (unsigned int*)mcol, v1, v2, v3, (mf->v4? v4: NULL), glattrib); } if (mcol) { mcol+=4; } } ddm->release(ddm); } void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, DerivedMesh *dm, int faceselect) { Mesh *me= ob->data; /* correct for negative scale */ if(ob->transflag & OB_NEG_SCALE) glFrontFace(GL_CW); else glFrontFace(GL_CCW); /* draw the textured mesh */ draw_textured_begin(scene, v3d, rv3d, ob); if(ob->mode & OB_MODE_EDIT) { glColor4f(1.0f,1.0f,1.0f,1.0f); dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, me->edit_mesh); } else if(faceselect) { if(ob->mode & OB_MODE_WEIGHT_PAINT) dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me, 1); else dm->drawMappedFacesTex(dm, draw_tface_mapped__set_draw, me); } else { if( GPU_buffer_legacy(dm) ) dm->drawFacesTex(dm, draw_tface__set_draw_legacy); else { glColor4f(1.0f,1.0f,1.0f,1.0f); if( !CustomData_has_layer(&dm->faceData,CD_TEXTURE_MCOL) ) add_tface_color_layer(dm); dm->drawFacesTex(dm, draw_tface__set_draw); } } /* draw game engine text hack */ if(get_ob_property(ob, "Text")) draw_mesh_text(scene, ob, 0); draw_textured_end(); /* draw edges and selected faces over textured mesh */ if(!(ob == scene->obedit) && faceselect) draw_tfaces3D(rv3d, ob, me, dm); /* reset from negative scale correction */ glFrontFace(GL_CCW); /* in editmode, the blend mode needs to be set incase it was ADD */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); }