Untitled diff

Created Diff never expires
305 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
1050 lines
289 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
1047 lines
/*
/*
* 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_pointcache.h"
#include "BKE_report.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_bvhutils.h"
#include "BKE_pointcache.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;
Text moved to lines 898-900
}
}

/*************************** 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 {
static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, bool nearest)
PSEL_NEAREST = (1 << 0),
PSEL_ALL_KEYS = (1 << 1),
};

static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, const enum eParticleSelectFlag flag)
{
{
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) {
/* only do end keys */
PTCacheEditKey *key = point->keys + point->totkey - 1;


if (selected == 0 || key->flag & PEK_SELECT) {
if (edit->psys)
float mouse_distance;
psmd = psys_get_modifier(data->ob, edit->psys);
if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {

if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
/* all is selected in path mode */
psys_mat_hair_to_global(data->ob, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, mat);
if (pset->selectmode == SCE_SELECT_PATH)
invert_m4_m4(imat, mat);
selected = 0;

LOOP_VISIBLE_POINTS {
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;
Text moved from lines 494-496
}
}

/************************************************/
/* 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;


i
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};

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);
}