Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/Makefile4
-rw-r--r--source/blender/blenkernel/BKE_blender.h4
-rw-r--r--source/blender/blenkernel/intern/blender.c21
-rw-r--r--source/blender/blenlib/BLI_editVert.h3
-rw-r--r--source/blender/editors/Makefile2
-rw-r--r--source/blender/editors/include/ED_mesh.h54
-rw-r--r--source/blender/editors/include/ED_multires.h59
-rw-r--r--source/blender/editors/include/ED_object.h4
-rw-r--r--source/blender/editors/mesh/Makefile6
-rw-r--r--source/blender/editors/mesh/SConscript11
-rw-r--r--source/blender/editors/mesh/editface.c1439
-rw-r--r--source/blender/editors/mesh/editmesh.c2190
-rw-r--r--source/blender/editors/mesh/editmesh.h202
-rw-r--r--source/blender/editors/mesh/editmesh_add.c1413
-rw-r--r--source/blender/editors/mesh/editmesh_lib.c2266
-rw-r--r--source/blender/editors/mesh/editmesh_loop.c713
-rw-r--r--source/blender/editors/mesh/editmesh_mods.c4326
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c6319
-rw-r--r--source/blender/editors/mesh/meshtools.c1130
-rw-r--r--source/blender/editors/object/object_edit.c20
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c4
-rw-r--r--source/blender/editors/space_view3d/drawobject.c29
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c50
-rw-r--r--source/blender/makesdna/DNA_scene_types.h3
-rw-r--r--source/blender/windowmanager/WM_api.h1
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c4
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c3
-rw-r--r--source/blender/windowmanager/intern/wm_subwindow.c2
-rw-r--r--source/blender/windowmanager/intern/wm_window.c19
-rw-r--r--source/blender/windowmanager/wm_window.h2
30 files changed, 20235 insertions, 68 deletions
diff --git a/source/Makefile b/source/Makefile
index 0cc4ebcad6e..85fbc364ce1 100644
--- a/source/Makefile
+++ b/source/Makefile
@@ -161,7 +161,6 @@ COMLIB += $(NAN_MEMUTIL)/lib/libmemutil.a
COMLIB += $(NAN_BMFONT)/lib/$(DEBUG_DIR)libbmfont.a
COMLIB += $(NAN_PNG)/lib/libpng.a
COMLIB += $(OCGDIR)/blender/yafray/$(DEBUG_DIR)libyafrayexport.a
-COMLIB += $(OCGDIR)/blender/blenlib/$(DEBUG_DIR)libblenlib.a
ifeq ($(WITH_QUICKTIME), true)
COMLIB += $(OCGDIR)/blender/blenderqt/$(DEBUG_DIR)libblenderqt.a
@@ -252,6 +251,7 @@ PULIB += $(OCGDIR)/blender/ed_time/libed_time.a
PULIB += $(OCGDIR)/blender/ed_view3d/libed_view3d.a
PULIB += $(OCGDIR)/blender/ed_interface/libed_interface.a
PULIB += $(OCGDIR)/blender/ed_object/libed_object.a
+PULIB += $(OCGDIR)/blender/ed_mesh/libed_mesh.a
PULIB += $(OCGDIR)/blender/ed_animation/libed_animation.a
PULIB += $(OCGDIR)/blender/ed_transform/libed_transform.a
PULIB += $(OCGDIR)/blender/ed_util/libed_util.a
@@ -260,6 +260,8 @@ PULIB += $(OCGDIR)/blender/ed_screen/libed_screen.a
PULIB += $(OCGDIR)/blender/windowmanager/libwindowmanager.a
PULIB += $(OCGDIR)/blender/python/$(DEBUG_DIR)libpython.a
PULIB += $(OCGDIR)/blender/makesrna/$(DEBUG_DIR)librna.a
+# note, no idea but it suddenly doesn't compile :(
+PULIB += $(OCGDIR)/blender/blenlib/$(DEBUG_DIR)libblenlib.a
ifeq ($(NAN_NO_KETSJI),true)
PULIB += $(NAN_MOTO)/lib/libmoto.a
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index dbdb570b2f8..5a374802d36 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -55,6 +55,10 @@ int BKE_read_file_from_memfile(struct bContext *C, struct MemFile *memfile, stru
void free_blender(void);
void initglobals(void);
+/* set this callback when a UI is running */
+void set_blender_test_break_cb(void (*func)(void) );
+int blender_test_break(void);
+
void pushdata(void *data, int len);
void popfirst(void *data);
void poplast(void *data);
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index 979e5d008c4..0dd9a34b0c1 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -90,7 +90,6 @@
#include "BKE_utildefines.h" // O_BINARY FALSE
-
Global G;
UserDef U;
ListBase WMlist= {NULL, NULL};
@@ -475,6 +474,26 @@ int BKE_read_file_from_memfile(bContext *C, MemFile *memfile, ReportList *report
return (bfd?1:0);
}
+/* ***************** testing for break ************* */
+
+static void (*blender_test_break_cb)(void)= NULL;
+
+void set_blender_test_break_cb(void (*func)(void) )
+{
+ blender_test_break_cb= func;
+}
+
+
+int blender_test_break(void)
+{
+ if (!G.background) {
+ if (blender_test_break_cb)
+ blender_test_break_cb();
+ }
+
+ return (G.afbreek==1);
+}
+
/* ***************** GLOBAL UNDO *************** */
diff --git a/source/blender/blenlib/BLI_editVert.h b/source/blender/blenlib/BLI_editVert.h
index 447f6a2a485..55be4cfae42 100644
--- a/source/blender/blenlib/BLI_editVert.h
+++ b/source/blender/blenlib/BLI_editVert.h
@@ -172,6 +172,9 @@ typedef struct EditMesh
* never access this directly, use EM_set_actFace and EM_get_actFace */
EditFace *act_face;
+ /* copy from scene */
+ int selectmode;
+
struct DerivedMesh *derivedCage, *derivedFinal;
/* the custom data layer mask that was last used to calculate
* derivedCage and derivedFinal
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);
}
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 1491cdcbeb3..325809dd588 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -721,13 +721,16 @@ typedef struct Scene {
#define MINFRAME 1
#define MINFRAMEF 1.0
+/* depricate this! */
#define TESTBASE(v3d, base) ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0) )
#define TESTBASELIB(v3d, base) ( ((base)->flag & SELECT) && ((base)->lay & v3d->lay) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
+#define TESTBASELIB_BGMODE(base) ( ((base)->flag & SELECT) && ((base)->lay & (v3d ? v3d->lay : G.scene->lay)) && ((base)->object->id.lib==0) && (((base)->object->restrictflag & OB_RESTRICT_VIEW)==0))
#define BASE_SELECTABLE(v3d, base) ((base->lay & v3d->lay) && (base->object->restrictflag & (OB_RESTRICT_SELECT|OB_RESTRICT_VIEW))==0)
#define FIRSTBASE scene->base.first
#define LASTBASE scene->base.last
#define BASACT (scene->basact)
#define OBACT (BASACT? BASACT->object: 0)
+
#define ID_NEW(a) if( (a) && (a)->id.newid ) (a)= (void *)(a)->id.newid
#define ID_NEW_US(a) if( (a)->id.newid) {(a)= (void *)(a)->id.newid; (a)->id.us++;}
#define ID_NEW_US2(a) if( ((ID *)a)->newid) {(a)= ((ID *)a)->newid; ((ID *)a)->us++;}
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 36198087d22..e3eaca7accd 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -155,6 +155,7 @@ void wmOrtho2 (float x1, float x2, float y1, float y2);
/* utilities */
void WM_set_framebuffer_index_color(int index);
+int WM_framebuffer_to_index(unsigned int col);
#endif /* WM_API_H */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index d76b7ebebd4..8a4f93d8a3f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1114,6 +1114,10 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
event.oskey= evt->oskey= event.val;
}
+ /* if test_break set, it catches this. Keep global for now? */
+ if(event.type==ESCKEY)
+ G.afbreek= 1;
+
wm_event_add(win, &event);
break;
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index eca4dec122e..500b04eccab 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -121,7 +121,8 @@ void WM_init(bContext *C)
wm_operatortype_init();
set_free_windowmanager_cb(wm_close_and_free); /* library.c */
-
+ set_blender_test_break_cb(wm_window_testbreak); /* blender.c */
+
ED_spacetypes_init(); /* editors/area/spacetype.c */
/* get the default database, plus a wm */
diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c
index abc78f06f8f..29baf9f4cc5 100644
--- a/source/blender/windowmanager/intern/wm_subwindow.c
+++ b/source/blender/windowmanager/intern/wm_subwindow.c
@@ -456,7 +456,7 @@ void WM_set_framebuffer_index_color(int index)
cpack(index_to_framebuffer(index));
}
-int framebuffer_to_index(unsigned int col)
+int WM_framebuffer_to_index(unsigned int col)
{
if (col==0) return 0;
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index b38de750114..73860af5f85 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -554,6 +554,25 @@ void wm_window_process_events(const bContext *C)
PIL_sleep_ms(5);
}
+/* exported as handle callback to bke blender.c */
+void wm_window_testbreak(void)
+{
+ static double ltime= 0;
+ double curtime= PIL_check_seconds_timer();
+
+ /* only check for breaks every 50 milliseconds
+ * if we get called more often.
+ */
+ if ((curtime-ltime)>.05) {
+ int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
+
+ if(hasevent)
+ GHOST_DispatchEvents(g_system);
+
+ ltime= curtime;
+ }
+}
+
/* **************** init ********************** */
void wm_ghost_init(bContext *C)
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 03f2060e706..97b9f434982 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -52,6 +52,8 @@ void wm_window_swap_buffers (wmWindow *win);
wmWindow *wm_window_copy (bContext *C, wmWindow *winorig);
wmWindow *wm_window_rip (bContext *C, wmWindow *winorig);
+void wm_window_testbreak (void);
+
/* *************** window operators ************** */
int wm_window_duplicate_op (bContext *C, wmOperator *op);
int wm_window_rip_op (bContext *C, wmOperator *op, struct wmEvent *event);