/**
 * $Id: editface.c,v 1.33 2004/11/07 13:15:04 blendix Exp $
 *
 * ***** 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>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#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_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "DNA_userdef_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 "butspace.h"

#include "TPT_DependKludge.h"

#ifdef NAN_TPT
#include "../img/IMG_Api.h"
#include "BSE_trans_types.h"
#endif /* NAN_TPT */

#include "BDR_unwrapper.h"

TFace *lasttface=NULL;

static void uv_calc_center_vector(float *result, Object *ob, Mesh *me)
{
	float min[3], max[3], *cursx;
	int a;
	TFace *tface;
	MFace *mface;

	switch (G.vd->around) 
	{
	case V3D_CENTRE: /* bounding box center */
		min[0]= min[1]= min[2]= 1e20f;
		max[0]= max[1]= max[2]= -1e20f; 

		tface= me->tface;
		mface= me->mface;
		for(a=0; a<me->totface; a++, mface++, tface++) {
			if(tface->flag & TF_SELECT && mface->v3) {
				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->v4)->co, min, max);
			}
		}
		VecMidf(result, min, max);
		break;
	case V3D_CURSOR: /*cursor center*/ 
		cursx= give_cursor();
		/* shift to objects world */
		result[0]= cursx[0]-ob->obmat[3][0];
		result[1]= cursx[1]-ob->obmat[3][1];
		result[2]= cursx[2]-ob->obmat[3][2];
		break;
	case V3D_LOCAL: /*object center*/
	case V3D_CENTROID: /* multiple objects centers, only one object here*/
	default:
		result[0]= result[1]= result[2]= 0.0;
		break;
	}
}

static void uv_calc_map_matrix(float result[][4], Object *ob, float upangledeg, float sideangledeg, float radius)
{
	float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
	float sideangle= 0.0, upangle= 0.0;
	int k;

	/* get rotation of the current view matrix */
	Mat4CpyMat4(viewmatrix,G.vd->viewmat);
	/* but shifting */
	for( k= 0; k< 4; k++) viewmatrix[3][k] =0.0;

	/* get rotation of the current object matrix */
	Mat4CpyMat4(rotobj,ob->obmat);
	/* but shifting */
	for( k= 0; k< 4; k++) rotobj[3][k] =0.0;

	Mat4Clr(*rotup);
	Mat4Clr(*rotside);

	/* compensate front/side.. against opengl x,y,z world definition */
	/* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
	/* i wanted to keep the reason here, so we're rotating*/
	sideangle= M_PI * (sideangledeg + 180.0) /180.0;
	rotside[0][0]= (float)cos(sideangle);
	rotside[0][1]= -(float)sin(sideangle);
	rotside[1][0]= (float)sin(sideangle);
	rotside[1][1]= (float)cos(sideangle);
	rotside[2][2]= 1.0f;
      
	upangle= M_PI * upangledeg /180.0;
	rotup[1][1]= (float)cos(upangle)/radius;
	rotup[1][2]= -(float)sin(upangle)/radius;
	rotup[2][1]= (float)sin(upangle)/radius;
	rotup[2][2]= (float)cos(upangle)/radius;
	rotup[0][0]= (float)1.0/radius;

	/* calculate transforms*/
	Mat4MulSerie(result,rotup,rotside,viewmatrix,rotobj,NULL,NULL,NULL,NULL);
}

