New: X-axis mirror weightpainting.

- Set the button in Paint Panel, Edit buttons context
- It assumes the mesh to be near-perfectly mirrored. Current threshold is
  set to 0.0001 (maximum difference allowed).
  In order to evaluate proper mirroring, a new option will be added in
  Mesh editmode later.
- When the flipped group doesn't exist yet, it creates the group
- Of course this doesn't work for mirror modifier!

New: Select/activate flipped bone or vertex group
- Press SHIFT+F in PoseMode or WeightPaint mode to get the flipped bone.
  Is especially to see while painting if the mirror copying works OK.

New: "Apply Envelope to VertexGroup" uses X-mirror option too.

Todo; check on mirror vertex painting, and mirror Mesh editmode....

The implementation is based on a 8x8x8 Octree, where vertex locations are
stored. Vertices on the threshold boundary of an Octree node are filled in
the neighbour nodes as well, ensuring that the lookup works with threshold.
The current size of the Octree gives good speedup, even for 128k vertices
it only needs 256 lookup cycles per checked vertex.
Same code could be used for the bevel tool for example.

src/meshtools.c:
int mesh_octree_table(Object *ob, float *co, char mode)

- mode 's' or 'e' is "start octree" or "end octree"
- mode 'u' is "use", it then returns an index nr of the found vertex.
  (return -1 if not found)
This commit is contained in:
Ton Roosendaal 2005-10-22 14:05:25 +00:00
parent ed7fb486a1
commit 5e8131309e
11 changed files with 363 additions and 32 deletions

@ -37,6 +37,11 @@
#ifndef BKE_BAD_LEVEL_CALLS_H
#define BKE_BAD_LEVEL_CALLS_H
/* blender.c */
void freeAllRad(void);
void free_editText(void);
void free_vertexpaint(void);
/* readfile.c */
struct PluginSeq;
void open_plugin_seq(struct PluginSeq *pis, char *seqname);
@ -47,8 +52,6 @@ void check_imasel_copy(struct SpaceImaSel *simasel);
struct ScrArea;
struct bScreen;
void unlink_screen(struct bScreen *sc);
void freeAllRad(void);
void free_editText(void);
void setscreen(struct bScreen *sc);
void force_draw_all(int);
/* otherwise the WHILE_SEQ doesn't work */

@ -55,6 +55,7 @@ void unlink_screen(struct bScreen *sc){}
void freeAllRad(void){}
void free_editText(void){}
void free_editArmature(void){}
void free_vertexpaint(void){}
char *getIpoCurveName( struct IpoCurve * icu )
{

@ -260,6 +260,8 @@ static void clear_global(void)
G.sima= NULL;
G.sipo= NULL;
free_vertexpaint();
G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT);
}

@ -33,6 +33,8 @@
#ifndef BIF_MESHTOOLS_H
#define BIF_MESHTOOLS_H
struct Object;
extern void join_mesh(void);
extern void fasterdraw(void);
@ -40,6 +42,8 @@ extern void slowerdraw(void);
extern void sort_faces(void);
extern int mesh_octree_table(struct Object *ob, float *co, char mode);
extern int mesh_get_x_mirror_vert(struct Object *ob, int index);
#endif

@ -61,6 +61,7 @@ void paste_posebuf (int flip);
void pose_adds_vgroups(struct Object *meshobj);
void pose_flip_names(void);
void pose_activate_flipped_bone(void);
#endif

@ -393,11 +393,12 @@
#define R_DISPLAYWIN 1
#define R_DISPLAYAUTO 2
/* Gvp.flag */
/* Gvp.flag and Gwp.flag */
#define VP_COLINDEX 1
#define VP_AREA 2
#define VP_SOFT 4
#define VP_NORMALS 8
#define VP_SPRAY 16
#define VP_MIRROR_X 32
#endif

