Untitled diff

Created Diff never expires
/*
/*
* This program is free software; you can redistribute it and/or
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* of the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* The Original Code is Copyright (C) 2007 by Janne Karhu.
* The Original Code is Copyright (C) 2007 by Janne Karhu.
* All rights reserved.
* All rights reserved.
*/
*/


/** \file
/** \file blender/editors/physics/particle_edit.c
* \ingroup edphys
* \ingroup edphys
*/
*/


#include <stdlib.h>
#include <stdlib.h>
#include <math.h>
#include <math.h>
#include <string.h>
#include <string.h>
#include <assert.h>
#include <assert.h>


#include "MEM_guardedalloc.h"
#include "MEM_guardedalloc.h"


#include "DNA_scene_types.h"
#include "DNA_scene_types.h"
#include "DNA_mesh_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_view3d_types.h"
#include "DNA_view3d_types.h"
#include "DNA_screen_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_space_types.h"


#include "BLI_math.h"
#include "BLI_math.h"
#include "BLI_lasso_2d.h"
#include "BLI_lasso_2d.h"
#include "BLI_listbase.h"
#include "BLI_listbase.h"
#include "BLI_kdtree.h"
#include "BLI_kdtree.h"
#include "BLI_rand.h"
#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLI_utildefines.h"


#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_global.h"
#include "BKE_object.h"
#include "BKE_main.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_particle.h"
#include "BKE_report.h"
#include "BKE_bvhutils.h"
#include "BKE_pointcache.h"
#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_scene.h"


#include "DEG_depsgraph.h"
#include "BIF_gl.h"

#include "BIF_glutil.h"


#include "ED_object.h"
#include "ED_object.h"
#include "ED_physics.h"
#include "ED_physics.h"
#include "ED_mesh.h"
#include "ED_mesh.h"
#include "ED_particle.h"
#include "ED_particle.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_view3d.h"
#include "ED_view3d.h"


#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_state.h"

#include "UI_resources.h"
#include "UI_resources.h"


#include "WM_api.h"
#include "WM_api.h"
#include "WM_types.h"
#include "WM_types.h"
#include "WM_message.h"
#include "WM_toolsystem.h"


#include "RNA_access.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_define.h"


#include "DEG_depsgraph_query.h"

#include "PIL_time_utildefines.h"

#include "physics_intern.h"
#include "physics_intern.h"


#include "particle_edit_utildefines.h"
#include "particle_edit_utildefines.h"


/**************************** utilities *******************************/
/**************************** utilities *******************************/


bool PE_poll(bContext *C)
bool PE_poll(bContext *C)
{
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Object *ob = CTX_data_active_object(C);


if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
return 0;
return 0;
}

return (PE_get_current(scene, ob) != NULL);
return (PE_get_current(bmain, scene, ob) != NULL);
}
}


bool PE_hair_poll(bContext *C)
bool PE_hair_poll(bContext *C)
{
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Object *ob = CTX_data_active_object(C);
PTCacheEdit *edit;
PTCacheEdit *edit;


if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
return 0;
return 0;
}

edit = PE_get_current(scene, ob);
edit = PE_get_current(bmain, scene, ob);


return (edit && edit->psys);
return (edit && edit->psys);
}
}


bool PE_poll_view3d(bContext *C)
bool PE_poll_view3d(bContext *C)
{
{
ScrArea *sa = CTX_wm_area(C);
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
ARegion *ar = CTX_wm_region(C);


return (PE_poll(C) &&
return (PE_poll(C) &&
(sa && sa->spacetype == SPACE_VIEW3D) &&
(sa && sa->spacetype == SPACE_VIEW3D) &&
(ar && ar->regiontype == RGN_TYPE_WINDOW));
(ar && ar->regiontype == RGN_TYPE_WINDOW));
}
}


void PE_free_ptcache_edit(PTCacheEdit *edit)
void PE_free_ptcache_edit(PTCacheEdit *edit)
{
{
POINT_P;
POINT_P;


if (edit == 0) return;
if (edit == 0) return;


if (edit->points) {
if (edit->points) {
LOOP_POINTS {
LOOP_POINTS {
if (point->keys)
if (point->keys)
MEM_freeN(point->keys);
MEM_freeN(point->keys);
}
}


MEM_freeN(edit->points);
MEM_freeN(edit->points);
}
}


if (edit->mirror_cache)
if (edit->mirror_cache)
MEM_freeN(edit->mirror_cache);
MEM_freeN(edit->mirror_cache);


if (edit->emitter_cosnos) {
if (edit->emitter_cosnos) {
MEM_freeN(edit->emitter_cosnos);
MEM_freeN(edit->emitter_cosnos);
edit->emitter_cosnos = 0;
edit->emitter_cosnos = 0;
}
}


if (edit->emitter_field) {
if (edit->emitter_field) {
BLI_kdtree_3d_free(edit->emitter_field);
BLI_kdtree_free(edit->emitter_field);
edit->emitter_field = 0;
edit->emitter_field = 0;
}
}


psys_free_path_cache(edit->psys, edit);
psys_free_path_cache(edit->psys, edit);


MEM_freeN(edit);
MEM_freeN(edit);
}
}


/************************************************/
/************************************************/
/* Edit Mode Helpers */
/* Edit Mode Helpers */
/************************************************/
/************************************************/


int PE_start_edit(PTCacheEdit *edit)
int PE_start_edit(PTCacheEdit *edit)
{
{
if (edit) {
if (edit) {
edit->edited = 1;
edit->edited = 1;
if (edit->psys)
if (edit->psys)
edit->psys->flag |= PSYS_EDITED;
edit->psys->flag |= PSYS_EDITED;
return 1;
return 1;
}
}


return 0;
return 0;
}
}


ParticleEditSettings *PE_settings(Scene *scene)
ParticleEditSettings *PE_settings(Scene *scene)
{
{
return scene->toolsettings ? &scene->toolsettings->particle : NULL;
return scene->toolsettings ? &scene->toolsettings->particle : NULL;
}
}


