- particle drawing was using invalid memory with weights.

- particle set weight operator (Shift + K) and from the menu.
- mirror vertex groups operator can also flip weight group names.

a number of utility functions for weight groups added
 int *get_defgroup_flip_map(struct Object *ob);
 void flip_vertexgroup_name (char *name_r, const char *name, int strip_number); // moved from modifier.c 
 void copy_defvert (struct MDeformVert *dvert_r, const struct MDeformVert *dvert);
 void flip_defvert (struct MDeformVert *dvert, int *flip_map);
This commit is contained in:
Campbell Barton 2010-01-15 17:28:00 +00:00
parent a4732eefa5
commit ace8d45c1c
12 changed files with 346 additions and 127 deletions

View File

@ -186,6 +186,8 @@ class VIEW3D_MT_mirror(bpy.types.Menu):
props.constraint_axis = (False, False, True)
props.constraint_orientation = 'LOCAL'
layout.operator("object.vertex_group_mirror")
class VIEW3D_MT_snap(bpy.types.Menu):
bl_label = "Snap"
@ -898,6 +900,7 @@ class VIEW3D_MT_particle(bpy.types.Menu):
layout.operator("particle.subdivide")
layout.operator("particle.rekey")
layout.operator("particle.weight_set")
layout.separator()

View File

@ -44,11 +44,16 @@ void copy_defgroups (struct ListBase *lb1, struct ListBase *lb2);
struct bDeformGroup *copy_defgroup (struct bDeformGroup *ingroup);
struct bDeformGroup *get_named_vertexgroup (Object *ob, char *name);
int get_defgroup_num (struct Object *ob, struct bDeformGroup *dg);
int *get_defgroup_flip_map(struct Object *ob);
int get_named_vertexgroup_num (Object *ob, const char *name);
void unique_vertexgroup_name (struct bDeformGroup *dg, struct Object *ob);
void flip_vertexgroup_name (char *name_r, const char *name, int strip_number);
float deformvert_get_weight(const struct MDeformVert *dvert, int group_num);
float vertexgroup_get_vertex_weight(const struct MDeformVert *dvert, int index, int group_num);
void copy_defvert (struct MDeformVert *dvert_r, const struct MDeformVert *dvert);
void flip_defvert (struct MDeformVert *dvert, int *flip_map);
#endif

View File