@ -3311,7 +3311,7 @@ static void editing_panel_links(Object *ob)
if (ob->actdef){
defGroup = BLI_findlink(&ob->defbase, ob->actdef-1);
but= uiDefBut(block, TEX, REDRAWBUTSEDIT,"", 161,132,140-18,21, defGroup->name, 0, 32, 0, 0, "Displays current vertex group name. Click to change. (Match bone name for deformation.)");
but= uiDefBut(block, TEX, REDRAWBUTSEDIT,"", 161,132,140-18,21, defGroup->name, 0, 31, 0, 0, "Displays current vertex group name. Click to change. (Match bone name for deformation.)");
uiButSetFunc(but, verify_vertexgroup_name_func, defGroup, NULL);
uiDefButF(block, NUM, REDRAWVIEW3D, "Weight:", 143, 111, 140, 21, &editbutvweight, 0, 1, 10, 0, "Sets the current vertex group's bone deformation strength");
@ -3543,8 +3543,9 @@ static void editing_panel_mesh_paint(void)
if(ob){
uiBlockBeginAlign(block);
uiDefButBitC(block, TOG, OB_DRAWWIRE, REDRAWVIEW3D, "Wire", 10,10,150,19, &ob->dtx, 0, 0, 0, 0, "Displays the active object's wireframe in shaded drawing modes");
uiDefBut(block, BUT, B_CLR_WPAINT, "Clear", 160,10,150,19, NULL, 0, 0, 0, 0, "Removes reference to this deform group from all vertices");
uiDefButBitS(block, TOG, VP_MIRROR_X, REDRAWVIEW3D, "X-Mirror", 10,10,100,19, &Gwp.flag, 0, 0, 0, 0, "Mirrored Paint, applying on mirrored Weight Group name");
uiDefButBitC(block, TOG, OB_DRAWWIRE, REDRAWVIEW3D, "Wire", 110,10,100,19, &ob->dtx, 0, 0, 0, 0, "Displays the active object's wireframe in shaded drawing modes");
uiDefBut(block, BUT, B_CLR_WPAINT, "Clear", 210,10,100,19, NULL, 0, 0, 0, 0, "Removes reference to this deform group from all vertices");
uiBlockEndAlign(block);
}
}

@ -590,4 +590,187 @@ void sort_faces(void)
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
}
/* ********************* MESH VERTEX OCTREE LOOKUP ************* */
#define MOC_RES 8
#define MOC_NODE_RES 8
#define MOC_THRESH 0.0001f
typedef struct MocNode {
struct MocNode *next;
int 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, int 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);
}
static int mesh_octree_find_index(MocNode **bt, MVert *mvert, float *co)
{
MVert *testvert;
int a;
if(*bt==NULL)
return -1;
for(a=0; a<MOC_NODE_RES; a++) {
if((*bt)->index[a]) {
testvert= mvert+(*bt)->index[a]-1;
if(FloatCompare(testvert->co, co, MOC_THRESH))
return (*bt)->index[a]-1;
}
else return -1;
}
if( (*bt)->next)
return mesh_octree_find_index(&(*bt)->next, mvert, co);
return -1;
}
/* 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, int index)
{
float fx, fy, fz;
int vx, vy, vz;
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);
}
/* mode is 's' start, or 'e' end, or 'u' use */
/* if end, ob can be NULL */
int mesh_octree_table(Object *ob, float *co, char mode)
{
MocNode **bt;
static MocNode **basetable= NULL;
static float offs[3], div[3];
int a;
if(mode=='u') { /* use table */
if(basetable) {
Mesh *me= ob->data;
bt= basetable + mesh_octree_get_base_offs(co, offs, div);
return mesh_octree_find_index(bt, me->mvert, co);
}
return -1;
}
else if(mode=='s') { /* start table */
Mesh *me= ob->data;
BoundBox *bb = mesh_get_bb(me);
MVert *mvert;
/* for quick unit coordinate calculus */
VECCOPY(offs, bb->vec[0]);
VecSubf(div, bb->vec[6], offs);
VecMulf(div, 1.0f/MOC_RES);
if(div[0]==0.0f) div[0]= 1.0f;
if(div[1]==0.0f) div[1]= 1.0f;
if(div[2]==0.0f) div[2]= 1.0f;
if(basetable) /* error should not happen, added to prevent coding errors */
error("Mesh octree table coding error");
basetable= MEM_callocN(MOC_RES*MOC_RES*MOC_RES*sizeof(void *), "sym table");
for(a=1, mvert= me->mvert; a<=me->totvert; a++, mvert++) {
mesh_octree_add_nodes(basetable, mvert->co, offs, div, a);
}
}
else if(mode=='e') { /* end table */
if(basetable) {
for(a=0, bt=basetable; a<MOC_RES*MOC_RES*MOC_RES; a++, bt++) {
if(*bt) mesh_octree_free_node(bt);
}
MEM_freeN(basetable);
basetable= NULL;
}
}
return 0;
}
int mesh_get_x_mirror_vert(Object *ob, int index)
{
Mesh *me= ob->data;
MVert *mvert= me->mvert+index;
float vec[3];
vec[0]= -mvert->co[0];
vec[1]= mvert->co[1];
vec[2]= mvert->co[2];
return mesh_octree_table(ob, vec, 'u');
}

