2011-02-23 10:52:22 +00:00
|
|
|
/*
|
2009-10-22 23:22:05 +00:00
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-10-22 23:22:05 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2009 Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): Blender Foundation
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
2011-02-27 20:29:51 +00:00
|
|
|
/** \file blender/editors/physics/particle_object.c
|
|
|
|
* \ingroup edphys
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "DNA_modifier_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
2009-11-10 20:43:45 +00:00
|
|
|
#include "BLI_math.h"
|
2009-10-22 23:22:05 +00:00
|
|
|
#include "BLI_listbase.h"
|
2011-01-07 18:36:47 +00:00
|
|
|
#include "BLI_utildefines.h"
|
2015-01-15 11:51:30 +01:00
|
|
|
#include "BLI_string.h"
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_depsgraph.h"
|
|
|
|
#include "BKE_DerivedMesh.h"
|
|
|
|
#include "BKE_cdderivedmesh.h"
|
2015-01-15 11:51:30 +01:00
|
|
|
#include "BKE_effect.h"
|
2010-01-22 06:48:29 +00:00
|
|
|
#include "BKE_global.h"
|
2015-01-15 11:51:30 +01:00
|
|
|
#include "BKE_library.h"
|
2009-10-22 23:22:05 +00:00
|
|
|
#include "BKE_main.h"
|
2015-01-15 11:51:30 +01:00
|
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "BKE_object.h"
|
2009-10-22 23:22:05 +00:00
|
|
|
#include "BKE_particle.h"
|
|
|
|
#include "BKE_pointcache.h"
|
2012-12-21 09:27:39 +00:00
|
|
|
#include "BKE_report.h"
|
2011-01-07 19:18:31 +00:00
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "RNA_define.h"
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "ED_particle.h"
|
2009-12-04 06:33:01 +00:00
|
|
|
#include "ED_screen.h"
|
2012-01-02 17:15:24 +00:00
|
|
|
#include "ED_object.h"
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
#include "UI_resources.h"
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
#include "physics_intern.h"
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
extern void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
|
|
|
|
extern void PTCacheUndo_clear(PTCacheEdit *edit);
|
|
|
|
extern void recalc_lengths(PTCacheEdit *edit);
|
|
|
|
extern void recalc_emitter_field(Object *ob, ParticleSystem *psys);
|
|
|
|
extern void update_world_cos(Object *ob, PTCacheEdit *edit);
|
|
|
|
|
|
|
|
#define KEY_K PTCacheEditKey *key; int k
|
|
|
|
#define POINT_P PTCacheEditPoint *point; int p
|
|
|
|
#define LOOP_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++)
|
|
|
|
#define LOOP_VISIBLE_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!(point->flag & PEP_HIDE))
|
|
|
|
#define LOOP_SELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point_is_selected(point))
|
|
|
|
#define LOOP_UNSELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!point_is_selected(point))
|
|
|
|
#define LOOP_EDITED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_EDIT_RECALC)
|
|
|
|
#define LOOP_TAGGED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_TAG)
|
|
|
|
#define LOOP_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++)
|
|
|
|
#define LOOP_VISIBLE_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (!(key->flag & PEK_HIDE))
|
|
|
|
#define LOOP_SELECTED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
|
|
|
|
#define LOOP_TAGGED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (key->flag & PEK_TAG)
|
|
|
|
|
|
|
|
#define KEY_WCO (key->flag & PEK_USE_WCO ? key->world_co : key->co)
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
/********************** particle system slot operators *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int particle_system_add_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
2012-01-02 17:15:24 +00:00
|
|
|
Object *ob= ED_object_context(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!scene || !ob)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2009-11-28 13:33:56 +00:00
|
|
|
object_add_particle_system(scene, ob, NULL);
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OBJECT_OT_particle_system_add(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Add Particle System Slot";
|
|
|
|
ot->idname = "OBJECT_OT_particle_system_add";
|
|
|
|
ot->description = "Add a particle system";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = ED_operator_object_active_editable;
|
|
|
|
ot->exec = particle_system_add_exec;
|
2009-12-04 06:33:01 +00:00
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int particle_system_remove_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
2012-09-14 06:17:14 +00:00
|
|
|
Object *ob = ED_object_context(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2012-09-14 06:17:14 +00:00
|
|
|
int mode_orig;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!scene || !ob)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2012-09-14 06:17:14 +00:00
|
|
|
mode_orig = ob->mode;
|
2009-10-22 23:22:05 +00:00
|
|
|
object_remove_particle_system(scene, ob);
|
2009-11-06 12:27:28 +00:00
|
|
|
|
|
|
|
/* possible this isn't the active object
|
|
|
|
* object_remove_particle_system() clears the mode on the last psys
|
2012-10-23 03:38:26 +00:00
|
|
|
*/
|
|
|
|
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
|
|
|
|
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
|
|
|
|
if (scene->basact && scene->basact->object == ob) {
|
2009-11-06 12:27:28 +00:00
|
|
|
WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
|
2012-10-23 03:38:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-06 12:27:28 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Remove Particle System Slot";
|
|
|
|
ot->idname = "OBJECT_OT_particle_system_remove";
|
|
|
|
ot->description = "Remove the selected particle system";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = ED_operator_object_active_editable;
|
|
|
|
ot->exec = particle_system_remove_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************** new particle settings operator *********************/
|
|
|
|
|
|
|
|
static int psys_poll(bContext *C)
|
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
return (ptr.data != NULL);
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
Main *bmain= CTX_data_main(C);
|
|
|
|
ParticleSystem *psys;
|
|
|
|
ParticleSettings *part = NULL;
|
|
|
|
Object *ob;
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
|
|
|
ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
|
|
|
|
psys = ptr.data;
|
|
|
|
|
|
|
|
/* add or copy particle setting */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (psys->part)
|
2012-05-05 14:03:12 +00:00
|
|
|
part= BKE_particlesettings_copy(psys->part);
|
2009-10-22 23:22:05 +00:00
|
|
|
else
|
|
|
|
part= psys_new_settings("ParticleSettings", bmain);
|
|
|
|
|
|
|
|
ob= ptr.id.data;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (psys->part)
|
2009-10-22 23:22:05 +00:00
|
|
|
psys->part->id.us--;
|
|
|
|
|
|
|
|
psys->part = part;
|
|
|
|
|
|
|
|
psys_check_boid_data(psys);
|
|
|
|
|
2013-02-21 19:33:04 +00:00
|
|
|
DAG_relations_tag_update(bmain);
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_new(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "New Particle Settings";
|
|
|
|
ot->idname = "PARTICLE_OT_new";
|
|
|
|
ot->description = "Add new particle settings";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = new_particle_settings_exec;
|
|
|
|
ot->poll = psys_poll;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************** keyed particle target operators *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int new_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
Object *ob = ptr.id.data;
|
|
|
|
|
|
|
|
ParticleTarget *pt;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
pt = psys->targets.first;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (; pt; pt=pt->next)
|
2009-10-22 23:22:05 +00:00
|
|
|
pt->flag &= ~PTARGET_CURRENT;
|
|
|
|
|
|
|
|
pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target");
|
|
|
|
|
|
|
|
pt->flag |= PTARGET_CURRENT;
|
|
|
|
pt->psys = 1;
|
|
|
|
|
|
|
|
BLI_addtail(&psys->targets, pt);
|
|
|
|
|
2013-02-21 19:33:04 +00:00
|
|
|
DAG_relations_tag_update(bmain);
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_new_target(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "New Particle Target";
|
|
|
|
ot->idname = "PARTICLE_OT_new_target";
|
|
|
|
ot->description = "Add a new particle target";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = new_particle_target_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int remove_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
Object *ob = ptr.id.data;
|
|
|
|
|
|
|
|
ParticleTarget *pt;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
pt = psys->targets.first;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (; pt; pt=pt->next) {
|
|
|
|
if (pt->flag & PTARGET_CURRENT) {
|
2009-10-22 23:22:05 +00:00
|
|
|
BLI_remlink(&psys->targets, pt);
|
|
|
|
MEM_freeN(pt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
pt = psys->targets.last;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (pt)
|
2009-10-22 23:22:05 +00:00
|
|
|
pt->flag |= PTARGET_CURRENT;
|
|
|
|
|
2013-02-21 19:33:04 +00:00
|
|
|
DAG_relations_tag_update(bmain);
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2009-11-28 14:37:21 +00:00
|
|
|
void PARTICLE_OT_target_remove(wmOperatorType *ot)
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Remove Particle Target";
|
|
|
|
ot->idname = "PARTICLE_OT_target_remove";
|
|
|
|
ot->description = "Remove the selected particle target";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = remove_particle_target_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************ move up particle target operator *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int target_move_up_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
Object *ob = ptr.id.data;
|
|
|
|
ParticleTarget *pt;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
pt = psys->targets.first;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (; pt; pt=pt->next) {
|
|
|
|
if (pt->flag & PTARGET_CURRENT && pt->prev) {
|
2009-10-22 23:22:05 +00:00
|
|
|
BLI_remlink(&psys->targets, pt);
|
2013-02-22 14:12:55 +00:00
|
|
|
BLI_insertlinkbefore(&psys->targets, pt->prev, pt);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_target_move_up(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Up Target";
|
|
|
|
ot->idname = "PARTICLE_OT_target_move_up";
|
|
|
|
ot->description = "Move particle target up in the list";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = target_move_up_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************ move down particle target operator *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int target_move_down_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
Object *ob = ptr.id.data;
|
|
|
|
ParticleTarget *pt;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
pt = psys->targets.first;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (; pt; pt=pt->next) {
|
|
|
|
if (pt->flag & PTARGET_CURRENT && pt->next) {
|
2009-10-22 23:22:05 +00:00
|
|
|
BLI_remlink(&psys->targets, pt);
|
2013-02-22 14:12:55 +00:00
|
|
|
BLI_insertlinkafter(&psys->targets, pt->next, pt);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_target_move_down(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Down Target";
|
|
|
|
ot->idname = "PARTICLE_OT_target_move_down";
|
|
|
|
ot->description = "Move particle target down in the list";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = target_move_down_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************ move up particle dupliweight operator *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int dupliob_move_up_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
ParticleSettings *part;
|
|
|
|
ParticleDupliWeight *dw;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
part = psys->part;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (dw=part->dupliweights.first; dw; dw=dw->next) {
|
|
|
|
if (dw->flag & PART_DUPLIW_CURRENT && dw->prev) {
|
2009-10-22 23:22:05 +00:00
|
|
|
BLI_remlink(&part->dupliweights, dw);
|
2013-02-22 14:12:55 +00:00
|
|
|
BLI_insertlinkbefore(&part->dupliweights, dw->prev, dw);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
|
2009-10-22 23:22:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Up Dupli Object";
|
|
|
|
ot->idname = "PARTICLE_OT_dupliob_move_up";
|
|
|
|
ot->description = "Move dupli object up in the list";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = dupliob_move_up_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2009-11-01 00:06:53 +00:00
|
|
|
/********************** particle dupliweight operators *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int copy_particle_dupliob_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-11-01 00:06:53 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
ParticleSettings *part;
|
|
|
|
ParticleDupliWeight *dw;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-11-01 00:06:53 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
part = psys->part;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (dw=part->dupliweights.first; dw; dw=dw->next) {
|
|
|
|
if (dw->flag & PART_DUPLIW_CURRENT) {
|
2009-11-01 00:06:53 +00:00
|
|
|
dw->flag &= ~PART_DUPLIW_CURRENT;
|
|
|
|
dw = MEM_dupallocN(dw);
|
|
|
|
dw->flag |= PART_DUPLIW_CURRENT;
|
|
|
|
BLI_addhead(&part->dupliweights, dw);
|
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
|
2009-11-01 00:06:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_dupliob_copy(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Copy Particle Dupliob";
|
|
|
|
ot->idname = "PARTICLE_OT_dupliob_copy";
|
|
|
|
ot->description = "Duplicate the current dupliobject";
|
2009-11-01 00:06:53 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = copy_particle_dupliob_exec;
|
2009-11-01 00:06:53 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-11-01 00:06:53 +00:00
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int remove_particle_dupliob_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-11-01 00:06:53 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
ParticleSettings *part;
|
|
|
|
ParticleDupliWeight *dw;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-11-01 00:06:53 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
part = psys->part;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (dw=part->dupliweights.first; dw; dw=dw->next) {
|
|
|
|
if (dw->flag & PART_DUPLIW_CURRENT) {
|
2009-11-01 00:06:53 +00:00
|
|
|
BLI_remlink(&part->dupliweights, dw);
|
|
|
|
MEM_freeN(dw);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
dw = part->dupliweights.last;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (dw)
|
2009-11-01 00:06:53 +00:00
|
|
|
dw->flag |= PART_DUPLIW_CURRENT;
|
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
|
2009-11-01 00:06:53 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_dupliob_remove(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Remove Particle Dupliobject";
|
|
|
|
ot->idname = "PARTICLE_OT_dupliob_remove";
|
|
|
|
ot->description = "Remove the selected dupliobject";
|
2009-11-01 00:06:53 +00:00
|
|
|
|
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = remove_particle_dupliob_exec;
|
2009-11-01 00:06:53 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-11-01 00:06:53 +00:00
|
|
|
}
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
/************************ move down particle dupliweight operator *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int dupliob_move_down_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= ptr.data;
|
|
|
|
ParticleSettings *part;
|
|
|
|
ParticleDupliWeight *dw;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
part = psys->part;
|
2012-03-24 06:38:07 +00:00
|
|
|
for (dw=part->dupliweights.first; dw; dw=dw->next) {
|
|
|
|
if (dw->flag & PART_DUPLIW_CURRENT && dw->next) {
|
2009-10-22 23:22:05 +00:00
|
|
|
BLI_remlink(&part->dupliweights, dw);
|
2013-02-22 14:12:55 +00:00
|
|
|
BLI_insertlinkafter(&part->dupliweights, dw->next, dw);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
|
2009-10-22 23:22:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Down Dupli Object";
|
|
|
|
ot->idname = "PARTICLE_OT_dupliob_move_down";
|
|
|
|
ot->description = "Move dupli object down in the list";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = dupliob_move_down_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************ connect/disconnect hair operators *********************/
|
|
|
|
|
|
|
|
static void disconnect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
|
|
|
|
{
|
2012-04-29 15:47:02 +00:00
|
|
|
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
|
2010-03-09 03:42:20 +00:00
|
|
|
ParticleEditSettings *pset= PE_settings(scene);
|
2009-10-22 23:22:05 +00:00
|
|
|
ParticleData *pa;
|
|
|
|
PTCacheEdit *edit;
|
|
|
|
PTCacheEditPoint *point;
|
|
|
|
PTCacheEditKey *ekey = NULL;
|
|
|
|
HairKey *key;
|
|
|
|
int i, k;
|
|
|
|
float hairmat[4][4];
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR)
|
2009-10-22 23:22:05 +00:00
|
|
|
return;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!psys->part || psys->part->type != PART_HAIR)
|
2009-10-22 23:22:05 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
edit = psys->edit;
|
|
|
|
point= edit ? edit->points : NULL;
|
|
|
|
|
2012-04-29 15:47:02 +00:00
|
|
|
for (i=0, pa=psys->particles; i<psys->totpart; i++, pa++) {
|
2012-03-24 06:38:07 +00:00
|
|
|
if (point) {
|
2009-10-22 23:22:05 +00:00
|
|
|
ekey = point->keys;
|
|
|
|
point++;
|
|
|
|
}
|
|
|
|
|
|
|
|
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
|
|
|
|
|
2012-04-29 15:47:02 +00:00
|
|
|
for (k=0, key=pa->hair; k<pa->totkey; k++, key++) {
|
|
|
|
mul_m4_v3(hairmat, key->co);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (ekey) {
|
2009-10-22 23:22:05 +00:00
|
|
|
ekey->flag &= ~PEK_USE_WCO;
|
|
|
|
ekey++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
psys_free_path_cache(psys, psys->edit);
|
|
|
|
|
|
|
|
psys->flag |= PSYS_GLOBAL_HAIR;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_PUFF))
|
2010-03-09 03:42:20 +00:00
|
|
|
pset->brushtype = PE_BRUSH_NONE;
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
PE_update_object(scene, ob, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int disconnect_hair_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
2012-01-02 17:15:24 +00:00
|
|
|
Object *ob= ED_object_context(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= NULL;
|
2014-02-03 18:55:59 +11:00
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!ob)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (all) {
|
|
|
|
for (psys=ob->particlesystem.first; psys; psys=psys->next) {
|
2009-10-22 23:22:05 +00:00
|
|
|
disconnect_hair(scene, ob, psys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
psys = ptr.data;
|
|
|
|
disconnect_hair(scene, ob, psys);
|
|
|
|
}
|
|
|
|
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Disconnect Hair";
|
|
|
|
ot->description = "Disconnect hair from the emitter mesh";
|
|
|
|
ot->idname = "PARTICLE_OT_disconnect_hair";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = disconnect_hair_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh");
|
|
|
|
}
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
static bool remap_hair_emitter(Scene *scene, Object *UNUSED(ob), ParticleSystem *psys,
|
|
|
|
Object *target_ob, DerivedMesh *target_dm, ParticleSystem *target_psys, PTCacheEdit *target_edit,
|
|
|
|
bool from_world_space, bool to_world_space)
|
2009-10-22 23:22:05 +00:00
|
|
|
{
|
2015-01-15 11:51:30 +01:00
|
|
|
ParticleData *pa, *tpa;
|
|
|
|
PTCacheEditPoint *edit_point;
|
|
|
|
PTCacheEditKey *ekey;
|
2011-03-05 10:29:10 +00:00
|
|
|
BVHTreeFromMesh bvhtree= {NULL};
|
2014-06-17 14:58:23 +06:00
|
|
|
MFace *mface = NULL, *mf;
|
|
|
|
MEdge *medge = NULL, *me;
|
2013-06-03 03:47:41 +00:00
|
|
|
MVert *mvert;
|
2015-01-15 11:51:30 +01:00
|
|
|
DerivedMesh *dm;
|
2009-10-22 23:22:05 +00:00
|
|
|
int numverts;
|
|
|
|
int i, k;
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
if (!psys->part || psys->part->type != PART_HAIR || !target_psys->part || target_psys->part->type != PART_HAIR)
|
2014-04-01 11:34:00 +11:00
|
|
|
return false;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
edit_point = target_edit ? target_edit->points : NULL;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
if (target_dm->deformedOnly) {
|
|
|
|
/* we don't want to mess up target_dm when converting to global coordinates below */
|
|
|
|
dm = target_dm;
|
2013-06-03 03:47:41 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-01-15 11:51:30 +01:00
|
|
|
dm = mesh_get_derived_deform(scene, target_ob, CD_MASK_BAREMESH);
|
2013-06-03 03:47:41 +00:00
|
|
|
}
|
|
|
|
/* don't modify the original vertices */
|
|
|
|
dm = CDDM_copy(dm);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-01-19 19:23:25 +00:00
|
|
|
/* BMESH_ONLY, deform dm may not have tessface */
|
|
|
|
DM_ensure_tessface(dm);
|
|
|
|
|
2013-04-18 01:52:38 +00:00
|
|
|
numverts = dm->getNumVerts(dm);
|
2013-06-03 03:47:41 +00:00
|
|
|
mvert = dm->getVertArray(dm);
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
/* convert to global coordinates */
|
|
|
|
for (i=0; i<numverts; i++)
|
2015-01-15 11:51:30 +01:00
|
|
|
mul_m4_v3(target_ob->obmat, mvert[i].co);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2014-06-17 14:58:23 +06:00
|
|
|
if (dm->getNumTessFaces(dm) != 0) {
|
|
|
|
mface = dm->getTessFaceArray(dm);
|
|
|
|
bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
|
|
|
|
}
|
|
|
|
else if (dm->getNumEdges(dm) != 0) {
|
|
|
|
medge = dm->getEdgeArray(dm);
|
|
|
|
bvhtree_from_mesh_edges(&bvhtree, dm, 0.0, 2, 6);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dm->release(dm);
|
|
|
|
return false;
|
|
|
|
}
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
for (i = 0, tpa = target_psys->particles, pa = psys->particles;
|
|
|
|
i < target_psys->totpart;
|
|
|
|
i++, tpa++, pa++) {
|
|
|
|
|
|
|
|
const float *co = from_world_space ? pa->hair[0].co : pa->hair[0].world_co;
|
|
|
|
BVHTreeNearest nearest;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
nearest.index = -1;
|
2014-02-03 02:46:45 +11:00
|
|
|
nearest.dist_sq = FLT_MAX;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
BLI_bvhtree_find_nearest(bvhtree.tree, co, &nearest, bvhtree.nearest_callback, &bvhtree);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (nearest.index == -1) {
|
2012-03-31 00:59:17 +00:00
|
|
|
if (G.debug & G_DEBUG)
|
2010-01-22 06:48:29 +00:00
|
|
|
printf("No nearest point found for hair root!");
|
2009-10-22 23:22:05 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-17 14:58:23 +06:00
|
|
|
if (mface) {
|
2015-01-15 11:51:30 +01:00
|
|
|
float v[4][3];
|
|
|
|
|
2014-06-17 14:58:23 +06:00
|
|
|
mf = &mface[nearest.index];
|
|
|
|
|
|
|
|
copy_v3_v3(v[0], mvert[mf->v1].co);
|
|
|
|
copy_v3_v3(v[1], mvert[mf->v2].co);
|
|
|
|
copy_v3_v3(v[2], mvert[mf->v3].co);
|
|
|
|
if (mf->v4) {
|
|
|
|
copy_v3_v3(v[3], mvert[mf->v4].co);
|
2015-01-15 11:51:30 +01:00
|
|
|
interp_weights_poly_v3(tpa->fuv, v, 4, nearest.co);
|
2014-06-17 14:58:23 +06:00
|
|
|
}
|
|
|
|
else
|
2015-01-15 11:51:30 +01:00
|
|
|
interp_weights_poly_v3(tpa->fuv, v, 3, nearest.co);
|
|
|
|
tpa->foffset = 0.0f;
|
2014-06-17 14:58:23 +06:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
tpa->num = nearest.index;
|
|
|
|
tpa->num_dmcache = psys_particle_dm_face_lookup(target_ob, dm, tpa->num, tpa->fuv, NULL);
|
2014-06-17 14:58:23 +06:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
me = &medge[nearest.index];
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
tpa->fuv[1] = line_point_factor_v3(nearest.co,
|
|
|
|
mvert[me->v1].co,
|
|
|
|
mvert[me->v2].co);
|
|
|
|
tpa->fuv[0] = 1.0f - tpa->fuv[1];
|
|
|
|
tpa->fuv[2] = tpa->fuv[3] = 0.0f;
|
|
|
|
tpa->foffset = 0.0f;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
tpa->num = nearest.index;
|
|
|
|
tpa->num_dmcache = -1;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
/* translate hair keys */
|
|
|
|
{
|
|
|
|
HairKey *key, *tkey;
|
|
|
|
float hairmat[4][4], imat[4][4];
|
|
|
|
float offset[3];
|
|
|
|
|
|
|
|
/* note: using target_dm here, which is in target_ob object space and has full modifiers */
|
|
|
|
psys_mat_hair_to_global(target_ob, target_dm, target_psys->part->from, tpa, hairmat);
|
|
|
|
invert_m4_m4(imat, hairmat);
|
|
|
|
|
|
|
|
/* offset in world space */
|
|
|
|
sub_v3_v3v3(offset, nearest.co, co);
|
|
|
|
{
|
|
|
|
SimDebugData *dd = psys->clmd ? psys->clmd->debug_data : NULL;
|
|
|
|
BKE_sim_debug_data_add_dot(dd, nearest.co, 1,1,1, "particle matrix", 689, i);
|
|
|
|
|
|
|
|
BKE_sim_debug_data_add_vector(dd, hairmat[3], hairmat[0], 1,0,0, "particle matrix", 222, i);
|
|
|
|
BKE_sim_debug_data_add_vector(dd, hairmat[3], hairmat[1], 0,1,0, "particle matrix", 333, i);
|
|
|
|
BKE_sim_debug_data_add_vector(dd, hairmat[3], hairmat[2], 0,0,1, "particle matrix", 444, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (edit_point) {
|
|
|
|
for (k=0, key=pa->hair, tkey=tpa->hair, ekey = edit_point->keys; k<tpa->totkey; k++, key++, tkey++, ekey++) {
|
|
|
|
const float *co_orig = from_world_space ? key->co : key->world_co;
|
|
|
|
|
|
|
|
add_v3_v3v3(tkey->co, co_orig, offset);
|
|
|
|
if (!to_world_space)
|
|
|
|
mul_m4_v3(imat, tkey->co);
|
|
|
|
|
|
|
|
ekey->flag |= PEK_USE_WCO;
|
|
|
|
}
|
|
|
|
|
|
|
|
edit_point++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (k=0, key=pa->hair, tkey=tpa->hair; k<tpa->totkey; k++, key++, tkey++) {
|
|
|
|
const float *co_orig = from_world_space ? key->co : key->world_co;
|
|
|
|
|
|
|
|
add_v3_v3v3(tkey->co, co_orig, offset);
|
|
|
|
if (!to_world_space)
|
|
|
|
mul_m4_v3(imat, tkey->co);
|
|
|
|
}
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free_bvhtree_from_mesh(&bvhtree);
|
2010-09-08 08:36:12 +00:00
|
|
|
dm->release(dm);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
psys_free_path_cache(target_psys, target_edit);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
PE_update_object(scene, target_ob, 0);
|
2012-12-21 09:27:39 +00:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
return true;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
|
|
|
|
{
|
|
|
|
ParticleSystemModifierData *psmd;
|
|
|
|
const bool from_global = psys->flag & PSYS_GLOBAL_HAIR;
|
|
|
|
const bool to_global = false;
|
|
|
|
|
|
|
|
if (!psys)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
psmd = psys_get_modifier(ob, psys);
|
|
|
|
if (!psmd->dm)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
psys->flag &= ~PSYS_GLOBAL_HAIR;
|
|
|
|
return remap_hair_emitter(scene, ob, psys, ob, psmd->dm, psys, psys->edit, from_global, to_global);
|
|
|
|
}
|
|
|
|
|
2009-10-22 23:22:05 +00:00
|
|
|
static int connect_hair_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
2012-01-02 17:15:24 +00:00
|
|
|
Object *ob= ED_object_context(C);
|
2009-10-22 23:22:05 +00:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
|
|
|
|
ParticleSystem *psys= NULL;
|
2014-02-03 18:55:59 +11:00
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
2014-04-01 11:34:00 +11:00
|
|
|
bool any_connected = false;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!ob)
|
2009-10-22 23:22:05 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (all) {
|
|
|
|
for (psys=ob->particlesystem.first; psys; psys=psys->next) {
|
2012-12-21 09:27:39 +00:00
|
|
|
any_connected |= connect_hair(scene, ob, psys);
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
psys = ptr.data;
|
2012-12-21 09:27:39 +00:00
|
|
|
any_connected |= connect_hair(scene, ob, psys);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!any_connected) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Can't disconnect hair if particle system modifier is disabled");
|
|
|
|
return OPERATOR_CANCELLED;
|
2009-10-22 23:22:05 +00:00
|
|
|
}
|
|
|
|
|
2010-12-05 18:59:23 +00:00
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
Timeline addition: Display cached frames
This started off doing pointcache debugging but it's also very useful for users too.
Previously it was very hard to see the state of the system when you're working caches
such as physics point cache - is it baked? which frames are cached? is it out of date?
Now, for better feedback, cached frames are drawn for the active object at the bottom
of the timeline - a semitransparent area shows the entire cache extents, and more
solid blocks on top show the frames that are cached. Darker versions indicate it's
using a disk cache.
It can be disabled in general in the timeline View -> Caches menu, or by each individual
system that can be shown.
There's still a bit to do on this, behaviour needs to be clarified still eg. deciding what
shows when it's out of date, or when it's been played back but not cached, etc. etc.
Part of this is due to a lack of definition in the point cache system itself, so we should
try and clean up/clarify this behaviour and what it means to users, at the same time.
Also would be interested in extending this to other caches such as fluid cache,
sequencer memory cache etc. in the future, too.
2010-06-22 02:29:52 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_connect_hair(wmOperatorType *ot)
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Connect Hair";
|
|
|
|
ot->description = "Connect hair to the emitter mesh";
|
|
|
|
ot->idname = "PARTICLE_OT_connect_hair";
|
2009-10-22 23:22:05 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = connect_hair_exec;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-10-22 23:22:05 +00:00
|
|
|
|
|
|
|
RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh");
|
|
|
|
}
|
|
|
|
|
2015-01-15 11:51:30 +01:00
|
|
|
/************************ particle system copy operator *********************/
|
|
|
|
|
|
|
|
static void copy_particle_edit(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystem *psys_from)
|
|
|
|
{
|
|
|
|
PTCacheEdit *edit_from = psys_from->edit, *edit;
|
|
|
|
ParticleData *pa;
|
|
|
|
KEY_K;
|
|
|
|
POINT_P;
|
|
|
|
|
|
|
|
if (!edit_from)
|
|
|
|
return;
|
|
|
|
|
|
|
|
edit = MEM_dupallocN(edit_from);
|
|
|
|
edit->psys = psys;
|
|
|
|
psys->edit = edit;
|
|
|
|
|
|
|
|
edit->pathcache = NULL;
|
|
|
|
BLI_listbase_clear(&edit->pathcachebufs);
|
|
|
|
|
|
|
|
edit->emitter_field = NULL;
|
|
|
|
edit->emitter_cosnos = NULL;
|
|
|
|
|
|
|
|
BLI_listbase_clear(&edit->undo);
|
|
|
|
edit->curundo = NULL;
|
|
|
|
|
|
|
|
edit->points = MEM_dupallocN(edit_from->points);
|
|
|
|
pa = psys->particles;
|
|
|
|
LOOP_POINTS {
|
|
|
|
HairKey *hkey = pa->hair;
|
|
|
|
|
|
|
|
point->keys= MEM_dupallocN(point->keys);
|
|
|
|
LOOP_KEYS {
|
|
|
|
key->co = hkey->co;
|
|
|
|
key->time = &hkey->time;
|
|
|
|
key->flag = hkey->editflag;
|
|
|
|
if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
|
|
|
|
key->flag |= PEK_USE_WCO;
|
|
|
|
hkey->editflag |= PEK_USE_WCO;
|
|
|
|
}
|
|
|
|
|
|
|
|
hkey++;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa++;
|
|
|
|
}
|
|
|
|
update_world_cos(ob, edit);
|
|
|
|
|
|
|
|
UI_GetThemeColor3ubv(TH_EDGE_SELECT, edit->sel_col);
|
|
|
|
UI_GetThemeColor3ubv(TH_WIRE, edit->nosel_col);
|
|
|
|
|
|
|
|
recalc_lengths(edit);
|
|
|
|
recalc_emitter_field(ob, psys);
|
|
|
|
PE_update_object(scene, ob, true);
|
|
|
|
|
|
|
|
PTCacheUndo_clear(edit);
|
|
|
|
PE_undo_push(scene, "Original");
|
|
|
|
}
|
|
|
|
|
2015-01-15 12:35:19 +01:00
|
|
|
static bool copy_particle_systems_to_object(Scene *scene, Object *ob_from, Object *ob_to)
|
2015-01-15 11:51:30 +01:00
|
|
|
{
|
|
|
|
ModifierData *md, *md_next;
|
2015-01-15 12:35:19 +01:00
|
|
|
ParticleSystem *psys, *psys_from;
|
2015-01-15 11:51:30 +01:00
|
|
|
DerivedMesh *final_dm;
|
|
|
|
int i;
|
|
|
|
|
2015-01-15 12:35:19 +01:00
|
|
|
if (ob_to->type != OB_MESH)
|
|
|
|
return false;
|
|
|
|
if (!ob_to->data || ((ID *)ob_to->data)->lib)
|
|
|
|
return false;
|
2015-01-15 11:51:30 +01:00
|
|
|
|
|
|
|
/* XXX in theory it could be nice to not delete existing particle systems,
|
|
|
|
* but the current code for copying assumes that the target object list is empty ...
|
|
|
|
*/
|
|
|
|
for (md = ob_to->modifiers.first; md; md = md_next) {
|
|
|
|
md_next = md->next;
|
|
|
|
|
|
|
|
/* remove all particle system modifiers as well,
|
|
|
|
* these need to sync to the particle system list
|
|
|
|
*/
|
|
|
|
if (ELEM(md->type, eModifierType_ParticleSystem, eModifierType_DynamicPaint, eModifierType_Smoke)) {
|
|
|
|
BLI_remlink(&ob_to->modifiers, md);
|
|
|
|
modifier_free(md);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BKE_object_free_particlesystems(ob_to);
|
|
|
|
|
|
|
|
/* For remapping we need a valid DM.
|
|
|
|
* Because the modifier is appended at the end it's safe to use
|
|
|
|
* the final DM of the object without particles
|
|
|
|
*/
|
|
|
|
if (ob_to->derivedFinal)
|
|
|
|
final_dm = ob_to->derivedFinal;
|
|
|
|
else
|
|
|
|
final_dm = mesh_get_derived_final(scene, ob_to, CD_MASK_BAREMESH);
|
|
|
|
|
|
|
|
BKE_object_copy_particlesystems(ob_to, ob_from);
|
|
|
|
for (psys = ob_to->particlesystem.first, psys_from = ob_from->particlesystem.first, i = 0;
|
|
|
|
psys;
|
|
|
|
psys = psys->next, psys_from = psys_from->next, ++i) {
|
|
|
|
|
|
|
|
ParticleSystemModifierData *psmd;
|
|
|
|
|
|
|
|
/* add a particle system modifier for each system */
|
|
|
|
md = modifier_new(eModifierType_ParticleSystem);
|
|
|
|
psmd = (ParticleSystemModifierData *)md;
|
|
|
|
/* push on top of the stack, no use trying to reproduce old stack order */
|
|
|
|
BLI_addtail(&ob_to->modifiers, md);
|
|
|
|
|
|
|
|
BLI_snprintf(md->name, sizeof(md->name), "ParticleSystem %i", i);
|
|
|
|
modifier_unique_name(&ob_to->modifiers, (ModifierData *)psmd);
|
|
|
|
|
|
|
|
psmd->psys = psys;
|
|
|
|
psmd->dm = CDDM_copy(final_dm);
|
|
|
|
CDDM_calc_normals(psmd->dm);
|
|
|
|
|
|
|
|
if (psys_from->edit)
|
|
|
|
copy_particle_edit(scene, ob_to, psys, psys_from);
|
|
|
|
|
|
|
|
remap_hair_emitter(scene, ob_from, psys_from, ob_to, psmd->dm, psys, psys->edit, false, false);
|
|
|
|
|
|
|
|
/* tag for recalc */
|
|
|
|
psys->recalc |= PSYS_RECALC_RESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
DAG_id_tag_update(&ob_to->id, OB_RECALC_DATA);
|
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to);
|
2015-01-15 12:35:19 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_particle_systems_poll(bContext *C)
|
|
|
|
{
|
|
|
|
Object *ob;
|
|
|
|
if (!ED_operator_object_active_editable(C))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ob = ED_object_active_context(C);
|
|
|
|
if (BLI_listbase_is_empty(&ob->particlesystem))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_particle_systems_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob_from = ED_object_active_context(C);
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
|
|
|
int changed_tot = 0;
|
|
|
|
int fail = 0;
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, ob_to, selected_editable_objects)
|
|
|
|
{
|
|
|
|
if (ob_from != ob_to) {
|
|
|
|
if (copy_particle_systems_to_object(scene, ob_from, ob_to))
|
|
|
|
changed_tot++;
|
|
|
|
else
|
|
|
|
fail++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
if ((changed_tot == 0 && fail == 0) || fail) {
|
|
|
|
BKE_reportf(op->reports, RPT_ERROR,
|
|
|
|
"Copy particle systems to selected: %d done, %d failed",
|
|
|
|
changed_tot, fail);
|
|
|
|
}
|
2015-01-15 11:51:30 +01:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
ot->name = "Copy Particle Systems";
|
2015-01-15 12:35:19 +01:00
|
|
|
ot->description = "Copy particle systems from the active object to selected objects";
|
2015-01-15 11:51:30 +01:00
|
|
|
ot->idname = "PARTICLE_OT_copy_particle_systems";
|
|
|
|
|
2015-01-15 12:35:19 +01:00
|
|
|
ot->poll = copy_particle_systems_poll;
|
2015-01-15 11:51:30 +01:00
|
|
|
ot->exec = copy_particle_systems_exec;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
}
|