diff options
Diffstat (limited to 'source/blender/src/editface.c')
-rw-r--r-- | source/blender/src/editface.c | 1338 |
1 files changed, 1338 insertions, 0 deletions
diff --git a/source/blender/src/editface.c b/source/blender/src/editface.c new file mode 100644 index 00000000000..8ae3b2a7035 --- /dev/null +++ b/source/blender/src/editface.c @@ -0,0 +1,1338 @@ +/** + * $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 <math.h> +#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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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; a<me->totface; 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 */ |