@ -65,6 +65,7 @@
#include "BIF_graphics.h"
#include "BIF_interface.h"
#include "BIF_poseobject.h"
#include "BIF_meshtools.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BIF_screen.h"
@ -73,6 +74,7 @@
#include "BSE_edit.h"
#include "BSE_editipo.h"
#include "BSE_trans_types.h"
#include "mydevice.h"
#include "blendef.h"
@ -582,7 +584,7 @@ void paste_posebuf (int flip)
struct vgroup_map {
float head[3], tail[3];
Bone *bone;
bDeformGroup *dg;
bDeformGroup *dg, *dgflip;
Object *meshobj;
};
@ -591,7 +593,6 @@ static void pose_adds_vgroups__mapFunc(void *userData, int index, float *co, flo
struct vgroup_map *map= userData;
float vec[3], fac;
VECCOPY(vec, co);
Mat4MulVecfl(map->meshobj->obmat, vec);
@ -604,17 +605,27 @@ static void pose_adds_vgroups__mapFunc(void *userData, int index, float *co, flo
else
remove_vert_defgroup (map->meshobj, map->dg, index);
if(map->dgflip) {
int j= mesh_get_x_mirror_vert(map->meshobj, index);
if(j>=0) {
if(fac!=0.0f)
add_vert_to_defgroup (map->meshobj, map->dgflip, j, fac, WEIGHT_REPLACE);
else
remove_vert_defgroup (map->meshobj, map->dgflip, j);
}
}
}
/* context weightpaint and deformer in posemode */
void pose_adds_vgroups(Object *meshobj)
{
extern VPaint Gwp; /* from vpaint */
struct vgroup_map map;
DerivedMesh *dm;
Object *poseobj= modifiers_isDeformedByArmature(meshobj);
bPoseChannel *pchan;
Bone *bone;
bDeformGroup *dg;
bDeformGroup *dg, *curdef;
int DMneedsFree;
if(poseobj==NULL || (poseobj->flag & OB_POSEMODE)==0) return;
@ -632,6 +643,20 @@ void pose_adds_vgroups(Object *meshobj)
if(dg==NULL)
dg= add_defgroup_name(meshobj, bone->name);
/* flipped bone */
if(Gwp.flag & VP_MIRROR_X) {
char name[32];
BLI_strncpy(name, dg->name, 32);
bone_flip_name(name, 0); // 0 = don't strip off number extensions
for (curdef = meshobj->defbase.first; curdef; curdef=curdef->next)
if (!strcmp(curdef->name, name))
break;
map.dgflip= curdef;
}
else map.dgflip= NULL;
/* get the root of the bone in global coords */
VECCOPY(map.head, bone->arm_head);
Mat4MulVecfl(poseobj->obmat, map.head);
@ -693,3 +718,52 @@ void pose_flip_names(void)
BIF_undo_push("Flip names");
}
/* context active object, or weightpainted object with armature in posemode */
void pose_activate_flipped_bone(void)
{
Object *ob= OBACT;
if(ob==NULL) return;
if(G.f & G_WEIGHTPAINT) {
ob= modifiers_isDeformedByArmature(ob);
}
if(ob && (ob->flag & OB_POSEMODE)) {
bPoseChannel *pchan, *pchanf;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
if(pchan->bone->flag & BONE_ACTIVE) {
break;
}
}
if(pchan) {
char name[32];
BLI_strncpy(name, pchan->name, 32);
bone_flip_name(name, 1); // 0 = do not strip off number extensions
pchanf= get_pose_channel(ob->pose, name);
if(pchanf && pchanf!=pchan) {
pchan->bone->flag &= ~(BONE_SELECTED|BONE_ACTIVE);
pchanf->bone->flag |= (BONE_SELECTED|BONE_ACTIVE);
/* in weightpaint we select the associated vertex group too */
if(G.f & G_WEIGHTPAINT) {
vertexgroup_select_by_name(OBACT, name);
DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);
}
select_actionchannel_by_name(ob->action, name, 1);
allqueue(REDRAWVIEW3D, 0);
allqueue(REDRAWACTION, 0);
allqueue(REDRAWIPO, 0); /* To force action/constraint ipo update */
allqueue(REDRAWBUTSEDIT, 0);
allqueue(REDRAWBUTSOBJECT, 0);
allqueue(REDRAWOOPS, 0);
}
}
}
}