static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *brush)
static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *brush)
{
{
// here we can enable unified brush size, needs more work...
// here we can enable unified brush size, needs more work...
// UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
// UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
// float size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;
// float size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;


return brush->size * U.pixelsize;
return brush->size * U.pixelsize;
}
}


PTCacheEdit *PE_get_current_from_psys(ParticleSystem *psys)
{
if (psys->part && psys->part->type == PART_HAIR) {
if ((psys->flag & PSYS_HAIR_DYNAMICS) != 0 &&
(psys->pointcache->flag & PTCACHE_BAKED) != 0)
{
return psys->pointcache->edit;
}
else {
return psys->edit;
}
}
else if (psys->pointcache->flag & PTCACHE_BAKED) {
return psys->pointcache->edit;
}
return NULL;
}

/* NOTE: Similar to creation of edit, but only updates pointers in the
* existing struct.
*/
static void pe_update_hair_particle_edit_pointers(PTCacheEdit *edit)
{
ParticleSystem *psys = edit->psys;
ParticleData *pa = psys->particles;
for (int p = 0; p < edit->totpoint; p++) {
PTCacheEditPoint *point = &edit->points[p];
HairKey *hair_key = pa->hair;
for (int k = 0; k < point->totkey; k++) {
PTCacheEditKey *key = &point->keys[k];
key->co = hair_key->co;
key->time = &hair_key->time;
key->flag = hair_key->editflag;
if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
key->flag |= PEK_USE_WCO;
hair_key->editflag |= PEK_USE_WCO;
}
hair_key++;
}
pa++;
}
}


/* always gets at least the first particlesystem even if PSYS_CURRENT flag is not set
/* always gets at least the first particlesystem even if PSYS_CURRENT flag is not set
*
*
* note: this function runs on poll, therefor it can runs many times a second
* note: this function runs on poll, therefor it can runs many times a second
* keep it fast! */
* keep it fast! */
static PTCacheEdit *pe_get_current(
static PTCacheEdit *pe_get_current(Main *bmain, Scene *scene, Object *ob, int create)
Depsgraph *depsgraph, Scene *scene, Object *ob, int create)
{
{
ParticleEditSettings *pset = PE_settings(scene);
ParticleEditSettings *pset = PE_settings(scene);
PTCacheEdit *edit = NULL;
PTCacheEdit *edit = NULL;
ListBase pidlist;
ListBase pidlist;
PTCacheID *pid;
PTCacheID *pid;


if (pset == NULL || ob == NULL)
if (pset == NULL || ob == NULL)
return NULL;
return NULL;


pset->scene = scene;
pset->scene = scene;
pset->object = ob;
pset->object = ob;


BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
BKE_ptcache_ids_from_object(bmain, &pidlist, ob, NULL, 0);


/* in the case of only one editable thing, set pset->edittype accordingly */
/* in the case of only one editable thing, set pset->edittype accordingly */
if (BLI_listbase_is_single(&pidlist)) {
if (BLI_listbase_is_single(&pidlist)) {
pid = pidlist.first;
pid = pidlist.first;
switch (pid->type) {
switch (pid->type) {
case PTCACHE_TYPE_PARTICLES:
case PTCACHE_TYPE_PARTICLES:
pset->edittype = PE_TYPE_PARTICLES;
pset->edittype = PE_TYPE_PARTICLES;
break;
break;
case PTCACHE_TYPE_SOFTBODY:
case PTCACHE_TYPE_SOFTBODY:
pset->edittype = PE_TYPE_SOFTBODY;
pset->edittype = PE_TYPE_SOFTBODY;
break;
break;
case PTCACHE_TYPE_CLOTH:
case PTCACHE_TYPE_CLOTH:
pset->edittype = PE_TYPE_CLOTH;
pset->edittype = PE_TYPE_CLOTH;
break;
break;
}
}
}
}


for (pid = pidlist.first; pid; pid = pid->next) {
for (pid = pidlist.first; pid; pid = pid->next) {
if (pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
if (pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
ParticleSystem *psys = pid->calldata;
ParticleSystem *psys = pid->calldata;


if (psys->flag & PSYS_CURRENT) {
if (psys->flag & PSYS_CURRENT) {
if (psys->part && psys->part->type == PART_HAIR) {
if (psys->part && psys->part->type == PART_HAIR) {
if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
if (create && !psys->pointcache->edit)
if (create && !psys->pointcache->edit)
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
PE_create_particle_edit(bmain, scene, ob, pid->cache, NULL);
edit = pid->cache->edit;
edit = pid->cache->edit;
}
}
else {
else {
if (create && !psys->edit) {
if (create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
if (psys->flag & PSYS_HAIR_DONE) {
PE_create_particle_edit(bmain, scene, ob, NULL, psys);
PE_create_particle_edit(depsgraph, scene, ob, NULL, psys);
}
}
edit = psys->edit;
edit = psys->edit;
}
}
}
}
else {
else {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, psys);
PE_create_particle_edit(bmain, scene, ob, pid->cache, psys);
edit = pid->cache->edit;
edit = pid->cache->edit;
}
}


break;
break;
}
}
}
}
else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
pset->flag |= PE_FADE_TIME;
pset->flag |= PE_FADE_TIME;
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
PE_create_particle_edit(bmain, scene, ob, pid->cache, NULL);
}
}
edit = pid->cache->edit;
edit = pid->cache->edit;
break;
break;
}
}
else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
pset->flag |= PE_FADE_TIME;
pset->flag |= PE_FADE_TIME;
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
PE_create_particle_edit(bmain, scene, ob, pid->cache, NULL);
}
}
edit = pid->cache->edit;
edit = pid->cache->edit;
break;
break;
}
}
}
}


if (edit) {
if (edit)
edit->pid = *pid;
edit->pid = *pid;
if (edit->flags & PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL) {
if (edit->psys != NULL) {
psys_copy_particles(edit->psys, edit->psys_eval);
pe_update_hair_particle_edit_pointers(edit);
}
edit->flags &= ~PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL;
}
}


BLI_freelistN(&pidlist);
BLI_freelistN(&pidlist);


