Disconnect/connect hair:

- Moves hair from face-space to global space and back.
- Allows for editing of emitter mesh after hair combing.
- Disconnect hair before doing topology changing changes in mesh edit mode, connect after changes.
- Notes:
	* The closest location on emitter surface to the hair root is used to connect the hair.
	* Emitter deflection, sticky roots and add brush don't apply for disconnect hair in particle mode.
- Todo for future:
	* Copy disconnected hair from object to another (when 2.5 has proper copy operators again).
	* Possible automatic disconnect/connect with topology changing operations in mesh edit mode.
	
Other changes/fixes:
- Proper subtypes for some particle mode notifiers.
- Particle mode selections didn't draw correctly because of using lighting for the paths.
This commit is contained in:
2009-09-05 20:12:08 +00:00
parent 05c44056dc
commit 08b8fc34cf
13 changed files with 294 additions and 43 deletions

View File

@@ -151,6 +151,11 @@ class PARTICLE_PT_particles(ParticleButtonsPanel):
row = split.row()
row.enabled = particle_panel_enabled(psys)
row.itemR(part, "hair_step")
if psys.edited==True:
if psys.global_hair:
layout.itemO("particle.connect_hair")
else:
layout.itemO("particle.disconnect_hair")
elif part.type=='REACTOR':
split.enabled = particle_panel_enabled(psys)
split.itemR(psys, "reactor_target_object")

View File