@ -1243,11 +1243,17 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
}
else if(G.qual==LR_CTRLKEY)
sort_faces();
else if((G.qual==LR_SHIFTKEY))
fly();
else if((G.qual==LR_SHIFTKEY)) {
if(ob && (ob->flag & OB_POSEMODE))
pose_activate_flipped_bone();
else if(G.f & G_WEIGHTPAINT)
pose_activate_flipped_bone();
else
fly();
}
else {
set_faceselect();
}
set_faceselect();
}
break;
case GKEY:

@ -63,6 +63,7 @@
#include "DNA_view3d_types.h"
#include "DNA_userdef_types.h"
#include "BKE_armature.h"
#include "BKE_DerivedMesh.h"
#include "BKE_depsgraph.h"
#include "BKE_deform.h"
@ -73,15 +74,16 @@
#include "BKE_object.h"
#include "BKE_utildefines.h"
#include "BIF_graphics.h"
#include "BIF_interface.h"
#include "BIF_mywindow.h"
#include "BIF_editview.h"
#include "BIF_graphics.h"
#include "BIF_glutil.h"
#include "BIF_gl.h"
#include "BIF_interface.h"
#include "BIF_meshtools.h"
#include "BIF_mywindow.h"
#include "BIF_space.h"
#include "BIF_screen.h"
#include "BIF_toolbox.h"
#include "BIF_glutil.h"
#include "BIF_gl.h"
#include "BDR_vpaint.h"
@ -459,6 +461,8 @@ void free_vertexpaint()
if(wpaintundobuf)
free_dverts(wpaintundobuf, totwpaintundo);
wpaintundobuf= NULL;
mesh_octree_table(NULL, NULL, 'e');
}
@ -882,17 +886,38 @@ void sample_wpaint()
}
static void do_weight_paint_vertex(Object *ob, int index, int alpha, float paintweight, int vgroup_mirror)
{
Mesh *me= ob->data;
MDeformWeight *dw, *uw;
int vgroup= ob->actdef-1;
dw= verify_defweight(me->dvert+index, vgroup);
uw= verify_defweight(wpaintundobuf+index, vgroup);
wpaint_blend(dw, uw, (float)alpha/255.0, paintweight);
if(Gwp.flag & VP_MIRROR_X) { /* x mirror painting */
if(vgroup_mirror != -1) {
int j= mesh_get_x_mirror_vert(ob, index);
if(j>=0) {
/* copy, not paint again */
uw= verify_defweight(me->dvert+j, vgroup_mirror);
uw->weight= dw->weight;
}
}
}
}
void weight_paint(void)
{
extern float editbutvweight;
MDeformWeight *dw, *uw;
Object *ob;
Mesh *me;
MFace *mface;
TFace *tface;
float mat[4][4], imat[4][4], paintweight;
int index, totindex, alpha, totw;
int vgroup_mirror= -1;
short mval[2], mvalo[2], firsttime=1, mousebut;
if((G.f & G_WEIGHTPAINT)==0) return;
@ -964,6 +989,26 @@ void weight_paint(void)
if (U.flag & USER_LMOUSESELECT) mousebut = R_MOUSE;
else mousebut = L_MOUSE;
/* if mirror painting, find the other group */
if(Gwp.flag & VP_MIRROR_X) {
bDeformGroup *defgroup= BLI_findlink(&ob->defbase, ob->actdef-1);
if(defgroup) {
bDeformGroup *curdef;
int actdef= 0;
char name[32];
BLI_strncpy(name, defgroup->name, 32);
bone_flip_name(name, 0); // 0 = don't strip off number extensions
for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++)
if (!strcmp(curdef->name, name))
break;
if(curdef && curdef!=defgroup)
vgroup_mirror= actdef;
}
}
while (get_mbut() & mousebut) {
getmouseco_areawin(mval);
@ -1029,6 +1074,7 @@ void weight_paint(void)
if(mface->v4) (me->dvert+mface->v4)->flag= 1;
if(Gwp.mode==VP_FILT) {
MDeformWeight *dw;
dw= verify_defweight(me->dvert+mface->v1, ob->actdef-1);
if(dw) {paintweight+= dw->weight; totw++;}
dw= verify_defweight(me->dvert+mface->v2, ob->actdef-1);
@ -1056,9 +1102,7 @@ void weight_paint(void)
if((me->dvert+mface->v1)->flag) {
alpha= calc_vp_alpha_dl(&Gwp, dm, mface->v1, mval);
if(alpha) {
dw= verify_defweight(me->dvert+mface->v1, ob->actdef-1);
uw= verify_defweight(wpaintundobuf+mface->v1, ob->actdef-1);
wpaint_blend(dw, uw, (float)alpha/255.0, paintweight);
do_weight_paint_vertex(ob, mface->v1, alpha, paintweight, vgroup_mirror);
}
(me->dvert+mface->v1)->flag= 0;
}
@ -1066,9 +1110,7 @@ void weight_paint(void)
if((me->dvert+mface->v2)->flag) {
alpha= calc_vp_alpha_dl(&Gwp, dm, mface->v2, mval);
if(alpha) {
dw= verify_defweight(me->dvert+mface->v2, ob->actdef-1);
uw= verify_defweight(wpaintundobuf+mface->v2, ob->actdef-1);
wpaint_blend(dw, uw, (float)alpha/255.0, paintweight);
do_weight_paint_vertex(ob, mface->v2, alpha, paintweight, vgroup_mirror);
}
(me->dvert+mface->v2)->flag= 0;
}
@ -1076,9 +1118,7 @@ void weight_paint(void)
if((me->dvert+mface->v3)->flag) {
alpha= calc_vp_alpha_dl(&Gwp, dm, mface->v3, mval);
if(alpha) {
dw= verify_defweight(me->dvert+mface->v3, ob->actdef-1);
uw= verify_defweight(wpaintundobuf+mface->v3, ob->actdef-1);
wpaint_blend(dw, uw, (float)alpha/255.0, paintweight);
do_weight_paint_vertex(ob, mface->v3, alpha, paintweight, vgroup_mirror);
}
(me->dvert+mface->v3)->flag= 0;
}
@ -1087,9 +1127,7 @@ void weight_paint(void)
if(mface->v4) {
alpha= calc_vp_alpha_dl(&Gwp, dm, mface->v4, mval);
if(alpha) {
dw= verify_defweight(me->dvert+mface->v4, ob->actdef-1);
uw= verify_defweight(wpaintundobuf+mface->v4, ob->actdef-1);
wpaint_blend(dw, uw, (float)alpha/255.0, paintweight);
do_weight_paint_vertex(ob, mface->v4, alpha, paintweight, vgroup_mirror);
}
(me->dvert+mface->v4)->flag= 0;
}
@ -1339,12 +1377,29 @@ void set_wpaint(void) /* toggle */
}
if(G.f & G_WEIGHTPAINT) {
Object *par;
setcursor_space(SPACE_VIEW3D, CURSOR_VPAINT);
mesh_octree_table(ob, NULL, 's');
/* verify if active weight group is also active bone */
par= modifiers_isDeformedByArmature(ob);
if(par && (par->flag & OB_POSEMODE)) {
bPoseChannel *pchan;
for(pchan= par->pose->chanbase.first; pchan; pchan= pchan->next)
if(pchan->bone->flag & BONE_ACTIVE)
break;
if(pchan)
vertexgroup_select_by_name(ob, pchan->name);
}
}
else {
freefastshade(); /* to be sure */
if(!(G.f & G_FACESELECT))
setcursor_space(SPACE_VIEW3D, CURSOR_STD);
mesh_octree_table(ob, NULL, 'e');
}
}