static void uv_calc_shift_project(float *target, float *shift, float rotmat[][4], int projectionmode, float *source, float *min, float *max)
{
	float pv[3];

	VecSubf(pv, source, shift);
	Mat4MulVecfl(rotmat, pv);

	switch(projectionmode) {
	case B_UVAUTO_CYLINDER: 
		tubemap(pv[0], pv[1], pv[2], &target[0],&target[1]);
		/* split line is always zero */
		if (target[0] >= 1.0f) target[0] -= 1.0f;  
		break;

	case B_UVAUTO_SPHERE: 
		spheremap(pv[0], pv[1], pv[2], &target[0],&target[1]);
		/* split line is always zero */
		if (target[0] >= 1.0f) target[0] -= 1.0f;
		break;

	case 3: /* ortho special case for BOUNDS */
		target[0] = -pv[0];
		target[1] = pv[2];
		break;

	case 4: 
		{
		/* very special case for FROM WINDOW */
		float pv4[4], dx, dy, x= 0.0, y= 0.0;

		dx= G.vd->area->winx;
		dy= G.vd->area->winy;

		VecCopyf(pv4, source);
        pv4[3] = 1.0;

		/* rotmat is the object matrix in this case */
        Mat4MulVec4fl(rotmat,pv4); 

		/* almost project_short */
	    Mat4MulVec4fl(G.vd->persmat,pv4);
		if (fabs(pv4[3]) > 0.00001) { /* avoid division by zero */
			target[0] = dx/2.0 + (dx/2.0)*pv4[0]/pv4[3];
			target[1] = dy/2.0 + (dy/2.0)*pv4[1]/pv4[3];
		}
		else {
			/* scaling is lost but give a valid result */
			target[0] = dx/2.0 + (dx/2.0)*pv4[0];
			target[1] = dy/2.0 + (dy/2.0)*pv4[1];
		}

        /* G.vd->persmat seems to do this funky scaling */ 
		if(dx > dy) {
			y= (dx-dy)/2.0;
			dy = dx;
		}
		else {
			x= (dy-dx)/2.0;
			dx = dy;
		}
		target[0]= (x + target[0])/dx;
		target[1]= (y + target[1])/dy;

		}
		break;

    default:
		target[0] = 0.0;
		target[1] = 1.0;
	}

	/* we know the values here and may need min_max later */
	/* max requests independand from min; not fastest but safest */ 
	if(min) {
		min[0] = MIN2(target[0], min[0]);
		min[1] = MIN2(target[1], min[1]);
	}
	if(max) {
		max[0] = MAX2(target[0], max[0]);
		max[1] = MAX2(target[1], max[1]);
	}
}

