diff options
author | Ton Roosendaal <ton@blender.org> | 2008-12-30 16:16:14 +0300 |
---|---|---|
committer | Ton Roosendaal <ton@blender.org> | 2008-12-30 16:16:14 +0300 |
commit | 25fac7b001aaa03f6341a0c04e08798a01379ca3 (patch) | |
tree | 1606ead91f45c805dd5035990ea04e75cbe87682 /source/blender/editors | |
parent | 446492c2669773e34dc2d827648021f9115dc821 (diff) |
2.5
Editmesh code cleaned and compiling/linking. A whopping
20k lines back! :)
Not that it does stuff... editmode in/out has to be done,
and loads of operators. Also linking/exporting editmesh
calls has to be reviewed.
Also: added a blender_test_break() mechanism in BKE.
Diffstat (limited to 'source/blender/editors')
19 files changed, 20173 insertions, 64 deletions
diff --git a/source/blender/editors/Makefile b/source/blender/editors/Makefile index dd3b0295b22..1dd69bb1d1f 100644 --- a/source/blender/editors/Makefile +++ b/source/blender/editors/Makefile @@ -29,6 +29,6 @@ # Bounces make to subdirectories. SOURCEDIR = source/blender/editors -DIRS = animation object datafiles transform screen space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer +DIRS = mesh animation object datafiles transform screen space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer include nan_subdirs.mk diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 882ced8a27b..9e01857d063 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -28,6 +28,60 @@ #ifndef ED_MESH_H #define ED_MESH_H +struct View3D; + +// edge and face flag both +#define EM_FGON 2 +// face flag +#define EM_FGON_DRAW 1 + +/* editbutflag */ +#define B_CLOCKWISE 1 +#define B_KEEPORIG 2 +#define B_BEAUTY 4 +#define B_SMOOTH 8 +#define B_BEAUTY_SHORT 16 +#define B_AUTOFGON 32 +#define B_KNIFE 0x80 +#define B_PERCENTSUBD 0x40 +#define B_MESH_X_MIRROR 0x100 +#define B_JOINTRIA_UV 0x200 +#define B_JOINTRIA_VCOL 0X400 +#define B_JOINTRIA_SHARP 0X800 +#define B_JOINTRIA_MAT 0X1000 + + +/* editmesh.c */ + +void EM_init_index_arrays(EditMesh *em, int forVert, int forEdge, int forFace); +void EM_free_index_arrays(void); +EditVert *EM_get_vert_for_index(int index); +EditEdge *EM_get_edge_for_index(int index); +EditFace *EM_get_face_for_index(int index); +int EM_texFaceCheck(EditMesh *em); +int EM_vertColorCheck(EditMesh *em); + + +/* editmesh_lib.c */ + +EditFace *EM_get_actFace(EditMesh *em, int sloppy); + +void EM_select_edge(EditEdge *eed, int sel); +void EM_select_face_fgon(EditMesh *em, EditFace *efa, int val); +void EM_selectmode_flush(EditMesh *em); +void EM_deselect_flush(EditMesh *em); + + + +/* editmesh_mods.c */ +extern unsigned int em_vertoffs, em_solidoffs, em_wireoffs; + +int EM_check_backbuf(unsigned int index); +int EM_mask_init_backbuf_border(struct View3D *v3d, short mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax); +void EM_free_backbuf(void); +int EM_init_backbuf_border(struct View3D *v3d, short xmin, short ymin, short xmax, short ymax); +int EM_init_backbuf_circle(struct View3D *v3d, short xs, short ys, short rads); + #endif /* ED_MESH_H */ diff --git a/source/blender/editors/include/ED_multires.h b/source/blender/editors/include/ED_multires.h new file mode 100644 index 00000000000..e4726c02d7e --- /dev/null +++ b/source/blender/editors/include/ED_multires.h @@ -0,0 +1,59 @@ +/* + * $Id$ + * + * ***** BEGIN GPL 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. + * + * 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) 2006 by Nicholas Bishop + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef MULTIRES_H +#define MULTIRES_H + +struct CustomData; +struct EditMesh; +struct Object; +struct MDeformVert; +struct Mesh; +struct MultiresLevel; +struct Multires; +struct uiBlock; + +/* For canceling operations that don't work with multires on or on a non-base level */ +int multires_test(); +int multires_level1_test(); + +void multires_draw_interface(struct uiBlock *block, unsigned short cx, unsigned short cy); + +void multires_make(void *ob, void *me); +void multires_delete(void *ob, void *me); +void multires_level_to_editmesh(struct Object *ob, struct Mesh *me, const int render); +void multires_finish_mesh_update(struct Object *ob); +void multires_subdivide(void *ob, void *me); +void multires_del_lower(void *ob, void *me); +void multires_del_higher(void *ob, void *me); +void multires_set_level_cb(void *ob, void *me); +void multires_edge_level_update_cb(void *ob, void *me); +int multires_modifier_warning(); + +#endif diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 3665c71da16..ee79a259d5a 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -29,6 +29,7 @@ #define ED_OBJECT_H struct wmWindowManager; +struct Scene; struct Object; struct bContext; struct Base; @@ -41,6 +42,9 @@ void ED_base_object_select(struct Base *base, short mode); /* includes notifier */ void ED_base_object_activate(struct bContext *C, struct Base *base); +void ED_base_object_free_and_unlink(struct Scene *scene, struct Base *base); + + /* cleanup */ int object_data_is_libdata(struct Object *ob); int object_is_libdata(struct Object *ob); diff --git a/source/blender/editors/mesh/Makefile b/source/blender/editors/mesh/Makefile index b2aeaa145f7..daa5702f1fd 100644 --- a/source/blender/editors/mesh/Makefile +++ b/source/blender/editors/mesh/Makefile @@ -28,17 +28,19 @@ # # Makes module object directory and bounces make to subdirectories. -LIBNAME = ed_screen +LIBNAME = ed_mesh DIR = $(OCGDIR)/blender/$(LIBNAME) include nan_compile.mk CFLAGS += $(LEVEL_1_C_WARNINGS) +CPPFLAGS += -I$(NAN_GLEW)/include CPPFLAGS += -I$(OPENGL_HEADERS) CPPFLAGS += -I$(NAN_BMFONT)/include CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I$(NAN_ELBEEM)/include CPPFLAGS += -I../../windowmanager CPPFLAGS += -I../../blenkernel @@ -46,6 +48,8 @@ CPPFLAGS += -I../../blenloader CPPFLAGS += -I../../blenlib CPPFLAGS += -I../../makesdna CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../gpu +CPPFLAGS += -I../../render/extern/include # own include diff --git a/source/blender/editors/mesh/SConscript b/source/blender/editors/mesh/SConscript index e69de29bb2d..40b1fa8551a 100644 --- a/source/blender/editors/mesh/SConscript +++ b/source/blender/editors/mesh/SConscript @@ -0,0 +1,11 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' #/intern/guardedalloc #intern/bmfont ../../gpu' +incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern' + +env.BlenderLib ( 'bf_editors_mesh', sources, Split(incs), [], libtype=['core'], priority=[35] ) diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c new file mode 100644 index 00000000000..69d73846710 --- /dev/null +++ b/source/blender/editors/mesh/editface.c @@ -0,0 +1,1439 @@ +/** + * $Id$ + * + * ***** BEGIN GPL 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. + * + * 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. + * + * Contributor(s): Blender Foundation, Campbell Barton + * + * ***** END GPL 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 "BLI_editVert.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_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_object.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" +#include "BKE_customdata.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "GPU_draw.h" + +#ifndef DISABLE_PYTHON +//#include "BPY_extern.h" +//#include "BPY_menus.h" +#endif + +#include "ED_mesh.h" +#include "ED_object.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* own include */ +#include "editmesh.h" + + +/* Pupmenu codes: */ +#define UV_CUBE_MAPPING 2 +#define UV_CYL_MAPPING 3 +#define UV_SPHERE_MAPPING 4 +#define UV_BOUNDS_MAPPING 5 +#define UV_RESET_MAPPING 6 +#define UV_WINDOW_MAPPING 7 +#define UV_UNWRAP_MAPPING 8 +#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|" + +/* ***************** XXX **************** */ +static int sample_backbuf_rect() {return 0;} +static int sample_backbuf() {return 0;} +static void BIF_undo_push() {} +static void error() {} +static int pupmenu() {return 0;} +static void *give_cursor() {return NULL;} +/* ***************** XXX **************** */ + + +/* returns 0 if not found, otherwise 1 */ +int facesel_face_pick(View3D *v3d, Mesh *me, short *mval, unsigned int *index, short rect) +{ + if (!me || me->totface==0) + return 0; + + if (v3d->flag & V3D_NEEDBACKBUFDRAW) { +// XXX drawview.c! check_backbuf(); +// XXX 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 */ + int dist; + *index = sample_backbuf_rect(mval, 3, 1, me->totface+1, &dist,0,NULL); + } + 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; +} + +/* only operates on the edit object - this is all thats needed at the moment */ +static void uv_calc_center_vector(Scene *scene, View3D *v3d, float *result, Object *ob, EditMesh *em) +{ + float min[3], max[3], *cursx; + + EditFace *efa; + switch (v3d->around) + { + case V3D_CENTER: /* bounding box center */ + min[0]= min[1]= min[2]= 1e20f; + max[0]= max[1]= max[2]= -1e20f; + + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + DO_MINMAX(efa->v1->co, min, max); + DO_MINMAX(efa->v2->co, min, max); + DO_MINMAX(efa->v3->co, min, max); + if(efa->v4) DO_MINMAX(efa->v4->co, min, max); + } + } + VecMidf(result, min, max); + break; + case V3D_CURSOR: /*cursor center*/ + cursx= give_cursor(scene, v3d); + /* 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], View3D *v3d, 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,v3d->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(ARegion *ar, View3D *v3d, 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= ar->winx; + dy= ar->winy; + + VecCopyf(pv4, source); + pv4[3] = 1.0; + + /* rotmat is the object matrix in this case */ + Mat4MulVec4fl(rotmat, pv4); + + /* almost project_short */ + Mat4MulVec4fl(v3d->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]; + } + + /* v3d->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]); + } +} + +static void correct_uv_aspect( EditMesh *em ) +{ + float aspx=1, aspy=1; + EditFace *efa = EM_get_actFace(em, 1); + MTFace *tface; + + if (efa) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); +// XXX image_final_aspect(tface->tpage, &aspx, &aspy); + } + + if (aspx != aspy) { + + float scale; + + if (aspx > aspy) { + scale = aspy/aspx; + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tface->uv[0][0] = ((tface->uv[0][0]-0.5)*scale)+0.5; + tface->uv[1][0] = ((tface->uv[1][0]-0.5)*scale)+0.5; + tface->uv[2][0] = ((tface->uv[2][0]-0.5)*scale)+0.5; + if(efa->v4) { + tface->uv[3][0] = ((tface->uv[3][0]-0.5)*scale)+0.5; + } + } + } + } else { + scale = aspx/aspy; + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tface->uv[0][1] = ((tface->uv[0][1]-0.5)*scale)+0.5; + tface->uv[1][1] = ((tface->uv[1][1]-0.5)*scale)+0.5; + tface->uv[2][1] = ((tface->uv[2][1]-0.5)*scale)+0.5; + if(efa->v4) { + tface->uv[3][1] = ((tface->uv[3][1]-0.5)*scale)+0.5; + } + } + } + } + } +} + +static 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; +} + +static void calculate_uv_map(Scene *scene, ARegion *ar, View3D *v3d, EditMesh *em, unsigned short mapmode) +{ + MTFace *tface; + Object *ob; + EditFace *efa; + 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, n; + + if(scene->toolsettings->uvcalc_mapdir==1) { + upangledeg= 90.0; + sideangledeg= 0.0; + } else { + upangledeg= 0.0; + if(scene->toolsettings->uvcalc_mapalign==1) sideangledeg= 0.0; + else sideangledeg= 90.0; + } + + /* add uvs if there not here */ + if (!EM_texFaceCheck(em)) { + if (em && em->faces.first) + EM_add_data_layer(em, &em->fdata, CD_MTFACE); + +// XXX if (G.sima && G.sima->image) /* this is a bit of a kludge, but assume they want the image on their mesh when UVs are added */ +// image_changed(G.sima, G.sima->image); + + if (!EM_texFaceCheck(em)) + return; + + /* select new UV's */ +// XX if ((G.sima && G.sima->flag & SI_SYNC_UVSEL)==0) { +// for(efa=em->faces.first; efa; efa=efa->next) { +// MTFace *tf= (MTFace *)CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); +// simaFaceSel_Set(efa, tf); +// } + + } + + ob=OBACT; + + switch(mapmode) { + case B_UVAUTO_BOUNDS: + min[0]= min[1]= 10000000.0; + max[0]= max[1]= -10000000.0; + + cent[0] = cent[1] = cent[2] = 0.0; + uv_calc_map_matrix(rotatematrix, v3d, ob, upangledeg, sideangledeg, 1.0f); + + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv_calc_shift_project(ar, v3d, tface->uv[0],cent,rotatematrix,3, efa->v1->co, min,max); + uv_calc_shift_project(ar, v3d, tface->uv[1],cent,rotatematrix,3, efa->v2->co, min,max); + uv_calc_shift_project(ar, v3d, tface->uv[2],cent,rotatematrix,3, efa->v3->co,min,max); + if(efa->v4) + uv_calc_shift_project(ar, v3d, tface->uv[3],cent,rotatematrix,3, efa->v4->co,min,max); + } + } + + /* rescale UV to be in 1/1 */ + dx= (max[0]-min[0]); + dy= (max[1]-min[1]); + + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(efa->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); + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv_calc_shift_project(ar, v3d, tface->uv[0],cent,rotatematrix,4, efa->v1->co, NULL,NULL); + uv_calc_shift_project(ar, v3d, tface->uv[1],cent,rotatematrix,4, efa->v2->co, NULL,NULL); + uv_calc_shift_project(ar, v3d, tface->uv[2],cent,rotatematrix,4, efa->v3->co, NULL,NULL); + if(efa->v4) + uv_calc_shift_project(ar, v3d, tface->uv[3],cent,rotatematrix,4, efa->v4->co, NULL,NULL); + } + } + break; + + case B_UVAUTO_RESET: + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + default_uv(tface->uv, 1.0); + } + } + break; + + case B_UVAUTO_CYLINDER: + case B_UVAUTO_SPHERE: + uv_calc_center_vector(scene, v3d, cent, ob, em); + + if(mapmode==B_UVAUTO_CYLINDER) radius = scene->toolsettings->uvcalc_radius; + + /* be compatible to the "old" sphere/cylinder mode */ + if (scene->toolsettings->uvcalc_mapdir== 2) + Mat4One(rotatematrix); + else + uv_calc_map_matrix(rotatematrix, v3d, ob, upangledeg,sideangledeg,radius); + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv_calc_shift_project(ar, v3d, tface->uv[0],cent,rotatematrix,mapmode, efa->v1->co, NULL,NULL); + uv_calc_shift_project(ar, v3d, tface->uv[1],cent,rotatematrix,mapmode, efa->v2->co, NULL,NULL); + uv_calc_shift_project(ar, v3d, tface->uv[2],cent,rotatematrix,mapmode, efa->v3->co, NULL,NULL); + n = 3; + if(efa->v4) { + uv_calc_shift_project(ar, v3d, tface->uv[3],cent,rotatematrix,mapmode, efa->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 = scene->toolsettings->uvcalc_cubesize; + + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + CalcNormFloat(efa->v1->co, efa->v2->co, efa->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] + efa->v1->co[cox]); + tface->uv[0][1]= 0.5+0.5*cubesize*(loc[coy] + efa->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] + efa->v2->co[cox]); + tface->uv[1][1]= 0.5+0.5*cubesize*(loc[coy] + efa->v2->co[coy]); + tface->uv[1][0] -= dx; + tface->uv[1][1] -= dy; + tface->uv[2][0]= 0.5+0.5*cubesize*(loc[cox] + efa->v3->co[cox]); + tface->uv[2][1]= 0.5+0.5*cubesize*(loc[coy] + efa->v3->co[coy]); + tface->uv[2][0] -= dx; + tface->uv[2][1] -= dy; + if(efa->v4) { + tface->uv[3][0]= 0.5+0.5*cubesize*(loc[cox] + efa->v4->co[cox]); + tface->uv[3][1]= 0.5+0.5*cubesize*(loc[coy] + efa->v4->co[coy]); + tface->uv[3][0] -= dx; + tface->uv[3][1] -= dy; + } + } + } + break; + } + default: + if ((scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT)==0) + correct_uv_aspect(em); + return; + } /* end switch mapmode */ + + /* clipping and wrapping */ + if(0) { // XXX (make it uv layer property!) G.sima && G.sima->flag & SI_CLIP_UV) { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->f & SELECT)) continue; + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + dx= dy= 0; + if(efa->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(efa->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); + } + } + } + + if ( (mapmode!=B_UVAUTO_BOUNDS) && + (mapmode!=B_UVAUTO_RESET) && + (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT)==0 + ) { + correct_uv_aspect(em); + } + + BIF_undo_push("UV calculation"); + +// XXX notifier object_uvs_changed(OBACT); + +} + +/* last_sel, use em->act_face otherwise get the last selected face in the editselections + * at the moment, last_sel is mainly useful for gaking sure the space image dosnt flicker */ +MTFace *get_active_mtface(EditMesh *em, EditFace **act_efa, MCol **mcol, int sloppy) +{ + EditFace *efa = NULL; + + if(!EM_texFaceCheck(em)) + return NULL; + + efa = EM_get_actFace(em, sloppy); + + if (efa) { + if (mcol) { + if (CustomData_has_layer(&em->fdata, CD_MCOL)) + *mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + else + *mcol = NULL; + } + if (act_efa) *act_efa = efa; + return CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + } + if (act_efa) *act_efa= NULL; + if(mcol) *mcol = NULL; + return NULL; +} + +void make_tfaces(Mesh *me) +{ + if(!me->mtface) { + if(me->mr) { + multires_add_layer(me, &me->mr->fdata, CD_MTFACE, + CustomData_number_of_layers(&me->fdata, CD_MTFACE)); + } + else { + me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, + NULL, me->totface); + } + } +} + +void reveal_tface(Scene *scene) +{ + Mesh *me; + MFace *mface; + int a; + + me= get_mesh(OBACT); + if(me==0 || me->totface==0) return; + + mface= me->mface; + a= me->totface; + while(a--) { + if(mface->flag & ME_HIDE) { + mface->flag |= ME_FACE_SEL; + mface->flag -= ME_HIDE; + } + mface++; + } + + BIF_undo_push("Reveal face"); + +// XXX notifier! object_tface_flags_changed(OBACT, 0); +} + +void hide_tface(Scene *scene) +{ + Mesh *me; + MFace *mface; + int a; + int shift=0, alt= 0; // XXX + + me= get_mesh(OBACT); + if(me==0 || me->totface==0) return; + + if(alt) { + reveal_tface(scene); + return; + } + + mface= me->mface; + a= me->totface; + while(a--) { + if(mface->flag & ME_HIDE); + else { + if(shift) { + if( (mface->flag & ME_FACE_SEL)==0) mface->flag |= ME_HIDE; + } + else { + if( (mface->flag & ME_FACE_SEL)) mface->flag |= ME_HIDE; + } + } + if(mface->flag & ME_HIDE) mface->flag &= ~ME_FACE_SEL; + + mface++; + } + + BIF_undo_push("Hide face"); + +// XXX notifier! object_tface_flags_changed(OBACT, 0); +} + +void select_linked_tfaces(Scene *scene, View3D *v3d, int mode) +{ + Object *ob; + Mesh *me; + short mval[2]; + unsigned int index=0; + + ob = OBACT; + me = get_mesh(ob); + if(me==0 || me->totface==0) return; + + if (mode==0 || mode==1) { + if (!(ob->lay & v3d->lay)) + error("The active object is not in this layer"); + +// XXX getmouseco_areawin(mval); + if (!facesel_face_pick(v3d, me, mval, &index, 1)) return; + } + +// XXX unwrapper.c select_linked_tfaces_with_seams(mode, me, index); +} + +void deselectall_tface(Scene *scene) +{ + Mesh *me; + MFace *mface; + int a, sel; + + me= get_mesh(OBACT); + if(me==0) return; + + mface= me->mface; + a= me->totface; + sel= 0; + while(a--) { + if(mface->flag & ME_HIDE); + else if(mface->flag & ME_FACE_SEL) sel= 1; + mface++; + } + + mface= me->mface; + a= me->totface; + while(a--) { + if(mface->flag & ME_HIDE); + else { + if(sel) mface->flag &= ~ME_FACE_SEL; + else mface->flag |= ME_FACE_SEL; + } + mface++; + } + + BIF_undo_push("(De)select all faces"); + +// XXX notifier! object_tface_flags_changed(OBACT, 0); +} + +void selectswap_tface(Scene *scene) +{ + Mesh *me; + MFace *mface; + int a; + + me= get_mesh(OBACT); + if(me==0) return; + + mface= me->mface; + a= me->totface; + while(a--) { + if(mface->flag & ME_HIDE); + else { + if(mface->flag & ME_FACE_SEL) mface->flag &= ~ME_FACE_SEL; + else mface->flag |= ME_FACE_SEL; + } + mface++; + } + + BIF_undo_push("Select inverse face"); + +// XXX notifier! object_tface_flags_changed(OBACT, 0); +} + +int minmax_tface(Scene *scene, float *min, float *max) +{ + Object *ob; + Mesh *me; + MFace *mf; + MTFace *tf; + MVert *mv; + int a, ok=0; + float vec[3], bmat[3][3]; + + ob = OBACT; + if (ob==0) return ok; + me= get_mesh(ob); + if(me==0 || me->mtface==0) return ok; + + Mat3CpyMat4(bmat, ob->obmat); + + mv= me->mvert; + mf= me->mface; + tf= me->mtface; + for (a=me->totface; a>0; a--, mf++, tf++) { + if (mf->flag & ME_HIDE || !(mf->flag & ME_FACE_SEL)) + 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); + } + ok= 1; + } + return ok; +} + +#define ME_SEAM_DONE 2 /* reuse this flag */ + +static float edgetag_cut_cost(EditMesh *em, int e1, int e2, int vert) +{ + EditVert *v = EM_get_vert_for_index(vert); + EditEdge *eed1 = EM_get_edge_for_index(e1), *eed2 = EM_get_edge_for_index(e2); + EditVert *v1 = EM_get_vert_for_index( (eed1->v1->tmp.l == vert)? eed1->v2->tmp.l: eed1->v1->tmp.l ); + EditVert *v2 = EM_get_vert_for_index( (eed2->v1->tmp.l == vert)? eed2->v2->tmp.l: eed2->v1->tmp.l ); + 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 edgetag_add_adjacent(EditMesh *em, 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]; + EditEdge *eedadj = EM_get_edge_for_index(adjnum); + float newcost; + + if (eedadj->f2 & ME_SEAM_DONE) + continue; + + newcost = cost[mednum] + edgetag_cut_cost(em, mednum, adjnum, vertnum); + + if (cost[adjnum] > newcost) { + cost[adjnum] = newcost; + prevedge[adjnum] = mednum; + BLI_heap_insert(heap, newcost, SET_INT_IN_POINTER(adjnum)); + } + } +} + +void edgetag_context_set(Scene *scene, EditEdge *eed, int val) +{ + switch (scene->toolsettings->edge_mode) { + case EDGE_MODE_TAG_SEAM: + if (val) {eed->seam = 255;} + else {eed->seam = 0;} + break; + case EDGE_MODE_TAG_SHARP: + if (val) {eed->sharp = 1;} + else {eed->sharp = 0;} + break; + case EDGE_MODE_TAG_CREASE: + if (val) {eed->crease = 1.0f;} + else {eed->crease = 0.0f;} + break; + case EDGE_MODE_TAG_BEVEL: + if (val) {eed->bweight = 1.0f;} + else {eed->bweight = 0.0f;} + break; + } +} + +int edgetag_context_check(Scene *scene, EditEdge *eed) +{ + switch (scene->toolsettings->edge_mode) { + case EDGE_MODE_TAG_SEAM: + return eed->seam ? 1 : 0; + case EDGE_MODE_TAG_SHARP: + return eed->sharp ? 1 : 0; + case EDGE_MODE_TAG_CREASE: + return eed->crease ? 1 : 0; + case EDGE_MODE_TAG_BEVEL: + return eed->bweight ? 1 : 0; + } + return 0; +} + + +int edgetag_shortest_path(Scene *scene, EditMesh *em, EditEdge *source, EditEdge *target) +{ + EditEdge *eed; + EditVert *ev; + + Heap *heap; + float *cost; + int a, totvert=0, totedge=0, *nedges, *edges, *prevedge, mednum = -1, nedgeswap = 0; + + + /* we need the vert */ + for (ev= em->verts.first, totvert=0; ev; ev= ev->next) { + ev->tmp.l = totvert; + totvert++; + } + + for (eed= em->edges.first; eed; eed = eed->next) { + eed->f2 = 0; + if (eed->h) { + eed->f2 |= ME_SEAM_DONE; + } + eed->tmp.l = totedge; + totedge++; + } + + /* alloc */ + nedges = MEM_callocN(sizeof(*nedges)*totvert+1, "SeamPathNEdges"); + edges = MEM_mallocN(sizeof(*edges)*totedge*2, "SeamPathEdges"); + prevedge = MEM_mallocN(sizeof(*prevedge)*totedge, "SeamPathPrevious"); + cost = MEM_mallocN(sizeof(*cost)*totedge, "SeamPathCost"); + + /* count edges, compute adjacent edges offsets and fill adjacent edges */ + for (eed= em->edges.first; eed; eed = eed->next) { + nedges[eed->v1->tmp.l+1]++; + nedges[eed->v2->tmp.l+1]++; + } + + for (a=1; a<totvert; a++) { + int newswap = nedges[a+1]; + nedges[a+1] = nedgeswap + nedges[a]; + nedgeswap = newswap; + } + nedges[0] = nedges[1] = 0; + + for (a=0, eed= em->edges.first; eed; a++, eed = eed->next) { + edges[nedges[eed->v1->tmp.l+1]++] = a; + edges[nedges[eed->v2->tmp.l+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, SET_INT_IN_POINTER(source->tmp.l)); + cost[source->tmp.l] = 0.0f; + + EM_init_index_arrays(em, 1, 1, 0); + + + while (!BLI_heap_empty(heap)) { + mednum = GET_INT_FROM_POINTER(BLI_heap_popmin(heap)); + eed = EM_get_edge_for_index( mednum ); + + if (mednum == target->tmp.l) + break; + + if (eed->f2 & ME_SEAM_DONE) + continue; + + eed->f2 |= ME_SEAM_DONE; + + edgetag_add_adjacent(em, heap, mednum, eed->v1->tmp.l, nedges, edges, prevedge, cost); + edgetag_add_adjacent(em, heap, mednum, eed->v2->tmp.l, nedges, edges, prevedge, cost); + } + + + MEM_freeN(nedges); + MEM_freeN(edges); + MEM_freeN(cost); + BLI_heap_free(heap, NULL); + + for (eed= em->edges.first; eed; eed = eed->next) { + eed->f2 &= ~ME_SEAM_DONE; + } + + if (mednum != target->tmp.l) { + MEM_freeN(prevedge); + EM_free_index_arrays(); + return 0; + } + + /* follow path back to source and mark as seam */ + if (mednum == target->tmp.l) { + short allseams = 1; + + mednum = target->tmp.l; + do { + eed = EM_get_edge_for_index( mednum ); + if (!edgetag_context_check(scene, eed)) { + allseams = 0; + break; + } + mednum = prevedge[mednum]; + } while (mednum != source->tmp.l); + + mednum = target->tmp.l; + do { + eed = EM_get_edge_for_index( mednum ); + if (allseams) + edgetag_context_set(scene, eed, 0); + else + edgetag_context_set(scene, eed, 1); + mednum = prevedge[mednum]; + } while (mednum != -1); + } + + MEM_freeN(prevedge); + EM_free_index_arrays(); + return 1; +} + +static 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(Scene *scene, short mode) +{ + Mesh *me; + MFace *mf; + MEdge *med; + int a; + + me= get_mesh(OBACT); + if(me==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; a<me->totface; a++, mf++) + if (!(mf->flag & ME_HIDE) && (mf->flag & ME_FACE_SEL)) + 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; a<me->totface; a++, mf++) { + if ((mf->flag & ME_HIDE) || !(mf->flag & ME_FACE_SEL)) + 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); + } + +// XXX if (G.rt == 8) +// unwrap_lscm(1); + + G.f |= G_DRAWSEAMS; + BIF_undo_push("Mark Seam"); + +// XXX notifier! object_tface_flags_changed(OBACT, 1); +} + +void face_select(Scene *scene, View3D *v3d) +{ + Object *ob; + Mesh *me; + MFace *mface, *msel; + short mval[2]; + unsigned int a, index; + int shift= 0; // XXX + + /* Get the face under the cursor */ + ob = OBACT; + if (!(ob->lay & v3d->lay)) { + error("The active object is not in this layer"); + } + me = get_mesh(ob); +// XXX getmouseco_areawin(mval); + + if (!facesel_face_pick(v3d, me, mval, &index, 1)) return; + + msel= (((MFace*)me->mface)+index); + if (msel->flag & ME_HIDE) return; + + /* clear flags */ + mface = me->mface; + a = me->totface; + if ((shift)==0) { + while (a--) { + mface->flag &= ~ME_FACE_SEL; + mface++; + } + } + + me->act_face = (int)index; + + if (shift) { + if (msel->flag & ME_FACE_SEL) + msel->flag &= ~ME_FACE_SEL; + else + msel->flag |= ME_FACE_SEL; + } + else msel->flag |= ME_FACE_SEL; + + /* image window redraw */ + + BIF_undo_push("Select UV face"); + +// XXX notifier! object_tface_flags_changed(OBACT, 1); +} + +void face_borderselect(Scene *scene, ARegion *ar) +{ + Mesh *me; + MFace *mface; + rcti rect; + struct ImBuf *ibuf; + unsigned int *rt; + int a, sx, sy, index, val; + char *selar; + + me= get_mesh(OBACT); + if(me==0) return; + if(me->totface==0) return; + +// XXX 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+ar->winrct.xmin, rect.ymin+ar->winrct.ymin, sx, sy, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + if(ENDIAN_ORDER==B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf); + + a= sx*sy; + while(a--) { + if(*rt) { + index= WM_framebuffer_to_index(*rt); + if(index<=me->totface) selar[index]= 1; + } + rt++; + } + + mface= me->mface; + for(a=1; a<=me->totface; a++, mface++) { + if(selar[a]) { + if(mface->flag & ME_HIDE); + else { + if(val==LEFTMOUSE) mface->flag |= ME_FACE_SEL; + else mface->flag &= ~ME_FACE_SEL; + } + } + } + + IMB_freeImBuf(ibuf); + MEM_freeN(selar); + + BIF_undo_push("Border Select UV face"); + +// XXX notifier! object_tface_flags_changed(OBACT, 0); + } +#ifdef __APPLE__ + glReadBuffer(GL_BACK); +#endif +} + +void uv_autocalc_tface(Scene *scene, ARegion *ar, View3D *v3d, EditMesh *em) +{ + short mode; +#ifndef DISABLE_PYTHON +// short i=0, has_pymenu=0; /* pymenu must be bigger then UV_*_MAPPING */ +// XXX BPyMenu *pym; +// char menu_number[3]; +#endif + + /* uvmenu, will add python items */ + char uvmenu[4096]=MENUTITLE("UV Calculation") + MENUSTRING("Unwrap", UV_UNWRAP_MAPPING) "|%l|" + + MENUSTRING("Cube Projection", UV_CUBE_MAPPING) "|" + MENUSTRING("Cylinder from View", UV_CYL_MAPPING) "|" + MENUSTRING("Sphere from View", UV_SPHERE_MAPPING) "|%l|" + + MENUSTRING("Project From View", UV_WINDOW_MAPPING) "|" + MENUSTRING("Project from View (Bounds)",UV_BOUNDS_MAPPING) "|%l|" + + MENUSTRING("Reset", UV_RESET_MAPPING); +#ifndef DISABLE_PYTHON +#if 0 + XXX + /* note that we account for the 10 previous entries with i+10: */ + for (pym = BPyMenuTable[PYMENU_UVCALCULATION]; pym; pym = pym->next, i++) { + + if (!has_pymenu) { + strcat(uvmenu, "|%l"); + has_pymenu = 1; + } + + strcat(uvmenu, "|"); + strcat(uvmenu, pym->name); + strcat(uvmenu, " %x"); + sprintf(menu_number, "%d", i+10); + strcat(uvmenu, menu_number); + } +#endif +#endif + + mode= pupmenu(uvmenu); +#ifndef DISABLE_PYTHON +// if (mode >= 10) { +// BPY_menu_do_python(PYMENU_UVCALCULATION, mode - 10); +// return; +// } +#endif + switch(mode) { + case UV_CUBE_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_CUBE); break; + case UV_CYL_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_CYLINDER); break; + case UV_SPHERE_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_SPHERE); break; + case UV_BOUNDS_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_BOUNDS); break; + case UV_RESET_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_RESET); break; + case UV_WINDOW_MAPPING: + calculate_uv_map(scene, ar, v3d, em, B_UVAUTO_WINDOW); break; + case UV_UNWRAP_MAPPING: +// XXX unwrap_lscm(0); + break; + } +} + +/* Texture Paint */ + +void set_texturepaint(Scene *scene) /* toggle */ +{ + Object *ob = OBACT; + Mesh *me = 0; + + if(ob==NULL) return; + + if (object_data_is_libdata(ob)) { +// XXX error_libdata(); + return; + } + + me= get_mesh(ob); + + if(me) + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + + if(G.f & G_TEXTUREPAINT) { + G.f &= ~G_TEXTUREPAINT; + GPU_paint_set_mipmap(1); + } + else if (me) { + G.f |= G_TEXTUREPAINT; + + if(me->mtface==NULL) + make_tfaces(me); + + brush_check_exists(&scene->toolsettings->imapaint.brush); + GPU_paint_set_mipmap(0); + } + +} + +static void texpaint_project(Object *ob, float *model, float *proj, float *co, float *pco) +{ + VECCOPY(pco, co); + pco[3]= 1.0f; + + Mat4MulVecfl(ob->obmat, pco); + Mat4MulVecfl((float(*)[4])model, pco); + Mat4MulVec4fl((float(*)[4])proj, pco); +} + +static void texpaint_tri_weights(Object *ob, float *v1, float *v2, float *v3, float *co, float *w) +{ + float pv1[4], pv2[4], pv3[4], h[3], divw; + float model[16], proj[16], wmat[3][3], invwmat[3][3]; + GLint view[4]; + + /* compute barycentric coordinates */ + + /* get the needed opengl matrices */ + glGetIntegerv(GL_VIEWPORT, view); + glGetFloatv(GL_MODELVIEW_MATRIX, model); + glGetFloatv(GL_PROJECTION_MATRIX, proj); + view[0] = view[1] = 0; + + /* project the verts */ + texpaint_project(ob, model, proj, v1, pv1); + texpaint_project(ob, model, proj, v2, pv2); + texpaint_project(ob, model, proj, v3, pv3); + + /* do inverse view mapping, see gluProject man page */ + h[0]= (co[0] - view[0])*2.0f/view[2] - 1; + h[1]= (co[1] - view[1])*2.0f/view[3] - 1; + h[2]= 1.0f; + + /* solve for (w1,w2,w3)/perspdiv in: + h*perspdiv = Project*Model*(w1*v1 + w2*v2 + w3*v3) */ + + wmat[0][0]= pv1[0]; wmat[1][0]= pv2[0]; wmat[2][0]= pv3[0]; + wmat[0][1]= pv1[1]; wmat[1][1]= pv2[1]; wmat[2][1]= pv3[1]; + wmat[0][2]= pv1[3]; wmat[1][2]= pv2[3]; wmat[2][2]= pv3[3]; + + Mat3Inv(invwmat, wmat); + Mat3MulVecfl(invwmat, h); + + VECCOPY(w, h); + + /* w is still divided by perspdiv, make it sum to one */ + divw= w[0] + w[1] + w[2]; + if(divw != 0.0f) + VecMulf(w, 1.0f/divw); +} + +/* compute uv coordinates of mouse in face */ +void texpaint_pick_uv(Object *ob, Mesh *mesh, unsigned int faceindex, short *xy, float *uv) +{ + DerivedMesh *dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH); + int *index = dm->getFaceDataArray(dm, CD_ORIGINDEX); + MTFace *tface = dm->getFaceDataArray(dm, CD_MTFACE), *tf; + int numfaces = dm->getNumFaces(dm), a; + float p[2], w[3], absw, minabsw; + MFace mf; + MVert mv[4]; + + minabsw = 1e10; + uv[0] = uv[1] = 0.0; + +// XXX persp(PERSP_VIEW); + + /* test all faces in the derivedmesh with the original index of the picked face */ + for (a = 0; a < numfaces; a++) { + if (index[a] == faceindex) { + dm->getFace(dm, a, &mf); + + dm->getVert(dm, mf.v1, &mv[0]); + dm->getVert(dm, mf.v2, &mv[1]); + dm->getVert(dm, mf.v3, &mv[2]); + if (mf.v4) + dm->getVert(dm, mf.v4, &mv[3]); + + tf= &tface[a]; + + p[0]= xy[0]; + p[1]= xy[1]; + + if (mf.v4) { + /* the triangle with the largest absolute values is the one + with the most negative weights */ + texpaint_tri_weights(ob, mv[0].co, mv[1].co, mv[3].co, p, w); + absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]); + if(absw < minabsw) { + 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]; + minabsw = absw; + } + + texpaint_tri_weights(ob, mv[1].co, mv[2].co, mv[3].co, p, w); + absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]); + if (absw < minabsw) { + 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]; + minabsw = absw; + } + } + else { + texpaint_tri_weights(ob, mv[0].co, mv[1].co, mv[2].co, p, w); + absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]); + if (absw < minabsw) { + 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]; + minabsw = absw; + } + } + } + } + + dm->release(dm); +} diff --git a/source/blender/editors/mesh/editmesh.c b/source/blender/editors/mesh/editmesh.c new file mode 100644 index 00000000000..e64667eee8b --- /dev/null +++ b/source/blender/editors/mesh/editmesh.c @@ -0,0 +1,2190 @@ +/** + * $Id$ + * + * ***** BEGIN GPL 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. + * + * 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. + * + * Contributor(s): Blender Foundation, full recode 2002-2008 + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_screen_types.h" +#include "DNA_key_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" +#include "DNA_material_types.h" +#include "DNA_modifier_types.h" +#include "DNA_texture_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_dynstr.h" +#include "BLI_rand.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_cloth.h" +#include "BKE_customdata.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_object.h" +#include "BKE_pointcache.h" +#include "BKE_softbody.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" + +#include "LBM_fluidsim.h" + +#include "BIF_retopo.h" + +#include "ED_mesh.h" + +/* own include */ +#include "editmesh.h" + +/* +editmesh.c: + - add/alloc/free data + - hashtables + - enter/exit editmode +*/ + +/* XXX */ +static void BIF_undo_push() {} +static void waitcursor() {} +static void error() {} +static int pupmenu() {return 0;} +static void key_to_mesh() {} +static void undo_editmode_clear() {} +static int multires_test() {return 0;} +static void adduplicate() {} +static void *undo_editmode_get_prev() {return NULL;} +static void undo_editmode_push() {} + + +/* ***************** HASH ********************* */ + + +#define EDHASHSIZE (512*512) +#define EDHASH(a, b) (a % EDHASHSIZE) + + +/* ************ ADD / REMOVE / FIND ****************** */ + +static void *calloc_em(EditMesh *em, size_t size, size_t nr) +{ + return calloc(size, nr); +} + +/* used to bypass normal calloc with fast one */ +static void *(*callocvert)(EditMesh *, size_t, size_t) = calloc_em; +static void *(*callocedge)(EditMesh *, size_t, size_t) = calloc_em; +static void *(*callocface)(EditMesh *, size_t, size_t) = calloc_em; + +EditVert *addvertlist(EditMesh *em, float *vec, EditVert *example) +{ + EditVert *eve; + static int hashnr= 0; + + eve= callocvert(em, sizeof(EditVert), 1); + BLI_addtail(&em->verts, eve); + + if(vec) VECCOPY(eve->co, vec); + + eve->hash= hashnr++; + if( hashnr>=EDHASHSIZE) hashnr= 0; + + /* new verts get keyindex of -1 since they did not + * have a pre-editmode vertex order + */ + eve->keyindex = -1; + + if(example) { + CustomData_em_copy_data(&em->vdata, &em->vdata, example->data, &eve->data); + eve->bweight = example->bweight; + } + else { + CustomData_em_set_default(&em->vdata, &eve->data); + } + + return eve; +} + +void free_editvert (EditMesh *em, EditVert *eve) +{ + + EM_remove_selection(em, eve, EDITVERT); + CustomData_em_free_block(&em->vdata, &eve->data); + if(eve->fast==0) + free(eve); +} + + +EditEdge *findedgelist(EditMesh *em, EditVert *v1, EditVert *v2) +{ + EditVert *v3; + struct HashEdge *he; + + /* swap ? */ + if( v1 > v2) { + v3= v2; + v2= v1; + v1= v3; + } + + if(em->hashedgetab==NULL) + em->hashedgetab= MEM_callocN(EDHASHSIZE*sizeof(struct HashEdge), "hashedgetab"); + + he= em->hashedgetab + EDHASH(v1->hash, v2->hash); + + while(he) { + + if(he->eed && he->eed->v1==v1 && he->eed->v2==v2) return he->eed; + + he= he->next; + } + return 0; +} + +static void insert_hashedge(EditMesh *em, EditEdge *eed) +{ + /* assuming that eed is not in the list yet, and that a find has been done before */ + + struct HashEdge *first, *he; + + first= em->hashedgetab + EDHASH(eed->v1->hash, eed->v2->hash); + + if( first->eed==0 ) { + first->eed= eed; + } + else { + he= &eed->hash; + he->eed= eed; + he->next= first->next; + first->next= he; + } +} + +static void remove_hashedge(EditMesh *em, EditEdge *eed) +{ + /* assuming eed is in the list */ + + struct HashEdge *first, *he, *prev=NULL; + + he=first= em->hashedgetab + EDHASH(eed->v1->hash, eed->v2->hash); + + while(he) { + if(he->eed == eed) { + /* remove from list */ + if(he==first) { + if(first->next) { + he= first->next; + first->eed= he->eed; + first->next= he->next; + } + else he->eed= 0; + } + else { + prev->next= he->next; + } + return; + } + prev= he; + he= he->next; + } +} + +EditEdge *addedgelist(EditMesh *em, EditVert *v1, EditVert *v2, EditEdge *example) +{ + EditVert *v3; + EditEdge *eed; + int swap= 0; + + if(v1==v2) return NULL; + if(v1==NULL || v2==NULL) return NULL; + + /* swap ? */ + if(v1>v2) { + v3= v2; + v2= v1; + v1= v3; + swap= 1; + } + + /* find in hashlist */ + eed= findedgelist(em, v1, v2); + + if(eed==NULL) { + + eed= (EditEdge *)callocedge(em, sizeof(EditEdge), 1); + eed->v1= v1; + eed->v2= v2; + BLI_addtail(&em->edges, eed); + eed->dir= swap; + insert_hashedge(em, eed); + + /* copy edge data: + rule is to do this with addedgelist call, before addfacelist */ + if(example) { + eed->crease= example->crease; + eed->bweight= example->bweight; + eed->sharp = example->sharp; + eed->seam = example->seam; + eed->h |= (example->h & EM_FGON); + } + } + + return eed; +} + +void remedge(EditMesh *em, EditEdge *eed) +{ + + BLI_remlink(&em->edges, eed); + remove_hashedge(em, eed); +} + +void free_editedge(EditMesh *em, EditEdge *eed) +{ + EM_remove_selection(em, eed, EDITEDGE); + if(eed->fast==0){ + free(eed); + } +} + +void free_editface(EditMesh *em, EditFace *efa) +{ + + EM_remove_selection(em, efa, EDITFACE); + + if (em->act_face==efa) { + EM_set_actFace(em, em->faces.first == efa ? NULL : em->faces.first); + } + + CustomData_em_free_block(&em->fdata, &efa->data); + if(efa->fast==0) + free(efa); +} + +void free_vertlist(EditMesh *em, ListBase *edve) +{ + EditVert *eve, *next; + + if (!edve) return; + + eve= edve->first; + while(eve) { + next= eve->next; + free_editvert(em, eve); + eve= next; + } + edve->first= edve->last= NULL; +} + +void free_edgelist(EditMesh *em, ListBase *lb) +{ + EditEdge *eed, *next; + + eed= lb->first; + while(eed) { + next= eed->next; + free_editedge(em, eed); + eed= next; + } + lb->first= lb->last= NULL; +} + +void free_facelist(EditMesh *em, ListBase *lb) +{ + EditFace *efa, *next; + + efa= lb->first; + while(efa) { + next= efa->next; + free_editface(em, efa); + efa= next; + } + lb->first= lb->last= NULL; +} + +EditFace *addfacelist(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, EditFace *example, EditFace *exampleEdges) +{ + EditFace *efa; + EditEdge *e1, *e2=0, *e3=0, *e4=0; + + /* added sanity check... seems to happen for some tools, or for enter editmode for corrupted meshes */ + if(v1==v4 || v2==v4 || v3==v4) v4= NULL; + + /* add face to list and do the edges */ + if(exampleEdges) { + e1= addedgelist(em, v1, v2, exampleEdges->e1); + e2= addedgelist(em, v2, v3, exampleEdges->e2); + if(v4) e3= addedgelist(em, v3, v4, exampleEdges->e3); + else e3= addedgelist(em, v3, v1, exampleEdges->e3); + if(v4) e4= addedgelist(em, v4, v1, exampleEdges->e4); + } + else { + e1= addedgelist(em, v1, v2, NULL); + e2= addedgelist(em, v2, v3, NULL); + if(v4) e3= addedgelist(em, v3, v4, NULL); + else e3= addedgelist(em, v3, v1, NULL); + if(v4) e4= addedgelist(em, v4, v1, NULL); + } + + if(v1==v2 || v2==v3 || v1==v3) return NULL; + if(e2==0) return NULL; + + efa= (EditFace *)callocface(em, sizeof(EditFace), 1); + efa->v1= v1; + efa->v2= v2; + efa->v3= v3; + efa->v4= v4; + + efa->e1= e1; + efa->e2= e2; + efa->e3= e3; + efa->e4= e4; + + if(example) { + efa->mat_nr= example->mat_nr; + efa->flag= example->flag; + CustomData_em_copy_data(&em->fdata, &em->fdata, example->data, &efa->data); + } + else { + if (G.obedit && G.obedit->actcol) + efa->mat_nr= G.obedit->actcol-1; + + CustomData_em_set_default(&em->fdata, &efa->data); + } + + BLI_addtail(&em->faces, efa); + + if(efa->v4) { + CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n); + CalcCent4f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co); + } + else { + CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n); + CalcCent3f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co); + } + + return efa; +} + +/* ************************ end add/new/find ************ */ + +/* ************************ Edit{Vert,Edge,Face} utilss ***************************** */ + +/* some nice utility functions */ + +EditVert *editedge_getOtherVert(EditEdge *eed, EditVert *eve) +{ + if (eve==eed->v1) { + return eed->v2; + } else if (eve==eed->v2) { + return eed->v1; + } else { + return NULL; + } +} + +EditVert *editedge_getSharedVert(EditEdge *eed, EditEdge *eed2) +{ + if (eed->v1==eed2->v1 || eed->v1==eed2->v2) { + return eed->v1; + } else if (eed->v2==eed2->v1 || eed->v2==eed2->v2) { + return eed->v2; + } else { + return NULL; + } +} + +int editedge_containsVert(EditEdge *eed, EditVert *eve) +{ + return (eed->v1==eve || eed->v2==eve); +} + +int editface_containsVert(EditFace *efa, EditVert *eve) +{ + return (efa->v1==eve || efa->v2==eve || efa->v3==eve || (efa->v4 && efa->v4==eve)); +} + +int editface_containsEdge(EditFace *efa, EditEdge *eed) +{ + return (efa->e1==eed || efa->e2==eed || efa->e3==eed || (efa->e4 && efa->e4==eed)); +} + + +/* ************************ stuct EditMesh manipulation ***************************** */ + +/* fake callocs for fastmalloc below */ +static void *calloc_fastvert(EditMesh *em, size_t size, size_t nr) +{ + EditVert *eve= em->curvert++; + eve->fast= 1; + return eve; +} +static void *calloc_fastedge(EditMesh *em, size_t size, size_t nr) +{ + EditEdge *eed= em->curedge++; + eed->fast= 1; + return eed; +} +static void *calloc_fastface(EditMesh *em, size_t size, size_t nr) +{ + EditFace *efa= em->curface++; + efa->fast= 1; + return efa; +} + +/* allocate 1 chunk for all vertices, edges, faces. These get tagged to + prevent it from being freed +*/ +static void init_editmesh_fastmalloc(EditMesh *em, int totvert, int totedge, int totface) +{ + if(totvert) em->allverts= MEM_callocN(totvert*sizeof(EditVert), "allverts"); + else em->allverts= NULL; + em->curvert= em->allverts; + + if(totedge==0) totedge= 4*totface; // max possible + + if(totedge) em->alledges= MEM_callocN(totedge*sizeof(EditEdge), "alledges"); + else em->alledges= NULL; + em->curedge= em->alledges; + + if(totface) em->allfaces= MEM_callocN(totface*sizeof(EditFace), "allfaces"); + else em->allfaces= NULL; + em->curface= em->allfaces; + + callocvert= calloc_fastvert; + callocedge= calloc_fastedge; + callocface= calloc_fastface; +} + +static void end_editmesh_fastmalloc(void) +{ + callocvert= calloc_em; + callocedge= calloc_em; + callocface= calloc_em; +} + +/* do not free editmesh itself here */ +void free_editMesh(EditMesh *em) +{ + if(em==NULL) return; + + if(em->verts.first) free_vertlist(em, &em->verts); + if(em->edges.first) free_edgelist(em, &em->edges); + if(em->faces.first) free_facelist(em, &em->faces); + if(em->selected.first) BLI_freelistN(&(em->selected)); + + CustomData_free(&em->vdata, 0); + CustomData_free(&em->fdata, 0); + + if(em->derivedFinal) { + if (em->derivedFinal!=em->derivedCage) { + em->derivedFinal->needsFree= 1; + em->derivedFinal->release(em->derivedFinal); + } + em->derivedFinal= NULL; + } + if(em->derivedCage) { + em->derivedCage->needsFree= 1; + em->derivedCage->release(em->derivedCage); + em->derivedCage= NULL; + } + + /* DEBUG: hashtabs are slowest part of enter/exit editmode. here a testprint */ +#if 0 + if(em->hashedgetab) { + HashEdge *he, *hen; + int a, used=0, max=0, nr; + he= em->hashedgetab; + for(a=0; a<EDHASHSIZE; a++, he++) { + if(he->eed) used++; + hen= he->next; + nr= 0; + while(hen) { + nr++; + hen= hen->next; + } + if(max<nr) max= nr; + } + printf("hastab used %d max %d\n", used, max); + } +#endif + if(em->hashedgetab) MEM_freeN(em->hashedgetab); + em->hashedgetab= NULL; + + if(em->allverts) MEM_freeN(em->allverts); + if(em->alledges) MEM_freeN(em->alledges); + if(em->allfaces) MEM_freeN(em->allfaces); + + em->allverts= em->curvert= NULL; + em->alledges= em->curedge= NULL; + em->allfaces= em->curface= NULL; + + mesh_octree_table(NULL, NULL, NULL, 'e'); + + G.totvert= G.totface= 0; + +// XXX if(em->retopo_paint_data) retopo_free_paint_data(em->retopo_paint_data); + em->retopo_paint_data= NULL; + em->act_face = NULL; +} + +static void editMesh_set_hash(EditMesh *em) +{ + EditEdge *eed; + + em->hashedgetab= NULL; + + for(eed=em->edges.first; eed; eed= eed->next) { + if( findedgelist(em, eed->v1, eed->v2)==NULL ) + insert_hashedge(em, eed); + } + +} + + +/* ************************ IN & OUT EDITMODE ***************************** */ + + +static void edge_normal_compare(EditEdge *eed, EditFace *efa1) +{ + EditFace *efa2; + float cent1[3], cent2[3]; + float inp; + + efa2 = eed->tmp.f; + if(efa1==efa2) return; + + inp= efa1->n[0]*efa2->n[0] + efa1->n[1]*efa2->n[1] + efa1->n[2]*efa2->n[2]; + if(inp<0.999 && inp >-0.999) eed->f2= 1; + + if(efa1->v4) CalcCent4f(cent1, efa1->v1->co, efa1->v2->co, efa1->v3->co, efa1->v4->co); + else CalcCent3f(cent1, efa1->v1->co, efa1->v2->co, efa1->v3->co); + if(efa2->v4) CalcCent4f(cent2, efa2->v1->co, efa2->v2->co, efa2->v3->co, efa2->v4->co); + else CalcCent3f(cent2, efa2->v1->co, efa2->v2->co, efa2->v3->co); + + VecSubf(cent1, cent2, cent1); + Normalize(cent1); + inp= cent1[0]*efa1->n[0] + cent1[1]*efa1->n[1] + cent1[2]*efa1->n[2]; + + if(inp < -0.001 ) eed->f1= 1; +} + +#if 0 +typedef struct { + EditEdge *eed; + float noLen,no[3]; + int adjCount; +} EdgeDrawFlagInfo; + +static int edgeDrawFlagInfo_cmp(const void *av, const void *bv) +{ + const EdgeDrawFlagInfo *a = av; + const EdgeDrawFlagInfo *b = bv; + + if (a->noLen<b->noLen) return -1; + else if (a->noLen>b->noLen) return 1; + else return 0; +} +#endif + +static void edge_drawflags(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed, *e1, *e2, *e3, *e4; + EditFace *efa; + + /* - count number of times edges are used in faces: 0 en 1 time means draw edge + * - edges more than 1 time used: in *tmp.f is pointer to first face + * - check all faces, when normal differs to much: draw (flag becomes 1) + */ + + /* later on: added flags for 'cylinder' and 'sphere' intersection tests in old + game engine (2.04) + */ + + recalc_editnormals(em); + + /* init */ + eve= em->verts.first; + while(eve) { + eve->f1= 1; /* during test it's set at zero */ + eve= eve->next; + } + eed= em->edges.first; + while(eed) { + eed->f2= eed->f1= 0; + eed->tmp.f = 0; + eed= eed->next; + } + + efa= em->faces.first; + while(efa) { + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + e4= efa->e4; + if(e1->f2<4) e1->f2+= 1; + if(e2->f2<4) e2->f2+= 1; + if(e3->f2<4) e3->f2+= 1; + if(e4 && e4->f2<4) e4->f2+= 1; + + if(e1->tmp.f == 0) e1->tmp.f = (void *) efa; + if(e2->tmp.f == 0) e2->tmp.f = (void *) efa; + if(e3->tmp.f ==0) e3->tmp.f = (void *) efa; + if(e4 && (e4->tmp.f == 0)) e4->tmp.f = (void *) efa; + + efa= efa->next; + } + + if(G.f & G_ALLEDGES) { + efa= em->faces.first; + while(efa) { + if(efa->e1->f2>=2) efa->e1->f2= 1; + if(efa->e2->f2>=2) efa->e2->f2= 1; + if(efa->e3->f2>=2) efa->e3->f2= 1; + if(efa->e4 && efa->e4->f2>=2) efa->e4->f2= 1; + + efa= efa->next; + } + } + else { + + /* handle single-edges for 'test cylinder flag' (old engine) */ + + eed= em->edges.first; + while(eed) { + if(eed->f2==1) eed->f1= 1; + eed= eed->next; + } + + /* all faces, all edges with flag==2: compare normal */ + efa= em->faces.first; + while(efa) { + if(efa->e1->f2==2) edge_normal_compare(efa->e1, efa); + else efa->e1->f2= 1; + if(efa->e2->f2==2) edge_normal_compare(efa->e2, efa); + else efa->e2->f2= 1; + if(efa->e3->f2==2) edge_normal_compare(efa->e3, efa); + else efa->e3->f2= 1; + if(efa->e4) { + if(efa->e4->f2==2) edge_normal_compare(efa->e4, efa); + else efa->e4->f2= 1; + } + efa= efa->next; + } + + /* sphere collision flag */ + + eed= em->edges.first; + while(eed) { + if(eed->f1!=1) { + eed->v1->f1= eed->v2->f1= 0; + } + eed= eed->next; + } + + } +} + +static int editmesh_pointcache_edit(Scene *scene, Object *ob, int totvert, PTCacheID *pid_p, float mat[][4], int load) +{ + Cloth *cloth; + SoftBody *sb; + ClothModifierData *clmd; + PTCacheID pid, tmpid; + int cfra= (int)scene->r.cfra, found= 0; + + pid.cache= NULL; + + /* check for cloth */ + if(modifiers_isClothEnabled(ob)) { + clmd= (ClothModifierData*)modifiers_findByType(ob, eModifierType_Cloth); + cloth= clmd->clothObject; + + BKE_ptcache_id_from_cloth(&tmpid, ob, clmd); + + /* verify vertex count and baked status */ + if(cloth && (totvert == cloth->numverts)) { + if((tmpid.cache->flag & PTCACHE_BAKED) && (tmpid.cache->flag & PTCACHE_BAKE_EDIT)) { + pid= tmpid; + + if(load && (pid.cache->flag & PTCACHE_BAKE_EDIT_ACTIVE)) + found= 1; + } + } + } + + /* check for softbody */ + if(!found && ob->soft) { + sb= ob->soft; + + BKE_ptcache_id_from_softbody(&tmpid, ob, sb); + + /* verify vertex count and baked status */ + if(sb->bpoint && (totvert == sb->totpoint)) { + if((tmpid.cache->flag & PTCACHE_BAKED) && (tmpid.cache->flag & PTCACHE_BAKE_EDIT)) { + pid= tmpid; + + if(load && (pid.cache->flag & PTCACHE_BAKE_EDIT_ACTIVE)) + found= 1; + } + } + } + + /* if not making editmesh verify editing was active for this point cache */ + if(load) { + if(found) + pid.cache->flag &= ~PTCACHE_BAKE_EDIT_ACTIVE; + else + return 0; + } + + /* check if we have cache for this frame */ + if(pid.cache && BKE_ptcache_id_exist(&pid, cfra)) { + *pid_p = pid; + + if(load) { + Mat4CpyMat4(mat, ob->obmat); + } + else { + pid.cache->editframe= cfra; + pid.cache->flag |= PTCACHE_BAKE_EDIT_ACTIVE; + Mat4Invert(mat, ob->obmat); /* ob->imat is not up to date */ + } + + return 1; + } + + return 0; +} + +/* turns Mesh into editmesh */ +void make_editMesh(Scene *scene, EditMesh *em) +{ + Mesh *me= G.obedit->data; + MFace *mface; + MVert *mvert; + MSelect *mselect; + KeyBlock *actkey; + EditVert *eve, **evlist, *eve1, *eve2, *eve3, *eve4; + EditFace *efa; + EditEdge *eed; + EditSelection *ese; + PTCacheID pid; + Cloth *cloth; + SoftBody *sb; + float cacheco[3], cachemat[4][4], *co; + int tot, a, cacheedit= 0, eekadoodle= 0; + + /* because of reload */ + free_editMesh(em); + + em->act_face = NULL; + G.totvert= tot= me->totvert; + G.totedge= me->totedge; + G.totface= me->totface; + + if(tot==0) { + return; + } + + /* initialize fastmalloc for editmesh */ + init_editmesh_fastmalloc(em, me->totvert, me->totedge, me->totface); + + actkey = ob_get_keyblock(G.obedit); + if(actkey) { + strcpy(G.editModeTitleExtra, "(Key) "); + key_to_mesh(actkey, me); + tot= actkey->totelem; + /* undo-ing in past for previous editmode sessions gives corrupt 'keyindex' values */ + undo_editmode_clear(); + } + + + /* make editverts */ + CustomData_copy(&me->vdata, &em->vdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + mvert= me->mvert; + + cacheedit= editmesh_pointcache_edit(scene, G.obedit, tot, &pid, cachemat, 0); + + evlist= (EditVert **)MEM_mallocN(tot*sizeof(void *),"evlist"); + for(a=0; a<tot; a++, mvert++) { + + if(cacheedit) { + if(pid.type == PTCACHE_TYPE_CLOTH) { + cloth= ((ClothModifierData*)pid.data)->clothObject; + VECCOPY(cacheco, cloth->verts[a].x) + } + else if(pid.type == PTCACHE_TYPE_SOFTBODY) { + sb= (SoftBody*)pid.data; + VECCOPY(cacheco, sb->bpoint[a].pos) + } + + Mat4MulVecfl(cachemat, cacheco); + co= cacheco; + } + else + co= mvert->co; + + eve= addvertlist(em, co, NULL); + evlist[a]= eve; + + // face select sets selection in next loop + if( (FACESEL_PAINT_TEST)==0 ) + eve->f |= (mvert->flag & 1); + + if (mvert->flag & ME_HIDE) eve->h= 1; + eve->no[0]= mvert->no[0]/32767.0; + eve->no[1]= mvert->no[1]/32767.0; + eve->no[2]= mvert->no[2]/32767.0; + + eve->bweight= ((float)mvert->bweight)/255.0f; + + /* lets overwrite the keyindex of the editvert + * with the order it used to be in before + * editmode + */ + eve->keyindex = a; + + CustomData_to_em_block(&me->vdata, &em->vdata, a, &eve->data); + } + + if(actkey && actkey->totelem!=me->totvert); + else { + MEdge *medge= me->medge; + + CustomData_copy(&me->edata, &em->edata, CD_MASK_EDITMESH, CD_CALLOC, 0); + /* make edges */ + for(a=0; a<me->totedge; a++, medge++) { + eed= addedgelist(em, evlist[medge->v1], evlist[medge->v2], NULL); + /* eed can be zero when v1 and v2 are identical, dxf import does this... */ + if(eed) { + eed->crease= ((float)medge->crease)/255.0f; + eed->bweight= ((float)medge->bweight)/255.0f; + + if(medge->flag & ME_SEAM) eed->seam= 1; + if(medge->flag & ME_SHARP) eed->sharp = 1; + if(medge->flag & SELECT) eed->f |= SELECT; + if(medge->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! + if(medge->flag & ME_HIDE) eed->h |= 1; + if(em->selectmode==SCE_SELECT_EDGE) + EM_select_edge(eed, eed->f & SELECT); // force edge selection to vertices, seems to be needed ... + CustomData_to_em_block(&me->edata,&em->edata, a, &eed->data); + } + } + + CustomData_copy(&me->fdata, &em->fdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + + /* make faces */ + mface= me->mface; + + for(a=0; a<me->totface; a++, mface++) { + eve1= evlist[mface->v1]; + eve2= evlist[mface->v2]; + if(!mface->v3) eekadoodle= 1; + eve3= evlist[mface->v3]; + if(mface->v4) eve4= evlist[mface->v4]; else eve4= NULL; + + efa= addfacelist(em, eve1, eve2, eve3, eve4, NULL, NULL); + + if(efa) { + CustomData_to_em_block(&me->fdata, &em->fdata, a, &efa->data); + + efa->mat_nr= mface->mat_nr; + efa->flag= mface->flag & ~ME_HIDE; + + /* select and hide face flag */ + if(mface->flag & ME_HIDE) { + efa->h= 1; + } else { + if (a==me->act_face) { + EM_set_actFace(em, efa); + } + + /* dont allow hidden and selected */ + if(mface->flag & ME_FACE_SEL) { + efa->f |= SELECT; + + if(FACESEL_PAINT_TEST) { + EM_select_face(efa, 1); /* flush down */ + } + } + } + } + } + } + + if(eekadoodle) + error("This Mesh has old style edgecodes, please put it in the bugtracker!"); + + MEM_freeN(evlist); + + end_editmesh_fastmalloc(); // resets global function pointers + + if(me->mselect){ + //restore editselections + EM_init_index_arrays(em, 1,1,1); + mselect = me->mselect; + + for(a=0; a<me->totselect; a++, mselect++){ + /*check if recorded selection is still valid, if so copy into editmesh*/ + if( (mselect->type == EDITVERT && me->mvert[mselect->index].flag & SELECT) || (mselect->type == EDITEDGE && me->medge[mselect->index].flag & SELECT) || (mselect->type == EDITFACE && me->mface[mselect->index].flag & ME_FACE_SEL) ){ + ese = MEM_callocN(sizeof(EditSelection), "Edit Selection"); + ese->type = mselect->type; + if(ese->type == EDITVERT) ese->data = EM_get_vert_for_index(mselect->index); else + if(ese->type == EDITEDGE) ese->data = EM_get_edge_for_index(mselect->index); else + if(ese->type == EDITFACE) ese->data = EM_get_face_for_index(mselect->index); + BLI_addtail(&(em->selected),ese); + } + } + EM_free_index_arrays(); + } + /* this creates coherent selections. also needed for older files */ + EM_selectmode_set(em); + /* paranoia check to enforce hide rules */ + EM_hide_reset(em); + /* sets helper flags which arent saved */ + EM_fgon_flags(em); + + if (EM_get_actFace(em, 0)==NULL) { + EM_set_actFace(em, em->faces.first ); /* will use the first face, this is so we alwats have an active face */ + } + + /* vertex coordinates change with cache edit, need to recalc */ + if(cacheedit) + recalc_editnormals(em); + +} + +/* makes Mesh out of editmesh */ +void load_editMesh(Scene *scene, EditMesh *em) +{ + Mesh *me= G.obedit->data; + MVert *mvert, *oldverts; + MEdge *medge; + MFace *mface; + MSelect *mselect; + EditVert *eve; + EditFace *efa, *efa_act; + EditEdge *eed; + EditSelection *ese; + SoftBody *sb; + Cloth *cloth; + ClothModifierData *clmd; + PTCacheID pid; + float *fp, *newkey, *oldkey, nor[3], cacheco[3], cachemat[4][4]; + int i, a, ototvert, totedge=0, cacheedit= 0; + + /* this one also tests of edges are not in faces: */ + /* eed->f2==0: not in face, f2==1: draw it */ + /* eed->f1 : flag for dynaface (cylindertest, old engine) */ + /* eve->f1 : flag for dynaface (sphere test, old engine) */ + /* eve->f2 : being used in vertexnormals */ + edge_drawflags(em); + + eed= em->edges.first; + while(eed) { + totedge++; + eed= eed->next; + } + + /* new Vertex block */ + if(G.totvert==0) mvert= NULL; + else mvert= MEM_callocN(G.totvert*sizeof(MVert), "loadeditMesh vert"); + + /* new Edge block */ + if(totedge==0) medge= NULL; + else medge= MEM_callocN(totedge*sizeof(MEdge), "loadeditMesh edge"); + + /* new Face block */ + if(G.totface==0) mface= NULL; + else mface= MEM_callocN(G.totface*sizeof(MFace), "loadeditMesh face"); + + /* lets save the old verts just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldverts= me->mvert; + ototvert= me->totvert; + + /* don't free this yet */ + CustomData_set_layer(&me->vdata, CD_MVERT, NULL); + + /* free custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + + /* add new custom data */ + me->totvert= G.totvert; + me->totedge= totedge; + me->totface= G.totface; + + CustomData_copy(&em->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); + CustomData_copy(&em->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); + CustomData_copy(&em->fdata, &me->fdata, CD_MASK_MESH, CD_CALLOC, me->totface); + + CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); + CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); + CustomData_add_layer(&me->fdata, CD_MFACE, CD_ASSIGN, mface, me->totface); + mesh_update_customdata_pointers(me); + + /* the vertices, use ->tmp.l as counter */ + eve= em->verts.first; + a= 0; + + /* check for point cache editing */ + cacheedit= editmesh_pointcache_edit(scene, G.obedit, G.totvert, &pid, cachemat, 1); + + while(eve) { + if(cacheedit) { + if(pid.type == PTCACHE_TYPE_CLOTH) { + clmd= (ClothModifierData*)pid.data; + cloth= clmd->clothObject; + + /* assign position */ + VECCOPY(cacheco, cloth->verts[a].x) + VECCOPY(cloth->verts[a].x, eve->co); + Mat4MulVecfl(cachemat, cloth->verts[a].x); + + /* find plausible velocity, not physical correct but gives + * nicer results when commented */ + VECSUB(cacheco, cloth->verts[a].x, cacheco); + VecMulf(cacheco, clmd->sim_parms->stepsPerFrame*10.0f); + VECADD(cloth->verts[a].v, cloth->verts[a].v, cacheco); + } + else if(pid.type == PTCACHE_TYPE_SOFTBODY) { + sb= (SoftBody*)pid.data; + + /* assign position */ + VECCOPY(cacheco, sb->bpoint[a].pos) + VECCOPY(sb->bpoint[a].pos, eve->co); + Mat4MulVecfl(cachemat, sb->bpoint[a].pos); + + /* changing velocity for softbody doesn't seem to give + * good results? */ +#if 0 + VECSUB(cacheco, sb->bpoint[a].pos, cacheco); + VecMulf(cacheco, sb->minloops*10.0f); + VECADD(sb->bpoint[a].vec, sb->bpoint[a].pos, cacheco); +#endif + } + + if(oldverts) + VECCOPY(mvert->co, oldverts[a].co) + } + else + VECCOPY(mvert->co, eve->co); + + mvert->mat_nr= 255; /* what was this for, halos? */ + + /* vertex normal */ + VECCOPY(nor, eve->no); + VecMulf(nor, 32767.0); + VECCOPY(mvert->no, nor); + + /* note: it used to remove me->dvert when it was not in use, cancelled + that... annoying when you have a fresh vgroup */ + CustomData_from_em_block(&em->vdata, &me->vdata, eve->data, a); + + eve->tmp.l = a++; /* counter */ + + mvert->flag= 0; + if(eve->f1==1) mvert->flag |= ME_SPHERETEST; + mvert->flag |= (eve->f & SELECT); + if (eve->h) mvert->flag |= ME_HIDE; + mvert->bweight= (char)(255.0*eve->bweight); + + eve= eve->next; + mvert++; + } + + /* write changes to cache */ + if(cacheedit) { + if(pid.type == PTCACHE_TYPE_CLOTH) + cloth_write_cache(G.obedit, pid.data, pid.cache->editframe); + else if(pid.type == PTCACHE_TYPE_SOFTBODY) + sbWriteCache(G.obedit, pid.cache->editframe); + } + + /* the edges */ + a= 0; + eed= em->edges.first; + while(eed) { + medge->v1= (unsigned int) eed->v1->tmp.l; + medge->v2= (unsigned int) eed->v2->tmp.l; + + medge->flag= (eed->f & SELECT) | ME_EDGERENDER; + if(eed->f2<2) medge->flag |= ME_EDGEDRAW; + if(eed->f2==0) medge->flag |= ME_LOOSEEDGE; + if(eed->sharp) medge->flag |= ME_SHARP; + if(eed->seam) medge->flag |= ME_SEAM; + if(eed->h & EM_FGON) medge->flag |= ME_FGON; // different defines yes + if(eed->h & 1) medge->flag |= ME_HIDE; + + medge->crease= (char)(255.0*eed->crease); + medge->bweight= (char)(255.0*eed->bweight); + CustomData_from_em_block(&em->edata, &me->edata, eed->data, a); + + eed->tmp.l = a++; + + medge++; + eed= eed->next; + } + + /* the faces */ + a = 0; + efa= em->faces.first; + efa_act= EM_get_actFace(em, 0); + i = 0; + me->act_face = -1; + while(efa) { + mface= &((MFace *) me->mface)[i]; + + mface->v1= (unsigned int) efa->v1->tmp.l; + mface->v2= (unsigned int) efa->v2->tmp.l; + mface->v3= (unsigned int) efa->v3->tmp.l; + if (efa->v4) mface->v4 = (unsigned int) efa->v4->tmp.l; + + mface->mat_nr= efa->mat_nr; + + mface->flag= efa->flag; + /* bit 0 of flag is already taken for smooth... */ + + if(efa->h) { + mface->flag |= ME_HIDE; + mface->flag &= ~ME_FACE_SEL; + } else { + if(efa->f & 1) mface->flag |= ME_FACE_SEL; + else mface->flag &= ~ME_FACE_SEL; + } + + /* mat_nr in vertex */ + if(me->totcol>1) { + mvert= me->mvert+mface->v1; + if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; + mvert= me->mvert+mface->v2; + if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; + mvert= me->mvert+mface->v3; + if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; + if(mface->v4) { + mvert= me->mvert+mface->v4; + if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; + } + } + + /* watch: efa->e1->f2==0 means loose edge */ + + if(efa->e1->f2==1) { + efa->e1->f2= 2; + } + if(efa->e2->f2==1) { + efa->e2->f2= 2; + } + if(efa->e3->f2==1) { + efa->e3->f2= 2; + } + if(efa->e4 && efa->e4->f2==1) { + efa->e4->f2= 2; + } + + CustomData_from_em_block(&em->fdata, &me->fdata, efa->data, i); + + /* no index '0' at location 3 or 4 */ + test_index_face(mface, &me->fdata, i, efa->v4?4:3); + + if (efa_act == efa) + me->act_face = a; + + efa->tmp.l = a++; + i++; + efa= efa->next; + } + + /* patch hook indices and vertex parents */ + { + Object *ob; + ModifierData *md; + EditVert **vertMap = NULL; + int i,j; + + for (ob=G.main->object.first; ob; ob=ob->id.next) { + if (ob->parent==G.obedit && ELEM(ob->partype, PARVERT1,PARVERT3)) { + + /* duplicate code from below, make it function later...? */ + if (!vertMap) { + vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap"); + + for (eve=em->verts.first; eve; eve=eve->next) { + if (eve->keyindex!=-1) + vertMap[eve->keyindex] = eve; + } + } + if(ob->par1 < ototvert) { + eve = vertMap[ob->par1]; + if(eve) ob->par1= eve->tmp.l; + } + if(ob->par2 < ototvert) { + eve = vertMap[ob->par2]; + if(eve) ob->par2= eve->tmp.l; + } + if(ob->par3 < ototvert) { + eve = vertMap[ob->par3]; + if(eve) ob->par3= eve->tmp.l; + } + + } + if (ob->data==me) { + for (md=ob->modifiers.first; md; md=md->next) { + if (md->type==eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData*) md; + + if (!vertMap) { + vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap"); + + for (eve=em->verts.first; eve; eve=eve->next) { + if (eve->keyindex!=-1) + vertMap[eve->keyindex] = eve; + } + } + + for (i=j=0; i<hmd->totindex; i++) { + if(hmd->indexar[i] < ototvert) { + eve = vertMap[hmd->indexar[i]]; + + if (eve) { + hmd->indexar[j++] = eve->tmp.l; + } + } + else j++; + } + + hmd->totindex = j; + } + } + } + } + + if (vertMap) MEM_freeN(vertMap); + } + + /* are there keys? */ + if(me->key) { + KeyBlock *currkey, *actkey = ob_get_keyblock(G.obedit); + + /* Lets reorder the key data so that things line up roughly + * with the way things were before editmode */ + currkey = me->key->block.first; + while(currkey) { + + fp= newkey= MEM_callocN(me->key->elemsize*G.totvert, "currkey->data"); + oldkey = currkey->data; + + eve= em->verts.first; + + i = 0; + mvert = me->mvert; + while(eve) { + if (eve->keyindex >= 0 && eve->keyindex < currkey->totelem) { // valid old vertex + if(currkey == actkey) { + if (actkey == me->key->refkey) { + VECCOPY(fp, mvert->co); + } + else { + VECCOPY(fp, mvert->co); + if(oldverts) { + VECCOPY(mvert->co, oldverts[eve->keyindex].co); + } + } + } + else { + if(oldkey) { + VECCOPY(fp, oldkey + 3 * eve->keyindex); + } + } + } + else { + VECCOPY(fp, mvert->co); + } + fp+= 3; + ++i; + ++mvert; + eve= eve->next; + } + currkey->totelem= G.totvert; + if(currkey->data) MEM_freeN(currkey->data); + currkey->data = newkey; + + currkey= currkey->next; + } + } + + if(oldverts) MEM_freeN(oldverts); + + i = 0; + for(ese=em->selected.first; ese; ese=ese->next) i++; + me->totselect = i; + if(i==0) mselect= NULL; + else mselect= MEM_callocN(i*sizeof(MSelect), "loadeditMesh selections"); + + if(me->mselect) MEM_freeN(me->mselect); + me->mselect= mselect; + + for(ese=em->selected.first; ese; ese=ese->next){ + mselect->type = ese->type; + if(ese->type == EDITVERT) mselect->index = ((EditVert*)ese->data)->tmp.l; + else if(ese->type == EDITEDGE) mselect->index = ((EditEdge*)ese->data)->tmp.l; + else if(ese->type == EDITFACE) mselect->index = ((EditFace*)ese->data)->tmp.l; + mselect++; + } + + /* to be sure: clear ->tmp.l pointers */ + eve= em->verts.first; + while(eve) { + eve->tmp.l = 0; + eve= eve->next; + } + + eed= em->edges.first; + while(eed) { + eed->tmp.l = 0; + eed= eed->next; + } + + efa= em->faces.first; + while(efa) { + efa->tmp.l = 0; + efa= efa->next; + } + + /* remake softbody of all users */ + if(me->id.us>1) { + Base *base; + for(base= scene->base.first; base; base= base->next) + if(base->object->data==me) + base->object->recalc |= OB_RECALC_DATA; + } + + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); +} + +void remake_editMesh(Scene *scene, EditMesh *em) +{ + make_editMesh(scene, em); +// allqueue(REDRAWVIEW3D, 0); +// allqueue(REDRAWBUTSOBJECT, 0); /* needed to have nice cloth panels */ + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Undo all changes"); +} + +/* *************** (partial exit editmode) *************/ + + + + +void separate_mesh(Scene *scene, EditMesh *em) +{ + EditMesh emcopy; + EditVert *eve, *v1; + EditEdge *eed, *e1; + EditFace *efa, *vl1; + Object *oldob; + Mesh *me, *men; + Base *base, *oldbase; + ListBase edve, eded, edvl; + + TEST_EDITMESH + if(multires_test()) return; + + waitcursor(1); + + me= get_mesh(G.obedit); + if(me->key) { + error("Can't separate with vertex keys"); + return; + } + + if(em->selected.first) BLI_freelistN(&(em->selected)); /* clear the selection order */ + + EM_selectmode_set(em); // enforce full consistant selection flags + + /* we are going to abuse the system as follows: + * 1. add a duplicate object: this will be the new one, we remember old pointer + * 2: then do a split if needed. + * 3. put apart: all NOT selected verts, edges, faces + * 4. call load_editMesh(): this will be the new object + * 5. freelist and get back old verts, edges, facs + */ + + /* make only obedit selected */ + base= FIRSTBASE; + while(base) { +// XXX if(base->lay & G.vd->lay) { + if(base->object==G.obedit) base->flag |= SELECT; + else base->flag &= ~SELECT; +// } + base= base->next; + } + + /* no test for split, split doesn't split when a loose part is selected */ + /* SPLIT: first make duplicate */ + adduplicateflag(em, SELECT); + + /* SPLIT: old faces have 3x flag 128 set, delete these ones */ + delfaceflag(em, 128); + + /* since we do tricky things with verts/edges/faces, this makes sure all is selected coherent */ + EM_selectmode_set(em); + + /* set apart: everything that is not selected */ + edve.first= edve.last= eded.first= eded.last= edvl.first= edvl.last= 0; + eve= em->verts.first; + while(eve) { + v1= eve->next; + if((eve->f & SELECT)==0) { + BLI_remlink(&em->verts, eve); + BLI_addtail(&edve, eve); + } + + eve= v1; + } + eed= em->edges.first; + while(eed) { + e1= eed->next; + if((eed->f & SELECT)==0) { + BLI_remlink(&em->edges, eed); + BLI_addtail(&eded, eed); + } + eed= e1; + } + efa= em->faces.first; + while(efa) { + vl1= efa->next; + if (efa == em->act_face && (efa->f & SELECT)) { + EM_set_actFace(em, NULL); + } + + if((efa->f & SELECT)==0) { + BLI_remlink(&em->faces, efa); + BLI_addtail(&edvl, efa); + } + efa= vl1; + } + + oldob= G.obedit; + oldbase= BASACT; + + adduplicate(1, 0); /* notrans and a linked duplicate */ + + G.obedit= BASACT->object; /* basact was set in adduplicate() */ + + men= copy_mesh(me); + set_mesh(G.obedit, men); + /* because new mesh is a copy: reduce user count */ + men->id.us--; + + load_editMesh(scene, em); + + BASACT->flag &= ~SELECT; + + /* we cannot free the original buffer... */ + emcopy= *em; + emcopy.allverts= NULL; + emcopy.alledges= NULL; + emcopy.allfaces= NULL; + emcopy.derivedFinal= emcopy.derivedCage= NULL; + memset(&emcopy.vdata, 0, sizeof(emcopy.vdata)); + memset(&emcopy.fdata, 0, sizeof(emcopy.fdata)); + free_editMesh(&emcopy); + + em->verts= edve; + em->edges= eded; + em->faces= edvl; + + /* hashedges are freed now, make new! */ + editMesh_set_hash(em); + + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + G.obedit= oldob; + BASACT= oldbase; + BASACT->flag |= SELECT; + + waitcursor(0); + +// allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + +} + +void separate_material(Scene *scene, EditMesh *em) +{ + unsigned char curr_mat; + Mesh *me; + + if(multires_test()) return; + + me= get_mesh(G.obedit); + if(me->key) { + error("Can't separate with vertex keys"); + return; + } + + if(G.obedit && em) { + if(G.obedit->type == OB_MESH) { + for (curr_mat = 1; curr_mat < G.obedit->totcol; ++curr_mat) { + /* clear selection, we're going to use that to select material group */ + EM_clear_flag_all(em, SELECT); + /* select the material */ + editmesh_select_by_material(em, curr_mat); + /* and now separate */ + separate_mesh(scene, em); + } + } + } + + // allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + +} + + +void separate_mesh_loose(Scene *scene, EditMesh *em) +{ + EditMesh emcopy; + EditVert *eve, *v1; + EditEdge *eed, *e1; + EditFace *efa, *vl1; + Object *oldob=NULL; + Mesh *me, *men; + Base *base, *oldbase; + ListBase edve, eded, edvl; + int vertsep=0; + short done=0, check=1; + + me= get_mesh(G.obedit); + if(me->key) { + error("Can't separate a mesh with vertex keys"); + return; + } + + TEST_EDITMESH + if(multires_test()) return; + waitcursor(1); + + /* we are going to abuse the system as follows: + * 1. add a duplicate object: this will be the new one, we remember old pointer + * 2: then do a split if needed. + * 3. put apart: all NOT selected verts, edges, faces + * 4. call load_editMesh(): this will be the new object + * 5. freelist and get back old verts, edges, facs + */ + + while(!done){ + vertsep=check=1; + + /* make only obedit selected */ + base= FIRSTBASE; + while(base) { +// XXX if(base->lay & G.vd->lay) { + if(base->object==G.obedit) base->flag |= SELECT; + else base->flag &= ~SELECT; +// } + base= base->next; + } + + /*--------- Select connected-----------*/ + + EM_clear_flag_all(em, SELECT); + + /* Select a random vert to start with */ + eve= em->verts.first; + eve->f |= SELECT; + + while(check==1) { + check= 0; + eed= em->edges.first; + while(eed) { + if(eed->h==0) { + if(eed->v1->f & SELECT) { + if( (eed->v2->f & SELECT)==0 ) { + eed->v2->f |= SELECT; + vertsep++; + check= 1; + } + } + else if(eed->v2->f & SELECT) { + if( (eed->v1->f & SELECT)==0 ) { + eed->v1->f |= SELECT; + vertsep++; + check= SELECT; + } + } + } + eed= eed->next; + } + } + /*----------End of select connected--------*/ + + + /* If the amount of vertices that is about to be split == the total amount + of verts in the mesh, it means that there is only 1 unconnected object, so we don't have to separate + */ + if(G.totvert==vertsep) done=1; + else{ + /* No splitting: select connected goes fine */ + + EM_select_flush(em); // from verts->edges->faces + + /* set apart: everything that is not selected */ + edve.first= edve.last= eded.first= eded.last= edvl.first= edvl.last= 0; + eve= em->verts.first; + while(eve) { + v1= eve->next; + if((eve->f & SELECT)==0) { + BLI_remlink(&em->verts, eve); + BLI_addtail(&edve, eve); + } + eve= v1; + } + eed= em->edges.first; + while(eed) { + e1= eed->next; + if( (eed->f & SELECT)==0 ) { + BLI_remlink(&em->edges, eed); + BLI_addtail(&eded, eed); + } + eed= e1; + } + efa= em->faces.first; + while(efa) { + vl1= efa->next; + if( (efa->f & SELECT)==0 ) { + BLI_remlink(&em->faces, efa); + BLI_addtail(&edvl, efa); + } + efa= vl1; + } + + oldob= G.obedit; + oldbase= BASACT; + + adduplicate(1, 0); /* notrans and a linked duplicate*/ + + G.obedit= BASACT->object; /* basact was set in adduplicate() */ + + men= copy_mesh(me); + set_mesh(G.obedit, men); + /* because new mesh is a copy: reduce user count */ + men->id.us--; + + load_editMesh(scene, em); + + BASACT->flag &= ~SELECT; + + /* we cannot free the original buffer... */ + emcopy= *em; + emcopy.allverts= NULL; + emcopy.alledges= NULL; + emcopy.allfaces= NULL; + emcopy.derivedFinal= emcopy.derivedCage= NULL; + memset(&emcopy.vdata, 0, sizeof(emcopy.vdata)); + memset(&emcopy.fdata, 0, sizeof(emcopy.fdata)); + free_editMesh(&emcopy); + + em->verts= edve; + em->edges= eded; + em->faces= edvl; + + /* hashedges are freed now, make new! */ + editMesh_set_hash(em); + + G.obedit= oldob; + BASACT= oldbase; + BASACT->flag |= SELECT; + + } + } + + /* unselect the vertices that we (ab)used for the separation*/ + EM_clear_flag_all(em, SELECT); + + waitcursor(0); +// allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); +} + +void separatemenu(Scene *scene, EditMesh *em) +{ + short event; + + if(em->verts.first==NULL) return; + + event = pupmenu("Separate %t|Selected%x1|All Loose Parts%x2|By Material%x3"); + + if (event==0) return; + waitcursor(1); + + switch (event) { + case 1: + separate_mesh(scene, em); + break; + case 2: + separate_mesh_loose(scene, em); + break; + case 3: + separate_material(scene, em); + break; + } + waitcursor(0); +} + + +/* ******************************************** */ + +/* *************** UNDO ***************************** */ +/* new mesh undo, based on pushing editmesh data itself */ +/* reuses same code as for global and curve undo... unify that (ton) */ + +/* only one 'hack', to save memory it doesn't store the first push, but does a remake editmesh */ + +/* a compressed version of editmesh data */ + +typedef struct EditVertC +{ + float no[3]; + float co[3]; + unsigned char f, h; + short bweight; + int keyindex; +} EditVertC; + +typedef struct EditEdgeC +{ + int v1, v2; + unsigned char f, h, seam, sharp, pad; + short crease, bweight, fgoni; +} EditEdgeC; + +typedef struct EditFaceC +{ + int v1, v2, v3, v4; + unsigned char mat_nr, flag, f, h, fgonf; + short pad1; +} EditFaceC; + +typedef struct EditSelectionC{ + short type; + int index; +}EditSelectionC; + +typedef struct EM_MultiresUndo { + int users; + Multires *mr; +} EM_MultiresUndo; + +typedef struct UndoMesh { + EditVertC *verts; + EditEdgeC *edges; + EditFaceC *faces; + EditSelectionC *selected; + int totvert, totedge, totface, totsel; + short selectmode; + RetopoPaintData *retopo_paint_data; + char retopo_mode; + CustomData vdata, edata, fdata; + EM_MultiresUndo *mru; +} UndoMesh; + +/* for callbacks */ + +static void free_undoMesh(void *umv) +{ + UndoMesh *um= umv; + + if(um->verts) MEM_freeN(um->verts); + if(um->edges) MEM_freeN(um->edges); + if(um->faces) MEM_freeN(um->faces); + if(um->selected) MEM_freeN(um->selected); +// XXX if(um->retopo_paint_data) retopo_free_paint_data(um->retopo_paint_data); + CustomData_free(&um->vdata, um->totvert); + CustomData_free(&um->edata, um->totedge); + CustomData_free(&um->fdata, um->totface); + if(um->mru) { + --um->mru->users; + if(um->mru->users==0) { + multires_free(um->mru->mr); + um->mru->mr= NULL; + MEM_freeN(um->mru); + } + } + MEM_freeN(um); +} + +static void *editMesh_to_undoMesh(Scene *scene, EditMesh *em) +{ + UndoMesh *um; + EditVert *eve; + EditEdge *eed; + EditFace *efa; + EditSelection *ese; + EditVertC *evec=NULL; + EditEdgeC *eedc=NULL; + EditFaceC *efac=NULL; + EditSelectionC *esec=NULL; + int a; + + um= MEM_callocN(sizeof(UndoMesh), "undomesh"); + + um->selectmode = scene->selectmode; + + for(eve=em->verts.first; eve; eve= eve->next) um->totvert++; + for(eed=em->edges.first; eed; eed= eed->next) um->totedge++; + for(efa=em->faces.first; efa; efa= efa->next) um->totface++; + for(ese=em->selected.first; ese; ese=ese->next) um->totsel++; + /* malloc blocks */ + + if(um->totvert) evec= um->verts= MEM_callocN(um->totvert*sizeof(EditVertC), "allvertsC"); + if(um->totedge) eedc= um->edges= MEM_callocN(um->totedge*sizeof(EditEdgeC), "alledgesC"); + if(um->totface) efac= um->faces= MEM_callocN(um->totface*sizeof(EditFaceC), "allfacesC"); + if(um->totsel) esec= um->selected= MEM_callocN(um->totsel*sizeof(EditSelectionC), "allselections"); + + if(um->totvert) CustomData_copy(&em->vdata, &um->vdata, CD_MASK_EDITMESH, CD_CALLOC, um->totvert); + if(um->totedge) CustomData_copy(&em->edata, &um->edata, CD_MASK_EDITMESH, CD_CALLOC, um->totedge); + if(um->totface) CustomData_copy(&em->fdata, &um->fdata, CD_MASK_EDITMESH, CD_CALLOC, um->totface); + + /* now copy vertices */ + a = 0; + for(eve=em->verts.first; eve; eve= eve->next, evec++, a++) { + VECCOPY(evec->co, eve->co); + VECCOPY(evec->no, eve->no); + + evec->f= eve->f; + evec->h= eve->h; + evec->keyindex= eve->keyindex; + eve->tmp.l = a; /*store index*/ + evec->bweight= (short)(eve->bweight*255.0); + + CustomData_from_em_block(&em->vdata, &um->vdata, eve->data, a); + } + + /* copy edges */ + a = 0; + for(eed=em->edges.first; eed; eed= eed->next, eedc++, a++) { + eedc->v1= (int)eed->v1->tmp.l; + eedc->v2= (int)eed->v2->tmp.l; + eedc->f= eed->f; + eedc->h= eed->h; + eedc->seam= eed->seam; + eedc->sharp= eed->sharp; + eedc->crease= (short)(eed->crease*255.0); + eedc->bweight= (short)(eed->bweight*255.0); + eedc->fgoni= eed->fgoni; + eed->tmp.l = a; /*store index*/ + CustomData_from_em_block(&em->edata, &um->edata, eed->data, a); + + } + + /* copy faces */ + a = 0; + for(efa=em->faces.first; efa; efa= efa->next, efac++, a++) { + efac->v1= (int)efa->v1->tmp.l; + efac->v2= (int)efa->v2->tmp.l; + efac->v3= (int)efa->v3->tmp.l; + if(efa->v4) efac->v4= (int)efa->v4->tmp.l; + else efac->v4= -1; + + efac->mat_nr= efa->mat_nr; + efac->flag= efa->flag; + efac->f= efa->f; + efac->h= efa->h; + efac->fgonf= efa->fgonf; + + efa->tmp.l = a; /*store index*/ + + CustomData_from_em_block(&em->fdata, &um->fdata, efa->data, a); + } + + a = 0; + for(ese=em->selected.first; ese; ese=ese->next, esec++){ + esec->type = ese->type; + if(ese->type == EDITVERT) a = esec->index = ((EditVert*)ese->data)->tmp.l; + else if(ese->type == EDITEDGE) a = esec->index = ((EditEdge*)ese->data)->tmp.l; + else if(ese->type == EDITFACE) a = esec->index = ((EditFace*)ese->data)->tmp.l; + } + +// XXX um->retopo_paint_data= retopo_paint_data_copy(em->retopo_paint_data); + um->retopo_mode= scene->toolsettings->retopo_mode; + + { + Multires *mr= get_mesh(G.obedit)->mr; + UndoMesh *prev= undo_editmode_get_prev(G.obedit); + + um->mru= NULL; + + if(mr) { + if(prev && prev->mru && prev->mru->mr && prev->mru->mr->current == mr->current) { + um->mru= prev->mru; + ++um->mru->users; + } + else { + um->mru= MEM_callocN(sizeof(EM_MultiresUndo), "EM_MultiresUndo"); + um->mru->users= 1; + um->mru->mr= multires_copy(mr); + } + } + } + + return um; +} + +static void undoMesh_to_editMesh(void *umv, EditMesh *em) +{ + UndoMesh *um= (UndoMesh*)umv; + EditVert *eve, **evar=NULL; + EditEdge *eed; + EditFace *efa; + EditSelection *ese; + EditVertC *evec; + EditEdgeC *eedc; + EditFaceC *efac; + EditSelectionC *esec; + int a=0; + + em->selectmode = um->selectmode; + + free_editMesh(em); + + /* malloc blocks */ + memset(em, 0, sizeof(EditMesh)); + + init_editmesh_fastmalloc(em, um->totvert, um->totedge, um->totface); + + CustomData_free(&em->vdata, 0); + CustomData_free(&em->edata, 0); + CustomData_free(&em->fdata, 0); + + CustomData_copy(&um->vdata, &em->vdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + CustomData_copy(&um->edata, &em->edata, CD_MASK_EDITMESH, CD_CALLOC, 0); + CustomData_copy(&um->fdata, &em->fdata, CD_MASK_EDITMESH, CD_CALLOC, 0); + + /* now copy vertices */ + + if(um->totvert) evar= MEM_mallocN(um->totvert*sizeof(EditVert *), "vertex ar"); + for(a=0, evec= um->verts; a<um->totvert; a++, evec++) { + eve= addvertlist(em, evec->co, NULL); + evar[a]= eve; + + VECCOPY(eve->no, evec->no); + eve->f= evec->f; + eve->h= evec->h; + eve->keyindex= evec->keyindex; + eve->bweight= ((float)evec->bweight)/255.0f; + + CustomData_to_em_block(&um->vdata, &em->vdata, a, &eve->data); + } + + /* copy edges */ + for(a=0, eedc= um->edges; a<um->totedge; a++, eedc++) { + eed= addedgelist(em, evar[eedc->v1], evar[eedc->v2], NULL); + + eed->f= eedc->f; + eed->h= eedc->h; + eed->seam= eedc->seam; + eed->sharp= eedc->sharp; + eed->fgoni= eedc->fgoni; + eed->crease= ((float)eedc->crease)/255.0f; + eed->bweight= ((float)eedc->bweight)/255.0f; + CustomData_to_em_block(&um->edata, &em->edata, a, &eed->data); + } + + /* copy faces */ + for(a=0, efac= um->faces; a<um->totface; a++, efac++) { + if(efac->v4 != -1) + efa= addfacelist(em, evar[efac->v1], evar[efac->v2], evar[efac->v3], evar[efac->v4], NULL, NULL); + else + efa= addfacelist(em, evar[efac->v1], evar[efac->v2], evar[efac->v3], NULL, NULL ,NULL); + + efa->mat_nr= efac->mat_nr; + efa->flag= efac->flag; + efa->f= efac->f; + efa->h= efac->h; + efa->fgonf= efac->fgonf; + + CustomData_to_em_block(&um->fdata, &em->fdata, a, &efa->data); + } + + end_editmesh_fastmalloc(); + if(evar) MEM_freeN(evar); + + G.totvert = um->totvert; + G.totedge = um->totedge; + G.totface = um->totface; + /*restore stored editselections*/ + if(um->totsel){ + EM_init_index_arrays(em, 1,1,1); + for(a=0, esec= um->selected; a<um->totsel; a++, esec++){ + ese = MEM_callocN(sizeof(EditSelection), "Edit Selection"); + ese->type = esec->type; + if(ese->type == EDITVERT) ese->data = EM_get_vert_for_index(esec->index); else + if(ese->type == EDITEDGE) ese->data = EM_get_edge_for_index(esec->index); else + if(ese->type == EDITFACE) ese->data = EM_get_face_for_index(esec->index); + BLI_addtail(&(em->selected),ese); + } + EM_free_index_arrays(); + } + +// XXX retopo_free_paint(); +// em->retopo_paint_data= retopo_paint_data_copy(um->retopo_paint_data); +// scene->toolsettings->retopo_mode= um->retopo_mode; +// if(scene->toolsettings->retopo_mode) { +// XXX if(G.vd->depths) G.vd->depths->damaged= 1; +// retopo_queue_updates(G.vd); +// retopo_paint_view_update(G.vd); +// } + + { + Mesh *me= get_mesh(G.obedit); + multires_free(me->mr); + me->mr= NULL; + if(um->mru && um->mru->mr) me->mr= multires_copy(um->mru->mr); + } +} + + +/* and this is all the undo system needs to know */ +void undo_push_mesh(char *name) +{ + undo_editmode_push(name, free_undoMesh, undoMesh_to_editMesh, editMesh_to_undoMesh, NULL); +} + + + +/* *************** END UNDO *************/ + +static EditVert **g_em_vert_array = NULL; +static EditEdge **g_em_edge_array = NULL; +static EditFace **g_em_face_array = NULL; + +void EM_init_index_arrays(EditMesh *em, int forVert, int forEdge, int forFace) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + int i; + + if (forVert) { + g_em_vert_array = MEM_mallocN(sizeof(*g_em_vert_array)*G.totvert, "em_v_arr"); + + for (i=0,eve=em->verts.first; eve; i++,eve=eve->next) + g_em_vert_array[i] = eve; + } + + if (forEdge) { + g_em_edge_array = MEM_mallocN(sizeof(*g_em_edge_array)*G.totedge, "em_e_arr"); + + for (i=0,eed=em->edges.first; eed; i++,eed=eed->next) + g_em_edge_array[i] = eed; + } + + if (forFace) { + g_em_face_array = MEM_mallocN(sizeof(*g_em_face_array)*G.totface, "em_f_arr"); + + for (i=0,efa=em->faces.first; efa; i++,efa=efa->next) + g_em_face_array[i] = efa; + } +} + +void EM_free_index_arrays(void) +{ + if (g_em_vert_array) MEM_freeN(g_em_vert_array); + if (g_em_edge_array) MEM_freeN(g_em_edge_array); + if (g_em_face_array) MEM_freeN(g_em_face_array); + g_em_vert_array = NULL; + g_em_edge_array = NULL; + g_em_face_array = NULL; +} + +EditVert *EM_get_vert_for_index(int index) +{ + return g_em_vert_array?g_em_vert_array[index]:NULL; +} + +EditEdge *EM_get_edge_for_index(int index) +{ + return g_em_edge_array?g_em_edge_array[index]:NULL; +} + +EditFace *EM_get_face_for_index(int index) +{ + return g_em_face_array?g_em_face_array[index]:NULL; +} + +/* can we edit UV's for this mesh?*/ +int EM_texFaceCheck(EditMesh *em) +{ + /* some of these checks could be a touch overkill */ + if ( (G.obedit) && + (G.obedit->type == OB_MESH) && + (em) && + (em->faces.first) && + (CustomData_has_layer(&em->fdata, CD_MTFACE))) + return 1; + return 0; +} + +/* can we edit colors for this mesh?*/ +int EM_vertColorCheck(EditMesh *em) +{ + /* some of these checks could be a touch overkill */ + if ( (G.obedit) && + (G.obedit->type == OB_MESH) && + (em) && + (em->faces.first) && + (CustomData_has_layer(&em->fdata, CD_MCOL))) + return 1; + return 0; +} + diff --git a/source/blender/editors/mesh/editmesh.h b/source/blender/editors/mesh/editmesh.h new file mode 100644 index 00000000000..a57408b87be --- /dev/null +++ b/source/blender/editors/mesh/editmesh.h @@ -0,0 +1,202 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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 LICENSE BLOCK ***** + */ + +/* Internal for editmesh_xxxx.c functions */ + +#ifndef EDITMESH_H +#define EDITMESH_H + +struct View3D; + +#define TEST_EDITMESH if(G.obedit==0) return; /* layer test XXX */ + +#define UVCOPY(t, s) memcpy(t, s, 2 * sizeof(float)); + +/* ******************** editface.c */ + +int edgetag_context_check(Scene *scene, EditEdge *eed); +void edgetag_context_set(Scene *scene, EditEdge *eed, int val); +int edgetag_shortest_path(Scene *scene, EditMesh *em, EditEdge *source, EditEdge *target); + +/* ******************* meshtools.c */ + +intptr_t mesh_octree_table(Object *ob, EditMesh *em, float *co, char mode); +EditVert *editmesh_get_x_mirror_vert(Object *ob, EditMesh *em, float *co); +int mesh_get_x_mirror_vert(Object *ob, int index); + +/* XXX move to uv editor? */ +enum { + B_UVAUTO_REDRAW = 3301, + B_UVAUTO_SPHERE, + B_UVAUTO_CYLINDER, + B_UVAUTO_CYLRADIUS, + B_UVAUTO_WINDOW, + B_UVAUTO_CUBE, + B_UVAUTO_CUBESIZE, + B_UVAUTO_RESET, + B_UVAUTO_BOUNDS, + B_UVAUTO_TOP, + B_UVAUTO_FACE, + B_UVAUTO_OBJECT, + B_UVAUTO_ALIGNX, + B_UVAUTO_ALIGNY, + B_UVAUTO_UNWRAP, + B_UVAUTO_DRAWFACES +}; + + +/* ******************* editmesh.c */ +void make_editMesh(Scene *scene, EditMesh *em); +void load_editMesh(Scene *scene, EditMesh *em); +void remake_editMesh(Scene *scene, EditMesh *em); + +extern void free_editvert(EditMesh *em, EditVert *eve); +extern void free_editedge(EditMesh *em, EditEdge *eed); +extern void free_editface(EditMesh *em, EditFace *efa); +void free_editMesh(EditMesh *em); + +extern void free_vertlist(EditMesh *em, ListBase *edve); +extern void free_edgelist(EditMesh *em, ListBase *lb); +extern void free_facelist(EditMesh *em, ListBase *lb); + +extern void remedge(EditMesh *em, EditEdge *eed); + +extern struct EditVert *addvertlist(EditMesh *em, float *vec, struct EditVert *example); +extern struct EditEdge *addedgelist(EditMesh *em, struct EditVert *v1, struct EditVert *v2, struct EditEdge *example); +extern struct EditFace *addfacelist(EditMesh *em, struct EditVert *v1, struct EditVert *v2, struct EditVert *v3, struct EditVert *v4, struct EditFace *example, struct EditFace *exampleEdges); +extern struct EditEdge *findedgelist(EditMesh *em, struct EditVert *v1, struct EditVert *v2); + +EditVert *editedge_getOtherVert(EditEdge *eed, EditVert *eve); +EditVert *editedge_getSharedVert(EditEdge *eed, EditEdge *eed2); +int editedge_containsVert(struct EditEdge *eed, struct EditVert *eve); +int editface_containsVert(struct EditFace *efa, struct EditVert *eve); +int editface_containsEdge(struct EditFace *efa, struct EditEdge *eed); + +/* ******************* editmesh_add.c */ + + +/* ******************* editmesh_lib.c */ +extern void EM_fgon_flags(EditMesh *em); +extern void EM_hide_reset(EditMesh *em); + +extern int faceselectedOR(EditFace *efa, int flag); +extern int faceselectedAND(EditFace *efa, int flag); + +void EM_remove_selection(EditMesh *em, void *data, int type); +void EM_set_actFace(EditMesh *em, EditFace *efa); +void EM_select_face(EditFace *efa, int sel); +void EM_selectmode_set(EditMesh *em); +void EM_clear_flag_all(EditMesh *em, int flag); +void EM_select_flush(EditMesh *em); +void EM_set_flag_all(EditMesh *em, int flag); +void EM_convertsel(EditMesh *em, short oldmode, short selectmode); + +void EM_add_data_layer(EditMesh *em, CustomData *data, int type); + +void EM_data_interp_from_verts(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *eve, float fac); +void EM_data_interp_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, EditFace *efan, int i1, int i2, int i3, int i4); + +int EM_nvertices_selected(EditMesh *em); +int EM_nfaces_selected(EditMesh *em); +float EM_face_area(EditFace *efa); +float EM_face_perimeter(EditFace *efa); + +void EM_store_selection(EditMesh *em, void *data, int type); + +extern EditFace *exist_face(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4); +extern void flipface(EditMesh *em, EditFace *efa); // flips for normal direction +extern int compareface(EditFace *vl1, EditFace *vl2); + +void recalc_editnormals(EditMesh *em); + +/* flag for selection bits, *nor will be filled with normal for extrusion constraint */ +/* return value defines if such normal was set */ +extern short extrudeflag_face_indiv(EditMesh *em, short flag, float *nor); +extern short extrudeflag_verts_indiv(EditMesh *em, short flag, float *nor); +extern short extrudeflag_edges_indiv(EditMesh *em, short flag, float *nor); +extern short extrudeflag_vert(EditMesh *em, short flag, float *nor); +extern short extrudeflag(EditMesh *em, short flag, float *nor); + +extern void adduplicateflag(EditMesh *em, int flag); +extern void delfaceflag(EditMesh *em, int flag); + +extern void rotateflag(EditMesh *em, short flag, float *cent, float rotmat[][3]); +extern void translateflag(EditMesh *em, short flag, float *vec); + +extern int convex(float *v1, float *v2, float *v3, float *v4); + +extern struct EditFace *EM_face_from_faces(EditMesh *em, struct EditFace *efa1, + struct EditFace *efa2, int i1, int i2, int i3, int i4); + + +/* ******************* editmesh_loop.c */ + +#define KNIFE_PROMPT 0 +#define KNIFE_EXACT 1 +#define KNIFE_MIDPOINT 2 +#define KNIFE_MULTICUT 3 + +#define LOOP_SELECT 1 +#define LOOP_CUT 2 + + +/* ******************* editmesh_mods.c */ +extern EditEdge *findnearestedge(struct View3D *v3d, EditMesh *em, int *dist); +extern void EM_automerge(int update); +void editmesh_select_by_material(EditMesh *em, int index); +void righthandfaces(EditMesh *em, int select); /* makes faces righthand turning */ +void EM_select_more(EditMesh *em); + +/** + * findnearestvert + * + * dist (in/out): minimal distance to the nearest and at the end, actual distance + * sel: selection bias + * if SELECT, selected vertice are given a 5 pixel bias to make them farter than unselect verts + * if 0, unselected vertice are given the bias + * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased + */ +extern EditVert *findnearestvert(struct View3D *v3d, EditMesh *em, int *dist, short sel, short strict); + + +/* ******************* editmesh_tools.c */ + +#define SUBDIV_SELECT_ORIG 0 +#define SUBDIV_SELECT_INNER 1 +#define SUBDIV_SELECT_INNER_SEL 2 +#define SUBDIV_SELECT_LOOPCUT 3 + +void join_triangles(EditMesh *em); +int removedoublesflag(EditMesh *em, short flag, short automerge, float limit); /* return amount */ +void esubdivideflag(EditMesh *em, int flag, float rad, int beauty, int numcuts, int seltype); +int EdgeSlide(EditMesh *em, short immediate, float imperc); + + +#endif + diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c new file mode 100644 index 00000000000..1e72efd11cc --- /dev/null +++ b/source/blender/editors/mesh/editmesh_add.c @@ -0,0 +1,1413 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "BIF_retopo.h" + +#include "ED_mesh.h" +#include "ED_multires.h" +#include "ED_view3d.h" + +#include "editmesh.h" + +/* bpymenu removed XXX */ + +/* XXX */ +static void BIF_undo_push() {} +static void waitcursor() {} +static void error() {} +static int pupmenu() {return 0;} +#define add_numbut(a, b, c, d, e, f, g) {} +static int do_clever_numbuts() {return 0;} +static void check_editmode() {} +static void exit_editmode() {} +/* XXX */ + +static float icovert[12][3] = { + {0,0,-200}, + {144.72, -105.144,-89.443}, + {-55.277, -170.128,-89.443}, + {-178.885,0,-89.443}, + {-55.277,170.128,-89.443}, + {144.72,105.144,-89.443}, + {55.277,-170.128,89.443}, + {-144.72,-105.144,89.443}, + {-144.72,105.144,89.443}, + {55.277,170.128,89.443}, + {178.885,0,89.443}, + {0,0,200} +}; +static short icoface[20][3] = { + {1,0,2}, + {1,0,5}, + {2,0,3}, + {3,0,4}, + {4,0,5}, + {1,5,10}, + {2,1,6}, + {3,2,7}, + {4,3,8}, + {5,4,9}, + {10,1,6}, + {6,2,7}, + {7,3,8}, + {8,4,9}, + {9,5,10}, + {6,10,11}, + {7,6,11}, + {8,7,11}, + {9,8,11}, + {10,9,11} +}; + +static void get_view_aligned_coordinate(float *fp, short mval[2]) +{ +// float dvec[3]; +// short mx, my; + +// mx= mval[0]; +// my= mval[1]; + +// XXX project_short_noclip(ar, v3d, fp, mval); + +// XXX initgrabz(fp[0], fp[1], fp[2]); + +// if(mval[0]!=IS_CLIPPED) { +// window_to_3d(dvec, mval[0]-mx, mval[1]-my); +// VecSubf(fp, fp, dvec); +// } +} + +void add_click_mesh(Scene *scene, EditMesh *em) +{ + View3D *v3d= NULL; // XXX + EditVert *eve, *v1; + float min[3], max[3]; + int done= 0; + + TEST_EDITMESH + if(multires_test()) return; + + INIT_MINMAX(min, max); + + for(v1= em->verts.first;v1; v1=v1->next) { + if(v1->f & SELECT) { + DO_MINMAX(v1->co, min, max); + done= 1; + } + } + + /* call extrude? */ + if(done) { + EditEdge *eed; + float vec[3], cent[3], mat[3][3]; + float nor[3]= {0.0, 0.0, 0.0}; + short mval[2]; + + /* check for edges that are half selected, use for rotation */ + done= 0; + for(eed= em->edges.first; eed; eed= eed->next) { + if( (eed->v1->f & SELECT)+(eed->v2->f & SELECT) == SELECT ) { + if(eed->v1->f & SELECT) VecSubf(vec, eed->v1->co, eed->v2->co); + else VecSubf(vec, eed->v2->co, eed->v1->co); + VecAddf(nor, nor, vec); + done= 1; + } + } + if(done) Normalize(nor); + + /* center */ + VecAddf(cent, min, max); + VecMulf(cent, 0.5f); + VECCOPY(min, cent); + + Mat4MulVecfl(G.obedit->obmat, min); // view space + get_view_aligned_coordinate(min, mval); + Mat4Invert(G.obedit->imat, G.obedit->obmat); + Mat4MulVecfl(G.obedit->imat, min); // back in object space + + VecSubf(min, min, cent); + + /* calculate rotation */ + Mat3One(mat); + if(done) { + float dot; + + VECCOPY(vec, min); + Normalize(vec); + dot= INPR(vec, nor); + + if( fabs(dot)<0.999) { + float cross[3], si, q1[4]; + + Crossf(cross, nor, vec); + Normalize(cross); + dot= 0.5f*saacos(dot); + si= (float)sin(dot); + q1[0]= (float)cos(dot); + q1[1]= cross[0]*si; + q1[2]= cross[1]*si; + q1[3]= cross[2]*si; + + QuatToMat3(q1, mat); + } + } + + extrudeflag(em, SELECT, nor); + rotateflag(em, SELECT, cent, mat); + translateflag(em, SELECT, min); + + recalc_editnormals(em); + } + else { + float mat[3][3],imat[3][3]; + float *curs= give_cursor(scene, v3d); + + eve= addvertlist(em, 0, NULL); + + Mat3CpyMat4(mat, G.obedit->obmat); + Mat3Inv(imat, mat); + + VECCOPY(eve->co, curs); + VecSubf(eve->co, eve->co, G.obedit->obmat[3]); + + Mat3MulVecfl(imat, eve->co); + + eve->f= SELECT; + } + + retopo_do_all(); + + BIF_undo_push("Add vertex/edge/face"); +// XXX DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + + while(0); // XXX get_mbut()&R_MOUSE); + +} + +/* selected faces get hidden edges */ +static void make_fgon(EditMesh *em, int make) +{ + EditFace *efa; + EditEdge *eed; + EditVert *eve; + float *nor=NULL; // reference + int done=0; + + if(!make) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + efa->fgonf= 0; + efa->e1->h &= ~EM_FGON; + efa->e2->h &= ~EM_FGON; + efa->e3->h &= ~EM_FGON; + if(efa->e4) efa->e4->h &= ~EM_FGON; + } + } + EM_fgon_flags(em); // redo flags and indices for fgons +// XXX DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Clear FGon"); + return; + } + + /* tagging edges. rule is: + - edge used by exactly 2 selected faces + - no vertices allowed with only tagged edges (return) + - face normals are allowed to difffer + + */ + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; // amount of selected + eed->f2= 0; // amount of unselected + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + if(nor==NULL) nor= efa->n; + if(efa->e1->f1 < 3) efa->e1->f1++; + if(efa->e2->f1 < 3) efa->e2->f1++; + if(efa->e3->f1 < 3) efa->e3->f1++; + if(efa->e4 && efa->e4->f1 < 3) efa->e4->f1++; + } + else { + if(efa->e1->f2 < 3) efa->e1->f2++; + if(efa->e2->f2 < 3) efa->e2->f2++; + if(efa->e3->f2 < 3) efa->e3->f2++; + if(efa->e4 && efa->e4->f2 < 3) efa->e4->f2++; + } + } + // now eed->f1 becomes tagged edge + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1==2 && eed->f2==0) eed->f1= 1; + else eed->f1= 0; + } + + // no vertices allowed with only tagged edges + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1) { + eed->v1->f1 |= 1; + eed->v2->f1 |= 1; + } + else { + eed->v1->f1 |= 2; + eed->v2->f1 |= 2; + } + } + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f1==1) break; + } + if(eve) { + error("Cannot make polygon with interior vertices"); + return; + } + + // check for faces + if(nor==NULL) { + error("No faces selected to make FGon"); + return; + } + + // and there we go + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1) { + eed->h |= EM_FGON; + done= 1; + } + } + + if(done==0) { + error("Didn't find FGon to create"); + } + else { + EM_fgon_flags(em); // redo flags and indices for fgons + +// XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Make FGon"); + } +} + +/* precondition; 4 vertices selected, check for 4 edges and create face */ +static EditFace *addface_from_edges(EditMesh *em) +{ + EditEdge *eed, *eedar[4]={NULL, NULL, NULL, NULL}; + EditVert *v1=NULL, *v2=NULL, *v3=NULL, *v4=NULL; + int a; + + /* find the 4 edges */ + for(eed= em->edges.first; eed; eed= eed->next) { + if( (eed->f & SELECT) || (eed->v1->f & eed->v2->f & SELECT) ) { + if(eedar[0]==NULL) eedar[0]= eed; + else if(eedar[1]==NULL) eedar[1]= eed; + else if(eedar[2]==NULL) eedar[2]= eed; + else eedar[3]= eed; + + } + } + + + if(eedar[3]) { + /* first 2 points */ + v1= eedar[0]->v1; + v2= eedar[0]->v2; + + /* find the 2 edges connected to first edge */ + for(a=1; a<4; a++) { + if( eedar[a]->v1 == v2) v3= eedar[a]->v2; + else if(eedar[a]->v2 == v2) v3= eedar[a]->v1; + else if( eedar[a]->v1 == v1) v4= eedar[a]->v2; + else if(eedar[a]->v2 == v1) v4= eedar[a]->v1; + } + + /* verify if last edge exists */ + if(v3 && v4) { + for(a=1; a<4; a++) { + if( eedar[a]->v1==v3 && eedar[a]->v2==v4) break; + if( eedar[a]->v2==v3 && eedar[a]->v1==v4) break; + } + if(a!=4) { + return addfacelist(em, v1, v2, v3, v4, NULL, NULL); + } + } + } + return NULL; +} + +/* this also allows to prevent triangles being made in quads */ +static int compareface_overlaps(EditFace *vl1, EditFace *vl2) +{ + EditVert *v1, *v2, *v3, *v4; + int equal= 0; + + v1= vl2->v1; + v2= vl2->v2; + v3= vl2->v3; + v4= vl2->v4; + + if(vl1==vl2) return 0; + + if(v4==NULL && vl1->v4==NULL) { + if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1) equal++; + if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2) equal++; + if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3) equal++; + } + else { + if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1 || vl1->v4==v1) equal++; + if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2 || vl1->v4==v2) equal++; + if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3 || vl1->v4==v3) equal++; + if(vl1->v1==v4 || vl1->v2==v4 || vl1->v3==v4 || vl1->v4==v4) equal++; + } + + if(v4 && vl1->v4) { + if(equal==4) return 1; + } + else + if(equal>=3) return 1; + + return 0; +} + +/* checks for existance, and for tria overlapping inside quad */ +static EditFace *exist_face_overlaps(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4) +{ + EditFace *efa, efatest; + + efatest.v1= v1; + efatest.v2= v2; + efatest.v3= v3; + efatest.v4= v4; + + efa= em->faces.first; + while(efa) { + if(compareface_overlaps(&efatest, efa)) return efa; + efa= efa->next; + } + return NULL; +} + +/* will be new face smooth or solid? depends on smoothness of face neighbours + * of new face, if function return 1, then new face will be smooth, when functio + * will return zero, then new face will be solid */ +static void fix_new_face(EditMesh *em, EditFace *eface) +{ + struct EditFace *efa; + struct EditEdge *eed=NULL; + struct EditVert *v1 = eface->v1, *v2 = eface->v2, *v3 = eface->v3, *v4 = eface->v4; + struct EditVert *ev1=NULL, *ev2=NULL; + short smooth=0; /* "total smoothnes" of faces in neighbourhood */ + short coef; /* "weight" of smoothness */ + short count=0; /* number of edges with same direction as eface */ + short vi00=0, vi01=0, vi10=0, vi11=0; /* vertex indexes */ + + efa = em->faces.first; + + while(efa) { + + if(efa==eface) { + efa = efa->next; + continue; + } + + coef = 0; + ev1 = ev2 = NULL; + eed = NULL; + + if(efa->v1==v1 || efa->v2==v1 || efa->v3==v1 || efa->v4==v1) { + ev1 = v1; + coef++; + } + if(efa->v1==v2 || efa->v2==v2 || efa->v3==v2 || efa->v4==v2) { + if(ev1) ev2 = v2; + else ev1 = v2; + coef++; + } + if(efa->v1==v3 || efa->v2==v3 || efa->v3==v3 || efa->v4==v3) { + if(coef<2) { + if(ev1) ev2 = v3; + else ev1 = v3; + } + coef++; + } + if((v4) && (efa->v1==v4 || efa->v2==v4 || efa->v3==v4 || efa->v4==v4)) { + if(ev1 && coef<2) ev2 = v4; + coef++; + } + + /* "democracy" of smoothness */ + if(efa->flag & ME_SMOOTH) + smooth += coef; + else + smooth -= coef; + + /* try to find edge using vertexes ev1 and ev2 */ + if((ev1) && (ev2) && (ev1!=ev2)) eed = findedgelist(em, ev1, ev2); + + /* has bordering edge of efa same direction as edge of eface ? */ + if(eed) { + if(eed->v1==v1) vi00 = 1; + else if(eed->v1==v2) vi00 = 2; + else if(eed->v1==v3) vi00 = 3; + else if(v4 && eed->v1==v4) vi00 = 4; + + if(eed->v2==v1) vi01 = 1; + else if(eed->v2==v2) vi01 = 2; + else if(eed->v2==v3) vi01 = 3; + else if(v4 && eed->v2==v4) vi01 = 4; + + if(v4) { + if(vi01==1 && vi00==4) vi00 = 0; + if(vi01==4 && vi00==1) vi01 = 0; + } + else { + if(vi01==1 && vi00==3) vi00 = 0; + if(vi01==3 && vi00==1) vi01 = 0; + } + + if(eed->v1==efa->v1) vi10 = 1; + else if(eed->v1==efa->v2) vi10 = 2; + else if(eed->v1==efa->v3) vi10 = 3; + else if(efa->v4 && eed->v1==efa->v4) vi10 = 4; + + if(eed->v2==efa->v1) vi11 = 1; + else if(eed->v2==efa->v2) vi11 = 2; + else if(eed->v2==efa->v3) vi11 = 3; + else if(efa->v4 && eed->v2==efa->v4) vi11 = 4; + + if(efa->v4) { + if(vi11==1 && vi10==4) vi10 = 0; + if(vi11==4 && vi10==1) vi11 = 0; + } + else { + if(vi11==1 && vi10==3) vi10 = 0; + if(vi11==3 && vi10==1) vi11 = 0; + } + + if(((vi00>vi01) && (vi10>vi11)) || + ((vi00<vi01) && (vi10<vi11))) + count++; + else + count--; + } + + efa = efa->next; + } + + /* set up smoothness according voting of face in neighbourhood */ + if(smooth >= 0) + eface->flag |= ME_SMOOTH; + else + eface->flag &= ~ME_SMOOTH; + + /* flip face, when too much "face normals" in neighbourhood is different */ + if(count > 0) { + flipface(em, eface); + } +} + +void addfaces_from_edgenet(EditMesh *em) +{ + EditVert *eve1, *eve2, *eve3, *eve4; + + for(eve1= em->verts.first; eve1; eve1= eve1->next) { + for(eve2= em->verts.first; (eve1->f & 1) && eve2; eve2= eve2->next) { + if(findedgelist(em, eve1,eve2)) { + for(eve3= em->verts.first; (eve2->f & 1) && eve3; eve3= eve3->next) { + if((eve2!=eve3 && (eve3->f & 1) && findedgelist(em, eve1,eve3))) { + EditEdge *sh_edge= NULL; + EditVert *sh_vert= NULL; + + sh_edge= findedgelist(em, eve2,eve3); + + if(sh_edge) { /* Add a triangle */ + if(!exist_face_overlaps(em, eve1,eve2,eve3,NULL)) + fix_new_face(em, addfacelist(em, eve1,eve2,eve3,NULL,NULL,NULL)); + } + else { /* Check for a shared vertex */ + for(eve4= em->verts.first; eve4; eve4= eve4->next) { + if(eve4!=eve1 && eve4!=eve2 && eve4!=eve3 && (eve4->f & 1) && + !findedgelist(em, eve1,eve4) && findedgelist(em, eve2,eve4) && + findedgelist(em, eve3,eve4)) { + sh_vert= eve4; + break; + } + } + + if(sh_vert) { + if(sh_vert) { + if(!exist_face_overlaps(em, eve1,eve2,eve4,eve3)) + fix_new_face(em, addfacelist(em, eve1,eve2,eve4,eve3,NULL,NULL)); + } + } + } + } + } + } + } + } + + EM_select_flush(em); + + BIF_undo_push("Add faces"); +// XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + +void addedgeface_mesh(EditMesh *em) +{ + EditVert *eve, *neweve[4]; + EditEdge *eed; + EditFace *efa; + short amount=0; + + if(multires_test()) return; + + /* how many selected ? */ + if(em->selectmode & SCE_SELECT_EDGE) { + /* in edge mode finding selected vertices means flushing down edge codes... */ + /* can't make face with only edge selection info... */ + EM_selectmode_set(em); + } + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + amount++; + if(amount>4) break; + neweve[amount-1]= eve; + } + } + + if(amount==2) { + eed= addedgelist(em, neweve[0], neweve[1], NULL); + EM_select_edge(eed, 1); + BIF_undo_push("Add edge"); + + // XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + return; + } + else if(amount > 4) { + + /* Python Menu removed XXX */ + int ret; + + /* facemenu, will add python items */ + char facemenu[4096]= "Make Faces%t|Auto%x1|Make FGon%x2|Clear FGon%x3"; + + ret= pupmenu(facemenu); + + if(ret==1) addfaces_from_edgenet(em); + else if(ret==2) make_fgon(em, 1); + else if(ret==3) make_fgon(em, 0); + + return; + } + else if(amount<2) { + error("Incorrect number of vertices to make edge/face"); + return; + } + + efa= NULL; // check later + + if(amount==3) { + + if(exist_face_overlaps(em, neweve[0], neweve[1], neweve[2], NULL)==0) { + efa= addfacelist(em, neweve[0], neweve[1], neweve[2], 0, NULL, NULL); + EM_select_face(efa, 1); + } + else error("The selected vertices already form a face"); + } + else if(amount==4) { + /* this test survives when theres 2 triangles */ + if(exist_face(em, neweve[0], neweve[1], neweve[2], neweve[3])==0) { + int tria= 0; + + /* remove trias if they exist, 4 cases.... */ + if(exist_face(em, neweve[0], neweve[1], neweve[2], NULL)) tria++; + if(exist_face(em, neweve[0], neweve[1], neweve[3], NULL)) tria++; + if(exist_face(em, neweve[0], neweve[2], neweve[3], NULL)) tria++; + if(exist_face(em, neweve[1], neweve[2], neweve[3], NULL)) tria++; + + if(tria==2) join_triangles(em); + else if(exist_face_overlaps(em, neweve[0], neweve[1], neweve[2], neweve[3])==0) { + /* If there are 4 Verts, But more selected edges, we need to call addfaces_from_edgenet */ + EditEdge *eedcheck; + int count; + count = 0; + for(eedcheck= em->edges.first; eedcheck; eedcheck= eedcheck->next) { + if(eedcheck->f & SELECT) { + count++; + } + } + + if(count++ > 4){ + addfaces_from_edgenet(em); + return; + } else { + /* if 4 edges exist, we just create the face, convex or not */ + efa= addface_from_edges(em); + if(efa==NULL) { + + /* the order of vertices can be anything, 6 cases to check */ + if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) { + efa= addfacelist(em, neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) { + efa= addfacelist(em, neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) { + efa= addfacelist(em, neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[1]->co, neweve[3]->co, neweve[2]->co) ) { + efa= addfacelist(em, neweve[0], neweve[1], neweve[3], neweve[2], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[3]->co, neweve[2]->co, neweve[1]->co) ) { + efa= addfacelist(em, neweve[0], neweve[3], neweve[2], neweve[1], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[3]->co, neweve[1]->co, neweve[2]->co) ) { + efa= addfacelist(em, neweve[0], neweve[3], neweve[1], neweve[2], NULL, NULL); + } + else printf("cannot find nice quad from concave set of vertices\n"); + } + } + } + else error("The selected vertices already form a face"); + } + else error("The selected vertices already form a face"); + } + + if(efa) { + EM_select_face(efa, 1); + + fix_new_face(em, efa); + + recalc_editnormals(em); + BIF_undo_push("Add face"); + } + +// XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + + +void adduplicate_mesh(EditMesh *em) +{ + + TEST_EDITMESH + if(multires_test()) return; + + waitcursor(1); + + adduplicateflag(em, SELECT); + + waitcursor(0); + + /* We need to force immediate calculation here because + * transform may use derived objects (which are now stale). + * + * This shouldn't be necessary, derived queries should be + * automatically building this data if invalid. Or something. + */ +// XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + object_handle_update(G.obedit); + +// XXX BIF_TransformSetUndo("Add Duplicate"); +// initTransform(TFM_TRANSLATION, CTX_NO_PET); +// Transform(); +} + +/* check whether an object to add mesh to exists, if not, create one +* returns 1 if new object created, else 0 */ +static int confirm_objectExists(EditMesh *em, Mesh **me, float mat[][3] ) +{ + Scene *scene= NULL; // XXX + int newob = 0; + + /* deselectall */ + EM_clear_flag_all(em, SELECT); + + /* if no obedit: new object and enter editmode */ + if(G.obedit==NULL) { + /* add_object actually returns an object ! :-) + But it also stores the added object struct in + G.scene->basact->object (BASACT->object) */ + +// XXX add_object_draw(OB_MESH); + + G.obedit= BASACT->object; + + where_is_object(G.obedit); + + make_editMesh(NULL, em); // XXX + newob= 1; + } + *me = G.obedit->data; + + /* imat and center and size */ + Mat3CpyMat4(mat, G.obedit->obmat); + + return newob; +} + +// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker +// this hack is only used so that scons+mingw + split-sources hack works + // ------------------------------- start copied code +/* these are not the monkeys you are looking for */ +int monkeyo= 4; +int monkeynv= 271; +int monkeynf= 250; +signed char monkeyv[271][3]= { +{-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92}, +{-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83}, +{-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102}, +{-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92}, +{-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74}, +{-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99}, +{-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105}, +{-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100}, +{-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100}, +{-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73}, +{-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45}, +{-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68}, +{-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68}, +{-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80}, +{-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96}, +{-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90}, +{-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85}, +{-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94}, +{-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96}, +{-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95}, +{-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84}, +{-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109}, +{-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88}, +{-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82}, +{-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96}, +{-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96}, +{-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100}, +{-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104}, +{-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71}, +{-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88}, +{-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84}, +{-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81}, +{-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99}, +{-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87}, +{-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100}, +{-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97}, +{-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86}, +{-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96}, +{-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79}, +{-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59}, +{-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37}, +{-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59}, +{-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7}, +{-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9}, +{-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52}, +{-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51}, +{-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55}, +{-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34}, +{-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30}, +{-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7}, +{-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57}, +{-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26}, +{-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16}, +{6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54}, +{4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52}, +{28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28}, +{-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57}, +{31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27}, +{-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35}, +{-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24}, +{-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41}, +{-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41}, +{6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62}, +{7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42}, +{-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48}, +{8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62}, +{33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63}, +{-26,-16,-42},{-17,49,-49}, +}; + +signed char monkeyf[250][4]= { +{27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4}, +{3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6}, +{5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8}, +{7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12}, +{5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12}, +{-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4}, +{10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4}, +{5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23}, +{20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15}, +{12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, +{18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, +{18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44}, +{24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19}, +{16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38}, +{8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39}, +{16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42}, +{19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16}, +{13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32}, +{-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35}, +{34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21}, +{1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11}, +{35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38}, +{-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39}, +{33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34}, +{8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34}, +{-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36}, +{-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27}, +{-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42}, +{25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34}, +{25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26}, +{23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35}, +{-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35}, +{-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58}, +{40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52}, +{-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49}, +{43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24}, +{-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100}, +{-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24}, +{-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110}, +{6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48}, +{17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43}, +{35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6}, +{-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30}, +{4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5}, +{-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13}, +{-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30}, +{-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31}, +{30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35}, +{32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27}, +{25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23}, +{11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35}, +{32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4}, +{-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35}, +{24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33}, +{24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35}, +{11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36}, +{35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39}, +{18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17}, +{24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19}, +{16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30}, +{-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30}, +{23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66}, +{-68,-67,24,-33}, +}; + // ------------------------------- end copied code + + +void make_prim(EditMesh *em, int type, float imat[3][3], int tot, int seg, + int subdiv, float dia, float d, int ext, int fill, + float cent[3]) +{ + /* + * type - for the type of shape + * dia - the radius for cone,sphere cylinder etc. + * d - depth for the cone + * ext - ? + * fill - end capping, and option to fill in circle + * cent[3] - center of the data. + * */ + + EditVert *eve, *v1=NULL, *v2, *v3, *v4=NULL, *vtop, *vdown; + float phi, phid, vec[3]; + float q[4], cmat[3][3], nor[3]= {0.0, 0.0, 0.0}; + short a, b; + + phid= 2*M_PI/tot; + phi= .25*M_PI; + + switch(type) { + case 10: /* grid */ + /* clear flags */ + eve= em->verts.first; + while(eve) { + eve->f= 0; + eve= eve->next; + } + /* one segment first: the X axis */ + phi= 1.0; + phid= 2.0/((float)tot-1); + for(a=0;a<tot;a++) { + vec[0]= cent[0]+dia*phi; + vec[1]= cent[1]- dia; + vec[2]= cent[2]; + Mat3MulVecfl(imat,vec); + eve= addvertlist(em, vec, NULL); + eve->f= 1+2+4; + if (a) { + addedgelist(em, eve->prev, eve, NULL); + } + phi-=phid; + } + /* extrude and translate */ + vec[0]= vec[2]= 0.0; + vec[1]= dia*phid; + Mat3MulVecfl(imat, vec); + for(a=0;a<seg-1;a++) { + extrudeflag_vert(em, 2, nor); // nor unused + translateflag(em, 2, vec); + } + break; + case 11: /* UVsphere */ + + /* clear all flags */ + eve= em->verts.first; + while(eve) { + eve->f= 0; + eve= eve->next; + } + + /* one segment first */ + phi= 0; + phid/=2; + for(a=0; a<=tot; a++) { + vec[0]= dia*sin(phi); + vec[1]= 0.0; + vec[2]= dia*cos(phi); + eve= addvertlist(em, vec, NULL); + eve->f= 1+2+4; + if(a==0) v1= eve; + else addedgelist(em, eve->prev, eve, NULL); + phi+= phid; + } + + /* extrude and rotate */ + phi= M_PI/seg; + q[0]= cos(phi); + q[3]= sin(phi); + q[1]=q[2]= 0; + QuatToMat3(q, cmat); + + for(a=0; a<seg; a++) { + extrudeflag_vert(em, 2, nor); // nor unused + rotateflag(em, 2, v1->co, cmat); + } + + removedoublesflag(em, 4, 0, 0.0001); + + /* and now do imat */ + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + VecAddf(eve->co,eve->co,cent); + Mat3MulVecfl(imat,eve->co); + } + eve= eve->next; + } + break; + case 12: /* Icosphere */ + { + EditVert *eva[12]; + EditEdge *eed; + + /* clear all flags */ + eve= em->verts.first; + while(eve) { + eve->f= 0; + eve= eve->next; + } + dia/=200; + for(a=0;a<12;a++) { + vec[0]= dia*icovert[a][0]; + vec[1]= dia*icovert[a][1]; + vec[2]= dia*icovert[a][2]; + eva[a]= addvertlist(em, vec, NULL); + eva[a]->f= 1+2; + } + for(a=0;a<20;a++) { + EditFace *evtemp; + v1= eva[ icoface[a][0] ]; + v2= eva[ icoface[a][1] ]; + v3= eva[ icoface[a][2] ]; + evtemp = addfacelist(em, v1, v2, v3, 0, NULL, NULL); + evtemp->e1->f = 1+2; + evtemp->e2->f = 1+2; + evtemp->e3->f = 1+2; + } + + dia*=200; + for(a=1; a<subdiv; a++) esubdivideflag(em, 2, dia, 0,1,0); + /* and now do imat */ + eve= em->verts.first; + while(eve) { + if(eve->f & 2) { + VecAddf(eve->co,eve->co,cent); + Mat3MulVecfl(imat,eve->co); + } + eve= eve->next; + } + + // Clear the flag 2 from the edges + for(eed=em->edges.first;eed;eed=eed->next){ + if(eed->f & 2){ + eed->f &= !2; + } + } + } + break; + case 13: /* Monkey */ + { + //extern int monkeyo, monkeynv, monkeynf; + //extern signed char monkeyf[][4]; + //extern signed char monkeyv[][3]; + EditVert **tv= MEM_mallocN(sizeof(*tv)*monkeynv*2, "tv"); + int i; + + for (i=0; i<monkeynv; i++) { + float v[3]; + v[0]= (monkeyv[i][0]+127)/128.0, v[1]= monkeyv[i][1]/128.0, v[2]= monkeyv[i][2]/128.0; + tv[i]= addvertlist(em, v, NULL); + tv[i]->f |= SELECT; + tv[monkeynv+i]= (fabs(v[0]= -v[0])<0.001)?tv[i]:addvertlist(em, v, NULL); + tv[monkeynv+i]->f |= SELECT; + } + for (i=0; i<monkeynf; i++) { + addfacelist(em, tv[monkeyf[i][0]+i-monkeyo], tv[monkeyf[i][1]+i-monkeyo], tv[monkeyf[i][2]+i-monkeyo], (monkeyf[i][3]!=monkeyf[i][2])?tv[monkeyf[i][3]+i-monkeyo]:NULL, NULL, NULL); + addfacelist(em, tv[monkeynv+monkeyf[i][2]+i-monkeyo], tv[monkeynv+monkeyf[i][1]+i-monkeyo], tv[monkeynv+monkeyf[i][0]+i-monkeyo], (monkeyf[i][3]!=monkeyf[i][2])?tv[monkeynv+monkeyf[i][3]+i-monkeyo]:NULL, NULL, NULL); + } + + MEM_freeN(tv); + + /* and now do imat */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + VecAddf(eve->co,eve->co,cent); + Mat3MulVecfl(imat,eve->co); + } + } + recalc_editnormals(em); + } + break; + default: /* all types except grid, sphere... */ + if(ext==0 && type!=7) d= 0; + + /* vertices */ + vtop= vdown= v1= v2= 0; + for(b=0; b<=ext; b++) { + for(a=0; a<tot; a++) { + + vec[0]= cent[0]+dia*sin(phi); + vec[1]= cent[1]+dia*cos(phi); + vec[2]= cent[2]+d; + + Mat3MulVecfl(imat, vec); + eve= addvertlist(em, vec, NULL); + eve->f= SELECT; + if(a==0) { + if(b==0) v1= eve; + else v2= eve; + } + phi+=phid; + } + d= -d; + } + /* center vertices */ + /* type 7, a cone can only have 1 one side filled + * if the cone has no capping, dont add vtop */ + if((fill && type>1) || type == 7) { + VECCOPY(vec,cent); + vec[2]-= -d; + Mat3MulVecfl(imat,vec); + vdown= addvertlist(em, vec, NULL); + if((ext || type==7) && fill) { + VECCOPY(vec,cent); + vec[2]-= d; + Mat3MulVecfl(imat,vec); + vtop= addvertlist(em, vec, NULL); + } + } else { + vdown= v1; + vtop= v2; + } + if(vtop) vtop->f= SELECT; + if(vdown) vdown->f= SELECT; + + /* top and bottom face */ + if(fill || type==7) { + if(tot==4 && (type==0 || type==1)) { + v3= v1->next->next; + if(ext) v4= v2->next->next; + + addfacelist(em, v3, v1->next, v1, v3->next, NULL, NULL); + if(ext) addfacelist(em, v2, v2->next, v4, v4->next, NULL, NULL); + + } + else { + v3= v1; + v4= v2; + for(a=1; a<tot; a++) { + addfacelist(em, vdown, v3, v3->next, 0, NULL, NULL); + v3= v3->next; + if(ext && fill) { + addfacelist(em, vtop, v4, v4->next, 0, NULL, NULL); + v4= v4->next; + } + } + if(type>1) { + addfacelist(em, vdown, v3, v1, 0, NULL, NULL); + if(ext) addfacelist(em, vtop, v4, v2, 0, NULL, NULL); + } + } + } + else if(type==4) { /* we need edges for a circle */ + v3= v1; + for(a=1;a<tot;a++) { + addedgelist(em, v3, v3->next, NULL); + v3= v3->next; + } + addedgelist(em, v3, v1, NULL); + } + /* side faces */ + if(ext) { + v3= v1; + v4= v2; + for(a=1; a<tot; a++) { + addfacelist(em, v3, v3->next, v4->next, v4, NULL, NULL); + v3= v3->next; + v4= v4->next; + } + addfacelist(em, v3, v1, v2, v4, NULL, NULL); + } + else if(type==7 && fill) { + /* add the bottom flat area of the cone + * if capping is disabled dont bother */ + v3= v1; + for(a=1; a<tot; a++) { + addfacelist(em, vtop, v3->next, v3, 0, NULL, NULL); + v3= v3->next; + } + addfacelist(em, vtop, v1, v3, 0, NULL, NULL); + } + } + /* simple selection flush OK, based on fact it's a single model */ + EM_select_flush(em); /* flushes vertex -> edge -> face selection */ + + if(type!=0 && type!=13) + righthandfaces(em, 1); /* otherwise monkey has eyes in wrong direction */ +} + +void add_primitiveMesh(Scene *scene, View3D *v3d, EditMesh *em, int type) +{ + Mesh *me; + float *curs, d, dia, phi, phid, cent[3], imat[3][3], mat[3][3]; + float cmat[3][3]; + static int tot=32, seg=32, subdiv=2, + /* so each type remembers its fill setting */ + fill_circle=0, fill_cone=1, fill_cylinder=1; + + int ext=0, fill=0, totoud, newob=0; + char *undostr="Add Primitive"; + char *name=NULL; + +// if(G.scene->id.lib) return; + + /* this function also comes from an info window */ +// XXX if ELEM(curarea->spacetype, SPACE_VIEW3D, SPACE_INFO); else return; + + if (G.obedit && G.obedit->type==OB_MESH && multires_test()) return; + + /* if editmode exists for other type, it exits */ + check_editmode(OB_MESH); + + if(G.f & (G_VERTEXPAINT+G_TEXTUREPAINT)) { + G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT); + } + + totoud= tot; /* store, and restore when cube/plane */ + + dia= v3d->grid; + d= v3d->grid; + + /* ext==extrudeflag, tot==amount of vertices in basis */ + switch(type) { + case 0: /* plane */ + tot= 4; + ext= 0; + fill= 1; + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Plane"; + undostr="Add Plane"; + break; + case 1: /* cube */ + tot= 4; + ext= 1; + fill= 1; + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Cube"; + undostr="Add Cube"; + break; + case 4: /* circle */ + add_numbut(0, NUM|INT, "Vertices:", 3, 500, &tot, NULL); + add_numbut(1, NUM|FLO, "Radius:", 0.001*v3d->grid, 100*v3d->grid, &dia, NULL); + add_numbut(2, TOG|INT, "Fill", 0, 0, &(fill_circle), NULL); + if (!(do_clever_numbuts("Add Circle", 3, 0))) return; + ext= 0; + fill = fill_circle; + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Circle"; + undostr="Add Circle"; + break; + case 5: /* cylinder */ + d*=2; + add_numbut(0, NUM|INT, "Vertices:", 2, 500, &tot, NULL); + add_numbut(1, NUM|FLO, "Radius:", 0.001*v3d->grid, 100*v3d->grid, &dia, NULL); + add_numbut(2, NUM|FLO, "Depth:", 0.001*v3d->grid, 100*v3d->grid, &d, NULL); + add_numbut(3, TOG|INT, "Cap Ends", 0, 0, &(fill_cylinder), NULL); + if (!(do_clever_numbuts("Add Cylinder", 4, 0))) return; + ext= 1; + fill = fill_cylinder; + d/=2; + newob = confirm_objectExists(em, &me, mat ); + if(newob) { + if (fill) name = "Cylinder"; + else name = "Tube"; + } + undostr="Add Cylinder"; + break; + case 7: /* cone */ + d*=2; + add_numbut(0, NUM|INT, "Vertices:", 2, 500, &tot, NULL); + add_numbut(1, NUM|FLO, "Radius:", 0.001*v3d->grid, 100*v3d->grid, &dia, NULL); + add_numbut(2, NUM|FLO, "Depth:", 0.001*v3d->grid, 100*v3d->grid, &d, NULL); + add_numbut(3, TOG|INT, "Cap End", 0, 0, &(fill_cone), NULL); + if (!(do_clever_numbuts("Add Cone", 4, 0))) return; + d/=2; + ext= 0; + fill = fill_cone; + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Cone"; + undostr="Add Cone"; + break; + case 10: /* grid */ + add_numbut(0, NUM|INT, "X res:", 3, 1000, &tot, NULL); + add_numbut(1, NUM|INT, "Y res:", 3, 1000, &seg, NULL); + if (!(do_clever_numbuts("Add Grid", 2, 0))) return; + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Grid"; + undostr="Add Grid"; + break; + case 11: /* UVsphere */ + add_numbut(0, NUM|INT, "Segments:", 3, 500, &seg, NULL); + add_numbut(1, NUM|INT, "Rings:", 3, 500, &tot, NULL); + add_numbut(2, NUM|FLO, "Radius:", 0.001*v3d->grid, 100*v3d->grid, &dia, NULL); + + if (!(do_clever_numbuts("Add UV Sphere", 3, 0))) return; + + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Sphere"; + undostr="Add UV Sphere"; + break; + case 12: /* Icosphere */ + add_numbut(0, NUM|INT, "Subdivision:", 1, 8, &subdiv, NULL); + add_numbut(1, NUM|FLO, "Radius:", 0.001*v3d->grid, 100*v3d->grid, &dia, NULL); + if (!(do_clever_numbuts("Add Ico Sphere", 2, 0))) return; + + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Sphere"; + undostr="Add Ico Sphere"; + break; + case 13: /* Monkey */ + newob = confirm_objectExists(em, &me, mat ); + if(newob) name = "Suzanne"; + undostr="Add Monkey"; + break; + default: + newob = confirm_objectExists(em, &me, mat ); + break; + } + + if( name!=NULL ) { + rename_id((ID *)G.obedit, name ); + rename_id((ID *)me, name ); + } + + d = -d; + curs= give_cursor(scene, v3d); + VECCOPY(cent, curs); + cent[0]-= G.obedit->obmat[3][0]; + cent[1]-= G.obedit->obmat[3][1]; + cent[2]-= G.obedit->obmat[3][2]; + + if ( !(newob) || U.flag & USER_ADD_VIEWALIGNED) Mat3CpyMat4(imat, v3d->viewmat); + else Mat3One(imat); + Mat3MulVecfl(imat, cent); + Mat3MulMat3(cmat, imat, mat); + Mat3Inv(imat,cmat); + + + if(type == 0 || type == 1) /* plane, cube (diameter of 1.41 makes it unit size) */ + dia *= sqrt(2.0); + + phid= 2*M_PI/tot; + phi= .25*M_PI; + + make_prim(em, type, imat, tot, seg, subdiv, dia, d, ext, fill, cent); + + if(type<2) tot = totoud; + + /* simple selection flush OK, based on fact it's a single model */ + EM_select_flush(em); // flushes vertex -> edge -> face selection + + if(type!=0 && type!=13) righthandfaces(em, 1); /* otherwise monkey has eyes in wrong direction... */ + +// XXX DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + /* if a new object was created, it stores it in Mesh, for reload original data and undo */ + if ( !(newob) || U.flag & USER_ADD_EDITMODE) { + if(newob) load_editMesh(scene, em); + } else { + exit_editmode(2); + } + + BIF_undo_push(undostr); +} + diff --git a/source/blender/editors/mesh/editmesh_lib.c b/source/blender/editors/mesh/editmesh_lib.c new file mode 100644 index 00000000000..667c8d5ac45 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_lib.c @@ -0,0 +1,2266 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_lib: generic (no UI, no menus) operations/evaluators for editmesh data + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_utildefines.h" + +#include "ED_mesh.h" + +#include "editmesh.h" + +/* this replaces the active flag used in uv/face mode */ +void EM_set_actFace(EditMesh *em, EditFace *efa) +{ + em->act_face = efa; +} + +EditFace *EM_get_actFace(EditMesh *em, int sloppy) +{ + if (em->act_face) { + return em->act_face; + } else if (sloppy) { + EditFace *efa= NULL; + EditSelection *ese; + + ese = em->selected.last; + for (; ese; ese=ese->prev){ + if(ese->type == EDITFACE) { + efa = (EditFace *)ese->data; + + if (efa->h) efa= NULL; + else break; + } + } + if (efa==NULL) { + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) + break; + } + } + return efa; /* can still be null */ + } + return NULL; +} + +int EM_get_actSelection(EditMesh *em, EditSelection *ese) +{ + EditSelection *ese_last = em->selected.last; + EditFace *efa = EM_get_actFace(em, 0); + + ese->next = ese->prev = NULL; + + if (ese_last) { + if (ese_last->type == EDITFACE) { /* if there is an active face, use it over the last selected face */ + if (efa) { + ese->data = (void *)efa; + } else { + ese->data = ese_last->data; + } + ese->type = EDITFACE; + } else { + ese->data = ese_last->data; + ese->type = ese_last->type; + } + } else if (efa) { /* no */ + ese->data = (void *)efa; + ese->type = EDITFACE; + } else { + ese->data = NULL; + return 0; + } + return 1; +} + +/* ********* Selection History ************ */ +static int EM_check_selection(EditMesh *em, void *data) +{ + EditSelection *ese; + + for(ese = em->selected.first; ese; ese = ese->next){ + if(ese->data == data) return 1; + } + + return 0; +} + +void EM_remove_selection(EditMesh *em, void *data, int type) +{ + EditSelection *ese; + for(ese=em->selected.first; ese; ese = ese->next){ + if(ese->data == data){ + BLI_freelinkN(&(em->selected),ese); + break; + } + } +} + +void EM_store_selection(EditMesh *em, void *data, int type) +{ + EditSelection *ese; + if(!EM_check_selection(em, data)){ + ese = (EditSelection*) MEM_callocN( sizeof(EditSelection), "Edit Selection"); + ese->type = type; + ese->data = data; + BLI_addtail(&(em->selected),ese); + } +} + +void EM_validate_selections(EditMesh *em) +{ + EditSelection *ese, *nextese; + + ese = em->selected.first; + while(ese){ + nextese = ese->next; + if(ese->type == EDITVERT && !(((EditVert*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese); + else if(ese->type == EDITEDGE && !(((EditEdge*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese); + else if(ese->type == EDITFACE && !(((EditFace*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese); + ese = nextese; + } +} + +static void EM_strip_selections(EditMesh *em) +{ + EditSelection *ese, *nextese; + if(!(em->selectmode & SCE_SELECT_VERTEX)){ + ese = em->selected.first; + while(ese){ + nextese = ese->next; + if(ese->type == EDITVERT) BLI_freelinkN(&(em->selected),ese); + ese = nextese; + } + } + if(!(em->selectmode & SCE_SELECT_EDGE)){ + ese=em->selected.first; + while(ese){ + nextese = ese->next; + if(ese->type == EDITEDGE) BLI_freelinkN(&(em->selected), ese); + ese = nextese; + } + } + if(!(em->selectmode & SCE_SELECT_FACE)){ + ese=em->selected.first; + while(ese){ + nextese = ese->next; + if(ese->type == EDITFACE) BLI_freelinkN(&(em->selected), ese); + ese = nextese; + } + } +} + +/* generic way to get data from an EditSelection type +These functions were written to be used by the Modifier widget when in Rotate about active mode, +but can be used anywhere. +EM_editselection_center +EM_editselection_normal +EM_editselection_plane +*/ +void EM_editselection_center(float *center, EditSelection *ese) +{ + if (ese->type==EDITVERT) { + EditVert *eve= ese->data; + VecCopyf(center, eve->co); + } else if (ese->type==EDITEDGE) { + EditEdge *eed= ese->data; + VecAddf(center, eed->v1->co, eed->v2->co); + VecMulf(center, 0.5); + } else if (ese->type==EDITFACE) { + EditFace *efa= ese->data; + VecCopyf(center, efa->cent); + } +} + +void EM_editselection_normal(float *normal, EditSelection *ese) +{ + if (ese->type==EDITVERT) { + EditVert *eve= ese->data; + VecCopyf(normal, eve->no); + } else if (ese->type==EDITEDGE) { + EditEdge *eed= ese->data; + float plane[3]; /* need a plane to correct the normal */ + float vec[3]; /* temp vec storage */ + + VecAddf(normal, eed->v1->no, eed->v2->no); + VecSubf(plane, eed->v2->co, eed->v1->co); + + /* the 2 vertex normals will be close but not at rightangles to the edge + for rotate about edge we want them to be at right angles, so we need to + do some extra colculation to correct the vert normals, + we need the plane for this */ + Crossf(vec, normal, plane); + Crossf(normal, plane, vec); + Normalize(normal); + + } else if (ese->type==EDITFACE) { + EditFace *efa= ese->data; + VecCopyf(normal, efa->n); + } +} + +/* Calculate a plane that is rightangles to the edge/vert/faces normal +also make the plane run allong an axis that is related to the geometry, +because this is used for the manipulators Y axis.*/ +void EM_editselection_plane(float *plane, EditSelection *ese) +{ + if (ese->type==EDITVERT) { + EditVert *eve= ese->data; + float vec[3]={0,0,0}; + + if (ese->prev) { /*use previously selected data to make a usefull vertex plane */ + EM_editselection_center(vec, ese->prev); + VecSubf(plane, vec, eve->co); + } else { + /* make a fake plane thats at rightangles to the normal + we cant make a crossvec from a vec thats the same as the vec + unlikely but possible, so make sure if the normal is (0,0,1) + that vec isnt the same or in the same direction even.*/ + if (eve->no[0]<0.5) vec[0]=1; + else if (eve->no[1]<0.5) vec[1]=1; + else vec[2]=1; + Crossf(plane, eve->no, vec); + } + } else if (ese->type==EDITEDGE) { + EditEdge *eed= ese->data; + + /*the plane is simple, it runs allong the edge + however selecting different edges can swap the direction of the y axis. + this makes it less likely for the y axis of the manipulator + (running along the edge).. to flip less often. + at least its more pradictable */ + if (eed->v2->co[1] > eed->v1->co[1]) /*check which to do first */ + VecSubf(plane, eed->v2->co, eed->v1->co); + else + VecSubf(plane, eed->v1->co, eed->v2->co); + + } else if (ese->type==EDITFACE) { + EditFace *efa= ese->data; + float vec[3]; + if (efa->v4) { /*if its a quad- set the plane along the 2 longest edges.*/ + float vecA[3], vecB[3]; + VecSubf(vecA, efa->v4->co, efa->v3->co); + VecSubf(vecB, efa->v1->co, efa->v2->co); + VecAddf(plane, vecA, vecB); + + VecSubf(vecA, efa->v1->co, efa->v4->co); + VecSubf(vecB, efa->v2->co, efa->v3->co); + VecAddf(vec, vecA, vecB); + /*use the biggest edge length*/ + if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]) + VecCopyf(plane, vec); + } else { + /*start with v1-2 */ + VecSubf(plane, efa->v1->co, efa->v2->co); + + /*test the edge between v2-3, use if longer */ + VecSubf(vec, efa->v2->co, efa->v3->co); + if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]) + VecCopyf(plane, vec); + + /*test the edge between v1-3, use if longer */ + VecSubf(vec, efa->v3->co, efa->v1->co); + if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]) + VecCopyf(plane, vec); + } + } + Normalize(plane); +} + + + +void EM_select_face(EditFace *efa, int sel) +{ + if(sel) { + efa->f |= SELECT; + efa->e1->f |= SELECT; + efa->e2->f |= SELECT; + efa->e3->f |= SELECT; + if(efa->e4) efa->e4->f |= SELECT; + efa->v1->f |= SELECT; + efa->v2->f |= SELECT; + efa->v3->f |= SELECT; + if(efa->v4) efa->v4->f |= SELECT; + } + else { + efa->f &= ~SELECT; + efa->e1->f &= ~SELECT; + efa->e2->f &= ~SELECT; + efa->e3->f &= ~SELECT; + if(efa->e4) efa->e4->f &= ~SELECT; + efa->v1->f &= ~SELECT; + efa->v2->f &= ~SELECT; + efa->v3->f &= ~SELECT; + if(efa->v4) efa->v4->f &= ~SELECT; + } +} + +void EM_select_edge(EditEdge *eed, int sel) +{ + if(sel) { + eed->f |= SELECT; + eed->v1->f |= SELECT; + eed->v2->f |= SELECT; + } + else { + eed->f &= ~SELECT; + eed->v1->f &= ~SELECT; + eed->v2->f &= ~SELECT; + } +} + +void EM_select_face_fgon(EditMesh *em, EditFace *efa, int val) +{ + short index=0; + + if(efa->fgonf==0) EM_select_face(efa, val); + else { + if(efa->e1->fgoni) index= efa->e1->fgoni; + if(efa->e2->fgoni) index= efa->e2->fgoni; + if(efa->e3->fgoni) index= efa->e3->fgoni; + if(efa->v4 && efa->e4->fgoni) index= efa->e4->fgoni; + + if(index==0) printf("wrong fgon select\n"); + + // select all ngon faces with index + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->fgonf) { + if(efa->e1->fgoni==index || efa->e2->fgoni==index || + efa->e3->fgoni==index || (efa->e4 && efa->e4->fgoni==index) ) { + EM_select_face(efa, val); + } + } + } + } +} + + +/* only vertices */ +int faceselectedOR(EditFace *efa, int flag) +{ + if ((efa->v1->f | efa->v2->f | efa->v3->f | (efa->v4?efa->v4->f:0))&flag) { + return 1; + } else { + return 0; + } +} + +// replace with (efa->f & SELECT) +int faceselectedAND(EditFace *efa, int flag) +{ + if ((efa->v1->f & efa->v2->f & efa->v3->f & (efa->v4?efa->v4->f:flag))&flag) { + return 1; + } else { + return 0; + } +} + +int EM_nfaces_selected(EditMesh *em) +{ + EditFace *efa; + int count= 0; + + for (efa= em->faces.first; efa; efa= efa->next) + if (efa->f & SELECT) + count++; + + return count; +} + +#if 0 +static int EM_nedges(EditMesh *em) +{ + EditEdge *eed; + int count= 0; + + for (eed= em->edges.first; eed; eed= eed->next) count++; + return count; +} +#endif + +int EM_nvertices_selected(EditMesh *em) +{ + EditVert *eve; + int count= 0; + + for (eve= em->verts.first; eve; eve= eve->next) + if (eve->f & SELECT) + count++; + + return count; +} + +void EM_clear_flag_all(EditMesh *em, int flag) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + for (eve= em->verts.first; eve; eve= eve->next) eve->f &= ~flag; + for (eed= em->edges.first; eed; eed= eed->next) eed->f &= ~flag; + for (efa= em->faces.first; efa; efa= efa->next) efa->f &= ~flag; + + if(flag & SELECT) BLI_freelistN(&(em->selected)); +} + +void EM_set_flag_all(EditMesh *em, int flag) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + for (eve= em->verts.first; eve; eve= eve->next) if(eve->h==0) eve->f |= flag; + for (eed= em->edges.first; eed; eed= eed->next) if(eed->h==0) eed->f |= flag; + for (efa= em->faces.first; efa; efa= efa->next) if(efa->h==0) efa->f |= flag; + +} + +/* flush for changes in vertices only */ +void EM_deselect_flush(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->f & eed->v2->f & SELECT); + else eed->f &= ~SELECT; + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v4) { + if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT ); + else efa->f &= ~SELECT; + } + else { + if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT ); + else efa->f &= ~SELECT; + } + } +} + + +/* flush selection to edges & faces */ + +/* this only based on coherent selected vertices, for example when adding new + objects. call clear_flag_all() before you select vertices to be sure it ends OK! + +*/ + +void EM_select_flush(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->f & eed->v2->f & SELECT) eed->f |= SELECT; + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v4) { + if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT ) efa->f |= SELECT; + } + else { + if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT ) efa->f |= SELECT; + } + } +} + +/* when vertices or edges can be selected, also make fgon consistant */ +static void check_fgons_selection(EditMesh *em) +{ + EditFace *efa, *efan; + EditEdge *eed; + ListBase *lbar; + int sel, desel, index, totfgon= 0; + + /* count amount of fgons */ + for(eed= em->edges.first; eed; eed= eed->next) + if(eed->fgoni>totfgon) totfgon= eed->fgoni; + + if(totfgon==0) return; + + lbar= MEM_callocN((totfgon+1)*sizeof(ListBase), "listbase array"); + + /* put all fgons in lbar */ + for(efa= em->faces.first; efa; efa= efan) { + efan= efa->next; + index= efa->e1->fgoni; + if(index==0) index= efa->e2->fgoni; + if(index==0) index= efa->e3->fgoni; + if(index==0 && efa->e4) index= efa->e4->fgoni; + if(index) { + BLI_remlink(&em->faces, efa); + BLI_addtail(&lbar[index], efa); + } + } + + /* now check the fgons */ + for(index=1; index<=totfgon; index++) { + /* we count on vertices/faces/edges being set OK, so we only have to set ngon itself */ + sel= desel= 0; + for(efa= lbar[index].first; efa; efa= efa->next) { + if(efa->e1->fgoni==0) { + if(efa->e1->f & SELECT) sel++; + else desel++; + } + if(efa->e2->fgoni==0) { + if(efa->e2->f & SELECT) sel++; + else desel++; + } + if(efa->e3->fgoni==0) { + if(efa->e3->f & SELECT) sel++; + else desel++; + } + if(efa->e4 && efa->e4->fgoni==0) { + if(efa->e4->f & SELECT) sel++; + else desel++; + } + + if(sel && desel) break; + } + + if(sel && desel) sel= 0; + else if(sel) sel= 1; + else sel= 0; + + /* select/deselect and put back */ + for(efa= lbar[index].first; efa; efa= efa->next) { + if(sel) efa->f |= SELECT; + else efa->f &= ~SELECT; + } + addlisttolist(&em->faces, &lbar[index]); + } + + MEM_freeN(lbar); +} + + +/* flush to edges & faces */ + +/* based on select mode it selects edges/faces + assumed is that verts/edges/faces were properly selected themselves + with the calls above +*/ + +void EM_selectmode_flush(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + // flush to edges & faces + if(em->selectmode & SCE_SELECT_VERTEX) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->f & eed->v2->f & SELECT) eed->f |= SELECT; + else eed->f &= ~SELECT; + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v4) { + if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT) efa->f |= SELECT; + else efa->f &= ~SELECT; + } + else { + if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT) efa->f |= SELECT; + else efa->f &= ~SELECT; + } + } + } + // flush to faces + else if(em->selectmode & SCE_SELECT_EDGE) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e4) { + if(efa->e1->f & efa->e2->f & efa->e3->f & efa->e4->f & SELECT) efa->f |= SELECT; + else efa->f &= ~SELECT; + } + else { + if(efa->e1->f & efa->e2->f & efa->e3->f & SELECT) efa->f |= SELECT; + else efa->f &= ~SELECT; + } + } + } + // make sure selected faces have selected edges too, for extrude (hack?) + else if(em->selectmode & SCE_SELECT_FACE) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) EM_select_face(efa, 1); + } + } + + if(!(em->selectmode & SCE_SELECT_FACE)) + check_fgons_selection(em); + +} + +void EM_convertsel(EditMesh *em, short oldmode, short selectmode) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + /*clear flags*/ + for(eve= em->verts.first; eve; eve= eve->next) eve->f1 = 0; + for(eed= em->edges.first; eed; eed= eed->next) eed->f1 = 0; + for(efa= em->faces.first; efa; efa= efa->next) efa->f1 = 0; + + /*have to find out what the selectionmode was previously*/ + if(oldmode == SCE_SELECT_VERTEX) { + if(selectmode == SCE_SELECT_EDGE){ + /*select all edges associated with every selected vertex*/ + for(eed= em->edges.first; eed; eed= eed->next){ + if(eed->v1->f&SELECT) eed->f1 = 1; + else if(eed->v2->f&SELECT) eed->f1 = 1; + } + + for(eed= em->edges.first; eed; eed= eed->next){ + if(eed->f1 == 1) EM_select_edge(eed,1); + } + } + else if(selectmode == SCE_SELECT_FACE){ + /*select all faces associated with every selected vertex*/ + for(efa= em->faces.first; efa; efa= efa->next){ + if(efa->v1->f&SELECT) efa->f1 = 1; + else if(efa->v2->f&SELECT) efa->f1 = 1; + else if(efa->v3->f&SELECT) efa->f1 = 1; + else{ + if(efa->v4){ + if(efa->v4->f&SELECT) efa->f1 =1; + } + } + } + for(efa= em->faces.first; efa; efa= efa->next){ + if(efa->f1 == 1) EM_select_face(efa,1); + } + } + } + + if(oldmode == SCE_SELECT_EDGE){ + if(selectmode == SCE_SELECT_FACE){ + for(efa= em->faces.first; efa; efa= efa->next){ + if(efa->e1->f&SELECT) efa->f1 = 1; + else if(efa->e2->f&SELECT) efa->f1 = 1; + else if(efa->e3->f&SELECT) efa->f1 = 1; + else if(efa->e4){ + if(efa->e4->f&SELECT) efa->f1 = 1; + } + } + for(efa= em->faces.first; efa; efa= efa->next){ + if(efa->f1 == 1) EM_select_face(efa,1); + } + } + } + + check_fgons_selection(em); +} + +/* when switching select mode, makes sure selection is consistant for editing */ +/* also for paranoia checks to make sure edge or face mode works */ +void EM_selectmode_set(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + EM_strip_selections(em); /*strip EditSelections from em->selected that are not relevant to new mode*/ + + if(em->selectmode & SCE_SELECT_VERTEX) { + /* vertices -> edges -> faces */ + for (eed= em->edges.first; eed; eed= eed->next) eed->f &= ~SELECT; + for (efa= em->faces.first; efa; efa= efa->next) efa->f &= ~SELECT; + + EM_select_flush(em); + } + else if(em->selectmode & SCE_SELECT_EDGE) { + /* deselect vertices, and select again based on edge select */ + for(eve= em->verts.first; eve; eve= eve->next) eve->f &= ~SELECT; + for(eed= em->edges.first; eed; eed= eed->next) + if(eed->f & SELECT) EM_select_edge(eed, 1); + /* selects faces based on edge status */ + EM_selectmode_flush(em); + } + else if(em->selectmode & SCE_SELECT_FACE) { + /* deselect eges, and select again based on face select */ + for(eed= em->edges.first; eed; eed= eed->next) EM_select_edge(eed, 0); + + for(efa= em->faces.first; efa; efa= efa->next) + if(efa->f & SELECT) EM_select_face(efa, 1); + } +} + +/* paranoia check, actually only for entering editmode. rule: +- vertex hidden, always means edge is hidden too +- edge hidden, always means face is hidden too +- face hidden, dont change anything +*/ +void EM_hide_reset(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + for(eed= em->edges.first; eed; eed= eed->next) + if(eed->v1->h || eed->v2->h) eed->h |= 1; + + for(efa= em->faces.first; efa; efa= efa->next) + if((efa->e1->h & 1) || (efa->e2->h & 1) || (efa->e3->h & 1) || (efa->e4 && (efa->e4->h & 1))) + efa->h= 1; + +} + +void EM_data_interp_from_verts(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *eve, float fac) +{ + void *src[2]; + float w[2]; + + if (v1->data && v2->data) { + src[0]= v1->data; + src[1]= v2->data; + w[0] = 1.0f-fac; + w[1] = fac; + + CustomData_em_interp(&em->vdata, src, w, NULL, 2, eve->data); + } +} + +void EM_data_interp_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, EditFace *efan, int i1, int i2, int i3, int i4) +{ + float w[2][4][4]; + void *src[2]; + int count = (efa2)? 2: 1; + + if (efa1->data) { + /* set weights for copying from corners directly to other corners */ + memset(w, 0, sizeof(w)); + + w[i1/4][0][i1%4]= 1.0f; + w[i2/4][1][i2%4]= 1.0f; + w[i3/4][2][i3%4]= 1.0f; + if (i4 != -1) + w[i4/4][3][i4%4]= 1.0f; + + src[0]= efa1->data; + src[1]= (efa2)? efa2->data: NULL; + + CustomData_em_interp(&em->fdata, src, NULL, (float*)w, count, efan->data); + } +} + +EditFace *EM_face_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, int i1, int i2, int i3, int i4) +{ + EditFace *efan; + EditVert **v[2]; + + v[0]= &efa1->v1; + v[1]= (efa2)? &efa2->v1: NULL; + + efan= addfacelist(em, v[i1/4][i1%4], v[i2/4][i2%4], v[i3/4][i3%4], + (i4 == -1)? 0: v[i4/4][i4%4], efa1, NULL); + + EM_data_interp_from_faces(em, efa1, efa2, efan, i1, i2, i3, i4); + + return efan; +} + +static void update_data_blocks(EditMesh *em, CustomData *olddata, CustomData *data) +{ + EditFace *efa; + EditVert *eve; + void *block; + + if (data == &em->vdata) { + for(eve= em->verts.first; eve; eve= eve->next) { + block = NULL; + CustomData_em_set_default(data, &block); + CustomData_em_copy_data(olddata, data, eve->data, &block); + CustomData_em_free_block(olddata, &eve->data); + eve->data= block; + } + } + else if (data == &em->fdata) { + for(efa= em->faces.first; efa; efa= efa->next) { + block = NULL; + CustomData_em_set_default(data, &block); + CustomData_em_copy_data(olddata, data, efa->data, &block); + CustomData_em_free_block(olddata, &efa->data); + efa->data= block; + } + } +} + +void EM_add_data_layer(EditMesh *em, CustomData *data, int type) +{ + CustomData olddata; + + olddata= *data; + olddata.layers= (olddata.layers)? MEM_dupallocN(olddata.layers): NULL; + CustomData_add_layer(data, type, CD_CALLOC, NULL, 0); + + update_data_blocks(em, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void EM_free_data_layer(EditMesh *em, CustomData *data, int type) +{ + CustomData olddata; + + olddata= *data; + olddata.layers= (olddata.layers)? MEM_dupallocN(olddata.layers): NULL; + CustomData_free_layer_active(data, type, 0); + + update_data_blocks(em, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +/* ******** EXTRUDE ********* */ + +static void add_normal_aligned(float *nor, float *add) +{ + if( INPR(nor, add) < -0.9999f) + VecSubf(nor, nor, add); + else + VecAddf(nor, nor, add); +} + +static void set_edge_directions_f2(EditMesh *em, int val) +{ + EditFace *efa; + int do_all= 1; + + /* edge directions are used for extrude, to detect direction of edges that make new faces */ + /* we have set 'f2' flags in edges that need to get a direction set (e.g. get new face) */ + /* the val argument differs... so we need it as arg */ + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + do_all= 0; + if(efa->e1->f2<val) { + if(efa->e1->v1 == efa->v1) efa->e1->dir= 0; + else efa->e1->dir= 1; + } + if(efa->e2->f2<val) { + if(efa->e2->v1 == efa->v2) efa->e2->dir= 0; + else efa->e2->dir= 1; + } + if(efa->e3->f2<val) { + if(efa->e3->v1 == efa->v3) efa->e3->dir= 0; + else efa->e3->dir= 1; + } + if(efa->e4 && efa->e4->f2<val) { + if(efa->e4->v1 == efa->v4) efa->e4->dir= 0; + else efa->e4->dir= 1; + } + } + } + /* ok, no faces done... then we at least set it for exterior edges */ + if(do_all) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e1->v1 == efa->v1) efa->e1->dir= 0; + else efa->e1->dir= 1; + if(efa->e2->v1 == efa->v2) efa->e2->dir= 0; + else efa->e2->dir= 1; + if(efa->e3->v1 == efa->v3) efa->e3->dir= 0; + else efa->e3->dir= 1; + if(efa->e4) { + if(efa->e4->v1 == efa->v4) efa->e4->dir= 0; + else efa->e4->dir= 1; + } + } + } +} + +/* individual face extrude */ +/* will use vertex normals for extrusion directions, so *nor is unaffected */ +short extrudeflag_face_indiv(EditMesh *em, short flag, float *nor) +{ + EditVert *eve, *v1, *v2, *v3, *v4; + EditEdge *eed; + EditFace *efa, *nextfa; + + if(G.obedit==0 || get_mesh(G.obedit)==0) return 0; + + /* selected edges with 1 or more selected face become faces */ + /* selected faces each makes new faces */ + /* always remove old faces, keeps volumes manifold */ + /* select the new extrusion, deselect old */ + + /* step 1; init, count faces in edges */ + recalc_editnormals(em); + + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; // new select flag + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f2= 0; // amount of unselected faces + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT); + else { + efa->e1->f2++; + efa->e2->f2++; + efa->e3->f2++; + if(efa->e4) efa->e4->f2++; + } + } + + /* step 2: make new faces from faces */ + for(efa= em->faces.last; efa; efa= efa->prev) { + if(efa->f & SELECT) { + v1= addvertlist(em, efa->v1->co, efa->v1); + v2= addvertlist(em, efa->v2->co, efa->v2); + v3= addvertlist(em, efa->v3->co, efa->v3); + + v1->f1= v2->f1= v3->f1= 1; + VECCOPY(v1->no, efa->n); + VECCOPY(v2->no, efa->n); + VECCOPY(v3->no, efa->n); + if(efa->v4) { + v4= addvertlist(em, efa->v4->co, efa->v4); + v4->f1= 1; + VECCOPY(v4->no, efa->n); + } + else v4= NULL; + + /* side faces, clockwise */ + addfacelist(em, efa->v2, v2, v1, efa->v1, efa, NULL); + addfacelist(em, efa->v3, v3, v2, efa->v2, efa, NULL); + if(efa->v4) { + addfacelist(em, efa->v4, v4, v3, efa->v3, efa, NULL); + addfacelist(em, efa->v1, v1, v4, efa->v4, efa, NULL); + } + else { + addfacelist(em, efa->v1, v1, v3, efa->v3, efa, NULL); + } + /* top face */ + addfacelist(em, v1, v2, v3, v4, efa, NULL); + } + } + + /* step 3: remove old faces */ + efa= em->faces.first; + while(efa) { + nextfa= efa->next; + if(efa->f & SELECT) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextfa; + } + + /* step 4: redo selection */ + EM_clear_flag_all(em, SELECT); + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f1) eve->f |= SELECT; + } + + EM_select_flush(em); + + return 'n'; +} + + +/* extrudes individual edges */ +/* nor is filled with constraint vector */ +short extrudeflag_edges_indiv(EditMesh *em, short flag, float *nor) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + for(eve= em->verts.first; eve; eve= eve->next) eve->tmp.v = NULL; + for(eed= em->edges.first; eed; eed= eed->next) { + eed->tmp.f = NULL; + eed->f2= ((eed->f & flag)!=0); + } + + set_edge_directions_f2(em, 2); + + /* sample for next loop */ + for(efa= em->faces.first; efa; efa= efa->next) { + efa->e1->tmp.f = efa; + efa->e2->tmp.f = efa; + efa->e3->tmp.f = efa; + if(efa->e4) efa->e4->tmp.f = efa; + } + /* make the faces */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f & flag) { + if(eed->v1->tmp.v == NULL) + eed->v1->tmp.v = addvertlist(em, eed->v1->co, eed->v1); + if(eed->v2->tmp.v == NULL) + eed->v2->tmp.v = addvertlist(em, eed->v2->co, eed->v2); + + if(eed->dir==1) + addfacelist(em, eed->v1, eed->v2, + eed->v2->tmp.v, eed->v1->tmp.v, + eed->tmp.f, NULL); + else + addfacelist(em, eed->v2, eed->v1, + eed->v1->tmp.v, eed->v2->tmp.v, + eed->tmp.f, NULL); + + /* for transform */ + if(eed->tmp.f) { + efa = eed->tmp.f; + if (efa->f & SELECT) add_normal_aligned(nor, efa->n); + } + } + } + Normalize(nor); + + /* set correct selection */ + EM_clear_flag_all(em, SELECT); + for(eve= em->verts.last; eve; eve= eve->prev) { + if(eve->tmp.v) { + eve->tmp.v->f |= flag; + } + } + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->f & eed->v2->f & flag) eed->f |= flag; + } + + if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // g is grab + return 'n'; // n is for normal constraint +} + +/* extrudes individual vertices */ +short extrudeflag_verts_indiv(EditMesh *em, short flag, float *nor) +{ + EditVert *eve; + + /* make the edges */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & flag) { + eve->tmp.v = addvertlist(em, eve->co, eve); + addedgelist(em, eve, eve->tmp.v, NULL); + } + else eve->tmp.v = NULL; + } + + /* set correct selection */ + EM_clear_flag_all(em, SELECT); + + for(eve= em->verts.last; eve; eve= eve->prev) + if (eve->tmp.v) + eve->tmp.v->f |= flag; + + return 'g'; // g is grab +} + + +/* this is actually a recode of extrudeflag(), using proper edge/face select */ +/* hurms, doesnt use 'flag' yet, but its not called by primitive making stuff anyway */ +static short extrudeflag_edge(EditMesh *em, short flag, float *nor) +{ + /* all select edges/faces: extrude */ + /* old select is cleared, in new ones it is set */ + EditVert *eve, *nextve; + EditEdge *eed, *nexted; + EditFace *efa, *nextfa, *efan; + short del_old= 0; + ModifierData *md; + + if(G.obedit==0 || get_mesh(G.obedit)==0) return 0; + + md = G.obedit->modifiers.first; + + /* selected edges with 0 or 1 selected face become faces */ + /* selected faces generate new faces */ + + /* if *one* selected face has edge with unselected face; remove old selected faces */ + + /* if selected edge is not used anymore; remove */ + /* if selected vertex is not used anymore: remove */ + + /* select the new extrusion, deselect old */ + + + /* step 1; init, count faces in edges */ + recalc_editnormals(em); + + for(eve= em->verts.first; eve; eve= eve->next) { + eve->tmp.v = NULL; + eve->f1= 0; + } + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; // amount of unselected faces + eed->f2= 0; // amount of selected faces + if(eed->f & SELECT) { + eed->v1->f1= 1; // we call this 'selected vertex' now + eed->v2->f1= 1; + } + eed->tmp.f = NULL; // here we tuck face pointer, as sample + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + efa->e1->f2++; + efa->e2->f2++; + efa->e3->f2++; + if(efa->e4) efa->e4->f2++; + + // sample for next loop + efa->e1->tmp.f = efa; + efa->e2->tmp.f = efa; + efa->e3->tmp.f = efa; + if(efa->e4) efa->e4->tmp.f = efa; + } + else { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + + /* If a mirror modifier with clipping is on, we need to adjust some + * of the cases above to handle edges on the line of symmetry. + */ + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + float mtx[4][4]; + if (mmd->mirror_ob) { + float imtx[4][4]; + Mat4Invert(imtx, mmd->mirror_ob->obmat); + Mat4MulMat4(mtx, G.obedit->obmat, imtx); + } + + for (eed= em->edges.first; eed; eed= eed->next) { + if(eed->f2 == 1) { + float co1[3], co2[3]; + + VecCopyf(co1, eed->v1->co); + VecCopyf(co2, eed->v2->co); + + if (mmd->mirror_ob) { + VecMat4MulVecfl(co1, mtx, co1); + VecMat4MulVecfl(co2, mtx, co2); + } + + if (mmd->flag & MOD_MIR_AXIS_X) + if ( (fabs(co1[0]) < mmd->tolerance) && + (fabs(co2[0]) < mmd->tolerance) ) + ++eed->f2; + + if (mmd->flag & MOD_MIR_AXIS_Y) + if ( (fabs(co1[1]) < mmd->tolerance) && + (fabs(co2[1]) < mmd->tolerance) ) + ++eed->f2; + + if (mmd->flag & MOD_MIR_AXIS_Z) + if ( (fabs(co1[2]) < mmd->tolerance) && + (fabs(co2[2]) < mmd->tolerance) ) + ++eed->f2; + } + } + } + } + } + + set_edge_directions_f2(em, 2); + + /* step 1.5: if *one* selected face has edge with unselected face; remove old selected faces */ + for(efa= em->faces.last; efa; efa= efa->prev) { + if(efa->f & SELECT) { + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1 || (efa->e4 && efa->e4->f1)) { + del_old= 1; + break; + } + } + } + + /* step 2: make new faces from edges */ + for(eed= em->edges.last; eed; eed= eed->prev) { + if(eed->f & SELECT) { + if(eed->f2<2) { + if(eed->v1->tmp.v == NULL) + eed->v1->tmp.v = addvertlist(em, eed->v1->co, eed->v1); + if(eed->v2->tmp.v == NULL) + eed->v2->tmp.v = addvertlist(em, eed->v2->co, eed->v2); + + /* if del_old, the preferred normal direction is exact + * opposite as for keep old faces + */ + if(eed->dir!=del_old) + addfacelist(em, eed->v1, eed->v2, + eed->v2->tmp.v, eed->v1->tmp.v, + eed->tmp.f, NULL); + else + addfacelist(em, eed->v2, eed->v1, + eed->v1->tmp.v, eed->v2->tmp.v, + eed->tmp.f, NULL); + } + } + } + + /* step 3: make new faces from faces */ + for(efa= em->faces.last; efa; efa= efa->prev) { + if(efa->f & SELECT) { + if (efa->v1->tmp.v == NULL) + efa->v1->tmp.v = addvertlist(em, efa->v1->co, efa->v1); + if (efa->v2->tmp.v ==NULL) + efa->v2->tmp.v = addvertlist(em, efa->v2->co, efa->v2); + if (efa->v3->tmp.v ==NULL) + efa->v3->tmp.v = addvertlist(em, efa->v3->co, efa->v3); + if (efa->v4 && (efa->v4->tmp.v == NULL)) + efa->v4->tmp.v = addvertlist(em, efa->v4->co, efa->v4); + + if(del_old==0) { // keep old faces means flipping normal + if(efa->v4) + efan = addfacelist(em, efa->v4->tmp.v, efa->v3->tmp.v, + efa->v2->tmp.v, efa->v1->tmp.v, efa, efa); + else + efan = addfacelist(em, efa->v3->tmp.v, efa->v2->tmp.v, + efa->v1->tmp.v, NULL, efa, efa); + } + else { + if(efa->v4) + efan = addfacelist(em, efa->v1->tmp.v, efa->v2->tmp.v, + efa->v3->tmp.v, efa->v4->tmp.v, efa, efa); + else + efan = addfacelist(em, efa->v1->tmp.v, efa->v2->tmp.v, + efa->v3->tmp.v, NULL, efa, efa); + } + + if (em->act_face == efa) { + em->act_face = efan; + } + + /* for transform */ + add_normal_aligned(nor, efa->n); + } + } + + if(del_old) { + + /* step 4: remove old faces, if del_old */ + efa= em->faces.first; + while(efa) { + nextfa= efa->next; + if(efa->f & SELECT) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextfa; + } + + + /* step 5: remove selected unused edges */ + /* start tagging again */ + for(eed= em->edges.first; eed; eed= eed->next) eed->f1=0; + for(efa= em->faces.first; efa; efa= efa->next) { + efa->e1->f1= 1; + efa->e2->f1= 1; + efa->e3->f1= 1; + if(efa->e4) efa->e4->f1= 1; + } + /* remove */ + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + if(eed->f1==0) { + remedge(em, eed); + free_editedge(em, eed); + } + } + eed= nexted; + } + + /* step 6: remove selected unused vertices */ + for(eed= em->edges.first; eed; eed= eed->next) + eed->v1->f1= eed->v2->f1= 0; + + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f1) { + // hack... but we need it for step 7, redoing selection + if(eve->tmp.v) eve->tmp.v->tmp.v= eve->tmp.v; + + BLI_remlink(&em->verts, eve); + free_editvert(em, eve); + } + eve= nextve; + } + } + + Normalize(nor); // translation normal grab + + /* step 7: redo selection */ + EM_clear_flag_all(em, SELECT); + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->tmp.v) { + eve->tmp.v->f |= SELECT; + } + } + + EM_select_flush(em); + + if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // grab + return 'n'; // normal constraint +} + +short extrudeflag_vert(EditMesh *em, short flag, float *nor) +{ + /* all verts/edges/faces with (f & 'flag'): extrude */ + /* from old verts, 'flag' is cleared, in new ones it is set */ + EditVert *eve, *v1, *v2, *v3, *v4, *nextve; + EditEdge *eed, *e1, *e2, *e3, *e4, *nexted; + EditFace *efa, *efa2, *nextvl; + short sel=0, del_old= 0, is_face_sel=0; + ModifierData *md; + + if(G.obedit==0 || get_mesh(G.obedit)==0) return 0; + + md = G.obedit->modifiers.first; + + /* clear vert flag f1, we use this to detect a loose selected vertice */ + eve= em->verts.first; + while(eve) { + if(eve->f & flag) eve->f1= 1; + else eve->f1= 0; + eve= eve->next; + } + /* clear edges counter flag, if selected we set it at 1 */ + eed= em->edges.first; + while(eed) { + if( (eed->v1->f & flag) && (eed->v2->f & flag) ) { + eed->f2= 1; + eed->v1->f1= 0; + eed->v2->f1= 0; + } + else eed->f2= 0; + + eed->f1= 1; /* this indicates it is an 'old' edge (in this routine we make new ones) */ + eed->tmp.f = NULL; /* used as sample */ + + eed= eed->next; + } + + /* we set a flag in all selected faces, and increase the associated edge counters */ + + efa= em->faces.first; + while(efa) { + efa->f1= 0; + + if(faceselectedAND(efa, flag)) { + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + e4= efa->e4; + + if(e1->f2 < 3) e1->f2++; + if(e2->f2 < 3) e2->f2++; + if(e3->f2 < 3) e3->f2++; + if(e4 && e4->f2 < 3) e4->f2++; + + efa->f1= 1; + is_face_sel= 1; // for del_old + } + else if(faceselectedOR(efa, flag)) { + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + e4= efa->e4; + + if( (e1->v1->f & flag) && (e1->v2->f & flag) ) e1->f1= 2; + if( (e2->v1->f & flag) && (e2->v2->f & flag) ) e2->f1= 2; + if( (e3->v1->f & flag) && (e3->v2->f & flag) ) e3->f1= 2; + if( e4 && (e4->v1->f & flag) && (e4->v2->f & flag) ) e4->f1= 2; + } + + // sample for next loop + efa->e1->tmp.f = efa; + efa->e2->tmp.f = efa; + efa->e3->tmp.f = efa; + if(efa->e4) efa->e4->tmp.f = efa; + + efa= efa->next; + } + + set_edge_directions_f2(em, 3); + + /* the current state now is: + eve->f1==1: loose selected vertex + + eed->f2==0 : edge is not selected, no extrude + eed->f2==1 : edge selected, is not part of a face, extrude + eed->f2==2 : edge selected, is part of 1 face, extrude + eed->f2==3 : edge selected, is part of more faces, no extrude + + eed->f1==0: new edge + eed->f1==1: edge selected, is part of selected face, when eed->f==3: remove + eed->f1==2: edge selected, part of a partially selected face + + efa->f1==1 : duplicate this face + */ + + /* If a mirror modifier with clipping is on, we need to adjust some + * of the cases above to handle edges on the line of symmetry. + */ + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + float mtx[4][4]; + if (mmd->mirror_ob) { + float imtx[4][4]; + Mat4Invert(imtx, mmd->mirror_ob->obmat); + Mat4MulMat4(mtx, G.obedit->obmat, imtx); + } + + for (eed= em->edges.first; eed; eed= eed->next) { + if(eed->f2 == 2) { + float co1[3], co2[3]; + + VecCopyf(co1, eed->v1->co); + VecCopyf(co2, eed->v2->co); + + if (mmd->mirror_ob) { + VecMat4MulVecfl(co1, mtx, co1); + VecMat4MulVecfl(co2, mtx, co2); + } + + if (mmd->flag & MOD_MIR_AXIS_X) + if ( (fabs(co1[0]) < mmd->tolerance) && + (fabs(co2[0]) < mmd->tolerance) ) + ++eed->f2; + + if (mmd->flag & MOD_MIR_AXIS_Y) + if ( (fabs(co1[1]) < mmd->tolerance) && + (fabs(co2[1]) < mmd->tolerance) ) + ++eed->f2; + if (mmd->flag & MOD_MIR_AXIS_Z) + if ( (fabs(co1[2]) < mmd->tolerance) && + (fabs(co2[2]) < mmd->tolerance) ) + ++eed->f2; + } + } + } + } + } + + /* copy all selected vertices, */ + /* write pointer to new vert in old struct at eve->tmp.v */ + eve= em->verts.last; + while(eve) { + eve->f &= ~128; /* clear, for later test for loose verts */ + if(eve->f & flag) { + sel= 1; + v1= addvertlist(em, 0, NULL); + + VECCOPY(v1->co, eve->co); + v1->f= eve->f; + eve->f-= flag; + eve->tmp.v = v1; + } + else eve->tmp.v = 0; + eve= eve->prev; + } + + if(sel==0) return 0; + + /* all edges with eed->f2==1 or eed->f2==2 become faces */ + + /* if del_old==1 then extrude is in partial geometry, to keep it manifold. + verts with f1==0 and (eve->f & 128)==0) are removed + edges with eed->f2>2 are removed + faces with efa->f1 are removed + if del_old==0 the extrude creates a volume. + */ + + /* find if we delete old faces */ + if(is_face_sel) { + for(eed= em->edges.first; eed; eed= eed->next) { + if( (eed->f2==1 || eed->f2==2) ) { + if(eed->f1==2) { + del_old= 1; + break; + } + } + } + } + + eed= em->edges.last; + while(eed) { + nexted= eed->prev; + if( eed->f2<3) { + eed->v1->f |= 128; /* = no loose vert! */ + eed->v2->f |= 128; + } + if( (eed->f2==1 || eed->f2==2) ) { + + /* if del_old, the preferred normal direction is exact opposite as for keep old faces */ + if(eed->dir != del_old) + efa2 = addfacelist(em, eed->v1, eed->v2, + eed->v2->tmp.v, eed->v1->tmp.v, + eed->tmp.f, NULL); + else + efa2 = addfacelist(em, eed->v2, eed->v1, + eed->v1->tmp.v, eed->v2->tmp.v, + eed->tmp.f, NULL); + + /* Needs smarter adaption of existing creases. + * If addedgelist is used, make sure seams are set to 0 on these + * new edges, since we do not want to add any seams on extrusion. + */ + efa2->e1->crease= eed->crease; + efa2->e2->crease= eed->crease; + efa2->e3->crease= eed->crease; + if(efa2->e4) efa2->e4->crease= eed->crease; + } + + eed= nexted; + } + if(del_old) { + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f2==3 && eed->f1==1) { + remedge(em, eed); + free_editedge(em, eed); + } + eed= nexted; + } + } + /* duplicate faces, if necessary remove old ones */ + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1 & 1) { + + v1 = efa->v1->tmp.v; + v2 = efa->v2->tmp.v; + v3 = efa->v3->tmp.v; + if(efa->v4) + v4 = efa->v4->tmp.v; + else + v4= 0; + + /* hmm .. not sure about edges here */ + if(del_old==0) // if we keep old, we flip normal + efa2= addfacelist(em, v3, v2, v1, v4, efa, efa); + else + efa2= addfacelist(em, v1, v2, v3, v4, efa, efa); + + /* for transform */ + add_normal_aligned(nor, efa->n); + + if(del_old) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + } + efa= nextvl; + } + + Normalize(nor); // for grab + + /* for all vertices with eve->tmp.v!=0 + if eve->f1==1: make edge + if flag!=128 : if del_old==1: remove + */ + eve= em->verts.last; + while(eve) { + nextve= eve->prev; + if(eve->tmp.v) { + if(eve->f1==1) addedgelist(em, eve, eve->tmp.v, NULL); + else if( (eve->f & 128)==0) { + if(del_old) { + BLI_remlink(&em->verts,eve); + free_editvert(em, eve); + eve= NULL; + } + } + } + if(eve) { + eve->f &= ~128; + } + eve= nextve; + } + // since its vertex select mode now, it also deselects higher order + EM_selectmode_flush(em); + + if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // g is grab, for correct undo print + return 'n'; +} + +/* generic extrude */ +short extrudeflag(EditMesh *em, short flag, float *nor) +{ + if(em->selectmode & SCE_SELECT_VERTEX) + return extrudeflag_vert(em, flag, nor); + else + return extrudeflag_edge(em, flag, nor); + +} + +void rotateflag(EditMesh *em, short flag, float *cent, float rotmat[][3]) +{ + /* all verts with (flag & 'flag') rotate */ + EditVert *eve; + + eve= em->verts.first; + while(eve) { + if(eve->f & flag) { + eve->co[0]-=cent[0]; + eve->co[1]-=cent[1]; + eve->co[2]-=cent[2]; + Mat3MulVecfl(rotmat,eve->co); + eve->co[0]+=cent[0]; + eve->co[1]+=cent[1]; + eve->co[2]+=cent[2]; + } + eve= eve->next; + } +} + +void translateflag(EditMesh *em, short flag, float *vec) +{ + /* all verts with (flag & 'flag') translate */ + EditVert *eve; + + eve= em->verts.first; + while(eve) { + if(eve->f & flag) { + eve->co[0]+=vec[0]; + eve->co[1]+=vec[1]; + eve->co[2]+=vec[2]; + } + eve= eve->next; + } +} + +/* helper call for below */ +static EditVert *adduplicate_vertex(EditMesh *em, EditVert *eve, int flag) +{ + /* FIXME: copy deformation weight from eve ok here? */ + EditVert *v1= addvertlist(em, eve->co, eve); + + v1->f= eve->f; + eve->f-= flag; + eve->f|= 128; + + eve->tmp.v = v1; + + return v1; +} + +/* old selection has flag 128 set, and flag 'flag' cleared +new selection has flag 'flag' set */ +void adduplicateflag(EditMesh *em, int flag) +{ + EditVert *eve, *v1, *v2, *v3, *v4; + EditEdge *eed, *newed; + EditFace *efa, *newfa, *act_efa = EM_get_actFace(em, 0); + + EM_clear_flag_all(em, 128); + EM_selectmode_set(em); // paranoia check, selection now is consistant + + /* vertices first */ + for(eve= em->verts.last; eve; eve= eve->prev) { + + if(eve->f & flag) + adduplicate_vertex(em, eve, flag); + else + eve->tmp.v = NULL; + } + + /* copy edges, note that vertex selection can be independent of edge */ + for(eed= em->edges.last; eed; eed= eed->prev) { + if( eed->f & flag ) { + v1 = eed->v1->tmp.v; + if(v1==NULL) v1= adduplicate_vertex(em, eed->v1, flag); + v2 = eed->v2->tmp.v; + if(v2==NULL) v2= adduplicate_vertex(em, eed->v2, flag); + + newed= addedgelist(em, v1, v2, eed); + + newed->f= eed->f; + eed->f -= flag; + eed->f |= 128; + } + } + + /* then duplicate faces, again create new vertices if needed */ + for(efa= em->faces.last; efa; efa= efa->prev) { + if(efa->f & flag) { + v1 = efa->v1->tmp.v; + if(v1==NULL) v1= adduplicate_vertex(em, efa->v1, flag); + v2 = efa->v2->tmp.v; + if(v2==NULL) v2= adduplicate_vertex(em, efa->v2, flag); + v3 = efa->v3->tmp.v; + if(v3==NULL) v3= adduplicate_vertex(em, efa->v3, flag); + if(efa->v4) { + v4 = efa->v4->tmp.v; + if(v4==NULL) v4= adduplicate_vertex(em, efa->v4, flag); + } + else v4= NULL; + + newfa= addfacelist(em, v1, v2, v3, v4, efa, efa); + + if (efa==act_efa) { + EM_set_actFace(em, newfa); + } + + newfa->f= efa->f; + efa->f -= flag; + efa->f |= 128; + } + } + + EM_fgon_flags(em); // redo flags and indices for fgons +} + +void delfaceflag(EditMesh *em, int flag) +{ + /* delete all faces with 'flag', including loose edges and loose vertices */ + /* this is maybe a bit weird, but this function is used for 'split' and 'separate' */ + /* in remaining vertices/edges 'flag' is cleared */ + EditVert *eve,*nextve; + EditEdge *eed, *nexted; + EditFace *efa,*nextvl; + + /* to detect loose edges, we put f2 flag on 1 */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f & flag) eed->f2= 1; + else eed->f2= 0; + } + + /* delete faces */ + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f & flag) { + + efa->e1->f2= 1; + efa->e2->f2= 1; + efa->e3->f2= 1; + if(efa->e4) { + efa->e4->f2= 1; + } + + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + + /* all remaining faces: make sure we keep the edges */ + for(efa= em->faces.first; efa; efa= efa->next) { + efa->e1->f2= 0; + efa->e2->f2= 0; + efa->e3->f2= 0; + if(efa->e4) { + efa->e4->f2= 0; + } + } + + /* remove tagged edges, and clear remaining ones */ + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + if(eed->f2==1) { + remedge(em, eed); + free_editedge(em, eed); + } + else { + eed->f &= ~flag; + eed->v1->f &= ~flag; + eed->v2->f &= ~flag; + } + eed= nexted; + } + + /* vertices with 'flag' now are the loose ones, and will be removed */ + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & flag) { + BLI_remlink(&em->verts, eve); + free_editvert(em, eve); + } + eve= nextve; + } + +} + +/* ********************* */ +#if 0 +static int check_vnormal_flip(float *n, float *vnorm) +{ + float inp; + + inp= n[0]*vnorm[0]+n[1]*vnorm[1]+n[2]*vnorm[2]; + + /* angles 90 degrees: dont flip */ + if(inp> -0.000001) return 0; + + return 1; +} +#endif + +void flipface(EditMesh *em, EditFace *efa) +{ + if(efa->v4) { + SWAP(EditVert *, efa->v2, efa->v4); + SWAP(EditEdge *, efa->e1, efa->e4); + SWAP(EditEdge *, efa->e2, efa->e3); + EM_data_interp_from_faces(em, efa, NULL, efa, 0, 3, 2, 1); + } + else { + SWAP(EditVert *, efa->v2, efa->v3); + SWAP(EditEdge *, efa->e1, efa->e3); + efa->e2->dir= 1-efa->e2->dir; + EM_data_interp_from_faces(em, efa, NULL, efa, 0, 2, 1, 3); + } + + if(efa->v4) CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n); + else CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n); +} + + +void flip_editnormals(EditMesh *em) +{ + EditFace *efa; + + efa= em->faces.first; + while(efa) { + if( efa->f & SELECT ){ + flipface(em, efa); + } + efa= efa->next; + } + + /* update vertex normals too */ + recalc_editnormals(em); + +} + +/* does face centers too */ +void recalc_editnormals(EditMesh *em) +{ + EditFace *efa; + EditVert *eve; + + for(eve= em->verts.first; eve; eve=eve->next) { + eve->no[0] = eve->no[1] = eve->no[2] = 0.0; + } + + for(efa= em->faces.first; efa; efa=efa->next) { + if(efa->v4) { + CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n); + CalcCent4f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co); + VecAddf(efa->v4->no, efa->v4->no, efa->n); + } + else { + CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n); + CalcCent3f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co); + } + VecAddf(efa->v1->no, efa->v1->no, efa->n); + VecAddf(efa->v2->no, efa->v2->no, efa->n); + VecAddf(efa->v3->no, efa->v3->no, efa->n); + } + + /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + for(eve= em->verts.first; eve; eve=eve->next) { + if (Normalize(eve->no)==0.0) { + VECCOPY(eve->no, eve->co); + Normalize(eve->no); + } + } +} + +int compareface(EditFace *vl1, EditFace *vl2) +{ + EditVert *v1, *v2, *v3, *v4; + + if(vl1->v4 && vl2->v4) { + v1= vl2->v1; + v2= vl2->v2; + v3= vl2->v3; + v4= vl2->v4; + + if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1 || vl1->v4==v1) { + if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2 || vl1->v4==v2) { + if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3 || vl1->v4==v3) { + if(vl1->v1==v4 || vl1->v2==v4 || vl1->v3==v4 || vl1->v4==v4) { + return 1; + } + } + } + } + } + else if(vl1->v4==0 && vl2->v4==0) { + v1= vl2->v1; + v2= vl2->v2; + v3= vl2->v3; + + if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1) { + if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2) { + if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3) { + return 1; + } + } + } + } + + return 0; +} + +/* checks for existance, not tria overlapping inside quad */ +EditFace *exist_face(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4) +{ + EditFace *efa, efatest; + + efatest.v1= v1; + efatest.v2= v2; + efatest.v3= v3; + efatest.v4= v4; + + efa= em->faces.first; + while(efa) { + if(compareface(&efatest, efa)) return efa; + efa= efa->next; + } + return NULL; +} + +/* evaluate if entire quad is a proper convex quad */ +int convex(float *v1, float *v2, float *v3, float *v4) +{ + float nor[3], nor1[3], nor2[3], vec[4][2]; + + /* define projection, do both trias apart, quad is undefined! */ + CalcNormFloat(v1, v2, v3, nor1); + CalcNormFloat(v1, v3, v4, nor2); + nor[0]= ABS(nor1[0]) + ABS(nor2[0]); + nor[1]= ABS(nor1[1]) + ABS(nor2[1]); + nor[2]= ABS(nor1[2]) + ABS(nor2[2]); + + if(nor[2] >= nor[0] && nor[2] >= nor[1]) { + vec[0][0]= v1[0]; vec[0][1]= v1[1]; + vec[1][0]= v2[0]; vec[1][1]= v2[1]; + vec[2][0]= v3[0]; vec[2][1]= v3[1]; + vec[3][0]= v4[0]; vec[3][1]= v4[1]; + } + else if(nor[1] >= nor[0] && nor[1]>= nor[2]) { + vec[0][0]= v1[0]; vec[0][1]= v1[2]; + vec[1][0]= v2[0]; vec[1][1]= v2[2]; + vec[2][0]= v3[0]; vec[2][1]= v3[2]; + vec[3][0]= v4[0]; vec[3][1]= v4[2]; + } + else { + vec[0][0]= v1[1]; vec[0][1]= v1[2]; + vec[1][0]= v2[1]; vec[1][1]= v2[2]; + vec[2][0]= v3[1]; vec[2][1]= v3[2]; + vec[3][0]= v4[1]; vec[3][1]= v4[2]; + } + + /* linetests, the 2 diagonals have to instersect to be convex */ + if( IsectLL2Df(vec[0], vec[2], vec[1], vec[3]) > 0 ) return 1; + return 0; +} + + +/* ********************* Fake Polgon support (FGon) ***************** */ + + +/* results in: + - faces having ->fgonf flag set (also for draw) + - edges having ->fgoni index set (for select) +*/ + +float EM_face_area(EditFace *efa) +{ + if(efa->v4) return AreaQ3Dfl(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co); + else return AreaT3Dfl(efa->v1->co, efa->v2->co, efa->v3->co); +} + +float EM_face_perimeter(EditFace *efa) +{ + if(efa->v4) return + VecLenf(efa->v1->co, efa->v2->co)+ + VecLenf(efa->v2->co, efa->v3->co)+ + VecLenf(efa->v3->co, efa->v4->co)+ + VecLenf(efa->v4->co, efa->v1->co); + + else return + VecLenf(efa->v1->co, efa->v2->co)+ + VecLenf(efa->v2->co, efa->v3->co)+ + VecLenf(efa->v3->co, efa->v1->co); +} + +void EM_fgon_flags(EditMesh *em) +{ + EditFace *efa, *efan, *efamax; + EditEdge *eed; + ListBase listb={NULL, NULL}; + float size, maxsize; + short done, curindex= 1; + + // for each face with fgon edge AND not fgon flag set + for(eed= em->edges.first; eed; eed= eed->next) eed->fgoni= 0; // index + for(efa= em->faces.first; efa; efa= efa->next) efa->fgonf= 0; // flag + + // for speed & simplicity, put fgon face candidates in new listbase + efa= em->faces.first; + while(efa) { + efan= efa->next; + if( (efa->e1->h & EM_FGON) || (efa->e2->h & EM_FGON) || + (efa->e3->h & EM_FGON) || (efa->e4 && (efa->e4->h & EM_FGON)) ) { + BLI_remlink(&em->faces, efa); + BLI_addtail(&listb, efa); + } + efa= efan; + } + + // find an undone face with fgon edge + for(efa= listb.first; efa; efa= efa->next) { + if(efa->fgonf==0) { + + // init this face + efa->fgonf= EM_FGON; + if(efa->e1->h & EM_FGON) efa->e1->fgoni= curindex; + if(efa->e2->h & EM_FGON) efa->e2->fgoni= curindex; + if(efa->e3->h & EM_FGON) efa->e3->fgoni= curindex; + if(efa->e4 && (efa->e4->h & EM_FGON)) efa->e4->fgoni= curindex; + + // we search for largest face, to give facedot drawing rights + maxsize= EM_face_area(efa); + efamax= efa; + + // now flush curendex over edges and set faceflags + done= 1; + while(done==1) { + done= 0; + + for(efan= listb.first; efan; efan= efan->next) { + if(efan->fgonf==0) { + // if one if its edges has index set, do other too + if( (efan->e1->fgoni==curindex) || (efan->e2->fgoni==curindex) || + (efan->e3->fgoni==curindex) || (efan->e4 && (efan->e4->fgoni==curindex)) ) { + + efan->fgonf= EM_FGON; + if(efan->e1->h & EM_FGON) efan->e1->fgoni= curindex; + if(efan->e2->h & EM_FGON) efan->e2->fgoni= curindex; + if(efan->e3->h & EM_FGON) efan->e3->fgoni= curindex; + if(efan->e4 && (efan->e4->h & EM_FGON)) efan->e4->fgoni= curindex; + + size= EM_face_area(efan); + if(size>maxsize) { + efamax= efan; + maxsize= size; + } + done= 1; + } + } + } + } + + efamax->fgonf |= EM_FGON_DRAW; + curindex++; + + } + } + + // put fgon face candidates back in listbase + efa= listb.first; + while(efa) { + efan= efa->next; + BLI_remlink(&listb, efa); + BLI_addtail(&em->faces, efa); + efa= efan; + } + + // remove fgon flags when edge not in fgon (anymore) + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->fgoni==0) eed->h &= ~EM_FGON; + } + +} + +/* editmesh vertmap, copied from intern.mesh.c + * if do_face_idx_array is 0 it means we need to run it as well as freeing + * */ + +UvVertMap *make_uv_vert_map_EM(EditMesh *em, int selected, int do_face_idx_array, float *limit) +{ + EditVert *ev; + EditFace *efa; + int totverts; + + /* vars from original func */ + UvVertMap *vmap; + UvMapVert *buf; + MTFace *tf; + unsigned int a; + int i, totuv, nverts; + + if (do_face_idx_array) + EM_init_index_arrays(em, 0, 0, 1); + + /* we need the vert */ + for (ev= em->verts.first, totverts=0; ev; ev= ev->next, totverts++) { + ev->tmp.l = totverts; + } + + totuv = 0; + + /* generate UvMapVert array */ + for (efa= em->faces.first; efa; efa= efa->next) + if(!selected || ((!efa->h) && (efa->f & SELECT))) + totuv += (efa->v4)? 4: 3; + + if(totuv==0) { + if (do_face_idx_array) + EM_free_index_arrays(); + return NULL; + } + vmap= (UvVertMap*)MEM_callocN(sizeof(*vmap), "UvVertMap"); + if (!vmap) { + if (do_face_idx_array) + EM_free_index_arrays(); + return NULL; + } + + vmap->vert= (UvMapVert**)MEM_callocN(sizeof(*vmap->vert)*totverts, "UvMapVert*"); + buf= vmap->buf= (UvMapVert*)MEM_callocN(sizeof(*vmap->buf)*totuv, "UvMapVert"); + + if (!vmap->vert || !vmap->buf) { + free_uv_vert_map(vmap); + if (do_face_idx_array) + EM_free_index_arrays(); + return NULL; + } + + for (a=0, efa= em->faces.first; efa; a++, efa= efa->next) { + if(!selected || ((!efa->h) && (efa->f & SELECT))) { + nverts= (efa->v4)? 4: 3; + + for(i=0; i<nverts; i++) { + buf->tfindex= i; + buf->f= a; + buf->separate = 0; + + buf->next= vmap->vert[(*(&efa->v1 + i))->tmp.l]; + vmap->vert[(*(&efa->v1 + i))->tmp.l]= buf; + + buf++; + } + } + } + + /* sort individual uvs for each vert */ + for(a=0, ev=em->verts.first; ev; a++, ev= ev->next) { + UvMapVert *newvlist= NULL, *vlist=vmap->vert[a]; + UvMapVert *iterv, *v, *lastv, *next; + float *uv, *uv2, uvdiff[2]; + + while(vlist) { + v= vlist; + vlist= vlist->next; + v->next= newvlist; + newvlist= v; + + efa = EM_get_face_for_index(v->f); + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv = tf->uv[v->tfindex]; + + lastv= NULL; + iterv= vlist; + + while(iterv) { + next= iterv->next; + efa = EM_get_face_for_index(iterv->f); + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv2 = tf->uv[iterv->tfindex]; + + Vec2Subf(uvdiff, uv2, uv); + + if(fabs(uv[0]-uv2[0]) < limit[0] && fabs(uv[1]-uv2[1]) < limit[1]) { + if(lastv) lastv->next= next; + else vlist= next; + iterv->next= newvlist; + newvlist= iterv; + } + else + lastv=iterv; + + iterv= next; + } + + newvlist->separate = 1; + } + + vmap->vert[a]= newvlist; + } + + if (do_face_idx_array) + EM_free_index_arrays(); + + return vmap; +} + +UvMapVert *get_uv_map_vert_EM(UvVertMap *vmap, unsigned int v) +{ + return vmap->vert[v]; +} + +void free_uv_vert_map_EM(UvVertMap *vmap) +{ + if (vmap) { + if (vmap->vert) MEM_freeN(vmap->vert); + if (vmap->buf) MEM_freeN(vmap->buf); + MEM_freeN(vmap); + } +} + diff --git a/source/blender/editors/mesh/editmesh_loop.c b/source/blender/editors/mesh/editmesh_loop.c new file mode 100644 index 00000000000..2cb7f48cc7c --- /dev/null +++ b/source/blender/editors/mesh/editmesh_loop.c @@ -0,0 +1,713 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_loop: tools with own drawing subloops, select, knife, subdiv + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_ghash.h" + +#include "BKE_depsgraph.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "PIL_time.h" + +#include "BIF_gl.h" + +#include "WM_types.h" + +#include "ED_mesh.h" +#include "ED_view3d.h" + +#include "editmesh.h" + +/* **** XXX ******** */ +static void BIF_undo_push() {} +static void BIF_undo() {} +static void error() {} +static int pupmenu() {return 0;} +static int qtest() {return 0;} +/* **** XXX ******** */ + + +/* New LoopCut */ +static void edgering_sel(EditMesh *em, EditEdge *startedge, int select, int previewlines) +{ + EditEdge *eed; + EditFace *efa; + EditVert *v[2][2]; + float co[2][3]; + int looking= 1,i; + + /* in eed->f1 we put the valence (amount of faces in edge) */ + /* in eed->f2 we put tagged flag as correct loop */ + /* in efa->f1 we put tagged flag as correct to select */ + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2= 0; + } + for(efa= em->faces.first; efa; efa= efa->next) { + efa->f1= 0; + if(efa->h==0) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + + // tag startedge OK + startedge->f2= 1; + + while(looking) { + looking= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e4 && efa->f1==0 && efa->h == 0) { // not done quad + if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { // valence ok + + // if edge tagged, select opposing edge and mark face ok + if(efa->e1->f2) { + efa->e3->f2= 1; + efa->f1= 1; + looking= 1; + } + else if(efa->e2->f2) { + efa->e4->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e3->f2) { + efa->e1->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e4->f2) { + efa->e2->f2= 1; + efa->f1= 1; + looking= 1; + } + } + } + } + } + + if(previewlines > 0 && select == 0){ +// XXX persp(PERSP_VIEW); +// XXX glPushMatrix(); +// XXX mymultmatrix(G.obedit->obmat); + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v4 == NULL) { continue; } + if(efa->h == 0){ + if(efa->e1->f2 == 1){ + if(efa->e1->h == 1 || efa->e3->h == 1 ) + continue; + + v[0][0] = efa->v1; + v[0][1] = efa->v2; + v[1][0] = efa->v4; + v[1][1] = efa->v3; + } else if(efa->e2->f2 == 1){ + if(efa->e2->h == 1 || efa->e4->h == 1) + continue; + v[0][0] = efa->v2; + v[0][1] = efa->v3; + v[1][0] = efa->v1; + v[1][1] = efa->v4; + } else { continue; } + + for(i=1;i<=previewlines;i++){ + co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0]; + co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1]; + co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2]; + + co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0]; + co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1]; + co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2]; + glColor3ub(255, 0, 255); + glBegin(GL_LINES); + glVertex3f(co[0][0],co[0][1],co[0][2]); + glVertex3f(co[1][0],co[1][1],co[1][2]); + glEnd(); + } + } + } + glPopMatrix(); + } else { + + /* (de)select the edges */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f2) EM_select_edge(eed, select); + } + } +} +void CutEdgeloop(EditMesh *em, int numcuts) +{ + View3D *v3d= NULL; // XXX + EditEdge *nearest=NULL, *eed; + float fac; + int keys = 0, holdnum=0, selectmode, dist; + short mvalo[2] = {0,0}, mval[2]; + short event, val, choosing=1, cancel=0, cuthalf = 0, smooth=0; + short hasHidden = 0; + char msg[128]; + + selectmode = em->selectmode; + + if(em->selectmode & SCE_SELECT_FACE){ + em->selectmode = SCE_SELECT_EDGE; + EM_selectmode_set(em); + } + + + BIF_undo_push("Loopcut Begin"); + while(choosing && !cancel){ +// XXX getmouseco_areawin(mval); + if (mval[0] != mvalo[0] || mval[1] != mvalo[1]) { + mvalo[0] = mval[0]; + mvalo[1] = mval[1]; + dist= 50; + nearest = findnearestedge(v3d, em, &dist); // returns actual distance in dist +// scrarea_do_windraw(curarea); // after findnearestedge, backbuf! + + sprintf(msg,"Number of Cuts: %d",numcuts); + if(smooth){ + sprintf(msg,"%s (S)mooth: on",msg); + } else { + sprintf(msg,"%s (S)mooth: off",msg); + } + +// headerprint(msg); + /* Need to figure preview */ + if(nearest){ + edgering_sel(em, nearest, 0, numcuts); + } +// XXX screen_swapbuffers(); + + /* backbuffer refresh for non-apples (no aux) */ +#ifndef __APPLE__ +// XXX if(G.vd->drawtype>OB_WIRE && (G.vd->flag & V3D_ZBUF_SELECT)) { +// backdrawview3d(0); +// } +#endif + } + else PIL_sleep_ms(10); // idle + + + while(qtest()) + { + val=0; +// XXX event= extern_qread(&val); + if(val && (event == MOUSEX || event == MOUSEY)){ ; } + else if(val && ((event==LEFTMOUSE || event==RETKEY) || (event == MIDDLEMOUSE || event==PADENTER))) + { + if(event == MIDDLEMOUSE){ + cuthalf = 1; + } + if (nearest==NULL) + cancel = 1; + choosing=0; + mvalo[0] = -1; + } + else if(val && (event==ESCKEY || event==RIGHTMOUSE )) + { + choosing=0; + cancel = 1; + mvalo[0] = -1; + } + else if(val && (event==PADPLUSKEY || event==WHEELUPMOUSE)) + { + numcuts++; + mvalo[0] = -1; + } + else if(val && (event==PADMINUS || event==WHEELDOWNMOUSE)) + { + if(numcuts > 1){ + numcuts--; + mvalo[0] = -1; + } + } + else if(val && event==SKEY) + { + if(smooth){smooth=0;} + else { smooth=1; } + mvalo[0] = -1; + } + + else if(val){ + holdnum = -1; + switch(event){ + case PAD9: + case NINEKEY: + holdnum = 9; break; + case PAD8: + case EIGHTKEY: + holdnum = 8;break; + case PAD7: + case SEVENKEY: + holdnum = 7;break; + case PAD6: + case SIXKEY: + holdnum = 6;break; + case PAD5: + case FIVEKEY: + holdnum = 5;break; + case PAD4: + case FOURKEY: + holdnum = 4;break; + case PAD3: + case THREEKEY: + holdnum = 3; break; + case PAD2: + case TWOKEY: + holdnum = 2;break; + case PAD1: + case ONEKEY: + holdnum = 1; break; + case PAD0: + case ZEROKEY: + holdnum = 0;break; + case BACKSPACEKEY: + holdnum = -2;break; + } + if(holdnum >= 0 && numcuts*10 < 130){ + if(keys == 0){ // first level numeric entry + if(holdnum > 0){ + numcuts = holdnum; + keys++; + } + } else if(keys > 0){//highrt level numeric entry + numcuts *= 10; + numcuts += holdnum; + keys++; + } + } else if (holdnum == -2){// backspace + if (keys > 1){ + numcuts /= 10; + keys--; + } else { + numcuts=1; + keys = 0; + } + } + mvalo[0] = -1; + break; + } // End Numeric Entry + } //End while(qtest()) + } // End Choosing + + if(cancel){ + return; + } + /* clean selection */ + for(eed=em->edges.first; eed; eed = eed->next){ + EM_select_edge(eed,0); + } + /* select edge ring */ + edgering_sel(em, nearest, 1, 0); + + /* now cut the loops */ + if(smooth){ + fac= 1.0f; +// XXX if(fbutton(&fac, 0.0f, 5.0f, 10, 10, "Smooth:")==0) return; + fac= 0.292f*fac; + esubdivideflag(em, SELECT,fac,B_SMOOTH,numcuts,SUBDIV_SELECT_LOOPCUT); + } else { + esubdivideflag(em, SELECT,0,0,numcuts,SUBDIV_SELECT_LOOPCUT); + } + /* if this was a single cut, enter edgeslide mode */ + if(numcuts == 1 && hasHidden == 0){ + if(cuthalf) + EdgeSlide(em, 1,0.0); + else { + if(EdgeSlide(em, 0,0.0) == -1){ + BIF_undo(); + } + } + } + + if(em->selectmode != selectmode){ + em->selectmode = selectmode; + EM_selectmode_set(em); + } + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + return; +} + + +/* *************** LOOP SELECT ************* */ +#if 0 +static short edgeFaces(EditMesh *em, EditEdge *e) +{ + EditFace *search=NULL; + short count = 0; + + search = em->faces.first; + while(search){ + if((search->e1 == e || search->e2 == e) || (search->e3 == e || search->e4 == e)) + count++; + search = search->next; + } + return count; +} +#endif + + + +/* ***************** TRAIL ************************ + +Read a trail of mouse coords and return them as an array of CutCurve structs +len returns number of mouse coords read before commiting with RETKEY +It is up to the caller to free the block when done with it, + +XXX Is only used here, so local inside this file (ton) + */ + +#define TRAIL_POLYLINE 1 /* For future use, They don't do anything yet */ +#define TRAIL_FREEHAND 2 +#define TRAIL_MIXED 3 /* (1|2) */ +#define TRAIL_AUTO 4 +#define TRAIL_MIDPOINTS 8 + +typedef struct CutCurve { + float x; + float y; +} CutCurve; + +static CutCurve *get_mouse_trail(int *len, char mode, char cutmode, struct GHash *gh) +{ + return NULL; // XXX +} + + +/* ******************************************************************** */ +/* Knife Subdivide Tool. Subdivides edges intersected by a mouse trail + drawn by user. + + Currently mapped to KKey when in MeshEdit mode. + Usage: + Hit Shift K, Select Centers or Exact + Hold LMB down to draw path, hit RETKEY. + ESC cancels as expected. + + Contributed by Robert Wenzlaff (Det. Thorn). +*/ + +/* prototype */ +static float seg_intersect(struct EditEdge * e, CutCurve *c, int len, char mode, struct GHash *gh); + +void KnifeSubdivide(EditMesh *em, char mode) +{ + EditEdge *eed; + EditVert *eve; + CutCurve *curve; + + struct GHash *gh; + int len=0; + float isect=0.0; + short numcuts=1; + float *scr, co[4]; + + if (G.obedit==0) return; + + if (EM_nvertices_selected(em) < 2) { + error("No edges are selected to operate on"); + return; + } + + if (mode==KNIFE_PROMPT) { + short val= pupmenu("Cut Type %t|Exact Line%x1|Midpoints%x2|Multicut%x3"); + if(val<1) return; + mode = val; // warning, mode is char, pupmenu returns -1 with ESC + } + + if(mode == KNIFE_MULTICUT) { +// XXX if(button(&numcuts, 2, 128, "Number of Cuts:")==0) return; + } + + /* XXX Set a knife cursor here */ + + for(eed=em->edges.first; eed; eed= eed->next) eed->tmp.fp = 0.0; /*store percentage of edge cut for KNIFE_EXACT here.*/ + + /*the floating point coordinates of verts in screen space will be stored in a hash table according to the vertices pointer*/ + gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + for(eve=em->verts.first; eve; eve=eve->next){ + scr = MEM_mallocN(sizeof(float)*2, "Vertex Screen Coordinates"); + VECCOPY(co, eve->co); + co[3]= 1.0; + Mat4MulVec4fl(G.obedit->obmat, co); +// XXX project_float(co,scr); + BLI_ghash_insert(gh, eve, scr); + eve->f1 = 0; /*store vertex intersection flag here*/ + + } + + curve=get_mouse_trail(&len, TRAIL_MIXED, mode, gh); + + if (curve && len && mode){ + eed= em->edges.first; + while(eed) { + if( eed->v1->f & eed->v2->f & SELECT ){ // NOTE: uses vertex select, subdiv doesnt do edges yet + isect=seg_intersect(eed, curve, len, mode, gh); + if (isect) eed->f2= 1; + else eed->f2=0; + eed->tmp.fp= isect; + //printf("isect=%i\n", isect); + } + else { + eed->f2=0; + eed->f1=0; + } + eed= eed->next; + } + + if(mode==KNIFE_EXACT) esubdivideflag(em, SELECT, 0, B_KNIFE|B_PERCENTSUBD,1,SUBDIV_SELECT_ORIG); + else if (mode==KNIFE_MIDPOINT) esubdivideflag(em, SELECT, 0, B_KNIFE,1,SUBDIV_SELECT_ORIG); + else if (mode==KNIFE_MULTICUT) esubdivideflag(em, SELECT, 0, B_KNIFE,numcuts,SUBDIV_SELECT_ORIG); + + eed=em->edges.first; + while(eed){ + eed->f2=0; + eed->f1=0; + eed=eed->next; + } + } + /* Return to old cursor and flags...*/ + + BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN); + if (curve) MEM_freeN(curve); + + BIF_undo_push("Knife"); +} + +/* seg_intersect() Determines if and where a mouse trail intersects an EditEdge */ + +static float seg_intersect(EditEdge *e, CutCurve *c, int len, char mode, struct GHash *gh) +{ +#define MAXSLOPE 100000 + float x11, y11, x12=0, y12=0, x2max, x2min, y2max; + float y2min, dist, lastdist=0, xdiff2, xdiff1; + float m1, b1, m2, b2, x21, x22, y21, y22, xi; + float yi, x1min, x1max, y1max, y1min, perc=0; + float *scr; + float threshold; + int i; + + //threshold = 0.000001; /*tolerance for vertex intersection*/ +// XXX threshold = G.scene->toolsettings->select_thresh / 100; + + /* Get screen coords of verts */ + scr = BLI_ghash_lookup(gh, e->v1); + x21=scr[0]; + y21=scr[1]; + + scr = BLI_ghash_lookup(gh, e->v2); + x22=scr[0]; + y22=scr[1]; + + xdiff2=(x22-x21); + if (xdiff2) { + m2=(y22-y21)/xdiff2; + b2= ((x22*y21)-(x21*y22))/xdiff2; + } + else { + m2=MAXSLOPE; /* Verticle slope */ + b2=x22; + } + + /*check for *exact* vertex intersection first*/ + if(mode!=KNIFE_MULTICUT){ + for (i=0; i<len; i++){ + if (i>0){ + x11=x12; + y11=y12; + } + else { + x11=c[i].x; + y11=c[i].y; + } + x12=c[i].x; + y12=c[i].y; + + /*test e->v1*/ + if((x11 == x21 && y11 == y21) || (x12 == x21 && y12 == y21)){ + e->v1->f1 = 1; + perc = 0; + return(perc); + } + /*test e->v2*/ + else if((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)){ + e->v2->f1 = 1; + perc = 0; + return(perc); + } + } + } + + /*now check for edge interesect (may produce vertex intersection as well)*/ + for (i=0; i<len; i++){ + if (i>0){ + x11=x12; + y11=y12; + } + else { + x11=c[i].x; + y11=c[i].y; + } + x12=c[i].x; + y12=c[i].y; + + /* Perp. Distance from point to line */ + if (m2!=MAXSLOPE) dist=(y12-m2*x12-b2);/* /sqrt(m2*m2+1); Only looking for */ + /* change in sign. Skip extra math */ + else dist=x22-x12; + + if (i==0) lastdist=dist; + + /* if dist changes sign, and intersect point in edge's Bound Box*/ + if ((lastdist*dist)<=0){ + xdiff1=(x12-x11); /* Equation of line between last 2 points */ + if (xdiff1){ + m1=(y12-y11)/xdiff1; + b1= ((x12*y11)-(x11*y12))/xdiff1; + } + else{ + m1=MAXSLOPE; + b1=x12; + } + x2max=MAX2(x21,x22)+0.001; /* prevent missed edges */ + x2min=MIN2(x21,x22)-0.001; /* due to round off error */ + y2max=MAX2(y21,y22)+0.001; + y2min=MIN2(y21,y22)-0.001; + + /* Found an intersect, calc intersect point */ + if (m1==m2){ /* co-incident lines */ + /* cut at 50% of overlap area*/ + x1max=MAX2(x11, x12); + x1min=MIN2(x11, x12); + xi= (MIN2(x2max,x1max)+MAX2(x2min,x1min))/2.0; + + y1max=MAX2(y11, y12); + y1min=MIN2(y11, y12); + yi= (MIN2(y2max,y1max)+MAX2(y2min,y1min))/2.0; + } + else if (m2==MAXSLOPE){ + xi=x22; + yi=m1*x22+b1; + } + else if (m1==MAXSLOPE){ + xi=x12; + yi=m2*x12+b2; + } + else { + xi=(b1-b2)/(m2-m1); + yi=(b1*m2-m1*b2)/(m2-m1); + } + + /* Intersect inside bounding box of edge?*/ + if ((xi>=x2min)&&(xi<=x2max)&&(yi<=y2max)&&(yi>=y2min)){ + /*test for vertex intersect that may be 'close enough'*/ + if(mode!=KNIFE_MULTICUT){ + if(xi <= (x21 + threshold) && xi >= (x21 - threshold)){ + if(yi <= (y21 + threshold) && yi >= (y21 - threshold)){ + e->v1->f1 = 1; + perc = 0; + break; + } + } + if(xi <= (x22 + threshold) && xi >= (x22 - threshold)){ + if(yi <= (y22 + threshold) && yi >= (y22 - threshold)){ + e->v2->f1 = 1; + perc = 0; + break; + } + } + } + if ((m2<=1.0)&&(m2>=-1.0)) perc = (xi-x21)/(x22-x21); + else perc=(yi-y21)/(y22-y21); /*lower slope more accurate*/ + //isect=32768.0*(perc+0.0000153); /* Percentage in 1/32768ths */ + break; + } + } + lastdist=dist; + } + return(perc); +} + +void LoopMenu(EditMesh *em) /* Called by KKey */ +{ + short ret; + + ret=pupmenu("Loop/Cut Menu %t|Loop Cut (CTRL-R)%x2|" + "Knife (Exact) %x3|Knife (Midpoints)%x4|Knife (Multicut)%x5"); + + switch (ret){ + case 2: + CutEdgeloop(em, 1); + break; + case 3: + KnifeSubdivide(em, KNIFE_EXACT); + break; + case 4: + KnifeSubdivide(em, KNIFE_MIDPOINT); + break; + case 5: + KnifeSubdivide(em, KNIFE_MULTICUT); + break; + } + +} + diff --git a/source/blender/editors/mesh/editmesh_mods.c b/source/blender/editors/mesh/editmesh_mods.c new file mode 100644 index 00000000000..41de2ea1bfe --- /dev/null +++ b/source/blender/editors/mesh/editmesh_mods.c @@ -0,0 +1,4326 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_mods.c, UI level access, no geometry changes + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "MTC_matrixops.h" + +#include "DNA_mesh_types.h" +#include "DNA_material_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_texture_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "BKE_displist.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "RE_render_ext.h" /* externtex */ + +#include "ED_multires.h" +#include "ED_mesh.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "editmesh.h" + +#include "BLO_sys_types.h" // for intptr_t support + +/* XXX ONLY TO GET COMPILED! XXX + the backbuffer select code will move to view3d later */ +#include "../space_view3d/view3d_intern.h" + +/* XXX ********************** XXX */ + +static void *read_backbuf() {return NULL;} +static int sample_backbuf_rect() {return 0;} +static int sample_backbuf() {return 0;} +static void BIF_undo_push() {} +static void waitcursor() {} +static void error() {} +static int pupmenu() {return 0;} + +/* ****************************** MIRROR **************** */ + +void EM_select_mirrored(EditMesh *em) +{ + if(em->selectmode & SCE_SELECT_VERTEX) { + EditVert *eve, *v1; + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + v1= editmesh_get_x_mirror_vert(G.obedit, em, eve->co); + if(v1) { + eve->f &= ~SELECT; + v1->f |= SELECT; + } + } + } + } +} + +void EM_automerge(int update) +{ +// XXX int len; + +// if ((G.scene->automerge) && +// (G.obedit && G.obedit->type==OB_MESH) && +// (((Mesh*)G.obedit->data)->mr==NULL) +// ) { +// len = removedoublesflag(1, 1, G.scene->toolsettings->doublimit); +// if (len) { +// G.totvert -= len; /* saves doing a countall */ +// if (update) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +// } +// } +// } +} + +/* ****************************** SELECTION ROUTINES **************** */ + +unsigned int em_solidoffs=0, em_wireoffs=0, em_vertoffs=0; /* set in drawobject.c ... for colorindices */ + +/* facilities for border select and circle select */ +static char *selbuf= NULL; + +/* opengl doesn't support concave... */ +static void draw_triangulated(short mcords[][2], short tot) +{ + ListBase lb={NULL, NULL}; + DispList *dl; + float *fp; + int a; + + /* make displist */ + dl= MEM_callocN(sizeof(DispList), "poly disp"); + dl->type= DL_POLY; + dl->parts= 1; + dl->nr= tot; + dl->verts= fp= MEM_callocN(tot*3*sizeof(float), "poly verts"); + BLI_addtail(&lb, dl); + + for(a=0; a<tot; a++, fp+=3) { + fp[0]= (float)mcords[a][0]; + fp[1]= (float)mcords[a][1]; + } + + /* do the fill */ + filldisplist(&lb, &lb); + + /* do the draw */ + dl= lb.first; /* filldisplist adds in head of list */ + if(dl->type==DL_INDEX3) { + int *index; + + a= dl->parts; + fp= dl->verts; + index= dl->index; + glBegin(GL_TRIANGLES); + while(a--) { + glVertex3fv(fp+3*index[0]); + glVertex3fv(fp+3*index[1]); + glVertex3fv(fp+3*index[2]); + index+= 3; + } + glEnd(); + } + + freedisplist(&lb); +} + + +/* reads rect, and builds selection array for quick lookup */ +/* returns if all is OK */ +int EM_init_backbuf_border(View3D *v3d, short xmin, short ymin, short xmax, short ymax) +{ + struct ImBuf *buf; + unsigned int *dr; + int a; + + if(G.obedit==NULL || v3d->drawtype<OB_SOLID || (v3d->flag & V3D_ZBUF_SELECT)==0) return 0; + if(em_vertoffs==0) return 0; + + buf= read_backbuf(xmin, ymin, xmax, ymax); + if(buf==NULL) return 0; + + dr = buf->rect; + + /* build selection lookup */ + selbuf= MEM_callocN(em_vertoffs+1, "selbuf"); + + a= (xmax-xmin+1)*(ymax-ymin+1); + while(a--) { + if(*dr>0 && *dr<=em_vertoffs) + selbuf[*dr]= 1; + dr++; + } + IMB_freeImBuf(buf); + return 1; +} + +int EM_check_backbuf(unsigned int index) +{ + if(selbuf==NULL) return 1; + if(index>0 && index<=em_vertoffs) + return selbuf[index]; + return 0; +} + +void EM_free_backbuf(void) +{ + if(selbuf) MEM_freeN(selbuf); + selbuf= NULL; +} + +/* mcords is a polygon mask + - grab backbuffer, + - draw with black in backbuffer, + - grab again and compare + returns 'OK' +*/ +int EM_mask_init_backbuf_border(View3D *v3d, short mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax) +{ + unsigned int *dr, *drm; + struct ImBuf *buf, *bufmask; + int a; + + /* method in use for face selecting too */ + if(G.obedit==NULL) { + if(FACESEL_PAINT_TEST); + else return 0; + } + else if(v3d->drawtype<OB_SOLID || (v3d->flag & V3D_ZBUF_SELECT)==0) return 0; + + if(em_vertoffs==0) return 0; + + buf= read_backbuf(xmin, ymin, xmax, ymax); + if(buf==NULL) return 0; + + dr = buf->rect; + + /* draw the mask */ +#ifdef __APPLE__ + glDrawBuffer(GL_AUX0); +#endif + glDisable(GL_DEPTH_TEST); + +// XXX persp(PERSP_WIN); + glColor3ub(0, 0, 0); + + /* yah, opengl doesn't do concave... tsk! */ + draw_triangulated(mcords, tot); + + glBegin(GL_LINE_LOOP); /* for zero sized masks, lines */ + for(a=0; a<tot; a++) glVertex2s(mcords[a][0], mcords[a][1]); + glEnd(); + +// XXX persp(PERSP_VIEW); + glFinish(); /* to be sure readpixels sees mask */ + + glDrawBuffer(GL_BACK); + + /* grab mask */ + bufmask= read_backbuf(xmin, ymin, xmax, ymax); + drm = bufmask->rect; + if(bufmask==NULL) return 0; /* only when mem alloc fails, go crash somewhere else! */ + + /* build selection lookup */ + selbuf= MEM_callocN(em_vertoffs+1, "selbuf"); + + a= (xmax-xmin+1)*(ymax-ymin+1); + while(a--) { + if(*dr>0 && *dr<=em_vertoffs && *drm==0) selbuf[*dr]= 1; + dr++; drm++; + } + IMB_freeImBuf(buf); + IMB_freeImBuf(bufmask); + return 1; + +} + +/* circle shaped sample area */ +int EM_init_backbuf_circle(View3D *v3d, short xs, short ys, short rads) +{ + struct ImBuf *buf; + unsigned int *dr; + short xmin, ymin, xmax, ymax, xc, yc; + int radsq; + + /* method in use for face selecting too */ + if(G.obedit==NULL) { + if(FACESEL_PAINT_TEST); + else return 0; + } + else if(v3d->drawtype<OB_SOLID || (v3d->flag & V3D_ZBUF_SELECT)==0) return 0; + if(em_vertoffs==0) return 0; + + xmin= xs-rads; xmax= xs+rads; + ymin= ys-rads; ymax= ys+rads; + buf= read_backbuf(xmin, ymin, xmax, ymax); + if(buf==NULL) return 0; + + dr = buf->rect; + + /* build selection lookup */ + selbuf= MEM_callocN(em_vertoffs+1, "selbuf"); + radsq= rads*rads; + for(yc= -rads; yc<=rads; yc++) { + for(xc= -rads; xc<=rads; xc++, dr++) { + if(xc*xc + yc*yc < radsq) { + if(*dr>0 && *dr<=em_vertoffs) selbuf[*dr]= 1; + } + } + } + + IMB_freeImBuf(buf); + return 1; + +} + +static void findnearestvert__doClosest(void *userData, EditVert *eve, int x, int y, int index) +{ + struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; EditVert *closest; } *data = userData; + + if (data->pass==0) { + if (index<=data->lastIndex) + return; + } else { + if (index>data->lastIndex) + return; + } + + if (data->dist>3) { + int temp = abs(data->mval[0] - x) + abs(data->mval[1]- y); + if ((eve->f&1) == data->select) { + if (data->strict == 1) + return; + else + temp += 5; + } + + if (temp<data->dist) { + data->dist = temp; + data->closest = eve; + data->closestIndex = index; + } + } +} + + + + +static unsigned int findnearestvert__backbufIndextest(unsigned int index) +{ + EditMesh *em= NULL; // XXX + EditVert *eve = BLI_findlink(&em->verts, index-1); + + if(eve && (eve->f & SELECT)) return 0; + return 1; +} +/** + * findnearestvert + * + * dist (in/out): minimal distance to the nearest and at the end, actual distance + * sel: selection bias + * if SELECT, selected vertice are given a 5 pixel bias to make them farter than unselect verts + * if 0, unselected vertice are given the bias + * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased + */ +EditVert *findnearestvert(View3D *v3d, EditMesh *em, int *dist, short sel, short strict) +{ + ARegion *ar= NULL; // XXX + short mval[2]; + +// XXX getmouseco_areawin(mval); + if(v3d->drawtype>OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)){ + int distance; + unsigned int index; + EditVert *eve; + + if(strict) index = sample_backbuf_rect(mval, 50, em_wireoffs, 0xFFFFFF, &distance, strict, findnearestvert__backbufIndextest); + else index = sample_backbuf_rect(mval, 50, em_wireoffs, 0xFFFFFF, &distance, 0, NULL); + + eve = BLI_findlink(&em->verts, index-1); + + if(eve && distance < *dist) { + *dist = distance; + return eve; + } else { + return NULL; + } + + } + else { + struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; EditVert *closest; } data; + static int lastSelectedIndex=0; + static EditVert *lastSelected=NULL; + + if (lastSelected && BLI_findlink(&em->verts, lastSelectedIndex)!=lastSelected) { + lastSelectedIndex = 0; + lastSelected = NULL; + } + + data.lastIndex = lastSelectedIndex; + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.select = sel; + data.dist = *dist; + data.strict = strict; + data.closest = NULL; + data.closestIndex = 0; + + data.pass = 0; + mesh_foreachScreenVert(ar, v3d, findnearestvert__doClosest, &data, 1); + + if (data.dist>3) { + data.pass = 1; + mesh_foreachScreenVert(ar, v3d, findnearestvert__doClosest, &data, 1); + } + + *dist = data.dist; + lastSelected = data.closest; + lastSelectedIndex = data.closestIndex; + + return data.closest; + } +} + +/* returns labda for closest distance v1 to line-piece v2-v3 */ +static float labda_PdistVL2Dfl( float *v1, float *v2, float *v3) +{ + float rc[2], len; + + rc[0]= v3[0]-v2[0]; + rc[1]= v3[1]-v2[1]; + len= rc[0]*rc[0]+ rc[1]*rc[1]; + if(len==0.0f) + return 0.0f; + + return ( rc[0]*(v1[0]-v2[0]) + rc[1]*(v1[1]-v2[1]) )/len; +} + +/* note; uses v3d, so needs active 3d window */ +static void findnearestedge__doClosest(void *userData, EditEdge *eed, int x0, int y0, int x1, int y1, int index) +{ + View3D *v3d; /* XXX */ + struct { float mval[2]; int dist; EditEdge *closest; } *data = userData; + float v1[2], v2[2]; + int distance; + + v1[0] = x0; + v1[1] = y0; + v2[0] = x1; + v2[1] = y1; + + distance= PdistVL2Dfl(data->mval, v1, v2); + + if(eed->f & SELECT) distance+=5; + if(distance < data->dist) { + if(v3d->flag & V3D_CLIPPING) { + float labda= labda_PdistVL2Dfl(data->mval, v1, v2); + float vec[3]; + + vec[0]= eed->v1->co[0] + labda*(eed->v2->co[0] - eed->v1->co[0]); + vec[1]= eed->v1->co[1] + labda*(eed->v2->co[1] - eed->v1->co[1]); + vec[2]= eed->v1->co[2] + labda*(eed->v2->co[2] - eed->v1->co[2]); + Mat4MulVecfl(G.obedit->obmat, vec); + + if(view3d_test_clipping(v3d, vec)==0) { + data->dist = distance; + data->closest = eed; + } + } + else { + data->dist = distance; + data->closest = eed; + } + } +} +EditEdge *findnearestedge(View3D *v3d, EditMesh *em, int *dist) +{ + ARegion *ar= NULL; // XXX + short mval[2]; + +// XXX getmouseco_areawin(mval); + + if(v3d->drawtype>OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)) { + int distance; + unsigned int index = sample_backbuf_rect(mval, 50, em_solidoffs, em_wireoffs, &distance,0, NULL); + EditEdge *eed = BLI_findlink(&em->edges, index-1); + + if (eed && distance<*dist) { + *dist = distance; + return eed; + } else { + return NULL; + } + } + else { + struct { float mval[2]; int dist; EditEdge *closest; } data; + + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.dist = *dist; + data.closest = NULL; + + mesh_foreachScreenEdge(ar, v3d, findnearestedge__doClosest, &data, 2); + + *dist = data.dist; + return data.closest; + } +} + +static void findnearestface__getDistance(void *userData, EditFace *efa, int x, int y, int index) +{ + struct { short mval[2]; int dist; EditFace *toFace; } *data = userData; + + if (efa==data->toFace) { + int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y); + + if (temp<data->dist) + data->dist = temp; + } +} +static void findnearestface__doClosest(void *userData, EditFace *efa, int x, int y, int index) +{ + struct { short mval[2], pass; int dist, lastIndex, closestIndex; EditFace *closest; } *data = userData; + + if (data->pass==0) { + if (index<=data->lastIndex) + return; + } else { + if (index>data->lastIndex) + return; + } + + if (data->dist>3) { + int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y); + + if (temp<data->dist) { + data->dist = temp; + data->closest = efa; + data->closestIndex = index; + } + } +} +static EditFace *findnearestface(View3D *v3d, EditMesh *em, int *dist) +{ + ARegion *ar= NULL; // XXX + short mval[2]; + +// XXX getmouseco_areawin(mval); + + if(v3d->drawtype>OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)) { + unsigned int index = sample_backbuf(mval[0], mval[1]); + EditFace *efa = BLI_findlink(&em->faces, index-1); + + if (efa) { + struct { short mval[2]; int dist; EditFace *toFace; } data; + + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.dist = 0x7FFF; /* largest short */ + data.toFace = efa; + + mesh_foreachScreenFace(ar, v3d, findnearestface__getDistance, &data); + + if(em->selectmode == SCE_SELECT_FACE || data.dist<*dist) { /* only faces, no dist check */ + *dist= data.dist; + return efa; + } + } + + return NULL; + } + else { + struct { short mval[2], pass; int dist, lastIndex, closestIndex; EditFace *closest; } data; + static int lastSelectedIndex=0; + static EditFace *lastSelected=NULL; + + if (lastSelected && BLI_findlink(&em->faces, lastSelectedIndex)!=lastSelected) { + lastSelectedIndex = 0; + lastSelected = NULL; + } + + data.lastIndex = lastSelectedIndex; + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.dist = *dist; + data.closest = NULL; + data.closestIndex = 0; + + data.pass = 0; + mesh_foreachScreenFace(ar, v3d, findnearestface__doClosest, &data); + + if (data.dist>3) { + data.pass = 1; + mesh_foreachScreenFace(ar, v3d, findnearestface__doClosest, &data); + } + + *dist = data.dist; + lastSelected = data.closest; + lastSelectedIndex = data.closestIndex; + + return data.closest; + } +} + +#if 0 +/* for interactivity, frontbuffer draw in current window */ +static void draw_dm_mapped_vert__mapFunc(void *theVert, int index, float *co, float *no_f, short *no_s) +{ + if (EM_get_vert_for_index(index)==theVert) { + bglVertex3fv(co); + } +} +static void draw_dm_mapped_vert(EditMesh *em, DerivedMesh *dm, EditVert *eve) +{ + EM_init_index_arrays(em, 1, 0, 0); + bglBegin(GL_POINTS); + dm->foreachMappedVert(dm, draw_dm_mapped_vert__mapFunc, eve); + bglEnd(); + EM_free_index_arrays(); +} + +static int draw_dm_mapped_edge__setDrawOptions(void *theEdge, int index) +{ + return EM_get_edge_for_index(index)==theEdge; +} +static void draw_dm_mapped_edge(EditMesh *em, DerivedMesh *dm, EditEdge *eed) +{ + EM_init_index_arrays(em, 0, 1, 0); + dm->drawMappedEdges(dm, draw_dm_mapped_edge__setDrawOptions, eed); + EM_free_index_arrays(); +} + +static void draw_dm_mapped_face_center__mapFunc(void *theFace, int index, float *cent, float *no) +{ + if (EM_get_face_for_index(index)==theFace) { + bglVertex3fv(cent); + } +} +static void draw_dm_mapped_face_center(EditMesh *em, DerivedMesh *dm, EditFace *efa) +{ + EM_init_index_arrays(em, 0, 0, 1); + bglBegin(GL_POINTS); + dm->foreachMappedFaceCenter(dm, draw_dm_mapped_face_center__mapFunc, efa); + bglEnd(); + EM_free_index_arrays(); +} + +#endif + +static void unified_select_draw(EditMesh *em, EditVert *eve, EditEdge *eed, EditFace *efa) +{ +#if 0 + /* XXX depricated, no frontbuffer, later we can partial copy? */ + + DerivedMesh *dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); + + glDrawBuffer(GL_FRONT); + +// XXX persp(PERSP_VIEW); + + if(v3d->flag & V3D_CLIPPING) + view3d_set_clipping(v3d); + + glPushMatrix(); + mymultmatrix(G.obedit->obmat); + + /* face selected */ + if(efa) { + if(em->selectmode & SCE_SELECT_VERTEX) { + glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE)); + + if(efa->f & SELECT) BIF_ThemeColor(TH_VERTEX_SELECT); + else BIF_ThemeColor(TH_VERTEX); + + bglBegin(GL_POINTS); + bglVertex3fv(efa->v1->co); + bglVertex3fv(efa->v2->co); + bglVertex3fv(efa->v3->co); + if(efa->v4) bglVertex3fv(efa->v4->co); + bglEnd(); + } + + if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_FACE)) { + if(efa->fgonf==0) { + BIF_ThemeColor((efa->f & SELECT)?TH_EDGE_SELECT:TH_WIRE); + + draw_dm_mapped_edge(em, dm, efa->e1); + draw_dm_mapped_edge(em, dm, efa->e2); + draw_dm_mapped_edge(em, dm, efa->e3); + if (efa->e4) { + draw_dm_mapped_edge(em, dm, efa->e4); + } + } + } + + if( CHECK_OB_DRAWFACEDOT(G.scene, v3d, G.obedit->dt) ) { + if(efa->fgonf==0) { + glPointSize(BIF_GetThemeValuef(TH_FACEDOT_SIZE)); + BIF_ThemeColor((efa->f & SELECT)?TH_FACE_DOT:TH_WIRE); + + draw_dm_mapped_face_center(em, dm, efa); + } + } + } + /* edge selected */ + if(eed) { + if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_FACE)) { + BIF_ThemeColor((eed->f & SELECT)?TH_EDGE_SELECT:TH_WIRE); + + draw_dm_mapped_edge(em, dm, eed); + } + if(em->selectmode & SCE_SELECT_VERTEX) { + glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE)); + + BIF_ThemeColor((eed->f & SELECT)?TH_VERTEX_SELECT:TH_VERTEX); + + draw_dm_mapped_vert(em, dm, eed->v1); + draw_dm_mapped_vert(em, dm, eed->v2); + } + } + if(eve) { + if(em->selectmode & SCE_SELECT_VERTEX) { + glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE)); + + BIF_ThemeColor((eve->f & SELECT)?TH_VERTEX_SELECT:TH_VERTEX); + + draw_dm_mapped_vert(em, dm, eve); + } + } + + glPointSize(1.0); + glPopMatrix(); + + bglFlush(); + glDrawBuffer(GL_BACK); + + if(v3d->flag & V3D_CLIPPING) + view3d_clr_clipping(); + + + dm->release(dm); +#endif +} + + +/* best distance based on screen coords. + use em->selectmode to define how to use + selected vertices and edges get disadvantage + return 1 if found one +*/ +static int unified_findnearest(View3D *v3d, EditMesh *em, EditVert **eve, EditEdge **eed, EditFace **efa) +{ + int dist= 75; + + *eve= NULL; + *eed= NULL; + *efa= NULL; + + if(em->selectmode & SCE_SELECT_VERTEX) + *eve= findnearestvert(v3d, em, &dist, SELECT, 0); + if(em->selectmode & SCE_SELECT_FACE) + *efa= findnearestface(v3d, em, &dist); + + dist-= 20; /* since edges select lines, we give dots advantage of 20 pix */ + if(em->selectmode & SCE_SELECT_EDGE) + *eed= findnearestedge(v3d, em, &dist); + + /* return only one of 3 pointers, for frontbuffer redraws */ + if(*eed) { + *efa= NULL; *eve= NULL; + } + else if(*efa) { + *eve= NULL; + } + + return (*eve || *eed || *efa); +} + +/* this as a way to compare the ares, perim of 2 faces thay will scale to different sizes + *0.5 so smaller faces arnt ALWAYS selected with a thresh of 1.0 */ +#define SCALE_CMP(a,b) ((a+a*thresh >= b) && (a-(a*thresh*0.5) <= b)) + +/* **************** GROUP SELECTS ************** */ +/* selects new faces/edges/verts based on the + existing selection + +FACES GROUP + mode 1: same material + mode 2: same image + mode 3: same area + mode 4: same perimeter + mode 5: same normal + mode 6: same co-planer +*/ +int facegroup_select(EditMesh *em, short mode) +{ + EditFace *efa, *base_efa=NULL; + unsigned int selcount=0; /*count how many new faces we select*/ + + /*deselcount, count how many deselected faces are left, so we can bail out early + also means that if there are no deselected faces, we can avoid a lot of looping */ + unsigned int deselcount=0; + + short ok=0; + float thresh=0; // XXX G.scene->toolsettings->select_thresh; + + for(efa= em->faces.first; efa; efa= efa->next) { + if (!efa->h) { + if (efa->f & SELECT) { + efa->f1=1; + ok=1; + } else { + efa->f1=0; + deselcount++; /* a deselected face we may select later */ + } + } + } + + if (!ok || !deselcount) /* no data selected OR no more data to select */ + return 0; + + /*if mode is 3 then record face areas, 4 record perimeter */ + if (mode==3) { + for(efa= em->faces.first; efa; efa= efa->next) { + efa->tmp.fp= EM_face_area(efa); + } + } else if (mode==4) { + for(efa= em->faces.first; efa; efa= efa->next) { + efa->tmp.fp= EM_face_perimeter(efa); + } + } + + for(base_efa= em->faces.first; base_efa; base_efa= base_efa->next) { + if (base_efa->f1) { /* This was one of the faces originaly selected */ + if (mode==1) { /* same material */ + for(efa= em->faces.first; efa; efa= efa->next) { + if ( + !(efa->f & SELECT) && + !efa->h && + base_efa->mat_nr == efa->mat_nr + ) { + EM_select_face(efa, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==2) { /* same image */ + MTFace *tf, *base_tf; + + base_tf = (MTFace*)CustomData_em_get(&em->fdata, base_efa->data, + CD_MTFACE); + + if(!base_tf) + return selcount; + + for(efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->f & SELECT) && !efa->h) { + tf = (MTFace*)CustomData_em_get(&em->fdata, efa->data, + CD_MTFACE); + + if(base_tf->tpage == tf->tpage) { + EM_select_face(efa, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } else if (mode==3 || mode==4) { /* same area OR same perimeter, both use the same temp var */ + for(efa= em->faces.first; efa; efa= efa->next) { + if ( + (!(efa->f & SELECT) && !efa->h) && + SCALE_CMP(base_efa->tmp.fp, efa->tmp.fp) + ) { + EM_select_face(efa, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==5) { /* same normal */ + float angle; + for(efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->f & SELECT) && !efa->h) { + angle= VecAngle2(base_efa->n, efa->n); + if (angle/180.0<=thresh) { + EM_select_face(efa, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } else if (mode==6) { /* same planer */ + float angle, base_dot, dot; + base_dot= Inpf(base_efa->cent, base_efa->n); + for(efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->f & SELECT) && !efa->h) { + angle= VecAngle2(base_efa->n, efa->n); + if (angle/180.0<=thresh) { + dot=Inpf(efa->cent, base_efa->n); + if (fabs(base_dot-dot) <= thresh) { + EM_select_face(efa, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } + } + } + } /* end base_efa loop */ + return selcount; +} + + +/* +EDGE GROUP + mode 1: same length + mode 2: same direction + mode 3: same number of face users + mode 4: similar face angles. + mode 5: similar crease + mode 6: similar seam + mode 7: similar sharp +*/ + +/* this function is only used by edgegroup_select's edge angle */ + + + +static int edgegroup_select__internal(EditMesh *em, short mode) +{ + EditEdge *eed, *base_eed=NULL; + unsigned int selcount=0; /* count how many new edges we select*/ + + /*count how many visible selected edges there are, + so we can return when there are none left */ + unsigned int deselcount=0; + + short ok=0; + float thresh=0; // XXX G.scene->toolsettings->select_thresh; + + for(eed= em->edges.first; eed; eed= eed->next) { + if (!eed->h) { + if (eed->f & SELECT) { + eed->f1=1; + ok=1; + } else { + eed->f1=0; + deselcount++; + } + /* set all eed->tmp.l to 0 we use it later. + for counting face users*/ + eed->tmp.l=0; + eed->f2=0; /* only for mode 4, edge animations */ + } + } + + if (!ok || !deselcount) /* no data selected OR no more data to select*/ + return 0; + + if (mode==1) { /*store length*/ + for(eed= em->edges.first; eed; eed= eed->next) { + if (!eed->h) /* dont calc data for hidden edges*/ + eed->tmp.fp= VecLenf(eed->v1->co, eed->v2->co); + } + } else if (mode==3) { /*store face users*/ + EditFace *efa; + /* cound how many faces each edge uses use tmp->l */ + for(efa= em->faces.first; efa; efa= efa->next) { + efa->e1->tmp.l++; + efa->e2->tmp.l++; + efa->e3->tmp.l++; + if (efa->e4) efa->e4->tmp.l++; + } + } else if (mode==4) { /*store edge angles */ + EditFace *efa; + int j; + /* cound how many faces each edge uses use tmp.l */ + for(efa= em->faces.first; efa; efa= efa->next) { + /* here we use the edges temp data to assign a face + if a face has alredy been assigned (eed->f2==1) + we calculate the angle between the current face and + the edges previously found face. + store the angle in eed->tmp.fp (loosing the face eed->tmp.f) + but tagging eed->f2==2, so we know not to look at it again. + This only works for edges that connect to 2 faces. but its good enough + */ + + /* se we can loop through face edges*/ + j=0; + eed= efa->e1; + while (j<4) { + if (j==1) eed= efa->e2; + else if (j==2) eed= efa->e3; + else if (j==3) { + eed= efa->e4; + if (!eed) + break; + } /* done looping */ + + if (!eed->h) { /* dont calc data for hidden edges*/ + if (eed->f2==2) + break; + else if (eed->f2==0) /* first access, assign the face */ + eed->tmp.f= efa; + else if (eed->f2==1) /* second, we assign the angle*/ + eed->tmp.fp= VecAngle2(eed->tmp.f->n, efa->n)/180; + eed->f2++; /* f2==0 no face assigned. f2==1 one face found. f2==2 angle calculated.*/ + } + j++; + } + } + } + + for(base_eed= em->edges.first; base_eed; base_eed= base_eed->next) { + if (base_eed->f1) { + if (mode==1) { /* same length */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + SCALE_CMP(base_eed->tmp.fp, eed->tmp.fp) + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==2) { /* same direction */ + float base_dir[3], dir[3], angle; + VecSubf(base_dir, base_eed->v1->co, base_eed->v2->co); + for(eed= em->edges.first; eed; eed= eed->next) { + if (!(eed->f & SELECT) && !eed->h) { + VecSubf(dir, eed->v1->co, eed->v2->co); + angle= VecAngle2(base_dir, dir); + + if (angle>90) /* use the smallest angle between the edges */ + angle= fabs(angle-180.0f); + + if (angle/90.0<=thresh) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } else if (mode==3) { /* face users */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + base_eed->tmp.l==eed->tmp.l + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==4 && base_eed->f2==2) { /* edge angles, f2==2 means the edge has an angle. */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + eed->f2==2 && + (fabs(base_eed->tmp.fp-eed->tmp.fp)<=thresh) + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==5) { /* edge crease */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + (fabs(base_eed->crease-eed->crease) <= thresh) + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==6) { /* edge seam */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + (eed->seam == base_eed->seam) + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==7) { /* edge sharp */ + for(eed= em->edges.first; eed; eed= eed->next) { + if ( + !(eed->f & SELECT) && + !eed->h && + (eed->sharp == base_eed->sharp) + ) { + EM_select_edge(eed, 1); + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } + } + return selcount; +} +/* wrap the above function but do selection flushing edge to face */ +int edgegroup_select(EditMesh *em, short mode) +{ + int selcount = edgegroup_select__internal(em, mode); + + if (selcount) { + /* Could run a generic flush function, + * but the problem is only that all edges of a face + * can be selected without the face becoming selected */ + EditFace *efa; + for(efa= em->faces.first; efa; efa= efa->next) { + if (efa->v4) { + if (efa->e1->f&SELECT && efa->e2->f&SELECT && efa->e3->f&SELECT && efa->e4->f&SELECT) + efa->f |= SELECT; + } else { + if (efa->e1->f&SELECT && efa->e2->f&SELECT && efa->e3->f&SELECT) + efa->f |= SELECT; + } + } + } + return selcount; +} + + + +/* +VERT GROUP + mode 1: same normal + mode 2: same number of face users + mode 3: same vertex groups +*/ +int vertgroup_select(EditMesh *em, short mode) +{ + EditVert *eve, *base_eve=NULL; + + unsigned int selcount=0; /* count how many new edges we select*/ + + /*count how many visible selected edges there are, + so we can return when there are none left */ + unsigned int deselcount=0; + + short ok=0; + float thresh=0; // XXX G.scene->toolsettings->select_thresh; + + for(eve= em->verts.first; eve; eve= eve->next) { + if (!eve->h) { + if (eve->f & SELECT) { + eve->f1=1; + ok=1; + } else { + eve->f1=0; + deselcount++; + } + /* set all eve->tmp.l to 0 we use them later.*/ + eve->tmp.l=0; + } + + } + + if (!ok || !deselcount) /* no data selected OR no more data to select*/ + return 0; + + + if (mode==2) { /* store face users */ + EditFace *efa; + + /* count how many faces each edge uses use tmp->l */ + for(efa= em->faces.first; efa; efa= efa->next) { + efa->v1->tmp.l++; + efa->v2->tmp.l++; + efa->v3->tmp.l++; + if (efa->v4) efa->v4->tmp.l++; + } + } + + + for(base_eve= em->verts.first; base_eve; base_eve= base_eve->next) { + if (base_eve->f1) { + + if (mode==1) { /* same normal */ + float angle; + for(eve= em->verts.first; eve; eve= eve->next) { + if (!(eve->f & SELECT) && !eve->h) { + angle= VecAngle2(base_eve->no, eve->no); + if (angle/180.0<=thresh) { + eve->f |= SELECT; + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } + } else if (mode==2) { /* face users */ + for(eve= em->verts.first; eve; eve= eve->next) { + if ( + !(eve->f & SELECT) && + !eve->h && + base_eve->tmp.l==eve->tmp.l + ) { + eve->f |= SELECT; + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + } + } + } else if (mode==3) { /* vertex groups */ + MDeformVert *dvert, *base_dvert; + short i, j; /* weight index */ + + base_dvert= CustomData_em_get(&em->vdata, base_eve->data, + CD_MDEFORMVERT); + + if (!base_dvert || base_dvert->totweight == 0) + return selcount; + + for(eve= em->verts.first; eve; eve= eve->next) { + dvert= CustomData_em_get(&em->vdata, eve->data, + CD_MDEFORMVERT); + + if (dvert && !(eve->f & SELECT) && !eve->h && dvert->totweight) { + /* do the extra check for selection in the following if, so were not + checking verts that may be alredy selected */ + for (i=0; base_dvert->totweight >i && !(eve->f & SELECT); i++) { + for (j=0; dvert->totweight >j; j++) { + if (base_dvert->dw[i].def_nr==dvert->dw[j].def_nr) { + eve->f |= SELECT; + selcount++; + deselcount--; + if (!deselcount) /*have we selected all posible faces?, if so return*/ + return selcount; + break; + } + } + } + } + } + } + } + } /* end basevert loop */ + return selcount; +} + +/* EditMode menu triggered from space.c by pressing Shift+G +handles face/edge vert context and +facegroup_select/edgegroup_select/vertgroup_select do all the work +*/ + +void select_mesh_group_menu(EditMesh *em) +{ + short ret; + int selcount, first_item=1, multi=0; + char str[512] = "Select Similar "; /* total max length is 404 at the moment */ + + if (!ELEM3(em->selectmode, SCE_SELECT_VERTEX, SCE_SELECT_EDGE, SCE_SELECT_FACE)) { + multi=1; + } + + if(em->selectmode & SCE_SELECT_VERTEX) { + if (multi) strcat(str, "%t|Vertices%x-1|"); + else strcat(str, "Vertices %t|"); + strcat(str, " Normal %x1| Face Users %x2| Shared Vertex Groups%x3"); + first_item=0; + } + + if(em->selectmode & SCE_SELECT_EDGE) { + if (multi) { + if (first_item) strcat(str, "%t|Edges%x-1|"); + else strcat(str, "|%l|Edges%x-1|"); + } else strcat(str, "Edges %t|"); + + strcat(str, " Length %x10| Direction %x20| Face Users%x30| Face Angle%x40| Crease%x50| Seam%x60| Sharp%x70"); + first_item=0; + } + + if(em->selectmode & SCE_SELECT_FACE) { + if (multi) { + strcat(str, "|%l|Faces%x-1|"); + } else strcat(str, "Faces %t|"); + strcat(str, " Material %x100| Image %x200| Area %x300| Perimeter %x400| Normal %x500| Co-Planar %x600"); + + } + + ret= pupmenu(str); + if (ret<1) return; + + if (ret<10) { + selcount= vertgroup_select(em, ret); + if (selcount) { /* update if data was selected */ + EM_select_flush(em); /* so that selected verts, go onto select faces */ + G.totvertsel += selcount; +// if (EM_texFaceCheck()) + BIF_undo_push("Select Similar Vertices"); + } + return; + } + + if (ret<100) { + selcount= edgegroup_select(em, ret/10); + + if (selcount) { /* update if data was selected */ + /*EM_select_flush(em);*/ /* dont use because it can end up selecting more edges and is not usefull*/ + G.totedgesel+=selcount; +// if (EM_texFaceCheck()) + BIF_undo_push("Select Similar Edges"); + } + return; + } + + if (ret<1000) { + selcount= facegroup_select(em, ret/100); + if (selcount) { /* update if data was selected */ + G.totfacesel+=selcount; +// if (EM_texFaceCheck()) + BIF_undo_push("Select Similar Faces"); + } + return; + } +} + +int mesh_layers_menu_charlen(CustomData *data, int type) +{ + int i, len = 0; + /* see if there is a duplicate */ + for(i=0; i<data->totlayer; i++) { + if((&data->layers[i])->type == type) { + /* we could count the chars here but we'll just assumeme each + * is 32 chars with some room for the menu text - 40 should be fine */ + len+=40; + } + } + return len; +} + +/* this function adds menu text into an existing string. + * this string's size should be allocated with mesh_layers_menu_charlen */ +void mesh_layers_menu_concat(CustomData *data, int type, char *str) { + int i, count = 0; + char *str_pt = str; + CustomDataLayer *layer; + + /* see if there is a duplicate */ + for(i=0; i<data->totlayer; i++) { + layer = &data->layers[i]; + if(layer->type == type) { + str_pt += sprintf(str_pt, "%s%%x%d|", layer->name, count); + count++; + } + } +} + +int mesh_layers_menu(CustomData *data, int type) { + int ret; + char *str_pt, *str; + + str_pt = str = MEM_mallocN(mesh_layers_menu_charlen(data, type) + 18, "layer menu"); + str[0] = '\0'; + + str_pt += sprintf(str_pt, "Layers%%t|"); + + mesh_layers_menu_concat(data, type, str_pt); + + ret = pupmenu(str); + MEM_freeN(str); + return ret; +} + +void EM_mesh_copy_edge(EditMesh *em, short type) +{ + EditSelection *ese; + short change=0; + + EditEdge *eed, *eed_act; + float vec[3], vec_mid[3], eed_len, eed_len_act; + + if (!em) return; + + ese = em->selected.last; + if (!ese) return; + + eed_act = (EditEdge*)ese->data; + + switch (type) { + case 1: /* copy crease */ + for(eed=em->edges.first; eed; eed=eed->next) { + if (eed->f & SELECT && eed != eed_act && eed->crease != eed_act->crease) { + eed->crease = eed_act->crease; + change = 1; + } + } + break; + case 2: /* copy bevel weight */ + for(eed=em->edges.first; eed; eed=eed->next) { + if (eed->f & SELECT && eed != eed_act && eed->bweight != eed_act->bweight) { + eed->bweight = eed_act->bweight; + change = 1; + } + } + break; + + case 3: /* copy length */ + eed_len_act = VecLenf(eed_act->v1->co, eed_act->v2->co); + for(eed=em->edges.first; eed; eed=eed->next) { + if (eed->f & SELECT && eed != eed_act) { + + eed_len = VecLenf(eed->v1->co, eed->v2->co); + + if (eed_len == eed_len_act) continue; + /* if this edge is zero length we cont do anything with it*/ + if (eed_len == 0.0f) continue; + if (eed_len_act == 0.0f) { + VecAddf(vec_mid, eed->v1->co, eed->v2->co); + VecMulf(vec_mid, 0.5); + VECCOPY(eed->v1->co, vec_mid); + VECCOPY(eed->v2->co, vec_mid); + } else { + /* copy the edge length */ + VecAddf(vec_mid, eed->v1->co, eed->v2->co); + VecMulf(vec_mid, 0.5); + + /* SCALE 1 */ + VecSubf(vec, eed->v1->co, vec_mid); + VecMulf(vec, eed_len_act/eed_len); + VecAddf(eed->v1->co, vec, vec_mid); + + /* SCALE 2 */ + VecSubf(vec, eed->v2->co, vec_mid); + VecMulf(vec, eed_len_act/eed_len); + VecAddf(eed->v2->co, vec, vec_mid); + } + change = 1; + } + } + + if (change) + recalc_editnormals(em); + + break; + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Copy Edge Attribute"); + } +} + +void EM_mesh_copy_face(EditMesh *em, short type) +{ + short change=0; + + EditFace *efa, *efa_act; + MTFace *tf, *tf_act = NULL; + MCol *mcol, *mcol_act = NULL; + if (!em) return; + efa_act = EM_get_actFace(em, 0); + + if (!efa_act) return; + + tf_act = CustomData_em_get(&em->fdata, efa_act->data, CD_MTFACE); + mcol_act = CustomData_em_get(&em->fdata, efa_act->data, CD_MCOL); + + switch (type) { + case 1: /* copy material */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa->mat_nr != efa_act->mat_nr) { + efa->mat_nr = efa_act->mat_nr; + change = 1; + } + } + break; + case 2: /* copy image */ + if (!tf_act) { + error("mesh has no uv/image layers"); + return; + } + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa != efa_act) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (tf_act->tpage) { + tf->tpage = tf_act->tpage; + tf->mode |= TF_TEX; + } else { + tf->tpage = NULL; + tf->mode &= ~TF_TEX; + } + tf->tile= tf_act->tile; + change = 1; + } + } + break; + + case 3: /* copy UV's */ + if (!tf_act) { + error("mesh has no uv/image layers"); + return; + } + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa != efa_act) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + memcpy(tf->uv, tf_act->uv, sizeof(tf->uv)); + change = 1; + } + } + break; + case 4: /* mode's */ + if (!tf_act) { + error("mesh has no uv/image layers"); + return; + } + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa != efa_act) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tf->mode= tf_act->mode; + change = 1; + } + } + break; + case 5: /* copy transp's */ + if (!tf_act) { + error("mesh has no uv/image layers"); + return; + } + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa != efa_act) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tf->transp= tf_act->transp; + change = 1; + } + } + break; + + case 6: /* copy vcols's */ + if (!mcol_act) { + error("mesh has no color layers"); + return; + } else { + /* guess the 4th color if needs be */ + float val =- 1; + + if (!efa_act->v4) { + /* guess the othe vale, we may need to use it + * + * Modifying the 4th value of the mcol is ok here since its not seen + * on a triangle + * */ + val = ((float)(mcol_act->r + (mcol_act+1)->r + (mcol_act+2)->r)) / 3; CLAMP(val, 0, 255); + (mcol_act+3)->r = (char)val; + + val = ((float)(mcol_act->g + (mcol_act+1)->g + (mcol_act+2)->g)) / 3; CLAMP(val, 0, 255); + (mcol_act+3)->g = (char)val; + + val = ((float)(mcol_act->b + (mcol_act+1)->b + (mcol_act+2)->b)) / 3; CLAMP(val, 0, 255); + (mcol_act+3)->b = (char)val; + } + + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT && efa != efa_act) { + /* TODO - make copy from tri to quad guess the 4th vert */ + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + memcpy(mcol, mcol_act, sizeof(MCol)*4); + change = 1; + } + } + } + break; + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + if (type==3) { +// XXX allqueue(REDRAWIMAGE, 0); + } + + BIF_undo_push("Copy Face Attribute"); + } +} + + +void EM_mesh_copy_face_layer(EditMesh *em, short type) +{ + short change=0; + + EditFace *efa; + MTFace *tf, *tf_from; + MCol *mcol, *mcol_from; + + if (!em) return; + + switch(type) { + case 7: + case 8: + case 9: + if (CustomData_number_of_layers(&em->fdata, CD_MTFACE)<2) { + error("mesh does not have multiple uv/image layers"); + return; + } else { + int layer_orig_idx, layer_idx; + + layer_idx = mesh_layers_menu(&em->fdata, CD_MTFACE); + if (layer_idx<0) return; + + /* warning, have not updated mesh pointers however this is not needed since we swicth back */ + layer_orig_idx = CustomData_get_active_layer(&em->fdata, CD_MTFACE); + if (layer_idx==layer_orig_idx) + return; + + /* get the tfaces */ + CustomData_set_layer_active(&em->fdata, CD_MTFACE, (int)layer_idx); + /* store the tfaces in our temp */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + efa->tmp.p = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + } + } + CustomData_set_layer_active(&em->fdata, CD_MTFACE, layer_orig_idx); + } + break; + + case 10: /* select vcol layers - make sure this stays in sync with above code */ + if (CustomData_number_of_layers(&em->fdata, CD_MCOL)<2) { + error("mesh does not have multiple color layers"); + return; + } else { + int layer_orig_idx, layer_idx; + + layer_idx = mesh_layers_menu(&em->fdata, CD_MCOL); + if (layer_idx<0) return; + + /* warning, have not updated mesh pointers however this is not needed since we swicth back */ + layer_orig_idx = CustomData_get_active_layer(&em->fdata, CD_MCOL); + if (layer_idx==layer_orig_idx) + return; + + /* get the tfaces */ + CustomData_set_layer_active(&em->fdata, CD_MCOL, (int)layer_idx); + /* store the tfaces in our temp */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + efa->tmp.p = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + } + } + CustomData_set_layer_active(&em->fdata, CD_MCOL, layer_orig_idx); + + } + break; + } + + /* layer copy only - sanity checks done above */ + switch (type) { + case 7: /* copy UV's only */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */ + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + memcpy(tf->uv, tf_from->uv, sizeof(tf->uv)); + change = 1; + } + } + break; + case 8: /* copy image settings only */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */ + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (tf_from->tpage) { + tf->tpage = tf_from->tpage; + tf->mode |= TF_TEX; + } else { + tf->tpage = NULL; + tf->mode &= ~TF_TEX; + } + tf->tile= tf_from->tile; + change = 1; + } + } + break; + case 9: /* copy all tface info */ + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */ + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + memcpy(tf->uv, ((MTFace *)efa->tmp.p)->uv, sizeof(tf->uv)); + tf->tpage = tf_from->tpage; + tf->mode = tf_from->mode; + tf->transp = tf_from->transp; + change = 1; + } + } + break; + case 10: + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol_from = (MCol *)efa->tmp.p; + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + memcpy(mcol, mcol_from, sizeof(MCol)*4); + change = 1; + } + } + break; + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Copy Face Layer"); + } +} + + +/* ctrl+c in mesh editmode */ +void mesh_copy_menu(EditMesh *em) +{ + EditSelection *ese; + int ret; + if (!em) return; + + ese = em->selected.last; + + /* Faces can have a NULL ese, so dont return on a NULL ese here */ + + if(ese && ese->type == EDITVERT) { + /* EditVert *ev, *ev_act = (EditVert*)ese->data; + ret= pupmenu(""); */ + } else if(ese && ese->type == EDITEDGE) { + ret= pupmenu("Copy Active Edge to Selected%t|Crease%x1|Bevel Weight%x2|Length%x3"); + if (ret<1) return; + + EM_mesh_copy_edge(em, ret); + + } else if(ese==NULL || ese->type == EDITFACE) { + ret= pupmenu( + "Copy Face Selected%t|" + "Active Material%x1|Active Image%x2|Active UV Coords%x3|" + "Active Mode%x4|Active Transp%x5|Active Vertex Colors%x6|%l|" + + "TexFace UVs from layer%x7|" + "TexFace Images from layer%x8|" + "TexFace All from layer%x9|" + "Vertex Colors from layer%x10"); + if (ret<1) return; + + if (ret<=6) { + EM_mesh_copy_face(em, ret); + } else { + EM_mesh_copy_face_layer(em, ret); + } + } +} + + +/* **************** LOOP SELECTS *************** */ + +/* selects quads in loop direction of indicated edge */ +/* only flush over edges with valence <= 2 */ +void faceloop_select(EditMesh *em, EditEdge *startedge, int select) +{ + EditEdge *eed; + EditFace *efa; + int looking= 1; + + /* in eed->f1 we put the valence (amount of faces in edge) */ + /* in eed->f2 we put tagged flag as correct loop */ + /* in efa->f1 we put tagged flag as correct to select */ + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2= 0; + } + for(efa= em->faces.first; efa; efa= efa->next) { + efa->f1= 0; + if(efa->h==0) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + + /* tag startedge OK*/ + startedge->f2= 1; + + while(looking) { + looking= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0 && efa->e4 && efa->f1==0) { /* not done quad */ + if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */ + + /* if edge tagged, select opposing edge and mark face ok */ + if(efa->e1->f2) { + efa->e3->f2= 1; + efa->f1= 1; + looking= 1; + } + else if(efa->e2->f2) { + efa->e4->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e3->f2) { + efa->e1->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e4->f2) { + efa->e2->f2= 1; + efa->f1= 1; + looking= 1; + } + } + } + } + } + + /* (de)select the faces */ + if(select!=2) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f1) EM_select_face(efa, select); + } + } +} + + +/* helper for edgeloop_select, checks for eed->f2 tag in faces */ +static int edge_not_in_tagged_face(EditMesh *em, EditEdge *eed) +{ + EditFace *efa; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + if(efa->e1==eed || efa->e2==eed || efa->e3==eed || efa->e4==eed) { /* edge is in face */ + if(efa->e1->f2 || efa->e2->f2 || efa->e3->f2 || (efa->e4 && efa->e4->f2)) { /* face is tagged */ + return 0; + } + } + } + } + return 1; +} + +/* selects or deselects edges that: +- if edges has 2 faces: + - has vertices with valence of 4 + - not shares face with previous edge +- if edge has 1 face: + - has vertices with valence 4 + - not shares face with previous edge + - but also only 1 face +- if edge no face: + - has vertices with valence 2 +*/ +static void edgeloop_select(EditMesh *em, EditEdge *starteed, int select) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + int looking= 1; + + /* in f1 we put the valence (amount of edges in a vertex, or faces in edge) */ + /* in eed->f2 and efa->f1 we put tagged flag as correct loop */ + for(eve= em->verts.first; eve; eve= eve->next) { + eve->f1= 0; + eve->f2= 0; + } + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2= 0; + if((eed->h & 1)==0) { /* fgon edges add to valence too */ + eed->v1->f1++; eed->v2->f1++; + } + } + for(efa= em->faces.first; efa; efa= efa->next) { + efa->f1= 0; + if(efa->h==0) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + + /* looped edges & vertices get tagged f2 */ + starteed->f2= 1; + if(starteed->v1->f1<5) starteed->v1->f2= 1; + if(starteed->v2->f1<5) starteed->v2->f2= 1; + /* sorry, first edge isnt even ok */ + if(starteed->v1->f2==0 && starteed->v2->f2==0) looking= 0; + + while(looking) { + looking= 0; + + /* find correct valence edges which are not tagged yet, but connect to tagged one */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0 && eed->f2==0) { /* edge not hidden, not tagged */ + if( (eed->v1->f1<5 && eed->v1->f2) || (eed->v2->f1<5 && eed->v2->f2)) { /* valence of vertex OK, and is tagged */ + /* new edge is not allowed to be in face with tagged edge */ + if(edge_not_in_tagged_face(em, eed)) { + if(eed->f1==starteed->f1) { /* same amount of faces */ + looking= 1; + eed->f2= 1; + if(eed->v2->f1<5) eed->v2->f2= 1; + if(eed->v1->f1<5) eed->v1->f2= 1; + } + } + } + } + } + } + /* and we do the select */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f2) EM_select_edge(eed, select); + } +} + +/* + Almostly exactly the same code as faceloop select +*/ +static void edgering_select(EditMesh *em, EditEdge *startedge, int select) +{ + EditEdge *eed; + EditFace *efa; + int looking= 1; + + /* in eed->f1 we put the valence (amount of faces in edge) */ + /* in eed->f2 we put tagged flag as correct loop */ + /* in efa->f1 we put tagged flag as correct to select */ + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2= 0; + } + for(efa= em->faces.first; efa; efa= efa->next) { + efa->f1= 0; + if(efa->h==0) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + + /* tag startedge OK */ + startedge->f2= 1; + + while(looking) { + looking= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e4 && efa->f1==0 && !efa->h) { /* not done quad */ + if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */ + + /* if edge tagged, select opposing edge and mark face ok */ + if(efa->e1->f2) { + efa->e3->f2= 1; + efa->f1= 1; + looking= 1; + } + else if(efa->e2->f2) { + efa->e4->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e3->f2) { + efa->e1->f2= 1; + efa->f1= 1; + looking= 1; + } + if(efa->e4->f2) { + efa->e2->f2= 1; + efa->f1= 1; + looking= 1; + } + } + } + } + } + + /* (de)select the edges */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f2) EM_select_edge(eed, select); + } +} + +void loop_multiselect(EditMesh *em, int looptype) +{ + EditEdge *eed; + EditEdge **edarray; + int edindex, edfirstcount; + + /*edarray = MEM_mallocN(sizeof(*edarray)*G.totedgesel,"edge array");*/ + edarray = MEM_mallocN(sizeof(EditEdge*)*G.totedgesel,"edge array"); + edindex = 0; + edfirstcount = G.totedgesel; + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + edarray[edindex] = eed; + edindex += 1; + } + } + + if(looptype){ + for(edindex = 0; edindex < edfirstcount; edindex +=1){ + eed = edarray[edindex]; + edgering_select(em, eed,SELECT); + } + EM_selectmode_flush(em); + BIF_undo_push("Edge Ring Multi-Select"); + } + else{ + for(edindex = 0; edindex < edfirstcount; edindex +=1){ + eed = edarray[edindex]; + edgeloop_select(em, eed,SELECT); + } + EM_selectmode_flush(em); + BIF_undo_push("Edge Loop Multi-Select"); + } + MEM_freeN(edarray); +// if (EM_texFaceCheck()) +} + +/* ***************** MAIN MOUSE SELECTION ************** */ + +/* just to have the functions nice together */ + +static void mouse_mesh_loop(Scene *scene, View3D *v3d, EditMesh *em) +{ + EditEdge *eed; + int select= 1; + int dist= 50; + int shift= 0, alt= 0, ctrl= 0; // XXX + + eed= findnearestedge(v3d, em, &dist); + if(eed) { + if (0) { // XXX G.scene->toolsettings->edge_mode == EDGE_MODE_SELECT) { + if(shift==0) EM_clear_flag_all(em, SELECT); + + if((eed->f & SELECT)==0) select=1; + else if(shift) select=0; + + if(em->selectmode & SCE_SELECT_FACE) { + faceloop_select(em, eed, select); + } + else if(em->selectmode & SCE_SELECT_EDGE) { + if((alt && ctrl)) + edgering_select(em, eed, select); + else if(alt) + edgeloop_select(em, eed, select); + } + else if(em->selectmode & SCE_SELECT_VERTEX) { + if((alt && ctrl)) + edgering_select(em, eed, select); + else if(alt) + edgeloop_select(em, eed, select); + } + + /* frontbuffer draw of last selected only */ + unified_select_draw(em, NULL, eed, NULL); + + EM_selectmode_flush(em); +// if (EM_texFaceCheck()) + } else { /*(G.scene->toolsettings->edge_mode == EDGE_MODE_TAG_*)*/ + int act = (edgetag_context_check(scene, eed)==0); + int path = 0; + + if (alt && ctrl && em->selected.last) { + EditSelection *ese = em->selected.last; + + if(ese && ese->type == EDITEDGE) { + EditEdge *eed_act; + eed_act = (EditEdge*)ese->data; + if (eed_act != eed) { + /* If shift is pressed we need to use the last active edge, (if it exists) */ + if (edgetag_shortest_path(scene, em, eed_act, eed)) { + EM_remove_selection(em, eed_act, EDITEDGE); + EM_select_edge(eed_act, 0); + path = 1; + } + } + } + } + if (path==0) { + edgetag_context_set(scene, eed, act); /* switch the edge option */ + } + + if (act) { + if ((eed->f & SELECT)==0) { + EM_select_edge(eed, 1); + EM_selectmode_flush(em); + } + /* even if this is selected it may not be in the selection list */ + EM_store_selection(em, eed, EDITEDGE); + } else { + if (eed->f & SELECT) { + EM_select_edge(eed, 0); + /* logic is differnt from above here since if this was selected we dont know if its in the selection list or not */ + EM_remove_selection(em, eed, EDITEDGE); + + EM_selectmode_flush(em); + } + } + + switch (0) { // XXX G.scene->toolsettings->edge_mode) { + case EDGE_MODE_TAG_SEAM: + G.f |= G_DRAWSEAMS; + break; + case EDGE_MODE_TAG_SHARP: + G.f |= G_DRAWSHARP; + break; + case EDGE_MODE_TAG_CREASE: + G.f |= G_DRAWCREASES; + break; + case EDGE_MODE_TAG_BEVEL: + G.f |= G_DRAWBWEIGHTS; + break; + } + + unified_select_draw(em, NULL, eed, NULL); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + } + + } +} + + +/* here actual select happens */ +void mouse_mesh(Scene *scene, View3D *v3d, EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + int shift= 0, alt= 0; // XXX + + if(alt) mouse_mesh_loop(scene, v3d, em); + else if(unified_findnearest(v3d, em, &eve, &eed, &efa)) { + + if((shift)==0) EM_clear_flag_all(em, SELECT); + + if(efa) { + /* set the last selected face */ + EM_set_actFace(em, efa); + + if( (efa->f & SELECT)==0 ) { + EM_store_selection(em, efa, EDITFACE); + EM_select_face_fgon(em, efa, 1); + } + else if(shift) { + EM_remove_selection(em, efa, EDITFACE); + EM_select_face_fgon(em, efa, 0); + } + } + else if(eed) { + if((eed->f & SELECT)==0) { + EM_store_selection(em, eed, EDITEDGE); + EM_select_edge(eed, 1); + } + else if(shift) { + EM_remove_selection(em, eed, EDITEDGE); + EM_select_edge(eed, 0); + } + } + else if(eve) { + if((eve->f & SELECT)==0) { + eve->f |= SELECT; + EM_store_selection(em, eve, EDITVERT); + } + else if(shift){ + EM_remove_selection(em, eve, EDITVERT); + eve->f &= ~SELECT; + } + } + + /* frontbuffer draw of last selected only */ + unified_select_draw(em, eve, eed, efa); + + EM_selectmode_flush(em); + +// if (EM_texFaceCheck()) { + + if (efa && efa->mat_nr != G.obedit->actcol-1) { + G.obedit->actcol= efa->mat_nr+1; +// BIF_preview_changed(ID_MA); + } + } + +// rightmouse_transform(); +} + + +void selectconnected_mesh_all(EditMesh *em) +{ + EditVert *v1,*v2; + EditEdge *eed; + short done=1, toggle=0; + + if(em->edges.first==0) return; + + while(done==1) { + done= 0; + + toggle++; + if(toggle & 1) eed= em->edges.first; + else eed= em->edges.last; + + while(eed) { + v1= eed->v1; + v2= eed->v2; + if(eed->h==0) { + if(v1->f & SELECT) { + if( (v2->f & SELECT)==0 ) { + v2->f |= SELECT; + done= 1; + } + } + else if(v2->f & SELECT) { + if( (v1->f & SELECT)==0 ) { + v1->f |= SELECT; + done= 1; + } + } + } + if(toggle & 1) eed= eed->next; + else eed= eed->prev; + } + } + + /* now use vertex select flag to select rest */ + EM_select_flush(em); + +// if (EM_texFaceCheck()) + BIF_undo_push("Select Connected (All)"); +} + +void selectconnected_mesh(View3D *v3d, EditMesh *em) +{ + EditVert *eve, *v1, *v2; + EditEdge *eed; + EditFace *efa; + short done=1, sel, toggle=0; + int shift= 0; // XXX + + if(em->edges.first==0) return; + + if( unified_findnearest(v3d, em, &eve, &eed, &efa)==0 ) { + /* error("Nothing indicated "); */ /* this is mostly annoying, eps with occluded geometry */ + return; + } + + sel= 1; + if(shift) sel=0; + + /* clear test flags */ + for(v1= em->verts.first; v1; v1= v1->next) v1->f1= 0; + + /* start vertex/face/edge */ + if(eve) eve->f1= 1; + else if(eed) eed->v1->f1= eed->v2->f1= 1; + else efa->v1->f1= efa->v2->f1= efa->v3->f1= 1; + + /* set flag f1 if affected */ + while(done==1) { + done= 0; + toggle++; + + if(toggle & 1) eed= em->edges.first; + else eed= em->edges.last; + + while(eed) { + v1= eed->v1; + v2= eed->v2; + + if(eed->h==0) { + if(v1->f1 && v2->f1==0) { + v2->f1= 1; + done= 1; + } + else if(v1->f1==0 && v2->f1) { + v1->f1= 1; + done= 1; + } + } + + if(toggle & 1) eed= eed->next; + else eed= eed->prev; + } + } + + /* now use vertex f1 flag to select/deselect */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->f1 && eed->v2->f1) + EM_select_edge(eed, sel); + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v1->f1 && efa->v2->f1 && efa->v3->f1 && (efa->v4==NULL || efa->v4->f1)) + EM_select_face(efa, sel); + } + /* no flush needed, connected geometry is done */ + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Linked"); + +} + +/* for use with selectconnected_delimit_mesh only! */ +#define is_edge_delimit_ok(eed) ((eed->tmp.l == 1) && (eed->seam==0)) +#define is_face_tag(efa) is_edge_delimit_ok(efa->e1) || is_edge_delimit_ok(efa->e2) || is_edge_delimit_ok(efa->e3) || (efa->v4 && is_edge_delimit_ok(efa->e4)) + +#define face_tag(efa)\ + if(efa->v4) efa->tmp.l= efa->e1->tmp.l= efa->e2->tmp.l= efa->e3->tmp.l= efa->e4->tmp.l= 1;\ + else efa->tmp.l= efa->e1->tmp.l= efa->e2->tmp.l= efa->e3->tmp.l= 1; + +/* all - 1) use all faces for extending the selection 2) only use the mouse face + * sel - 1) select 0) deselect + * */ +static void selectconnected_delimit_mesh__internal(EditMesh *em, short all, short sel) +{ + View3D *v3d= NULL; // XXX + EditFace *efa; + short done=1, change=0; + int dist = 75; + EditEdge *eed; + if(em->faces.first==0) return; + + /* flag all edges as off*/ + for(eed= em->edges.first; eed; eed= eed->next) + eed->tmp.l=0; + + if (all) { + for(efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + face_tag(efa); + } else { + efa->tmp.l = 0; + } + } + } else { + EditFace *efa_mouse = findnearestface(v3d, em, &dist); + + if( !efa_mouse ) { + /* error("Nothing indicated "); */ /* this is mostly annoying, eps with occluded geometry */ + return; + } + + for(efa= em->faces.first; efa; efa= efa->next) { + efa->tmp.l = 0; + } + efa_mouse->tmp.l = 1; + face_tag(efa_mouse); + } + + while(done==1) { + done= 0; + /* simple algo - select all faces that have a selected edge + * this intern selects the edge, repeat until nothing is left to do */ + for(efa= em->faces.first; efa; efa= efa->next) { + if ((efa->tmp.l == 0) && (!efa->h)) { + if (is_face_tag(efa)) { + face_tag(efa); + done= 1; + } + } + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if (efa->tmp.l) { + if (sel) { + if (!(efa->f & SELECT)) { + EM_select_face(efa, 1); + change = 1; + } + } else { + if (efa->f & SELECT) { + EM_select_face(efa, 0); + change = 1; + } + } + } + } + + if (!change) + return; + + if (!sel) /* make sure de-selecting faces didnt de-select the verts/edges connected to selected faces, this is common with boundries */ + for(efa= em->faces.first; efa; efa= efa->next) + if (efa->f & SELECT) + EM_select_face(efa, 1); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Linked Delimeted"); + +} + +#undef is_edge_delimit_ok +#undef is_face_tag +#undef face_tag + +void selectconnected_delimit_mesh(EditMesh *em) +{ + + // XXX selectconnected_delimit_mesh__internal(em, 0, ((G.qual & LR_SHIFTKEY)==0)); +} +void selectconnected_delimit_mesh_all(EditMesh *em) +{ + selectconnected_delimit_mesh__internal(em, 1, 1); +} + + +/* swap is 0 or 1, if 1 it hides not selected */ +void hide_mesh(EditMesh *em, int swap) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + int a; + + if(G.obedit==0) return; + + /* hide happens on least dominant select mode, and flushes up, not down! (helps preventing errors in subsurf) */ + /* - vertex hidden, always means edge is hidden too + - edge hidden, always means face is hidden too + - face hidden, only set face hide + - then only flush back down what's absolute hidden + */ + if(em->selectmode & SCE_SELECT_VERTEX) { + for(eve= em->verts.first; eve; eve= eve->next) { + if((eve->f & SELECT)!=swap) { + eve->f &= ~SELECT; + eve->h= 1; + } + } + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->v1->h || eed->v2->h) { + eed->h |= 1; + eed->f &= ~SELECT; + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e1->h & 1 || efa->e2->h & 1 || efa->e3->h & 1 || (efa->e4 && efa->e4->h & 1)) { + efa->h= 1; + efa->f &= ~SELECT; + } + } + } + else if(em->selectmode & SCE_SELECT_EDGE) { + + for(eed= em->edges.first; eed; eed= eed->next) { + if((eed->f & SELECT)!=swap) { + eed->h |= 1; + EM_select_edge(eed, 0); + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->e1->h & 1 || efa->e2->h & 1 || efa->e3->h & 1 || (efa->e4 && efa->e4->h & 1)) { + efa->h= 1; + efa->f &= ~SELECT; + } + } + } + else { + + for(efa= em->faces.first; efa; efa= efa->next) { + if((efa->f & SELECT)!=swap) { + efa->h= 1; + EM_select_face(efa, 0); + } + } + } + + /* flush down, only whats 100% hidden */ + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed= em->edges.first; eed; eed= eed->next) eed->f1= 0; + + if(em->selectmode & SCE_SELECT_FACE) { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h) a= 1; else a= 2; + efa->e1->f1 |= a; + efa->e2->f1 |= a; + efa->e3->f1 |= a; + if(efa->e4) efa->e4->f1 |= a; + /* When edges are not delt with in their own loop, we need to explicitly re-selct select edges that are joined to unselected faces */ + if (swap && (em->selectmode == SCE_SELECT_FACE) && (efa->f & SELECT)) { + EM_select_face(efa, 1); + } + } + } + + if(em->selectmode >= SCE_SELECT_EDGE) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1==1) eed->h |= 1; + if(eed->h & 1) a= 1; else a= 2; + eed->v1->f1 |= a; + eed->v2->f1 |= a; + } + } + + if(em->selectmode >= SCE_SELECT_VERTEX) { + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f1==1) eve->h= 1; + } + } + + G.totedgesel= G.totfacesel= G.totvertsel= 0; +// if(EM_texFaceCheck()) + + // DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Hide"); +} + + +void reveal_mesh(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + if(G.obedit==0) return; + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h) { + eve->h= 0; + eve->f |= SELECT; + } + } + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h & 1) { + eed->h &= ~1; + if(em->selectmode & SCE_SELECT_VERTEX); + else EM_select_edge(eed, 1); + } + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h) { + efa->h= 0; + if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_VERTEX)); + else EM_select_face(efa, 1); + } + } + + EM_fgon_flags(em); /* redo flags and indices for fgons */ + EM_selectmode_flush(em); + +// if (EM_texFaceCheck()) +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Reveal"); +} + +void hide_tface_uv(EditMesh *em, int swap) +{ +#if 0 + /* no space image here */ + EditFace *efa; + MTFace *tface; + + if( is_uv_tface_editing_allowed()==0 ) return; + + /* call the mesh function if we are in mesh sync sel */ + if (G.sima->flag & SI_SYNC_UVSEL) { + hide_mesh(swap); + return; + } + + if(swap) { + for (efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (G.sima->flag & SI_SELACTFACE) { + /* Pretend face mode */ + if (( (efa->v4==NULL && + ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) == (TF_SEL1|TF_SEL2|TF_SEL3) ) || + ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) == (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4) ) == 0) { + + if (em->selectmode == SCE_SELECT_FACE) { + efa->f &= ~SELECT; + /* must re-select after */ + efa->e1->f &= ~SELECT; + efa->e2->f &= ~SELECT; + efa->e3->f &= ~SELECT; + if(efa->e4) efa->e4->f &= ~SELECT; + } else { + EM_select_face(efa, 0); + } + } + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } else if (em->selectmode == SCE_SELECT_FACE) { + if((tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3))==0) { + if(!efa->v4) + EM_select_face(efa, 0); + else if(!(tface->flag & TF_SEL4)) + EM_select_face(efa, 0); + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } else { + /* EM_deselect_flush will deselect the face */ + if((tface->flag & TF_SEL1)==0) efa->v1->f &= ~SELECT; + if((tface->flag & TF_SEL2)==0) efa->v2->f &= ~SELECT; + if((tface->flag & TF_SEL3)==0) efa->v3->f &= ~SELECT; + if((efa->v4) && (tface->flag & TF_SEL4)==0) efa->v4->f &= ~SELECT; + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } + } + } else { + for (efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (G.sima->flag & SI_SELACTFACE) { + if ( (efa->v4==NULL && + ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) == (TF_SEL1|TF_SEL2|TF_SEL3) ) || + ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) == (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4) ) { + + if (em->selectmode == SCE_SELECT_FACE) { + efa->f &= ~SELECT; + /* must re-select after */ + efa->e1->f &= ~SELECT; + efa->e2->f &= ~SELECT; + efa->e3->f &= ~SELECT; + if(efa->e4) efa->e4->f &= ~SELECT; + } else { + EM_select_face(efa, 0); + } + } + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } else if (em->selectmode == SCE_SELECT_FACE) { + if(tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) + EM_select_face(efa, 0); + else if(efa->v4 && tface->flag & TF_SEL4) + EM_select_face(efa, 0); + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } else { + /* EM_deselect_flush will deselect the face */ + if(tface->flag & TF_SEL1) efa->v1->f &= ~SELECT; + if(tface->flag & TF_SEL2) efa->v2->f &= ~SELECT; + if(tface->flag & TF_SEL3) efa->v3->f &= ~SELECT; + if((efa->v4) && tface->flag & TF_SEL4) efa->v4->f &= ~SELECT; + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } + } + } + + + /*deselects too many but ok for now*/ + if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_VERTEX)) { + EM_deselect_flush(em); + } + + if (em->selectmode==SCE_SELECT_FACE) { + /* de-selected all edges from faces that were de-selected. + * now make sure all faces that are selected also have selected edges */ + for (efa= em->faces.first; efa; efa= efa->next) { + if (efa->f & SELECT) { + EM_select_face(efa, 1); + } + } + } + + EM_validate_selections(); + + BIF_undo_push("Hide UV"); + +// XXX object_tface_flags_changed(OBACT, 0); +#endif +} + +void reveal_tface_uv(EditMesh *em) +{ +#if 0 + /* function should move away? */ + EditFace *efa; + MTFace *tface; + + if( is_uv_tface_editing_allowed()==0 ) return; + + /* call the mesh function if we are in mesh sync sel */ + if (G.sima->flag & SI_SYNC_UVSEL) { + reveal_mesh(); + return; + } + + if (G.sima->flag & SI_SELACTFACE) { + if (em->selectmode == SCE_SELECT_FACE) { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->h) && !(efa->f & SELECT)) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + EM_select_face(efa, 1); + tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4; + } + } + } else { + /* enable adjacent faces to have disconnected UV selections if sticky is disabled */ + if (G.sima->sticky == SI_STICKY_DISABLE) { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->h) && !(efa->f & SELECT)) { + /* All verts must be unselected for the face to be selected in the UV view */ + if ((efa->v1->f&SELECT)==0 && (efa->v2->f&SELECT)==0 && (efa->v3->f&SELECT)==0 && (efa->v4==0 || (efa->v4->f&SELECT)==0)) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4; + /* Cant use EM_select_face here because it unselects the verts + * and we cant tell if the face was totally unselected or not */ + /*EM_select_face(efa, 1); + * + * See Loop with EM_select_face() below... */ + efa->f |= SELECT; + } + } + } + } else { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->h) && !(efa->f & SELECT)) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if ((efa->v1->f & SELECT)==0) {tface->flag |= TF_SEL1;} + if ((efa->v2->f & SELECT)==0) {tface->flag |= TF_SEL2;} + if ((efa->v3->f & SELECT)==0) {tface->flag |= TF_SEL3;} + if ((efa->v4 && (efa->v4->f & SELECT)==0)) {tface->flag |= TF_SEL4;} + efa->f |= SELECT; + } + } + } + + /* Select all edges and verts now */ + for (efa= em->faces.first; efa; efa= efa->next) { + /* we only selected the face flags, and didnt changes edges or verts, fix this now */ + if (!(efa->h) && (efa->f & SELECT)) { + EM_select_face(efa, 1); + } + } + EM_select_flush(em); + } + } else if (em->selectmode == SCE_SELECT_FACE) { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->h) && !(efa->f & SELECT)) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + efa->f |= SELECT; + tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4; + } + } + + /* Select all edges and verts now */ + for (efa= em->faces.first; efa; efa= efa->next) { + /* we only selected the face flags, and didnt changes edges or verts, fix this now */ + if (!(efa->h) && (efa->f & SELECT)) { + EM_select_face(efa, 1); + } + } + + } else { + for (efa= em->faces.first; efa; efa= efa->next) { + if (!(efa->h) && !(efa->f & SELECT)) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if ((efa->v1->f & SELECT)==0) {tface->flag |= TF_SEL1;} + if ((efa->v2->f & SELECT)==0) {tface->flag |= TF_SEL2;} + if ((efa->v3->f & SELECT)==0) {tface->flag |= TF_SEL3;} + if ((efa->v4 && (efa->v4->f & SELECT)==0)) {tface->flag |= TF_SEL4;} + efa->f |= SELECT; + } + } + + /* Select all edges and verts now */ + for (efa= em->faces.first; efa; efa= efa->next) { + /* we only selected the face flags, and didnt changes edges or verts, fix this now */ + if (!(efa->h) && (efa->f & SELECT)) { + EM_select_face(efa, 1); + } + } + } + + BIF_undo_push("Reveal UV"); + +// XXX object_tface_flags_changed(OBACT, 0); +#endif +} + +void select_faces_by_numverts(EditMesh *em, int numverts) +{ + EditFace *efa; + + /* Selects trias/qiads or isolated verts, and edges that do not have 2 neighboring + * faces + */ + + /* for loose vertices/edges, we first select all, loop below will deselect */ + if(numverts==5) + EM_set_flag_all(em, SELECT); + else if(em->selectmode!=SCE_SELECT_FACE) { + error("Only works in face selection mode"); + return; + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if (efa->e4) { + EM_select_face(efa, (numverts==4) ); + } + else { + EM_select_face(efa, (numverts==3) ); + } + } + +// if (EM_texFaceCheck()) + + if (numverts==3) + BIF_undo_push("Select Triangles"); + else if (numverts==4) + BIF_undo_push("Select Quads"); + else + BIF_undo_push("Select non-Triangles/Quads"); +} + +void select_sharp_edges(EditMesh *em) +{ + /* Find edges that have exactly two neighboring faces, + * check the angle between those faces, and if angle is + * small enough, select the edge + */ + EditEdge *eed; + EditFace *efa; + EditFace **efa1; + EditFace **efa2; + intptr_t edgecount = 0, i; + static short sharpness = 135; + float fsharpness; + + if(em->selectmode==SCE_SELECT_FACE) { + error("Doesn't work in face selection mode"); + return; + } + +// XXX if(button(&sharpness,0, 180,"Max Angle:")==0) return; + /* if faces are at angle 'sharpness', then the face normals + * are at angle 180.0 - 'sharpness' (convert to radians too) + */ + fsharpness = ((180.0 - sharpness) * M_PI) / 180.0; + + i=0; + /* count edges, use tmp.l */ + eed= em->edges.first; + while(eed) { + edgecount++; + eed->tmp.l = i; + eed= eed->next; + ++i; + } + + /* for each edge, we want a pointer to two adjacent faces */ + efa1 = MEM_callocN(edgecount*sizeof(EditFace *), + "pairs of edit face pointers"); + efa2 = MEM_callocN(edgecount*sizeof(EditFace *), + "pairs of edit face pointers"); + +#define face_table_edge(eed) { \ + i = eed->tmp.l; \ + if (i != -1) { \ + if (efa1[i]) { \ + if (efa2[i]) { \ + /* invalidate, edge has more than two neighbors */ \ + eed->tmp.l = -1; \ + } \ + else { \ + efa2[i] = efa; \ + } \ + } \ + else { \ + efa1[i] = efa; \ + } \ + } \ + } + + /* find the adjacent faces of each edge, we want only two */ + efa= em->faces.first; + while(efa) { + face_table_edge(efa->e1); + face_table_edge(efa->e2); + face_table_edge(efa->e3); + if (efa->e4) { + face_table_edge(efa->e4); + } + efa= efa->next; + } + +#undef face_table_edge + + eed = em->edges.first; + while(eed) { + i = eed->tmp.l; + if (i != -1) { + /* edge has two or less neighboring faces */ + if ( (efa1[i]) && (efa2[i]) ) { + /* edge has exactly two neighboring faces, check angle */ + float angle; + angle = saacos(efa1[i]->n[0]*efa2[i]->n[0] + + efa1[i]->n[1]*efa2[i]->n[1] + + efa1[i]->n[2]*efa2[i]->n[2]); + if (fabs(angle) >= fsharpness) + EM_select_edge(eed, 1); + } + } + + eed= eed->next; + } + + MEM_freeN(efa1); + MEM_freeN(efa2); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Sharp Edges"); +} + +void select_linked_flat_faces(EditMesh *em) +{ + /* Find faces that are linked to selected faces that are + * relatively flat (angle between faces is higher than + * specified angle) + */ + EditEdge *eed; + EditFace *efa; + EditFace **efa1; + EditFace **efa2; + intptr_t edgecount = 0, i, faceselcount=0, faceselcountold=0; + static short sharpness = 135; + float fsharpness; + + if(em->selectmode!=SCE_SELECT_FACE) { + error("Only works in face selection mode"); + return; + } + +// XXX if(button(&sharpness,0, 180,"Min Angle:")==0) return; + /* if faces are at angle 'sharpness', then the face normals + * are at angle 180.0 - 'sharpness' (convert to radians too) + */ + fsharpness = ((180.0 - sharpness) * M_PI) / 180.0; + + i=0; + /* count edges, use tmp.l */ + eed= em->edges.first; + while(eed) { + edgecount++; + eed->tmp.l = i; + eed= eed->next; + ++i; + } + + /* for each edge, we want a pointer to two adjacent faces */ + efa1 = MEM_callocN(edgecount*sizeof(EditFace *), + "pairs of edit face pointers"); + efa2 = MEM_callocN(edgecount*sizeof(EditFace *), + "pairs of edit face pointers"); + +#define face_table_edge(eed) { \ + i = eed->tmp.l; \ + if (i != -1) { \ + if (efa1[i]) { \ + if (efa2[i]) { \ + /* invalidate, edge has more than two neighbors */ \ + eed->tmp.l = -1; \ + } \ + else { \ + efa2[i] = efa; \ + } \ + } \ + else { \ + efa1[i] = efa; \ + } \ + } \ + } + + /* find the adjacent faces of each edge, we want only two */ + efa= em->faces.first; + while(efa) { + face_table_edge(efa->e1); + face_table_edge(efa->e2); + face_table_edge(efa->e3); + if (efa->e4) { + face_table_edge(efa->e4); + } + + /* while were at it, count the selected faces */ + if (efa->f & SELECT) ++faceselcount; + + efa= efa->next; + } + +#undef face_table_edge + + eed= em->edges.first; + while(eed) { + i = eed->tmp.l; + if (i != -1) { + /* edge has two or less neighboring faces */ + if ( (efa1[i]) && (efa2[i]) ) { + /* edge has exactly two neighboring faces, check angle */ + float angle; + angle = saacos(efa1[i]->n[0]*efa2[i]->n[0] + + efa1[i]->n[1]*efa2[i]->n[1] + + efa1[i]->n[2]*efa2[i]->n[2]); + /* invalidate: edge too sharp */ + if (fabs(angle) >= fsharpness) + eed->tmp.l = -1; + } + else { + /* invalidate: less than two neighbors */ + eed->tmp.l = -1; + } + } + + eed= eed->next; + } + +#define select_flat_neighbor(eed) { \ + i = eed->tmp.l; \ + if (i!=-1) { \ + if (! (efa1[i]->f & SELECT) ) { \ + EM_select_face(efa1[i], 1); \ + ++faceselcount; \ + } \ + if (! (efa2[i]->f & SELECT) ) { \ + EM_select_face(efa2[i], 1); \ + ++faceselcount; \ + } \ + } \ + } + + while (faceselcount != faceselcountold) { + faceselcountold = faceselcount; + + efa= em->faces.first; + while(efa) { + if (efa->f & SELECT) { + select_flat_neighbor(efa->e1); + select_flat_neighbor(efa->e2); + select_flat_neighbor(efa->e3); + if (efa->e4) { + select_flat_neighbor(efa->e4); + } + } + efa= efa->next; + } + } + +#undef select_flat_neighbor + + MEM_freeN(efa1); + MEM_freeN(efa2); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Linked Flat Faces"); +} + +void select_non_manifold(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + /* Selects isolated verts, and edges that do not have 2 neighboring + * faces + */ + + if(em->selectmode==SCE_SELECT_FACE) { + error("Doesn't work in face selection mode"); + return; + } + + eve= em->verts.first; + while(eve) { + /* this will count how many edges are connected + * to this vert */ + eve->f1= 0; + eve= eve->next; + } + + eed= em->edges.first; + while(eed) { + /* this will count how many faces are connected to + * this edge */ + eed->f1= 0; + /* increase edge count for verts */ + ++eed->v1->f1; + ++eed->v2->f1; + eed= eed->next; + } + + efa= em->faces.first; + while(efa) { + /* increase face count for edges */ + ++efa->e1->f1; + ++efa->e2->f1; + ++efa->e3->f1; + if (efa->e4) + ++efa->e4->f1; + efa= efa->next; + } + + /* select verts that are attached to an edge that does not + * have 2 neighboring faces */ + eed= em->edges.first; + while(eed) { + if (eed->h==0 && eed->f1 != 2) { + EM_select_edge(eed, 1); + } + eed= eed->next; + } + + /* select isolated verts */ + if(em->selectmode & SCE_SELECT_VERTEX) { + eve= em->verts.first; + while(eve) { + if (eve->f1 == 0) { + if (!eve->h) eve->f |= SELECT; + } + eve= eve->next; + } + } + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Non Manifold"); +} + +void selectswap_mesh(EditMesh *em) /* UI level */ +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + if(em->selectmode & SCE_SELECT_VERTEX) { + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0) { + if(eve->f & SELECT) eve->f &= ~SELECT; + else eve->f|= SELECT; + } + } + } + else if(em->selectmode & SCE_SELECT_EDGE) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + EM_select_edge(eed, !(eed->f & SELECT)); + } + } + } + else { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + EM_select_face(efa, !(efa->f & SELECT)); + } + } + } + + EM_selectmode_flush(em); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Select Swap"); + +} + +void deselectall_mesh(EditMesh *em) /* this toggles!!!, UI level */ +{ + + if( EM_nvertices_selected(em) ) { + EM_clear_flag_all(em, SELECT); + BIF_undo_push("Deselect All"); + } + else { + EM_set_flag_all(em, SELECT); + BIF_undo_push("Select All"); + } + +// if (EM_texFaceCheck()) + +} + +void EM_select_more(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) eve->f1= 1; + else eve->f1 = 0; + } + + /* set f1 flags in vertices to select 'more' */ + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + if (eed->v1->f & SELECT) + eed->v2->f1 = 1; + if (eed->v2->f & SELECT) + eed->v1->f1 = 1; + } + } + + /* new selected edges, but not in facemode */ + if(em->selectmode <= SCE_SELECT_EDGE) { + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + if(eed->v1->f1 && eed->v2->f1) EM_select_edge(eed, 1); + } + } + } + /* new selected faces */ + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + if(efa->v1->f1 && efa->v2->f1 && efa->v3->f1 && (efa->v4==NULL || efa->v4->f1)) + EM_select_face(efa, 1); + } + } +} + +void select_more(EditMesh *em) +{ + EM_select_more(em); + +// if (EM_texFaceCheck(em)) + + BIF_undo_push("Select More"); +} + +void EM_select_less(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + if(em->selectmode <= SCE_SELECT_EDGE) { + /* eed->f1 == 1: edge with a selected and deselected vert */ + + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + if(eed->h==0) { + + if ( !(eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) + eed->f1= 1; + if ( (eed->v1->f & SELECT) && !(eed->v2->f & SELECT) ) + eed->f1= 1; + } + } + + /* deselect edges with flag set */ + for(eed= em->edges.first; eed; eed= eed->next) { + if (eed->h==0 && eed->f1 == 1) { + EM_select_edge(eed, 0); + } + } + EM_deselect_flush(em); + + } + else { + /* deselect faces with 1 or more deselect edges */ + /* eed->f1 == mixed selection edge */ + for(eed= em->edges.first; eed; eed= eed->next) eed->f1= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + if(efa->f & SELECT) { + efa->e1->f1 |= 1; + efa->e2->f1 |= 1; + efa->e3->f1 |= 1; + if(efa->e4) efa->e4->f1 |= 1; + } + else { + efa->e1->f1 |= 2; + efa->e2->f1 |= 2; + efa->e3->f1 |= 2; + if(efa->e4) efa->e4->f1 |= 2; + } + } + } + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + if(efa->e1->f1==3 || efa->e2->f1==3 || efa->e3->f1==3 || (efa->e4 && efa->e4->f1==3)) { + EM_select_face(efa, 0); + } + } + } + EM_selectmode_flush(em); + + } +} + +void select_less(EditMesh *em) +{ + EM_select_less(em); + + BIF_undo_push("Select Less"); + +// if (EM_texFaceCheck(em)) +} + + +void selectrandom_mesh(EditMesh *em) /* randomly selects a user-set % of vertices/edges/faces */ +{ + EditVert *eve; + EditEdge *eed; + EditFace *efa; + static short randfac = 50; + + /* Get the percentage of vertices to randomly select as 'randfac' */ +// XXX if(button(&randfac,0, 100,"Percentage:")==0) return; + + BLI_srand( BLI_rand() ); /* random seed */ + + if(em->selectmode & SCE_SELECT_VERTEX) { + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0) { + if ( (BLI_frand() * 100) < randfac) + eve->f |= SELECT; + } + } + EM_selectmode_flush(em); + BIF_undo_push("Select Random: Vertices"); + } + else if(em->selectmode & SCE_SELECT_EDGE) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + if ( (BLI_frand() * 100) < randfac) + EM_select_edge(eed, 1); + } + } + EM_selectmode_flush(em); + BIF_undo_push("Select Random:Edges"); + } + else { + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0) { + if ( (BLI_frand() * 100) < randfac) + EM_select_face(efa, 1); + } + } + + EM_selectmode_flush(em); + BIF_undo_push("Select Random:Faces"); + } +// if (EM_texFaceCheck()) +} + +void editmesh_select_by_material(EditMesh *em, int index) +{ + EditFace *efa; + + for (efa=em->faces.first; efa; efa= efa->next) { + if (efa->mat_nr==index) { + EM_select_face(efa, 1); + } + } + + EM_selectmode_flush(em); +} + +void editmesh_deselect_by_material(EditMesh *em, int index) +{ + EditFace *efa; + + for (efa=em->faces.first; efa; efa= efa->next) { + if (efa->mat_nr==index) { + EM_select_face(efa, 0); + } + } + + EM_selectmode_flush(em); +} + +void EM_selectmode_menu(EditMesh *em) +{ + int val, ctrl= 0; // XXX + +// if(em->selectmode & SCE_SELECT_VERTEX) pupmenu_set_active(1); +// else if(em->selectmode & SCE_SELECT_EDGE) pupmenu_set_active(2); +// else pupmenu_set_active(3); + +// val= pupmenu("Select Mode%t|Vertices|Edges|Faces"); + + + if(val>0) { + if(val==1){ + em->selectmode= SCE_SELECT_VERTEX; + EM_selectmode_set(em); + BIF_undo_push("Selectmode Set: Vertex"); + } + else if(val==2){ + if(ctrl) EM_convertsel(em, em->selectmode, SCE_SELECT_EDGE); + em->selectmode= SCE_SELECT_EDGE; + EM_selectmode_set(em); + BIF_undo_push("Selectmode Set: Edge"); + } + + else{ + if((ctrl)) EM_convertsel(em, em->selectmode, SCE_SELECT_FACE); + em->selectmode= SCE_SELECT_FACE; + EM_selectmode_set(em); + BIF_undo_push("Selectmode Set: Vertex"); + } + +// if (EM_texFaceCheck()) + } +} + +/* ************************* SEAMS AND EDGES **************** */ + +void editmesh_mark_seam(EditMesh *em, int clear) +{ + EditEdge *eed; + +// XXX if(multires_level1_test()) return; + + /* auto-enable seams drawing */ + if(clear==0) { + if(!(G.f & G_DRAWSEAMS)) { + G.f |= G_DRAWSEAMS; + } + } + + if(clear) { + eed= em->edges.first; + while(eed) { + if((eed->h==0) && (eed->f & SELECT)) { + eed->seam = 0; + } + eed= eed->next; + } + BIF_undo_push("Mark Seam"); + } + else { + eed= em->edges.first; + while(eed) { + if((eed->h==0) && (eed->f & SELECT)) { + eed->seam = 1; + } + eed= eed->next; + } + BIF_undo_push("Clear Seam"); + } + +} + +void editmesh_mark_sharp(EditMesh *em, int set) +{ + EditEdge *eed; + +#if 0 + /* auto-enable sharp edge drawing */ + if(set) { + if(!(G.f & G_DRAWSEAMS)) { + G.f |= G_DRAWSEAMS; + } + } +#endif + +// XXX if(multires_level1_test()) return; + + if(set) { + eed= em->edges.first; + while(eed) { + if(!eed->h && (eed->f & SELECT)) eed->sharp = 1; + eed = eed->next; + } + } else { + eed= em->edges.first; + while(eed) { + if(!eed->h && (eed->f & SELECT)) eed->sharp = 0; + eed = eed->next; + } + } + +} + +void BME_Menu() { + short ret; + ret= pupmenu("BME modeller%t|Select Edges of Vert%x1"); + + switch(ret) + { + case 1: + //BME_edges_of_vert(); + break; + } +} + + + +void Vertex_Menu(EditMesh *em) +{ + short ret; + ret= pupmenu("Vertex Specials%t|Remove Doubles%x1|Merge%x2|Smooth %x3|Select Vertex Path%x4|Blend From Shape%x5|Propagate To All Shapes%x6"); + + switch(ret) + { + case 1: +// XXX notice("Removed %d Vertices", removedoublesflag(1, 0, G.scene->toolsettings->doublimit)); + BIF_undo_push("Remove Doubles"); + break; + case 2: +// XXX mergemenu(em); + break; + case 3: +// XXX vertexsmooth(em); + break; + case 4: +// XXX pathselect(em); + BIF_undo_push("Select Vertex Path"); + break; + case 5: +// XXX shape_copy_select_from(em); + break; + case 6: +// XXX shape_propagate(em); + break; + } + /* some items crashed because this is in the original W menu but not here. should really manage this better */ +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + + +void Edge_Menu(EditMesh *em) +{ + short ret; + + ret= pupmenu("Edge Specials%t|Mark Seam %x1|Clear Seam %x2|Rotate Edge CW%x3|Rotate Edge CCW%x4|Loopcut%x6|Edge Slide%x5|Edge Loop Select%x7|Edge Ring Select%x8|Loop to Region%x9|Region to Loop%x10|Mark Sharp%x11|Clear Sharp%x12"); + + switch(ret) + { + case 1: + editmesh_mark_seam(em, 0); + break; + case 2: + editmesh_mark_seam(em, 1); + break; + case 3: +// edge_rotate_selected(em, 2); + break; + case 4: +// edge_rotate_selected(em, 1); + break; + case 5: +// EdgeSlide(em, 0,0.0); + // BIF_undo_push("EdgeSlide"); + break; + case 6: +// CutEdgeloop(em, 1); + BIF_undo_push("Loopcut New"); + break; + case 7: +// loop_multiselect(em, 0); + break; + case 8: +// loop_multiselect(em, 1); + break; + case 9: +// loop_to_region(em); + break; + case 10: +// region_to_loop(em); + break; + case 11: +// editmesh_mark_sharp(em, 1); + BIF_undo_push("Mark Sharp"); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + break; + case 12: +// editmesh_mark_sharp(em, 0); + BIF_undo_push("Clear Sharp"); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + break; + } + /* some items crashed because this is in the original W menu but not here. should really manage this better */ +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + +void Face_Menu(EditMesh *em) +{ + short ret; + ret= pupmenu( + "Face Specials%t|Flip Normals%x1|Bevel%x2|Shade Smooth%x3|Shade Flat%x4|" + "Triangulate (Ctrl T)%x5|Quads from Triangles (Alt J)%x6|Flip Triangle Edges (Ctrl Shift F)%x7|%l|" + "Face Mode Set%x8|Face Mode Clear%x9|%l|" + "UV Rotate (Shift - CCW)%x10|UV Mirror (Shift - Switch Axis)%x11|" + "Color Rotate (Shift - CCW)%x12|Color Mirror (Shift - Switch Axis)%x13"); + + switch(ret) + { + case 1: +// flip_editnormals(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Flip Normals"); + break; + case 2: +// bevel_menu(em); + break; + case 3: +// mesh_set_smooth_faces(em, 1); + break; + case 4: +// mesh_set_smooth_faces(em, 0); + break; + + case 5: /* Quads to Tris */ +// convert_to_triface(em, 0); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + break; + case 6: /* Tris to Quads */ +// join_triangles(em); + break; + case 7: /* Flip triangle edges */ +// edge_flip(em); + break; + case 8: +// mesh_set_face_flags(em, 1); + break; + case 9: +// mesh_set_face_flags(em, 0); + break; + + /* uv texface options */ + case 10: +// mesh_rotate_uvs(em); + break; + case 11: +// mesh_mirror_uvs(em); + break; + case 12: +// mesh_rotate_colors(em); + break; + case 13: +// mesh_mirror_colors(em); + break; + } + /* some items crashed because this is in the original W menu but not here. should really manage this better */ +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + + +/* **************** NORMALS ************** */ + +void righthandfaces(EditMesh *em, int select) /* makes faces righthand turning */ +{ + EditEdge *eed, *ed1, *ed2, *ed3, *ed4; + EditFace *efa, *startvl; + float maxx, nor[3], cent[3]; + int totsel, found, foundone, direct, turn, tria_nr; + + /* based at a select-connected to witness loose objects */ + + /* count per edge the amount of faces */ + + /* find the ultimate left, front, upper face (not manhattan dist!!) */ + /* also evaluate both triangle cases in quad, since these can be non-flat */ + + /* put normal to the outside, and set the first direction flags in edges */ + + /* then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces */ + /* this is in fact the 'select connected' */ + + /* in case (selected) faces were not done: start over with 'find the ultimate ...' */ + + waitcursor(1); + + eed= em->edges.first; + while(eed) { + eed->f2= 0; /* edge direction */ + eed->f1= 0; /* counter */ + eed= eed->next; + } + + /* count faces and edges */ + totsel= 0; + efa= em->faces.first; + while(efa) { + if(select==0 || (efa->f & SELECT) ) { + efa->f1= 1; + totsel++; + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->v4) efa->e4->f1++; + } + else efa->f1= 0; + + efa= efa->next; + } + + while(totsel>0) { + /* from the outside to the inside */ + + efa= em->faces.first; + startvl= NULL; + maxx= -1.0e10; + tria_nr= 0; + + while(efa) { + if(efa->f1) { + CalcCent3f(cent, efa->v1->co, efa->v2->co, efa->v3->co); + cent[0]= cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2]; + + if(cent[0]>maxx) { + maxx= cent[0]; + startvl= efa; + tria_nr= 0; + } + if(efa->v4) { + CalcCent3f(cent, efa->v1->co, efa->v3->co, efa->v4->co); + cent[0]= cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2]; + + if(cent[0]>maxx) { + maxx= cent[0]; + startvl= efa; + tria_nr= 1; + } + } + } + efa= efa->next; + } + + if (startvl==NULL) + startvl= em->faces.first; + + /* set first face correct: calc normal */ + + if(tria_nr==1) { + CalcNormFloat(startvl->v1->co, startvl->v3->co, startvl->v4->co, nor); + CalcCent3f(cent, startvl->v1->co, startvl->v3->co, startvl->v4->co); + } else { + CalcNormFloat(startvl->v1->co, startvl->v2->co, startvl->v3->co, nor); + CalcCent3f(cent, startvl->v1->co, startvl->v2->co, startvl->v3->co); + } + /* first normal is oriented this way or the other */ + if(select) { + if(select==2) { + if(cent[0]*nor[0]+cent[1]*nor[1]+cent[2]*nor[2] > 0.0) flipface(em, startvl); + } + else { + if(cent[0]*nor[0]+cent[1]*nor[1]+cent[2]*nor[2] < 0.0) flipface(em, startvl); + } + } + else if(cent[0]*nor[0]+cent[1]*nor[1]+cent[2]*nor[2] < 0.0) flipface(em, startvl); + + + eed= startvl->e1; + if(eed->v1==startvl->v1) eed->f2= 1; + else eed->f2= 2; + + eed= startvl->e2; + if(eed->v1==startvl->v2) eed->f2= 1; + else eed->f2= 2; + + eed= startvl->e3; + if(eed->v1==startvl->v3) eed->f2= 1; + else eed->f2= 2; + + eed= startvl->e4; + if(eed) { + if(eed->v1==startvl->v4) eed->f2= 1; + else eed->f2= 2; + } + + startvl->f1= 0; + totsel--; + + /* test normals */ + found= 1; + direct= 1; + while(found) { + found= 0; + if(direct) efa= em->faces.first; + else efa= em->faces.last; + while(efa) { + if(efa->f1) { + turn= 0; + foundone= 0; + + ed1= efa->e1; + ed2= efa->e2; + ed3= efa->e3; + ed4= efa->e4; + + if(ed1->f2) { + if(ed1->v1==efa->v1 && ed1->f2==1) turn= 1; + if(ed1->v2==efa->v1 && ed1->f2==2) turn= 1; + foundone= 1; + } + else if(ed2->f2) { + if(ed2->v1==efa->v2 && ed2->f2==1) turn= 1; + if(ed2->v2==efa->v2 && ed2->f2==2) turn= 1; + foundone= 1; + } + else if(ed3->f2) { + if(ed3->v1==efa->v3 && ed3->f2==1) turn= 1; + if(ed3->v2==efa->v3 && ed3->f2==2) turn= 1; + foundone= 1; + } + else if(ed4 && ed4->f2) { + if(ed4->v1==efa->v4 && ed4->f2==1) turn= 1; + if(ed4->v2==efa->v4 && ed4->f2==2) turn= 1; + foundone= 1; + } + + if(foundone) { + found= 1; + totsel--; + efa->f1= 0; + + if(turn) { + if(ed1->v1==efa->v1) ed1->f2= 2; + else ed1->f2= 1; + if(ed2->v1==efa->v2) ed2->f2= 2; + else ed2->f2= 1; + if(ed3->v1==efa->v3) ed3->f2= 2; + else ed3->f2= 1; + if(ed4) { + if(ed4->v1==efa->v4) ed4->f2= 2; + else ed4->f2= 1; + } + + flipface(em, efa); + + } + else { + if(ed1->v1== efa->v1) ed1->f2= 1; + else ed1->f2= 2; + if(ed2->v1==efa->v2) ed2->f2= 1; + else ed2->f2= 2; + if(ed3->v1==efa->v3) ed3->f2= 1; + else ed3->f2= 2; + if(ed4) { + if(ed4->v1==efa->v4) ed4->f2= 1; + else ed4->f2= 2; + } + } + } + } + if(direct) efa= efa->next; + else efa= efa->prev; + } + direct= 1-direct; + } + } + + recalc_editnormals(em); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + waitcursor(0); +} + + +/* ********** ALIGN WITH VIEW **************** */ + + +static void editmesh_calc_selvert_center(EditMesh *em, float cent_r[3]) +{ + EditVert *eve; + int nsel= 0; + + cent_r[0]= cent_r[1]= cent_r[0]= 0.0; + + for (eve= em->verts.first; eve; eve= eve->next) { + if (eve->f & SELECT) { + cent_r[0]+= eve->co[0]; + cent_r[1]+= eve->co[1]; + cent_r[2]+= eve->co[2]; + nsel++; + } + } + + if (nsel) { + cent_r[0]/= nsel; + cent_r[1]/= nsel; + cent_r[2]/= nsel; + } +} + +static int mface_is_selected(MFace *mf) +{ + return (!(mf->flag & ME_HIDE) && (mf->flag & ME_FACE_SEL)); +} + + /* XXX, code for both these functions should be abstract, + * then unified, then written for other things (like objects, + * which would use same as vertices method), then added + * to interface! Hoera! - zr + */ +void faceselect_align_view_to_selected(View3D *v3d, Mesh *me, int axis) +{ + float norm[3]; + int i, totselected = 0; + + norm[0]= norm[1]= norm[2]= 0.0; + for (i=0; i<me->totface; i++) { + MFace *mf= ((MFace*) me->mface) + i; + + if (mface_is_selected(mf)) { + float *v1, *v2, *v3, fno[3]; + + v1= me->mvert[mf->v1].co; + v2= me->mvert[mf->v2].co; + v3= me->mvert[mf->v3].co; + if (mf->v4) { + float *v4= me->mvert[mf->v4].co; + CalcNormFloat4(v1, v2, v3, v4, fno); + } else { + CalcNormFloat(v1, v2, v3, fno); + } + + norm[0]+= fno[0]; + norm[1]+= fno[1]; + norm[2]+= fno[2]; + + totselected++; + } + } + + if (totselected == 0) + error("No faces selected."); + else + view3d_align_axis_to_vector(v3d, axis, norm); +} + +/* helper for below, to survive non-uniform scaled objects */ +static void face_getnormal_obspace(EditFace *efa, float *fno) +{ + float vec[4][3]; + + VECCOPY(vec[0], efa->v1->co); + Mat4Mul3Vecfl(G.obedit->obmat, vec[0]); + VECCOPY(vec[1], efa->v2->co); + Mat4Mul3Vecfl(G.obedit->obmat, vec[1]); + VECCOPY(vec[2], efa->v3->co); + Mat4Mul3Vecfl(G.obedit->obmat, vec[2]); + if(efa->v4) { + VECCOPY(vec[3], efa->v4->co); + Mat4Mul3Vecfl(G.obedit->obmat, vec[3]); + + CalcNormFloat4(vec[0], vec[1], vec[2], vec[3], fno); + } + else CalcNormFloat(vec[0], vec[1], vec[2], fno); +} + + +void editmesh_align_view_to_selected(EditMesh *em, View3D *v3d, int axis) +{ + int nselverts= EM_nvertices_selected(em); + float norm[3]={0.0, 0.0, 0.0}; /* used for storing the mesh normal */ + + if (nselverts==0) { + error("No faces or vertices selected."); + } + else if (EM_nfaces_selected(em)) { + EditFace *efa; + for (efa= em->faces.first; efa; efa= efa->next) { + if (faceselectedAND(efa, SELECT)) { + float fno[3]; + + face_getnormal_obspace(efa, fno); + norm[0]+= fno[0]; + norm[1]+= fno[1]; + norm[2]+= fno[2]; + } + } + + view3d_align_axis_to_vector(v3d, axis, norm); + } + else if (nselverts>2) { + float cent[3]; + EditVert *eve, *leve= NULL; + + editmesh_calc_selvert_center(em, cent); + for (eve= em->verts.first; eve; eve= eve->next) { + if (eve->f & SELECT) { + if (leve) { + float tno[3]; + CalcNormFloat(cent, leve->co, eve->co, tno); + + /* XXX, fixme, should be flipped intp a + * consistent direction. -zr + */ + norm[0]+= tno[0]; + norm[1]+= tno[1]; + norm[2]+= tno[2]; + } + leve= eve; + } + } + + Mat4Mul3Vecfl(G.obedit->obmat, norm); + view3d_align_axis_to_vector(v3d, axis, norm); + } + else if (nselverts==2) { /* Align view to edge (or 2 verts) */ + EditVert *eve, *leve= NULL; + + for (eve= em->verts.first; eve; eve= eve->next) { + if (eve->f & SELECT) { + if (leve) { + norm[0]= leve->co[0] - eve->co[0]; + norm[1]= leve->co[1] - eve->co[1]; + norm[2]= leve->co[2] - eve->co[2]; + break; /* we know there are only 2 verts so no need to keep looking */ + } + leve= eve; + } + } + Mat4Mul3Vecfl(G.obedit->obmat, norm); + view3d_align_axis_to_vector(v3d, axis, norm); + } + else if (nselverts==1) { /* Align view to vert normal */ + EditVert *eve; + + for (eve= em->verts.first; eve; eve= eve->next) { + if (eve->f & SELECT) { + norm[0]= eve->no[0]; + norm[1]= eve->no[1]; + norm[2]= eve->no[2]; + break; /* we know this is the only selected vert, so no need to keep looking */ + } + } + Mat4Mul3Vecfl(G.obedit->obmat, norm); + view3d_align_axis_to_vector(v3d, axis, norm); + } +} + +/* **************** VERTEX DEFORMS *************** */ + +void vertexsmooth(EditMesh *em) +{ + EditVert *eve, *eve_mir = NULL; + EditEdge *eed; + float *adror, *adr, fac; + float fvec[3]; + int teller=0; + ModifierData *md= G.obedit->modifiers.first; + + if(G.obedit==0) return; + + /* count */ + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) teller++; + eve= eve->next; + } + if(teller==0) return; + + adr=adror= (float *)MEM_callocN(3*sizeof(float *)*teller, "vertsmooth"); + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + eve->tmp.p = (void*)adr; + eve->f1= 0; + eve->f2= 0; + adr+= 3; + } + eve= eve->next; + } + + /* if there is a mirror modifier with clipping, flag the verts that + * are within tolerance of the plane(s) of reflection + */ + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + for (eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + + switch(mmd->axis){ + case 0: + if (fabs(eve->co[0]) < mmd->tolerance) + eve->f2 |= 1; + break; + case 1: + if (fabs(eve->co[1]) < mmd->tolerance) + eve->f2 |= 2; + break; + case 2: + if (fabs(eve->co[2]) < mmd->tolerance) + eve->f2 |= 4; + break; + } + } + } + } + } + } + + eed= em->edges.first; + while(eed) { + if( (eed->v1->f & SELECT) || (eed->v2->f & SELECT) ) { + fvec[0]= (eed->v1->co[0]+eed->v2->co[0])/2.0; + fvec[1]= (eed->v1->co[1]+eed->v2->co[1])/2.0; + fvec[2]= (eed->v1->co[2]+eed->v2->co[2])/2.0; + + if((eed->v1->f & SELECT) && eed->v1->f1<255) { + eed->v1->f1++; + VecAddf(eed->v1->tmp.p, eed->v1->tmp.p, fvec); + } + if((eed->v2->f & SELECT) && eed->v2->f1<255) { + eed->v2->f1++; + VecAddf(eed->v2->tmp.p, eed->v2->tmp.p, fvec); + } + } + eed= eed->next; + } + + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + if(eve->f1) { + +// XXX if (G.scene->toolsettings->editbutflag & B_MESH_X_MIRROR) { +// eve_mir= editmesh_get_x_mirror_vert(G.obedit, em, eve->co); +// } + + adr = eve->tmp.p; + fac= 0.5/(float)eve->f1; + + eve->co[0]= 0.5*eve->co[0]+fac*adr[0]; + eve->co[1]= 0.5*eve->co[1]+fac*adr[1]; + eve->co[2]= 0.5*eve->co[2]+fac*adr[2]; + + + /* clip if needed by mirror modifier */ + if (eve->f2) { + if (eve->f2 & 1) { + eve->co[0]= 0.0f; + } + if (eve->f2 & 2) { + eve->co[1]= 0.0f; + } + if (eve->f2 & 4) { + eve->co[2]= 0.0f; + } + } + + if (eve_mir) { + eve_mir->co[0]=-eve->co[0]; + eve_mir->co[1]= eve->co[1]; + eve_mir->co[2]= eve->co[2]; + } + + } + eve->tmp.p= NULL; + } + eve= eve->next; + } + MEM_freeN(adror); + + recalc_editnormals(em); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Vertex Smooth"); +} + +void vertexnoise(EditMesh *em) +{ + Material *ma; + Tex *tex; + EditVert *eve; + float b2, ofs, vec[3]; + + if(G.obedit==0) return; + + ma= give_current_material(G.obedit, G.obedit->actcol); + if(ma==0 || ma->mtex[0]==0 || ma->mtex[0]->tex==0) { + return; + } + tex= ma->mtex[0]->tex; + + ofs= tex->turbul/200.0; + + eve= (struct EditVert *)em->verts.first; + while(eve) { + if(eve->f & SELECT) { + + if(tex->type==TEX_STUCCI) { + + b2= BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1], eve->co[2]); + if(tex->stype) ofs*=(b2*b2); + vec[0]= 0.2*(b2-BLI_hnoise(tex->noisesize, eve->co[0]+ofs, eve->co[1], eve->co[2])); + vec[1]= 0.2*(b2-BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1]+ofs, eve->co[2])); + vec[2]= 0.2*(b2-BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1], eve->co[2]+ofs)); + + VecAddf(eve->co, eve->co, vec); + } + else { + float tin, dum; + externtex(ma->mtex[0], eve->co, &tin, &dum, &dum, &dum, &dum); + eve->co[2]+= 0.05*tin; + } + } + eve= eve->next; + } + + recalc_editnormals(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Vertex Noise"); +} + +void vertices_to_sphere(Scene *scene, View3D *v3d, EditMesh *em) +{ + EditVert *eve; + float *curs, len, vec[3], cent[3], fac, facm, imat[3][3], bmat[3][3]; + int tot; + short perc=100; + + TEST_EDITMESH + +// XXX if(button(&perc, 1, 100, "Percentage:")==0) return; + + fac= perc/100.0; + facm= 1.0-fac; + + Mat3CpyMat4(bmat, G.obedit->obmat); + Mat3Inv(imat, bmat); + + /* center */ + curs= give_cursor(scene, v3d); + cent[0]= curs[0]-G.obedit->obmat[3][0]; + cent[1]= curs[1]-G.obedit->obmat[3][1]; + cent[2]= curs[2]-G.obedit->obmat[3][2]; + Mat3MulVecfl(imat, cent); + + len= 0.0; + tot= 0; + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + tot++; + len+= VecLenf(cent, eve->co); + } + eve= eve->next; + } + len/=tot; + + if(len==0.0) len= 10.0; + + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + vec[0]= eve->co[0]-cent[0]; + vec[1]= eve->co[1]-cent[1]; + vec[2]= eve->co[2]-cent[2]; + + Normalize(vec); + + eve->co[0]= fac*(cent[0]+vec[0]*len) + facm*eve->co[0]; + eve->co[1]= fac*(cent[1]+vec[1]*len) + facm*eve->co[1]; + eve->co[2]= fac*(cent[2]+vec[2]*len) + facm*eve->co[2]; + + } + eve= eve->next; + } + + recalc_editnormals(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("To Sphere"); +} + diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c new file mode 100644 index 00000000000..e693155706d --- /dev/null +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -0,0 +1,6319 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Johnny Matthews, Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#include "MEM_guardedalloc.h" +#include "PIL_time.h" + +#include "BLO_sys_types.h" // for intptr_t support + +#include "DNA_mesh_types.h" +#include "DNA_material_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_key_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_heap.h" + +#include "BKE_depsgraph.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "WM_types.h" + +#include "BMF_Api.h" + +#include "ED_multires.h" +#include "ED_mesh.h" +#include "ED_view3d.h" + +#include "editmesh.h" + +#include "../space_view3d/view3d_intern.h" // <--- mesh_foreachScreenVert XXX + +/* XXX */ +static void BIF_undo_push() {} +static int extern_qread() {return 0;} +static void waitcursor() {} +static void error() {} +static int pupmenu() {return 0;} +static int okee() {return 0;} +static int qtest() {return 0;} +#define add_numbut(a, b, c, d, e, f, g) {} +static int do_clever_numbuts() {return 0;} +static int snap_sel_to_curs() {return 0;} +static int snap_to_center() {return 0;} + +/* XXX */ + +/* local prototypes ---------------*/ +static void free_tagged_edges_faces(EditMesh *em, EditEdge *eed, EditFace *efa); +int EdgeLoopDelete(EditMesh *em); + +/********* qsort routines *********/ + + +typedef struct xvertsort { + float x; + EditVert *v1; +} xvertsort; + +static int vergxco(const void *v1, const void *v2) +{ + const xvertsort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + +struct facesort { + uintptr_t x; + struct EditFace *efa; +}; + + +static int vergface(const void *v1, const void *v2) +{ + const struct facesort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + + +/* *********************************** */ + +void convert_to_triface(EditMesh *em, int direction) +{ + EditFace *efa, *efan, *next; + float fac; + + if(multires_test()) return; + + efa= em->faces.last; + while(efa) { + next= efa->prev; + if(efa->v4) { + if(efa->f & SELECT) { + /* choose shortest diagonal for split */ + fac= VecLenf(efa->v1->co, efa->v3->co) - VecLenf(efa->v2->co, efa->v4->co); + /* this makes sure exact squares get split different in both cases */ + if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) { + efan= EM_face_from_faces(em, efa, NULL, 0, 1, 2, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(em, efa, NULL, 0, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + else { + efan= EM_face_from_faces(em, efa, NULL, 0, 1, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(em, efa, NULL, 1, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + } + efa= next; + } + + EM_fgon_flags(em); // redo flags and indices for fgons + + BIF_undo_push("Convert Quads to Triangles"); + +} + +int removedoublesflag(EditMesh *em, short flag, short automerge, float limit) /* return amount */ +{ + /* + flag - Test with vert->flags + automerge - Alternative operation, merge unselected into selected. + Used for "Auto Weld" mode. warning. + limit - Quick manhattan distance between verts. + */ + + /* all verts with (flag & 'flag') are being evaluated */ + EditVert *eve, *v1, *nextve; + EditEdge *eed, *e1, *nexted; + EditFace *efa, *nextvl; + xvertsort *sortblock, *sb, *sb1; + struct facesort *vlsortblock, *vsb, *vsb1; + int a, b, test, amount; + + if(multires_test()) return 0; + + + /* flag 128 is cleared, count */ + + /* Normal non weld operation */ + eve= em->verts.first; + amount= 0; + while(eve) { + eve->f &= ~128; + if(eve->h==0 && (automerge || (eve->f & flag))) amount++; + eve= eve->next; + } + if(amount==0) return 0; + + /* allocate memory and qsort */ + sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->h==0 && (automerge || (eve->f & flag))) { + sb->x= eve->co[0]+eve->co[1]+eve->co[2]; + sb->v1= eve; + sb++; + } + eve= eve->next; + } + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + + /* test for doubles */ + sb= sortblock; + if (automerge) { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) { + if(sb1->x - sb->x > limit) break; + + /* when automarge, only allow unselected->selected */ + v1= sb1->v1; + if( (v1->f & 128)==0 ) { + if ((eve->f & flag)==0 && (v1->f & flag)==1) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + eve->f|= 128; + eve->tmp.v = v1; + } + } else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + } else { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount; b++, sb1++) { + /* first test: simpel dist */ + if(sb1->x - sb->x > limit) break; + v1= sb1->v1; + + /* second test: is vertex allowed */ + if( (v1->f & 128)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + MEM_freeN(sortblock); + + if (!automerge) + for(eve = em->verts.first; eve; eve=eve->next) + if((eve->f & flag) && (eve->f & 128)) + EM_data_interp_from_verts(em, eve, eve->tmp.v, eve->tmp.v, 0.5f); + + /* test edges and insert again */ + eed= em->edges.first; + while(eed) { + eed->f2= 0; + eed= eed->next; + } + eed= em->edges.last; + while(eed) { + nexted= eed->prev; + + if(eed->f2==0) { + if( (eed->v1->f & 128) || (eed->v2->f & 128) ) { + remedge(em, eed); + + if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v; + if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v; + e1= addedgelist(em, eed->v1, eed->v2, eed); + + if(e1) { + e1->f2= 1; + if(eed->f & SELECT) + e1->f |= SELECT; + } + if(e1!=eed) free_editedge(em, eed); + } + } + eed= nexted; + } + + /* first count amount of test faces */ + efa= (struct EditFace *)em->faces.first; + amount= 0; + while(efa) { + efa->f1= 0; + if(efa->v1->f & 128) efa->f1= 1; + else if(efa->v2->f & 128) efa->f1= 1; + else if(efa->v3->f & 128) efa->f1= 1; + else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1; + + if(efa->f1==1) amount++; + efa= efa->next; + } + + /* test faces for double vertices, and if needed remove them */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1==1) { + + if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v; + if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v; + if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v; + if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v; + + test= 0; + if(efa->v1==efa->v2) test+=1; + if(efa->v2==efa->v3) test+=2; + if(efa->v3==efa->v1) test+=4; + if(efa->v4==efa->v1) test+=8; + if(efa->v3==efa->v4) test+=16; + if(efa->v2==efa->v4) test+=32; + + if(test) { + if(efa->v4) { + if(test==1 || test==2) { + efa->v2= efa->v3; + efa->v3= efa->v4; + efa->v4= 0; + + EM_data_interp_from_faces(em, efa, NULL, efa, 0, 2, 3, 3); + + test= 0; + } + else if(test==8 || test==16) { + efa->v4= 0; + test= 0; + } + else { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + amount--; + } + } + else { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + amount--; + } + } + + if(test==0) { + /* set edge pointers */ + efa->e1= findedgelist(em, efa->v1, efa->v2); + efa->e2= findedgelist(em, efa->v2, efa->v3); + if(efa->v4==0) { + efa->e3= findedgelist(em, efa->v3, efa->v1); + efa->e4= 0; + } + else { + efa->e3= findedgelist(em, efa->v3, efa->v4); + efa->e4= findedgelist(em, efa->v4, efa->v1); + } + } + } + efa= nextvl; + } + + /* double faces: sort block */ + /* count again, now all selected faces */ + amount= 0; + efa= em->faces.first; + while(efa) { + efa->f1= 0; + if(faceselectedOR(efa, 1)) { + efa->f1= 1; + amount++; + } + efa= efa->next; + } + + if(amount) { + /* double faces: sort block */ + vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub"); + efa= em->faces.first; + while(efa) { + if(efa->f1 & 1) { + if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4); + else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3); + + vsb->efa= efa; + vsb++; + } + efa= efa->next; + } + + qsort(vlsortblock, amount, sizeof(struct facesort), vergface); + + vsb= vlsortblock; + for(a=0; a<amount; a++) { + efa= vsb->efa; + if( (efa->f1 & 128)==0 ) { + vsb1= vsb+1; + + for(b=a+1; b<amount; b++) { + + /* first test: same pointer? */ + if(vsb->x != vsb1->x) break; + + /* second test: is test permitted? */ + efa= vsb1->efa; + if( (efa->f1 & 128)==0 ) { + if( compareface(efa, vsb->efa)) efa->f1 |= 128; + + } + vsb1++; + } + } + vsb++; + } + + MEM_freeN(vlsortblock); + + /* remove double faces */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1 & 128) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + } + + /* remove double vertices */ + a= 0; + eve= (struct EditVert *)em->verts.first; + while(eve) { + nextve= eve->next; + if(automerge || eve->f & flag) { + if(eve->f & 128) { + a++; + BLI_remlink(&em->verts, eve); + free_editvert(em, eve); + } + } + eve= nextve; + } + + return a; /* amount */ +} + +/* called from buttons */ +static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index) +{ + xvertsort *sortblock = userData; + + sortblock[index].x = x; +} + +void xsortvert_flag(ARegion *ar, View3D *v3d, EditMesh *em, int flag) +{ + /* all verts with (flag & 'flag') are sorted */ + EditVert *eve; + xvertsort *sortblock; + ListBase tbase; + int i, amount = BLI_countlist(&em->verts); + + if(multires_test()) return; + + sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort"); + for (i=0,eve=em->verts.first; eve; i++,eve=eve->next) + if(eve->f & flag) + sortblock[i].v1 = eve; + + mesh_foreachScreenVert(ar, v3d, xsortvert_flag__doSetX, sortblock, 0); + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + for (i=0; i<amount; i++) { + eve = sortblock[i].v1; + + if (eve) { + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + } + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); + + BIF_undo_push("Xsort"); + +} + +/* called from buttons */ +void hashvert_flag(EditMesh *em, int flag) +{ + /* switch vertex order using hash table */ + EditVert *eve; + struct xvertsort *sortblock, *sb, onth, *newsort; + ListBase tbase; + int amount, a, b; + + if(multires_test()) return; + + /* count */ + eve= em->verts.first; + amount= 0; + while(eve) { + if(eve->f & flag) amount++; + eve= eve->next; + } + if(amount==0) return; + + /* allocate memory */ + sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->f & flag) { + sb->v1= eve; + sb++; + } + eve= eve->next; + } + + BLI_srand(1); + + sb= sortblock; + for(a=0; a<amount; a++, sb++) { + b= (int)(amount*BLI_drand()); + if(b>=0 && b<amount) { + newsort= sortblock+b; + onth= *sb; + *sb= *newsort; + *newsort= onth; + } + } + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + sb= sortblock; + while(amount--) { + eve= sb->v1; + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + sb++; + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); + + BIF_undo_push("Hash"); + +} + +/* generic extern called extruder */ +void extrude_mesh(EditMesh *em) +{ + float nor[3]= {0.0, 0.0, 0.0}; + short nr, transmode= 0; + + TEST_EDITMESH + if(multires_test()) return; + + if(em->selectmode & SCE_SELECT_VERTEX) { + if(G.totvertsel==0) nr= 0; + else if(G.totvertsel==1) nr= 4; + else if(G.totedgesel==0) nr= 4; + else if(G.totfacesel==0) + nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4"); + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4"); + } + else if(em->selectmode & SCE_SELECT_EDGE) { + if (G.totedgesel==0) nr = 0; + else if (G.totedgesel==1) nr = 3; + else if(G.totfacesel==0) nr = 3; + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3"); + } + else { + if (G.totfacesel == 0) nr = 0; + else if (G.totfacesel == 1) nr = 1; + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2"); + } + + if(nr<1) return; + + if(nr==1) transmode= extrudeflag(em, SELECT, nor); + else if(nr==4) transmode= extrudeflag_verts_indiv(em, SELECT, nor); + else if(nr==3) transmode= extrudeflag_edges_indiv(em, SELECT, nor); + else transmode= extrudeflag_face_indiv(em, SELECT, nor); + + if(transmode==0) { + error("No valid selection"); + } + else { + EM_fgon_flags(em); + + /* We need to force immediate calculation here because + * transform may use derived objects (which are now stale). + * + * This shouldn't be necessary, derived queries should be + * automatically building this data if invalid. Or something. + */ +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + object_handle_update(G.obedit); + + /* individual faces? */ +// BIF_TransformSetUndo("Extrude"); + if(nr==2) { +// initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR); +// Transform(); + } + else { +// initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR); + if(transmode=='n') { + Mat4MulVecfl(G.obedit->obmat, nor); + VecSubf(nor, nor, G.obedit->obmat[3]); +// BIF_setSingleAxisConstraint(nor, "along normal"); + } +// Transform(); + } + } + +} + +void split_mesh(EditMesh *em) +{ + + TEST_EDITMESH + if(multires_test()) return; + + if(okee(" Split ")==0) return; + + waitcursor(1); + + /* make duplicate first */ + adduplicateflag(em, SELECT); + /* old faces have flag 128 set, delete them */ + delfaceflag(em, 128); + recalc_editnormals(em); + + waitcursor(0); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Split"); + +} + +void extrude_repeat_mesh(View3D *v3d, EditMesh *em, int steps, float offs) +{ + float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0}; + short a; + + TEST_EDITMESH + if(multires_test()) return; + + /* dvec */ + dvec[0]= v3d->persinv[2][0]; + dvec[1]= v3d->persinv[2][1]; + dvec[2]= v3d->persinv[2][2]; + Normalize(dvec); + dvec[0]*= offs; + dvec[1]*= offs; + dvec[2]*= offs; + + /* base correction */ + Mat3CpyMat4(bmat, G.obedit->obmat); + Mat3Inv(tmat, bmat); + Mat3MulVecfl(tmat, dvec); + + for(a=0; a<steps; a++) { + extrudeflag(em, SELECT, nor); + translateflag(em, SELECT, dvec); + } + + recalc_editnormals(em); + + EM_fgon_flags(em); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Extrude Repeat"); +} + +void spin_mesh(View3D *v3d, EditMesh *em, int steps, float degr, float *dvec, int mode) +{ + EditVert *eve,*nextve; + float nor[3]= {0.0, 0.0, 0.0}; + float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3]; + float cent[3],bmat[3][3]; + float phi; + short a,ok; + + TEST_EDITMESH + if(multires_test()) return; + + /* imat and center and size */ + Mat3CpyMat4(bmat, G.obedit->obmat); + Mat3Inv(imat,bmat); + + curs= give_cursor(NULL, v3d); + VECCOPY(cent, curs); + cent[0]-= G.obedit->obmat[3][0]; + cent[1]-= G.obedit->obmat[3][1]; + cent[2]-= G.obedit->obmat[3][2]; + Mat3MulVecfl(imat, cent); + + phi= degr*M_PI/360.0; + phi/= steps; +// if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi; + + if(dvec) { + n[0]= v3d->viewinv[1][0]; + n[1]= v3d->viewinv[1][1]; + n[2]= v3d->viewinv[1][2]; + } else { + n[0]= v3d->viewinv[2][0]; + n[1]= v3d->viewinv[2][1]; + n[2]= v3d->viewinv[2][2]; + } + Normalize(n); + + q[0]= (float)cos(phi); + si= (float)sin(phi); + q[1]= n[0]*si; + q[2]= n[1]*si; + q[3]= n[2]*si; + QuatToMat3(q, cmat); + + Mat3MulMat3(tmat,cmat,bmat); + Mat3MulMat3(bmat,imat,tmat); + +// if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1); + ok= 1; + + for(a=0;a<steps;a++) { + if(mode==0) ok= extrudeflag(em, SELECT, nor); + else adduplicateflag(em, SELECT); + if(ok==0) { + error("No valid vertices are selected"); + break; + } + rotateflag(em, SELECT, cent, bmat); + if(dvec) { + Mat3MulVecfl(bmat,dvec); + translateflag(em, SELECT, dvec); + } + } + + if(ok==0) { + /* no vertices or only loose ones selected, remove duplicates */ + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(em, eve); + } + eve= nextve; + } + } + recalc_editnormals(em); + + EM_fgon_flags(em); + + // DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + + if(dvec==NULL) BIF_undo_push("Spin"); +} + +void screw_mesh(EditMesh *em, int steps, int turns) +{ + View3D *v3d= NULL; // XXX + EditVert *eve,*v1=0,*v2=0; + EditEdge *eed; + float dvec[3], nor[3]; + + TEST_EDITMESH + if(multires_test()) return; + + /* clear flags */ + eve= em->verts.first; + while(eve) { + eve->f1= 0; + eve= eve->next; + } + /* edges set flags in verts */ + eed= em->edges.first; + while(eed) { + if(eed->v1->f & SELECT) { + if(eed->v2->f & SELECT) { + /* watch: f1 is a byte */ + if(eed->v1->f1<2) eed->v1->f1++; + if(eed->v2->f1<2) eed->v2->f1++; + } + } + eed= eed->next; + } + /* find two vertices with eve->f1==1, more or less is wrong */ + eve= em->verts.first; + while(eve) { + if(eve->f1==1) { + if(v1==0) v1= eve; + else if(v2==0) v2= eve; + else { + v1=0; + break; + } + } + eve= eve->next; + } + if(v1==0 || v2==0) { + error("You have to select a string of connected vertices too"); + return; + } + + /* calculate dvec */ + dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps); + dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps); + dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps); + + VECCOPY(nor, G.obedit->obmat[2]); + + if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) { + dvec[0]= -dvec[0]; + dvec[1]= -dvec[1]; + dvec[2]= -dvec[2]; + } + + spin_mesh(v3d, em, turns*steps, turns*360, dvec, 0); + + BIF_undo_push("Spin"); +} + + +static void erase_edges(EditMesh *em, ListBase *l) +{ + EditEdge *ed, *nexted; + + ed = (EditEdge *) l->first; + while(ed) { + nexted= ed->next; + if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) { + remedge(em, ed); + free_editedge(em, ed); + } + ed= nexted; + } +} + +static void erase_faces(EditMesh *em, ListBase *l) +{ + EditFace *f, *nextf; + + f = (EditFace *) l->first; + + while(f) { + nextf= f->next; + if( faceselectedOR(f, SELECT) ) { + BLI_remlink(l, f); + free_editface(em, f); + } + f = nextf; + } +} + +static void erase_vertices(EditMesh *em, ListBase *l) +{ + EditVert *v, *nextv; + + v = (EditVert *) l->first; + while(v) { + nextv= v->next; + if(v->f & 1) { + BLI_remlink(l, v); + free_editvert(em, v); + } + v = nextv; + } +} + +void delete_mesh(EditMesh *em) +{ + EditFace *efa, *nextvl; + EditVert *eve,*nextve; + EditEdge *eed,*nexted; + short event; + int count; + char *str="Erase"; + + TEST_EDITMESH + if(multires_test()) return; + + event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6"); + if(event<1) return; + + if(event==10 ) { + str= "Erase Vertices"; + erase_edges(em, &em->edges); + erase_faces(em, &em->faces); + erase_vertices(em, &em->verts); + } + else if(event==6) { + if(!EdgeLoopDelete(em)) + return; + + str= "Erase Edge Loop"; + } + else if(event==4) { + str= "Erase Edges & Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + /* delete only faces with 1 or more edges selected */ + count= 0; + if(efa->e1->f & SELECT) count++; + if(efa->e2->f & SELECT) count++; + if(efa->e3->f & SELECT) count++; + if(efa->e4 && (efa->e4->f & SELECT)) count++; + if(count) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(em, eed); + free_editedge(em, eed); + } + eed= nexted; + } + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->v1->f & SELECT) event++; + if( efa->v2->f & SELECT) event++; + if( efa->v3->f & SELECT) event++; + if(efa->v4 && (efa->v4->f & SELECT)) event++; + + if(event>1) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + } + else if(event==1) { + str= "Erase Edges"; + // faces first + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->e1->f & SELECT) event++; + if( efa->e2->f & SELECT) event++; + if( efa->e3->f & SELECT) event++; + if(efa->e4 && (efa->e4->f & SELECT)) event++; + + if(event) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(em, eed); + free_editedge(em, eed); + } + eed= nexted; + } + /* to remove loose vertices: */ + eed= em->edges.first; + while(eed) { + if( eed->v1->f & SELECT) eed->v1->f-=SELECT; + if( eed->v2->f & SELECT) eed->v2->f-=SELECT; + eed= eed->next; + } + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(em, eve); + } + eve= nextve; + } + + } + else if(event==2) { + str="Erase Faces"; + delfaceflag(em, SELECT); + } + else if(event==3) { + str= "Erase All"; + if(em->verts.first) free_vertlist(em, &em->verts); + if(em->edges.first) free_edgelist(em, &em->edges); + if(em->faces.first) free_facelist(em, &em->faces); + if(em->selected.first) BLI_freelistN(&(em->selected)); + } + else if(event==5) { + str= "Erase Only Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f & SELECT) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + efa= nextvl; + } + } + + EM_fgon_flags(em); // redo flags and indices for fgons + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push(str); +} + + +/* Got this from scanfill.c. You will need to juggle around the + * callbacks for the scanfill.c code a bit for this to work. */ +void fill_mesh(EditMesh *em) +{ + EditVert *eve,*v1; + EditEdge *eed,*e1,*nexted; + EditFace *efa,*nextvl, *efan; + short ok; + + if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return; + if(multires_test()) return; + + waitcursor(1); + + /* copy all selected vertices */ + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + v1= BLI_addfillvert(eve->co); + eve->tmp.v= v1; + v1->tmp.v= eve; + v1->xs= 0; // used for counting edges + } + eve= eve->next; + } + /* copy all selected edges */ + eed= em->edges.first; + while(eed) { + if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) { + e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v); + e1->v1->xs++; + e1->v2->xs++; + } + eed= eed->next; + } + /* from all selected faces: remove vertices and edges to prevent doubles */ + /* all edges add values, faces subtract, + then remove edges with vertices ->xs<2 */ + efa= em->faces.first; + ok= 0; + while(efa) { + nextvl= efa->next; + if( faceselectedAND(efa, 1) ) { + efa->v1->tmp.v->xs--; + efa->v2->tmp.v->xs--; + efa->v3->tmp.v->xs--; + if(efa->v4) efa->v4->tmp.v->xs--; + ok= 1; + + } + efa= nextvl; + } + if(ok) { /* there are faces selected */ + eed= filledgebase.first; + while(eed) { + nexted= eed->next; + if(eed->v1->xs<2 || eed->v2->xs<2) { + BLI_remlink(&filledgebase,eed); + } + eed= nexted; + } + } + + if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) { + efa= fillfacebase.first; + while(efa) { + /* normals default pointing up */ + efan= addfacelist(em, efa->v3->tmp.v, efa->v2->tmp.v, + efa->v1->tmp.v, 0, NULL, NULL); + if(efan) EM_select_face(efan, 1); + efa= efa->next; + } + } + + BLI_end_edgefill(); + + waitcursor(0); + EM_select_flush(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Fill"); +} +/*GB*/ +/*-------------------------------------------------------------------------------*/ +/*--------------------------- Edge Based Subdivide ------------------------------*/ + +#define EDGENEW 2 +#define FACENEW 2 +#define EDGEINNER 4 +#define EDGEOLD 8 + +/*used by faceloop cut to select only edges valid for edge slide*/ +#define DOUBLEOPFILL 16 + +/* calculates offset for co, based on fractal, sphere or smooth settings */ +static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc) +{ + float vec1[3], fac; + + if(beauty & B_SMOOTH) { + /* we calculate an offset vector vec1[], to be added to *co */ + float len, fac, nor[3], nor1[3], nor2[3]; + + VecSubf(nor, edge->v1->co, edge->v2->co); + len= 0.5f*Normalize(nor); + + VECCOPY(nor1, edge->v1->no); + VECCOPY(nor2, edge->v2->no); + + /* cosine angle */ + fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ; + + vec1[0]= fac*nor1[0]; + vec1[1]= fac*nor1[1]; + vec1[2]= fac*nor1[2]; + + /* cosine angle */ + fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ; + + vec1[0]+= fac*nor2[0]; + vec1[1]+= fac*nor2[1]; + vec1[2]+= fac*nor2[2]; + + vec1[0]*= rad*len; + vec1[1]*= rad*len; + vec1[2]*= rad*len; + + co[0] += vec1[0]; + co[1] += vec1[1]; + co[2] += vec1[2]; + } + else { + if(rad > 0.0) { /* subdivide sphere */ + Normalize(co); + co[0]*= rad; + co[1]*= rad; + co[2]*= rad; + } + else if(rad< 0.0) { /* fractal subdivide */ + fac= rad* VecLenf(edge->v1->co, edge->v2->co); + vec1[0]= fac*(float)(0.5-BLI_drand()); + vec1[1]= fac*(float)(0.5-BLI_drand()); + vec1[2]= fac*(float)(0.5-BLI_drand()); + VecAddf(co, co, vec1); + } + + } +} + +/* assumes in the edge is the correct interpolated vertices already */ +/* percent defines the interpolation, rad and beauty are for special options */ +/* results in new vertex with correct coordinate, vertex normal and weight group info */ +static EditVert *subdivide_edge_addvert(EditMesh *em, EditEdge *edge, float rad, int beauty, float percent) +{ + EditVert *ev; + float co[3]; + + co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0]; + co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1]; + co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2]; + + /* offset for smooth or sphere or fractal */ + alter_co(co, edge, rad, beauty, percent); + + /* clip if needed by mirror modifier */ + if (edge->v1->f2) { + if ( edge->v1->f2 & edge->v2->f2 & 1) { + co[0]= 0.0f; + } + if ( edge->v1->f2 & edge->v2->f2 & 2) { + co[1]= 0.0f; + } + if ( edge->v1->f2 & edge->v2->f2 & 4) { + co[2]= 0.0f; + } + } + + ev = addvertlist(em, co, NULL); + + /* vert data (vgroups, ..) */ + EM_data_interp_from_verts(em, edge->v1, edge->v2, ev, percent); + + /* normal */ + ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0]; + ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1]; + ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2]; + Normalize(ev->no); + + return ev; +} + +static void flipvertarray(EditVert** arr, short size) +{ + EditVert *hold; + int i; + + for(i=0; i<size/2; i++) { + hold = arr[i]; + arr[i] = arr[size-i-1]; + arr[size-i-1] = hold; + } +} + +static void facecopy(EditMesh *em, EditFace *source, EditFace *target) +{ + float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co; + float *v4 = source->v4? source->v4->co: NULL; + float w[4][4]; + + CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data); + + target->mat_nr = source->mat_nr; + target->flag = source->flag; + target->h = source->h; + + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v1->co, w[0]); + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v2->co, w[1]); + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v3->co, w[2]); + if (target->v4) + InterpWeightsQ3Dfl(v1, v2, v3, v4, target->v4->co, w[3]); + + CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data); +} + +static void fill_quad_single(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| |---*---| + | / \ | | / \ | + | / \ | | / \ | + |/ \| |/ \| + ------------- --------- + */ + + // Make center face + if(vertsize % 2 == 0) { + hold = addfacelist(em, verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + }else{ + hold = addfacelist(em, verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + } + facecopy(em, efa,hold); + + // Make side faces + for(i=0;i<(vertsize-1)/2;i++) { + hold = addfacelist(em, verts[i],verts[i+1],v[right],NULL,NULL,NULL); + facecopy(em, efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + hold = addfacelist(em, verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL); + facecopy(em, efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e3->f2 |= EDGEINNER; + } + } + } +} + +static void fill_tri_single(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[3], **verts; + EditFace *hold; + short start=0, end, op, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%3; + op = (start+2)%3; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + \ | + \ | + \ | + \ | + \ | + \ | + |op + + where start,end,op are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + 3 2 1 0 + |---*---*---| + \ \ \ | + \ \ \ | + \ \ \ | + \ \ \| + \ \\| + \ | + |op + */ + + // Make side faces + for(i=0;i<(vertsize-1);i++) { + hold = addfacelist(em, verts[i],verts[i+1],v[op],NULL,NULL,NULL); + if(i+1 != vertsize-1) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + facecopy(em, efa,hold); + } +} + +static void fill_quad_double_op(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;} + else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + |---*---*---| + 0 1 2 3 + left right + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| + | | | | + | | | | + | | | | + |---*---*---| + */ + + // Make side faces + for(i=0;i<vertsize-1;i++) { + hold = addfacelist(em, verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL); + if(i < vertsize-2) { + hold->e2->f2 |= EDGEINNER; + hold->e2->f2 |= DOUBLEOPFILL; + } + facecopy(em, efa,hold); + } +} + +static void fill_quad_double_adj_path(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + int ctrl= 0; // XXX + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / | + * / / | + | / / | + * / | + | / | + |-----------| + */ + + // Make outside tris + hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (ctrl) + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + hold = addfacelist(em, verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (ctrl) + hold->e1->f2 |= EDGEINNER; + facecopy(em, efa,hold); + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(em, verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + } + //EM_fgon_flags(em); + +} + +static void fill_quad_double_adj_fan(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*---*---| + | \ \ \ | + *---\ \ \ | + | \ \ \ \| + *---- \ \ \ | + | --- \\\| + |-----------| + */ + + for(i=0;i<=numcuts;i++) { + hold = addfacelist(em, op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL); + hold->e1->f2 |= EDGEINNER; + facecopy(em, efa,hold); + + hold = addfacelist(em, op,verts[0][i],verts[0][i+1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + } +} + +static void fill_quad_double_adj_inner(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2],**inner; + EditFace *hold; + short start=0, start2=0, vertsize,i; + float co[3]; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*-----*---| + | * / | + * \ / | + | * | + | / \ | + * \ | + | \ | + |-------------| + */ + + // Add Inner Vert(s) + inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts"); + + for(i=0;i<numcuts;i++) { + co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ; + co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ; + co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ; + inner[i] = addvertlist(em, co, NULL); + inner[i]->f2 |= EDGEINNER; + + EM_data_interp_from_verts(em, verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f); + } + + // Add Corner Quad + hold = addfacelist(em, verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + // Add Bottom Quads + hold = addfacelist(em, verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + + hold = addfacelist(em, op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Add Fill Quads (if # cuts > 1) + + for(i=0;i<numcuts-1;i++) { + hold = addfacelist(em, inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + + hold = addfacelist(em, inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + facecopy(em, efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + } + + //EM_fgon_flags(em); + + MEM_freeN(inner); +} + +static void fill_tri_double(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[3], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | / + 1* / + | / + 2* / + | / + end2 3| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / + * / / + | / / + * / + | / + | + */ + + // Make outside tri + hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(em, verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + } +} + +static void fill_quad_triple(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[3]={0}; + EditVert *v[4], **verts[3]; + EditFace *hold; + short start=0, start2=0, start3=0, vertsize, i, repeats; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(!(efa->e1->f & SELECT)) { + cedge[0] = efa->e2; + cedge[1] = efa->e3; + cedge[2] = efa->e4; + start = 1;start2 = 2;start3 = 3; + } + if(!(efa->e2->f & SELECT)) { + cedge[0] = efa->e3; + cedge[1] = efa->e4; + cedge[2] = efa->e1; + start = 2;start2 = 3;start3 = 0; + } + if(!(efa->e3->f & SELECT)) { + cedge[0] = efa->e4; + cedge[1] = efa->e1; + cedge[2] = efa->e2; + start = 3;start2 = 0;start3 = 1; + } + if(!(efa->e4->f & SELECT)) { + cedge[0] = efa->e1; + cedge[1] = efa->e2; + cedge[2] = efa->e3; + start = 0;start2 = 1;start3 = 2; + } + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + verts[2] = BLI_ghash_lookup(gh, cedge[2]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + + start2 + 3 2 1 0 + start3 0|---*---*---|3 + | | + 1* *2 + | | + 2* *1 + | | + 3|-----------|0 start + + We will fill this case like this or this depending on even or odd cuts + there are a couple of differences. For odd cuts, there is a tri in the + middle as well as 1 quad at the bottom (not including the extra quads + for odd cuts > 1 + + For even cuts, there is a quad in the middle and 2 quads on the bottom + + they are numbered here for clarity + + 1 outer tris and bottom quads + 2 inner tri or quad + 3 repeating quads + + |---*---*---*---| + |1/ / \ \ 1| + |/ 3 / \ 3 \| + * / 2 \ * + | / \ | + |/ \ | + *---------------* + | 3 | + | | + *---------------* + | | + | 1 | + | | + |---------------| + + |---*---*---*---*---| + | 1/ / \ \ 1| + | / / \ \ | + |/ 3 / \ 3 \| + * / \ * + | / \ | + | / 2 \ | + |/ \| + *-------------------* + | | + | 3 | + | | + *-------------------* + | | + | 1 | + | | + *-------------------* + | | + | 1 | + | | + |-------------------| + + */ + + // Make outside tris + hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + hold = addfacelist(em, verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa,hold); + // Make bottom quad + hold = addfacelist(em, verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + //If it is even cuts, add the 2nd lower quad + if(numcuts % 2 == 0) { + hold = addfacelist(em, verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + // Also Make inner quad + hold = addfacelist(em, verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL); + hold->e3->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e3->h |= EM_FGON; + //} + facecopy(em, efa,hold); + repeats = (numcuts / 2) -1; + } else { + // Make inner tri + hold = addfacelist(em, verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e2->h |= EM_FGON; + //} + facecopy(em, efa,hold); + repeats = ((numcuts+1) / 2)-1; + } + + // cuts for 1 and 2 do not have the repeating quads + if(numcuts < 3) {repeats = 0;} + for(i=0;i<repeats;i++) { + //Make side repeating Quads + hold = addfacelist(em, verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + hold = addfacelist(em, verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL); + hold->e4->f2 |= EDGEINNER; + facecopy(em, efa,hold); + } + // Do repeating bottom quads + for(i=0;i<repeats;i++) { + if(numcuts % 2 == 1) { + hold = addfacelist(em, verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL); + } else { + hold = addfacelist(em, verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL); + } + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa,hold); + } + //EM_fgon_flags(em); +} + +static void fill_quad_quadruple(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[4], ***innerverts; + EditFace *hold; + EditEdge temp; + short vertsize, i, j; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + verts[3] = BLI_ghash_lookup(gh, efa->e4); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);} + if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);} + /* + We should have something like this now + 1 + + 3 2 1 0 + 0|---*---*---|0 + | | + 1* *1 + 2 | | 4 + 2* *2 + | | + 3|---*---*---|3 + 3 2 1 0 + + 3 + // we will fill a 2 dim array of editvert*s to make filling easier + // the innervert order is shown + + 0 0---1---2---3 + | | | | + 1 0---1---2---3 + | | | | + 2 0---1---2---3 + | | | | + 3 0---1---2---3 + + */ + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array"); + } + + // first row is e1 last row is e3 + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[0][(numcuts+1)-i]; + innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts;i++) { + /* we create a fake edge for the next loop */ + temp.v2 = innerverts[i][0] = verts[1][i]; + temp.v1 = innerverts[i][numcuts+1] = verts[3][i]; + + for(j=1;j<=numcuts;j++) { + float percent= (float)j/(float)(numcuts+1); + + innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(em, &temp, rad, beauty, percent); + } + } + // Fill with faces + for(i=0;i<numcuts+1;i++) { + for(j=0;j<numcuts+1;j++) { + hold = addfacelist(em, innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL); + hold->e1->f2 = EDGENEW; + hold->e2->f2 = EDGENEW; + hold->e3->f2 = EDGENEW; + hold->e4->f2 = EDGENEW; + + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(i != numcuts) { hold->e3->f2 |= EDGEINNER; } + if(j != numcuts) { hold->e4->f2 |= EDGEINNER; } + + facecopy(em, efa,hold); + } + } + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +static void fill_tri_triple(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[3], ***innerverts; + short vertsize, i, j; + EditFace *hold; + EditEdge temp; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + 3 + + 3 2 1 0 + 0|---*---*---|3 + | / + 1 1* *2 + | / + 2* *1 2 + | / + 3|/ + 0 + + we will fill a 2 dim array of editvert*s to make filling easier + + 3 + + 0 0---1---2---3---4 + | / | / |/ | / + 1 0---1----2---3 + 1 | / | / | / + 2 0----1---2 2 + | / | / + |/ |/ + 3 0---1 + | / + |/ + 4 0 + + */ + + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array"); + } + //top row is e3 backwards + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts+1;i++) { + //fake edge, first vert is from e1, last is from e2 + temp.v1= innerverts[i][0] = verts[0][i]; + temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i]; + + for(j=1;j<(numcuts+1)-i;j++) { + float percent= (float)j/(float)((numcuts+1)-i); + + innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(em, &temp, rad, beauty, 1-percent); + } + } + + // Now fill the verts with happy little tris :) + for(i=0;i<=numcuts+1;i++) { + for(j=0;j<(numcuts+1)-i;j++) { + //We always do the first tri + hold = addfacelist(em, innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;} + + facecopy(em, efa,hold); + //if there are more to come, we do the 2nd + if(j+1 <= numcuts-i) { + hold = addfacelist(em, innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL); + facecopy(em, efa,hold); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + } + } + } + + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +//Next two fill types are for knife exact only and are provided to allow for knifing through vertices +//This means there is no multicut! +static void fill_quad_doublevert(EditMesh *em, EditFace *efa, int v1, int v2) +{ + EditFace *hold; + /* + Depending on which two vertices have been knifed through (v1 and v2), we + triangulate like the patterns below. + X-------| |-------X + | \ | | / | + | \ | | / | + | \ | | / | + --------X X-------- + */ + + if(v1 == 1 && v2 == 3){ + hold= addfacelist(em, efa->v1, efa->v2, efa->v3, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa, hold); + + hold= addfacelist(em, efa->v1, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(em, efa, hold); + } + else{ + hold= addfacelist(em, efa->v1, efa->v2, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e2->f2 |= EDGEINNER; + facecopy(em, efa, hold); + + hold= addfacelist(em, efa->v2, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa, hold); + } +} + +static void fill_quad_singlevert(EditMesh *em, EditFace *efa, struct GHash *gh) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = 3; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,3);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + +/* + We should have something like this now + + end start + 2 1 0 + |-----*-----| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2 are the indexes of the new verts stored in verts. We fill like + this, depending on whether its vertex 'left' or vertex 'right' thats + been knifed through... + + |---*---| |---*---| + | / | | \ | + | / | | \ | + |/ | | \| + X-------- --------X +*/ + + if(v[left]->f1){ + //triangle is composed of cutvert, end and left + hold = addfacelist(em, verts[1],v[end],v[left],NULL, NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(em, efa, hold); + + //quad is composed of cutvert, left, right and start + hold = addfacelist(em, verts[1],v[left],v[right],v[start], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(em, efa, hold); + } + else if(v[right]->f1){ + //triangle is composed of cutvert, right and start + hold = addfacelist(em, verts[1],v[right],v[start], NULL, NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(em, efa, hold); + //quad is composed of cutvert, end, left, right + hold = addfacelist(em, verts[1],v[end], v[left], v[right], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e4->f2 |= EDGEINNER; + facecopy(em, efa, hold); + } + +} + +// This function takes an example edge, the current point to create and +// the total # of points to create, then creates the point and return the +// editvert pointer to it. +static EditVert *subdivideedgenum(EditMesh *em, EditEdge *edge, int curpoint, int totpoint, float rad, int beauty) +{ + EditVert *ev; + float percent; + + if (beauty & (B_PERCENTSUBD) && totpoint == 1) + //percent=(float)(edge->tmp.l)/32768.0f; + percent= edge->tmp.fp; + else + percent= (float)curpoint/(float)(totpoint+1); + + ev= subdivide_edge_addvert(em, edge, rad, beauty, percent); + ev->f = edge->v1->f; + + return ev; +} + +void esubdivideflag(EditMesh *em, int flag, float rad, int beauty, int numcuts, int seltype) +{ + EditFace *ef; + EditEdge *eed, *cedge, *sort[4]; + EditVert *eve, **templist; + struct GHash *gh; + float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3]; + int i, j, edgecount, touchcount, facetype,hold; + ModifierData *md= G.obedit->modifiers.first; + int ctrl= 0; // XXX + + if(multires_test()) return; + + //Set faces f1 to 0 cause we need it later + for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0; + for(eve=em->verts.first; eve; eve=eve->next) { + if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */ + eve->f1 = 0; + eve->f2 = 0; + } + + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + for (eve= em->verts.first; eve; eve= eve->next) { + eve->f2= 0; + switch(mmd->axis){ + case 0: + if (fabs(eve->co[0]) < mmd->tolerance) + eve->f2 |= 1; + break; + case 1: + if (fabs(eve->co[1]) < mmd->tolerance) + eve->f2 |= 2; + break; + case 2: + if (fabs(eve->co[2]) < mmd->tolerance) + eve->f2 |= 4; + break; + } + } + } + } + } + + //Flush vertex flags upward to the edges + for(eed = em->edges.first;eed;eed = eed->next) { + //if(eed->f & flag && eed->v1->f == eed->v2->f) { + // eed->f |= eed->v1->f; + // } + eed->f2 = 0; + if(eed->f & flag) { + eed->f2 |= EDGEOLD; + } + } + + // We store an array of verts for each edge that is subdivided, + // we put this array as a value in a ghash which is keyed by the EditEdge* + + // Now for beauty subdivide deselect edges based on length + if(beauty & B_BEAUTY) { + for(ef = em->faces.first;ef;ef = ef->next) { + if(!ef->v4) { + continue; + } + if(ef->f & SELECT) { + VECCOPY(v1mat, ef->v1->co); + VECCOPY(v2mat, ef->v2->co); + VECCOPY(v3mat, ef->v3->co); + VECCOPY(v4mat, ef->v4->co); + Mat4Mul3Vecfl(G.obedit->obmat, v1mat); + Mat4Mul3Vecfl(G.obedit->obmat, v2mat); + Mat4Mul3Vecfl(G.obedit->obmat, v3mat); + Mat4Mul3Vecfl(G.obedit->obmat, v4mat); + + length[0] = VecLenf(v1mat, v2mat); + length[1] = VecLenf(v2mat, v3mat); + length[2] = VecLenf(v3mat, v4mat); + length[3] = VecLenf(v4mat, v1mat); + sort[0] = ef->e1; + sort[1] = ef->e2; + sort[2] = ef->e3; + sort[3] = ef->e4; + + + // Beauty Short Edges + if(beauty & B_BEAUTY_SHORT) { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] < length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + + // Beauty Long Edges + else { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] > length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + } + } + } + + gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + // If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut + if(beauty & B_KNIFE) { + for(eed= em->edges.first;eed;eed=eed->next) { + if( eed->tmp.fp == 0 ) { + EM_select_edge(eed,0); + } + } + } + // So for each edge, if it is selected, we allocate an array of size cuts+2 + // so we can have a place for the v1, the new verts and v2 + for(eed=em->edges.first;eed;eed = eed->next) { + if(eed->f & flag) { + templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist"); + templist[0] = eed->v1; + for(i=0;i<numcuts;i++) { + // This function creates the new vert and returns it back + // to the array + templist[i+1] = subdivideedgenum(em, eed, i+1, numcuts, rad, beauty); + //while we are here, we can copy edge info from the original edge + cedge = addedgelist(em, templist[i],templist[i+1],eed); + // Also set the edge f2 to EDGENEW so that we can use this info later + cedge->f2 = EDGENEW; + } + templist[i+1] = eed->v2; + //Do the last edge too + cedge = addedgelist(em, templist[i],templist[i+1],eed); + cedge->f2 = EDGENEW; + // Now that the edge is subdivided, we can put its verts in the ghash + BLI_ghash_insert(gh, eed, templist); + } + } + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + // Now for each face in the mesh we need to figure out How many edges were cut + // and which filling method to use for that face + for(ef = em->faces.first;ef;ef = ef->next) { + edgecount = 0; + facetype = 3; + if(ef->e1->f & flag) {edgecount++;} + if(ef->e2->f & flag) {edgecount++;} + if(ef->e3->f & flag) {edgecount++;} + if(ef->v4) { + facetype = 4; + if(ef->e4->f & flag) {edgecount++;} + } + if(facetype == 4) { + switch(edgecount) { + case 0: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through two opposite verts but no edges*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + if(touchcount == 2){ + if(ef->v1->f1 && ef->v3->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(em, ef, 1, 3); + } + else if(ef->v2->f1 && ef->v4->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(em, ef, 2, 4); + } + } + } + break; + + case 1: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through an edge and one vert*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + + if(touchcount == 1){ + if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) || + (ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) || + (ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) || + (ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){ + + ef->f1 = SELECT; + fill_quad_singlevert(em, ef, gh); + } + else{ + ef->f1 = SELECT; + fill_quad_single(em, ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(em, ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(em, ef, gh, numcuts, seltype); + } + break; + case 2: ef->f1 = SELECT; + // if there are 2, we check if edge 1 and 3 are either both on or off that way + // we can tell if the selected pair is Adjacent or Opposite of each other + if((ef->e1->f & flag && ef->e3->f & flag) || + (ef->e2->f & flag && ef->e4->f & flag)) { + fill_quad_double_op(em, ef, gh, numcuts); + }else{ + switch(0) { // XXX G.scene->toolsettings->cornertype) { + case 0: fill_quad_double_adj_path(em, ef, gh, numcuts); break; + case 1: fill_quad_double_adj_inner(em, ef, gh, numcuts); break; + case 2: fill_quad_double_adj_fan(em, ef, gh, numcuts); break; + } + + } + break; + case 3: ef->f1 = SELECT; + fill_quad_triple(em, ef, gh, numcuts); + break; + case 4: ef->f1 = SELECT; + fill_quad_quadruple(em, ef, gh, numcuts, rad, beauty); + break; + } + } else { + switch(edgecount) { + case 0: break; + case 1: ef->f1 = SELECT; + fill_tri_single(em, ef, gh, numcuts, seltype); + break; + case 2: ef->f1 = SELECT; + fill_tri_double(em, ef, gh, numcuts); + break; + case 3: ef->f1 = SELECT; + fill_tri_triple(em, ef, gh, numcuts, rad, beauty); + break; + } + } + } + + // Delete Old Edges and Faces + for(eed = em->edges.first;eed;eed = eed->next) { + if(BLI_ghash_haskey(gh,eed)) { + eed->f1 = SELECT; + } else { + eed->f1 = 0; + } + } + free_tagged_edges_faces(em, em->edges.first, em->faces.first); + + if(seltype == SUBDIV_SELECT_ORIG && !ctrl) { + /* bugfix: vertex could get flagged as "not-selected" + // solution: clear flags before, not at the same time as setting SELECT flag -dg + */ + for(eed = em->edges.first;eed;eed = eed->next) { + if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) { + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) { + eed->f |= flag; + EM_select_edge(eed,1); + } + } + } else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| ctrl) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGEINNER) { + eed->f |= flag; + EM_select_edge(eed,1); + if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT; + if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT; + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } else if(seltype == SUBDIV_SELECT_LOOPCUT){ + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & DOUBLEOPFILL){ + eed->f |= flag; + EM_select_edge(eed,1); + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } + if(em->selectmode & SCE_SELECT_VERTEX) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f & SELECT) { + eed->v1->f |= SELECT; + eed->v2->f |= SELECT; + } + } + } + + //fix hide flags for edges. First pass, hide edges of hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h){ + ef->e1->h |= 1; + ef->e2->h |= 1; + ef->e3->h |= 1; + if(ef->e4) ef->e4->h |= 1; + } + } + //second pass: unhide edges of visible faces adjacent to hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h == 0){ + ef->e1->h &= ~1; + ef->e2->h &= ~1; + ef->e3->h &= ~1; + if(ef->e4) ef->e4->h &= ~1; + } + } + + // Free the ghash and call MEM_freeN on all the value entries to return + // that memory + BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN); + + EM_selectmode_flush(em); + for(ef=em->faces.first;ef;ef = ef->next) { + if(ef->e4) { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && + (ef->e3->f & SELECT && ef->e4->f & SELECT) ) { + ef->f |= SELECT; + } + } else { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) { + ef->f |= SELECT; + } + } + } + + recalc_editnormals(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + +static int count_selected_edges(EditEdge *ed) +{ + int totedge = 0; + while(ed) { + ed->tmp.p = 0; + if( ed->f & SELECT ) totedge++; + ed= ed->next; + } + return totedge; +} + +/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */ +typedef EditFace *EVPtr; +typedef EVPtr EVPTuple[2]; + +/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces) + sharing one edge. + arguments: selected edge list, face list. + Edges will also be tagged accordingly (see eed->f2) */ + +static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa) +{ + EditEdge *e1, *e2, *e3; + EVPtr *evp; + int i = 0; + + /* run through edges, if selected, set pointer edge-> facearray */ + while(eed) { + eed->f2= 0; + eed->f1= 0; + if( eed->f & SELECT ) { + eed->tmp.p = (EditVert *) (&efaa[i]); + i++; + } + else eed->tmp.p = NULL; + + eed= eed->next; + } + + + /* find edges pointing to 2 faces by procedure: + + - run through faces and their edges, increase + face counter e->f1 for each face + */ + + while(efa) { + efa->f1= 0; + if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */ + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + if(e1->f2<3 && e1->tmp.p) { + if(e1->f2<2) { + evp= (EVPtr *) e1->tmp.p; + evp[(int)e1->f2] = efa; + } + e1->f2+= 1; + } + if(e2->f2<3 && e2->tmp.p) { + if(e2->f2<2) { + evp= (EVPtr *) e2->tmp.p; + evp[(int)e2->f2]= efa; + } + e2->f2+= 1; + } + if(e3->f2<3 && e3->tmp.p) { + if(e3->f2<2) { + evp= (EVPtr *) e3->tmp.p; + evp[(int)e3->f2]= efa; + } + e3->f2+= 1; + } + } + else { + /* set to 3 to make sure these are not flipped or joined */ + efa->e1->f2= 3; + efa->e2->f2= 3; + efa->e3->f2= 3; + if (efa->e4) efa->e4->f2= 3; + } + + efa= efa->next; + } + return i; +} + + +/* returns vertices of two adjacent triangles forming a quad + - can be righthand or lefthand + + 4-----3 + |\ | + | \ 2 | <- efa1 + | \ | + efa-> | 1 \ | + | \| + 1-----2 + +*/ +#define VTEST(face, num, other) \ + (face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3) + +static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex) +{ + if VTEST(efa, 1, efa1) { + *v1= efa->v1; + *v2= efa->v2; + vindex[0]= 0; + vindex[1]= 1; + } + else if VTEST(efa, 2, efa1) { + *v1= efa->v2; + *v2= efa->v3; + vindex[0]= 1; + vindex[1]= 2; + } + else if VTEST(efa, 3, efa1) { + *v1= efa->v3; + *v2= efa->v1; + vindex[0]= 2; + vindex[1]= 0; + } + + if VTEST(efa1, 1, efa) { + *v3= efa1->v1; + *v4= efa1->v2; + vindex[2]= 0; + vindex[3]= 1; + } + else if VTEST(efa1, 2, efa) { + *v3= efa1->v2; + *v4= efa1->v3; + vindex[2]= 1; + vindex[3]= 2; + } + else if VTEST(efa1, 3, efa) { + *v3= efa1->v3; + *v4= efa1->v1; + vindex[2]= 2; + vindex[3]= 0; + } + else + *v3= *v4= NULL; +} + +/* Helper functions for edge/quad edit features*/ +static void untag_edges(EditFace *f) +{ + f->e1->f1 = 0; + f->e2->f1 = 0; + f->e3->f1 = 0; + if (f->e4) f->e4->f1 = 0; +} + +/** remove and free list of tagged edges and faces */ +static void free_tagged_edges_faces(EditMesh *em, EditEdge *eed, EditFace *efa) +{ + EditEdge *nexted; + EditFace *nextvl; + + while(efa) { + nextvl= efa->next; + if(efa->f1) { + BLI_remlink(&em->faces, efa); + free_editface(em, efa); + } + else + /* avoid deleting edges that are still in use */ + untag_edges(efa); + efa= nextvl; + } + + while(eed) { + nexted= eed->next; + if(eed->f1) { + remedge(em, eed); + free_editedge(em, eed); + } + eed= nexted; + } +} + +/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the + edge/face flags, with very mixed results.... */ +void beauty_fill(EditMesh *em) +{ + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditEdge dia1, dia2; + EditFace *efa, *w; + // void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; + int totedge, ok, notbeauty=8, onedone, vindex[4]; + + if(multires_test()) return; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + * - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_set(em); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temp block with face pointers */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill"); + + while (notbeauty) { + notbeauty--; + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + /* there we go */ + onedone= 0; + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + /* f2 is set in collect_quadedges() */ + if(eed->f2==2 && eed->h==0) { + + efaa = (EVPtr *) eed->tmp.p; + + /* none of the faces should be treated before, nor be part of fgon */ + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + + /* test edges */ + if( (v1) > (v3) ) { + dia1.v1= v3; + dia1.v2= v1; + } + else { + dia1.v1= v1; + dia1.v2= v3; + } + + if( (v2) > (v4) ) { + dia2.v1= v4; + dia2.v2= v2; + } + else { + dia2.v1= v2; + dia2.v2= v4; + } + + /* testing rule: + * the area divided by the total edge lengths + */ + + len1= VecLenf(v1->co, v2->co); + len2= VecLenf(v2->co, v3->co); + len3= VecLenf(v3->co, v4->co); + len4= VecLenf(v4->co, v1->co); + len5= VecLenf(v1->co, v3->co); + len6= VecLenf(v2->co, v4->co); + + opp1= AreaT3Dfl(v1->co, v2->co, v3->co); + opp2= AreaT3Dfl(v1->co, v3->co, v4->co); + + fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5); + + opp1= AreaT3Dfl(v2->co, v3->co, v4->co); + opp2= AreaT3Dfl(v2->co, v4->co, v1->co); + + fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6); + + ok= 0; + if(fac1 > fac2) { + if(dia2.v1==eed->v1 && dia2.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + w= EM_face_from_faces(em, efaa[0], efaa[1], + vindex[0], vindex[1], 4+vindex[2], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(em, efaa[0], efaa[1], + vindex[0], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + else if(fac1 < fac2) { + if(dia1.v1==eed->v1 && dia1.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + + w= EM_face_from_faces(em, efaa[0], efaa[1], + vindex[1], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(em, efaa[0], efaa[1], + vindex[0], 4+vindex[1], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + } + } + } + + } + eed= nexted; + } + + free_tagged_edges_faces(em, em->edges.first, em->faces.first); + + if(onedone==0) break; + + EM_selectmode_set(em); // new edges/faces were added + } + + MEM_freeN(efaar); + + EM_select_flush(em); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Beauty Fill"); +} + + +/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */ +static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit) +{ + + /*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/ + /*Note: this is more complicated than it needs to be and should be cleaned up...*/ + float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff, + edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff, + minarea, maxarea, areaA, areaB; + + /*First Test: Normal difference*/ + CalcNormFloat(v1->co, v2->co, v3->co, noA1); + CalcNormFloat(v1->co, v3->co, v4->co, noA2); + + if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0; + else normalADiff = VecAngle2(noA1, noA2); + //if(!normalADiff) normalADiff = 179; + CalcNormFloat(v2->co, v3->co, v4->co, noB1); + CalcNormFloat(v4->co, v1->co, v2->co, noB2); + + if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0; + else normalBDiff = VecAngle2(noB1, noB2); + //if(!normalBDiff) normalBDiff = 179; + + measure += (normalADiff/360) + (normalBDiff/360); + if(measure > limit) return measure; + + /*Second test: Colinearity*/ + VecSubf(edgeVec1, v1->co, v2->co); + VecSubf(edgeVec2, v2->co, v3->co); + VecSubf(edgeVec3, v3->co, v4->co); + VecSubf(edgeVec4, v4->co, v1->co); + + diff = 0.0; + + diff = ( + fabs(VecAngle2(edgeVec1, edgeVec2) - 90) + + fabs(VecAngle2(edgeVec2, edgeVec3) - 90) + + fabs(VecAngle2(edgeVec3, edgeVec4) - 90) + + fabs(VecAngle2(edgeVec4, edgeVec1) - 90)) / 360; + if(!diff) return 0.0; + + measure += diff; + if(measure > limit) return measure; + + /*Third test: Concavity*/ + areaA = AreaT3Dfl(v1->co, v2->co, v3->co) + AreaT3Dfl(v1->co, v3->co, v4->co); + areaB = AreaT3Dfl(v2->co, v3->co, v4->co) + AreaT3Dfl(v4->co, v1->co, v2->co); + + if(areaA <= areaB) minarea = areaA; + else minarea = areaB; + + if(areaA >= areaB) maxarea = areaA; + else maxarea = areaB; + + if(!maxarea) measure += 1; + else measure += (1 - (minarea / maxarea)); + + return measure; +} + +#define T2QUV_LIMIT 0.005 +#define T2QCOL_LIMIT 3 +static int compareFaceAttribs(EditMesh *em, EditFace *f1, EditFace *f2, EditEdge *eed) +{ + /*Test to see if the per-face attributes for the joining edge match within limit*/ + MTFace *tf1, *tf2; + unsigned int *col1, *col2; + short i,attrok=0, flag = 0, /* XXX G.scene->toolsettings->editbutflag,*/ fe1[2], fe2[2]; + + tf1 = CustomData_em_get(&em->fdata, f1->data, CD_MTFACE); + tf2 = CustomData_em_get(&em->fdata, f2->data, CD_MTFACE); + + col1 = CustomData_em_get(&em->fdata, f1->data, CD_MCOL); + col2 = CustomData_em_get(&em->fdata, f2->data, CD_MCOL); + + /*store indices for faceedges*/ + f1->v1->f1 = 0; + f1->v2->f1 = 1; + f1->v3->f1 = 2; + + fe1[0] = eed->v1->f1; + fe1[1] = eed->v2->f1; + + f2->v1->f1 = 0; + f2->v2->f1 = 1; + f2->v3->f1 = 2; + + fe2[0] = eed->v1->f1; + fe2[1] = eed->v2->f1; + + /*compare faceedges for each face attribute. Additional per face attributes can be added later*/ + /*do UVs*/ + if(flag & B_JOINTRIA_UV){ + + if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV; + else if(tf1->tpage != tf2->tpage); /*do nothing*/ + else{ + for(i = 0; i < 2; i++){ + if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] && + tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV; + } + } + } + + /*do VCOLs*/ + if(flag & B_JOINTRIA_VCOL){ + if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL; + else{ + char *f1vcol, *f2vcol; + for(i = 0; i < 2; i++){ + f1vcol = (char *)&(col1[fe1[i]]); + f2vcol = (char *)&(col2[fe2[i]]); + + /*compare f1vcol with f2vcol*/ + if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] && + f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] && + f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL; + } + } + } + + if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1; + return 0; +} + +static int fplcmp(const void *v1, const void *v2) +{ + const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2); + + if( e1->crease > e2->crease) return 1; + else if( e1->crease < e2->crease) return -1; + + return 0; +} + +/*Bitflags for edges.*/ +#define T2QDELETE 1 +#define T2QCOMPLEX 2 +#define T2QJOIN 4 +void join_triangles(EditMesh *em) +{ + EditVert *v1, *v2, *v3, *v4, *eve; + EditEdge *eed, **edsortblock = NULL, **edb = NULL; + EditFace *efa; + EVPTuple *efaar = NULL; + EVPtr *efaa = NULL; + float *creases = NULL; + float measure; /*Used to set tolerance*/ + float limit = 0.0f; // XXX G.scene->toolsettings->jointrilimit; + int i, ok, totedge=0, totseledge=0, complexedges, vindex[4]; + + /*test for multi-resolution data*/ + if(multires_test()) return; + + /*if we take a long time on very dense meshes we want waitcursor to display*/ + waitcursor(1); + + totseledge = count_selected_edges(em->edges.first); + if(totseledge==0) return; + + /*abusing crease value to store weights for edge pairs. Nasty*/ + for(eed=em->edges.first; eed; eed=eed->next) totedge++; + if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array"); + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){ + creases[i] = eed->crease; + eed->crease = 0.0; + } + + /*clear temp flags*/ + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0; + for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0; + for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0; + + /*For every selected 2 manifold edge, create pointers to its two faces.*/ + efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad"); + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + complexedges = 0; + + if(ok){ + + + /*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/ + for(eed=em->edges.first; eed; eed=eed->next){ + /* eed->f2 is 2 only if this edge is part of exactly two + triangles, and both are selected, and it has EVPTuple assigned */ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + efaa[0]->tmp.l++; + efaa[1]->tmp.l++; + } + } + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4){ + /*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/ + if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){ + if( convex(v1->co, v2->co, v3->co, v4->co) ){ + eed->f1 |= T2QJOIN; + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + } + } + else{ + + /* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved. + Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria: + + 1: the two faces do not share the same material + 2: the edge joining the two faces is marked as sharp. + 3: the two faces UV's do not make a good match + 4: the two faces Vertex colors do not make a good match + + If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function. + This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user + the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the + same faces in the current pair later. + + This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of + the python scripts bundled with Blender releases. + */ + +// XXX if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/ +// else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/ +// else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) && + compareFaceAttribs(em, efaa[0], efaa[1], eed); // XXX == 0); /*do nothing*/ +// else{ + measure = measure_facepair(v1, v2, v3, v4, limit); + if(measure < limit){ + complexedges++; + eed->f1 |= T2QCOMPLEX; + eed->crease = measure; /*we dont mark edges for join yet*/ + } +// } + } + } + } + } + + /*Quicksort the complex edges according to their weighting*/ + if(complexedges){ + edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array"); + for(eed = em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QCOMPLEX){ + *edb = eed; + edb++; + } + } + qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp); + /*now go through and mark the edges who get the highest weighting*/ + for(edb=edsortblock, i=0; i < complexedges; edb++, i++){ + efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/ + if( !efaa[0]->f1 && !efaa[1]->f1){ + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + (*edb)->f1 |= T2QJOIN; + } + } + } + + /*finally go through all edges marked for join (simple and complex) and create new faces*/ + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QJOIN){ + efaa= (EVPtr *)eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if((v1 && v2 && v3 && v4) && (exist_face(em, v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/ + /*flag for delete*/ + eed->f1 |= T2QDELETE; + /*create new quad and select*/ + efa = EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]); + EM_select_face(efa,1); + } + else{ + efaa[0]->f1 = 0; + efaa[1]->f1 = 0; + } + } + } + } + + /*free data and cleanup*/ + if(creases){ + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i]; + MEM_freeN(creases); + } + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QDELETE) eed->f1 = 1; + else eed->f1 = 0; + } + free_tagged_edges_faces(em, em->edges.first, em->faces.first); + if(efaar) MEM_freeN(efaar); + if(edsortblock) MEM_freeN(edsortblock); + + EM_selectmode_flush(em); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Convert Triangles to Quads"); +} +/* ******************** END TRIANGLE TO QUAD ************************************* */ + +#define FACE_MARKCLEAR(f) (f->f1 = 1) + +/* quick hack, basically a copy of beauty_fill */ +void edge_flip(EditMesh *em) +{ + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditFace *efa, *w; + //void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + int totedge, ok, vindex[4]; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_flush(em); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temporary array for : edge -> face[1], face[2] */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip"); + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + if(eed->f2==2) { /* points to 2 faces */ + + efaa= (EVPtr *) eed->tmp.p; + + /* don't do it if flagged */ + + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + +/* + 4-----3 4-----3 + |\ | | /| + | \ 1 | | 1 / | + | \ | -> | / | + | 0 \ | | / 0 | + | \| |/ | + 1-----2 1-----2 +*/ + /* make new faces */ + if (v1 && v2 && v3) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + if(exist_face(em, v1, v2, v3, v4)==0) { + /* outch this may break seams */ + w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], + vindex[1], 4+vindex[2], -1); + + EM_select_face(w, 1); + + /* outch this may break seams */ + w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], + 4+vindex[2], 4+vindex[3], -1); + + EM_select_face(w, 1); + } + /* tag as to-be-removed */ + FACE_MARKCLEAR(efaa[1]); + FACE_MARKCLEAR(efaa[0]); + eed->f1 = 1; + + } /* endif test convex */ + } + } + } + eed= nexted; + } + + /* clear tagged edges and faces: */ + free_tagged_edges_faces(em, em->edges.first, em->faces.first); + + MEM_freeN(efaar); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Flip Triangle Edges"); + +} + +static void edge_rotate(EditMesh *em, EditEdge *eed,int dir) +{ + EditVert **verts[2]; + EditFace *face[2], *efa, *newFace[2]; + EditEdge **edges[2], **hiddenedges, *srchedge; + int facecount, p1, p2, p3, p4, fac1, fac2, i, j; + int numhidden, numshared, p[2][4]; + + /* check to make sure that the edge is only part of 2 faces */ + facecount = 0; + for(efa = em->faces.first;efa;efa = efa->next) { + if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) { + if(facecount >= 2) { + /* more than two faces with this edge */ + return; + } + else { + face[facecount] = efa; + facecount++; + } + } + } + + if(facecount < 2) + return; + + /* how many edges does each face have */ + if(face[0]->e4) fac1= 4; + else fac1= 3; + + if(face[1]->e4) fac2= 4; + else fac2= 3; + + /* make a handy array for verts and edges */ + verts[0]= &face[0]->v1; + edges[0]= &face[0]->e1; + verts[1]= &face[1]->v1; + edges[1]= &face[1]->e1; + + /* we don't want to rotate edges between faces that share more than one edge */ + numshared= 0; + for(i=0; i<fac1; i++) + for(j=0; j<fac2; j++) + if (edges[0][i] == edges[1][j]) + numshared++; + + if(numshared > 1) + return; + + /* coplaner faces only please */ + if(Inpf(face[0]->n,face[1]->n) <= 0.000001) + return; + + /* we want to construct an array of vertex indicis in both faces, starting at + the last vertex of the edge being rotated. + - first we find the two vertices that lie on the rotating edge + - then we make sure they are ordered according to the face vertex order + - and then we construct the array */ + p1= p2= p3= p4= 0; + + for(i=0; i<4; i++) { + if(eed->v1 == verts[0][i]) p1 = i; + if(eed->v2 == verts[0][i]) p2 = i; + if(eed->v1 == verts[1][i]) p3 = i; + if(eed->v2 == verts[1][i]) p4 = i; + } + + if((p1+1)%fac1 == p2) + SWAP(int, p1, p2); + if((p3+1)%fac2 == p4) + SWAP(int, p3, p4); + + for (i = 0; i < 4; i++) { + p[0][i]= (p1 + i)%fac1; + p[1][i]= (p3 + i)%fac2; + } + + /* create an Array of the Edges who have h set prior to rotate */ + numhidden = 0; + for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next) + if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT))) + numhidden++; + + hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts"); + if(!hiddenedges) { + error("Malloc Was not happy!"); + return; + } + + numhidden = 0; + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT)) + hiddenedges[numhidden++] = srchedge; + + /* create the 2 new faces */ + if(fac1 == 3 && fac2 == 3) { + /* no need of reverse setup */ + + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } + else if(fac1 == 4 && fac2 == 3) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]); + newFace[1]= EM_face_from_faces(em, face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + } + else if(fac1 == 3 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + + } + else if(fac1 == 4 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]); + newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + } + else + return; /* This should never happen */ + + if(dir == 1 || (fac1 == 3 && fac2 == 3)) { + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + + /* copy old edge's flags to new center edge*/ + for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) { + if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) { + srchedge->f = eed->f; + srchedge->h = eed->h; + srchedge->dir = eed->dir; + srchedge->seam = eed->seam; + srchedge->crease = eed->crease; + srchedge->bweight = eed->bweight; + } + } + + /* resetting hidden flag */ + for(numhidden--; numhidden>=0; numhidden--) + hiddenedges[numhidden]->h= 1; + + /* check for orhphan edges */ + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + srchedge->f1= -1; + + /* cleanup */ + MEM_freeN(hiddenedges); + + /* get rid of the old edge and faces*/ + remedge(em, eed); + free_editedge(em, eed); + BLI_remlink(&em->faces, face[0]); + free_editface(em, face[0]); + BLI_remlink(&em->faces, face[1]); + free_editface(em, face[1]); +} + +/* only accepts 1 selected edge, or 2 selected faces */ +void edge_rotate_selected(EditMesh *em, int dir) +{ + EditEdge *eed; + EditFace *efa; + short edgeCount = 0; + + /*clear new flag for new edges, count selected edges */ + for(eed= em->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2 &= ~2; + if(eed->f & SELECT) edgeCount++; + } + + if(edgeCount>1) { + /* more selected edges, check faces */ + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + edgeCount= 0; + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1==2) edgeCount++; + } + if(edgeCount==1) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f1==2) { + edge_rotate(em, eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + } + else if(edgeCount==1) { + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f & SELECT) { + EM_select_edge(eed, 0); + edge_rotate(em, eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + + + /* flush selected vertices (again) to edges/faces */ + EM_select_flush(em); + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Rotate Edge"); +} + +/******************* BEVEL CODE STARTS HERE ********************/ + + /* XXX old bevel not ported yet */ + +void bevel_menu(EditMesh *em) +{ + BME_Mesh *bm; + BME_TransData_Head *td; +// TransInfo *t; + int options, res, gbm_free = 0; + +// t = BIF_GetTransInfo(); + if (!G.editBMesh) { + G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh"); + gbm_free = 1; + } + + G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT; + G.editBMesh->res = 1; + + while(G.editBMesh->options & BME_BEVEL_RUNNING) { + options = G.editBMesh->options; + res = G.editBMesh->res; + bm = BME_editmesh_to_bmesh(em); + BIF_undo_push("Pre-Bevel"); + free_editMesh(em); + BME_bevel(bm,0.1f,res,options,0,0,&td); + BME_bmesh_to_editmesh(bm, td); + EM_selectmode_flush(em); + G.editBMesh->bm = bm; + G.editBMesh->td = td; +// initTransform(TFM_BEVEL,CTX_BMESH); +// Transform(); + BME_free_transdata(td); + BME_free_mesh(bm); +// if (t->state != TRANS_CONFIRM) { +// BIF_undo(); +// } + if (options == G.editBMesh->options) { + G.editBMesh->options &= ~BME_BEVEL_RUNNING; + } + } + + if (gbm_free) { + MEM_freeN(G.editBMesh); + G.editBMesh = NULL; + } +} + + +/* *********** END BEVEL *********/ + +/* this utility function checks to see if 2 edit edges share a face, +returns 1 if they do +returns 0 if they do not, or if the function is passed the same edge 2 times +*/ +short sharesFace(EditMesh *em, EditEdge* e1, EditEdge* e2) +{ + EditFace *search=NULL; + + search = em->faces.first; + if (e1 == e2){ + return 0 ; + } + while(search){ + if( + ((search->e1 == e1 || search->e2 == e1) || (search->e3 == e1 || search->e4 == e1)) && + ((search->e1 == e2 || search->e2 == e2) || (search->e3 == e2 || search->e4 == e2)) + ) { + return 1; + } + search = search->next; + } + return 0; +} + + +typedef struct SlideUv { + float origuv[2]; + float *uv_up, *uv_down; + //float *fuv[4]; + LinkNode *fuv_list; +} SlideUv; + +typedef struct SlideVert { + EditEdge *up,*down; + EditVert origvert; +} SlideVert; + +int EdgeSlide(EditMesh *em, short immediate, float imperc) +{ +// NumInput num; + EditFace *efa; + EditEdge *eed,*first=NULL,*last=NULL, *temp = NULL; + EditVert *ev, *nearest; + LinkNode *edgelist = NULL, *vertlist=NULL, *look; + GHash *vertgh; + + SlideVert *tempsv; + float perc = 0, percp = 0,vertdist; // XXX, projectMat[4][4], viewMat[4][4]; + float shiftlabda= 0.0f,len = 0.0f; + int i = 0,j, numsel, numadded=0, timesthrough = 0, vertsel=0, prop=1, cancel = 0,flip=0; + int wasshift = 0; + + /* UV correction vars */ + GHash **uvarray= NULL; + int uvlay_tot= CustomData_number_of_layers(&em->fdata, CD_MTFACE); + int uvlay_idx; + SlideUv *slideuvs=NULL, *suv=NULL, *suv_last=NULL; + float uv_tmp[2]; + LinkNode *fuv_link; + + short event, draw=1; + short mval[2], mvalo[2]; + char str[128]; + float labda = 0.0f; + +// initNumInput(&num); + +// view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat); + + mvalo[0] = -1; mvalo[1] = -1; + numsel =0; + + // Get number of selected edges and clear some flags + for(eed=em->edges.first;eed;eed=eed->next) { + eed->f1 = 0; + eed->f2 = 0; + if(eed->f & SELECT) numsel++; + } + + for(ev=em->verts.first;ev;ev=ev->next) { + ev->f1 = 0; + } + + //Make sure each edge only has 2 faces + // make sure loop doesn't cross face + for(efa=em->faces.first;efa;efa=efa->next) { + int ct = 0; + if(efa->e1->f & SELECT) { + ct++; + efa->e1->f1++; + if(efa->e1->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e2->f & SELECT) { + ct++; + efa->e2->f1++; + if(efa->e2->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e3->f & SELECT) { + ct++; + efa->e3->f1++; + if(efa->e3->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + if(efa->e4 && efa->e4->f & SELECT) { + ct++; + efa->e4->f1++; + if(efa->e4->f1 > 2) { + error("3+ face edge"); + return 0; + } + } + // Make sure loop is not 2 edges of same face + if(ct > 1) { + error("loop crosses itself"); + return 0; + } + } + // Get # of selected verts + for(ev=em->verts.first;ev;ev=ev->next) { + if(ev->f & SELECT) vertsel++; + } + + // Test for multiple segments + if(vertsel > numsel+1) { + error("Was not a single edge loop"); + return 0; + } + + // Get the edgeloop in order - mark f1 with SELECT once added + for(eed=em->edges.first;eed;eed=eed->next) { + if((eed->f & SELECT) && !(eed->f1 & SELECT)) { + // If this is the first edge added, just put it in + if(!edgelist) { + BLI_linklist_prepend(&edgelist,eed); + numadded++; + first = eed; + last = eed; + eed->f1 = SELECT; + } else { + if(editedge_getSharedVert(eed, last)) { + BLI_linklist_append(&edgelist,eed); + eed->f1 = SELECT; + numadded++; + last = eed; + } else if(editedge_getSharedVert(eed, first)) { + BLI_linklist_prepend(&edgelist,eed); + eed->f1 = SELECT; + numadded++; + first = eed; + } + } + } + if(eed->next == NULL && numadded != numsel) { + eed=em->edges.first; + timesthrough++; + } + + // It looks like there was an unexpected case - Hopefully should not happen + if(timesthrough >= numsel*2) { + BLI_linklist_free(edgelist,NULL); + error("could not order loop"); + return 0; + } + } + + // Put the verts in order in a linklist + look = edgelist; + while(look) { + eed = look->link; + if(!vertlist) { + if(look->next) { + temp = look->next->link; + + //This is the first entry takes care of extra vert + if(eed->v1 != temp->v1 && eed->v1 != temp->v2) { + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } else { + BLI_linklist_append(&vertlist,eed->v2); + eed->v2->f1 = 1; + } + } else { + //This is the case that we only have 1 edge + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } + } + // for all the entries + if(eed->v1->f1 != 1) { + BLI_linklist_append(&vertlist,eed->v1); + eed->v1->f1 = 1; + } else if(eed->v2->f1 != 1) { + BLI_linklist_append(&vertlist,eed->v2); + eed->v2->f1 = 1; + } + look = look->next; + } + + // populate the SlideVerts + + vertgh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + look = vertlist; + while(look) { + i=0; + j=0; + ev = look->link; + tempsv = (struct SlideVert*)MEM_mallocN(sizeof(struct SlideVert),"SlideVert"); + tempsv->up = NULL; + tempsv->down = NULL; + tempsv->origvert.co[0] = ev->co[0]; + tempsv->origvert.co[1] = ev->co[1]; + tempsv->origvert.co[2] = ev->co[2]; + tempsv->origvert.no[0] = ev->no[0]; + tempsv->origvert.no[1] = ev->no[1]; + tempsv->origvert.no[2] = ev->no[2]; + // i is total edges that vert is on + // j is total selected edges that vert is on + + for(eed=em->edges.first;eed;eed=eed->next) { + if(eed->v1 == ev || eed->v2 == ev) { + i++; + if(eed->f & SELECT) { + j++; + } + } + } + // If the vert is in the middle of an edge loop, it touches 2 selected edges and 2 unselected edges + if(i == 4 && j == 2) { + for(eed=em->edges.first;eed;eed=eed->next) { + if(editedge_containsVert(eed, ev)) { + if(!(eed->f & SELECT)) { + if(!tempsv->up) { + tempsv->up = eed; + } else if (!(tempsv->down)) { + tempsv->down = eed; + } + } + } + } + } + // If it is on the end of the loop, it touches 1 selected and as least 2 more unselected + if(i >= 3 && j == 1) { + for(eed=em->edges.first;eed;eed=eed->next) { + if(editedge_containsVert(eed, ev) && eed->f & SELECT) { + for(efa = em->faces.first;efa;efa=efa->next) { + if(editface_containsEdge(efa, eed)) { + if(editedge_containsVert(efa->e1, ev) && efa->e1 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e1; + } else if (!(tempsv->down)) { + tempsv->down = efa->e1; + } + } + if(editedge_containsVert(efa->e2, ev) && efa->e2 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e2; + } else if (!(tempsv->down)) { + tempsv->down = efa->e2; + } + } + if(editedge_containsVert(efa->e3, ev) && efa->e3 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e3; + } else if (!(tempsv->down)) { + tempsv->down = efa->e3; + } + } + if(efa->e4) { + if(editedge_containsVert(efa->e4, ev) && efa->e4 != eed) { + if(!tempsv->up) { + tempsv->up = efa->e4; + } else if (!(tempsv->down)) { + tempsv->down = efa->e4; + } + } + } + + } + } + } + } + } + if(i > 4 && j == 2) { + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + return 0; + } + BLI_ghash_insert(vertgh,ev,tempsv); + + look = look->next; + } + + // make sure the UPs nad DOWNs are 'faceloops' + // Also find the nearest slidevert to the cursor +// XXX getmouseco_areawin(mval); + look = vertlist; + nearest = NULL; + vertdist = -1; + while(look) { + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + + if(!tempsv->up || !tempsv->down) { + error("Missing rails"); + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + return 0; + } + + if(G.f & G_DRAW_EDGELEN) { + if(!(tempsv->up->f & SELECT)) { + tempsv->up->f |= SELECT; + tempsv->up->f2 |= 16; + } else { + tempsv->up->f2 |= ~16; + } + if(!(tempsv->down->f & SELECT)) { + tempsv->down->f |= SELECT; + tempsv->down->f2 |= 16; + } else { + tempsv->down->f2 |= ~16; + } + } + + if(look->next != NULL) { + SlideVert *sv; + + sv = BLI_ghash_lookup(vertgh,(EditVert*)look->next->link); + + if(sv) { + float tempdist, co[2]; + + if(!sharesFace(em, tempsv->up,sv->up)) { + EditEdge *swap; + swap = sv->up; + sv->up = sv->down; + sv->down = swap; + } + +// view3d_project_float(curarea, tempsv->origvert.co, co, projectMat); + + tempdist = sqrt(pow(co[0] - mval[0],2)+pow(co[1] - mval[1],2)); + + if(vertdist < 0) { + vertdist = tempdist; + nearest = (EditVert*)look->link; + } else if ( tempdist < vertdist ) { + vertdist = tempdist; + nearest = (EditVert*)look->link; + } + } + } + + + + look = look->next; + } + + + if (uvlay_tot) { // XXX && (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + int maxnum = 0; + uvarray = MEM_callocN( uvlay_tot * sizeof(GHash *), "SlideUVs Array"); + suv_last = slideuvs = MEM_callocN( uvlay_tot * (numadded+1) * sizeof(SlideUv), "SlideUVs"); /* uvLayers * verts */ + suv = NULL; + + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + + uvarray[uvlay_idx] = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + for(ev=em->verts.first;ev;ev=ev->next) { + ev->tmp.l = 0; + } + look = vertlist; + while(look) { + float *uv_new; + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + + ev = look->link; + suv = NULL; + for(efa = em->faces.first;efa;efa=efa->next) { + if (ev->tmp.l != -1) { /* test for self, in this case its invalid */ + int k=-1; /* face corner */ + + /* Is this vert in the faces corner? */ + if (efa->v1==ev) k=0; + else if (efa->v2==ev) k=1; + else if (efa->v3==ev) k=2; + else if (efa->v4 && efa->v4==ev) k=3; + + if (k != -1) { + MTFace *tf = CustomData_em_get_n(&em->fdata, efa->data, CD_MTFACE, uvlay_idx); + EditVert *ev_up, *ev_down; + + uv_new = tf->uv[k]; + + if (ev->tmp.l) { + if (fabs(suv->origuv[0]-uv_new[0]) > 0.0001 || fabs(suv->origuv[1]-uv_new[1])) { + ev->tmp.l = -1; /* Tag as invalid */ + BLI_linklist_free(suv->fuv_list,NULL); + suv->fuv_list = NULL; + BLI_ghash_remove(uvarray[uvlay_idx],ev, NULL, NULL); + suv = NULL; + break; + } + } else { + ev->tmp.l = 1; + suv = suv_last; + + suv->fuv_list = NULL; + suv->uv_up = suv->uv_down = NULL; + suv->origuv[0] = uv_new[0]; + suv->origuv[1] = uv_new[1]; + + BLI_linklist_prepend(&suv->fuv_list, uv_new); + BLI_ghash_insert(uvarray[uvlay_idx],ev,suv); + + suv_last++; /* advance to next slide UV */ + maxnum++; + } + + /* Now get the uvs along the up or down edge if we can */ + if (suv) { + if (!suv->uv_up) { + ev_up = editedge_getOtherVert(tempsv->up,ev); + if (efa->v1==ev_up) suv->uv_up = tf->uv[0]; + else if (efa->v2==ev_up) suv->uv_up = tf->uv[1]; + else if (efa->v3==ev_up) suv->uv_up = tf->uv[2]; + else if (efa->v4 && efa->v4==ev_up) suv->uv_up = tf->uv[3]; + } + if (!suv->uv_down) { /* if the first face was apart of the up edge, it cant be apart of the down edge */ + ev_down = editedge_getOtherVert(tempsv->down,ev); + if (efa->v1==ev_down) suv->uv_down = tf->uv[0]; + else if (efa->v2==ev_down) suv->uv_down = tf->uv[1]; + else if (efa->v3==ev_down) suv->uv_down = tf->uv[2]; + else if (efa->v4 && efa->v4==ev_down) suv->uv_down = tf->uv[3]; + } + + /* Copy the pointers to the face UV's */ + BLI_linklist_prepend(&suv->fuv_list, uv_new); + } + } + } + } + look = look->next; + } + } /* end uv layer loop */ + } /* end uvlay_tot */ + + + + // we should have enough info now to slide + + len = 0.0f; + + percp = -1; + while(draw) { + /* For the % calculation */ + short mval[2]; + float rc[2]; + float v2[2], v3[2]; + EditVert *centerVert, *upVert, *downVert; + +// XXX getmouseco_areawin(mval); + + if (!immediate && (mval[0] == mvalo[0] && mval[1] == mvalo[1])) { + PIL_sleep_ms(10); + } else { + char *p = str;; + int ctrl= 0, shift= 0; // XXX + + mvalo[0] = mval[0]; + mvalo[1] = mval[1]; + + + tempsv = BLI_ghash_lookup(vertgh,nearest); + + centerVert = editedge_getSharedVert(tempsv->up, tempsv->down); + upVert = editedge_getOtherVert(tempsv->up, centerVert); + downVert = editedge_getOtherVert(tempsv->down, centerVert); + +// view3d_project_float(curarea, upVert->co, v2, projectMat); +// view3d_project_float(curarea, downVert->co, v3, projectMat); + + /* Determine the % on which the loop should be cut */ + + rc[0]= v3[0]-v2[0]; + rc[1]= v3[1]-v2[1]; + len= rc[0]*rc[0]+ rc[1]*rc[1]; + if (len==0) {len = 0.0001;} + + if (shift) { + wasshift = 0; + labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len; + } + else { + if (wasshift==0) { + wasshift = 1; + shiftlabda = labda; + } + labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len / 10.0 + shiftlabda; + } + + + if(labda<=0.0) labda=0.0; + else if(labda>=1.0)labda=1.0; + + perc=((1-labda)*2)-1; + + if(shift == 0 && ctrl==0) { + perc *= 100; + perc = floor(perc); + perc /= 100; + } else if (ctrl) { + perc *= 10; + perc = floor(perc); + perc /= 10; + } + + if(prop == 0) { + len = VecLenf(upVert->co,downVert->co)*((perc+1)/2); + if(flip == 1) { + len = VecLenf(upVert->co,downVert->co) - len; + } + } + + if (0) // XXX hasNumInput(&num)) + { +// XXX applyNumInput(&num, &perc); + + if (prop) + { + perc = MIN2(perc, 1); + perc = MAX2(perc, -1); + } + else + { + len = MIN2(perc, VecLenf(upVert->co,downVert->co)); + len = MAX2(len, 0); + } + } + + //Adjust Edgeloop + if(immediate) { + perc = imperc; + } + percp = perc; + if(prop) { + look = vertlist; + while(look) { + EditVert *tempev; + ev = look->link; + tempsv = BLI_ghash_lookup(vertgh,ev); + + tempev = editedge_getOtherVert((perc>=0)?tempsv->up:tempsv->down, ev); + VecLerpf(ev->co, tempsv->origvert.co, tempev->co, fabs(perc)); + + if (0) { // XXX G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->origuv, (perc>=0)?suv->uv_up:suv->uv_down, fabs(perc)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + + look = look->next; + } + } + else { + //Non prop code + look = vertlist; + while(look) { + float newlen; + ev = look->link; + tempsv = BLI_ghash_lookup(vertgh,ev); + newlen = (len / VecLenf(editedge_getOtherVert(tempsv->up,ev)->co,editedge_getOtherVert(tempsv->down,ev)->co)); + if(newlen > 1.0) {newlen = 1.0;} + if(newlen < 0.0) {newlen = 0.0;} + if(flip == 0) { + VecLerpf(ev->co, editedge_getOtherVert(tempsv->down,ev)->co, editedge_getOtherVert(tempsv->up,ev)->co, fabs(newlen)); + if (0) { // XXX G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + /* dont do anything if no UVs */ + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->uv_down, suv->uv_up, fabs(newlen)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + } else{ + VecLerpf(ev->co, editedge_getOtherVert(tempsv->up,ev)->co, editedge_getOtherVert(tempsv->down,ev)->co, fabs(newlen)); + + if (0) { // XXX G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { + /* dont do anything if no UVs */ + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + suv = BLI_ghash_lookup( uvarray[uvlay_idx], ev ); + if (suv && suv->fuv_list && suv->uv_up && suv->uv_down) { + Vec2Lerpf(uv_tmp, suv->uv_up, suv->uv_down, fabs(newlen)); + fuv_link = suv->fuv_list; + while (fuv_link) { + VECCOPY2D(((float *)fuv_link->link), uv_tmp); + fuv_link = fuv_link->next; + } + } + } + } + } + look = look->next; + } + + } + + // Highlight the Control Edges +// scrarea_do_windraw(curarea); +// persp(PERSP_VIEW); +// glPushMatrix(); +// mymultmatrix(G.obedit->obmat); + + glColor3ub(0, 255, 0); + glBegin(GL_LINES); + glVertex3fv(upVert->co); + glVertex3fv(downVert->co); + glEnd(); + + if(prop == 0) { + // draw start edge for non-prop + glPointSize(5); + glBegin(GL_POINTS); + glColor3ub(255,0,255); + if(flip) { + glVertex3fv(upVert->co); + } else { + glVertex3fv(downVert->co); + } + glEnd(); + } + + + glPopMatrix(); + + if(prop) { + p += sprintf(str, "(P)ercentage: "); + } else { + p += sprintf(str, "Non (P)rop Length: "); + } + + if (0) // XXX hasNumInput(&num)) + { + char num_str[20]; + + // XX outputNumInput(&num, num_str); + p += sprintf(p, "%s", num_str); + } + else + { + if (prop) + { + p += sprintf(p, "%f", perc); + } + else + { + p += sprintf(p, "%f", len); + } + } + + + if (prop == 0) { + p += sprintf(p, ", Press (F) to flip control side"); + } + +// headerprint(str); +// screen_swapbuffers(); + } + if(!immediate) { + while(qtest()) { + short val=0; + event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle + + /* val==0 on key-release event */ + if (val) { + if(ELEM(event, ESCKEY, RIGHTMOUSE)) { + prop = 1; // Go back to prop mode + imperc = 0; // This is the % that gets set for immediate + immediate = 1; //Run through eval code 1 more time + cancel = 1; // Return -1 + mvalo[0] = -1; + } else if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)) { + draw = 0; // End looping now + } else if(event==MIDDLEMOUSE) { + perc = 0; + immediate = 1; + } else if(event==PKEY) { +// XXX initNumInput(&num); /* reset num input */ + if (prop) { + prop = 0; +// XXX num.flag |= NUM_NO_NEGATIVE; + } + else { + prop = 1; + } + mvalo[0] = -1; + } else if(event==FKEY) { + (flip == 1) ? (flip = 0):(flip = 1); + mvalo[0] = -1; + } else if(ELEM(event, RIGHTARROWKEY, WHEELUPMOUSE)) { // Scroll through Control Edges + look = vertlist; + while(look) { + if(nearest == (EditVert*)look->link) { + if(look->next == NULL) { + nearest = (EditVert*)vertlist->link; + } else { + nearest = (EditVert*)look->next->link; + } + mvalo[0] = -1; + break; + } + look = look->next; + } + } else if(ELEM(event, LEFTARROWKEY, WHEELDOWNMOUSE)) { // Scroll through Control Edges + look = vertlist; + while(look) { + if(look->next) { + if(look->next->link == nearest) { + nearest = (EditVert*)look->link; + mvalo[0] = -1; + break; + } + } else { + if((EditVert*)vertlist->link == nearest) { + nearest = look->link; + mvalo[0] = -1; + break; + } + } + look = look->next; + } + } + +// XXX if (handleNumInput(&num, event)) + { + mvalo[0] = -1; /* NEED A BETTER WAY TO TRIGGER REDRAW */ + } + } + + } + } else { + draw = 0; + } +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + } + + + if(G.f & G_DRAW_EDGELEN) { + look = vertlist; + while(look) { + tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); + if(tempsv != NULL) { + tempsv->up->f &= !SELECT; + tempsv->down->f &= !SELECT; + } + look = look->next; + } + } + +// force_draw(0); + + if(!immediate) + EM_automerge(0); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +// scrarea_queue_winredraw(curarea); + + //BLI_ghash_free(edgesgh, freeGHash, NULL); + BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); + BLI_linklist_free(vertlist,NULL); + BLI_linklist_free(edgelist,NULL); + + if (uvlay_tot) { // XXX && (G.scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + for (uvlay_idx=0; uvlay_idx<uvlay_tot; uvlay_idx++) { + BLI_ghash_free(uvarray[uvlay_idx], NULL, NULL); + } + MEM_freeN(uvarray); + MEM_freeN(slideuvs); + + suv = suv_last-1; + while (suv >= slideuvs) { + if (suv->fuv_list) { + BLI_linklist_free(suv->fuv_list,NULL); + } + suv--; + } + } + + if(cancel == 1) { + return -1; + } + + return 1; +} + +int EdgeLoopDelete(EditMesh *em) +{ + + /* temporal flag setting so we keep UVs when deleting edge loops, + * this is a bit of a hack but it works how you would want in almost all cases */ + // short uvcalc_flag_orig = 0; // XXX G.scene->toolsettings->uvcalc_flag; + // G.scene->toolsettings->uvcalc_flag |= UVCALC_TRANSFORM_CORRECT; + + if(!EdgeSlide(em, 1, 1)) { + return 0; + } + + /* restore uvcalc flag */ + // G.scene->toolsettings->uvcalc_flag = uvcalc_flag_orig; + + EM_select_more(em); + removedoublesflag(em, 1,0, 0.001); + EM_select_flush(em); + // DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + return 1; +} + + +/* -------------------- More tools ------------------ */ + +void mesh_set_face_flags(EditMesh *em, short mode) +{ + EditFace *efa; + MTFace *tface; + short m_tex=0, m_tiles=0, m_shared=0, + m_light=0, m_invis=0, m_collision=0, + m_twoside=0, m_obcolor=0, m_halo=0, + m_billboard=0, m_shadow=0, m_text=0, + m_sort=0; + short flag = 0, change = 0; + +// XXX if (!EM_texFaceCheck()) { +// error("not a mesh with uv/image layers"); +// return; +// } + + add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL); + add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL); + add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL); + add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL); + add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL); + add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL); + add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL); + add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL); + add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL); + add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL); + add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL); + add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL); + add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL); + + if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW)) + return; + + /* these 2 cant both be on */ + if (mode) /* are we seeting*/ + if (m_halo) + m_billboard = 0; + + if (m_tex) flag |= TF_TEX; + if (m_tiles) flag |= TF_TILES; + if (m_shared) flag |= TF_SHAREDCOL; + if (m_light) flag |= TF_LIGHT; + if (m_invis) flag |= TF_INVISIBLE; + if (m_collision) flag |= TF_DYNAMIC; + if (m_twoside) flag |= TF_TWOSIDE; + if (m_obcolor) flag |= TF_OBCOL; + if (m_halo) flag |= TF_BILLBOARD; + if (m_billboard) flag |= TF_BILLBOARD2; + if (m_shadow) flag |= TF_SHADOW; + if (m_text) flag |= TF_BMFONT; + if (m_sort) flag |= TF_ALPHASORT; + + if (flag==0) + return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (mode) tface->mode |= flag; + else tface->mode &= ~flag; + change = 1; + } + efa= efa->next; + } + + if (change) { + BIF_undo_push((mode ? "Set Flags" : "Clear Flags")); + } +} + +void mesh_set_smooth_faces(EditMesh *em, short event) +{ + EditFace *efa; + + if(G.obedit==0) return; + + if(G.obedit->type != OB_MESH) return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + if(event==1) efa->flag |= ME_SMOOTH; + else if(event==0) efa->flag &= ~ME_SMOOTH; + } + efa= efa->next; + } + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + if(event==1) BIF_undo_push("Set Smooth"); + else if(event==0) BIF_undo_push("Set Solid"); +} + +/* helper to find edge for edge_rip */ +static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval) +{ + float vec1[3], vec2[3], mvalf[2]; + +// XXX view3d_project_float(curarea, co1, vec1, mat); +// view3d_project_float(curarea, co2, vec2, mat); + mvalf[0]= (float)mval[0]; + mvalf[1]= (float)mval[1]; + + return PdistVL2Dfl(mvalf, vec1, vec2); +} + +/* helper for below */ +static void mesh_rip_setface(EditMesh *em, EditFace *sefa) +{ + /* put new vertices & edges in best face */ + if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v; + if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v; + if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v; + if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v; + + sefa->e1= addedgelist(em, sefa->v1, sefa->v2, sefa->e1); + sefa->e2= addedgelist(em, sefa->v2, sefa->v3, sefa->e2); + if(sefa->v4) { + sefa->e3= addedgelist(em, sefa->v3, sefa->v4, sefa->e3); + sefa->e4= addedgelist(em, sefa->v4, sefa->v1, sefa->e4); + } + else + sefa->e3= addedgelist(em, sefa->v3, sefa->v1, sefa->e3); + +} + +/* based on mouse cursor position, it defines how is being ripped */ +void mesh_rip(EditMesh *em) +{ + extern void faceloop_select(EditEdge *startedge, int select); + EditVert *eve, *nextve; + EditEdge *eed, *seed= NULL; + EditFace *efa, *sefa= NULL; + float projectMat[4][4], vec[3], dist, mindist; // viewMat[4][4], XXX + short doit= 1, mval[2]; // XXX ,propmode,prop; + +// XXX propmode = G.scene->prop_mode; +// G.scene->prop_mode = 0; +// prop = G.scene->proportional; +// G.scene->proportional = 0; + + /* select flush... vertices are important */ + EM_selectmode_set(em); + +// XXX getmouseco_areawin(mval); +// view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat); + + /* find best face, exclude triangles and break on face select or faces with 2 edges select */ + mindist= 1000000.0f; + for(efa= em->faces.first; efa; efa=efa->next) { + if( efa->f & 1) + break; + if(efa->v4 && faceselectedOR(efa, SELECT) ) { + int totsel=0; + + if(efa->e1->f & SELECT) totsel++; + if(efa->e2->f & SELECT) totsel++; + if(efa->e3->f & SELECT) totsel++; + if(efa->e4->f & SELECT) totsel++; + + if(totsel>1) + break; +// view3d_project_float(curarea, efa->cent, vec, projectMat); + dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) ); + if(dist<mindist) { + mindist= dist; + sefa= efa; + } + } + } + + if(efa) { + error("Can't perform ripping with faces selected this way"); + return; + } + if(sefa==NULL) { + error("No proper selection or faces included"); + return; + } + + + /* duplicate vertices, new vertices get selected */ + for(eve = em->verts.last; eve; eve= eve->prev) { + eve->tmp.v = NULL; + if(eve->f & SELECT) { + eve->tmp.v = addvertlist(em, eve->co, eve); + eve->f &= ~SELECT; + eve->tmp.v->f |= SELECT; + } + } + + /* find the best candidate edge */ + /* or one of sefa edges is selected... */ + if(sefa->e1->f & SELECT) seed= sefa->e2; + if(sefa->e2->f & SELECT) seed= sefa->e1; + if(sefa->e3->f & SELECT) seed= sefa->e2; + if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3; + + /* or we do the distance trick */ + if(seed==NULL) { + mindist= 1000000.0f; + if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e1->v1->co, + sefa->e1->v2->co, mval); + if(dist<mindist) { + seed= sefa->e1; + mindist= dist; + } + } + if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e2->v1->co, + sefa->e2->v2->co, mval); + if(dist<mindist) { + seed= sefa->e2; + mindist= dist; + } + } + if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) { + dist= mesh_rip_edgedist(projectMat, + sefa->e3->v1->co, + sefa->e3->v2->co, mval); + if(dist<mindist) { + seed= sefa->e3; + mindist= dist; + } + } + if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) { + dist= mesh_rip_edgedist(projectMat, + sefa->e4->v1->co, + sefa->e4->v2->co, mval); + if(dist<mindist) { + seed= sefa->e4; + mindist= dist; + } + } + } + + if(seed==NULL) { // never happens? + error("No proper edge found to start"); + return; + } + + faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1 + + /* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */ + for(eed = em->edges.last; eed; eed= eed->prev) { + eed->tmp.v = NULL; + if((eed->v1->tmp.v) || (eed->v2->tmp.v)) { + EditEdge *newed; + + newed= addedgelist(em, eed->v1->tmp.v?eed->v1->tmp.v:eed->v1, + eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed); + if(eed->f & SELECT) { + eed->f &= ~SELECT; + newed->f |= SELECT; + } + eed->tmp.v = (EditVert *)newed; + } + } + + /* first clear edges to help finding neighbours */ + for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; + + /* put new vertices & edges && flag in best face */ + mesh_rip_setface(em, sefa); + + /* starting with neighbours of best face, we loop over the seam */ + sefa->f1= 2; + doit= 1; + while(doit) { + doit= 0; + + for(efa= em->faces.first; efa; efa=efa->next) { + /* new vert in face */ + if (efa->v1->tmp.v || efa->v2->tmp.v || + efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) { + /* face is tagged with loop */ + if(efa->f1==1) { + mesh_rip_setface(em, efa); + efa->f1= 2; + doit= 1; + } + } + } + } + + /* remove loose edges, that were part of a ripped face */ + for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; + for(efa= em->faces.first; efa; efa=efa->next) { + efa->e1->f1= 1; + efa->e2->f1= 1; + efa->e3->f1= 1; + if(efa->e4) efa->e4->f1= 1; + } + + for(eed = em->edges.last; eed; eed= seed) { + seed= eed->prev; + if(eed->f1==0) { + if(eed->v1->tmp.v || eed->v2->tmp.v || + (eed->v1->f & SELECT) || (eed->v2->f & SELECT)) { + remedge(em, eed); + free_editedge(em, eed); + eed= NULL; + } + } + if(eed) { + eed->v1->f1= 1; + eed->v2->f1= 1; + } + } + + /* and remove loose selected vertices, that got duplicated accidentally */ + for(eve = em->verts.first; eve; eve= nextve) { + nextve= eve->next; + if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) { + BLI_remlink(&em->verts,eve); + free_editvert(em, eve); + } + } + +// BIF_TransformSetUndo("Rip"); +// initTransform(TFM_TRANSLATION, 0); +// Transform(); + +// G.scene->prop_mode = propmode; +// XXX G.scene->proportional = prop; +} + +void shape_propagate(Scene *scene, EditMesh *em) +{ + EditVert *ev = NULL; + Mesh* me = (Mesh*)G.obedit->data; + Key* ky = NULL; + KeyBlock* kb = NULL; + Base* base=NULL; + + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + for(kb=ky->block.first;kb;kb = kb->next){ + float *data; + data = kb->data; + VECCOPY(data+(ev->keyindex*3),ev->co); + } + } + } + } else { + error("Object Has No Blendshapes"); + return; + } + + //TAG Mesh Objects that share this data + for(base = scene->base.first; base; base = base->next){ + if(base->object && base->object->data == me){ + base->object->recalc = OB_RECALC_DATA; + } + } + + BIF_undo_push("Propagate Blendshape Verts"); + DAG_object_flush_update(scene, G.obedit, OB_RECALC_DATA); + return; +} + +void shape_copy_from_lerp(EditMesh *em, KeyBlock* thisBlock, KeyBlock* fromBlock) +{ + EditVert *ev = NULL; + short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ; + float perc = 0; + char str[64]; + float *data, *odata; + + data = fromBlock->data; + odata = thisBlock->data; + +// XXX getmouseco_areawin(mval); + curval[0] = mval[0] + 1; curval[1] = mval[1] + 1; + + while (finished == 0) + { +// XXX getmouseco_areawin(mval); + if (mval[0] != curval[0] || mval[1] != curval[1]) + { + + if(mval[0] > curval[0]) + perc += 0.1; + else if(mval[0] < curval[0]) + perc -= 0.1; + + if(perc < 0) perc = 0; + if(perc > 1) perc = 1; + + curval[0] = mval[0]; + curval[1] = mval[1]; + + if(fullcopy == 1){ + perc = 1; + } + + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + VecLerpf(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc); + } + } + sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%'); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +// headerprint(str); +// force_draw(0); + + if(fullcopy == 1){ + break; + } + + } else { + PIL_sleep_ms(10); + } + + while(qtest()) { + short val=0; + event= extern_qread(&val); + if(val){ + if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){ + finished = 1; + } + else if (event == MIDDLEMOUSE){ + fullcopy = 1; + } + else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){ + canceled = 1; + finished = 1; + } + } + } + } + if(!canceled) + BIF_undo_push("Copy Blendshape Verts"); + else + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + VECCOPY(ev->co, odata+(ev->keyindex*3)); + } + } + return; +} + + + +void shape_copy_select_from(EditMesh *em) +{ + Mesh* me = (Mesh*)G.obedit->data; + EditVert *ev = NULL; + int totverts = 0,curshape = G.obedit->shapenr; + + Key* ky = NULL; + KeyBlock *kb = NULL,*thisBlock = NULL; + int maxlen=32, nr=0, a=0; + char *menu; + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(kb=ky->block.first;kb;kb = kb->next){ + maxlen += 40; // Size of a block name + if(a == curshape-1){ + thisBlock = kb; + } + + a++; + } + a=0; + menu = MEM_callocN(maxlen, "Copy Shape Menu Text"); + strcpy(menu, "Copy Vert Positions from Shape %t|"); + for(kb=ky->block.first;kb;kb = kb->next){ + if(a != curshape-1){ + sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a); + } + a++; + } +// XXX nr = pupmenu_col(menu, 20); + MEM_freeN(menu); + } else { + error("Object Has No Blendshapes"); + return; + } + + a = 0; + + for(kb=ky->block.first;kb;kb = kb->next){ + if(a == nr){ + + for(ev = em->verts.first;ev;ev = ev->next){ + totverts++; + } + + if(me->totvert != totverts){ + error("Shape Has had Verts Added/Removed, please cycle editmode before copying"); + return; + } + shape_copy_from_lerp(em, thisBlock,kb); + + return; + } + a++; + } + return; +} + +/* Collection Routines|Currently used by the improved merge code*/ +/* buildEdge_collection() creates a list of lists*/ +/* these lists are filled with edges that are topologically connected.*/ +/* This whole tool needs to be redone, its rather poorly implemented...*/ + +typedef struct Collection{ + struct Collection *next, *prev; + int index; + ListBase collectionbase; +} Collection; + +typedef struct CollectedEdge{ + struct CollectedEdge *next, *prev; + EditEdge *eed; +} CollectedEdge; + +#define MERGELIMIT 0.000001 + +static void build_edgecollection(EditMesh *em, ListBase *allcollections) +{ + EditEdge *eed; + Collection *edgecollection, *newcollection; + CollectedEdge *newedge; + + int currtag = 1; + short ebalanced = 0; + short collectionfound = 0; + + for (eed=em->edges.first; eed; eed = eed->next){ + eed->tmp.l = 0; + eed->v1->tmp.l = 0; + eed->v2->tmp.l = 0; + } + + /*1st pass*/ + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + eed->v1->tmp.l = currtag; + eed->v2->tmp.l = currtag; + currtag +=1; + } + } + + /*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */ + while(ebalanced == 0){ + ebalanced = 1; + for(eed=em->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT){ + if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{ + if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l; + else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l; + ebalanced = 0; + } + } + } + } + + /*3rd pass, set all the edge flags (unnessecary?)*/ + for(eed=em->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l; + } + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + if(allcollections->first){ + for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){ + if(edgecollection->index == eed->tmp.l){ + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + BLI_addtail(&(edgecollection->collectionbase), newedge); + collectionfound = 1; + break; + } + else collectionfound = 0; + } + } + if(allcollections->first == NULL || collectionfound == 0){ + newcollection = MEM_mallocN(sizeof(Collection), "element collection"); + newcollection->index = eed->tmp.l; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + + BLI_addtail(&(newcollection->collectionbase), newedge); + BLI_addtail(allcollections, newcollection); + } + } + + } +} + +static void freecollections(ListBase *allcollections) +{ + struct Collection *curcollection; + + for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next) + BLI_freelistN(&(curcollection->collectionbase)); + BLI_freelistN(allcollections); +} + +/*Begin UV Edge Collapse Code + Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail + in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it. + The welded UV edges can then be sorted and collapsed. +*/ +typedef struct wUV{ + struct wUV *next, *prev; + ListBase nodes; + float u, v; /*cached copy of UV coordinates pointed to by nodes*/ + EditVert *eve; + int f; +} wUV; + +typedef struct wUVNode{ + struct wUVNode *next, *prev; + float *u; /*pointer to original tface data*/ + float *v; /*pointer to original tface data*/ +} wUVNode; + +typedef struct wUVEdge{ + struct wUVEdge *next, *prev; + float v1uv[2], v2uv[2]; /*nasty.*/ + struct wUV *v1, *v2; /*oriented same as editedge*/ + EditEdge *eed; + int f; +} wUVEdge; + +typedef struct wUVEdgeCollect{ /*used for grouping*/ + struct wUVEdgeCollect *next, *prev; + wUVEdge *uved; + int id; +} wUVEdgeCollect; + +static void append_weldedUV(EditMesh *em, EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts) +{ + wUV *curwvert, *newwvert; + wUVNode *newnode; + int found; + MTFace *tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + found = 0; + + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + BLI_addtail(&(curwvert->nodes), newnode); + found = 1; + break; + } + } + + if(!found){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + + newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert"); + newwvert->u = *(newnode->u); + newwvert->v = *(newnode->v); + newwvert->eve = eve; + + BLI_addtail(&(newwvert->nodes), newnode); + BLI_addtail(uvverts, newwvert); + + } +} + +static void build_weldedUVs(EditMesh *em, ListBase *uvverts) +{ + EditFace *efa; + for(efa=em->faces.first; efa; efa=efa->next){ + if(efa->v1->f1) append_weldedUV(em, efa, efa->v1, 0, uvverts); + if(efa->v2->f1) append_weldedUV(em, efa, efa->v2, 1, uvverts); + if(efa->v3->f1) append_weldedUV(em, efa, efa->v3, 2, uvverts); + if(efa->v4 && efa->v4->f1) append_weldedUV(em, efa, efa->v4, 3, uvverts); + } +} + +static void append_weldedUVEdge(EditMesh *em, EditFace *efa, EditEdge *eed, ListBase *uvedges) +{ + wUVEdge *curwedge, *newwedge; + int v1tfindex, v2tfindex, found; + MTFace *tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + found = 0; + + if(eed->v1 == efa->v1) v1tfindex = 0; + else if(eed->v1 == efa->v2) v1tfindex = 1; + else if(eed->v1 == efa->v3) v1tfindex = 2; + else /* if(eed->v1 == efa->v4) */ v1tfindex = 3; + + if(eed->v2 == efa->v1) v2tfindex = 0; + else if(eed->v2 == efa->v2) v2tfindex = 1; + else if(eed->v2 == efa->v3) v2tfindex = 2; + else /* if(eed->v2 == efa->v4) */ v2tfindex = 3; + + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){ + found = 1; + break; //do nothing, we don't need another welded uv edge + } + } + + if(!found){ + newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge"); + newwedge->v1uv[0] = tf->uv[v1tfindex][0]; + newwedge->v1uv[1] = tf->uv[v1tfindex][1]; + newwedge->v2uv[0] = tf->uv[v2tfindex][0]; + newwedge->v2uv[1] = tf->uv[v2tfindex][1]; + newwedge->eed = eed; + + BLI_addtail(uvedges, newwedge); + } +} + +static void build_weldedUVEdges(EditMesh *em, ListBase *uvedges, ListBase *uvverts) +{ + wUV *curwvert; + wUVEdge *curwedge; + EditFace *efa; + + for(efa=em->faces.first; efa; efa=efa->next){ + if(efa->e1->f1) append_weldedUVEdge(em, efa, efa->e1, uvedges); + if(efa->e2->f1) append_weldedUVEdge(em, efa, efa->e2, uvedges); + if(efa->e3->f1) append_weldedUVEdge(em, efa, efa->e3, uvedges); + if(efa->e4 && efa->e4->f1) append_weldedUVEdge(em, efa, efa->e4, uvedges); + } + + + //link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){ + curwedge->v1 = curwvert; + break; + } + } + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){ + curwedge->v2 = curwvert; + break; + } + } + } +} + +static void free_weldedUVs(ListBase *uvverts) +{ + wUV *curwvert; + for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes)); + BLI_freelistN(uvverts); +} + +static void collapse_edgeuvs(EditMesh *em) +{ + ListBase uvedges, uvverts, allcollections; + wUVEdge *curwedge; + wUVNode *curwnode; + wUVEdgeCollect *collectedwuve, *newcollectedwuve; + Collection *wuvecollection, *newcollection; + int curtag, balanced, collectionfound= 0, vcount; + float avg[2]; + + if (!EM_texFaceCheck(em)) + return; + + uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL; + + build_weldedUVs(em, &uvverts); + build_weldedUVEdges(em, &uvedges, &uvverts); + + curtag = 0; + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + curwedge->v1->f = curtag; + curwedge->v2->f = curtag; + curtag +=1; + } + + balanced = 0; + while(!balanced){ + balanced = 1; + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(curwedge->v1->f != curwedge->v2->f){ + if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f; + else curwedge->v2->f = curwedge->v1->f; + balanced = 0; + } + } + } + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f; + + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(allcollections.first){ + for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + if(wuvecollection->index == curwedge->f){ + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve); + collectionfound = 1; + break; + } + + else collectionfound = 0; + } + } + if(allcollections.first == NULL || collectionfound == 0){ + newcollection = MEM_callocN(sizeof(Collection), "element collection"); + newcollection->index = curwedge->f; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + + BLI_addtail(&(newcollection->collectionbase), newcollectedwuve); + BLI_addtail(&allcollections, newcollection); + } + } + + for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + + vcount = avg[0] = avg[1] = 0; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + avg[0] += collectedwuve->uved->v1uv[0]; + avg[1] += collectedwuve->uved->v1uv[1]; + + avg[0] += collectedwuve->uved->v2uv[0]; + avg[1] += collectedwuve->uved->v2uv[1]; + + vcount +=2; + + } + + avg[0] /= vcount; avg[1] /= vcount; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + } + } + + free_weldedUVs(&uvverts); + BLI_freelistN(&uvedges); + freecollections(&allcollections); +} + +/*End UV Edge collapse code*/ + +static void collapseuvs(EditMesh *em, EditVert *mergevert) +{ + EditFace *efa; + MTFace *tf; + int uvcount; + float uvav[2]; + + if (!EM_texFaceCheck(em)) + return; + + uvcount = 0; + uvav[0] = 0; + uvav[1] = 0; + + for(efa = em->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) { + uvav[0] += tf->uv[0][0]; + uvav[1] += tf->uv[0][1]; + uvcount += 1; + } + if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){ + uvav[0] += tf->uv[1][0]; + uvav[1] += tf->uv[1][1]; + uvcount += 1; + } + if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){ + uvav[0] += tf->uv[2][0]; + uvav[1] += tf->uv[2][1]; + uvcount += 1; + } + if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){ + uvav[0] += tf->uv[3][0]; + uvav[1] += tf->uv[3][1]; + uvcount += 1; + } + } + + if(uvcount > 0) { + uvav[0] /= uvcount; + uvav[1] /= uvcount; + + for(efa = em->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1){ + tf->uv[0][0] = uvav[0]; + tf->uv[0][1] = uvav[1]; + } + if(efa->v2->f1){ + tf->uv[1][0] = uvav[0]; + tf->uv[1][1] = uvav[1]; + } + if(efa->v3->f1){ + tf->uv[2][0] = uvav[0]; + tf->uv[2][1] = uvav[1]; + } + if(efa->v4 && efa->v4->f1){ + tf->uv[3][0] = uvav[0]; + tf->uv[3][1] = uvav[1]; + } + } + } +} + +int collapseEdges(EditMesh *em) +{ + EditVert *eve; + EditEdge *eed; + + ListBase allcollections; + CollectedEdge *curredge; + Collection *edgecollection; + + int totedges, groupcount, mergecount,vcount; + float avgcount[3]; + + allcollections.first = 0; + allcollections.last = 0; + + mergecount = 0; + + if(multires_test()) return 0; + + build_edgecollection(em, &allcollections); + groupcount = BLI_countlist(&allcollections); + + + for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){ + totedges = BLI_countlist(&(edgecollection->collectionbase)); + mergecount += totedges; + avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0; + + vcount = 0; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2]; + + avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2]; + + vcount +=2; + } + + avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + VECCOPY(((EditEdge*)curredge->eed)->v1->co,avgcount); + VECCOPY(((EditEdge*)curredge->eed)->v2->co,avgcount); + } + + if (EM_texFaceCheck(em)) { + /*uv collapse*/ + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + curredge->eed->v1->f1 = 1; + curredge->eed->v2->f1 = 1; + curredge->eed->f1 = 1; + } + collapse_edgeuvs(em); + } + + } + freecollections(&allcollections); + removedoublesflag(em, 1, 0, MERGELIMIT); + /*get rid of this!*/ +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +// if (EM_texFaceCheck()) + + return mergecount; +} + +int merge_firstlast(EditMesh *em, int first, int uvmerge) +{ + EditVert *eve,*mergevert; + EditSelection *ese; + + if(multires_test()) return 0; + + /* do sanity check in mergemenu in edit.c ?*/ + if(first == 0){ + ese = em->selected.last; + mergevert= (EditVert*)ese->data; + } + else{ + ese = em->selected.first; + mergevert = (EditVert*)ese->data; + } + + if(mergevert->f&SELECT){ + for (eve=em->verts.first; eve; eve=eve->next){ + if (eve->f&SELECT) + VECCOPY(eve->co,mergevert->co); + } + } + + if(uvmerge && CustomData_has_layer(&em->fdata, CD_MTFACE)){ + + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=em->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(em, mergevert); + } + + return removedoublesflag(em, 1, 0, MERGELIMIT); +} + +int merge_target(EditMesh *em, int target, int uvmerge) +{ + EditVert *eve; + + if(multires_test()) return 0; + + if(target) snap_sel_to_curs(); + else snap_to_center(); + + if(uvmerge && CustomData_has_layer(&em->fdata, CD_MTFACE)){ + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=em->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(em, NULL); + } + +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + return removedoublesflag(em, 1, 0, MERGELIMIT); + +} +#undef MERGELIMIT + +typedef struct PathNode{ + int u; + int visited; + ListBase edges; +} PathNode; + +typedef struct PathEdge{ + struct PathEdge *next, *prev; + int v; + float w; +} PathEdge; + +void pathselect(EditMesh *em) +{ + EditVert *eve, *s, *t; + EditEdge *eed; + EditSelection *ese; + PathEdge *newpe, *currpe; + PathNode *currpn; + PathNode *Q; + int v, *previous, pathvert, pnindex; /*pnindex redundant?*/ + int unbalanced, totnodes; + short physical; + float *cost; + Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/ + + s = t = NULL; + + ese = ((EditSelection*)em->selected.last); + if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){ + physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0"); + + t = (EditVert*)ese->data; + s = (EditVert*)ese->prev->data; + + /*need to find out if t is actually reachable by s....*/ + for(eve=em->verts.first; eve; eve=eve->next){ + eve->f1 = 0; + } + + s->f1 = 1; + + unbalanced = 1; + totnodes = 1; + while(unbalanced){ + unbalanced = 0; + for(eed=em->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1 && !eed->v2->f1){ + eed->v2->f1 = 1; + totnodes++; + unbalanced = 1; + } + else if(eed->v2->f1 && !eed->v1->f1){ + eed->v1->f1 = 1; + totnodes++; + unbalanced = 1; + } + } + } + } + + + + if(s->f1 && t->f1){ /*t can be reached by s*/ + Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes"); + totnodes = 0; + for(eve=em->verts.first; eve; eve=eve->next){ + if(eve->f1){ + Q[totnodes].u = totnodes; + Q[totnodes].edges.first = 0; + Q[totnodes].edges.last = 0; + Q[totnodes].visited = 0; + eve->tmp.p = &(Q[totnodes]); + totnodes++; + } + else eve->tmp.p = NULL; + } + + for(eed=em->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1){ + currpn = ((PathNode*)eed->v1->tmp.p); + + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v2->tmp.p)->u; + if(physical){ + newpe->w = VecLenf(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + if(eed->v2->f1){ + currpn = ((PathNode*)eed->v2->tmp.p); + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v1->tmp.p)->u; + if(physical){ + newpe->w = VecLenf(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + } + } + + heap = BLI_heap_new(); + cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs"); + previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices"); + + for(v=0; v < totnodes; v++){ + cost[v] = 1000000; + previous[v] = -1; /*array of indices*/ + } + + pnindex = ((PathNode*)s->tmp.p)->u; + cost[pnindex] = 0; + BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex)); + + while( !BLI_heap_empty(heap) ){ + + pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap)); + currpn = &(Q[pnindex]); + + if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/ + break; + + for(currpe=currpn->edges.first; currpe; currpe=currpe->next){ + if(!Q[currpe->v].visited){ + if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){ + cost[currpe->v] = cost[currpn->u] + currpe->w; + previous[currpe->v] = currpn->u; + Q[currpe->v].visited = 1; + BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v)); + } + } + } + } + + pathvert = ((PathNode*)t->tmp.p)->u; + while(pathvert != -1){ + for(eve=em->verts.first; eve; eve=eve->next){ + if(eve->f1){ + if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT; + } + } + pathvert = previous[pathvert]; + } + + for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges)); + MEM_freeN(Q); + MEM_freeN(cost); + MEM_freeN(previous); + BLI_heap_free(heap, NULL); + EM_select_flush(em); + // DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +// if (EM_texFaceCheck()) + } + } + else{ + error("Path Selection requires that exactly two vertices be selected"); + return; + } +} + +void region_to_loop(EditMesh *em) +{ + EditEdge *eed; + EditFace *efa; + + if(G.totfacesel){ + for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = 0; + + for(efa=em->faces.first; efa; efa=efa->next){ + if(efa->f&SELECT){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) + efa->e4->f1++; + } + } + + EM_clear_flag_all(em, SELECT); + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 == 1) EM_select_edge(eed, 1); + } + + em->selectmode = SCE_SELECT_EDGE; + EM_selectmode_set(em); + // DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Face Region to Edge Loop"); + + } +} + +static int validate_loop(EditMesh *em, Collection *edgecollection) +{ + EditEdge *eed; + EditFace *efa; + CollectedEdge *curredge; + + /*1st test*/ + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1 = 0; + curredge->eed->v2->f1 = 0; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1++; + curredge->eed->v2->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->v1->f1 > 2) return(0); else + if(curredge->eed->v2->f1 > 2) return(0); + } + + /*2nd test*/ + for(eed = em->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(efa=em->faces.first; efa; efa=efa->next){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->f1 > 2) return(0); + } + return(1); +} + +static int loop_bisect(EditMesh *em, Collection *edgecollection){ + + EditFace *efa, *sf1, *sf2; + EditEdge *eed, *sed; + CollectedEdge *curredge; + int totsf1, totsf2, unbalanced,balancededges; + + for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0; + for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = 0; + + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1; + + sf1 = sf2 = NULL; + sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed; + + for(efa=em->faces.first; efa; efa=efa->next){ + if(sf2) break; + else if(sf1){ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa; + } + else{ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa; + } + } + + if(sf1==NULL || sf2==NULL) + return(-1); + + if(!(sf1->e1->f1)) sf1->e1->f2 = 1; + if(!(sf1->e2->f1)) sf1->e2->f2 = 1; + if(!(sf1->e3->f1)) sf1->e3->f2 = 1; + if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1; + sf1->f1 = 1; + totsf1 = 1; + + if(!(sf2->e1->f1)) sf2->e1->f2 = 2; + if(!(sf2->e2->f1)) sf2->e2->f2 = 2; + if(!(sf2->e3->f1)) sf2->e3->f2 = 2; + if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2; + sf2->f1 = 2; + totsf2 = 1; + + /*do sf1*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=em->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1; + if(balancededges){ + unbalanced = 1; + efa->f1 = 1; + totsf1++; + } + } + } + } + } + + /*do sf2*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=em->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2; + if(balancededges){ + unbalanced = 1; + efa->f1 = 2; + totsf2++; + } + } + } + } + } + + if(totsf1 < totsf2) return(1); + else return(2); +} + +void loop_to_region(EditMesh *em) +{ + EditFace *efa; + ListBase allcollections={NULL,NULL}; + Collection *edgecollection; + int testflag; + + build_edgecollection(em, &allcollections); + + for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){ + if(validate_loop(em, edgecollection)){ + testflag = loop_bisect(em, edgecollection); + for(efa=em->faces.first; efa; efa=efa->next){ + if(efa->f1 == testflag){ + if(efa->f&SELECT) EM_select_face(efa, 0); + else EM_select_face(efa,1); + } + } + } + } + + for(efa=em->faces.first; efa; efa=efa->next){ /*fix this*/ + if(efa->f&SELECT) EM_select_face(efa,1); + } + + freecollections(&allcollections); +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +// if (EM_texFaceCheck()) + + BIF_undo_push("Edge Loop to Face Region"); +} + + +/* texface and vertex color editmode tools for the face menu */ + +void mesh_rotate_uvs(EditMesh *em) +{ + EditFace *efa; + short change = 0, ccw; + MTFace *tf; + float u1, v1; + int shift = 0; // XXX + + if (!EM_texFaceCheck(em)) { + error("mesh has no uv/image layers"); + return; + } + + ccw = (shift); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + + if (ccw) { + if(efa->v4) { + tf->uv[0][0]= tf->uv[3][0]; + tf->uv[0][1]= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + } else { + tf->uv[0][0]= tf->uv[2][0]; + tf->uv[0][1]= tf->uv[2][1]; + } + + tf->uv[2][0]= tf->uv[1][0]; + tf->uv[2][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + if(efa->v4) { + tf->uv[2][0]= tf->uv[3][0]; + tf->uv[2][1]= tf->uv[3][1]; + + tf->uv[3][0]= u1; + tf->uv[3][1]= v1; + } + else { + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + } + change = 1; + } + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Rotate UV face"); + } +} + +void mesh_mirror_uvs(EditMesh *em) +{ + EditFace *efa; + short change = 0, altaxis; + MTFace *tf; + float u1, v1; + int shift= 0; // XXX + + if (!EM_texFaceCheck(em)) { + error("mesh has no uv/image layers"); + return; + } + + altaxis = (shift); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (altaxis) { + u1= tf->uv[1][0]; + v1= tf->uv[1][1]; + if(efa->v4) { + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[0][0]; + tf->uv[3][1]= tf->uv[0][1]; + + tf->uv[0][0]= u1; + tf->uv[0][1]= v1; + } + else { + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + + } else { + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + if(efa->v4) { + + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } + } + change = 1; + } + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Mirror UV face"); + } +} + +void mesh_rotate_colors(EditMesh *em) +{ + EditFace *efa; + short change = 0, ccw; + MCol tmpcol, *mcol; + int shift= 0; // XXX + + if (!EM_vertColorCheck(em)) { + error("mesh has no color layers"); + return; + } + + ccw = (shift); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + tmpcol= mcol[0]; + + if (ccw) { + if(efa->v4) { + mcol[0]= mcol[3]; + mcol[3]= mcol[2]; + } else { + mcol[0]= mcol[2]; + } + mcol[2]= mcol[1]; + mcol[1]= tmpcol; + } else { + mcol[0]= mcol[1]; + mcol[1]= mcol[2]; + + if(efa->v4) { + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + else + mcol[2]= tmpcol; + } + change = 1; + } + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Rotate Color face"); + } +} + +void mesh_mirror_colors(EditMesh *em) +{ + EditFace *efa; + short change = 0, altaxis; + MCol tmpcol, *mcol; + int shift= 0; // XXX + + if (!EM_vertColorCheck(em)) { + error("mesh has no color layers"); + return; + } + + altaxis = (shift); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + if (altaxis) { + tmpcol= mcol[1]; + mcol[1]= mcol[2]; + mcol[2]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[0]; + mcol[0]= mcol[3]; + mcol[3]= tmpcol; + } + } else { + tmpcol= mcol[0]; + mcol[0]= mcol[1]; + mcol[1]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[2]; + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + } + change = 1; + } + } + + if (change) { +// DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push("Mirror Color face"); + } +} diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c new file mode 100644 index 00000000000..00ff434978e --- /dev/null +++ b/source/blender/editors/mesh/meshtools.c @@ -0,0 +1,1130 @@ +/** + * $Id: + * + * ***** BEGIN GPL 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. + * + * 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) 2004 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + meshtools.c: no editmode (violated already :), tools operating on meshes +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_ghash.h" +#include "BLI_rand.h" /* for randome face sorting */ +#include "BLI_threads.h" + + +#include "BKE_blender.h" +#include "BKE_depsgraph.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "RE_pipeline.h" +#include "RE_shader_ext.h" + +#include "PIL_time.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "GPU_draw.h" + +#include "BLO_sys_types.h" // for intptr_t support + +#include "ED_mesh.h" +#include "ED_object.h" + +/* own include */ +#include "editmesh.h" + + +/* from rendercode.c */ +#define VECMUL(dest, f) dest[0]*= f; dest[1]*= f; dest[2]*= f + +/* XXX */ +static void BIF_undo_push() {} +static void waitcursor() {} +static void error() {} +static int pupmenu() {return 0;} +/* XXX */ + + +/* * ********************** no editmode!!! *********** */ + +/* join selected meshes into the active mesh, context sensitive +return 0 if no join is made (error) and 1 of the join is done */ +int join_mesh(Scene *scene, View3D *v3d) +{ + Base *base, *nextb; + Object *ob; + Material **matar, *ma; + Mesh *me; + MVert *mvert, *mvertmain; + MEdge *medge = NULL, *medgemain; + MFace *mface = NULL, *mfacemain; + float imat[4][4], cmat[4][4]; + int a, b, totcol, totedge=0, totvert=0, totface=0, ok=0, vertofs, map[MAXMAT]; + int i, j, index, haskey=0, hasmulti=0, edgeofs, faceofs; + bDeformGroup *dg, *odg; + MDeformVert *dvert; + CustomData vdata, edata, fdata; + + if(G.obedit) return 0; + + ob= OBACT; + if(!ob || ob->type!=OB_MESH) return 0; + + if (object_data_is_libdata(ob)) { +// XXX error_libdata(); + return 0; + } + + /* count & check */ + base= FIRSTBASE; + while(base) { + if(TESTBASELIB_BGMODE(base)) { /* BGMODE since python can access */ + if(base->object->type==OB_MESH) { + me= base->object->data; + totvert+= me->totvert; + totface+= me->totface; + + if(base->object == ob) ok= 1; + + if(me->key) { + haskey= 1; + break; + } + if(me->mr) { + hasmulti= 1; + break; + } + } + } + base= base->next; + } + + if(haskey) { + error("Can't join meshes with vertex keys"); + return 0; + } + if(hasmulti) { + error("Can't join meshes with Multires"); + return 0; + } + /* that way the active object is always selected */ + if(ok==0) return 0; + + if(totvert==0 || totvert>MESH_MAX_VERTS) return 0; + + /* if needed add edges to other meshes */ + for(base= FIRSTBASE; base; base= base->next) { + if(TESTBASELIB_BGMODE(base)) { + if(base->object->type==OB_MESH) { + me= base->object->data; + totedge += me->totedge; + } + } + } + + /* new material indices and material array */ + matar= MEM_callocN(sizeof(void *)*MAXMAT, "join_mesh"); + totcol= ob->totcol; + + /* obact materials in new main array, is nicer start! */ + for(a=1; a<=ob->totcol; a++) { + matar[a-1]= give_current_material(ob, a); + id_us_plus((ID *)matar[a-1]); + /* increase id->us : will be lowered later */ + } + + base= FIRSTBASE; + while(base) { + if(TESTBASELIB_BGMODE(base)) { + if(ob!=base->object && base->object->type==OB_MESH) { + me= base->object->data; + + // Join this object's vertex groups to the base one's + for (dg=base->object->defbase.first; dg; dg=dg->next){ + /* See if this group exists in the object */ + for (odg=ob->defbase.first; odg; odg=odg->next){ + if (!strcmp(odg->name, dg->name)){ + break; + } + } + if (!odg){ + odg = MEM_callocN (sizeof(bDeformGroup), "join deformGroup"); + memcpy (odg, dg, sizeof(bDeformGroup)); + BLI_addtail(&ob->defbase, odg); + } + + } + if (ob->defbase.first && ob->actdef==0) + ob->actdef=1; + + if(me->totvert) { + for(a=1; a<=base->object->totcol; a++) { + ma= give_current_material(base->object, a); + if(ma) { + for(b=0; b<totcol; b++) { + if(ma == matar[b]) break; + } + if(b==totcol) { + matar[b]= ma; + ma->id.us++; + totcol++; + } + if(totcol>=MAXMAT-1) break; + } + } + } + } + if(totcol>=MAXMAT-1) break; + } + base= base->next; + } + + me= ob->data; + + memset(&vdata, 0, sizeof(vdata)); + memset(&edata, 0, sizeof(edata)); + memset(&fdata, 0, sizeof(fdata)); + + mvert= CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + medge= CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); + mface= CustomData_add_layer(&fdata, CD_MFACE, CD_CALLOC, NULL, totface); + + mvertmain= mvert; + medgemain= medge; + mfacemain= mface; + + /* inverse transorm all selected meshes in this object */ + Mat4Invert(imat, ob->obmat); + + vertofs= 0; + edgeofs= 0; + faceofs= 0; + base= FIRSTBASE; + while(base) { + nextb= base->next; + if (TESTBASELIB_BGMODE(base)) { + if(base->object->type==OB_MESH) { + + me= base->object->data; + + if(me->totvert) { + CustomData_merge(&me->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert); + CustomData_copy_data(&me->vdata, &vdata, 0, vertofs, me->totvert); + + dvert= CustomData_get(&vdata, vertofs, CD_MDEFORMVERT); + + /* NEW VERSION */ + if (dvert){ + for (i=0; i<me->totvert; i++){ + for (j=0; j<dvert[i].totweight; j++){ + // Find the old vertex group + odg = BLI_findlink (&base->object->defbase, dvert[i].dw[j].def_nr); + if(odg) { + // Search for a match in the new object + for (dg=ob->defbase.first, index=0; dg; dg=dg->next, index++){ + if (!strcmp(dg->name, odg->name)){ + dvert[i].dw[j].def_nr = index; + break; + } + } + } + } + } + } + + if(base->object != ob) { + /* watch this: switch matmul order really goes wrong */ + Mat4MulMat4(cmat, base->object->obmat, imat); + + a= me->totvert; + while(a--) { + Mat4MulVecfl(cmat, mvert->co); + mvert++; + } + } + else mvert+= me->totvert; + } + if(me->totface) { + + /* make mapping for materials */ + memset(map, 0, 4*MAXMAT); + for(a=1; a<=base->object->totcol; a++) { + ma= give_current_material(base->object, a); + if(ma) { + for(b=0; b<totcol; b++) { + if(ma == matar[b]) { + map[a-1]= b; + break; + } + } + } + } + + CustomData_merge(&me->fdata, &fdata, CD_MASK_MESH, CD_DEFAULT, totface); + CustomData_copy_data(&me->fdata, &fdata, 0, faceofs, me->totface); + + for(a=0; a<me->totface; a++, mface++) { + mface->v1+= vertofs; + mface->v2+= vertofs; + mface->v3+= vertofs; + if(mface->v4) mface->v4+= vertofs; + + mface->mat_nr= map[(int)mface->mat_nr]; + } + + faceofs += me->totface; + } + + if(me->totedge) { + CustomData_merge(&me->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge); + CustomData_copy_data(&me->edata, &edata, 0, edgeofs, me->totedge); + + for(a=0; a<me->totedge; a++, medge++) { + medge->v1+= vertofs; + medge->v2+= vertofs; + } + + edgeofs += me->totedge; + } + + vertofs += me->totvert; + + if(base->object!=ob) + ED_base_object_free_and_unlink(scene, base); + } + } + base= nextb; + } + + me= ob->data; + + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + + me->totvert= totvert; + me->totedge= totedge; + me->totface= totface; + + me->vdata= vdata; + me->edata= edata; + me->fdata= fdata; + + mesh_update_customdata_pointers(me); + + /* old material array */ + for(a=1; a<=ob->totcol; a++) { + ma= ob->mat[a-1]; + if(ma) ma->id.us--; + } + for(a=1; a<=me->totcol; a++) { + ma= me->mat[a-1]; + if(ma) ma->id.us--; + } + if(ob->mat) MEM_freeN(ob->mat); + if(me->mat) MEM_freeN(me->mat); + ob->mat= me->mat= 0; + + if(totcol) { + me->mat= matar; + ob->mat= MEM_callocN(sizeof(void *)*totcol, "join obmatar"); + } + else MEM_freeN(matar); + + ob->totcol= me->totcol= totcol; + ob->colbits= 0; + + /* other mesh users */ + test_object_materials((ID *)me); + + DAG_scene_sort(scene); // removed objects, need to rebuild dag before editmode call + +// XXX enter_editmode(EM_WAITCURSOR); +// exit_editmode(EM_FREEDATA|EM_WAITCURSOR); // freedata, but no undo + + BIF_undo_push("Join Mesh"); + return 1; +} + + +/* ********************** SORT FACES ******************* */ + +static void permutate(void *list, int num, int size, int *index) +{ + void *buf; + int len; + int i; + + len = num * size; + + buf = MEM_mallocN(len, "permutate"); + memcpy(buf, list, len); + + for (i = 0; i < num; i++) { + memcpy((char *)list + (i * size), (char *)buf + (index[i] * size), size); + } + MEM_freeN(buf); +} + +/* sort faces on view axis */ +static float *face_sort_floats; +static int float_sort(const void *v1, const void *v2) +{ + float x1, x2; + + x1 = face_sort_floats[((int *) v1)[0]]; + x2 = face_sort_floats[((int *) v2)[0]]; + + if( x1 > x2 ) return 1; + else if( x1 < x2 ) return -1; + return 0; +} + + +void sort_faces(Scene *scene, View3D *v3d) +{ + Object *ob= OBACT; + Mesh *me; + CustomDataLayer *layer; + int i, *index; + short event; + float reverse = 1; + int ctrl= 0; // XXX + + if(!ob) return; + if(G.obedit) return; + if(ob->type!=OB_MESH) return; + if (!v3d) return; + + me= ob->data; + if(me->totface==0) return; + + event = pupmenu( + "Sort Faces (Ctrl to reverse)%t|" + "View Axis%x1|" + "Cursor Distance%x2|" + "Material%x3|" + "Selection%x4|" + "Randomize%x5"); + + if (event==-1) return; + + if(ctrl) + reverse = -1; + +/* create index list */ + index = (int *) MEM_mallocN(sizeof(int) * me->totface, "sort faces"); + for (i = 0; i < me->totface; i++) { + index[i] = i; + } + + face_sort_floats = (float *) MEM_mallocN(sizeof(float) * me->totface, "sort faces float"); + +/* sort index list instead of faces itself + and apply this permutation to all face layers */ + + if (event == 5) { + /* Random */ + for(i=0; i<me->totface; i++) { + face_sort_floats[i] = BLI_frand(); + } + qsort(index, me->totface, sizeof(int), float_sort); + } else { + MFace *mf; + float vec[3]; + float mat[4][4]; + float cur[3]; + + if (event == 1) + Mat4MulMat4(mat, OBACT->obmat, v3d->viewmat); /* apply the view matrix to the object matrix */ + else if (event == 2) { /* sort from cursor */ + if( v3d && v3d->localview ) { + VECCOPY(cur, v3d->cursor); + } else { + VECCOPY(cur, scene->cursor); + } + Mat4Invert(mat, OBACT->obmat); + Mat4MulVecfl(mat, cur); + } + + mf= me->mface; + for(i=0; i<me->totface; i++, mf++) { + + if (event==3) { + face_sort_floats[i] = ((float)mf->mat_nr)*reverse; + } else if (event==4) { + /*selected first*/ + if (mf->flag & ME_FACE_SEL) face_sort_floats[i] = 0.0; + else face_sort_floats[i] = reverse; + } else { + /* find the faces center */ + VECADD(vec, (me->mvert+mf->v1)->co, (me->mvert+mf->v2)->co); + if (mf->v4) { + VECADD(vec, vec, (me->mvert+mf->v3)->co); + VECADD(vec, vec, (me->mvert+mf->v4)->co); + VECMUL(vec, 0.25f); + } else { + VECADD(vec, vec, (me->mvert+mf->v3)->co); + VECMUL(vec, 1.0f/3.0f); + } /* done */ + + if (event == 1) { /* sort on view axis */ + Mat4MulVecfl(mat, vec); + face_sort_floats[i] = vec[2] * reverse; + } else { /* distance from cursor*/ + face_sort_floats[i] = VecLenf(cur, vec) * reverse; /* back to front */ + } + } + } + qsort(index, me->totface, sizeof(int), float_sort); + } + + MEM_freeN(face_sort_floats); + + for(i = 0; i < me->fdata.totlayer; i++) { + layer = &me->fdata.layers[i]; + permutate(layer->data, me->totface, CustomData_sizeof(layer->type), index); + } + + MEM_freeN(index); + + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); +} + + + +/* ********************* MESH VERTEX OCTREE LOOKUP ************* */ + +/* important note; this is unfinished, needs better API for editmode, and custom threshold */ + +#define MOC_RES 8 +#define MOC_NODE_RES 8 +#define MOC_THRESH 0.0002f + +typedef struct MocNode { + struct MocNode *next; + intptr_t index[MOC_NODE_RES]; +} MocNode; + +static int mesh_octree_get_base_offs(float *co, float *offs, float *div) +{ + int vx, vy, vz; + + vx= floor( (co[0]-offs[0])/div[0] ); + vy= floor( (co[1]-offs[1])/div[1] ); + vz= floor( (co[2]-offs[2])/div[2] ); + + CLAMP(vx, 0, MOC_RES-1); + CLAMP(vy, 0, MOC_RES-1); + CLAMP(vz, 0, MOC_RES-1); + + return (vx*MOC_RES*MOC_RES) + vy*MOC_RES + vz; +} + +static void mesh_octree_add_node(MocNode **bt, intptr_t index) +{ + if(*bt==NULL) { + *bt= MEM_callocN(sizeof(MocNode), "MocNode"); + (*bt)->index[0]= index; + } + else { + int a; + for(a=0; a<MOC_NODE_RES; a++) { + if((*bt)->index[a]==index) + return; + else if((*bt)->index[a]==0) { + (*bt)->index[a]= index; + return; + } + } + mesh_octree_add_node(&(*bt)->next, index); + } +} + +static void mesh_octree_free_node(MocNode **bt) +{ + if( (*bt)->next ) { + mesh_octree_free_node(&(*bt)->next); + } + MEM_freeN(*bt); +} + + +/* temporal define, just to make nicer code below */ +#define MOC_ADDNODE(vx, vy, vz) mesh_octree_add_node(basetable + ((vx)*MOC_RES*MOC_RES) + (vy)*MOC_RES + (vz), index) + +static void mesh_octree_add_nodes(MocNode **basetable, float *co, float *offs, float *div, intptr_t index) +{ + float fx, fy, fz; + int vx, vy, vz; + + if (isnan(co[0]) || !finite(co[0]) || + isnan(co[1]) || !finite(co[1]) || + isnan(co[2]) || !finite(co[2]) + ) { + return; + } + + fx= (co[0]-offs[0])/div[0]; + fy= (co[1]-offs[1])/div[1]; + fz= (co[2]-offs[2])/div[2]; + CLAMP(fx, 0.0f, MOC_RES-MOC_THRESH); + CLAMP(fy, 0.0f, MOC_RES-MOC_THRESH); + CLAMP(fz, 0.0f, MOC_RES-MOC_THRESH); + + vx= floor(fx); + vy= floor(fy); + vz= floor(fz); + + MOC_ADDNODE(vx, vy, vz); + + if( vx>0 ) + if( fx-((float)vx)-MOC_THRESH < 0.0f) + MOC_ADDNODE(vx-1, vy, vz); + if( vx<MOC_RES-2 ) + if( fx-((float)vx)+MOC_THRESH > 1.0f) + MOC_ADDNODE(vx+1, vy, vz); + + if( vy>0 ) + if( fy-((float)vy)-MOC_THRESH < 0.0f) + MOC_ADDNODE(vx, vy-1, vz); + if( vy<MOC_RES-2 ) + if( fy-((float)vy)+MOC_THRESH > 1.0f) + MOC_ADDNODE(vx, vy+1, vz); + + if( vz>0 ) + if( fz-((float)vz)-MOC_THRESH < 0.0f) + MOC_ADDNODE(vx, vy, vz-1); + if( vz<MOC_RES-2 ) + if( fz-((float)vz)+MOC_THRESH > 1.0f) + MOC_ADDNODE(vx, vy, vz+1); + +} + +static intptr_t mesh_octree_find_index(MocNode **bt, float (*orco)[3], MVert *mvert, float *co) +{ + float *vec; + int a; + + if(*bt==NULL) + return -1; + + for(a=0; a<MOC_NODE_RES; a++) { + if((*bt)->index[a]) { + /* does mesh verts and editmode, code looks potential dangerous, octree should really be filled OK! */ + if(orco) { + vec= orco[(*bt)->index[a]-1]; + if(FloatCompare(vec, co, MOC_THRESH)) + return (*bt)->index[a]-1; + } + else if(mvert) { + vec= (mvert+(*bt)->index[a]-1)->co; + if(FloatCompare(vec, co, MOC_THRESH)) + return (*bt)->index[a]-1; + } + else { + EditVert *eve= (EditVert *)((*bt)->index[a]); + if(FloatCompare(eve->co, co, MOC_THRESH)) + return (*bt)->index[a]; + } + } + else return -1; + } + if( (*bt)->next) + return mesh_octree_find_index(&(*bt)->next, orco, mvert, co); + + return -1; +} + +static struct { + MocNode **table; + float offs[3], div[3]; + float (*orco)[3]; + float orcoloc[3]; +} MeshOctree = {NULL, {0, 0, 0}, {0, 0, 0}, NULL}; + +/* mode is 's' start, or 'e' end, or 'u' use */ +/* if end, ob can be NULL */ +intptr_t mesh_octree_table(Object *ob, EditMesh *em, float *co, char mode) +{ + MocNode **bt; + + if(mode=='u') { /* use table */ + if(MeshOctree.table==NULL) + mesh_octree_table(ob, em, NULL, 's'); + + if(MeshOctree.table) { + Mesh *me= ob->data; + bt= MeshOctree.table + mesh_octree_get_base_offs(co, MeshOctree.offs, MeshOctree.div); + if(ob==G.obedit) + return mesh_octree_find_index(bt, NULL, NULL, co); + else + return mesh_octree_find_index(bt, MeshOctree.orco, me->mvert, co); + } + return -1; + } + else if(mode=='s') { /* start table */ + Mesh *me= ob->data; + float min[3], max[3]; + + /* we compute own bounding box and don't reuse ob->bb because + * we are using the undeformed coordinates*/ + INIT_MINMAX(min, max); + + if(ob==G.obedit) { + EditVert *eve; + + for(eve= em->verts.first; eve; eve= eve->next) + DO_MINMAX(eve->co, min, max) + } + else { + MVert *mvert; + float *vco; + int a, totvert; + + MeshOctree.orco= mesh_getRefKeyCos(me, &totvert); + mesh_get_texspace(me, MeshOctree.orcoloc, NULL, NULL); + + for(a=0, mvert= me->mvert; a<me->totvert; a++, mvert++) { + vco= (MeshOctree.orco)? MeshOctree.orco[a]: mvert->co; + DO_MINMAX(vco, min, max); + } + } + + /* for quick unit coordinate calculus */ + VECCOPY(MeshOctree.offs, min); + MeshOctree.offs[0]-= MOC_THRESH; /* we offset it 1 threshold unit extra */ + MeshOctree.offs[1]-= MOC_THRESH; + MeshOctree.offs[2]-= MOC_THRESH; + + VecSubf(MeshOctree.div, max, min); + MeshOctree.div[0]+= 2*MOC_THRESH; /* and divide with 2 threshold unit more extra (try 8x8 unit grid on paint) */ + MeshOctree.div[1]+= 2*MOC_THRESH; + MeshOctree.div[2]+= 2*MOC_THRESH; + + VecMulf(MeshOctree.div, 1.0f/MOC_RES); + if(MeshOctree.div[0]==0.0f) MeshOctree.div[0]= 1.0f; + if(MeshOctree.div[1]==0.0f) MeshOctree.div[1]= 1.0f; + if(MeshOctree.div[2]==0.0f) MeshOctree.div[2]= 1.0f; + + if(MeshOctree.table) /* happens when entering this call without ending it */ + mesh_octree_table(ob, em, co, 'e'); + + MeshOctree.table= MEM_callocN(MOC_RES*MOC_RES*MOC_RES*sizeof(void *), "sym table"); + + if(ob==G.obedit) { + EditVert *eve; + + for(eve= em->verts.first; eve; eve= eve->next) { + mesh_octree_add_nodes(MeshOctree.table, eve->co, MeshOctree.offs, MeshOctree.div, (intptr_t)(eve)); + } + } + else { + MVert *mvert; + float *vco; + int a; + + for(a=0, mvert= me->mvert; a<me->totvert; a++, mvert++) { + vco= (MeshOctree.orco)? MeshOctree.orco[a]: mvert->co; + mesh_octree_add_nodes(MeshOctree.table, vco, MeshOctree.offs, MeshOctree.div, a+1); + } + } + } + else if(mode=='e') { /* end table */ + if(MeshOctree.table) { + int a; + + for(a=0, bt=MeshOctree.table; a<MOC_RES*MOC_RES*MOC_RES; a++, bt++) { + if(*bt) mesh_octree_free_node(bt); + } + MEM_freeN(MeshOctree.table); + MeshOctree.table= NULL; + } + if(MeshOctree.orco) { + MEM_freeN(MeshOctree.orco); + MeshOctree.orco= NULL; + } + } + return 0; +} + +int mesh_get_x_mirror_vert(Object *ob, int index) +{ + Mesh *me= ob->data; + MVert *mvert; + float vec[3]; + + if(MeshOctree.orco) { + float *loc= MeshOctree.orcoloc; + + vec[0]= -(MeshOctree.orco[index][0] + loc[0]) - loc[0]; + vec[1]= MeshOctree.orco[index][1]; + vec[2]= MeshOctree.orco[index][2]; + } + else { + mvert= me->mvert+index; + vec[0]= -mvert->co[0]; + vec[1]= mvert->co[1]; + vec[2]= mvert->co[2]; + } + + return mesh_octree_table(ob, NULL, vec, 'u'); +} + +EditVert *editmesh_get_x_mirror_vert(Object *ob, EditMesh *em, float *co) +{ + float vec[3]; + intptr_t poinval; + + /* ignore nan verts */ + if (isnan(co[0]) || !finite(co[0]) || + isnan(co[1]) || !finite(co[1]) || + isnan(co[2]) || !finite(co[2]) + ) + return NULL; + + vec[0]= -co[0]; + vec[1]= co[1]; + vec[2]= co[2]; + + poinval= mesh_octree_table(ob, em, vec, 'u'); + if(poinval != -1) + return (EditVert *)(poinval); + return NULL; +} + +static unsigned int mirror_facehash(void *ptr) +{ + MFace *mf= ptr; + int v0, v1; + + if(mf->v4) { + v0= MIN4(mf->v1, mf->v2, mf->v3, mf->v4); + v1= MAX4(mf->v1, mf->v2, mf->v3, mf->v4); + } + else { + v0= MIN3(mf->v1, mf->v2, mf->v3); + v1= MAX3(mf->v1, mf->v2, mf->v3); + } + + return ((v0*39)^(v1*31)); +} + +static int mirror_facerotation(MFace *a, MFace *b) +{ + if(b->v4) { + if(a->v1==b->v1 && a->v2==b->v2 && a->v3==b->v3 && a->v4==b->v4) + return 0; + else if(a->v4==b->v1 && a->v1==b->v2 && a->v2==b->v3 && a->v3==b->v4) + return 1; + else if(a->v3==b->v1 && a->v4==b->v2 && a->v1==b->v3 && a->v2==b->v4) + return 2; + else if(a->v2==b->v1 && a->v3==b->v2 && a->v4==b->v3 && a->v1==b->v4) + return 3; + } + else { + if(a->v1==b->v1 && a->v2==b->v2 && a->v3==b->v3) + return 0; + else if(a->v3==b->v1 && a->v1==b->v2 && a->v2==b->v3) + return 1; + else if(a->v2==b->v1 && a->v3==b->v2 && a->v1==b->v3) + return 2; + } + + return -1; +} + +static int mirror_facecmp(void *a, void *b) +{ + return (mirror_facerotation((MFace*)a, (MFace*)b) == -1); +} + +int *mesh_get_x_mirror_faces(Object *ob, EditMesh *em) +{ + Mesh *me= ob->data; + MVert *mv, *mvert= me->mvert; + MFace mirrormf, *mf, *hashmf, *mface= me->mface; + GHash *fhash; + int *mirrorverts, *mirrorfaces; + int a; + + mirrorverts= MEM_callocN(sizeof(int)*me->totvert, "MirrorVerts"); + mirrorfaces= MEM_callocN(sizeof(int)*2*me->totface, "MirrorFaces"); + + mesh_octree_table(ob, em, NULL, 's'); + + for(a=0, mv=mvert; a<me->totvert; a++, mv++) + mirrorverts[a]= mesh_get_x_mirror_vert(ob, a); + + mesh_octree_table(ob, em, NULL, 'e'); + + fhash= BLI_ghash_new(mirror_facehash, mirror_facecmp); + for(a=0, mf=mface; a<me->totface; a++, mf++) + BLI_ghash_insert(fhash, mf, mf); + + for(a=0, mf=mface; a<me->totface; a++, mf++) { + mirrormf.v1= mirrorverts[mf->v3]; + mirrormf.v2= mirrorverts[mf->v2]; + mirrormf.v3= mirrorverts[mf->v1]; + mirrormf.v4= (mf->v4)? mirrorverts[mf->v4]: 0; + + /* make sure v4 is not 0 if a quad */ + if(mf->v4 && mirrormf.v4==0) { + SWAP(int, mirrormf.v1, mirrormf.v3); + SWAP(int, mirrormf.v2, mirrormf.v4); + } + + hashmf= BLI_ghash_lookup(fhash, &mirrormf); + if(hashmf) { + mirrorfaces[a*2]= hashmf - mface; + mirrorfaces[a*2+1]= mirror_facerotation(&mirrormf, hashmf); + } + else + mirrorfaces[a*2]= -1; + } + + BLI_ghash_free(fhash, NULL, NULL); + MEM_freeN(mirrorverts); + + return mirrorfaces; +} + +/* ****************** render BAKING ********************** */ + +/* threaded break test */ +static int thread_break(void) +{ + return G.afbreek; +} + +static ScrArea *biggest_image_area(void) +{ + ScrArea *sa, *big= NULL; + int size, maxsize= 0; + + for(sa= G.curscreen->areabase.first; sa; sa= sa->next) { + if(sa->spacetype==SPACE_IMAGE) { + size= sa->winx*sa->winy; + if(sa->winx > 10 && sa->winy > 10 && size > maxsize) { + maxsize= size; + big= sa; + } + } + } + return big; +} + + +typedef struct BakeRender { + Render *re; + struct Object *actob; + int event, tot, ready; +} BakeRender; + +static void *do_bake_render(void *bake_v) +{ + BakeRender *bkr= bake_v; + + bkr->tot= RE_bake_shade_all_selected(bkr->re, bkr->event, bkr->actob); + bkr->ready= 1; + + return NULL; +} + + +void objects_bake_render(Scene *scene, short event, char **error_msg) +{ + Object *actob= OBACT; + int active= scene->r.bake_flag & R_BAKE_TO_ACTIVE; + short prev_r_raytrace= 0, prev_wo_amb_occ= 0; + + if(event==0) event= scene->r.bake_mode; + + if(scene->r.renderer!=R_INTERN) { + *error_msg = "Bake only supported for Internal Renderer"; + return; + } + + if(active && !actob) { + *error_msg = "No active object"; + return; + } + + if(event>0) { + Render *re= RE_NewRender("_Bake View_"); + ScrArea *area= biggest_image_area(); + ListBase threads; + BakeRender bkr; + int timer=0, tot; // XXX, sculptmode= G.f & G_SCULPTMODE; + +// XXX if(sculptmode) set_sculptmode(); + + if(event==1) event= RE_BAKE_ALL; + else if(event==2) event= RE_BAKE_AO; + else if(event==3) event= RE_BAKE_NORMALS; + else if(event==4) event= RE_BAKE_TEXTURE; + else if(event==5) event= RE_BAKE_DISPLACEMENT; + else event= RE_BAKE_SHADOW; + + if(event==RE_BAKE_AO) { + if(scene->world==NULL) { + *error_msg = "No world set up"; + return; + } + + /* If raytracing or AO is disabled, switch it on temporarily for baking. */ + prev_wo_amb_occ = (scene->world->mode & WO_AMB_OCC) != 0; + scene->world->mode |= WO_AMB_OCC; + } + if(event==RE_BAKE_AO || active) { + prev_r_raytrace = (scene->r.mode & R_RAYTRACE) != 0; + scene->r.mode |= R_RAYTRACE; + } + + waitcursor(1); + RE_test_break_cb(re, thread_break); + G.afbreek= 0; /* blender_test_break uses this global */ + + RE_Database_Baking(re, scene, event, (active)? actob: NULL); + + /* baking itself is threaded, cannot use test_break in threads. we also update optional imagewindow */ + + BLI_init_threads(&threads, do_bake_render, 1); + bkr.re= re; + bkr.event= event; + bkr.ready= 0; + bkr.actob= (active)? actob: NULL; + BLI_insert_thread(&threads, &bkr); + + while(bkr.ready==0) { + PIL_sleep_ms(50); + if(bkr.ready) + break; + + if (!G.background) { + blender_test_break(); + + timer++; + if(area && timer==20) { + Image *ima= RE_bake_shade_get_image(); + if(ima) ((SpaceImage *)area->spacedata.first)->image= ima; +// XX scrarea_do_windraw(area); +// myswapbuffers(); + timer= 0; + } + } + } + BLI_end_threads(&threads); + tot= bkr.tot; + + RE_Database_Free(re); + waitcursor(0); + + if(tot==0) *error_msg = "No Images found to bake to"; + else { + Image *ima; + /* force OpenGL reload and mipmap recalc */ + for(ima= G.main->image.first; ima; ima= ima->id.next) { + if(ima->ok==IMA_OK_LOADED) { + ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL); + if(ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) { + GPU_free_image(ima); + imb_freemipmapImBuf(ibuf); + } + } + } + } + + /* restore raytrace and AO */ + if(event==RE_BAKE_AO) + if(prev_wo_amb_occ == 0) + scene->world->mode &= ~WO_AMB_OCC; + + if(event==RE_BAKE_AO || active) + if(prev_r_raytrace == 0) + scene->r.mode &= ~R_RAYTRACE; + +// XXX if(sculptmode) set_sculptmode(); + + } +} + +/* all selected meshes with UV maps are rendered for current scene visibility */ +static void objects_bake_render_ui(Scene *scene, short event) +{ + char *error_msg = NULL; +// int is_editmode = (G.obedit!=NULL); + + /* Deal with editmode, this is a bit clunky but since UV's are in editmode, users are likely to bake from their */ +// XXX if (is_editmode) exit_editmode(0); + + objects_bake_render(scene, event, &error_msg); + +// XXX if (is_editmode) enter_editmode(0); + + if (error_msg) + error(error_msg); +} + +void objects_bake_render_menu(Scene *scene) +{ + short event; + + event= pupmenu("Bake Selected Meshes %t|Full Render %x1|Ambient Occlusion %x2|Normals %x3|Texture Only %x4|Displacement %x5|Shadow %x6"); + if (event < 1) return; + objects_bake_render_ui(scene, event); +} + diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index a2f91cf2788..1250e7c1bed 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -293,21 +293,14 @@ void add_objectLamp(Scene *scene, View3D *v3d, short type) /* remove base from a specific scene */ /* note: now unlinks constraints as well */ -void free_and_unlink_base_from_scene(Scene *scene, Base *base) +void ED_base_object_free_and_unlink(Scene *scene, Base *base) { BLI_remlink(&scene->base, base); free_libblock_us(&G.main->object, base->object); + if(scene->basact==base) scene->basact= NULL; MEM_freeN(base); } -/* remove base from the current scene */ -void free_and_unlink_base(Scene *scene, Base *base) -{ - if (base==BASACT) - BASACT= NULL; - free_and_unlink_base_from_scene(scene, base); -} - void delete_obj(Scene *scene, View3D *v3d, int ok) { Base *base, *nbase; @@ -344,15 +337,14 @@ void delete_obj(Scene *scene, View3D *v3d, int ok) if (scene != scene && !(scene->id.lib)) { base_other= object_in_scene( base->object, scene ); if (base_other) { - if (base_other == scene->basact) scene->basact= NULL; /* in case the object was active */ - free_and_unlink_base_from_scene( scene, base_other ); + ED_base_object_free_and_unlink( scene, base_other ); } } } } /* remove from current scene only */ - free_and_unlink_base(scene, base); + ED_base_object_free_and_unlink(scene, base); } } @@ -3313,9 +3305,7 @@ void convertmenu(Scene *scene, View3D *v3d) } } if(basedel != NULL && nr == 2) { - if(basedel==basact) - basact= NULL; - free_and_unlink_base(scene, basedel); + ED_base_object_free_and_unlink(scene, basedel); } basedel = NULL; } diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index bbe89afd411..c0670b3dc1e 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -72,6 +72,8 @@ #include "GPU_extensions.h" #include "GPU_draw.h" +#include "ED_mesh.h" + #include "view3d_intern.h" // own include /***/ @@ -424,7 +426,7 @@ static int draw_tface_mapped__set_draw(void *userData, int index) static int draw_em_tf_mapped__set_draw(void *userData, int index) { EditMesh *em = userData; - EditFace *efa= NULL; // XXX = EM_get_face_for_index(index); + EditFace *efa= EM_get_face_for_index(index); MTFace *tface; MCol *mcol; int matnr; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 4e042d8ede5..60497d52666 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -101,6 +101,7 @@ #include "GPU_material.h" #include "GPU_extensions.h" +#include "ED_mesh.h" #include "ED_types.h" #include "ED_util.h" @@ -126,16 +127,6 @@ static void drawcircle_size(float size); static void draw_empty_sphere(float size); static void draw_empty_cone(float size); -EditVert *EM_get_vert_for_index(int x) {return 0;} // XXX -EditEdge *EM_get_edge_for_index(int x) {return 0;} // XXX -EditFace *EM_get_face_for_index(int x) {return 0;} // XXX -void EM_init_index_arrays(int x, int y, int z) {} // XXX -void EM_free_index_arrays(void) {} // XXX -#define EM_FGON 0 -EditFace *EM_get_actFace(int x) {return NULL;} // XXX - -extern unsigned int em_vertoffs, em_solidoffs, em_wireoffs; - /* check for glsl drawing */ int draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, int dt) @@ -1219,7 +1210,8 @@ void mesh_foreachScreenVert(ARegion *ar, View3D *v3d, void (*func)(void *userDat { struct { void (*func)(void *userData, EditVert *eve, int x, int y, int index); void *userData; ARegion *ar; View3D *v3d; int clipVerts; float pmat[4][4], vmat[4][4]; } data; DerivedMesh *dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); - + EditMesh *em= NULL; // XXX + data.func = func; data.userData = userData; data.ar= ar; @@ -1228,7 +1220,7 @@ void mesh_foreachScreenVert(ARegion *ar, View3D *v3d, void (*func)(void *userDat view3d_get_object_project_mat(v3d, G.obedit, data.pmat, data.vmat); - EM_init_index_arrays(1, 0, 0); + EM_init_index_arrays(em, 1, 0, 0); dm->foreachMappedVert(dm, mesh_foreachScreenVert__mapFunc, &data); EM_free_index_arrays(); @@ -1264,6 +1256,7 @@ void mesh_foreachScreenEdge(ARegion *ar, View3D *v3d, void (*func)(void *userDat { struct { void (*func)(void *userData, EditEdge *eed, int x0, int y0, int x1, int y1, int index); void *userData; ARegion *ar; View3D *v3d; int clipVerts; float pmat[4][4], vmat[4][4]; } data; DerivedMesh *dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); + EditMesh *em= NULL; // XXX data.func = func; data.ar= ar; @@ -1273,7 +1266,7 @@ void mesh_foreachScreenEdge(ARegion *ar, View3D *v3d, void (*func)(void *userDat view3d_get_object_project_mat(v3d, G.obedit, data.pmat, data.vmat); - EM_init_index_arrays(0, 1, 0); + EM_init_index_arrays(em, 0, 1, 0); dm->foreachMappedEdge(dm, mesh_foreachScreenEdge__mapFunc, &data); EM_free_index_arrays(); @@ -1297,6 +1290,7 @@ void mesh_foreachScreenFace(ARegion *ar, View3D *v3d, void (*func)(void *userDat { struct { void (*func)(void *userData, EditFace *efa, int x, int y, int index); void *userData; ARegion *ar; View3D *v3d; float pmat[4][4], vmat[4][4]; } data; DerivedMesh *dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); + EditMesh *em= NULL; // XXX data.func = func; data.ar= ar; @@ -1305,7 +1299,7 @@ void mesh_foreachScreenFace(ARegion *ar, View3D *v3d, void (*func)(void *userDat view3d_get_object_project_mat(v3d, G.obedit, data.pmat, data.vmat); - EM_init_index_arrays(0, 0, 1); + EM_init_index_arrays(em, 0, 0, 1); dm->foreachMappedFaceCenter(dm, mesh_foreachScreenFace__mapFunc, &data); EM_free_index_arrays(); @@ -1965,7 +1959,7 @@ static int draw_em_fancy__setGLSLFaceOpts(void *userData, int index) static void draw_em_fancy(Scene *scene, View3D *v3d, Object *ob, EditMesh *em, DerivedMesh *cageDM, DerivedMesh *finalDM, int dt) { Mesh *me = ob->data; - EditFace *efa_act = EM_get_actFace(0); /* annoying but active faces is stored differently */ + EditFace *efa_act = EM_get_actFace(em, 0); /* annoying but active faces is stored differently */ EditEdge *eed_act = NULL; EditVert *eve_act = NULL; @@ -1981,7 +1975,7 @@ static void draw_em_fancy(Scene *scene, View3D *v3d, Object *ob, EditMesh *em, D } } - EM_init_index_arrays(1, 1, 1); + EM_init_index_arrays(em, 1, 1, 1); if(dt>OB_WIRE) { if(CHECK_OB_DRAWTEXTURE(v3d, dt)) { @@ -5387,9 +5381,10 @@ void draw_object_backbufsel(Scene *scene, View3D *v3d, Object *ob) switch( ob->type) { case OB_MESH: if(ob==G.obedit) { + EditMesh *em= NULL; // XXX DerivedMesh *dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); - EM_init_index_arrays(1, 1, 1); + EM_init_index_arrays(em, 1, 1, 1); em_solidoffs= bbs_mesh_solid_EM(scene, v3d, dm, scene->selectmode & SCE_SELECT_FACE); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3b3eba41ce8..e4dc82c30a7 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -75,6 +75,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_types.h" @@ -90,19 +91,7 @@ /* ********************** view3d_select: selection manipulations ********************* */ /* XXX to solve *************** */ -unsigned int em_vertoffs=0, em_solidoffs=0, em_wireoffs=0; -int EM_check_backbuf() {return 0;} -void EM_select_edge() {} -void EM_select_face_fgon() {} -int EM_mask_init_backbuf_border() {return 0;} -void EM_free_backbuf() {} -void EM_selectmode_flush() {} -void EM_deselect_flush() {} static void BIF_undo_push() {} -int EM_texFaceCheck() {return 0;} -int EM_init_backbuf_border() {return 0;} -int EM_init_backbuf_circle() {return 0;} - /* XXX end ********************* */ /* local prototypes */ @@ -143,7 +132,7 @@ void EM_backbuf_checkAndSelectFaces(EditMesh *em, int select) for(efa= em->faces.first; efa; efa= efa->next, index++) { if(efa->h==0) { if(EM_check_backbuf(index)) { - EM_select_face_fgon(efa, select); + EM_select_face_fgon(em, efa, select); } } } @@ -386,16 +375,17 @@ static void do_lasso_select_mesh__doSelectEdge(void *userData, EditEdge *eed, in static void do_lasso_select_mesh__doSelectFace(void *userData, EditFace *efa, int x, int y, int index) { struct { rcti *rect; short (*mcords)[2], moves, select, pass, done; } *data = userData; + EditMesh *em= NULL; // XXX if (BLI_in_rcti(data->rect, x, y) && lasso_inside(data->mcords, data->moves, x, y)) { - EM_select_face_fgon(efa, data->select); + EM_select_face_fgon(em, efa, data->select); } } static void do_lasso_select_mesh(Scene *scene, ARegion *ar, View3D *v3d, short mcords[][2], short moves, short select) { struct { rcti *rect; short (*mcords)[2], moves, select, pass, done; } data; - EditMesh *em = G.editMesh; + EditMesh *em = G.editMesh; // XXX rcti rect; int bbsel; @@ -408,7 +398,7 @@ static void do_lasso_select_mesh(Scene *scene, ARegion *ar, View3D *v3d, short m data.done = 0; data.pass = 0; - bbsel= EM_mask_init_backbuf_border(mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + bbsel= EM_mask_init_backbuf_border(v3d, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); if(scene->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { @@ -438,7 +428,7 @@ static void do_lasso_select_mesh(Scene *scene, ARegion *ar, View3D *v3d, short m } EM_free_backbuf(); - EM_selectmode_flush(); + EM_selectmode_flush(em); } #if 0 @@ -494,7 +484,7 @@ static void do_lasso_select_mesh_uv(short mcords[][2], short moves, short select } if (ok && G.sima->flag & SI_SYNC_UVSEL) { if (select) EM_select_flush(); - else EM_deselect_flush(); + else EM_deselect_flush(em); } } #endif @@ -588,7 +578,7 @@ static void do_lasso_select_armature(ARegion *ar, View3D *v3d, short mcords[][2] // XXX countall(); /* abused for flushing selection!!!! */ } -static void do_lasso_select_facemode(Scene *scene, short mcords[][2], short moves, short select) +static void do_lasso_select_facemode(Scene *scene, View3D *v3d, short mcords[][2], short moves, short select) { Object *ob= OBACT; Mesh *me= ob?ob->data:NULL; @@ -600,7 +590,7 @@ static void do_lasso_select_facemode(Scene *scene, short mcords[][2], short move em_vertoffs= me->totface+1; /* max index array */ lasso_select_boundbox(&rect, mcords, moves); - EM_mask_init_backbuf_border(mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + EM_mask_init_backbuf_border(v3d, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); EM_backbuf_checkAndSelectTFaces(me, select); @@ -644,7 +634,7 @@ void view3d_lasso_select(Scene *scene, ARegion *ar, View3D *v3d, short mcords[][ { if(G.obedit==NULL) { if(FACESEL_PAINT_TEST) - do_lasso_select_facemode(scene, mcords, moves, select); + do_lasso_select_facemode(scene, v3d, mcords, moves, select); else if(G.f & (G_VERTEXPAINT|G_TEXTUREPAINT|G_WEIGHTPAINT)) ; // XX else if(G.f & G_PARTICLEEDIT) @@ -1154,9 +1144,10 @@ static void do_mesh_box_select__doSelectEdge(void *userData, EditEdge *eed, int static void do_mesh_box_select__doSelectFace(void *userData, EditFace *efa, int x, int y, int index) { struct { rcti *rect; short select, pass, done; } *data = userData; + EditMesh *em= NULL; // XXX if (BLI_in_rcti(data->rect, x, y)) { - EM_select_face_fgon(efa, data->select); + EM_select_face_fgon(em, efa, data->select); } } static void do_mesh_box_select(Scene *scene, ARegion *ar, View3D *v3d, rcti *rect, int select) @@ -1170,7 +1161,7 @@ static void do_mesh_box_select(Scene *scene, ARegion *ar, View3D *v3d, rcti *rec data.pass = 0; data.done = 0; - bbsel= EM_init_backbuf_border(rect->xmin, rect->ymin, rect->xmax, rect->ymax); + bbsel= EM_init_backbuf_border(v3d, rect->xmin, rect->ymin, rect->xmax, rect->ymax); if(scene->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { @@ -1201,7 +1192,7 @@ static void do_mesh_box_select(Scene *scene, ARegion *ar, View3D *v3d, rcti *rec EM_free_backbuf(); - EM_selectmode_flush(); + EM_selectmode_flush(em); } static int view3d_borderselect_exec(bContext *C, wmOperator *op) @@ -1535,9 +1526,10 @@ static void mesh_selectionCB__doSelectFace(void *userData, EditFace *efa, int x, struct { short select, mval[2]; float radius; } *data = userData; int mx = x - data->mval[0], my = y - data->mval[1]; float r = sqrt(mx*mx + my*my); - + EditMesh *em= NULL; // XXX + if (r<=data->radius) { - EM_select_face_fgon(efa, data->select); + EM_select_face_fgon(em, efa, data->select); } } @@ -1554,7 +1546,7 @@ static void mesh_selectionCB(Scene *scene, ARegion *ar, View3D *v3d, int selecti if (me) { em_vertoffs= me->totface+1; /* max index array */ - bbsel= EM_init_backbuf_circle(mval[0], mval[1], (short)(rad+1.0)); + bbsel= EM_init_backbuf_circle(v3d, mval[0], mval[1], (short)(rad+1.0)); EM_backbuf_checkAndSelectTFaces(me, selecting==LEFTMOUSE); EM_free_backbuf(); @@ -1564,7 +1556,7 @@ static void mesh_selectionCB(Scene *scene, ARegion *ar, View3D *v3d, int selecti return; } - bbsel= EM_init_backbuf_circle(mval[0], mval[1], (short)(rad+1.0)); + bbsel= EM_init_backbuf_circle(v3d, mval[0], mval[1], (short)(rad+1.0)); data.select = (selecting==LEFTMOUSE); data.mval[0] = mval[0]; @@ -1596,7 +1588,7 @@ static void mesh_selectionCB(Scene *scene, ARegion *ar, View3D *v3d, int selecti } EM_free_backbuf(); - EM_selectmode_flush(); + EM_selectmode_flush(em); } |