/** * $Id$ * * ***** BEGIN GPL/BL DUAL 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. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * 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) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include #ifdef WIN32 #include "BLI_winstuff.h" #endif #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "MTC_matrixops.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "DNA_image_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "BKE_utildefines.h" #include "BKE_displist.h" #include "BKE_global.h" #include "BKE_mesh.h" #include "BKE_texture.h" #include "BSE_view.h" #include "BSE_edit.h" #include "BSE_drawview.h" /* for backdrawview3d */ #include "BIF_interface.h" #include "BIF_mywindow.h" #include "BIF_toolbox.h" #include "BIF_screen.h" #include "BIF_gl.h" #include "BIF_graphics.h" #include "BIF_space.h" /* for allqueue */ #include "BDR_drawmesh.h" #include "BDR_editface.h" #include "BDR_vpaint.h" #include "BDR_editface.h" #include "BDR_vpaint.h" #include "mydevice.h" #include "blendef.h" #include "render.h" /* #include "graphics.h" */ // #include "blendef.h" #include "interface.h" #include "TPT_DependKludge.h" #ifdef NAN_TPT #include "../img/IMG_Api.h" #include "BSE_trans_types.h" #endif /* NAN_TPT */ TFace *lasttface=0; void set_lasttface() { Mesh *me; TFace *tface; int a; lasttface= 0; me= get_mesh(OBACT); if(me==0 || me->tface==0) return; tface= me->tface; a= me->totface; while(a--) { if(tface->flag & TF_ACTIVE) { lasttface= tface; return; } tface++; } tface= me->tface; a= me->totface; while(a--) { if(tface->flag & TF_SELECT) { lasttface= tface; return; } tface++; } tface= me->tface; a= me->totface; while(a--) { if((tface->flag & TF_HIDE)==0) { lasttface= tface; return; } tface++; } } void default_uv(float uv[][2], float size) { int dy; if(size>1.0) size= 1.0; dy= 1.0-size; uv[0][0]= 0; uv[0][1]= size+dy; uv[1][0]= 0; uv[1][1]= dy; uv[2][0]= size; uv[2][1]= dy; uv[3][0]= size; uv[3][1]= size+dy; } void default_tface(TFace *tface) { default_uv(tface->uv, 1.0); tface->col[0]= tface->col[1]= tface->col[2]= tface->col[3]= vpaint_get_current_col(); tface->mode= TF_TEX; tface->mode= 0; tface->flag= TF_SELECT; tface->tpage= 0; tface->mode |= TF_DYNAMIC; } void make_tfaces(Mesh *me) { TFace *tface; int a; a= me->totface; if(a==0) return; tface= me->tface= MEM_callocN(a*sizeof(TFace), "tface"); while(a--) { default_tface(tface); tface++; } if(me->mcol) { mcol_to_tface(me, 1); } } void reveal_tface() { Mesh *me; TFace *tface; int a; me= get_mesh(OBACT); if(me==0 || me->tface==0 || me->totface==0) return; tface= me->tface; a= me->totface; while(a--) { if(tface->flag & TF_HIDE) { tface->flag |= TF_SELECT; tface->flag -= TF_HIDE; } tface++; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } void hide_tface() { Mesh *me; TFace *tface; int a; me= get_mesh(OBACT); if(me==0 || me->tface==0 || me->totface==0) return; if(G.qual & LR_ALTKEY) { reveal_tface(); return; } tface= me->tface; a= me->totface; while(a--) { if(tface->flag & TF_HIDE); else { if(G.qual & LR_SHIFTKEY) { if( (tface->flag & TF_SELECT)==0) tface->flag |= TF_HIDE; } else { if( (tface->flag & TF_SELECT)) tface->flag |= TF_HIDE; } } if(tface->flag & TF_HIDE) tface->flag &= ~TF_SELECT; tface++; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } void select_linked_tfaces() { Mesh *me; TFace *tface; MFace *mface; int a, doit=1; char *cpmain; me= get_mesh(OBACT); if(me==0 || me->tface==0 || me->totface==0) return; cpmain= MEM_callocN(me->totvert, "cpmain"); while(doit) { doit= 0; /* select connected: array vullen */ tface= me->tface; mface= me->mface; a= me->totface; while(a--) { if(tface->flag & TF_HIDE); else if(tface->flag & TF_SELECT) { if( mface->v3) { cpmain[mface->v1]= 1; cpmain[mface->v2]= 1; cpmain[mface->v3]= 1; if(mface->v4) cpmain[mface->v4]= 1; } } tface++; mface++; } /* omgekeerd: vanuit array vlakken selecteren */ tface= me->tface; mface= me->mface; a= me->totface; while(a--) { if(tface->flag & TF_HIDE); else if((tface->flag & TF_SELECT)==0) { if( mface->v3) { if(mface->v4) { if(cpmain[mface->v4]) { tface->flag |= TF_SELECT; doit= 1; } } if( cpmain[mface->v1] || cpmain[mface->v2] || cpmain[mface->v3] ) { tface->flag |= TF_SELECT; doit= 1; } } } tface++; mface++; } } MEM_freeN(cpmain); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } void deselectall_tface() { Mesh *me; TFace *tface; int a, sel; me= get_mesh(OBACT); if(me==0 || me->tface==0) return; tface= me->tface; a= me->totface; sel= 0; while(a--) { if(tface->flag & TF_HIDE); else if(tface->flag & TF_SELECT) sel= 1; tface++; } tface= me->tface; a= me->totface; while(a--) { if(tface->flag & TF_HIDE); else { if(sel) tface->flag &= ~TF_SELECT; else tface->flag |= TF_SELECT; } tface++; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } void rotate_uv_tface() { Mesh *me; TFace *tface; MFace *mface; short mode; int a; me= get_mesh(OBACT); if(me==0 || me->tface==0) return; mode= pupmenu("OK? %t|Rot UV %x1|Rot VertexCol %x2"); if(mode<1) return; tface= me->tface; mface= me->mface; a= me->totface; while(a--) { if(tface->flag & TF_SELECT) { if(mode==1) { float u1= tface->uv[0][0]; float v1= tface->uv[0][1]; tface->uv[0][0]= tface->uv[1][0]; tface->uv[0][1]= tface->uv[1][1]; tface->uv[1][0]= tface->uv[2][0]; tface->uv[1][1]= tface->uv[2][1]; if(mface->v4) { tface->uv[2][0]= tface->uv[3][0]; tface->uv[2][1]= tface->uv[3][1]; tface->uv[3][0]= u1; tface->uv[3][1]= v1; } else { tface->uv[2][0]= u1; tface->uv[2][1]= v1; } } else if(mode==2) { unsigned int tcol= tface->col[0]; tface->col[0]= tface->col[1]; tface->col[1]= tface->col[2]; if(mface->v4) { tface->col[2]= tface->col[3]; tface->col[3]= tcol; } else { tface->col[2]= tcol; } } } tface++; mface++; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } /** * Returns the face under the give position in screen coordinates. * Code extracted from face_select routine. * Question: why is all of the backbuffer drawn? * We're only interested in one pixel! * @author Maarten Gribnau * @param me the mesh with the faces to be picked * @param x the x-coordinate to pick at * @param y the y-coordinate to pick at * @return the face under the cursor (0 if there was no face found) */ TFace* face_pick(Mesh *me, short x, short y) { unsigned int col; int index; TFace *ret = 0; if (me==0 || me->tface==0) { return ret; } /* Have OpenGL draw in the back buffer with color coded face indices */ if (curarea->win_swap==WIN_EQUAL) { G.vd->flag |= V3D_NEEDBACKBUFDRAW; } if (G.vd->flag & V3D_NEEDBACKBUFDRAW) { backdrawview3d(0); } /* Read the pixel under the cursor */ glReadPixels(x+curarea->winrct.xmin, y+curarea->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); /* Unbelievable! */ if (G.order==B_ENDIAN) { SWITCH_INT(col); } /* Convert the color back to a face index */ index = framebuffer_to_index(col); if (col==0 || index<=0 || index>me->totface) { return ret; } /* Return the face */ ret = ((TFace*)me->tface) + (index-1); return ret; } void face_select() { Object *ob; Mesh *me; TFace *tface, *tsel; short mval[2]; int a; /* Get the face under the cursor */ ob = OBACT; if (!(ob->lay & G.vd->lay)) { error("Active object not in this layer!"); } me = get_mesh(ob); getmouseco_areawin(mval); tsel = face_pick(me, mval[0], mval[1]); if (!tsel) return; if (tsel->flag & TF_HIDE) return; /* clear flags */ tface = me->tface; a = me->totface; while (a--) { if (G.qual & LR_SHIFTKEY) { tface->flag &= ~TF_ACTIVE; } else { tface->flag &= ~(TF_ACTIVE+TF_SELECT); } tface++; } tsel->flag |= TF_ACTIVE; if (G.qual & LR_SHIFTKEY) { if (tsel->flag & TF_SELECT) { tsel->flag &= ~TF_SELECT; } else { tsel->flag |= TF_SELECT; } } else { tsel->flag |= TF_SELECT; } lasttface = tsel; /* image window redraw */ allqueue(REDRAWIMAGE, 0); allqueue(REDRAWBUTSGAME, 0); allqueue(REDRAWVIEW3D, 0); } void face_borderselect() { Mesh *me; TFace *tface; rcti rect; unsigned int *rectm, *rt; int a, sx, sy, index, val; char *selar; me= get_mesh(OBACT); if(me==0 || me->tface==0) return; if(me->totface==0) return; val= get_border(&rect, 3); /* why readbuffer here? shouldn't be necessary */ glReadBuffer(GL_BACK); if(val) { selar= MEM_callocN(me->totface+1, "selar"); sx= (rect.xmax-rect.xmin+1); sy= (rect.ymax-rect.ymin+1); if(sx*sy<=0) return; rt=rectm= MEM_mallocN(sizeof(int)*sx*sy, "selrect"); glReadPixels(rect.xmin+curarea->winrct.xmin, rect.ymin+curarea->winrct.ymin, sx, sy, GL_RGBA, GL_UNSIGNED_BYTE, rectm); if(G.order==B_ENDIAN) IMB_convert_rgba_to_abgr(sx*sy, rectm); a= sx*sy; while(a--) { if(*rt) { index= framebuffer_to_index(*rt); if(index<=me->totface) selar[index]= 1; } rt++; } tface= me->tface; for(a=1; a<=me->totface; a++, tface++) { if(selar[a]) { if(tface->flag & TF_HIDE); else { if(val==LEFTMOUSE) tface->flag |= TF_SELECT; else tface->flag &= ~TF_SELECT; } } } MEM_freeN(rectm); MEM_freeN(selar); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); } } #define TEST_STRUBI 1 #ifdef TEST_STRUBI float CalcNormUV(float *a, float *b, float *c) { float d1[3], d2[3]; d1[0] = a[0] - b[0]; d1[1] = a[1] - b[1]; d2[0] = b[0] - c[0]; d2[1] = b[1] - c[1]; return (d1[0] * d2[1] - d1[1] * d2[0]); } #endif /* Pupmenu codes: */ #define UV_CUBE_MAPPING 2 #define UV_CYL_MAPPING 3 #define UV_SPHERE_MAPPING 4 #define UV_BOUNDS4_MAPPING 65 #define UV_BOUNDS2_MAPPING 66 #define UV_BOUNDS1_MAPPING 67 #define UV_STD4_MAPPING 130 #define UV_STD2_MAPPING 129 #define UV_STD1_MAPPING 128 #define UV_WINDOW_MAPPING 5 /* Some macro tricks to make pupmenu construction look nicer :-) Sorry, just did it for fun. */ #define _STR(x) " " #x #define STRING(x) _STR(x) #define MENUSTRING(string, code) string " %x" STRING(code) #define MENUTITLE(string) string " %t|" void uv_autocalc_tface() { Mesh *me; TFace *tface; MFace *mface; MVert *mv; Object *ob; extern float cumapsize; /* buttons.c */ float dx, dy, min[3], cent[3], max[3], no[3], *loc, mat[4][4]; float fac = 1.0; int i, n, mi; /* strubi */ int a, b; short cox, coy, mode, adr[2]; me= get_mesh(ob=OBACT); if(me==0 || me->tface==0) return; if(me->totface==0) return; mode= pupmenu(MENUTITLE("UV Calculation") MENUSTRING("Cube", UV_CUBE_MAPPING) "|" MENUSTRING("Cylinder", UV_CYL_MAPPING) "|" MENUSTRING("Bounds to 1/4", UV_BOUNDS4_MAPPING) "|" MENUSTRING("Bounds to 1/2", UV_BOUNDS2_MAPPING) "|" MENUSTRING("Bounds to 1/1", UV_BOUNDS1_MAPPING) "|" MENUSTRING("Standard 1/4", UV_STD4_MAPPING) "|" MENUSTRING("Standard 1/2", UV_STD2_MAPPING) "|" MENUSTRING("Standard 1/1", UV_STD1_MAPPING) "|" MENUSTRING("From Window", UV_WINDOW_MAPPING) ); switch(mode) { case UV_CUBE_MAPPING: tface= me->tface; mface= me->mface; mv= me->mvert; loc= ob->obmat[3]; fbutton(&cumapsize, 0.0001, 100.0, "Cubemap size"); for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; CalcNormFloat((mv+mface->v1)->co, (mv+mface->v2)->co, (mv+mface->v3)->co, no); no[0]= fabs(no[0]); no[1]= fabs(no[1]); no[2]= fabs(no[2]); cox=0; coy= 1; if(no[2]>=no[0] && no[2]>=no[1]); else if(no[1]>=no[0] && no[1]>=no[2]) coy= 2; else { cox= 1; coy= 2;} tface->uv[0][0]= 0.5+0.5*cumapsize*(loc[cox] + (mv+mface->v1)->co[cox]); tface->uv[0][1]= 0.5+0.5*cumapsize*(loc[coy] + (mv+mface->v1)->co[coy]); dx = floor(tface->uv[0][0]); dy = floor(tface->uv[0][1]); tface->uv[0][0] -= dx; tface->uv[0][1] -= dy; tface->uv[1][0]= 0.5+0.5*cumapsize*(loc[cox] + (mv+mface->v2)->co[cox]); tface->uv[1][1]= 0.5+0.5*cumapsize*(loc[coy] + (mv+mface->v2)->co[coy]); tface->uv[1][0] -= dx; tface->uv[1][1] -= dy; tface->uv[2][0]= 0.5+0.5*cumapsize*(loc[cox] + (mv+mface->v3)->co[cox]); tface->uv[2][1]= 0.5+0.5*cumapsize*(loc[coy] + (mv+mface->v3)->co[coy]); tface->uv[2][0] -= dx; tface->uv[2][1] -= dy; if(mface->v4) { tface->uv[3][0]= 0.5+0.5*cumapsize*(loc[cox] + (mv+mface->v4)->co[cox]); tface->uv[3][1]= 0.5+0.5*cumapsize*(loc[coy] + (mv+mface->v4)->co[coy]); tface->uv[3][0] -= dx; tface->uv[3][1] -= dy; } } } case UV_BOUNDS4_MAPPING: fac = 0.25; goto bounds_mapping; case UV_BOUNDS2_MAPPING: fac = 0.5; goto bounds_mapping; case UV_BOUNDS1_MAPPING: // fac = 1.0; was already initialized as 1.0 case UV_WINDOW_MAPPING: bounds_mapping: mymultmatrix(ob->obmat); MTC_Mat4SwapMat4(G.vd->persmat, mat); mygetsingmatrix(G.vd->persmat); tface= me->tface; mface= me->mface; dx = curarea->winx; dy = curarea->winy; if (dx > dy) dy = dx; else dx = dy; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; project_short( (me->mvert+mface->v1)->co, adr); if(adr[0]!=3200) { tface->uv[0][0]= ((float)adr[0])/dx; tface->uv[0][1]= ((float)adr[1])/dy; } project_short( (me->mvert+mface->v2)->co, adr); if(adr[0]!=3200) { tface->uv[1][0]= ((float)adr[0])/dx; tface->uv[1][1]= ((float)adr[1])/dy; } project_short( (me->mvert+mface->v3)->co, adr); if(adr[0]!=3200) { tface->uv[2][0]= ((float)adr[0])/dx; tface->uv[2][1]= ((float)adr[1])/dy; } if(mface->v4) { project_short( (me->mvert+mface->v4)->co, adr); if(adr[0]!=3200) { tface->uv[3][0]= ((float)adr[0])/dx; tface->uv[3][1]= ((float)adr[1])/dy; } } } } //stop here if WINDOW_MAPPING: if (mode == UV_WINDOW_MAPPING) break; /* minmax */ min[0]= min[1]= 1.0; max[0]= max[1]= 0.0; tface= me->tface; mface= me->mface; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; if(mface->v4) b= 3; else b= 2; for(; b>=0; b--) { min[0]= MIN2(tface->uv[b][0], min[0]); min[1]= MIN2(tface->uv[b][1], min[1]); max[0]= MAX2(tface->uv[b][0], max[0]); max[1]= MAX2(tface->uv[b][1], max[1]); } } } dx= max[0]-min[0]; dy= max[1]-min[1]; tface= me->tface; mface= me->mface; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; if(mface->v4) b= 3; else b= 2; for(; b>=0; b--) { tface->uv[b][0]= ((tface->uv[b][0] - min[0])* fac) / dx; tface->uv[b][1]= 1.0 - fac + ((tface->uv[b][1] - min[1]) * fac) /dy; } } } break; case UV_STD4_MAPPING: fac = 0.25; goto standard_mapping; case UV_STD2_MAPPING: fac = 0.5; goto standard_mapping; case UV_STD1_MAPPING: fac = 1.0; standard_mapping: tface= me->tface; for(a=0; atotface; a++, tface++) { if(tface->flag & TF_SELECT) { default_uv(tface->uv, fac); } } break; case UV_SPHERE_MAPPING: case UV_CYL_MAPPING: /* calc centre */ INIT_MINMAX(min, max); tface= me->tface; mface= me->mface; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; DO_MINMAX( (me->mvert+mface->v1)->co, min, max); DO_MINMAX( (me->mvert+mface->v2)->co, min, max); DO_MINMAX( (me->mvert+mface->v3)->co, min, max); if(mface->v4) DO_MINMAX( (me->mvert+mface->v3)->co, min, max); } } VecMidf(cent, min, max); tface= me->tface; mface= me->mface; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; VecSubf(no, (me->mvert+mface->v1)->co, cent); if(mode==UV_CYL_MAPPING) tubemap(no[0], no[1], no[2], tface->uv[0], &tface->uv[0][1]); else spheremap(no[0], no[1], no[2], tface->uv[0], &tface->uv[0][1]); VecSubf(no, (me->mvert+mface->v2)->co, cent); if(mode==UV_CYL_MAPPING) tubemap(no[0], no[1], no[2], tface->uv[1], &tface->uv[1][1]); else spheremap(no[0], no[1], no[2], tface->uv[1], &tface->uv[1][1]); VecSubf(no, (me->mvert+mface->v3)->co, cent); if(mode==UV_CYL_MAPPING) tubemap(no[0], no[1], no[2], tface->uv[2], &tface->uv[2][1]); else spheremap(no[0], no[1], no[2], tface->uv[2], &tface->uv[2][1]); n = 3; if(mface->v4) { VecSubf(no, (me->mvert+mface->v4)->co, cent); if(mode==3) tubemap(no[0], no[1], no[2], tface->uv[3], &tface->uv[3][1]); else spheremap(no[0], no[1], no[2], tface->uv[3], &tface->uv[3][1]); n = 4; } mi = 0; for (i = 1; i < n; i++) { if (tface->uv[i][0] > tface->uv[mi][0]) mi = i; } for (i = 0; i < n; i++) { if (i != mi) { dx = tface->uv[mi][0] - tface->uv[i][0]; if (dx > 0.5) { tface->uv[i][0] += 1.0; } } } } } break; default: return; } // end switch /* clipping and wrapping */ if(G.sima && G.sima->flag & SI_CLIP_UV) { tface= me->tface; mface= me->mface; for(a=0; atotface; a++, mface++, tface++) { if(tface->flag & TF_SELECT) { if(mface->v3==0) continue; dx= dy= 0; if(mface->v4) b= 3; else b= 2; for(; b>=0; b--) { while(tface->uv[b][0] + dx < 0.0) dx+= 0.5; while(tface->uv[b][0] + dx > 1.0) dx-= 0.5; while(tface->uv[b][1] + dy < 0.0) dy+= 0.5; while(tface->uv[b][1] + dy > 1.0) dy-= 0.5; } if(mface->v4) b= 3; else b= 2; for(; b>=0; b--) { tface->uv[b][0]+= dx; CLAMP(tface->uv[b][0], 0.0, 1.0); tface->uv[b][1]+= dy; CLAMP(tface->uv[b][1], 0.0, 1.0); } } } } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); myloadmatrix(G.vd->viewmat); MTC_Mat4SwapMat4(G.vd->persmat, mat); } void set_faceselect() /* toggle */ { Object *ob = OBACT; Mesh *me = 0; scrarea_queue_headredraw(curarea); if(G.f & G_FACESELECT) G.f &= ~G_FACESELECT; else { if (ob && ob->type == OB_MESH) G.f |= G_FACESELECT; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSGAME, 0); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWIMAGE, 0); ob= OBACT; me= get_mesh(ob); if(me && me->tface==NULL) make_tfaces(me); if(G.f & G_FACESELECT) { setcursor_space(SPACE_VIEW3D, CURSOR_FACESEL); if(me) set_lasttface(); } else if((G.f & (G_WEIGHTPAINT|G_VERTEXPAINT|G_TEXTUREPAINT))==0) { if(me) reveal_tface(); setcursor_space(SPACE_VIEW3D, CURSOR_STD); makeDispList(ob); } countall(); } #ifdef NAN_TPT /** * Get the view ray through the screen point. * Uses the OpenGL settings of the active view port. * The coordinates should be given in viewport coordinates. * @author Maarten Gribnau * @param x the x-coordinate of the screen point. * @param y the y-coordinate of the screen point. * @param org origin of the view ray. * @param dir direction of the view ray. */ void get_pick_ray(short x, short y, float org[3], float dir[3]) { double mvmatrix[16]; double projmatrix[16]; GLint viewport[4]; double px, py, pz; float l; /* Get the matrices needed for gluUnProject */ glGetIntegerv(GL_VIEWPORT, viewport); glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); /* Set up viewport so that gluUnProject will give correct values */ viewport[0] = 0; viewport[1] = 0; /* printf("viewport = (%4d, %4d, %4d, %4d)\n", viewport[0], viewport[1], viewport[2], viewport[3]); */ /* printf("cursor = (%4d, %4d)\n", x, y); */ gluUnProject((GLdouble) x, (GLdouble) y, 0.0, mvmatrix, projmatrix, viewport, &px, &py, &pz); org[0] = (float)px; org[1] = (float)py; org[2] = (float)pz; /* printf("world point at z=0.0 is (%f, %f, %f)\n", org[0], org[1], org[2]); */ gluUnProject((GLdouble) x, (GLdouble) y, 1.0, mvmatrix, projmatrix, viewport, &px, &py, &pz); /* printf("world point at z=1.0 is (%f, %f, %f)\n", px, py, pz); */ dir[0] = ((float)px) - org[0]; dir[1] = ((float)py) - org[1]; dir[2] = ((float)pz) - org[2]; l = (float)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); if (!l) return; l = 1. / l; dir[0] *= l; dir[1] *= l; dir[2] *= l; /* printf("ray org. is (%f, %f, %f)\n", org[0], org[1], org[2]); */ /* printf("ray dir. is (%f, %f, %f)\n", dir[0], dir[1], dir[2]); */ } int triangle_ray_intersect(float tv0[3], float tv1[3], float tv2[3], float org[3], float dir[3], float uv[2]) { float v1v0[3]; float v2v0[3]; float n[3], an[3]; float t, d, l; float p[3]; double u0, v0, u1, v1, u2, v2, uvtemp; unsigned int iu, iv; /* Calculate normal of the plane (cross, normalize) * Could really use moto here... */ v1v0[0] = tv1[0] - tv0[0]; v1v0[1] = tv1[1] - tv0[1]; v1v0[2] = tv1[2] - tv0[2]; v2v0[0] = tv2[0] - tv0[0]; v2v0[1] = tv2[1] - tv0[1]; v2v0[2] = tv2[2] - tv0[2]; n[0] = (v1v0[1] * v2v0[2]) - (v1v0[2] * v2v0[1]); n[1] = (v1v0[2] * v2v0[0]) - (v1v0[0] * v2v0[2]); n[2] = (v1v0[0] * v2v0[1]) - (v1v0[1] * v2v0[0]); l = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); if (!l) return 0; l = 1. / l; n[0] *= l; n[1] *= l; n[2] *= l; /* Calculate intersection point */ t = n[0]*dir[0] + n[1]*dir[1] + n[2]*dir[2]; if (fabs(t) < 1.0e-6) return 0; d = -(n[0]*tv0[0] + n[1]*tv0[1] + n[2]*tv0[2]); t = -(((n[0]*org[0] + n[1]*org[1] + n[2]*org[2]) + d) / t); if (t < 0) return 0; p[0] = org[0] + dir[0]*t; p[1] = org[1] + dir[1]*t; p[2] = org[2] + dir[2]*t; /*printf("intersection at (%f, %f, %f)\n", p[0], p[1], p[2]);*/ /* Calculate the largest component of the normal */ an[0] = fabs(n[0]); an[1] = fabs(n[1]); an[2] = fabs(n[2]); if ((an[0] > an[1]) && (an[0] > an[2])) { iu = 1; iv = 2; } else if ((an[1] > an[0]) && (an[1] > an[2])) { iu = 2; iv = 0; } else { iu = 0; iv = 1; } /* printf("iu, iv = (%d, %d)\n", iu, iv); */ /* Calculate (u,v) */ u0 = p[iu] - tv0[iu]; v0 = p[iv] - tv0[iv]; u1 = tv1[iu] - tv0[iu]; v1 = tv1[iv] - tv0[iv]; u2 = tv2[iu] - tv0[iu]; v2 = tv2[iv] - tv0[iv]; /* printf("u0, v0, u1, v1, u2, v2 = (%f, %f, %f, %f, %f, %f)\n", u0, v0, u1, v1, u2, v2); */ /* These calculations should be in double precision. * On windows we get inpredictable results in single precision */ if (u1 == 0) { uvtemp = u0/u2; uv[1] = (float)uvtemp; /* if ((uv[1] >= 0.) && (uv[1] <= 1.)) { */ uv[0] = (float)((v0 - uvtemp*v2) / v1); /* } */ } else { uvtemp = (v0*u1 - u0*v1)/(v2*u1-u2*v1); uv[1] = (float)uvtemp; /* if ((uv[1] >= 0) && (uv[1] <= 1)) { */ uv[0] = (float)((u0 - uvtemp*u2) / u1); /* } */ } /* printf("uv[0], uv[1] = (%f, %f)\n", uv[0], uv[1]); */ return ((uv[0] >= 0) && (uv[1] >= 0) && ((uv[0]+uv[1]) <= 1)) ? 2 : 1; } /** * Returns the vertex (local) coordinates of a face. * No bounds checking! * @author Maarten Gribnau * @param mesh the mesh with the face. * @param face the face. * @param v1 vertex 1 coordinates. * @param v2 vertex 2 coordinates. * @param v3 vertex 3 coordinates. * @param v4 vertex 4 coordinates. * @return number of vertices of this face */ int face_get_vertex_coordinates(Mesh* mesh, TFace* face, float v1[3], float v2[3], float v3[3], float v4[3]) { int num_vertices; MVert *mv; MFace *mf = (MFace *) (((MFace *)mesh->mface) + (face - (TFace *) mesh->tface)); num_vertices = mf->v4 == 0 ? 3 : 4; mv = mesh->mvert + mf->v1; v1[0] = mv->co[0]; v1[1] = mv->co[1]; v1[2] = mv->co[2]; mv = mesh->mvert + mf->v2; v2[0] = mv->co[0]; v2[1] = mv->co[1]; v2[2] = mv->co[2]; mv = mesh->mvert + mf->v3; v3[0] = mv->co[0]; v3[1] = mv->co[1]; v3[2] = mv->co[2]; if (num_vertices == 4) { mv = mesh->mvert + mf->v4; v4[0] = mv->co[0]; v4[1] = mv->co[1]; v4[2] = mv->co[2]; } return num_vertices; } /** * Finds texture coordinates from face edge interpolation values. * @author Maarten Gribnau * @param face the face. * @param v1 vertex 1 index. * @param v2 vertex 2 index. * @param v3 vertex 3 index. * @param a interpolation value of edge v2-v1. * @param b interpolation value of edge v3-v1. * @param u (u,v) coordinate. * @param v (u,v) coordinate. */ void face_get_uv(TFace* face, int v1, int v2, int v3, float a, float b, float* u, float* v) { float uv01[2], uv21[2]; /* Pin a,b inside [0,1] range */ #if 0 a = (float)fmod(a, 1.); b = (float)fmod(b, 1.); #else if (a < 0.f) a = 0.f; else if (a > 1.f) a = 1.f; if (b < 0.f) b = 0.f; else if (b > 1.f) b = 1.f; #endif /* Convert to texture coordinates */ uv01[0] = face->uv[v2][0] - face->uv[v1][0]; uv01[1] = face->uv[v2][1] - face->uv[v1][1]; uv21[0] = face->uv[v3][0] - face->uv[v1][0]; uv21[1] = face->uv[v3][1] - face->uv[v1][1]; uv01[0] *= a; uv01[1] *= a; uv21[0] *= b; uv21[1] *= b; *u = face->uv[v1][0] + (uv01[0] + uv21[0]); *v = face->uv[v1][1] + (uv01[1] + uv21[1]); } /** * Get the (u,v) coordinates on a face from a point in screen coordinates. * The coordinates should be given in viewport coordinates. * @author Maarten Gribnau * @param object the object with the mesh * @param mesh the mesh with the face to be picked. * @param face the face to be picked. * @param x the x-coordinate to pick at. * @param y the y-coordinate to pick at. * @param u the u-coordinate calculated. * @param v the v-coordinate calculated. * @return intersection result: * 0 == no intersection, (u,v) invalid * 1 == intersection, (u,v) valid */ int face_pick_uv(Object* object, Mesh* mesh, TFace* face, short x, short y, float* u, float* v) { float org[3], dir[3]; float ab[2]; float v1[3], v2[3], v3[3], v4[3]; int result; int num_verts; /* Get a view ray to intersect with the face */ get_pick_ray(x, y, org, dir); /* Convert local vertex coordinates to world */ num_verts = face_get_vertex_coordinates(mesh, face, v1, v2, v3, v4); /* Convert local vertex coordinates to world */ Mat4MulVecfl(object->obmat, v1); Mat4MulVecfl(object->obmat, v2); Mat4MulVecfl(object->obmat, v3); if (num_verts > 3) { Mat4MulVecfl(object->obmat, v4); } /* Get (u,v) values (local face coordinates) of intersection point * If face is a quad, there are two triangles to check. */ result = triangle_ray_intersect(v2, v1, v3, org, dir, ab); if ((num_verts == 3) || (num_verts == 4) && (result > 1)) { /* Face is a triangle or a quad with a hit on the first triangle */ face_get_uv(face, 1, 0, 2, ab[0], ab[1], u, v); /* printf("triangle 1, texture (u,v)=(%f, %f)\n", *u, *v); */ } else { /* Face is a quad and no intersection with first triangle */ result = triangle_ray_intersect(v4, v3, v1, org, dir, ab); face_get_uv(face, 3, 2, 0, ab[0], ab[1], u, v); /* printf("triangle 2, texture (u,v)=(%f, %f)\n", *u, *v); */ } return result > 0; } /** * First attempt at drawing in the texture of a face. * @author Maarten Gribnau */ void face_draw() { Object *ob; Mesh *me; TFace *face, *face_old = 0; short xy[2], xy_old[2]; //int a, index; Image *img, *img_old; IMG_BrushPtr brush; IMG_CanvasPtr canvas = 0; int rowBytes; char *warn_packed_file = 0; float uv[2], uv_old[2]; extern VPaint Gvp; ob = OBACT; if (!ob) { error("No active object"); return; } if (!(ob->lay & G.vd->lay)) { error("Active object not in this layer"); return; } me = get_mesh(ob); if (!me) { error("Active object does not have a mesh"); return; } brush = IMG_BrushCreate(Gvp.size, Gvp.size, Gvp.r, Gvp.g, Gvp.b, Gvp.a); if (!brush) { error("Can not create brush"); return; } getmouseco_areawin(xy_old); while (get_mbut() & L_MOUSE) { getmouseco_areawin(xy); /* Check if cursor has moved */ if ((xy[0] != xy_old[0]) || (xy[1] != xy_old[1])) { /* Get face to draw on */ face = face_pick(me, xy[0], xy[1]); /* Check if this is another face. */ if (face != face_old) { /* The active face changed, check the texture */ if (face) { img = face->tpage; } else { img = 0; } if (img != img_old) { /* Faces have different textures. Finish drawing in the old face. */ if (face_old && canvas) { face_pick_uv(ob, me, face_old, xy[0], xy[1], &uv[0], &uv[1]); IMG_CanvasDrawLineUV(canvas, brush, uv_old[0], uv_old[1], uv[0], uv[1]); img_old->ibuf->userflags |= IB_BITMAPDIRTY; /* Delete old canvas */ IMG_CanvasDispose(canvas); canvas = 0; } /* Create new canvas and start drawing in the new face. */ if (img) { if (img->ibuf && img->packedfile == 0) { /* MAART: skipx is not set most of the times. Make a guess. */ rowBytes = img->ibuf->skipx ? img->ibuf->skipx : img->ibuf->x * 4; canvas = IMG_CanvasCreateFromPtr(img->ibuf->rect, img->ibuf->x, img->ibuf->y, rowBytes); if (canvas) { face_pick_uv(ob, me, face, xy_old[0], xy_old[1], &uv_old[0], &uv_old[1]); face_pick_uv(ob, me, face, xy[0], xy[1], &uv[0], &uv[1]); IMG_CanvasDrawLineUV(canvas, brush, uv_old[0], uv_old[1], uv[0], uv[1]); img->ibuf->userflags |= IB_BITMAPDIRTY; } } else { /* TODO: should issue warning that no texture is assigned */ if (img->packedfile) { warn_packed_file = img->id.name + 2; img = 0; } } } } else { /* Face changed and faces have the same texture. */ if (canvas) { /* Finish drawing in the old face. */ if (face_old) { face_pick_uv(ob, me, face_old, xy[0], xy[1], &uv[0], &uv[1]); IMG_CanvasDrawLineUV(canvas, brush, uv_old[0], uv_old[1], uv[0], uv[1]); img_old->ibuf->userflags |= IB_BITMAPDIRTY; } /* Start drawing in the new face. */ if (face) { face_pick_uv(ob, me, face, xy_old[0], xy_old[1], &uv_old[0], &uv_old[1]); face_pick_uv(ob, me, face, xy[0], xy[1], &uv[0], &uv[1]); IMG_CanvasDrawLineUV(canvas, brush, uv_old[0], uv_old[1], uv[0], uv[1]); img->ibuf->userflags |= IB_BITMAPDIRTY; } } } } else { /* Same face, continue drawing */ if (face && canvas) { /* Get the new (u,v) coordinates */ face_pick_uv(ob, me, face, xy[0], xy[1], &uv[0], &uv[1]); IMG_CanvasDrawLineUV(canvas, brush, uv_old[0], uv_old[1], uv[0], uv[1]); img->ibuf->userflags |= IB_BITMAPDIRTY; } } if (face && img) { /* Make OpenGL aware of a change in the texture */ free_realtime_image(img); /* Redraw the view */ scrarea_do_windraw(curarea); screen_swapbuffers(); } xy_old[0] = xy[0]; xy_old[1] = xy[1]; uv_old[0] = uv[0]; uv_old[1] = uv[1]; face_old = face; img_old = img; } } IMG_BrushDispose(brush); if (canvas) { IMG_CanvasDispose(canvas); canvas = 0; } if (warn_packed_file) { error("Painting in packed images not supported: %s", warn_packed_file); } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWIMAGE, 0); allqueue(REDRAWHEADERS, 0); } #endif /* NAN_TPT */