@@ -192,6 +192,7 @@ typedef struct PTCacheUndo {
struct ParticleData *particles;
struct KDTree *emitter_field;
float *emitter_cosnos;
int psys_flag;
/* cache stuff */
struct ListBase mem_cache;

View File

@@ -418,7 +418,7 @@ void psys_free_path_cache(ParticleSystem *psys, PTCacheEdit *edit)
edit->pathcache= NULL;
edit->totcached= 0;
}
else {
if(psys) {
psys_free_path_cache_buffers(psys->pathcache, &psys->pathcachebufs);
psys->pathcache= NULL;
psys->totcached= 0;
@@ -2676,7 +2676,7 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra
baked = psys->pointcache->flag & PTCACHE_BAKED;
/* clear out old and create new empty path cache */
psys_free_path_cache(psys, NULL);
psys_free_path_cache(psys, psys->edit);
cache= psys->pathcache= psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, steps+1);
if(psys->soft && psys->softflag & OB_SB_ENABLE) {
@@ -2891,7 +2891,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
if(!cache || edit->totpoint != edit->totcached) {
/* clear out old and create new empty path cache */
psys_free_path_cache(NULL, edit);
psys_free_path_cache(edit->psys, edit);
cache= edit->pathcache= psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, steps+1);
}
@@ -2946,7 +2946,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
do_particle_interpolation(psys, i, pa, t, frs_sec, &pind, &result);
/* non-hair points are allready in global space */
if(psys)
if(psys && !(psys->flag & PSYS_GLOBAL_HAIR))
Mat4MulVecfl(hairmat, result.co);
VECCOPY(ca->co, result.co);

View File

@@ -158,7 +158,7 @@ void psys_reset(ParticleSystem *psys, int mode)
psys->totchild= 0;
/* reset path cache */
psys_free_path_cache(psys, NULL);
psys_free_path_cache(psys, psys->edit);
/* reset point cache */
psys->pointcache->flag &= ~PTCACHE_SIMULATION_VALID;

View File

@@ -555,7 +555,7 @@ static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected
Mat4One(mat);
LOOP_VISIBLE_POINTS {
if(edit->psys) {
if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
Mat4Invert(imat,mat);
}
@@ -812,7 +812,7 @@ static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
float *vec, *nor, dvec[3], dot, dist_1st;
float hairimat[4][4], hairmat[4][4];
if(edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0)
if(edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
return;
psys = edit->psys;
@@ -876,6 +876,9 @@ void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
return;
if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
return;
LOOP_EDITED_POINTS {
LOOP_KEYS {
if(k) {
@@ -899,10 +902,10 @@ static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
float dv1[3]= {0.0f, 0.0f, 0.0f};
float dv2[3]= {0.0f, 0.0f, 0.0f};
if(edit==0)
if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
return;
if((pset->flag & PE_KEEP_LENGTHS)==0)
if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
return;
LOOP_EDITED_POINTS {
@@ -1057,11 +1060,13 @@ static void update_world_cos(Object *ob, PTCacheEdit *edit)
return;
LOOP_POINTS {
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles+p, hairmat);
if(!(psys->flag & PSYS_GLOBAL_HAIR))
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles+p, hairmat);
LOOP_KEYS {
VECCOPY(key->world_co,key->co);
Mat4MulVecfl(hairmat, key->world_co);
if(!(psys->flag & PSYS_GLOBAL_HAIR))
Mat4MulVecfl(hairmat, key->world_co);
}
}
}
@@ -1480,7 +1485,7 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
Mat4One(mat);
LOOP_VISIBLE_POINTS {
if(edit->psys)
if(edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + p, mat);
if(pset->selectmode==SCE_SELECT_POINT) {
@@ -1777,7 +1782,8 @@ static void rekey_particle(PEData *data, int pa_index)
for(k=0, key=pa->hair; k<pa->totkey; k++, key++, ekey++) {
ekey->co= key->co;
ekey->time= &key->time;
ekey->flag |= PEK_USE_WCO;
if(!(psys->flag & PSYS_GLOBAL_HAIR))
ekey->flag |= PEK_USE_WCO;
}
pa->flag &= ~PARS_REKEY;
@@ -2059,7 +2065,9 @@ static void subdivide_particle(PEData *data, int pa_index)
nekey->co= nkey->co;
nekey->time= &nkey->time;
nekey->flag |= (PEK_SELECT|PEK_USE_WCO);
nekey->flag |= PEK_SELECT;
if(!(psys->flag & PSYS_GLOBAL_HAIR))
nekey->flag |= PEK_USE_WCO;
nekey++;
nkey++;
@@ -2129,6 +2137,9 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
int n, totn, removed, flag, totremoved;
if(psys->flag & PSYS_GLOBAL_HAIR)
return OPERATOR_CANCELLED;
edit= psys->edit;
psmd= psys_get_modifier(ob, psys);
totremoved= 0;
@@ -2400,6 +2411,9 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
int *mirrorfaces;
int rotation, totpart, newtotpart;
if(psys->flag & PSYS_GLOBAL_HAIR)
return;
psmd= psys_get_modifier(ob, psys);
mirrorfaces= mesh_get_x_mirror_faces(ob, NULL);
@@ -2750,7 +2764,7 @@ static void brush_puff(PEData *data, int point_index)
float mat[4][4], imat[4][4];
float lastco[3], rootco[3], co[3], nor[3], kco[3], dco[3], fac, length;
if(psys) {
if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
Mat4Invert(imat,mat);
}
@@ -2849,6 +2863,9 @@ static void brush_add(PEData *data, short number)
DerivedMesh *dm=0;
Mat4Invert(imat,ob->obmat);
if(psys->flag & PSYS_GLOBAL_HAIR)
return;
BLI_srandom(psys->seed+data->mval[0]+data->mval[1]);
/* painting onto the deformed mesh, could be an option? */
@@ -3070,6 +3087,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
float vec[3], mousef[2];
short mval[2], mvalo[2];
int flip, mouse[2], dx, dy, removed= 0, selected= 0;
int lock_root = pset->flag & PE_LOCK_FIRST;
if(!PE_start_edit(edit))
return;
@@ -3093,6 +3111,10 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
mvalo[0]= bedit->lastmouse[0];
mvalo[1]= bedit->lastmouse[1];
/* disable locking temporatily for disconnected hair */
if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
pset->flag &= ~PE_LOCK_FIRST;
if(((pset->brushtype == PE_BRUSH_ADD) ?
(sqrt(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0))
|| bedit->first) {
@@ -3248,6 +3270,8 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
bedit->lastmouse[1]= mouse[1];
bedit->first= 0;
}
pset->flag |= lock_root;
}
static void brush_edit_exit(bContext *C, wmOperator *op)
@@ -3382,6 +3406,8 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
for(i=0; i<edit->totpoint; i++, pa++)
pa->hair= MEM_dupallocN(pa->hair);
undo->psys_flag = edit->psys->flag;
}
else {
PTCacheMem *pm;
@@ -3449,6 +3475,8 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
hkey++;
}
}
psys->flag = undo->psys_flag;
}
else {
PTCacheMem *pm;
@@ -3704,7 +3732,8 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
key->co= hkey->co;
key->time= &hkey->time;
key->flag= hkey->editflag;
key->flag |= PEK_USE_WCO;
if(!(psys->flag & PSYS_GLOBAL_HAIR))
key->flag |= PEK_USE_WCO;
hkey++;
}
pa++;
@@ -3828,6 +3857,7 @@ static int clear_edited_exec(bContext *C, wmOperator *op)
psys->free_edit = NULL;
psys->recalc |= PSYS_RECALC_RESET;
psys->flag &= ~PSYS_GLOBAL_HAIR;
psys_reset(psys, PSYS_RESET_DEPSGRAPH);
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);

View File

@@ -85,6 +85,8 @@ void PARTICLE_OT_new_target(struct wmOperatorType *ot);
void PARTICLE_OT_remove_target(struct wmOperatorType *ot);
void PARTICLE_OT_target_move_up(struct wmOperatorType *ot);
void PARTICLE_OT_target_move_down(struct wmOperatorType *ot);
void PARTICLE_OT_connect_hair(struct wmOperatorType *ot);
void PARTICLE_OT_disconnect_hair(struct wmOperatorType *ot);
void SCENE_OT_render_layer_add(struct wmOperatorType *ot);
void SCENE_OT_render_layer_remove(struct wmOperatorType *ot);

View File

@@ -35,6 +35,8 @@
#include "DNA_group_types.h"
#include "DNA_object_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_texture_types.h"
#include "DNA_scene_types.h"
@@ -42,8 +44,11 @@
#include "DNA_space_types.h"
#include "DNA_world_types.h"
#include "BKE_bvhutils.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_group.h"
#include "BKE_font.h"
#include "BKE_library.h"
@@ -51,11 +56,13 @@
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
#include "BKE_world.h"
#include "BLI_arithb.h"
#include "BLI_editVert.h"
#include "BLI_listbase.h"
@@ -838,6 +845,217 @@ void PARTICLE_OT_target_move_down(wmOperatorType *ot)
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
/************************ connect/disconnect hair operators *********************/
static void disconnect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
{
ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
ParticleData *pa = psys->particles;
PTCacheEdit *edit = psys->edit;
PTCacheEditPoint *point = edit ? edit->points : NULL;
PTCacheEditKey *ekey = NULL;
HairKey *key;
int i, k;
float hairmat[4][4];
if(!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR)
return;
if(!psys->part || psys->part->type != PART_HAIR)
return;
for(i=0; i<psys->totpart; i++,pa++) {
if(point) {
ekey = point->keys;
point++;
}
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
Mat4MulVecfl(hairmat,key->co);
if(ekey) {
ekey->flag &= ~PEK_USE_WCO;
ekey++;
}
}
}
psys_free_path_cache(psys, psys->edit);
psys->flag |= PSYS_GLOBAL_HAIR;
PE_update_object(scene, ob, 0);
}
static int disconnect_hair_exec(bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);
Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys= NULL;
int all = RNA_boolean_get(op->ptr, "all");
if(!ob)
return OPERATOR_CANCELLED;
if(all) {
for(psys=ob->particlesystem.first; psys; psys=psys->next) {
disconnect_hair(scene, ob, psys);
}
}
else {
psys = ptr.data;
disconnect_hair(scene, ob, psys);
}
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
return OPERATOR_FINISHED;
}
void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
{
ot->name= "Disconnect Hair";
ot->description= "Disconnect hair from the emitter mesh.";
ot->idname= "PARTICLE_OT_disconnect_hair";
ot->exec= disconnect_hair_exec;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh");
}
static void connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
{
ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
ParticleData *pa = psys->particles;
PTCacheEdit *edit = psys->edit;
PTCacheEditPoint *point = edit ? edit->points : NULL;
PTCacheEditKey *ekey;
HairKey *key;
BVHTreeFromMesh bvhtree;
BVHTreeNearest nearest;
MFace *mface;
DerivedMesh *dm = CDDM_copy(psmd->dm);
int numverts = dm->getNumVerts (dm);
int i, k;
float hairmat[4][4], imat[4][4];
float v[4][3], vec[3];
if(!psys || !psys->part || psys->part->type != PART_HAIR)
return;
memset( &bvhtree, 0, sizeof(bvhtree) );
/* convert to global coordinates */
for (i=0; i<numverts; i++)
Mat4MulVecfl (ob->obmat, CDDM_get_vert(dm, i)->co);
bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
for(i=0; i<psys->totpart; i++,pa++) {
key = pa->hair;
nearest.index = -1;
nearest.dist = FLT_MAX;
BLI_bvhtree_find_nearest(bvhtree.tree, key->co, &nearest, bvhtree.nearest_callback, &bvhtree);
if(nearest.index == -1) {
printf("No nearest point found for hair root!");
continue;
}
mface = CDDM_get_face(dm,nearest.index);
VecCopyf(v[0], CDDM_get_vert(dm,mface->v1)->co);
VecCopyf(v[1], CDDM_get_vert(dm,mface->v2)->co);
VecCopyf(v[2], CDDM_get_vert(dm,mface->v3)->co);
if(mface->v4) {
VecCopyf(v[3], CDDM_get_vert(dm,mface->v4)->co);
MeanValueWeights(v, 4, nearest.co, pa->fuv);
}
else
MeanValueWeights(v, 3, nearest.co, pa->fuv);
pa->num = nearest.index;
pa->num_dmcache = psys_particle_dm_face_lookup(ob,psmd->dm,pa->num,pa->fuv,NULL);
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
Mat4Invert(imat,hairmat);
VECSUB(vec, nearest.co, key->co);
if(point) {
ekey = point->keys;
point++;
}
for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
VECADD(key->co, key->co, vec);
Mat4MulVecfl(imat,key->co);
if(ekey) {
ekey->flag |= PEK_USE_WCO;
ekey++;
}
}
}
free_bvhtree_from_mesh(&bvhtree);
dm->release(dm);
psys_free_path_cache(psys, psys->edit);
psys->flag &= ~PSYS_GLOBAL_HAIR;
PE_update_object(scene, ob, 0);
}
static int connect_hair_exec(bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);
Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys= NULL;
int all = RNA_boolean_get(op->ptr, "all");
if(!ob)
return OPERATOR_CANCELLED;
if(all) {
for(psys=ob->particlesystem.first; psys; psys=psys->next) {
connect_hair(scene, ob, psys);
}
}
else {
psys = ptr.data;
connect_hair(scene, ob, psys);
}
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
return OPERATOR_FINISHED;
}
void PARTICLE_OT_connect_hair(wmOperatorType *ot)
{
ot->name= "Connect Hair";
ot->description= "Connect hair to the emitter mesh.";
ot->idname= "PARTICLE_OT_connect_hair";
ot->exec= connect_hair_exec;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh");
}
/********************** render layer operators *********************/
static int render_layer_add_exec(bContext *C, wmOperator *op)

