1663 lines
38 KiB
C
1663 lines
38 KiB
C
/**
|
|
* $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>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
#include "BLI_heap.h"
|
|
#include "BLI_edgehash.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_scene_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
|
|
#include "BKE_brush.h"
|
|
#include "BKE_depsgraph.h"
|
|
#include "BKE_displist.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_texture.h"
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "BSE_view.h"
|
|
#include "BSE_edit.h"
|
|
#include "BSE_drawview.h" /* for backdrawview3d */
|
|
|
|
#include "BIF_editsima.h"
|
|
#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 "butspace.h"
|
|
|
|
#include "BSE_trans_types.h"
|
|
|
|
#include "BDR_unwrapper.h"
|
|
|
|
/* 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_UNWRAP_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|"
|
|
|
|
|
|
/* returns 0 if not found, otherwise 1 */
|
|
int facesel_face_pick(Mesh *me, short *mval, unsigned int *index, short rect)
|
|
{
|
|
if (!me || !me->tface || me->totface==0)
|
|
return 0;
|
|
|
|
if (G.vd->flag & V3D_NEEDBACKBUFDRAW) {
|
|
check_backbuf();
|
|
persp(PERSP_VIEW);
|
|
}
|
|
|
|
if (rect) {
|
|
/* sample rect to increase changes of selecting, so that when clicking
|
|
on an edge in the backbuf, we can still select a face */
|
|
short dist;
|
|
*index = sample_backbuf_rect(mval, 3, 1, me->totface+1, &dist);
|
|
}
|
|
else
|
|
/* sample only on the exact position */
|
|
*index = sample_backbuf(mval[0], mval[1]);
|
|
|
|
if ((*index)<=0 || (*index)>(unsigned int)me->totface)
|
|
return 0;
|
|
|
|
(*index)--;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* returns 0 if not found, otherwise 1 */
|
|
static int facesel_edge_pick(Mesh *me, short *mval, unsigned int *index)
|
|
{
|
|
short dist;
|
|
unsigned int min = me->totface + 1;
|
|
unsigned int max = me->totface + me->totedge + 1;
|
|
|
|
if (me->totedge == 0)
|
|
return 0;
|
|
|
|
if (G.vd->flag & V3D_NEEDBACKBUFDRAW) {
|
|
check_backbuf();
|
|
persp(PERSP_VIEW);
|
|
}
|
|
|
|
*index = sample_backbuf_rect(mval, 50, min, max, &dist);
|
|
|
|
if (*index == 0)
|
|
return 0;
|
|
|
|
(*index)--;
|
|
|
|
return 1;
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
|
|
if(G.scene->toolsettings->uvcalc_mapdir==1) {
|
|
upangledeg= 90.0;
|
|
sideangledeg= 0.0;
|
|
}
|
|
else {
|
|
upangledeg= 0.0;
|
|
if(G.scene->toolsettings->uvcalc_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) {
|
|
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) {
|
|
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) {
|
|
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 = G.scene->toolsettings->uvcalc_radius;
|
|
|
|
/* be compatible to the "old" sphere/cylinder mode */
|
|
if (G.scene->toolsettings->uvcalc_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) {
|
|
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;
|
|
float cubesize = G.scene->toolsettings->uvcalc_cubesize;
|
|
|
|
tface= me->tface;
|
|
mface= me->mface;
|
|
for(a=0; a<me->totface; a++, mface++, tface++) {
|
|
if(tface->flag & TF_SELECT) {
|
|
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*cubesize*(loc[cox] + (mv+mface->v1)->co[cox]);
|
|
tface->uv[0][1]= 0.5+0.5*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*cubesize*(loc[cox] + (mv+mface->v2)->co[cox]);
|
|
tface->uv[1][1]= 0.5+0.5*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*cubesize*(loc[cox] + (mv+mface->v3)->co[cox]);
|
|
tface->uv[2][1]= 0.5+0.5*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*cubesize*(loc[cox] + (mv+mface->v4)->co[cox]);
|
|
tface->uv[3][1]= 0.5+0.5*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)) 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");
|
|
|
|
object_uvs_changed(OBACT);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWIMAGE, 0);
|
|
}
|
|
|
|
TFace *get_active_tface()
|
|
{
|
|
Mesh *me;
|
|
TFace *tf;
|
|
int a;
|
|
|
|
if(OBACT==NULL || OBACT->type!=OB_MESH)
|
|
return NULL;
|
|
|
|
me= get_mesh(OBACT);
|
|
if(me==0 || me->tface==0)
|
|
return NULL;
|
|
|
|
for(a=0, tf=me->tface; a < me->totface; a++, tf++)
|
|
if(tf->flag & TF_ACTIVE)
|
|
return tf;
|
|
|
|
for(a=0, tf=me->tface; a < me->totface; a++, tf++)
|
|
if(tf->flag & TF_SELECT)
|
|
return tf;
|
|
|
|
for(a=0, tf=me->tface; a < me->totface; a++, tf++)
|
|
if((tf->flag & TF_HIDE)==0)
|
|
return tf;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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");
|
|
|
|
object_tface_flags_changed(OBACT, 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");
|
|
|
|
object_tface_flags_changed(OBACT, 0);
|
|
}
|
|
|
|
void select_linked_tfaces(int mode)
|
|
{
|
|
Object *ob;
|
|
Mesh *me;
|
|
short mval[2];
|
|
unsigned int index=0;
|
|
|
|
ob = OBACT;
|
|
me = get_mesh(ob);
|
|
if(me==0 || me->tface==0 || me->totface==0) return;
|
|
|
|
if (mode==0 || mode==1) {
|
|
if (!(ob->lay & G.vd->lay))
|
|
error("The active object is not in this layer");
|
|
|
|
getmouseco_areawin(mval);
|
|
if (!facesel_face_pick(me, mval, &index, 1)) return;
|
|
}
|
|
|
|
select_linked_tfaces_with_seams(mode, me, index);
|
|
}
|
|
|
|
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");
|
|
|
|
object_tface_flags_changed(OBACT, 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");
|
|
|
|
object_tface_flags_changed(OBACT, 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");
|
|
|
|
object_uvs_changed(OBACT);
|
|
}
|
|
|
|
void mirror_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("Mirror %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];
|
|
if(mface->v4) {
|
|
tface->uv[0][0]= tface->uv[3][0];
|
|
tface->uv[0][1]= tface->uv[3][1];
|
|
|
|
tface->uv[3][0]= u1;
|
|
tface->uv[3][1]= v1;
|
|
|
|
u1= tface->uv[1][0];
|
|
v1= tface->uv[1][1];
|
|
|
|
tface->uv[1][0]= tface->uv[2][0];
|
|
tface->uv[1][1]= tface->uv[2][1];
|
|
|
|
tface->uv[2][0]= u1;
|
|
tface->uv[2][1]= v1;
|
|
}
|
|
else {
|
|
tface->uv[0][0]= tface->uv[2][0];
|
|
tface->uv[0][1]= tface->uv[2][1];
|
|
tface->uv[2][0]= u1;
|
|
tface->uv[2][1]= v1;
|
|
}
|
|
}
|
|
else if(mode==2) {
|
|
unsigned int tcol= tface->col[0];
|
|
if(mface->v4) {
|
|
tface->col[0]= tface->col[3];
|
|
tface->col[3]= tcol;
|
|
|
|
tcol = tface->col[1];
|
|
tface->col[1]= tface->col[2];
|
|
tface->col[2]= tcol;
|
|
}
|
|
else {
|
|
tface->col[0]= tface->col[2];
|
|
tface->col[2]= tcol;
|
|
}
|
|
}
|
|
}
|
|
tface++;
|
|
mface++;
|
|
}
|
|
|
|
BIF_undo_push("Mirror UV face");
|
|
|
|
object_uvs_changed(OBACT);
|
|
}
|
|
|
|
void minmax_tface(float *min, float *max)
|
|
{
|
|
Object *ob;
|
|
Mesh *me;
|
|
MFace *mf;
|
|
TFace *tf;
|
|
MVert *mv;
|
|
int a;
|
|
float vec[3], bmat[3][3];
|
|
|
|
ob = OBACT;
|
|
if (ob==0) return;
|
|
me= get_mesh(ob);
|
|
if(me==0 || me->tface==0) return;
|
|
|
|
Mat3CpyMat4(bmat, ob->obmat);
|
|
|
|
mv= me->mvert;
|
|
mf= me->mface;
|
|
tf= me->tface;
|
|
for (a=me->totface; a>0; a--, mf++, tf++) {
|
|
if (tf->flag & TF_HIDE || !(tf->flag & TF_SELECT))
|
|
continue;
|
|
|
|
VECCOPY(vec, (mv+mf->v1)->co);
|
|
Mat3MulVecfl(bmat, vec);
|
|
VecAddf(vec, vec, ob->obmat[3]);
|
|
DO_MINMAX(vec, min, max);
|
|
|
|
VECCOPY(vec, (mv+mf->v2)->co);
|
|
Mat3MulVecfl(bmat, vec);
|
|
VecAddf(vec, vec, ob->obmat[3]);
|
|
DO_MINMAX(vec, min, max);
|
|
|
|
VECCOPY(vec, (mv+mf->v3)->co);
|
|
Mat3MulVecfl(bmat, vec);
|
|
VecAddf(vec, vec, ob->obmat[3]);
|
|
DO_MINMAX(vec, min, max);
|
|
|
|
if (mf->v4) {
|
|
VECCOPY(vec, (mv+mf->v4)->co);
|
|
Mat3MulVecfl(bmat, vec);
|
|
VecAddf(vec, vec, ob->obmat[3]);
|
|
DO_MINMAX(vec, min, max);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ME_SEAM_DONE ME_SEAM_LAST /* reuse this flag */
|
|
|
|
static float seam_cut_cost(Mesh *me, int e1, int e2, int vert)
|
|
{
|
|
MVert *v = me->mvert + vert;
|
|
MEdge *med1 = me->medge + e1, *med2 = me->medge + e2;
|
|
MVert *v1 = me->mvert + ((med1->v1 == vert)? med1->v2: med1->v1);
|
|
MVert *v2 = me->mvert + ((med2->v1 == vert)? med2->v2: med2->v1);
|
|
float cost, d1[3], d2[3];
|
|
|
|
cost = VecLenf(v1->co, v->co);
|
|
cost += VecLenf(v->co, v2->co);
|
|
|
|
VecSubf(d1, v->co, v1->co);
|
|
VecSubf(d2, v2->co, v->co);
|
|
|
|
cost = cost + 0.5f*cost*(2.0f - fabs(d1[0]*d2[0] + d1[1]*d2[1] + d1[2]*d2[2]));
|
|
|
|
return cost;
|
|
}
|
|
|
|
static void seam_add_adjacent(Mesh *me, Heap *heap, int mednum, int vertnum, int *nedges, int *edges, int *prevedge, float *cost)
|
|
{
|
|
int startadj, endadj = nedges[vertnum+1];
|
|
|
|
for (startadj = nedges[vertnum]; startadj < endadj; startadj++) {
|
|
int adjnum = edges[startadj];
|
|
MEdge *medadj = me->medge + adjnum;
|
|
float newcost;
|
|
|
|
if (medadj->flag & ME_SEAM_DONE)
|
|
continue;
|
|
|
|
newcost = cost[mednum] + seam_cut_cost(me, mednum, adjnum, vertnum);
|
|
|
|
if (cost[adjnum] > newcost) {
|
|
cost[adjnum] = newcost;
|
|
prevedge[adjnum] = mednum;
|
|
BLI_heap_insert(heap, newcost, (void*)adjnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int seam_shortest_path(Mesh *me, int source, int target)
|
|
{
|
|
Heap *heap;
|
|
EdgeHash *ehash;
|
|
float *cost;
|
|
MEdge *med;
|
|
int a, *nedges, *edges, *prevedge, mednum = -1, nedgeswap = 0;
|
|
TFace *tf;
|
|
MFace *mf;
|
|
|
|
/* mark hidden edges as done, so we don't use them */
|
|
ehash = BLI_edgehash_new();
|
|
|
|
for (a=0, mf=me->mface, tf=me->tface; a<me->totface; a++, tf++, mf++) {
|
|
if (!(tf->flag & TF_HIDE)) {
|
|
BLI_edgehash_insert(ehash, mf->v1, mf->v2, NULL);
|
|
BLI_edgehash_insert(ehash, mf->v2, mf->v3, NULL);
|
|
if (mf->v4) {
|
|
BLI_edgehash_insert(ehash, mf->v3, mf->v4, NULL);
|
|
BLI_edgehash_insert(ehash, mf->v4, mf->v1, NULL);
|
|
}
|
|
else
|
|
BLI_edgehash_insert(ehash, mf->v3, mf->v1, NULL);
|
|
}
|
|
}
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++)
|
|
if (!BLI_edgehash_haskey(ehash, med->v1, med->v2))
|
|
med->flag |= ME_SEAM_DONE;
|
|
|
|
BLI_edgehash_free(ehash, NULL);
|
|
|
|
/* alloc */
|
|
nedges = MEM_callocN(sizeof(*nedges)*me->totvert+1, "SeamPathNEdges");
|
|
edges = MEM_mallocN(sizeof(*edges)*me->totedge*2, "SeamPathEdges");
|
|
prevedge = MEM_mallocN(sizeof(*prevedge)*me->totedge, "SeamPathPrevious");
|
|
cost = MEM_mallocN(sizeof(*cost)*me->totedge, "SeamPathCost");
|
|
|
|
/* count edges, compute adjacent edges offsets and fill adjacent edges */
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++) {
|
|
nedges[med->v1+1]++;
|
|
nedges[med->v2+1]++;
|
|
}
|
|
|
|
for (a=1; a<me->totvert; a++) {
|
|
int newswap = nedges[a+1];
|
|
nedges[a+1] = nedgeswap + nedges[a];
|
|
nedgeswap = newswap;
|
|
}
|
|
nedges[0] = nedges[1] = 0;
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++) {
|
|
edges[nedges[med->v1+1]++] = a;
|
|
edges[nedges[med->v2+1]++] = a;
|
|
|
|
cost[a] = 1e20f;
|
|
prevedge[a] = -1;
|
|
}
|
|
|
|
/* regular dijkstra shortest path, but over edges instead of vertices */
|
|
heap = BLI_heap_new();
|
|
BLI_heap_insert(heap, 0.0f, (void*)source);
|
|
cost[source] = 0.0f;
|
|
|
|
while (!BLI_heap_empty(heap)) {
|
|
mednum = (int)BLI_heap_popmin(heap);
|
|
med = me->medge + mednum;
|
|
|
|
if (mednum == target)
|
|
break;
|
|
|
|
if (med->flag & ME_SEAM_DONE)
|
|
continue;
|
|
|
|
med->flag |= ME_SEAM_DONE;
|
|
|
|
seam_add_adjacent(me, heap, mednum, med->v1, nedges, edges, prevedge, cost);
|
|
seam_add_adjacent(me, heap, mednum, med->v2, nedges, edges, prevedge, cost);
|
|
}
|
|
|
|
MEM_freeN(nedges);
|
|
MEM_freeN(edges);
|
|
MEM_freeN(cost);
|
|
BLI_heap_free(heap, NULL);
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++)
|
|
med->flag &= ~ME_SEAM_DONE;
|
|
|
|
if (mednum != target) {
|
|
MEM_freeN(prevedge);
|
|
return 0;
|
|
}
|
|
|
|
/* follow path back to source and mark as seam */
|
|
if (mednum == target) {
|
|
short allseams = 1;
|
|
|
|
mednum = target;
|
|
do {
|
|
med = me->medge + mednum;
|
|
if (!(med->flag & ME_SEAM)) {
|
|
allseams = 0;
|
|
break;
|
|
}
|
|
mednum = prevedge[mednum];
|
|
} while (mednum != source);
|
|
|
|
mednum = target;
|
|
do {
|
|
med = me->medge + mednum;
|
|
if (allseams)
|
|
med->flag &= ~ME_SEAM;
|
|
else
|
|
med->flag |= ME_SEAM;
|
|
mednum = prevedge[mednum];
|
|
} while (mednum != -1);
|
|
}
|
|
|
|
MEM_freeN(prevedge);
|
|
return 1;
|
|
}
|
|
|
|
static void seam_select(Mesh *me, short *mval, short path)
|
|
{
|
|
unsigned int index = 0;
|
|
MEdge *medge, *med;
|
|
int a, lastindex = -1;
|
|
|
|
if (!facesel_edge_pick(me, mval, &index))
|
|
return;
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++) {
|
|
if (med->flag & ME_SEAM_LAST) {
|
|
lastindex = a;
|
|
med->flag &= ~ME_SEAM_LAST;
|
|
break;
|
|
}
|
|
}
|
|
|
|
medge = me->medge + index;
|
|
if (!path || (lastindex == -1) || (index == lastindex) ||
|
|
!seam_shortest_path(me, lastindex, index))
|
|
medge->flag ^= ME_SEAM;
|
|
medge->flag |= ME_SEAM_LAST;
|
|
|
|
G.f |= G_DRAWSEAMS;
|
|
|
|
if (G.rt == 8)
|
|
unwrap_lscm(1);
|
|
|
|
BIF_undo_push("Mark Seam");
|
|
|
|
object_tface_flags_changed(OBACT, 1);
|
|
}
|
|
|
|
void seam_edgehash_insert_face(EdgeHash *ehash, MFace *mf)
|
|
{
|
|
BLI_edgehash_insert(ehash, mf->v1, mf->v2, NULL);
|
|
BLI_edgehash_insert(ehash, mf->v2, mf->v3, NULL);
|
|
if (mf->v4) {
|
|
BLI_edgehash_insert(ehash, mf->v3, mf->v4, NULL);
|
|
BLI_edgehash_insert(ehash, mf->v4, mf->v1, NULL);
|
|
}
|
|
else
|
|
BLI_edgehash_insert(ehash, mf->v3, mf->v1, NULL);
|
|
}
|
|
|
|
void seam_mark_clear_tface(short mode)
|
|
{
|
|
Mesh *me;
|
|
TFace *tf;
|
|
MFace *mf;
|
|
MEdge *med;
|
|
int a;
|
|
|
|
me= get_mesh(OBACT);
|
|
if(me==0 || me->tface==0 || me->totface==0) return;
|
|
|
|
if (mode == 0)
|
|
mode = pupmenu("Seams%t|Mark Border Seam %x1|Clear Seam %x2");
|
|
|
|
if (mode != 1 && mode != 2)
|
|
return;
|
|
|
|
if (mode == 2) {
|
|
EdgeHash *ehash = BLI_edgehash_new();
|
|
|
|
for (a=0, mf=me->mface, tf=me->tface; a<me->totface; a++, tf++, mf++)
|
|
if (!(tf->flag & TF_HIDE) && (tf->flag & TF_SELECT))
|
|
seam_edgehash_insert_face(ehash, mf);
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++)
|
|
if (BLI_edgehash_haskey(ehash, med->v1, med->v2))
|
|
med->flag &= ~ME_SEAM;
|
|
|
|
BLI_edgehash_free(ehash, NULL);
|
|
}
|
|
else {
|
|
/* mark edges that are on both selected and deselected faces */
|
|
EdgeHash *ehash1 = BLI_edgehash_new();
|
|
EdgeHash *ehash2 = BLI_edgehash_new();
|
|
|
|
for (a=0, mf=me->mface, tf=me->tface; a<me->totface; a++, tf++, mf++) {
|
|
if ((tf->flag & TF_HIDE) || !(tf->flag & TF_SELECT))
|
|
seam_edgehash_insert_face(ehash1, mf);
|
|
else
|
|
seam_edgehash_insert_face(ehash2, mf);
|
|
}
|
|
|
|
for (a=0, med=me->medge; a<me->totedge; a++, med++)
|
|
if (BLI_edgehash_haskey(ehash1, med->v1, med->v2) &&
|
|
BLI_edgehash_haskey(ehash2, med->v1, med->v2))
|
|
med->flag |= ME_SEAM;
|
|
|
|
BLI_edgehash_free(ehash1, NULL);
|
|
BLI_edgehash_free(ehash2, NULL);
|
|
}
|
|
|
|
if (G.rt == 8)
|
|
unwrap_lscm(1);
|
|
|
|
G.f |= G_DRAWSEAMS;
|
|
BIF_undo_push("Mark Seam");
|
|
|
|
object_tface_flags_changed(OBACT, 1);
|
|
}
|
|
|
|
void face_select()
|
|
{
|
|
Object *ob;
|
|
Mesh *me;
|
|
TFace *tface, *tsel;
|
|
MFace *msel;
|
|
short mval[2];
|
|
unsigned 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);
|
|
|
|
if (G.qual & LR_ALTKEY) {
|
|
seam_select(me, mval, (G.qual & LR_SHIFTKEY) != 0);
|
|
return;
|
|
}
|
|
|
|
if (!facesel_face_pick(me, mval, &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;
|
|
|
|
/* image window redraw */
|
|
|
|
BIF_undo_push("Select UV face");
|
|
|
|
object_tface_flags_changed(OBACT, 1);
|
|
}
|
|
|
|
void face_borderselect()
|
|
{
|
|
Mesh *me;
|
|
TFace *tface;
|
|
rcti rect;
|
|
struct ImBuf *ibuf;
|
|
unsigned int *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;
|
|
|
|
ibuf = IMB_allocImBuf(sx,sy,32,IB_rect,0);
|
|
rt = ibuf->rect;
|
|
glReadPixels(rect.xmin+curarea->winrct.xmin, rect.ymin+curarea->winrct.ymin, sx, sy, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
|
|
if(G.order==B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
IMB_freeImBuf(ibuf);
|
|
MEM_freeN(selar);
|
|
|
|
BIF_undo_push("Border Select UV face");
|
|
|
|
object_tface_flags_changed(OBACT, 0);
|
|
}
|
|
#ifdef __APPLE__
|
|
glReadBuffer(GL_BACK);
|
|
#endif
|
|
}
|
|
|
|
void uv_autocalc_tface()
|
|
{
|
|
short mode;
|
|
mode= pupmenu(MENUTITLE("UV Calculation")
|
|
MENUSTRING("Cube Projection", UV_CUBE_MAPPING) "|"
|
|
MENUSTRING("Cylinder from View", UV_CYL_MAPPING) "|"
|
|
MENUSTRING("Sphere from View", UV_SPHERE_MAPPING) "|"
|
|
MENUSTRING("Unwrap", UV_UNWRAP_MAPPING) "|"
|
|
MENUSTRING("Project From View", UV_WINDOW_MAPPING) "|"
|
|
MENUSTRING("Project from View 1/1", UV_BOUNDS1_MAPPING) "|"
|
|
MENUSTRING("Project from View 1/2", UV_BOUNDS2_MAPPING) "|"
|
|
MENUSTRING("Project from View 1/4", UV_BOUNDS4_MAPPING) "|"
|
|
MENUSTRING("Project from View 1/8", UV_BOUNDS8_MAPPING) "|"
|
|
MENUSTRING("Reset 1/1", UV_STD1_MAPPING) "|"
|
|
MENUSTRING("Reset 1/2", UV_STD2_MAPPING) "|"
|
|
MENUSTRING("Reset 1/4", UV_STD4_MAPPING) "|"
|
|
MENUSTRING("Reset 1/8", UV_STD8_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_UNWRAP_MAPPING:
|
|
unwrap_lscm(0); break;
|
|
}
|
|
}
|
|
|
|
void set_faceselect() /* toggle */
|
|
{
|
|
Object *ob = OBACT;
|
|
Mesh *me = 0;
|
|
|
|
if(ob==NULL) return;
|
|
if(ob->id.lib) {
|
|
error("Can't edit library data");
|
|
return;
|
|
}
|
|
|
|
me= get_mesh(ob);
|
|
if(me && me->id.lib) {
|
|
error("Can't edit library data");
|
|
return;
|
|
}
|
|
|
|
scrarea_queue_headredraw(curarea);
|
|
|
|
if(me) /* make sure modifiers are updated for mapping requirements */
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
|
|
if(G.f & G_FACESELECT) {
|
|
G.f &= ~G_FACESELECT;
|
|
|
|
if((G.f & (G_WEIGHTPAINT|G_VERTEXPAINT|G_TEXTUREPAINT))==0) {
|
|
if(me)
|
|
reveal_tface();
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_STD);
|
|
BIF_undo_push("End UV Faceselect");
|
|
}
|
|
}
|
|
else if (me && (ob->lay & G.vd->lay)) {
|
|
G.f |= G_FACESELECT;
|
|
if(me->tface==NULL)
|
|
make_tfaces(me);
|
|
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_FACESEL);
|
|
BIF_undo_push("Set UV Faceselect");
|
|
}
|
|
|
|
countall();
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWIMAGE, 0);
|
|
}
|
|
|
|
/* Texture Paint */
|
|
|
|
void set_texturepaint() /* toggle */
|
|
{
|
|
Object *ob = OBACT;
|
|
Mesh *me = 0;
|
|
|
|
scrarea_queue_headredraw(curarea);
|
|
if(ob==NULL) return;
|
|
if(ob->id.lib) {
|
|
error("Can't edit library data");
|
|
return;
|
|
}
|
|
|
|
me= get_mesh(ob);
|
|
if(me && me->id.lib) {
|
|
error("Can't edit library data");
|
|
return;
|
|
}
|
|
|
|
if(me)
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
|
|
if(G.f & G_TEXTUREPAINT)
|
|
G.f &= ~G_TEXTUREPAINT;
|
|
else if (me) {
|
|
G.f |= G_TEXTUREPAINT;
|
|
brush_check_exists(&G.scene->toolsettings->imapaint.brush);
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
|
|
/* Get the barycentric coordinates of 2d point p in 2d triangle (v1, v2, v3) */
|
|
static void texpaint_barycentric_2d(float *v1, float *v2, float *v3, float *p, float *w)
|
|
{
|
|
float b[2], c[2], h[2], div;
|
|
|
|
Vec2Subf(b, v1, v3);
|
|
Vec2Subf(c, v2, v3);
|
|
Vec2Subf(h, p, v3);
|
|
|
|
div= b[0]*c[1] - b[1]*c[0];
|
|
|
|
if (div == 0.0) {
|
|
w[0]= w[1]= w[2]= 1.0f/3.0f;
|
|
}
|
|
else {
|
|
div = 1.0/div;
|
|
w[0] = (h[0]*c[1] - h[1]*c[0])*div;
|
|
w[1] = (b[0]*h[1] - b[1]*h[0])*div;
|
|
w[2] = 1.0 - w[0] - w[1];
|
|
}
|
|
}
|
|
|
|
/* Get 2d vertex coordinates of tface projected onto screen */
|
|
static void texpaint_project(Object *ob, double *model, double *proj, GLint *view, float *co, float *pco)
|
|
{
|
|
float obco[3];
|
|
double winx, winy, winz;
|
|
|
|
VecCopyf(obco, co);
|
|
Mat4MulVecfl(ob->obmat, obco);
|
|
gluProject(obco[0], obco[1], obco[2], model, proj, view, &winx, &winy, &winz);
|
|
|
|
pco[0]= (float)winx;
|
|
pco[1]= (float)winy;
|
|
}
|
|
|
|
static int texpaint_projected_verts(Object *ob, Mesh *mesh, TFace *tf, float *v1, float *v2, float *v3, float *v4)
|
|
{
|
|
MFace *mf = mesh->mface + (tf - mesh->tface);
|
|
double model[16], proj[16];
|
|
GLint view[4];
|
|
|
|
persp(PERSP_VIEW);
|
|
|
|
/* get the need opengl matrices */
|
|
glGetIntegerv(GL_VIEWPORT, view);
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, model);
|
|
glGetDoublev(GL_PROJECTION_MATRIX, proj);
|
|
view[0] = view[1] = 0;
|
|
|
|
/* project the verts */
|
|
texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v1)->co, v1);
|
|
texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v2)->co, v2);
|
|
texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v3)->co, v3);
|
|
if(mf->v4)
|
|
texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v4)->co, v4);
|
|
|
|
return (mf->v4? 4: 3);
|
|
}
|
|
|
|
/* compute uv coordinates of mouse in face */
|
|
void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *uv)
|
|
{
|
|
float v1[2], v2[2], v3[2], v4[2], p[2], w[3];
|
|
int nvert;
|
|
|
|
/* compute barycentric coordinates of point in face and interpolate uv's.
|
|
it's ok to compute the barycentric coords on the projected positions,
|
|
because they are invariant under affine transform */
|
|
nvert= texpaint_projected_verts(ob, mesh, tf, v1, v2, v3, v4);
|
|
|
|
p[0]= xy[0];
|
|
p[1]= xy[1];
|
|
|
|
if (nvert == 4) {
|
|
texpaint_barycentric_2d(v1, v2, v4, p, w);
|
|
|
|
if(w[0] < 0.0f) {
|
|
/* if w[0] is negative, co is on the other side of the v1-v3 edge,
|
|
so we interpolate using the other triangle */
|
|
texpaint_barycentric_2d(v2, v3, v4, p, w);
|
|
|
|
uv[0]= tf->uv[1][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
|
|
uv[1]= tf->uv[1][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
|
|
}
|
|
else {
|
|
uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[3][0]*w[2];
|
|
uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[3][1]*w[2];
|
|
}
|
|
}
|
|
else {
|
|
texpaint_barycentric_2d(v1, v2, v3, p, w);
|
|
uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
|
|
uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
|
|
}
|
|
}
|
|
|
|
/* 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");
|
|
|
|
object_tface_flags_changed(OBACT, 0);
|
|
}
|
|
|