/**
 * $Id: editface.c,v 1.6 2003/05/03 16:06:30 phase 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>

#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_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: 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((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_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

/* 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/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:
		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_BOUNDS8_MAPPING:		
		fac = 0.125;
		goto bounds_mapping;
	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_STD8_MAPPING:
		fac = 0.125;
		goto standard_mapping;
	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 = 0;
	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 */