return edit;
return edit;
}
}


PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
PTCacheEdit *PE_get_current(Main *bmain, Scene *scene, Object *ob)
{
{
return pe_get_current(NULL, scene, ob, 0);
return pe_get_current(bmain, scene, ob, 0);
}
}


PTCacheEdit *PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
PTCacheEdit *PE_create_current(Main *bmain, Scene *scene, Object *ob)
{
{
return pe_get_current(depsgraph, scene, ob, 1);
return pe_get_current(bmain, scene, ob, 1);
}
}


void PE_current_changed(Depsgraph *depsgraph, Scene *scene, Object *ob)
void PE_current_changed(Main *bmain, Scene *scene, Object *ob)
{
{
if (ob->mode == OB_MODE_PARTICLE_EDIT) {
if (ob->mode == OB_MODE_PARTICLE_EDIT)
PE_create_current(depsgraph, scene, ob);
PE_create_current(bmain, scene, ob);
}
}
}


void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
{
{
ParticleEditSettings *pset = PE_settings(scene);
ParticleEditSettings *pset = PE_settings(scene);
POINT_P; KEY_K;
POINT_P; KEY_K;




if (pset->flag & PE_FADE_TIME && pset->selectmode == SCE_SELECT_POINT) {
if (pset->flag & PE_FADE_TIME && pset->selectmode == SCE_SELECT_POINT) {
LOOP_POINTS {
LOOP_POINTS {
LOOP_KEYS {
LOOP_KEYS {
if (fabsf(cfra - *key->time) < pset->fade_frames)
if (fabsf(cfra - *key->time) < pset->fade_frames)
key->flag &= ~PEK_HIDE;
key->flag &= ~PEK_HIDE;
else {
else {
key->flag |= PEK_HIDE;
key->flag |= PEK_HIDE;
//key->flag &= ~PEK_SELECT;
//key->flag &= ~PEK_SELECT;
}
}
}
}
}
}
}
}
else {
else {
LOOP_POINTS {
LOOP_POINTS {
LOOP_KEYS {
LOOP_KEYS {
key->flag &= ~PEK_HIDE;
key->flag &= ~PEK_HIDE;
}
}
}
}
}
}
}
}