void calculate_uv_map(unsigned short mapmode)
{
	Mesh *me;
	TFace *tface;
	MFace *mface;
	Object *ob;
	float dx, dy, rotatematrix[4][4], radius= 1.0, min[3], cent[3], max[3];
	float fac= 1.0, upangledeg= 0.0, sideangledeg= 90.0;
	int i, b, mi, a, n;
	/* settings from buttonswindow */
	extern float uv_calc_radius, uv_calc_cubesize;
	extern short uv_calc_mapdir, uv_calc_mapalign;

	if(uv_calc_mapdir==1)  {
		upangledeg= 90.0;
		sideangledeg= 0.0;
	}
	else {
		upangledeg= 0.0;
		if(uv_calc_mapalign==1) sideangledeg= 0.0;
		else sideangledeg= 90.0;
	}

	me= get_mesh(ob=OBACT);
	if(me==0 || me->tface==0) return;
	if(me->totface==0) return;
	
	switch(mapmode) {
	case B_UVAUTO_BOUNDS1:
	case B_UVAUTO_BOUNDS2:
	case B_UVAUTO_BOUNDS4:
	case B_UVAUTO_BOUNDS8:
		switch(mapmode) {
		case B_UVAUTO_BOUNDS2: fac = 0.5; break;
		case B_UVAUTO_BOUNDS4: fac = 0.25; break;
		case B_UVAUTO_BOUNDS8: fac = 0.125; break;
		}

		min[0]= min[1]= 1.0;
		max[0]= max[1]= 0.0;

		uv_calc_center_vector(cent, ob, me);
		uv_calc_map_matrix(rotatematrix, ob, upangledeg, sideangledeg, 1.0f);
			
		tface= me->tface;
		mface= me->mface;
		for(a=0; a<me->totface; a++, mface++, tface++) {
			if(tface->flag & TF_SELECT && mface->v3) {
				uv_calc_shift_project(tface->uv[0],cent,rotatematrix,3,(me->mvert+mface->v1)->co,min,max);
				uv_calc_shift_project(tface->uv[1],cent,rotatematrix,3,(me->mvert+mface->v2)->co,min,max);
				uv_calc_shift_project(tface->uv[2],cent,rotatematrix,3,(me->mvert+mface->v3)->co,min,max);
				if(mface->v4)
					uv_calc_shift_project(tface->uv[3],cent,rotatematrix,3,(me->mvert+mface->v4)->co,min,max);
			}
		}
		
		/* rescale UV to be in 0..1,1/2,1/4,1/8 */
		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 && mface->v3) {
				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 B_UVAUTO_WINDOW:		
		cent[0] = cent[1] = cent[2] = 0.0; 
		Mat4CpyMat4(rotatematrix,ob->obmat);

		tface= me->tface;
		mface= me->mface;
		for(a=0; a<me->totface; a++, mface++, tface++) {
			if(tface->flag & TF_SELECT && mface->v3) {
				uv_calc_shift_project(tface->uv[0],cent,rotatematrix,4,(me->mvert+mface->v1)->co,NULL,NULL);
				uv_calc_shift_project(tface->uv[1],cent,rotatematrix,4,(me->mvert+mface->v2)->co,NULL,NULL);
				uv_calc_shift_project(tface->uv[2],cent,rotatematrix,4,(me->mvert+mface->v3)->co,NULL,NULL);
				if(mface->v4)
					uv_calc_shift_project(tface->uv[3],cent,rotatematrix,4,(me->mvert+mface->v4)->co,NULL,NULL);
			}
		}
		break;

	case B_UVAUTO_STD8:
	case B_UVAUTO_STD4:
	case B_UVAUTO_STD2:
	case B_UVAUTO_STD1:
		switch(mapmode) {
		case B_UVAUTO_STD8: fac = 0.125; break;
		case B_UVAUTO_STD4: fac = 0.25; break;
		case B_UVAUTO_STD2: fac = 0.5; break;
		}

		tface= me->tface;
		for(a=0; a<me->totface; a++, tface++)
			if(tface->flag & TF_SELECT) 
				default_uv(tface->uv, fac);
		break;

	case B_UVAUTO_CYLINDER:
	case B_UVAUTO_SPHERE:
		uv_calc_center_vector(cent, ob, me);
			
		if(mapmode==B_UVAUTO_CYLINDER) radius = uv_calc_radius;

		/* be compatible to the "old" sphere/cylinder mode */
		if (uv_calc_mapdir== 2)
			Mat4One(rotatematrix);
		else 
			uv_calc_map_matrix(rotatematrix,ob,upangledeg,sideangledeg,radius);

		tface= me->tface;
		mface= me->mface;
		for(a=0; a<me->totface; a++, mface++, tface++) {
			if(tface->flag & TF_SELECT && mface->v3) {
				uv_calc_shift_project(tface->uv[0],cent,rotatematrix,mapmode,(me->mvert+mface->v1)->co,NULL,NULL);
				uv_calc_shift_project(tface->uv[1],cent,rotatematrix,mapmode,(me->mvert+mface->v2)->co,NULL,NULL);
				uv_calc_shift_project(tface->uv[2],cent,rotatematrix,mapmode,(me->mvert+mface->v3)->co,NULL,NULL);
				n = 3;       
				if(mface->v4) {
					uv_calc_shift_project(tface->uv[3],cent,rotatematrix,mapmode,(me->mvert+mface->v4)->co,NULL,NULL);
					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;

	case B_UVAUTO_CUBE:
		{
		/* choose x,y,z axis for projetion depending on the largest normal */
		/* component, but clusters all together around the center of map */
		float no[3];
		short cox, coy;
		float *loc= ob->obmat[3];
		MVert *mv= me->mvert;

		tface= me->tface;
		mface= me->mface;
		for(a=0; a<me->totface; a++, mface++, tface++) {
			if(tface->flag & TF_SELECT && mface->v3) {
				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*uv_calc_cubesize*(loc[cox] + (mv+mface->v1)->co[cox]);
				tface->uv[0][1]= 0.5+0.5*uv_calc_cubesize*(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*uv_calc_cubesize*(loc[cox] + (mv+mface->v2)->co[cox]);
				tface->uv[1][1]= 0.5+0.5*uv_calc_cubesize*(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*uv_calc_cubesize*(loc[cox] + (mv+mface->v3)->co[cox]);
				tface->uv[2][1]= 0.5+0.5*uv_calc_cubesize*(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*uv_calc_cubesize*(loc[cox] + (mv+mface->v4)->co[cox]);
					tface->uv[3][1]= 0.5+0.5*uv_calc_cubesize*(loc[coy] + (mv+mface->v4)->co[coy]);
					tface->uv[3][0] -= dx;
					tface->uv[3][1] -= dy;
				}
			}
		}
		}
		break; 
	default:
		return;
	} /* end switch mapmode */

	/* 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 && mface->v3)) 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);
			}
		}
	}

	BIF_undo_push("UV calculation");

	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIMAGE, 0);
}

void set_lasttface()
{
	Mesh *me;
	TFace *tface;
	int a;
	
	lasttface= 0;
	if(OBACT==NULL || OBACT->type!=OB_MESH) return;
	
	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++;
	}

	BIF_undo_push("Reveal UV face");

	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++;
	}
	BIF_undo_push("Hide UV face");
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIMAGE, 0);
	
}

void select_linked_tfaces()
{
	Mesh *me;
	TFace *tface;
	MFace *mface;
	int a, doit=1, mark=0;
	char *cpmain;
	
	me= get_mesh(OBACT);
	if(me==0 || me->tface==0 || me->totface==0) return;

	if(me->medge) {
		select_linked_tfaces_with_seams();
		return;
	}
	
	cpmain= MEM_callocN(me->totvert, "cpmain");
	
	while(doit) {
		doit= 0;
		
		/* select connected: fill array */
		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++;
		}
		
		/* reverse: using array select the faces */

		tface= me->tface;
		mface= me->mface;
		a= me->totface;
		while(a--) {
			if(tface->flag & TF_HIDE);
			else if(mface->v3 && ((tface->flag & TF_SELECT)==0)) {
				mark= 0;

				if(cpmain[mface->v1] || cpmain[mface->v2] || cpmain[mface->v3])
					mark= 1;
				else if(mface->v4 && cpmain[mface->v4])
					mark= 1;

				if(mark) {
					tface->flag |= TF_SELECT;
					doit= 1;
				}
			}
			tface++; mface++;
		}
		
	}
	MEM_freeN(cpmain);
	
	BIF_undo_push("Select linked UV face");
	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++;
	}

	BIF_undo_push("(De)select all UV face");
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIMAGE, 0);
}

void selectswap_tface(void)
{
	Mesh *me;
	TFace *tface;
	int a;
		
	me= get_mesh(OBACT);
	if(me==0 || me->tface==0) return;
	
	tface= me->tface;
	a= me->totface;
	while(a--) {
		if(tface->flag & TF_HIDE);
		else {
			if(tface->flag & TF_SELECT) tface->flag &= ~TF_SELECT;
			else tface->flag |= TF_SELECT;
		}
		tface++;
	}

	BIF_undo_push("Select inverse UV face");
	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("Rotate %t|UV Co-ordinates %x1|Vertex Colors %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++;
	}
	
	BIF_undo_push("Rotate UV face");
	makeDispList(OBACT);
	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 (-1 if there was no face found)
 */
int face_pick(Mesh *me, short x, short y)
{
	unsigned int col;
	int index;

	if (me==0 || me->tface==0) {
		return -1;
	}

	/* 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);
		persp(PERSP_VIEW);
	}
	/* Read the pixel under the cursor */
#ifdef __APPLE__
	glReadBuffer(GL_AUX0);
#endif
	glReadPixels(x+curarea->winrct.xmin, y+curarea->winrct.ymin, 1, 1,
		GL_RGBA, GL_UNSIGNED_BYTE, &col);
	glReadBuffer(GL_BACK);

	/* 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 -1;
	}
	return (index-1);
}

void face_select()
{
	Object *ob;
	Mesh *me;
	TFace *tface, *tsel;
	MFace *msel;
	short mval[2];
	int a, index;

	/* Get the face under the cursor */
	ob = OBACT;
	if (!(ob->lay & G.vd->lay)) {
		error("The active object is not in this layer");
	}
	me = get_mesh(ob);
	getmouseco_areawin(mval);
	index = face_pick(me, mval[0], mval[1]);
	if (index==-1) return;
	
	tsel= (((TFace*)me->tface)+index);
	msel= (((MFace*)me->mface)+index);

	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 */
	
	BIF_undo_push("Select UV face");
	allqueue(REDRAWIMAGE, 0);
	allqueue(REDRAWBUTSEDIT, 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 (maybe a flush or so) */
	glReadBuffer(GL_BACK);
#ifdef __APPLE__
	glReadBuffer(GL_AUX0); /* apple only */
#endif
	
	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);

		BIF_undo_push("Border Select UV face");

		allqueue(REDRAWVIEW3D, 0);
		allqueue(REDRAWIMAGE, 0);
	}
#ifdef __APPLE__	
	glReadBuffer(GL_BACK);
#endif
}

#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_BOUNDS8_MAPPING 68
#define UV_BOUNDS4_MAPPING 65
#define UV_BOUNDS2_MAPPING 66
#define UV_BOUNDS1_MAPPING 67
#define UV_STD8_MAPPING 131
#define UV_STD4_MAPPING 130
#define UV_STD2_MAPPING 129
#define UV_STD1_MAPPING 128
#define UV_WINDOW_MAPPING 5
#define UV_LSCM_MAPPING 6
#define UV_CYL_EX 32
#define UV_SPHERE_EX 34

/* 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()
{
	short mode;
	mode= pupmenu(MENUTITLE("UV Calculation")
	              MENUSTRING("Cube",          UV_CUBE_MAPPING) "|"
	              MENUSTRING("Cylinder",      UV_CYL_MAPPING) "|"
	              MENUSTRING("Sphere",        UV_SPHERE_MAPPING) "|"
	              MENUSTRING("LSCM",          UV_LSCM_MAPPING) "|"
	              MENUSTRING("Bounds to 1/8", UV_BOUNDS8_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/8",  UV_STD8_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:
		calculate_uv_map(B_UVAUTO_CUBE); break;
	case UV_CYL_MAPPING:
		calculate_uv_map(B_UVAUTO_CYLINDER); break;
	case UV_SPHERE_MAPPING:
		calculate_uv_map(B_UVAUTO_SPHERE); break;
	case UV_BOUNDS8_MAPPING:
		calculate_uv_map(B_UVAUTO_BOUNDS8); break;
	case UV_BOUNDS4_MAPPING:
		calculate_uv_map(B_UVAUTO_BOUNDS4); break;
	case UV_BOUNDS2_MAPPING:
		calculate_uv_map(B_UVAUTO_BOUNDS2); break;
	case UV_BOUNDS1_MAPPING:
		calculate_uv_map(B_UVAUTO_BOUNDS1); break;
	case UV_STD8_MAPPING:
		calculate_uv_map(B_UVAUTO_STD8); break;
	case UV_STD4_MAPPING:
		calculate_uv_map(B_UVAUTO_STD4); break;
	case UV_STD2_MAPPING:
		calculate_uv_map(B_UVAUTO_STD2); break;
	case UV_STD1_MAPPING:
		calculate_uv_map(B_UVAUTO_STD1); break;
	case UV_WINDOW_MAPPING:
		calculate_uv_map(B_UVAUTO_WINDOW); break;
	case UV_LSCM_MAPPING:
		unwrap_lscm(); break;
	}
}

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(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();
			set_seamtface(); /* set TF_SEAM flags in tface */
		}
		BIF_undo_push("Set UV Faceselect");
	}
	else if((G.f & (G_WEIGHTPAINT|G_VERTEXPAINT|G_TEXTUREPAINT))==0) {
		if(me) reveal_tface();
		setcursor_space(SPACE_VIEW3D, CURSOR_STD);
		makeDispList(ob);
		BIF_undo_push("End UV Faceselect");
	}
	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=NULL, *img_old = NULL;
	IMG_BrushPtr brush;
	IMG_CanvasPtr canvas = 0;
	int rowBytes, face_index;
	char *warn_packed_file = 0;
	float uv[2], uv_old[2];
	extern VPaint Gvp;
	short mousebut;

	ob = OBACT;
	if (!ob) {
		error("No active object"); return;
	}
	if (!(ob->lay & G.vd->lay)) {
		error("The active object is not in this layer"); return;
	}
	me = get_mesh(ob);
	if (!me) {
		error("The active object does not have a mesh obData"); return;
	}

	brush = IMG_BrushCreate(Gvp.size, Gvp.size, Gvp.r, Gvp.g, Gvp.b, Gvp.a);
	if (!brush) {
		error("Can't create brush"); return;
	}

	if (U.flag & USER_LMOUSESELECT) mousebut = R_MOUSE;
	else mousebut = L_MOUSE;

	persp(PERSP_VIEW);

	getmouseco_areawin(xy_old);
	while (get_mbut() & mousebut) {
		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_index = face_pick(me, xy[0], xy[1]);
			if (face_index == -1) face = NULL;
			else face = (((TFace*)me->tface)+face_index);

			/* 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 is not supported: %s", warn_packed_file);
	}

	persp(PERSP_WIN);

	BIF_undo_push("UV face draw");
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIMAGE, 0);
	allqueue(REDRAWHEADERS, 0);
}

 /* Selects all faces which have the same uv-texture as the active face 
 * @author	Roel Spruit
 * @return	Void
 * Errors:	- Active object not in this layer
 *		- No active face or active face has no UV-texture			
 */
void get_same_uv(void)
{
	Object *ob;
	Mesh *me;
	TFace *tface;	
	short a, foundtex=0;
	Image *ima;
	char uvname[160];
	
	ob = OBACT;
	if (!(ob->lay & G.vd->lay)) {
		error("The active object is not in this layer");
		return;
	}
	me = get_mesh(ob);
	
		
	/* Search for the active face with a UV-Texture */
	tface = me->tface;
	a = me->totface;
	while (a--) {		
		if(tface->flag & TF_ACTIVE){			
			ima=tface->tpage;
			if(ima && ima->name){
				strcpy(uvname,ima->name);			
				a=0;
				foundtex=1;
			}
		}
		tface++;
	}		
	
	if(!foundtex) {
		error("No active face, or active face has no UV texture");
		return;
	}

	/* select everything with the same texture */
	tface = me->tface;
	a = me->totface;
	while (a--) {		
		ima=tface->tpage;
		if(ima && ima->name){
			if(!strcmp(ima->name, uvname)){
				tface->flag |= TF_SELECT;
			}
			else tface->flag &= ~TF_SELECT;
		}
		else tface->flag &= ~TF_SELECT;
		tface++;
	}
	
	/* image window redraw */
	BIF_undo_push("Get same UV");
	allqueue(REDRAWIMAGE, 0);
	allqueue(REDRAWVIEW3D, 0);
}
#endif /* NAN_TPT */
