2.8x branch added bContext arg in many places, pass eval-context instead since its not simple to reason about what what nested functions do when they can access and change almost anything. Also use const to prevent unexpected modifications. This fixes crash loading files with shadows, since off-screen buffers use a NULL context for rendering.
757 lines
17 KiB
C
757 lines
17 KiB
C
/*
|
|
* ***** 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,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/editors/metaball/mball_edit.c
|
|
* \ingroup edmeta
|
|
*/
|
|
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_defs.h"
|
|
#include "DNA_meta_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "RNA_define.h"
|
|
#include "RNA_access.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_mball.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "ED_mball.h"
|
|
#include "ED_screen.h"
|
|
#include "ED_view3d.h"
|
|
#include "ED_util.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "mball_intern.h"
|
|
|
|
/* This function is used to free all MetaElems from MetaBall */
|
|
void ED_mball_editmball_free(Object *obedit)
|
|
{
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
|
|
mb->editelems = NULL;
|
|
mb->lastelem = NULL;
|
|
}
|
|
|
|
/* This function is called, when MetaBall Object is
|
|
* switched from object mode to edit mode */
|
|
void ED_mball_editmball_make(Object *obedit)
|
|
{
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml; /*, *newml;*/
|
|
|
|
ml = mb->elems.first;
|
|
|
|
while (ml) {
|
|
if (ml->flag & SELECT) mb->lastelem = ml;
|
|
ml = ml->next;
|
|
}
|
|
|
|
mb->editelems = &mb->elems;
|
|
}
|
|
|
|
/* This function is called, when MetaBall Object switched from
|
|
* edit mode to object mode. List of MetaElements is copied
|
|
* from object->data->edit_elems to object->data->elems. */
|
|
void ED_mball_editmball_load(Object *UNUSED(obedit))
|
|
{
|
|
}
|
|
|
|
/* Add metaelem primitive to metaball object (which is in edit mode) */
|
|
MetaElem *ED_mball_add_primitive(bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type)
|
|
{
|
|
MetaBall *mball = (MetaBall *)obedit->data;
|
|
MetaElem *ml;
|
|
|
|
/* Deselect all existing metaelems */
|
|
ml = mball->editelems->first;
|
|
while (ml) {
|
|
ml->flag &= ~SELECT;
|
|
ml = ml->next;
|
|
}
|
|
|
|
ml = BKE_mball_element_add(mball, type);
|
|
ml->rad *= dia;
|
|
mball->wiresize *= dia;
|
|
mball->rendersize *= dia;
|
|
copy_v3_v3(&ml->x, mat[3]);
|
|
|
|
ml->flag |= SELECT;
|
|
mball->lastelem = ml;
|
|
return ml;
|
|
}
|
|
|
|
/***************************** Select/Deselect operator *****************************/
|
|
|
|
/* Select or deselect all MetaElements */
|
|
static int mball_select_all_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml;
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
if (BLI_listbase_is_empty(mb->editelems))
|
|
return OPERATOR_CANCELLED;
|
|
|
|
if (action == SEL_TOGGLE) {
|
|
action = SEL_SELECT;
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (ml->flag & SELECT) {
|
|
action = SEL_DESELECT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (action) {
|
|
case SEL_SELECT:
|
|
BKE_mball_select_all(mb);
|
|
break;
|
|
case SEL_DESELECT:
|
|
BKE_mball_deselect_all(mb);
|
|
break;
|
|
case SEL_INVERT:
|
|
BKE_mball_select_swap(mb);
|
|
break;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_select_all(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "(De)select All";
|
|
ot->description = "Change selection of all meta elements";
|
|
ot->idname = "MBALL_OT_select_all";
|
|
|
|
/* callback functions */
|
|
ot->exec = mball_select_all_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
WM_operator_properties_select_all(ot);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Select Similar */
|
|
|
|
enum {
|
|
SIMMBALL_TYPE = 1,
|
|
SIMMBALL_RADIUS,
|
|
SIMMBALL_STIFFNESS,
|
|
SIMMBALL_ROTATION
|
|
};
|
|
|
|
static EnumPropertyItem prop_similar_types[] = {
|
|
{SIMMBALL_TYPE, "TYPE", 0, "Type", ""},
|
|
{SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""},
|
|
{SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""},
|
|
{SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""},
|
|
{0, NULL, 0, NULL, NULL}
|
|
};
|
|
|
|
static bool mball_select_similar_type(MetaBall *mb)
|
|
{
|
|
MetaElem *ml;
|
|
bool changed = false;
|
|
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (ml->flag & SELECT) {
|
|
MetaElem *ml_iter;
|
|
|
|
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
|
|
if ((ml_iter->flag & SELECT) == 0) {
|
|
if (ml->type == ml_iter->type) {
|
|
ml_iter->flag |= SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static bool mball_select_similar_radius(MetaBall *mb, const float thresh)
|
|
{
|
|
MetaElem *ml;
|
|
bool changed = false;
|
|
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (ml->flag & SELECT) {
|
|
MetaElem *ml_iter;
|
|
|
|
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
|
|
if ((ml_iter->flag & SELECT) == 0) {
|
|
if (fabsf(ml_iter->rad - ml->rad) <= (thresh * ml->rad)) {
|
|
ml_iter->flag |= SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static bool mball_select_similar_stiffness(MetaBall *mb, const float thresh)
|
|
{
|
|
MetaElem *ml;
|
|
bool changed = false;
|
|
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (ml->flag & SELECT) {
|
|
MetaElem *ml_iter;
|
|
|
|
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
|
|
if ((ml_iter->flag & SELECT) == 0) {
|
|
if (fabsf(ml_iter->s - ml->s) <= thresh) {
|
|
ml_iter->flag |= SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static bool mball_select_similar_rotation(MetaBall *mb, const float thresh)
|
|
{
|
|
const float thresh_rad = thresh * (float)M_PI_2;
|
|
MetaElem *ml;
|
|
bool changed = false;
|
|
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (ml->flag & SELECT) {
|
|
MetaElem *ml_iter;
|
|
|
|
float ml_mat[3][3];
|
|
|
|
unit_m3(ml_mat);
|
|
mul_qt_v3(ml->quat, ml_mat[0]);
|
|
mul_qt_v3(ml->quat, ml_mat[1]);
|
|
mul_qt_v3(ml->quat, ml_mat[2]);
|
|
normalize_m3(ml_mat);
|
|
|
|
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
|
|
if ((ml_iter->flag & SELECT) == 0) {
|
|
float ml_iter_mat[3][3];
|
|
|
|
unit_m3(ml_iter_mat);
|
|
mul_qt_v3(ml_iter->quat, ml_iter_mat[0]);
|
|
mul_qt_v3(ml_iter->quat, ml_iter_mat[1]);
|
|
mul_qt_v3(ml_iter->quat, ml_iter_mat[2]);
|
|
normalize_m3(ml_iter_mat);
|
|
|
|
if ((angle_normalized_v3v3(ml_mat[0], ml_iter_mat[0]) +
|
|
angle_normalized_v3v3(ml_mat[1], ml_iter_mat[1]) +
|
|
angle_normalized_v3v3(ml_mat[2], ml_iter_mat[2])) < thresh_rad)
|
|
{
|
|
ml_iter->flag |= SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static int mball_select_similar_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
|
|
int type = RNA_enum_get(op->ptr, "type");
|
|
float thresh = RNA_float_get(op->ptr, "threshold");
|
|
bool changed = false;
|
|
|
|
switch (type) {
|
|
case SIMMBALL_TYPE:
|
|
changed = mball_select_similar_type(mb);
|
|
break;
|
|
case SIMMBALL_RADIUS:
|
|
changed = mball_select_similar_radius(mb, thresh);
|
|
break;
|
|
case SIMMBALL_STIFFNESS:
|
|
changed = mball_select_similar_stiffness(mb, thresh);
|
|
break;
|
|
case SIMMBALL_ROTATION:
|
|
changed = mball_select_similar_rotation(mb, thresh);
|
|
break;
|
|
default:
|
|
BLI_assert(0);
|
|
break;
|
|
}
|
|
|
|
if (changed) {
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_select_similar(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Select Similar";
|
|
ot->idname = "MBALL_OT_select_similar";
|
|
|
|
/* callback functions */
|
|
ot->invoke = WM_menu_invoke;
|
|
ot->exec = mball_select_similar_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
ot->description = "Select similar metaballs by property types";
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
|
|
|
|
RNA_def_float(ot->srna, "threshold", 0.1, 0.0, 1.0, "Threshold", "", 0.01, 1.0);
|
|
}
|
|
|
|
|
|
/***************************** Select random operator *****************************/
|
|
|
|
/* Random metaball selection */
|
|
static int select_random_metaelems_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml;
|
|
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
|
|
const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
|
|
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
|
|
|
|
RNG *rng = BLI_rng_new_srandom(seed);
|
|
|
|
for (ml = mb->editelems->first; ml; ml = ml->next) {
|
|
if (BLI_rng_get_float(rng) < randfac) {
|
|
if (select)
|
|
ml->flag |= SELECT;
|
|
else
|
|
ml->flag &= ~SELECT;
|
|
}
|
|
}
|
|
|
|
BLI_rng_free(rng);
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
|
|
void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Select Random";
|
|
ot->description = "Randomly select metaelements";
|
|
ot->idname = "MBALL_OT_select_random_metaelems";
|
|
|
|
/* callback functions */
|
|
ot->exec = select_random_metaelems_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
WM_operator_properties_select_random(ot);
|
|
}
|
|
|
|
/***************************** Duplicate operator *****************************/
|
|
|
|
/* Duplicate selected MetaElements */
|
|
static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml, *newml;
|
|
|
|
ml = mb->editelems->last;
|
|
if (ml) {
|
|
while (ml) {
|
|
if (ml->flag & SELECT) {
|
|
newml = MEM_dupallocN(ml);
|
|
BLI_addtail(mb->editelems, newml);
|
|
mb->lastelem = newml;
|
|
ml->flag &= ~SELECT;
|
|
}
|
|
ml = ml->prev;
|
|
}
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
|
|
DEG_id_tag_update(obedit->data, 0);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Duplicate Metaelements";
|
|
ot->description = "Duplicate selected metaelement(s)";
|
|
ot->idname = "MBALL_OT_duplicate_metaelems";
|
|
|
|
/* callback functions */
|
|
ot->exec = duplicate_metaelems_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/***************************** Delete operator *****************************/
|
|
|
|
/* Delete all selected MetaElems (not MetaBall) */
|
|
static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml, *next;
|
|
|
|
ml = mb->editelems->first;
|
|
if (ml) {
|
|
while (ml) {
|
|
next = ml->next;
|
|
if (ml->flag & SELECT) {
|
|
if (mb->lastelem == ml) mb->lastelem = NULL;
|
|
BLI_remlink(mb->editelems, ml);
|
|
MEM_freeN(ml);
|
|
}
|
|
ml = next;
|
|
}
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
|
|
DEG_id_tag_update(obedit->data, 0);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_delete_metaelems(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Delete";
|
|
ot->description = "Delete selected metaelement(s)";
|
|
ot->idname = "MBALL_OT_delete_metaelems";
|
|
|
|
/* callback functions */
|
|
ot->exec = delete_metaelems_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/***************************** Hide operator *****************************/
|
|
|
|
/* Hide selected MetaElems */
|
|
static int hide_metaelems_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml;
|
|
const bool invert = RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
|
|
|
|
ml = mb->editelems->first;
|
|
|
|
if (ml) {
|
|
while (ml) {
|
|
if ((ml->flag & SELECT) != invert)
|
|
ml->flag |= MB_HIDE;
|
|
ml = ml->next;
|
|
}
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
|
|
DEG_id_tag_update(obedit->data, 0);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_hide_metaelems(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Hide";
|
|
ot->description = "Hide (un)selected metaelement(s)";
|
|
ot->idname = "MBALL_OT_hide_metaelems";
|
|
|
|
/* callback functions */
|
|
ot->exec = hide_metaelems_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* props */
|
|
RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
|
|
}
|
|
|
|
/***************************** Unhide operator *****************************/
|
|
|
|
/* Unhide all edited MetaElems */
|
|
static int reveal_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml;
|
|
|
|
ml = mb->editelems->first;
|
|
|
|
if (ml) {
|
|
while (ml) {
|
|
ml->flag &= ~MB_HIDE;
|
|
ml = ml->next;
|
|
}
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
|
|
DEG_id_tag_update(obedit->data, 0);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Reveal";
|
|
ot->description = "Reveal all hidden metaelements";
|
|
ot->idname = "MBALL_OT_reveal_metaelems";
|
|
|
|
/* callback functions */
|
|
ot->exec = reveal_metaelems_exec;
|
|
ot->poll = ED_operator_editmball;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/* Select MetaElement with mouse click (user can select radius circle or
|
|
* stiffness circle) */
|
|
bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
|
|
{
|
|
static MetaElem *startelem = NULL;
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
EvaluationContext eval_ctx;
|
|
ViewContext vc;
|
|
MetaBall *mb = (MetaBall *)obedit->data;
|
|
MetaElem *ml, *ml_act = NULL;
|
|
int a, hits;
|
|
unsigned int buffer[MAXPICKBUF];
|
|
rcti rect;
|
|
|
|
CTX_data_eval_ctx(C, &eval_ctx);
|
|
view3d_set_viewcontext(C, &vc);
|
|
|
|
BLI_rcti_init_pt_radius(&rect, mval, 12);
|
|
|
|
hits = view3d_opengl_select(&eval_ctx, &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST);
|
|
|
|
/* does startelem exist? */
|
|
ml = mb->editelems->first;
|
|
while (ml) {
|
|
if (ml == startelem) break;
|
|
ml = ml->next;
|
|
}
|
|
|
|
if (ml == NULL) startelem = mb->editelems->first;
|
|
|
|
if (hits > 0) {
|
|
ml = startelem;
|
|
while (ml) {
|
|
for (a = 0; a < hits; a++) {
|
|
/* index converted for gl stuff */
|
|
if (ml->selcol1 == buffer[4 * a + 3]) {
|
|
ml->flag |= MB_SCALE_RAD;
|
|
ml_act = ml;
|
|
}
|
|
if (ml->selcol2 == buffer[4 * a + 3]) {
|
|
ml->flag &= ~MB_SCALE_RAD;
|
|
ml_act = ml;
|
|
}
|
|
}
|
|
if (ml_act) break;
|
|
ml = ml->next;
|
|
if (ml == NULL) ml = mb->editelems->first;
|
|
if (ml == startelem) break;
|
|
}
|
|
|
|
/* When some metaelem was found, then it is necessary to select or
|
|
* deselect it. */
|
|
if (ml_act) {
|
|
if (extend) {
|
|
ml_act->flag |= SELECT;
|
|
}
|
|
else if (deselect) {
|
|
ml_act->flag &= ~SELECT;
|
|
}
|
|
else if (toggle) {
|
|
if (ml_act->flag & SELECT)
|
|
ml_act->flag &= ~SELECT;
|
|
else
|
|
ml_act->flag |= SELECT;
|
|
}
|
|
else {
|
|
/* Deselect all existing metaelems */
|
|
BKE_mball_deselect_all(mb);
|
|
|
|
/* Select only metaelem clicked on */
|
|
ml_act->flag |= SELECT;
|
|
}
|
|
|
|
mb->lastelem = ml_act;
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* ************* undo for MetaBalls ************* */
|
|
|
|
typedef struct UndoMBall {
|
|
ListBase editelems;
|
|
int lastelem_index;
|
|
} UndoMBall;
|
|
|
|
/* free all MetaElems from ListBase */
|
|
static void freeMetaElemlist(ListBase *lb)
|
|
{
|
|
MetaElem *ml;
|
|
|
|
if (lb == NULL) return;
|
|
|
|
while ((ml = BLI_pophead(lb))) {
|
|
MEM_freeN(ml);
|
|
}
|
|
}
|
|
|
|
|
|
static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata))
|
|
{
|
|
MetaBall *mb = mb_v;
|
|
UndoMBall *umb = umb_v;
|
|
|
|
freeMetaElemlist(mb->editelems);
|
|
mb->lastelem = NULL;
|
|
|
|
/* copy 'undo' MetaElems to 'edit' MetaElems */
|
|
int index = 0;
|
|
for (MetaElem *ml_undo = umb->editelems.first; ml_undo; ml_undo = ml_undo->next, index += 1) {
|
|
MetaElem *ml_edit = MEM_dupallocN(ml_undo);
|
|
BLI_addtail(mb->editelems, ml_edit);
|
|
if (index == umb->lastelem_index) {
|
|
mb->lastelem = ml_edit;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
|
|
{
|
|
MetaBall *mb = mb_v;
|
|
UndoMBall *umb;
|
|
|
|
/* allocate memory for undo ListBase */
|
|
umb = MEM_callocN(sizeof(UndoMBall), __func__);
|
|
umb->lastelem_index = -1;
|
|
|
|
/* copy contents of current ListBase to the undo ListBase */
|
|
int index = 0;
|
|
for (MetaElem *ml_edit = mb->editelems->first; ml_edit; ml_edit = ml_edit->next, index += 1) {
|
|
MetaElem *ml_undo = MEM_dupallocN(ml_edit);
|
|
BLI_addtail(&umb->editelems, ml_undo);
|
|
if (ml_edit == mb->lastelem) {
|
|
umb->lastelem_index = index;
|
|
}
|
|
}
|
|
|
|
return umb;
|
|
}
|
|
|
|
/* free undo ListBase of MetaElems */
|
|
static void free_undoMball(void *umb_v)
|
|
{
|
|
UndoMBall *umb = umb_v;
|
|
|
|
freeMetaElemlist(&umb->editelems);
|
|
MEM_freeN(umb);
|
|
}
|
|
|
|
static MetaBall *metaball_get_obdata(Object *ob)
|
|
{
|
|
if (ob && ob->type == OB_MBALL) {
|
|
return ob->data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void *get_data(bContext *C)
|
|
{
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
return metaball_get_obdata(obedit);
|
|
}
|
|
|
|
/* this is undo system for MetaBalls */
|
|
void undo_push_mball(bContext *C, const char *name)
|
|
{
|
|
undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
|
|
}
|