static int pe_x_mirror(Object *ob)
static int pe_x_mirror(Object *ob)
{
{
if (ob->type == OB_MESH)
if (ob->type == OB_MESH)
return (((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X);
return (((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X);


return 0;
return 0;
}
}


/****************** common struct passed to callbacks ******************/
/****************** common struct passed to callbacks ******************/


typedef struct PEData {
typedef struct PEData {
ViewContext vc;
ViewContext vc;
bglMats mats;


const bContext *context;
Main *bmain;
Main *bmain;
Scene *scene;
Scene *scene;
ViewLayer *view_layer;
Object *ob;
Object *ob;
Mesh *mesh;
DerivedMesh *dm;
PTCacheEdit *edit;
PTCacheEdit *edit;
BVHTreeFromMesh shape_bvh;
BVHTreeFromMesh shape_bvh;
Depsgraph *depsgraph;

RNG *rng;


const int *mval;
const int *mval;
const rcti *rect;
const rcti *rect;
float rad;
float rad;
float dist;
float dval;
float dval;
int select;
int select;
eSelectOp sel_op;


float *dvec;
float *dvec;
float combfac;
float combfac;
float pufffac;
float pufffac;
float cutfac;
float cutfac;
float smoothfac;
float smoothfac;
float weightfac;
float weightfac;
float growfac;
float growfac;
int totrekey;
int totrekey;


int invert;
int invert;
int tot;
int tot;
float vec[3];
float vec[3];


int select_action;
int select_action;
int select_toggle_action;
int select_toggle_action;
bool is_changed;
} PEData;
} PEData;


static void PE_set_data(bContext *C, PEData *data)
static void PE_set_data(bContext *C, PEData *data)
{
{
memset(data, 0, sizeof(*data));
memset(data, 0, sizeof(*data));


data->context = C;
data->bmain = CTX_data_main(C);
data->bmain = CTX_data_main(C);
data->scene = CTX_data_scene(C);
data->scene = CTX_data_scene(C);
data->view_layer = CTX_data_view_layer(C);
data->ob = CTX_data_active_object(C);
data->ob = CTX_data_active_object(C);
data->depsgraph = CTX_data_depsgraph(C);
data->edit = PE_get_current(data->bmain, data->scene, data->ob);
data->edit = PE_get_current(data->scene, data->ob);
}
}


static void PE_set_view3d_data(bContext *C, PEData *data)
static void PE_set_view3d_data(bContext *C, PEData *data)
{
{
PE_set_data(C, data);
PE_set_data(C, data);


ED_view3d_viewcontext_init(C, &data->vc);
ED_view3d_viewcontext_init(C, &data->vc);
/* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather than (obmat * viewmat) */
view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);


if (V3D_IS_ZBUF(data->vc.v3d)) {
if (V3D_IS_ZBUF(data->vc.v3d)) {
if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
/* needed or else the draw matrix can be incorrect */
/* needed or else the draw matrix can be incorrect */
view3d_operator_needs_opengl(C);
view3d_operator_needs_opengl(C);


ED_view3d_backbuf_depth_validate(&data->vc);
ED_view3d_backbuf_validate(&data->vc);
/* we may need to force an update here by setting the rv3d as dirty
/* we may need to force an update here by setting the rv3d as dirty
* for now it seems ok, but take care!:
* for now it seems ok, but take care!:
* rv3d->depths->dirty = 1; */
* rv3d->depths->dirty = 1; */
ED_view3d_depth_update(data->vc.ar);
ED_view3d_depth_update(data->vc.ar);
}
}
}
}
}
}


static bool PE_create_shape_tree(PEData *data, Object *shapeob)
static bool PE_create_shape_tree(PEData *data, Object *shapeob)
{
{
Mesh *mesh = BKE_object_get_evaluated_mesh(data->depsgraph, shapeob);
DerivedMesh *dm = shapeob->derivedFinal;


memset(&data->shape_bvh, 0, sizeof(data->shape_bvh));
memset(&data->shape_bvh, 0, sizeof(data->shape_bvh));


if (!mesh) {
if (!dm) {
return false;
return false;
}
}


return (BKE_bvhtree_from_mesh_get(&data->shape_bvh, mesh, BVHTREE_FROM_LOOPTRI, 4) != NULL);
return (bvhtree_from_mesh_get(&data->shape_bvh, dm, BVHTREE_FROM_LOOPTRI, 4) != NULL);
}
}


static void PE_free_shape_tree(PEData *data)
static void PE_free_shape_tree(PEData *data)
{
{
free_bvhtree_from_mesh(&data->shape_bvh);
free_bvhtree_from_mesh(&data->shape_bvh);
}
}


static void PE_create_random_generator(PEData *data)
{
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
rng_seed ^= POINTER_AS_UINT(data->ob);
rng_seed ^= POINTER_AS_UINT(data->edit);
data->rng = BLI_rng_new(rng_seed);
}

static void PE_free_random_generator(PEData *data)
{
if (data->rng != NULL) {
BLI_rng_free(data->rng);
data->rng = NULL;
}
}

/*************************** selection utilities *******************************/
/*************************** selection utilities *******************************/


static bool key_test_depth(PEData *data, const float co[3], const int screen_co[2])
static bool key_test_depth(PEData *data, const float co[3], const int screen_co[2])
{
{
View3D *v3d = data->vc.v3d;
View3D *v3d = data->vc.v3d;
ViewDepths *vd = data->vc.rv3d->depths;
ViewDepths *vd = data->vc.rv3d->depths;
double ux, uy, uz;
float depth;
float depth;


/* nothing to do */
/* nothing to do */
if (!V3D_IS_ZBUF(v3d))
if (!V3D_IS_ZBUF(v3d))
return true;
return true;


/* used to calculate here but all callers have the screen_co already, so pass as arg */
/* used to calculate here but all callers have the screen_co already, so pass as arg */
#if 0
#if 0
if (ED_view3d_project_int_global(data->vc.ar, co, screen_co,
if (ED_view3d_project_int_global(data->vc.ar, co, screen_co,
V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
{
{
return 0;
return 0;
}
}
#endif
#endif


gluProject(co[0], co[1], co[2], data->mats.modelview, data->mats.projection,
(GLint *)data->mats.viewport, &ux, &uy, &uz);

/* check if screen_co is within bounds because brush_cut uses out of screen coords */
/* check if screen_co is within bounds because brush_cut uses out of screen coords */
if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
BLI_assert(vd && vd->depths);
BLI_assert(vd && vd->depths);
/* we know its not clipped */
/* we know its not clipped */
depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
}
}
else
else
return 0;
return 0;


float win[3];
if ((float)uz - 0.00001f > depth)
ED_view3d_project(data->vc.ar, co, win);

if (win[2] - 0.00001f > depth)
return 0;
return 0;
else
else
return 1;
return 1;
}
}


static bool key_inside_circle(PEData *data, float rad, const float co[3], float *distance)
static bool key_inside_circle(PEData *data, float rad, const float co[3], float *distance)
{
{
float dx, dy, dist;
float dx, dy, dist;
int screen_co[2];
int screen_co[2];


/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
return 0;
return 0;
}
}


dx = data->mval[0] - screen_co[0];
dx = data->mval[0] - screen_co[0];
dy = data->mval[1] - screen_co[1];
dy = data->mval[1] - screen_co[1];
dist = sqrtf(dx * dx + dy * dy);
dist = sqrtf(dx * dx + dy * dy);


if (dist > rad)
if (dist > rad)
return 0;
return 0;


if (key_test_depth(data, co, screen_co)) {
if (key_test_depth(data, co, screen_co)) {
if (distance)
if (distance)
*distance = dist;
*distance = dist;


return 1;
return 1;
}
}


return 0;
return 0;
}
}


static bool key_inside_rect(PEData *data, const float co[3])
static bool key_inside_rect(PEData *data, const float co[3])
{
{
int screen_co[2];
int screen_co[2];


if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
return 0;
return 0;
}
}


if (screen_co[0] > data->rect->xmin && screen_co[0] < data->rect->xmax &&
if (screen_co[0] > data->rect->xmin && screen_co[0] < data->rect->xmax &&
screen_co[1] > data->rect->ymin && screen_co[1] < data->rect->ymax)
screen_co[1] > data->rect->ymin && screen_co[1] < data->rect->ymax)
{
{
return key_test_depth(data, co, screen_co);
return key_test_depth(data, co, screen_co);
}
}


return 0;
return 0;
}
}


static bool key_inside_test(PEData *data, const float co[3])
static bool key_inside_test(PEData *data, const float co[3])
{
{
if (data->mval)
if (data->mval)
return key_inside_circle(data, data->rad, co, NULL);
return key_inside_circle(data, data->rad, co, NULL);
else
else
return key_inside_rect(data, co);
return key_inside_rect(data, co);
}
}


static bool point_is_selected(PTCacheEditPoint *point)
static bool point_is_selected(PTCacheEditPoint *point)
{
{
KEY_K;
KEY_K;


if (point->flag & PEP_HIDE)
if (point->flag & PEP_HIDE)
return 0;
return 0;


LOOP_SELECTED_KEYS {
LOOP_SELECTED_KEYS {
return 1;
return 1;
}
}


return 0;
return 0;
}
}


/*************************** iterators *******************************/
/*************************** iterators *******************************/


typedef void (*ForPointFunc)(
typedef void (*ForPointFunc)(PEData *data, int point_index);
PEData *data,
typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index);
int point_index);
typedef void (*ForKeyMatFunc)(PEData *data, float mat[4][4], float imat[4][4], int point_index, int key_index, PTCacheEditKey *key);
typedef void (*ForHitPointFunc)(
PEData *data,
int point_index,
float mouse_distance);

typedef void (*ForKeyFunc)(
PEData *data,
int point_index,
int key_index,
bool is_inside);