@ -34,6 +34,7 @@
#include <string.h>
#include <math.h>
#include "ctype.h"
#include "MEM_guardedalloc.h"
@ -95,6 +96,36 @@ bDeformGroup *copy_defgroup (bDeformGroup *ingroup)
return outgroup;
}
void copy_defvert (MDeformVert *dvert_r, const MDeformVert *dvert)
{
if(dvert_r->totweight == dvert->totweight) {
if(dvert->totweight)
memcpy(dvert_r->dw, dvert->dw, dvert->totweight * sizeof(MDeformWeight));
}
else {
if(dvert_r->dw)
MEM_freeN(dvert_r->dw);
if(dvert->totweight)
dvert_r->dw= MEM_dupallocN(dvert->dw);
else
dvert_r->dw= NULL;
dvert_r->totweight = dvert->totweight;
}
}
void flip_defvert (MDeformVert *dvert, int *flip_map)
{
MDeformWeight *dw;
int i;
for(dw= dvert->dw, i=0; i<dvert->totweight; dw++, i++)
if(flip_map[dw->def_nr] >= 0)
dw->def_nr= flip_map[dw->def_nr];
}
bDeformGroup *get_named_vertexgroup (Object *ob, char *name)
{
/* return a pointer to the deform group with this name
@ -167,6 +198,36 @@ int get_defgroup_num (Object *ob, bDeformGroup *dg)
}
/* note, must be freed */
int *get_defgroup_flip_map(Object *ob)
{
bDeformGroup *dg;
int totdg= BLI_countlist(&ob->defbase);
if(totdg==0) {
return NULL;
}
else {
char name[sizeof(dg->name)];
int i, flip_num, *map= MEM_mallocN(totdg * sizeof(int), "get_defgroup_flip_map");
memset(map, -1, totdg * sizeof(int));
for (dg=ob->defbase.first, i=0; dg; dg=dg->next, i++) {
if(map[i] == -1) { /* may be calculated previously */
flip_vertexgroup_name(name, dg->name, 0);
if(strcmp(name, dg->name)) {
flip_num= get_named_vertexgroup_num(ob, name);
if(flip_num > -1) {
map[i]= flip_num;
map[flip_num]= i; /* save an extra lookup */
}
}
}
}
return map;
}
}
void unique_vertexgroup_name (bDeformGroup *dg, Object *ob)
{
bDeformGroup *curdef;
@ -221,6 +282,121 @@ void unique_vertexgroup_name (bDeformGroup *dg, Object *ob)
}
}
/* finds the best possible flipped name. For renaming; check for unique names afterwards */
/* if strip_number: removes number extensions */
void flip_vertexgroup_name (char *name_r, const char *name, int strip_number)
{
int len;
char prefix[sizeof((bDeformGroup *)NULL)->name]={""}; /* The part before the facing */
char suffix[sizeof((bDeformGroup *)NULL)->name]={""}; /* The part after the facing */
char replace[sizeof((bDeformGroup *)NULL)->name]={""}; /* The replacement string */
char number[sizeof((bDeformGroup *)NULL)->name]={""}; /* The number extension string */
char *index=NULL;
len= strlen(name);
if(len<3) return; // we don't do names like .R or .L
/* We first check the case with a .### extension, let's find the last period */
if(isdigit(name[len-1])) {
index= strrchr(name, '.'); // last occurrence
if (index && isdigit(index[1]) ) { // doesnt handle case bone.1abc2 correct..., whatever!
if(strip_number==0)
strcpy(number, index);
*index= 0;
len= strlen(name);
}
}
strcpy (prefix, name);
#define IS_SEPARATOR(a) ((a)=='.' || (a)==' ' || (a)=='-' || (a)=='_')
/* first case; separator . - _ with extensions r R l L */
if( IS_SEPARATOR(name[len-2]) ) {
switch(name[len-1]) {
case 'l':
prefix[len-1]= 0;
strcpy(replace, "r");
break;
case 'r':
prefix[len-1]= 0;
strcpy(replace, "l");
break;
case 'L':
prefix[len-1]= 0;
strcpy(replace, "R");
break;
case 'R':
prefix[len-1]= 0;
strcpy(replace, "L");
break;
}
}
/* case; beginning with r R l L , with separator after it */
else if( IS_SEPARATOR(name[1]) ) {
switch(name[0]) {
case 'l':
strcpy(replace, "r");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'r':
strcpy(replace, "l");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'L':
strcpy(replace, "R");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'R':
strcpy(replace, "L");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
}
}
else if(len > 5) {
/* hrms, why test for a separator? lets do the rule 'ultimate left or right' */
index = BLI_strcasestr(prefix, "right");
if (index==prefix || index==prefix+len-5) {
if(index[0]=='r')
strcpy (replace, "left");
else {
if(index[1]=='I')
strcpy (replace, "LEFT");
else
strcpy (replace, "Left");
}
*index= 0;
strcpy (suffix, index+5);
}
else {
index = BLI_strcasestr(prefix, "left");
if (index==prefix || index==prefix+len-4) {
if(index[0]=='l')
strcpy (replace, "right");
else {
if(index[1]=='E')
strcpy (replace, "RIGHT");
else
strcpy (replace, "Right");
}
*index= 0;
strcpy (suffix, index+4);
}
}
}
#undef IS_SEPARATOR
sprintf (name_r, "%s%s%s%s", prefix, replace, suffix, number);
}
float deformvert_get_weight(const struct MDeformVert *dvert, int group_num)
{
if(dvert)

View File

@ -39,7 +39,6 @@
#include "stdarg.h"
#include "math.h"
#include "float.h"
#include "ctype.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
@ -1801,118 +1800,6 @@ static void mirrorModifier_updateDepgraph(ModifierData *md, DagForest *forest, S
}
}
/* finds the best possible flipped name. For renaming; check for unique names afterwards */
/* if strip_number: removes number extensions */
static void vertgroup_flip_name (char *name, int strip_number)
{
int len;
char prefix[128]={""}; /* The part before the facing */
char suffix[128]={""}; /* The part after the facing */
char replace[128]={""}; /* The replacement string */
char number[128]={""}; /* The number extension string */
char *index=NULL;
len= strlen(name);
if(len<3) return; // we don't do names like .R or .L
/* We first check the case with a .### extension, let's find the last period */
if(isdigit(name[len-1])) {
index= strrchr(name, '.'); // last occurrence
if (index && isdigit(index[1]) ) { // doesnt handle case bone.1abc2 correct..., whatever!
if(strip_number==0)
strcpy(number, index);
*index= 0;
len= strlen(name);
}
}
strcpy (prefix, name);
#define IS_SEPARATOR(a) ((a)=='.' || (a)==' ' || (a)=='-' || (a)=='_')
/* first case; separator . - _ with extensions r R l L */
if( IS_SEPARATOR(name[len-2]) ) {
switch(name[len-1]) {
case 'l':
prefix[len-1]= 0;
strcpy(replace, "r");
break;
case 'r':
prefix[len-1]= 0;
strcpy(replace, "l");
break;
case 'L':
prefix[len-1]= 0;
strcpy(replace, "R");
break;
case 'R':
prefix[len-1]= 0;
strcpy(replace, "L");
break;
}
}
/* case; beginning with r R l L , with separator after it */
else if( IS_SEPARATOR(name[1]) ) {
switch(name[0]) {
case 'l':
strcpy(replace, "r");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'r':
strcpy(replace, "l");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'L':
strcpy(replace, "R");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
case 'R':
strcpy(replace, "L");
strcpy(suffix, name+1);
prefix[0]= 0;
break;
}
}
else if(len > 5) {
/* hrms, why test for a separator? lets do the rule 'ultimate left or right' */
index = BLI_strcasestr(prefix, "right");
if (index==prefix || index==prefix+len-5) {
if(index[0]=='r')
strcpy (replace, "left");
else {
if(index[1]=='I')
strcpy (replace, "LEFT");
else
strcpy (replace, "Left");
}
*index= 0;
strcpy (suffix, index+5);
}
else {
index = BLI_strcasestr(prefix, "left");
if (index==prefix || index==prefix+len-4) {
if(index[0]=='l')
strcpy (replace, "right");
else {
if(index[1]=='E')
strcpy (replace, "RIGHT");
else
strcpy (replace, "Right");
}
*index= 0;
strcpy (suffix, index+4);
}
}
}
#undef IS_SEPARATOR
sprintf (name, "%s%s%s%s", prefix, replace, suffix, number);
}
static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd,
Object *ob,
DerivedMesh *dm,
@ -2022,8 +1909,7 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd,
continue;
def = vector_def[dvert->dw[j].def_nr];
strcpy(tmpname, def->name);
vertgroup_flip_name(tmpname,0);
flip_vertexgroup_name(tmpname, def->name, 0);
for(b = 0, defb = ob->defbase.first; defb;
defb = defb->next, b++)