View File

@@ -204,6 +204,8 @@ void buttons_operatortypes(void)
WM_operatortype_append(PARTICLE_OT_remove_target);
WM_operatortype_append(PARTICLE_OT_target_move_up);
WM_operatortype_append(PARTICLE_OT_target_move_down);
WM_operatortype_append(PARTICLE_OT_connect_hair);
WM_operatortype_append(PARTICLE_OT_disconnect_hair);
WM_operatortype_append(SCENE_OT_render_layer_add);
WM_operatortype_append(SCENE_OT_render_layer_remove);

View File

@@ -3749,7 +3749,6 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obj
nosel_col[1]=(float)nosel[1]/255.0f;
nosel_col[2]=(float)nosel[2]/255.0f;
/* draw paths */
if(timed) {
glEnable(GL_BLEND);
@@ -3758,24 +3757,16 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obj
}
glEnableClientState(GL_VERTEX_ARRAY);
/* solid shaded with lighting */
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
/* only draw child paths with lighting */
if(dt > OB_WIRE)
glEnable(GL_LIGHTING);
/* draw paths without lighting */
cache=edit->pathcache;
for(i=0; i<totpoint; i++){
path = cache[i];
glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel);
if(timed) {
for(k=0, pcol=pathcol, pkey=path; k<steps; k++, pkey++, pcol+=4){
@@ -3796,9 +3787,6 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obj
/* draw edit vertices */
if(pset->selectmode!=SCE_SELECT_PATH){
glDisableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDisable(GL_LIGHTING);
glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE));
if(pset->selectmode==SCE_SELECT_POINT){
@@ -3810,7 +3798,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obj
if(!(point->flag & PEP_HIDE))
totkeys += point->totkey;
if(!edit->psys)
if(!(edit->points->keys->flag & PEK_USE_WCO))
pd=pdata=MEM_callocN(totkeys*3*sizeof(float), "particle edit point data");
cd=cdata=MEM_callocN(totkeys*(timed?4:3)*sizeof(float), "particle edit color data");
@@ -3843,7 +3831,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obj
if(point->flag & PEP_HIDE)
continue;
if(edit->psys)
if(point->keys->flag & PEK_USE_WCO)
glVertexPointer(3, GL_FLOAT, sizeof(PTCacheEditKey), point->keys->world_co);
else
glVertexPointer(3, GL_FLOAT, 3*sizeof(float), pd);

View File

@@ -1760,17 +1760,17 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event)
case B_SEL_PATH:
ts->particle.selectmode= SCE_SELECT_PATH;
WM_event_add_notifier(C, NC_OBJECT, ob);
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
ED_undo_push(C, "Selectmode Set: Path");
break;
case B_SEL_POINT:
ts->particle.selectmode = SCE_SELECT_POINT;
WM_event_add_notifier(C, NC_OBJECT, ob);
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
ED_undo_push(C, "Selectmode Set: Point");
break;
case B_SEL_END:
ts->particle.selectmode = SCE_SELECT_END;
WM_event_add_notifier(C, NC_OBJECT, ob);
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
ED_undo_push(C, "Selectmode Set: End point");
break;

View File

@@ -1690,11 +1690,11 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
if(!(point->flag & PEP_TRANSFORM)) continue;
if(psys)
if(psys && !(psys->flag & PSYS_GLOBAL_HAIR))
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + i, mat);
for(k=0, key=point->keys; k<point->totkey; k++, key++) {
if(psys) {
if(key->flag & PEK_USE_WCO) {
VECCOPY(key->world_co, key->co);
Mat4MulVecfl(mat, key->world_co);
td->loc = key->world_co;
@@ -1714,7 +1714,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
Mat3One(td->smtx);
/* don't allow moving roots */
if(k==0 && pset->flag & PE_LOCK_FIRST)
if(k==0 && pset->flag & PE_LOCK_FIRST && (!psys || !(psys->flag & PSYS_GLOBAL_HAIR)))
td->protectflag |= OB_LOCK_LOC;
td->ob = ob;
@@ -1764,7 +1764,7 @@ void flushTransParticles(TransInfo *t)
for(i=0, point=edit->points; i<edit->totpoint; i++, point++, td++) {
if(!(point->flag & PEP_TRANSFORM)) continue;
if(psys) {
if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + i, mat);
Mat4Invert(imat,mat);

View File

@@ -1884,6 +1884,11 @@ static void rna_def_particle_system(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "softflag", OB_SB_ENABLE);
RNA_def_property_ui_text(prop, "Use Soft Body", "Enable use of soft body for hair physics simulation.");
prop= RNA_def_property(srna, "global_hair", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_GLOBAL_HAIR);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Global Hair", "Hair keys are in global coordinate space");
/* reactor */
prop= RNA_def_property(srna, "reactor_target_object", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "target_ob");

View File

@@ -107,7 +107,7 @@ static void rna_ParticleEdit_redo(bContext *C, PointerRNA *ptr)
if(!edit)
return;
psys_free_path_cache(NULL, edit);
psys_free_path_cache(edit->psys, edit);
}
static void rna_ParticleEdit_update(bContext *C, PointerRNA *ptr)
@@ -410,7 +410,7 @@ static void rna_def_particle_edit(BlenderRNA *brna)
prop= RNA_def_property(srna, "fade_time", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_FADE_TIME);
RNA_def_property_ui_text(prop, "Fade Time", "Fade paths and keys further away from current frame.");
RNA_def_property_update(prop, NC_OBJECT, "rna_ParticleEdit_update");
RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_ParticleEdit_update");
prop= RNA_def_property(srna, "auto_velocity", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_AUTO_VELOCITY);
@@ -443,18 +443,18 @@ static void rna_def_particle_edit(BlenderRNA *brna)
prop= RNA_def_property(srna, "draw_step", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 2, 10);
RNA_def_property_ui_text(prop, "Steps", "How many steps to draw the path with.");
RNA_def_property_update(prop, NC_OBJECT, "rna_ParticleEdit_redo");
RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_ParticleEdit_redo");
prop= RNA_def_property(srna, "fade_frames", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 2, 100);
RNA_def_property_ui_text(prop, "Frames", "How many frames to fade.");
RNA_def_property_update(prop, NC_OBJECT, "rna_ParticleEdit_update");
RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_ParticleEdit_update");
prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "edittype");
RNA_def_property_enum_items(prop, edit_type_items);
RNA_def_property_ui_text(prop, "Type", "");
RNA_def_property_update(prop, NC_OBJECT, "rna_ParticleEdit_redo");
RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_ParticleEdit_redo");
prop= RNA_def_property(srna, "editable", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_ParticleEdit_editable_get", NULL);