typedef void (*ForKeyMatFunc)(
PEData *data,
float mat[4][4],
float imat[4][4],
int point_index,
int key_index,
PTCacheEditKey *key);
typedef void (*ForHitKeyMatFunc)(
PEData *data,
float mat[4][4],
float imat[4][4],
int point_index,
int key_index,
PTCacheEditKey *key,
float mouse_distance);

enum eParticleSelectFlag {
PSEL_NEAREST = (1 << 0),
PSEL_ALL_KEYS = (1 << 1),
};


static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, const enum eParticleSelectFlag flag)
static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, bool nearest)
{
{
ParticleEditSettings *pset = PE_settings(data->scene);
ParticleEditSettings *pset = PE_settings(data->scene);
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
POINT_P; KEY_K;
POINT_P; KEY_K;
int nearest_point, nearest_key;
int nearest_point, nearest_key;
float dist = data->rad;
float dist = data->rad;


/* in path select mode we have no keys */
/* in path select mode we have no keys */
if (pset->selectmode == SCE_SELECT_PATH)
if (pset->selectmode == SCE_SELECT_PATH)
return;
return;


nearest_point = -1;
nearest_point = -1;
nearest_key = -1;
nearest_key = -1;


LOOP_VISIBLE_POINTS {
LOOP_VISIBLE_POINTS {
if (pset->selectmode == SCE_SELECT_END) {
if (pset->selectmode == SCE_SELECT_END) {
if (point->totkey) {
if (point->totkey) {
/* only do end keys */
/* only do end keys */
key = point->keys + point->totkey - 1;
key = point->keys + point->totkey - 1;


if (flag & PSEL_NEAREST) {
if (nearest) {
if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
nearest_point = p;
nearest_point = p;
nearest_key = point->totkey - 1;
nearest_key = point->totkey - 1;
}
}
}
}
else {
else if (key_inside_test(data, KEY_WCO))
const bool is_inside = key_inside_test(data, KEY_WCO);
func(data, p, point->totkey - 1);
if (is_inside || (flag & PSEL_ALL_KEYS)) {
func(data, p, point->totkey - 1, is_inside);
}
}
}
}
}
}
else {
else {
/* do all keys */
/* do all keys */
LOOP_VISIBLE_KEYS {
LOOP_VISIBLE_KEYS {
if (flag & PSEL_NEAREST) {
if (nearest) {
if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
nearest_point = p;
nearest_point = p;
nearest_key = k;
nearest_key = k;
}
}
}
}
else {
else if (key_inside_test(data, KEY_WCO))
const bool is_inside = key_inside_test(data, KEY_WCO);
func(data, p, k);
if (is_inside || (flag & PSEL_ALL_KEYS)) {
func(data, p, k, is_inside);
}
}
}
}
}
}
}
}


/* do nearest only */
/* do nearest only */
if (flag & PSEL_NEAREST) {
if (nearest && nearest_point > -1)
if (nearest_point != -1) {
func(data, nearest_point, nearest_key);
func(data, nearest_point, nearest_key, true);
}
}
}
}


static void foreach_mouse_hit_point(PEData *data, ForHitPointFunc func, int selected)
static void foreach_mouse_hit_point(PEData *data, ForPointFunc func, int selected)
{
{
ParticleEditSettings *pset = PE_settings(data->scene);
ParticleEditSettings *pset = PE_settings(data->scene);
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
POINT_P; KEY_K;
POINT_P; KEY_K;


/* all is selected in path mode */
/* all is selected in path mode */
if (pset->selectmode == SCE_SELECT_PATH)
if (pset->selectmode == SCE_SELECT_PATH)
selected = 0;
selected = 0;


LOOP_VISIBLE_POINTS {
LOOP_VISIBLE_POINTS {
if (pset->selectmode == SCE_SELECT_END) {
if (pset->selectmode == SCE_SELECT_END) {
if (point->totkey) {
if (point->totkey) {
/* only do end keys */
/* only do end keys */
key = point->keys + point->totkey - 1;
key = point->keys + point->totkey - 1;


if (selected == 0 || key->flag & PEK_SELECT) {
if (selected == 0 || key->flag & PEK_SELECT)
float mouse_distance;
if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
func(data, p);
func(data, p, mouse_distance);
}
}
}
}
}
}
else {
else {
/* do all keys */
/* do all keys */
LOOP_VISIBLE_KEYS {
LOOP_VISIBLE_KEYS {
if (selected == 0 || key->flag & PEK_SELECT) {
if (selected == 0 || key->flag & PEK_SELECT) {
float mouse_distance;
if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
func(data, p);
func(data, p, mouse_distance);
break;
break;
}
}
}
}
}
}
}
}
}
}
}
}


typedef struct KeyIterData {
static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected)
PEData *data;
PTCacheEdit *edit;
int selected;
ForHitKeyMatFunc func;
} KeyIterData;

