Four mirrored Weight Paint improvement/fixes:
[blender.git] / source / blender / src / meshtools.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2004 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 /*
34
35 meshtools.c: no editmode, tools operating on meshes
36
37 void join_mesh(void);
38
39 void fasterdraw(void);
40 void slowerdraw(void);
41
42 void sort_faces(void);
43
44 */
45
46 #include <stdlib.h>
47 #include <string.h>
48 #include <math.h>
49
50 #ifdef HAVE_CONFIG_H
51 #include <config.h>
52 #endif
53
54 #include "MEM_guardedalloc.h"
55
56 #include "DNA_mesh_types.h"
57 #include "DNA_meshdata_types.h"
58 #include "DNA_object_types.h"
59 #include "DNA_material_types.h"
60 #include "DNA_scene_types.h"
61 #include "DNA_screen_types.h"
62 #include "DNA_view3d_types.h"
63
64 #include "BLI_blenlib.h"
65 #include "BLI_arithb.h"
66
67 #include "BKE_depsgraph.h"
68 #include "BKE_global.h"
69 #include "BKE_library.h"
70 #include "BKE_main.h"
71 #include "BKE_mesh.h"
72 #include "BKE_material.h"
73 #include "BKE_object.h"
74 #include "BKE_utildefines.h"
75
76 #include "BIF_editmesh.h"
77 #include "BIF_graphics.h"
78 #include "BIF_mywindow.h"
79 #include "BIF_screen.h"
80 #include "BIF_space.h"
81 #include "BIF_toolbox.h"
82 #include "BIF_editconstraint.h"
83
84 #include "BDR_editobject.h" 
85 #include "BDR_editface.h" 
86
87 #include "BLI_editVert.h"
88
89 #include "mydevice.h"
90 #include "blendef.h"
91
92 #include "BIF_meshtools.h" /* include ourself for prototypes */
93
94
95 /* * ********************** no editmode!!! *********** */
96
97
98 /** tests whether selected mesh objects have tfaces */
99 static int testSelected_TfaceMesh(void)
100 {
101         Base *base;
102         Mesh *me;
103
104         base = FIRSTBASE;
105         while (base) {
106                 if TESTBASE(base) {
107                         if(base->object->type==OB_MESH) {
108                                 me= base->object->data;
109                                 if (me->tface) 
110                                         return 1;
111                         }               
112                 }                       
113                 base= base->next;
114         }       
115         return 0;
116 }       
117
118 void join_mesh(void)
119 {
120         Base *base, *nextb;
121         Object *ob;
122         Material **matar, *ma;
123         Mesh *me;
124         MVert *mvert, *mvertmain;
125         MEdge *medge = NULL, *medgemain;
126         MFace *mface = NULL, *mfacemain;
127         TFace *tface = NULL, *tfacemain;
128         unsigned int *mcol=NULL, *mcolmain;
129         float imat[4][4], cmat[4][4];
130         int a, b, totcol, totedge=0, totvert=0, totface=0, ok=0, vertofs, map[MAXMAT];
131         int     i, j, index, haskey=0, hasdefgroup=0;
132         bDeformGroup *dg, *odg;
133         MDeformVert *dvert, *dvertmain;
134         
135         if(G.obedit) return;
136         
137         ob= OBACT;
138         if(!ob || ob->type!=OB_MESH) return;
139         
140         /* count */
141         base= FIRSTBASE;
142         while(base) {
143                 if TESTBASELIB(base) {
144                         if(base->object->type==OB_MESH) {
145                                 me= base->object->data;
146                                 totvert+= me->totvert;
147                                 totface+= me->totface;
148
149                                 if(base->object == ob) ok= 1;
150
151                                 if(me->key) {
152                                         haskey= 1;
153                                         break;
154                                 }
155                         }
156                 }
157                 base= base->next;
158         }
159         
160         if(haskey) {
161                 error("Can't join meshes with vertex keys");
162                 return;
163         }
164         /* that way the active object is always selected */ 
165         if(ok==0) return;
166         
167         if(totvert==0 || totvert>MESH_MAX_VERTS) return;
168         
169         if(okee("Join selected meshes")==0) return;
170
171
172         /* if needed add edges to other meshes */
173         for(base= FIRSTBASE; base; base= base->next) {
174                 if TESTBASELIB(base) {
175                         if(base->object->type==OB_MESH) {
176                                 me= base->object->data;
177                                 totedge += me->totedge;
178                         }
179                 }
180         }
181         
182         /* new material indices and material array */
183         matar= MEM_callocN(sizeof(void *)*MAXMAT, "join_mesh");
184         totcol= ob->totcol;
185         
186         /* obact materials in new main array, is nicer start! */
187         for(a=1; a<=ob->totcol; a++) {
188                 matar[a-1]= give_current_material(ob, a);
189                 id_us_plus((ID *)matar[a-1]);
190                 /* increase id->us : will be lowered later */
191         }
192         
193         base= FIRSTBASE;
194         while(base) {
195                 if TESTBASELIB(base) {
196                         if(ob!=base->object && base->object->type==OB_MESH) {
197                                 me= base->object->data;
198
199                                 // Join this object's vertex groups to the base one's
200                                 for (dg=base->object->defbase.first; dg; dg=dg->next){
201                                         hasdefgroup= 1;
202                                         
203                                         /* See if this group exists in the object */
204                                         for (odg=ob->defbase.first; odg; odg=odg->next){
205                                                 if (!strcmp(odg->name, dg->name)){
206                                                         break;
207                                                 }
208                                         }
209                                         if (!odg){
210                                                 odg = MEM_callocN (sizeof(bDeformGroup), "join deformGroup");
211                                                 memcpy (odg, dg, sizeof(bDeformGroup));
212                                                 BLI_addtail(&ob->defbase, odg);
213                                         }
214
215                                 }
216                                 if (ob->defbase.first && ob->actdef==0)
217                                         ob->actdef=1;
218
219                                 if(me->totvert) {
220                                         for(a=1; a<=base->object->totcol; a++) {
221                                                 ma= give_current_material(base->object, a);
222                                                 if(ma) {
223                                                         for(b=0; b<totcol; b++) {
224                                                                 if(ma == matar[b]) break;
225                                                         }
226                                                         if(b==totcol) {
227                                                                 matar[b]= ma;
228                                                                 ma->id.us++;
229                                                                 totcol++;
230                                                         }
231                                                         if(totcol>=MAXMAT-1) break;
232                                                 }
233                                         }
234                                 }
235                         }
236                         if(totcol>=MAXMAT-1) break;
237                 }
238                 base= base->next;
239         }
240
241         me= ob->data;
242         mvert= mvertmain= MEM_mallocN(totvert*sizeof(MVert), "joinmesh vert");
243
244         if(totedge) medge= medgemain= MEM_callocN(totedge*sizeof(MEdge), "joinmesh edge");
245         else medgemain= NULL;
246         
247         if (totface) mface= mfacemain= MEM_mallocN(totface*sizeof(MFace), "joinmesh face");
248         else mfacemain= NULL;
249
250         if(me->mcol) mcol= mcolmain= MEM_callocN(totface*4*sizeof(int), "joinmesh mcol");
251         else mcolmain= NULL;
252
253         /* if active object doesn't have Tfaces, but one in the selection does,
254            make TFaces for active, so we don't lose texture information in the
255            join process */
256         if(me->tface || testSelected_TfaceMesh()) tface= tfacemain= MEM_callocN(totface*4*sizeof(TFace), "joinmesh4");
257         else tfacemain= NULL;
258
259         if(me->dvert || hasdefgroup)
260                 dvert= dvertmain= MEM_callocN(totvert*sizeof(MDeformVert), "joinmesh5");
261         else dvert=dvertmain= NULL;
262
263         vertofs= 0;
264         
265         /* inverse transorm all selected meshes in this object */
266         Mat4Invert(imat, ob->obmat);
267         
268         base= FIRSTBASE;
269         while(base) {
270                 nextb= base->next;
271                 if TESTBASELIB(base) {
272                         if(base->object->type==OB_MESH) {
273                                 
274                                 me= base->object->data;
275                                 
276                                 if(me->totvert) {
277                                         
278                                         memcpy(mvert, me->mvert, me->totvert*sizeof(MVert));
279                                         
280                                         copy_dverts(dvert, me->dvert, me->totvert);
281
282                                         /* NEW VERSION */
283                                         if (dvertmain){
284                                                 for (i=0; i<me->totvert; i++){
285                                                         for (j=0; j<dvert[i].totweight; j++){
286                                                                 //      Find the old vertex group
287                                                                 odg = BLI_findlink (&base->object->defbase, dvert[i].dw[j].def_nr);
288                                                                 if(odg) {
289                                                                         //      Search for a match in the new object
290                                                                         for (dg=ob->defbase.first, index=0; dg; dg=dg->next, index++){
291                                                                                 if (!strcmp(dg->name, odg->name)){
292                                                                                         dvert[i].dw[j].def_nr = index;
293                                                                                         break;
294                                                                                 }
295                                                                         }
296                                                                 }
297                                                         }
298                                                 }
299                                                 dvert+=me->totvert;
300                                         }
301
302                                         if(base->object != ob) {
303                                                 /* watch this: switch matmul order really goes wrong */
304                                                 Mat4MulMat4(cmat, base->object->obmat, imat);
305                                                 
306                                                 a= me->totvert;
307                                                 while(a--) {
308                                                         Mat4MulVecfl(cmat, mvert->co);
309                                                         mvert++;
310                                                 }
311                                         }
312                                         else mvert+= me->totvert;
313                                         
314                                         if(mcolmain) {
315                                                 if(me->mcol) memcpy(mcol, me->mcol, me->totface*4*4);
316                                                 mcol+= 4*me->totface;
317                                         }
318                                 }
319                                 if(me->totface) {
320                                 
321                                         /* make mapping for materials */
322                                         memset(map, 0, 4*MAXMAT);
323                                         for(a=1; a<=base->object->totcol; a++) {
324                                                 ma= give_current_material(base->object, a);
325                                                 if(ma) {
326                                                         for(b=0; b<totcol; b++) {
327                                                                 if(ma == matar[b]) {
328                                                                         map[a-1]= b;
329                                                                         break;
330                                                                 }
331                                                         }
332                                                 }
333                                         }
334
335                                         memcpy(mface, me->mface, me->totface*sizeof(MFace));
336                                         
337                                         a= me->totface;
338                                         while(a--) {
339                                                 mface->v1+= vertofs;
340                                                 mface->v2+= vertofs;
341                                                 mface->v3+= vertofs;
342                                                 if(mface->v4) mface->v4+= vertofs;
343                                                 
344                                                 mface->mat_nr= map[(int)mface->mat_nr];
345                                                 
346                                                 mface++;
347                                         }
348                                         
349                                         if(tfacemain) {
350                                                 if(me->tface) {
351                                                         memcpy(tface, me->tface, me->totface*sizeof(TFace));
352                                                         tface+= me->totface;
353                                                 }
354                                                 else {
355                                                         for(a=0; a<me->totface; a++, tface++) {
356                                                                 default_tface(tface);
357                                                         }
358                                                 }
359                                         }
360                                         
361                                 }
362                                 
363                                 if(me->totedge) {
364                                         memcpy(medge, me->medge, me->totedge*sizeof(MEdge));
365                                         
366                                         a= me->totedge;
367                                         while(a--) {
368                                                 medge->v1+= vertofs;
369                                                 medge->v2+= vertofs;
370                                                 medge++;
371                                         }
372                                 }
373                                 
374                                 vertofs+= me->totvert;
375                                 
376                                 if(base->object!=ob) {
377                                         free_and_unlink_base(base);
378                                 }
379                         }
380                 }
381                 base= nextb;
382         }
383         
384         me= ob->data;
385         
386         if(me->mvert) MEM_freeN(me->mvert);
387         me->mvert= mvertmain;
388
389         if(me->medge) MEM_freeN(me->medge);
390         me->medge= medgemain;
391
392         if(me->mface) MEM_freeN(me->mface);
393         me->mface= mfacemain;
394
395         if(me->dvert) free_dverts(me->dvert, me->totvert);
396         me->dvert = dvertmain;
397
398         if(me->mcol) MEM_freeN(me->mcol);
399         me->mcol= (MCol *)mcolmain;
400         
401         if(me->tface) MEM_freeN(me->tface);
402         me->tface= tfacemain;
403         
404         me->totvert= totvert;
405         me->totedge= totedge;
406         me->totface= totface;
407         
408         /* old material array */
409         for(a=1; a<=ob->totcol; a++) {
410                 ma= ob->mat[a-1];
411                 if(ma) ma->id.us--;
412         }
413         for(a=1; a<=me->totcol; a++) {
414                 ma= me->mat[a-1];
415                 if(ma) ma->id.us--;
416         }
417         if(ob->mat) MEM_freeN(ob->mat);
418         if(me->mat) MEM_freeN(me->mat);
419         ob->mat= me->mat= 0;
420         
421         if(totcol) {
422                 me->mat= matar;
423                 ob->mat= MEM_callocN(sizeof(void *)*totcol, "join obmatar");
424         }
425         else MEM_freeN(matar);
426         
427         ob->totcol= me->totcol= totcol;
428         ob->colbits= 0;
429         
430         /* other mesh users */
431         test_object_materials((ID *)me);
432         
433         DAG_scene_sort(G.scene);        // removed objects, need to rebuild dag before editmode call
434         
435         enter_editmode();
436         exit_editmode(1);       // freedata, but no undo
437         
438         allqueue(REDRAWVIEW3D, 0);
439         allqueue(REDRAWBUTSSHADING, 0);
440
441         BIF_undo_push("Join Mesh");
442 }
443
444
445 void fasterdraw(void)
446 {
447         Base *base;
448         Mesh *me;
449         int toggle, a;
450
451         if(G.obedit) return;
452
453         /* reset flags */
454         me= G.main->mesh.first;
455         while(me) {
456                 me->flag &= ~ME_ISDONE;
457                 me= me->id.next;
458         }
459
460         base= FIRSTBASE;
461         while(base) {
462                 if( TESTBASELIB(base) && (base->object->type==OB_MESH)) {
463                         me= base->object->data;
464                         if(me->id.lib==0 && (me->flag & ME_ISDONE)==0) {
465                                 me->flag |= ME_ISDONE;
466                                 toggle= 0;
467                                 for(a=0; a<me->totedge; a++) {
468                                         MEdge *med = &me->medge[a];
469
470                                         if( (med->flag & ME_EDGEDRAW) && !( (toggle++) & 3) ) {
471                                                 med->flag ^= ME_EDGEDRAW;
472                                         }
473                                 }
474                         }
475                 }
476                 base= base->next;
477         }
478
479         /* important?: reset flags again */
480         me= G.main->mesh.first;
481         while(me) {
482                 me->flag &= ~ME_ISDONE;
483                 me= me->id.next;
484         }
485
486         allqueue(REDRAWVIEW3D, 0);
487 }
488
489 void slowerdraw(void)           /* reset fasterdraw */
490 {
491         Base *base;
492         Mesh *me;
493         int a;
494
495         if(G.obedit) return;
496
497         base= FIRSTBASE;
498         while(base) {
499                 if( TESTBASELIB(base) && (base->object->type==OB_MESH)) {
500                         me= base->object->data;
501                         if(me->id.lib==0) {
502                                 for(a=0; a<me->totedge; a++) {
503                                         me->medge[a].flag |= ME_EDGEDRAW;
504                                 }
505                         }
506                 }
507                 base= base->next;
508         }
509
510         allqueue(REDRAWVIEW3D, 0);
511 }
512
513 /* ********************** SORT FACES ******************* */
514
515 static void permutate(void *list, int num, int size, int *index)
516 {
517         void *buf;
518         int len;
519         int i;
520
521         len = num * size;
522
523         buf = MEM_mallocN(len, "permutate");
524         memcpy(buf, list, len);
525         
526         for (i = 0; i < num; i++) {
527                 memcpy((char *)list + (i * size), (char *)buf + (index[i] * size), size);
528         }
529         MEM_freeN(buf);
530 }
531
532 static MVert *mvertbase;
533 static MFace *mfacebase;
534
535 static int verg_mface(const void *v1, const void *v2)
536 {
537         MFace *x1, *x2;
538
539         MVert *ve1, *ve2;
540         int i1, i2;
541
542         i1 = ((int *) v1)[0];
543         i2 = ((int *) v2)[0];
544         
545         x1 = mfacebase + i1;
546         x2 = mfacebase + i2;
547
548         ve1= mvertbase+x1->v1;
549         ve2= mvertbase+x2->v1;
550         
551         if( ve1->co[2] > ve2->co[2] ) return 1;
552         else if( ve1->co[2] < ve2->co[2]) return -1;
553         return 0;
554 }
555
556
557 void sort_faces(void)
558 {
559         Object *ob= OBACT;
560         Mesh *me;
561         
562         int i, *index;
563         
564         if(ob==0) return;
565         if(G.obedit) return;
566         if(ob->type!=OB_MESH) return;
567         
568         if(okee("Sort faces in Z axis")==0) return;
569         me= ob->data;
570         if(me->totface==0) return;
571
572 /*      create index list */
573         index = (int *) MEM_mallocN(sizeof(int) * me->totface, "sort faces");
574         for (i = 0; i < me->totface; i++) {
575                 index[i] = i;
576         }
577         mvertbase= me->mvert;
578         mfacebase = me->mface;
579
580 /* sort index list instead of faces itself 
581    and apply this permutation to the face list plus
582    to the texture faces */
583         qsort(index, me->totface, sizeof(int), verg_mface);
584
585         permutate(mfacebase, me->totface, sizeof(MFace), index);
586         if (me->tface) 
587                 permutate(me->tface, me->totface, sizeof(TFace), index);
588
589         MEM_freeN(index);
590
591         allqueue(REDRAWVIEW3D, 0);
592         DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
593 }
594
595 /* ********************* MESH VERTEX OCTREE LOOKUP ************* */
596
597 /* important note; this is unfinished, needs better API for editmode, and custom threshold */
598
599 #define MOC_RES                 8
600 #define MOC_NODE_RES    8
601 #define MOC_THRESH              0.0002f
602
603 typedef struct MocNode {
604         struct MocNode *next;
605         int index[MOC_NODE_RES];
606 } MocNode;
607
608 static int mesh_octree_get_base_offs(float *co, float *offs, float *div)
609 {
610         int vx, vy, vz;
611         
612         vx= floor( (co[0]-offs[0])/div[0] );
613         vy= floor( (co[1]-offs[1])/div[1] );
614         vz= floor( (co[2]-offs[2])/div[2] );
615         
616         CLAMP(vx, 0, MOC_RES-1);
617         CLAMP(vy, 0, MOC_RES-1);
618         CLAMP(vz, 0, MOC_RES-1);
619
620         return (vx*MOC_RES*MOC_RES) + vy*MOC_RES + vz;
621 }
622
623 static void mesh_octree_add_node(MocNode **bt, int index)
624 {
625         if(*bt==NULL) {
626                 *bt= MEM_callocN(sizeof(MocNode), "MocNode");
627                 (*bt)->index[0]= index;
628         }
629         else {
630                 int a;
631                 for(a=0; a<MOC_NODE_RES; a++) {
632                         if((*bt)->index[a]==index)
633                                 return;
634                         else if((*bt)->index[a]==0) {
635                                 (*bt)->index[a]= index;
636                                 return;
637                         }
638                 }
639                 mesh_octree_add_node(&(*bt)->next, index);
640         }
641 }
642
643 static void mesh_octree_free_node(MocNode **bt)
644 {
645         if( (*bt)->next ) {
646                 mesh_octree_free_node(&(*bt)->next);
647         }
648         MEM_freeN(*bt);
649 }
650
651
652 /* temporal define, just to make nicer code below */
653 #define MOC_ADDNODE(vx, vy, vz) mesh_octree_add_node(basetable + ((vx)*MOC_RES*MOC_RES) + (vy)*MOC_RES + (vz), index)
654
655 static void mesh_octree_add_nodes(MocNode **basetable, float *co, float *offs, float *div, int index)
656 {
657         float fx, fy, fz;
658         int vx, vy, vz;
659         
660         fx= (co[0]-offs[0])/div[0];
661         fy= (co[1]-offs[1])/div[1];
662         fz= (co[2]-offs[2])/div[2];
663         CLAMP(fx, 0.0f, MOC_RES-MOC_THRESH);
664         CLAMP(fy, 0.0f, MOC_RES-MOC_THRESH);
665         CLAMP(fz, 0.0f, MOC_RES-MOC_THRESH);
666         
667         vx= floor(fx);
668         vy= floor(fy);
669         vz= floor(fz);
670         
671         MOC_ADDNODE(vx, vy, vz);
672         
673         if( vx>0 )
674                 if( fx-((float)vx)-MOC_THRESH < 0.0f)
675                         MOC_ADDNODE(vx-1, vy, vz);
676         if( vx<MOC_RES-2 )
677                 if( fx-((float)vx)+MOC_THRESH > 1.0f)
678                         MOC_ADDNODE(vx+1, vy, vz);
679
680         if( vy>0 )
681                 if( fy-((float)vy)-MOC_THRESH < 0.0f) 
682                         MOC_ADDNODE(vx, vy-1, vz);
683         if( vy<MOC_RES-2 )
684                 if( fy-((float)vy)+MOC_THRESH > 1.0f) 
685                         MOC_ADDNODE(vx, vy+1, vz);
686
687         if( vz>0 )
688                 if( fz-((float)vz)-MOC_THRESH < 0.0f) 
689                         MOC_ADDNODE(vx, vy, vz-1);
690         if( vz<MOC_RES-2 )
691                 if( fz-((float)vz)+MOC_THRESH > 1.0f) 
692                         MOC_ADDNODE(vx, vy, vz+1);
693         
694 }
695
696 static int mesh_octree_find_index(MocNode **bt, MVert *mvert, float *co)
697 {
698         float *vec;
699         int a;
700         
701         if(*bt==NULL)
702                 return -1;
703         
704         for(a=0; a<MOC_NODE_RES; a++) {
705                 if((*bt)->index[a]) {
706                         /* does mesh verts and editmode, code looks potential dangerous, octree should really be filled OK! */
707                         if(mvert) {
708                                 vec= (mvert+(*bt)->index[a]-1)->co;
709                                 
710                                 if(FloatCompare(vec, co, MOC_THRESH))
711                                         return (*bt)->index[a]-1;
712                         }
713                         else {
714                                 EditVert *eve= (EditVert *)INT_TO_POINTER((*bt)->index[a]);
715                                 
716                                 if(FloatCompare(eve->co, co, MOC_THRESH))
717                                         return (int)eve;
718                         }
719                 }
720                 else return -1;
721         }
722         if( (*bt)->next)
723                 return mesh_octree_find_index(&(*bt)->next, mvert, co);
724         
725         return -1;
726 }
727
728
729 /* mode is 's' start, or 'e' end, or 'u' use */
730 /* if end, ob can be NULL */
731 int mesh_octree_table(Object *ob, float *co, char mode)
732 {
733         MocNode **bt;
734         static MocNode **basetable= NULL;
735         static float offs[3], div[3];
736         
737         if(mode=='u') {         /* use table */
738                 if(basetable==NULL)
739                         mesh_octree_table(ob, NULL, 's');
740            
741                 if(basetable) {
742                         Mesh *me= ob->data;
743                         bt= basetable + mesh_octree_get_base_offs(co, offs, div);
744                         if(ob==G.obedit)
745                                 return mesh_octree_find_index(bt, NULL, co);
746                         else
747                                 return mesh_octree_find_index(bt, me->mvert, co);
748                 }
749                 return -1;
750         }
751         else if(mode=='s') {    /* start table */
752                 Mesh *me= ob->data;
753                 BoundBox *bb = mesh_get_bb(me);
754                 
755                 /* for quick unit coordinate calculus */
756                 VECCOPY(offs, bb->vec[0]);
757                 offs[0]+= MOC_THRESH;           /* we offset it 1 threshold unit extra */
758                 offs[1]+= MOC_THRESH;
759                 offs[2]+= MOC_THRESH;
760                         
761                 VecSubf(div, bb->vec[6], offs);
762                 div[0]+= MOC_THRESH;            /* and divide with 1 threshold unit more extra (try 8x8 unit grid on paint) */
763                 div[1]+= MOC_THRESH;
764                 div[2]+= MOC_THRESH;
765                 
766                 VecMulf(div, 1.0f/MOC_RES);
767                 if(div[0]==0.0f) div[0]= 1.0f;
768                 if(div[1]==0.0f) div[1]= 1.0f;
769                 if(div[2]==0.0f) div[2]= 1.0f;
770         
771                 if(basetable) /* happens when entering wpaint without closing it */
772                         mesh_octree_table(ob, co, 'e');
773                 
774                 basetable= MEM_callocN(MOC_RES*MOC_RES*MOC_RES*sizeof(void *), "sym table");
775                 
776                 if(ob==G.obedit) {
777                         EditVert *eve;
778                         
779                         for(eve= G.editMesh->verts.first; eve; eve= eve->next) {
780                                 mesh_octree_add_nodes(basetable, eve->co, offs, div, POINTER_TO_INT(eve));
781                         }
782                 }
783                 else {          
784                         MVert *mvert;
785                         int a;
786                         
787                         for(a=1, mvert= me->mvert; a<=me->totvert; a++, mvert++) {
788                                 mesh_octree_add_nodes(basetable, mvert->co, offs, div, a);
789                         }
790                 }
791         }
792         else if(mode=='e') { /* end table */
793                 if(basetable) {
794                         int a;
795                         
796                         for(a=0, bt=basetable; a<MOC_RES*MOC_RES*MOC_RES; a++, bt++) {
797                                 if(*bt) mesh_octree_free_node(bt);
798                         }
799                         MEM_freeN(basetable);
800                         basetable= NULL;
801                 }
802         }
803         return 0;
804 }
805
806 int mesh_get_x_mirror_vert(Object *ob, int index)
807 {
808         Mesh *me= ob->data;
809         MVert *mvert= me->mvert+index;
810         float vec[3];
811         
812         vec[0]= -mvert->co[0];
813         vec[1]= mvert->co[1];
814         vec[2]= mvert->co[2];
815         
816         return mesh_octree_table(ob, vec, 'u');
817 }