View File

@ -2999,8 +2999,8 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
/* should init_particle_interpolation set this ? */
if(pset->brushtype==PE_BRUSH_WEIGHT){
pind.hkey[0] = pa->hair;
pind.hkey[1] = pa->hair + 1;
pind.hkey[0] = NULL;
pind.hkey[1] = pa->hair;
}
@ -3036,12 +3036,6 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
do_particle_interpolation(psys, i, pa, t, frs_sec, &pind, &result);
/* should init_particle_interpolation set this ? */
if(pset->brushtype==PE_BRUSH_WEIGHT){
pind.hkey[0] = pind.hkey[1];
pind.hkey[1]++;
}
/* non-hair points are already in global space */
if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
mul_m4_v3(hairmat, result.co);
@ -3099,10 +3093,17 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
/* selection coloring in edit mode */
if(pset->brushtype==PE_BRUSH_WEIGHT){
if(k==steps)
if(k==0)
weight_to_rgb(pind.hkey[1]->weight, ca->col, ca->col+1, ca->col+2);
else if(k >= steps - 1)
weight_to_rgb(pind.hkey[0]->weight, ca->col, ca->col+1, ca->col+2);
else
weight_to_rgb((1.0f - keytime) * pind.hkey[0]->weight + keytime * pind.hkey[1]->weight, ca->col, ca->col+1, ca->col+2);
/* at the moment this is only used for weight painting.
* will need to move out of this check if its used elsewhere. */
pind.hkey[0] = pind.hkey[1];
pind.hkey[1]++;
}
else {
if((ekey + (pind.ekey[0] - point->keys))->flag & PEK_SELECT){

View File

@ -120,7 +120,7 @@ void EM_cache_x_mirror_vert(struct Object *ob, struct EditMesh *em)
}
}
void EM_select_mirrored(Object *obedit, EditMesh *em, int extend)
static void EM_select_mirrored(Object *obedit, EditMesh *em, int extend)
{
EditVert *eve;

View File

@ -187,6 +187,7 @@ void OBJECT_OT_vertex_group_levels(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_invert(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_blend(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_clean(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_mirror(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_set_active(struct wmOperatorType *ot);
void OBJECT_OT_game_property_new(struct wmOperatorType *ot);

View File

@ -173,6 +173,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_vertex_group_levels);
WM_operatortype_append(OBJECT_OT_vertex_group_blend);
WM_operatortype_append(OBJECT_OT_vertex_group_clean);
WM_operatortype_append(OBJECT_OT_vertex_group_mirror);
WM_operatortype_append(OBJECT_OT_vertex_group_set_active);
WM_operatortype_append(OBJECT_OT_game_property_new);

View File

@ -903,6 +903,68 @@ static void vgroup_clean_all(Object *ob, float eul, int keep_single)
}
}
static void vgroup_mirror(Object *ob, int mirror_weights, int flip_vgroups)
{
EditVert *eve, *eve_mirr;
MDeformVert *dvert, *dvert_mirr;
int *flip_map;
if(mirror_weights==0 && flip_vgroups==0)
return;
/* only the active group */
if(ob->type == OB_MESH) {
Mesh *me= ob->data;
EditMesh *em = BKE_mesh_get_editmesh(me);
EM_cache_x_mirror_vert(ob, em);
if(!CustomData_has_layer(&em->vdata, CD_MDEFORMVERT))
return;
flip_map= get_defgroup_flip_map(ob);
/* Go through the list of editverts and assign them */
for(eve=em->verts.first; eve; eve=eve->next){
if((eve_mirr=eve->tmp.v)) {
if(eve_mirr->f & SELECT || eve->f & SELECT) {
dvert= CustomData_em_get(&em->vdata, eve->data, CD_MDEFORMVERT);
dvert_mirr= CustomData_em_get(&em->vdata, eve_mirr->data, CD_MDEFORMVERT);
if(dvert && dvert_mirr) {
if(eve_mirr->f & SELECT && eve->f & SELECT) {
/* swap */
if(mirror_weights)
SWAP(MDeformVert, *dvert, *dvert_mirr);
if(flip_vgroups) {
flip_defvert(dvert, flip_map);
flip_defvert(dvert_mirr, flip_map);
}
}
else {
/* dvert should always be the target */
if(eve_mirr->f & SELECT) {
SWAP(MDeformVert *, dvert, dvert_mirr);
}
if(mirror_weights)
copy_defvert(dvert, dvert_mirr);
if(flip_vgroups) {
flip_defvert(dvert, flip_map);
}
}
}
}
eve->tmp.v= eve_mirr->tmp.v= NULL;
}
}
MEM_freeN(flip_map);
BKE_mesh_end_editmesh(me, em);
}
}
static void vgroup_delete_update_users(Object *ob, int id)
{
ExplodeModifierData *emd;
@ -1026,7 +1088,7 @@ static void vgroup_active_remove_verts(Object *ob, int allverts)
for(eve=em->verts.first; eve; eve=eve->next){
dvert= CustomData_em_get(&em->vdata, eve->data, CD_MDEFORMVERT);
if(dvert && dvert->dw && ((eve->f & 1) || allverts)){
if(dvert && dvert->dw && ((eve->f & SELECT) || allverts)){
for(i=0; i<dvert->totweight; i++){
/* Find group */
eg = BLI_findlink(&ob->defbase, dvert->dw[i].def_nr);
@ -1208,7 +1270,7 @@ static void vgroup_assign_verts(Object *ob, float weight)
for(eve=em->verts.first; eve; eve=eve->next){
dvert= CustomData_em_get(&em->vdata, eve->data, CD_MDEFORMVERT);
if(dvert && (eve->f & 1)){
if(dvert && (eve->f & SELECT)){
done=0;
/* See if this vert already has a reference to this group */
/* If so: Change its weight */
@ -1684,6 +1746,40 @@ void OBJECT_OT_vertex_group_clean(wmOperatorType *ot)
}
static int vertex_group_mirror_exec(bContext *C, wmOperator *op)
{
Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
vgroup_mirror(ob, RNA_boolean_get(op->ptr,"mirror_weights"), RNA_boolean_get(op->ptr,"flip_group_names"));
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
return OPERATOR_FINISHED;
}
void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot)
{
/* identifiers */
ot->name= "Mirror Vertex Group";
ot->idname= "OBJECT_OT_vertex_group_mirror";
ot->description= "Mirror weights, and flip vertex group names, copying when only one side is selected.";
/* api callbacks */
ot->poll= vertex_group_poll_edit;
ot->exec= vertex_group_mirror_exec;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna, "mirror_weights", TRUE, "Mirror Weights", "Mirror weights.");
RNA_def_boolean(ot->srna, "flip_group_names", TRUE, "Flip Groups", "Flip vertex group names while mirroring.");
}
static int vertex_group_copy_to_linked_exec(bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);

View File

@ -2366,6 +2366,52 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX, "Threshold", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
}
static int weight_set_exec(bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);
ParticleEditSettings *pset= PE_settings(scene);
Object *ob= CTX_data_active_object(C);
PTCacheEdit *edit= PE_get_current(scene, ob);
ParticleSystem *psys = edit->psys;
POINT_P;
KEY_K;
HairKey *hkey;
float weight;
ParticleBrushData *brush= &pset->brush[pset->brushtype];
edit= psys->edit;
weight= (float)(brush->strength / 100.0f);
LOOP_SELECTED_POINTS {
ParticleData *pa= psys->particles + p;
LOOP_SELECTED_KEYS {
hkey= pa->hair + k;
hkey->weight= weight;
}
}
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
return OPERATOR_FINISHED;
}
void PARTICLE_OT_weight_set(wmOperatorType *ot)
{
/* identifiers */
ot->name= "Weight Set";
ot->idname= "PARTICLE_OT_weight_set";
/* api callbacks */
ot->exec= weight_set_exec;
ot->poll= PE_poll;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
/************************ cursor drawing *******************************/
static void brush_drawcursor(bContext *C, int x, int y, void *customdata)

View File

@ -50,6 +50,7 @@ void PARTICLE_OT_reveal(struct wmOperatorType *ot);
void PARTICLE_OT_rekey(struct wmOperatorType *ot);
void PARTICLE_OT_subdivide(struct wmOperatorType *ot);
void PARTICLE_OT_remove_doubles(struct wmOperatorType *ot);
void PARTICLE_OT_weight_set(struct wmOperatorType *ot);
void PARTICLE_OT_delete(struct wmOperatorType *ot);
void PARTICLE_OT_mirror(struct wmOperatorType *ot);

View File

@ -57,6 +57,7 @@ static void operatortypes_particle(void)
WM_operatortype_append(PARTICLE_OT_rekey);
WM_operatortype_append(PARTICLE_OT_subdivide);
WM_operatortype_append(PARTICLE_OT_remove_doubles);
WM_operatortype_append(PARTICLE_OT_weight_set);
WM_operatortype_append(PARTICLE_OT_delete);
WM_operatortype_append(PARTICLE_OT_mirror);
@ -111,6 +112,8 @@ static void keymap_particle(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "PARTICLE_OT_brush_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
WM_keymap_add_menu(keymap, "VIEW3D_MT_particle_specials", WKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "PARTICLE_OT_weight_set", KKEY, KM_PRESS, KM_SHIFT, 0);
ED_object_generic_keymap(keyconf, keymap, 1);
}