static void foreach_mouse_hit_key_iter(
void *__restrict iter_data_v,
const int iter,
const ParallelRangeTLS *__restrict UNUSED(tls))
{
{
KeyIterData *iter_data = (KeyIterData *)iter_data_v;
PEData *data = iter_data->data;
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
PTCacheEditPoint *point = &edit->points[iter];
if (point->flag & PEP_HIDE) {
return;
}
ParticleSystem *psys = edit->psys;
ParticleSystem *psys = edit->psys;
ParticleSystemModifierData *psmd_eval = iter_data->edit->psmd_eval;
ParticleSystemModifierData *psmd = NULL;
ParticleEditSettings *pset = PE_settings(data->scene);
ParticleEditSettings *pset = PE_settings(data->scene);
const int selected = iter_data->selected;
POINT_P; KEY_K;
float mat[4][4], imat[4][4];
float mat[4][4], imat[4][4];

unit_m4(mat);
unit_m4(mat);
unit_m4(imat);
unit_m4(imat);
if (pset->selectmode == SCE_SELECT_END) {

if (point->totkey) {
if (edit->psys)
/* only do end keys */
psmd = psys_get_modifier(data->ob, edit->psys);
PTCacheEditKey *key = point->keys + point->totkey - 1;


if (selected == 0 || key->flag & PEK_SELECT) {
/* all is selected in path mode */
float mouse_distance;
if (pset->selectmode == SCE_SELECT_PATH)
if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
selected = 0;
if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {

psys_mat_hair_to_global(data->ob, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, mat);
LOOP_VISIBLE_POINTS {
invert_m4_m4(imat, mat);
if (pset->selectmode == SCE_SELECT_END) {
if (point->totkey) {
/* only do end keys */
key = point->keys + point->totkey - 1;

if (selected == 0 || key->flag & PEK_SELECT) {
if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
invert_m4_m4(imat, mat);
}

func(data, mat, imat, p, point->totkey - 1, key);
}
}
iter_data->func(data, mat, imat, iter, point->totkey - 1, key, mouse_distance);
}
}
}
}
}
}
}
else {
else {
/* do all keys */
/* do all keys */
LOOP_VISIBLE_KEYS {
PTCacheEditKey *key;
if (selected == 0 || key->flag & PEK_SELECT) {
int k;
if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
LOOP_VISIBLE_KEYS {
if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
if (selected == 0 || key->flag & PEK_SELECT) {
psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
float mouse_distance;
invert_m4_m4(imat, mat);
if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
}
if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {

psys_mat_hair_to_global(data->ob, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, mat);
func(data, mat, imat, p, k, key);
invert_m4_m4(imat, mat);
}
}
iter_data->func(data, mat, imat, iter, k, key, mouse_distance);
}
}
}
}
}
}
}
}
}
}


static void foreach_mouse_hit_key(PEData *data, ForHitKeyMatFunc func, int selected)
{
PTCacheEdit *edit = data->edit;
ParticleEditSettings *pset = PE_settings(data->scene);
/* all is selected in path mode */
if (pset->selectmode == SCE_SELECT_PATH) {
selected = 0;
}

KeyIterData iter_data;
iter_data.data = data;
iter_data.edit = edit;
iter_data.selected = selected;
iter_data.func = func;

ParallelRangeSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC;
BLI_task_parallel_range(0, edit->totpoint, &iter_data, foreach_mouse_hit_key_iter, &settings);
}

static void foreach_selected_point(PEData *data, ForPointFunc func)
static void foreach_selected_point(PEData *data, ForPointFunc func)
{
{
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
POINT_P;
POINT_P;


LOOP_SELECTED_POINTS {
LOOP_SELECTED_POINTS {
func(data, p);
func(data, p);
}
}
}
}


static void foreach_selected_key(PEData *data, ForKeyFunc func)
static void foreach_selected_key(PEData *data, ForKeyFunc func)
{
{
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
POINT_P; KEY_K;
POINT_P; KEY_K;


LOOP_VISIBLE_POINTS {
LOOP_VISIBLE_POINTS {
LOOP_SELECTED_KEYS {
LOOP_SELECTED_KEYS {
func(data, p, k, true);
func(data, p, k);
}
}
}
}
}
}


static void foreach_point(PEData *data, ForPointFunc func)
static void foreach_point(PEData *data, ForPointFunc func)
{
{
PTCacheEdit *edit = data->edit;
PTCacheEdit *edit = data->edit;
POINT_P;
POINT_P;


LOOP_POINTS {
LOOP_POINTS {
func(data, p);
func(data, p);
}
}
}
}


static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
{
{
ParticleEditSettings *pset = PE_settings(scene);
ParticleEditSettings *pset = PE_settings(scene);
POINT_P; KEY_K;
POINT_P; KEY_K;
int sel = 0;
int sel = 0;


LOOP_VISIBLE_POINTS {
LOOP_VISIBLE_POINTS {
if (pset->selectmode == SCE_SELECT_POINT) {
if (pset->selectmode == SCE_SELECT_POINT) {
LOOP_SELECTED_KEYS {
LOOP_SELECTED_KEYS {
sel++;
sel++;
}
}
}
}
else if (pset->selectmode == SCE_SELECT_END) {
else if (pset->selectmode == SCE_SELECT_END) {
if (point->totkey) {
if (point->totkey) {
key = point->keys + point->totkey - 1;
key = point->keys + point->totkey - 1;
if (key->flag & PEK_SELECT)
if (key->flag & PEK_SELECT)
sel++;
sel++;
}
}
}
}
}
}


return sel;
return sel;
}
}


/************************************************/
/************************************************/
/* Particle Edit Mirroring */
/* Particle Edit Mirroring */
/************************************************/
/************************************************/


static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
{
{
PTCacheEdit *edit;
PTCacheEdit *edit;
ParticleSystemModifierData *psmd_eval;
ParticleSystemModifierData *psmd;
KDTree_3d *tree;
KDTree *tree;
KDTreeNearest_3d nearest;
KDTreeNearest nearest;
HairKey *key;
HairKey *key;
PARTICLE_P;
PARTICLE_P;
float mat[4][4], co[3];
float mat[4][4], co[3];
int index, totpart;
int index, totpart;


edit = psys->edit;
edit = psys->edit;
psmd_eval = edit->psmd_eval;
psmd = psys_get_modifier(ob, psys);
totpart = psys->totpart;
totpart = psys->totpart;


if (!psmd_eval->mesh_final)
if (!psmd->dm_final)
return;
return;


tree = BLI_kdtree_3d_new(totpart);
tree = BLI_kdtree_new(totpart);


/* insert particles into kd tree */
/* insert particles into kd tree */
LOOP_PARTICLES {
LOOP_PARTICLES {
key = pa->hair;
key = pa->hair;
psys_mat_hair_to_orco(ob, psmd_eval->mesh_final, psys->part->from, pa, mat);
psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
copy_v3_v3(co, key->co);
copy_v3_v3(co, key->co);
mul_m4_v3(mat, co);
mul_m4_v3(mat, co);
BLI_kdtree_3d_insert(tree, p, co);
BLI_kdtree_insert(tree, p, co);
}
}


BLI_kdtree_3d_balance(tree);
BLI_kdtree_balance(tree);


/* lookup particles and set in mirror cache */
/* lookup particles and set in mirror cache */
if (!edit->mirror_cache)
if (!edit->mirror_cache)
edit->mirror_cache = MEM_callocN(sizeof(int) * totpart, "PE mirror cache");
edit->mirror_cache = MEM_callocN(sizeof(int) * totpart, "PE mirror cache");


LOOP_PARTICLES {
LOOP_PARTICLES {
key = pa->hair;
key = pa->hair;
psys_mat_hair_to_orco(ob, psmd_eval->mesh_final, psys->part->from, pa, mat);
psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
copy_v3_v3(co, key->co);
copy_v3_v3(co, key->co);
mul_m4_v3(mat, co);
mul_m4_v3(mat, co);
co[0] = -co[0];
co[0] = -co[0];


index = BLI_kdtree_3d_find_nearest(tree, co, &nearest);
index = BLI_kdtree_find_nearest(tree, co, &nearest);


/* this needs a custom threshold still, duplicated for editmode mirror */
/* this needs a custom threshold still, duplicated for editmode mirror */
if (index != -1 && index != p && (nearest.dist <= 0.0002f))
if (index != -1 && index != p && (nearest.dist <= 0.0002f))
edit->mirror_cache[p] = index;
edit->mirror_cache[p] = index;
else
else
edit->mirror_cache[p] = -1;
edit->mirror_cache[p] = -1;
}
}


/* make sure mirrors are in two directions */
/* make sure mirrors are in two directions */
LOOP_PARTICLES {
LOOP_PARTICLES {
if (edit->mirror_cache[p]) {
if (edit->mirror_cache[p]) {
index = edit->mirror_cache[p];
index = edit->mirror_cache[p];
if (edit->mirror_cache[index] != p)
if (edit->mirror_cache[index] != p)
edit->mirror_cache[p] = -1;
edit->mirror_cache[p] = -1;
}
}
}
}


BLI_kdtree_3d_free(tree);
BLI_kdtree_free(tree);
}
}


static void PE_mirror_particle(Object *ob, Mesh *mesh, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
{
{
HairKey *hkey, *mhkey;
HairKey *hkey, *mhkey;
PTCacheEditPoint *point, *mpoint;
PTCacheEditPoint *point, *mpoint;
PTCacheEditKey *key, *mkey;
PTCacheEditKey *key, *mkey;
PTCacheEdit *edit;
PTCacheEdit *edit;
float mat[4][4], mmat[4][4], immat[4][4];
float mat[4][4], mmat[4][4], immat[4][4];
int i, mi, k;
int i, mi, k;


edit = psys->edit;
edit = psys->edit;
i = pa - psys->particles;
i = pa - psys->particles;


/* find mirrored particle if needed */
/* find mirrored particle if needed */
if (!mpa) {
if (!mpa) {
if (!edit->mirror_cache)
if (!edit->mirror_cache)
PE_update_mirror_cache(ob, psys);
PE_update_mirror_cache(ob, psys);


if (!edit->mirror_cache)
if (!edit->mirror_cache)
return; /* something went wrong! */
return; /* something went wrong! */


mi = edit->mirror_cache[i];
mi = edit->mirror_cache[i];
if (mi == -1)
if (mi == -1)
return;
return;
mpa = psys->particles + mi;
mpa = psys->particles + mi;
}
}
else
else
mi = mpa - psys->particles;
mi = mpa - psys->particles;


point = edit->points + i;
point = edit->points + i;
mpoint = edit->points + mi;
mpoint = edit->points + mi;


/* make sure they have the same amount of keys */
/* make sure they have the same amount of keys */
if (pa->totkey != mpa->totkey) {
if (pa->totkey != mpa->totkey) {
if (mpa->hair) MEM_freeN(mpa->hair);
if (mpa->hair) MEM_freeN(mpa->hair);
if (mpoint->keys) MEM_freeN(mpoint->keys);
if (mpoint->keys) MEM_freeN(mpoint->keys);


mpa->hair = MEM_dupallocN(pa->hair);
mpa->hair = MEM_dupallocN(pa->hair);
mpa->totkey = pa->totkey;
mpa->totkey = pa->totkey;
mpoint->keys = MEM_dupallocN(point->keys);
mpoint->keys = MEM_dupallocN(point->keys);
mpoint->totkey = point->totkey;
mpoint->totkey = point->totkey;


mhkey = mpa->hair;
mhkey = mpa->hair;
mkey = mpoint->keys;
mkey = mpoint->keys;
for (k = 0; k < mpa->totkey; k++, mkey++, mhkey++) {
for (k = 0; k < mpa->totkey; k++, mkey++, mhkey++) {
mkey->co = mhkey->co;
mkey->co = mhkey->co;
mkey->time = &mhkey->time;
mkey->time = &mhkey->time;
mkey->flag &= ~PEK_SELECT;
mkey->flag &= ~PEK_SELECT;
}
}
}
}


/* mirror positions and tags */
/* mirror positions and tags */
psys_mat_hair_to_orco(ob, mesh, psys->part->from, pa, mat);
psys_mat_hair_to_orco(ob, dm, psys->part->from, pa, mat);
psys_mat_hair_to_orco(ob, mesh, psys->part->from, mpa, mmat);
psys_mat_hair_to_orco(ob, dm, psys->part->from, mpa, mmat);
invert_m4_m4(immat, mmat);
invert_m4_m4(immat, mmat);


hkey = pa->hair;
hkey = pa->hair;
mhkey = mpa->hair;
mhkey = mpa->hair;
key = point->keys;
key = point->keys;
mkey = mpoint->keys;
mkey = mpoint->keys;
for (k = 0; k < pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
for (k = 0; k < pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
copy_v3_v3(mhkey->co, hkey->co);
copy_v3_v3(mhkey->co, hkey->co);
mul_m4_v3(mat, mhkey->co);
mul_m4_v3(mat, mhkey->co);
mhkey->co[0] = -mhkey->co[0];
mhkey->co[0] = -mhkey->co[0];
mul_m4_v3(immat, mhkey->co);
mul_m4_v3(immat, mhkey->co);


if (key->flag & PEK_TAG)
if (key->flag & PEK_TAG)
mkey->flag |= PEK_TAG;
mkey->flag |= PEK_TAG;


mkey->length = key->length;
mkey->length = key->length;
}
}


if (point->flag & PEP_TAG)
if (point->flag & PEP_TAG)
mpoint->flag |= PEP_TAG;
mpoint->flag |= PEP_TAG;
if (point->flag & PEP_EDIT_RECALC)
if (point->flag & PEP_EDIT_RECALC)
mpoint->flag |= PEP_EDIT_RECALC;
mpoint->flag |= PEP_EDIT_RECALC;
}
}


static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
{
{
PTCacheEdit *edit;
PTCacheEdit *edit;
ParticleSystemModifierData *psmd_eval;
ParticleSystemModifierData *psmd;
POINT_P;
POINT_P;


if (!psys)
if (!psys)
return;
return;


edit = psys->edit;
edit = psys->edit;
psmd_eval = edit->psmd_eval;
psmd = psys_get_modifier(ob, psys);


if (!psmd_eval->mesh_final)
if (!psmd->dm_final)
return;
return;


if (!edit->mirror_cache)
if (!edit->mirror_cache)
PE_update_mirror_cache(ob, psys);
PE_update_mirror_cache(ob, psys);


if (!edit->mirror_cache)
if (!edit->mirror_cache)
return; /* something went wrong */
return; /* something went wrong */


/* we delay settings the PARS_EDIT_RECALC for mirrored particles
/* we delay settings the PARS_EDIT_RECALC for mirrored particles
* to avoid doing mirror twice */
* to avoid doing mirror twice */
LOOP_POINTS {
LOOP_POINTS {
if (point->flag & PEP_EDIT_RECALC) {
if (point->flag & PEP_EDIT_RECALC) {
PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL);
PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);

if (edit->mirror_cache[p] != -1)
edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
}
}

LOOP_POINTS {
if (point->flag & PEP_EDIT_RECALC)
if (edit->mirror_cache[p] != -1)
edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
}
}

/************************************************/
/* Edit Calculation */
/************************************************/
/* tries to stop edited particles from going through the emitter's surface */
static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
{
ParticleEditSettings *pset = PE_settings(scene);
ParticleSystem *psys;
ParticleSystemModifierData *psmd;
POINT_P; KEY_K;
int index;
float *vec, *nor, dvec[3], dot, dist_1st = 0.0f;
float hairimat[4][4], hairmat[4][4];
const float dist = ED_view3d_select_dist_px() * 0.01f;

if (edit == NULL || edit->psys == NULL || (pset->flag & PE_DEFLECT_EMITTER) == 0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
return;

psys = edit->psys;
psmd = psys_get_modifier(ob, psys);

if (!psmd->dm_final)
return;

LOOP_EDITED_POINTS {
psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles + p, hairmat);

LOOP_KEYS {
mul_m4_v3(hairmat, key->co);
}

LOOP_KEYS {
if (k == 0) {
dist_1st = len_v3v3((key + 1)->co, key->co);
dist_1st *= dist * pset->emitterdist;
}
else {
index = BLI_kdtree_find_nearest(edit->emitter_field, key->co, NULL);

vec = edit->emitter_cosnos + index * 6;
nor = vec + 3;

sub_v3_v3v3(dvec, key->co, vec);

dot = dot_v3v3(dvec, nor);
copy_v3_v3(dvec, nor);

if (dot > 0.0f) {
if (dot < dist_1st) {
normalize_v3(dvec);
mul_v3_fl(dvec, dist_1st - dot);
add_v3_v3(key->co, dvec);
}
}
else {
normalize_v3(dvec);
mul_v3_fl(dvec, dist_1st - dot);
add_v3_v3(key->co, dvec);
}
if (k == 1)
dist_1st *= 1.3333f;
}
}

invert_m4_m4(hairimat, hairmat);

LOOP_KEYS {
mul_m4_v3(hairimat, key->co);
}
}
}
/* force set distances between neighboring keys */
static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
{

ParticleEditSettings *pset = PE_settings(scene);
POINT_P; KEY_K;
float dv1[3];

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) {
sub_v3_v3v3(dv1, key->co, (key - 1)->co);
normalize_v3(dv1);
mul_v3_fl(dv1, (key - 1)->length);
add_v3_v3v3(key->co, (key - 1)->co, dv1);
}
}
}
}
/* try to find a nice solution to keep distances between neighboring keys */
static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
{
ParticleEditSettings *pset = PE_settings(scene);
POINT_P;
PTCacheEditKey *key;
int j, k;
float tlen;
float dv0[3] = {0.0f, 0.0f, 0.0f};
float dv1[3] = {0.0f, 0.0f, 0.0f};
float dv2[3] = {0.0f, 0.0f, 0.0f};


i
if (edit == 0 || (pset->flag & PE_KEEP_LENGTHS) == 0)
return;

if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
return;

LOOP_EDITED_POINTS {
for (j = 1; j < point->totkey; j++) {
float mul = 1.0f / (float)point->totkey;

if (pset->flag & PE_LOCK_FIRST) {
key = point->keys + 1;
k = 1;
dv1[0] = dv1[1] = dv1[2] = 0.0;
}
else {
key = point->keys;
k = 0;
dv0[0] = dv0[1] = dv0[2] = 0.0;
}

for (; k < point->totkey; k++, key++) {
if (k) {
sub_v3_v3v3(dv0, (key - 1)->co, key->co);
tlen = normalize_v3(dv0);
mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
}

if (k < point->totkey - 1) {
sub_v3_v3v3(dv2, (key + 1)->co, key->co);
tlen = normalize_v3(dv2);
mul_v3_fl(dv2, mul * (tlen - key->length));
}

if (k) {
add_v3_v3((key - 1)->co, dv1);
}