2011-10-24 23:32:24 +00:00
|
|
|
/*
|
2009-03-02 02:21:18 +00:00
|
|
|
* 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,
|
2012-02-11 04:16:17 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-03-02 02:21:18 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2004 by Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup edmesh
|
2012-03-24 00:20:36 +00:00
|
|
|
*/
|
|
|
|
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2012-02-19 22:17:30 +00:00
|
|
|
#include "DNA_mesh_types.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "DNA_object_types.h"
|
2014-11-10 13:48:27 +01:00
|
|
|
#include "DNA_key_types.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
|
2009-11-23 14:41:22 +00:00
|
|
|
#include "BLI_math.h"
|
2013-07-28 10:38:25 +00:00
|
|
|
#include "BLI_alloca.h"
|
2015-04-09 16:12:20 +02:00
|
|
|
#include "BLI_buffer.h"
|
2015-05-21 13:01:44 +10:00
|
|
|
#include "BLI_kdtree.h"
|
2014-11-10 13:48:27 +01:00
|
|
|
#include "BLI_listbase.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
#include "BKE_DerivedMesh.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "BKE_context.h"
|
2018-06-13 16:29:12 +02:00
|
|
|
#include "BKE_main.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "BKE_mesh.h"
|
2013-12-12 16:26:11 +11:00
|
|
|
#include "BKE_mesh_mapping.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "BKE_report.h"
|
2013-04-13 20:31:52 +00:00
|
|
|
#include "BKE_editmesh.h"
|
2013-04-16 05:23:34 +00:00
|
|
|
#include "BKE_editmesh_bvh.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
|
2017-06-08 10:14:53 +02:00
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
|
2017-11-27 15:39:32 +01:00
|
|
|
#include "BKE_object.h" /* XXX. only for EDBM_mesh_load(). */
|
2012-09-28 11:02:43 +00:00
|
|
|
|
2009-03-02 02:21:18 +00:00
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "ED_mesh.h"
|
2013-04-01 10:18:01 +00:00
|
|
|
#include "ED_screen.h"
|
2013-04-16 05:23:34 +00:00
|
|
|
#include "ED_view3d.h"
|
2009-03-02 02:21:18 +00:00
|
|
|
|
2013-04-01 10:18:01 +00:00
|
|
|
#include "mesh_intern.h" /* own include */
|
2012-03-27 04:46:52 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Redo API
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
/* Mesh backup implementation.
|
|
|
|
* This would greatly benefit from some sort of binary diffing
|
|
|
|
* just as the undo stack would.
|
|
|
|
* So leaving this as an interface for further work */
|
2012-05-16 14:30:41 +00:00
|
|
|
|
|
|
|
BMBackup EDBM_redo_state_store(BMEditMesh *em)
|
|
|
|
{
|
|
|
|
BMBackup backup;
|
|
|
|
backup.bmcopy = BM_mesh_copy(em->bm);
|
|
|
|
return backup;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EDBM_redo_state_restore(BMBackup backup, BMEditMesh *em, int recalctess)
|
|
|
|
{
|
|
|
|
BMesh *tmpbm;
|
2018-03-16 20:46:14 +11:00
|
|
|
if (!em || !backup.bmcopy) {
|
2012-05-16 14:30:41 +00:00
|
|
|
return;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2012-05-16 14:30:41 +00:00
|
|
|
|
|
|
|
BM_mesh_data_free(em->bm);
|
|
|
|
tmpbm = BM_mesh_copy(backup.bmcopy);
|
|
|
|
*em->bm = *tmpbm;
|
|
|
|
MEM_freeN(tmpbm);
|
|
|
|
tmpbm = NULL;
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (recalctess) {
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_tessface_calc(em);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2012-05-16 14:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void EDBM_redo_state_free(BMBackup *backup, BMEditMesh *em, int recalctess)
|
|
|
|
{
|
|
|
|
if (em && backup->bmcopy) {
|
|
|
|
BM_mesh_data_free(em->bm);
|
|
|
|
*em->bm = *backup->bmcopy;
|
|
|
|
}
|
|
|
|
else if (backup->bmcopy) {
|
|
|
|
BM_mesh_data_free(backup->bmcopy);
|
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (backup->bmcopy) {
|
2012-05-16 14:30:41 +00:00
|
|
|
MEM_freeN(backup->bmcopy);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2012-05-16 14:30:41 +00:00
|
|
|
backup->bmcopy = NULL;
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (recalctess && em) {
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_tessface_calc(em);
|
2013-02-20 01:36:35 +00:00
|
|
|
}
|
2011-11-08 00:20:50 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
2009-05-18 14:55:34 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name BMesh Operator (BMO) API Wrapper
|
|
|
|
* \{ */
|
2013-07-10 02:05:16 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt, ...)
|
2009-05-28 04:41:02 +00:00
|
|
|
{
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
va_list list;
|
|
|
|
|
|
|
|
va_start(list, fmt);
|
|
|
|
|
2012-07-21 00:58:02 +00:00
|
|
|
if (!BMO_op_vinitf(bm, bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
|
2012-02-20 01:52:35 +00:00
|
|
|
BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
|
2009-05-28 04:41:02 +00:00
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2009-05-28 04:41:02 +00:00
|
|
|
}
|
2018-03-16 20:46:14 +11:00
|
|
|
|
|
|
|
if (!em->emcopy) {
|
2013-04-16 05:59:48 +00:00
|
|
|
em->emcopy = BKE_editmesh_copy(em);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2009-09-09 06:28:58 +00:00
|
|
|
em->emcopyusers++;
|
2009-06-18 07:11:55 +00:00
|
|
|
|
2009-05-28 04:41:02 +00:00
|
|
|
va_end(list);
|
2009-11-06 12:59:58 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return true;
|
2009-05-28 04:41:02 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
/* returns 0 on error, 1 on success. executes and finishes a bmesh operator */
|
2013-03-19 23:17:44 +00:00
|
|
|
bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
|
2011-11-07 09:02:10 +00:00
|
|
|
{
|
2011-05-07 02:48:14 +00:00
|
|
|
const char *errmsg;
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_finish(em->bm, bmop);
|
2009-05-28 04:41:02 +00:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
if (BMO_error_get(em->bm, &errmsg, NULL)) {
|
2009-06-18 07:11:55 +00:00
|
|
|
BMEditMesh *emcopy = em->emcopy;
|
2009-05-28 04:41:02 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
if (do_report) {
|
2012-03-28 06:44:19 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, errmsg);
|
|
|
|
}
|
2009-06-18 07:11:55 +00:00
|
|
|
|
2012-04-23 04:24:11 +00:00
|
|
|
EDBM_mesh_free(em);
|
2009-06-18 07:11:55 +00:00
|
|
|
*em = *emcopy;
|
2009-06-18 07:15:17 +00:00
|
|
|
|
|
|
|
MEM_freeN(emcopy);
|
2009-09-09 06:28:58 +00:00
|
|
|
em->emcopyusers = 0;
|
|
|
|
em->emcopy = NULL;
|
2012-03-28 06:44:19 +00:00
|
|
|
|
|
|
|
/* when copying, tessellation isn't to for faster copying,
|
|
|
|
* but means we need to re-tessellate here */
|
|
|
|
if (em->looptris == NULL) {
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_tessface_calc(em);
|
2012-03-28 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2018-06-12 20:43:46 +02:00
|
|
|
if (em->ob) {
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&((Mesh *)em->ob->data)->id, ID_RECALC_COPY_ON_WRITE);
|
2018-06-12 20:43:46 +02:00
|
|
|
}
|
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2012-02-05 15:55:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-09-09 06:28:58 +00:00
|
|
|
em->emcopyusers--;
|
|
|
|
if (em->emcopyusers < 0) {
|
2014-11-21 14:14:50 +01:00
|
|
|
printf("warning: em->emcopyusers was less than zero.\n");
|
2009-09-09 06:28:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (em->emcopyusers <= 0) {
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_free(em->emcopy);
|
2009-09-09 06:28:58 +00:00
|
|
|
MEM_freeN(em->emcopy);
|
|
|
|
em->emcopy = NULL;
|
|
|
|
}
|
2009-05-28 04:41:02 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
return true;
|
2012-03-28 06:44:19 +00:00
|
|
|
}
|
2009-05-28 04:41:02 +00:00
|
|
|
}
|
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt, ...)
|
Created a printf-style method of calling operators. I did this to cut down on duplicated
code, and also because calling operators was such a pain. The basic form of the format
is "opname %[code]", where each % matches to an argument.
The codes are fairly simple:
d - int
i - int
f - float
h[v/e/f] - all verts/edges/faces with a certain header flag.
f[v/e/f] - all verts/edges/faces with a certain flag.
For example:
EDBM_CallOpf(em, op, "dissolveverts %hv", BM_SELECT)
will call the dissolve verts operator.
The relevent functions are:
//calls a bmesh operator, doing necassary conversions and error reporting.
int EDBM_CallOpf(EditMesh *em, struct wmOperator *op, char *fmt, ...);
//execute an operator
int BMO_CallOpf(BMesh *bm, char *fmt, ...);
//initializes but doesn't execute an op.
int BMO_InitOpf(BMesh *bm, BMOperator *op, char *fmt, ...);
//vlist version of above.
int BMO_VInitOpf(BMesh *bm, BMOperator *op, char *fmt, va_list vlist);
Note this system is dependant on getting the slot codes from the argument
order. I'd like to make it better, possibly pass in slot names, but that'd
mean actually giving the slots names (which I can do, but wanted to discuss with
Briggs and others what I have now first).
2009-03-02 04:08:24 +00:00
|
|
|
{
|
2009-05-19 00:33:54 +00:00
|
|
|
BMesh *bm = em->bm;
|
Created a printf-style method of calling operators. I did this to cut down on duplicated
code, and also because calling operators was such a pain. The basic form of the format
is "opname %[code]", where each % matches to an argument.
The codes are fairly simple:
d - int
i - int
f - float
h[v/e/f] - all verts/edges/faces with a certain header flag.
f[v/e/f] - all verts/edges/faces with a certain flag.
For example:
EDBM_CallOpf(em, op, "dissolveverts %hv", BM_SELECT)
will call the dissolve verts operator.
The relevent functions are:
//calls a bmesh operator, doing necassary conversions and error reporting.
int EDBM_CallOpf(EditMesh *em, struct wmOperator *op, char *fmt, ...);
//execute an operator
int BMO_CallOpf(BMesh *bm, char *fmt, ...);
//initializes but doesn't execute an op.
int BMO_InitOpf(BMesh *bm, BMOperator *op, char *fmt, ...);
//vlist version of above.
int BMO_VInitOpf(BMesh *bm, BMOperator *op, char *fmt, va_list vlist);
Note this system is dependant on getting the slot codes from the argument
order. I'd like to make it better, possibly pass in slot names, but that'd
mean actually giving the slots names (which I can do, but wanted to discuss with
Briggs and others what I have now first).
2009-03-02 04:08:24 +00:00
|
|
|
BMOperator bmop;
|
|
|
|
va_list list;
|
|
|
|
|
|
|
|
va_start(list, fmt);
|
|
|
|
|
2012-07-21 00:58:02 +00:00
|
|
|
if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
|
2012-02-20 01:52:35 +00:00
|
|
|
BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
|
Created a printf-style method of calling operators. I did this to cut down on duplicated
code, and also because calling operators was such a pain. The basic form of the format
is "opname %[code]", where each % matches to an argument.
The codes are fairly simple:
d - int
i - int
f - float
h[v/e/f] - all verts/edges/faces with a certain header flag.
f[v/e/f] - all verts/edges/faces with a certain flag.
For example:
EDBM_CallOpf(em, op, "dissolveverts %hv", BM_SELECT)
will call the dissolve verts operator.
The relevent functions are:
//calls a bmesh operator, doing necassary conversions and error reporting.
int EDBM_CallOpf(EditMesh *em, struct wmOperator *op, char *fmt, ...);
//execute an operator
int BMO_CallOpf(BMesh *bm, char *fmt, ...);
//initializes but doesn't execute an op.
int BMO_InitOpf(BMesh *bm, BMOperator *op, char *fmt, ...);
//vlist version of above.
int BMO_VInitOpf(BMesh *bm, BMOperator *op, char *fmt, va_list vlist);
Note this system is dependant on getting the slot codes from the argument
order. I'd like to make it better, possibly pass in slot names, but that'd
mean actually giving the slots names (which I can do, but wanted to discuss with
Briggs and others what I have now first).
2009-03-02 04:08:24 +00:00
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
Created a printf-style method of calling operators. I did this to cut down on duplicated
code, and also because calling operators was such a pain. The basic form of the format
is "opname %[code]", where each % matches to an argument.
The codes are fairly simple:
d - int
i - int
f - float
h[v/e/f] - all verts/edges/faces with a certain header flag.
f[v/e/f] - all verts/edges/faces with a certain flag.
For example:
EDBM_CallOpf(em, op, "dissolveverts %hv", BM_SELECT)
will call the dissolve verts operator.
The relevent functions are:
//calls a bmesh operator, doing necassary conversions and error reporting.
int EDBM_CallOpf(EditMesh *em, struct wmOperator *op, char *fmt, ...);
//execute an operator
int BMO_CallOpf(BMesh *bm, char *fmt, ...);
//initializes but doesn't execute an op.
int BMO_InitOpf(BMesh *bm, BMOperator *op, char *fmt, ...);
//vlist version of above.
int BMO_VInitOpf(BMesh *bm, BMOperator *op, char *fmt, va_list vlist);
Note this system is dependant on getting the slot codes from the argument
order. I'd like to make it better, possibly pass in slot names, but that'd
mean actually giving the slots names (which I can do, but wanted to discuss with
Briggs and others what I have now first).
2009-03-02 04:08:24 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (!em->emcopy) {
|
2013-04-16 05:59:48 +00:00
|
|
|
em->emcopy = BKE_editmesh_copy(em);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2009-09-09 06:28:58 +00:00
|
|
|
em->emcopyusers++;
|
2009-06-18 07:11:55 +00:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_exec(bm, &bmop);
|
Created a printf-style method of calling operators. I did this to cut down on duplicated
code, and also because calling operators was such a pain. The basic form of the format
is "opname %[code]", where each % matches to an argument.
The codes are fairly simple:
d - int
i - int
f - float
h[v/e/f] - all verts/edges/faces with a certain header flag.
f[v/e/f] - all verts/edges/faces with a certain flag.
For example:
EDBM_CallOpf(em, op, "dissolveverts %hv", BM_SELECT)
will call the dissolve verts operator.
The relevent functions are:
//calls a bmesh operator, doing necassary conversions and error reporting.
int EDBM_CallOpf(EditMesh *em, struct wmOperator *op, char *fmt, ...);
//execute an operator
int BMO_CallOpf(BMesh *bm, char *fmt, ...);
//initializes but doesn't execute an op.
int BMO_InitOpf(BMesh *bm, BMOperator *op, char *fmt, ...);
//vlist version of above.
int BMO_VInitOpf(BMesh *bm, BMOperator *op, char *fmt, va_list vlist);
Note this system is dependant on getting the slot codes from the argument
order. I'd like to make it better, possibly pass in slot names, but that'd
mean actually giving the slots names (which I can do, but wanted to discuss with
Briggs and others what I have now first).
2009-03-02 04:08:24 +00:00
|
|
|
|
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return EDBM_op_finish(em, &bmop, op, true);
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
bool EDBM_op_call_and_selectf(
|
|
|
|
BMEditMesh *em, wmOperator *op,
|
|
|
|
const char *select_slot_out, const bool select_extend,
|
|
|
|
const char *fmt, ...)
|
2010-01-28 00:45:30 +00:00
|
|
|
{
|
2012-11-27 02:34:40 +00:00
|
|
|
BMOpSlot *slot_select_out;
|
2010-01-28 00:45:30 +00:00
|
|
|
BMesh *bm = em->bm;
|
|
|
|
BMOperator bmop;
|
|
|
|
va_list list;
|
2012-11-27 02:34:40 +00:00
|
|
|
char hflag;
|
2010-01-28 00:45:30 +00:00
|
|
|
|
|
|
|
va_start(list, fmt);
|
|
|
|
|
2012-07-21 00:58:02 +00:00
|
|
|
if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
|
2012-02-20 01:52:35 +00:00
|
|
|
BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
|
2010-01-28 00:45:30 +00:00
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
2010-01-28 00:45:30 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (!em->emcopy) {
|
2013-04-16 05:59:48 +00:00
|
|
|
em->emcopy = BKE_editmesh_copy(em);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2010-01-28 00:45:30 +00:00
|
|
|
em->emcopyusers++;
|
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_exec(bm, &bmop);
|
2012-02-16 19:33:46 +00:00
|
|
|
|
2012-11-27 02:34:40 +00:00
|
|
|
slot_select_out = BMO_slot_get(bmop.slots_out, select_slot_out);
|
|
|
|
hflag = slot_select_out->slot_subtype.elem & BM_ALL_NOLOOP;
|
2013-07-25 07:00:07 +00:00
|
|
|
BLI_assert(hflag != 0);
|
2012-11-27 02:34:40 +00:00
|
|
|
|
2013-07-25 07:00:07 +00:00
|
|
|
if (select_extend == false) {
|
|
|
|
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
|
|
|
|
}
|
2012-02-16 19:33:46 +00:00
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, select_slot_out, hflag, BM_ELEM_SELECT, true);
|
2010-01-28 00:45:30 +00:00
|
|
|
|
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return EDBM_op_finish(em, &bmop, op, true);
|
2010-01-28 00:45:30 +00:00
|
|
|
}
|
|
|
|
|
2013-03-19 23:17:44 +00:00
|
|
|
bool EDBM_op_call_silentf(BMEditMesh *em, const char *fmt, ...)
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
{
|
2009-05-19 00:33:54 +00:00
|
|
|
BMesh *bm = em->bm;
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
BMOperator bmop;
|
|
|
|
va_list list;
|
|
|
|
|
|
|
|
va_start(list, fmt);
|
|
|
|
|
2012-07-21 00:58:02 +00:00
|
|
|
if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return false;
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (!em->emcopy) {
|
2013-04-16 05:59:48 +00:00
|
|
|
em->emcopy = BKE_editmesh_copy(em);
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2009-09-09 06:28:58 +00:00
|
|
|
em->emcopyusers++;
|
2009-06-18 07:11:55 +00:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_exec(bm, &bmop);
|
Printf-style method of calling operations now take a modified format string,
like so:
[opname] [slotname]=%[format code]
Before it was relying on the input format codes being in the same proper
order as the slots, which seemed like a potential maintainance nightmare to
me. Also the flags for creating buffers from bmop flags or header flags,
now support additional modifiers for combining vert/edge/face inputs.
E.g. %hfvef would accept all geometry with a header flag, and
%fef would accept edges and faces with a certain bmop flag set.
Example from the UI code:
if (!EDBM_CallOpf(em, op, "del geom=%hf context=%d", BM_SELECT, DEL_ONLYFACES))
return OPERATOR_CANCELLED;
(remember EDBM_CallOpf is the UI wrapper for this that does conversion,
error reporting, etc).
On todo is cleaning up/splitting bmesh_operators.h,
since it's kindof a mesh right now. I'm thinking of adding the slot
names in comments next to the slot ids, but I definitely would have to
clean up bmesh_operators.h first, or it'd just be too chaotic for me.
BTW, the operator API should now have enough meta info to wrap with
a scripting language, not that it matters since that's not happening till
much much later.
Also hopefully corrected some SConscripts, fix mostly provided by Elia Sarti,
though I also copied some SConscripts from 2.5 (not sure if doing
so was especially helpful).
Finally, I refactored a few places to use the new operator calling api,
as an example of how this is beneficial.
2009-03-04 08:21:10 +00:00
|
|
|
|
|
|
|
va_end(list);
|
2013-03-19 23:17:44 +00:00
|
|
|
return EDBM_op_finish(em, &bmop, NULL, false);
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
2010-03-09 04:32:40 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Edit BMesh API
|
|
|
|
*
|
|
|
|
* Make/Clear/Free functions.
|
|
|
|
* \{ */
|
2010-03-09 04:32:40 +00:00
|
|
|
|
2018-03-14 02:02:05 +11:00
|
|
|
void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
|
2009-05-16 16:18:08 +00:00
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
BMesh *bm;
|
|
|
|
|
2012-03-27 01:28:25 +00:00
|
|
|
if (UNLIKELY(!me->mpoly && me->totface)) {
|
|
|
|
BKE_mesh_convert_mfaces_to_mpolys(me);
|
2009-05-26 04:17:47 +00:00
|
|
|
}
|
2009-05-16 16:18:08 +00:00
|
|
|
|
2016-07-01 19:07:11 +10:00
|
|
|
bm = BKE_mesh_to_bmesh(
|
|
|
|
me, ob, add_key_index,
|
|
|
|
&((struct BMeshCreateParams){.use_toolflags = true,}));
|
2012-03-27 01:28:25 +00:00
|
|
|
|
2019-02-17 18:05:18 +11:00
|
|
|
if (me->edit_mesh) {
|
2011-10-30 02:15:32 +00:00
|
|
|
/* this happens when switching shape keys */
|
2019-02-17 18:05:18 +11:00
|
|
|
EDBM_mesh_free(me->edit_mesh);
|
|
|
|
MEM_freeN(me->edit_mesh);
|
2011-10-30 02:15:32 +00:00
|
|
|
}
|
|
|
|
|
2012-03-03 11:45:08 +00:00
|
|
|
/* currently executing operators re-tessellates, so we can avoid doing here
|
2012-01-22 22:20:20 +00:00
|
|
|
* but at some point it may need to be added back. */
|
|
|
|
#if 0
|
2019-02-17 18:05:18 +11:00
|
|
|
me->edit_mesh = BKE_editmesh_create(bm, true);
|
2012-01-22 22:20:20 +00:00
|
|
|
#else
|
2019-02-17 18:05:18 +11:00
|
|
|
me->edit_mesh = BKE_editmesh_create(bm, false);
|
2012-01-22 22:20:20 +00:00
|
|
|
#endif
|
|
|
|
|
2019-02-17 18:05:18 +11:00
|
|
|
me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode;
|
|
|
|
me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0;
|
|
|
|
me->edit_mesh->ob = ob;
|
2013-07-22 10:44:24 +00:00
|
|
|
|
|
|
|
/* we need to flush selection because the mode may have changed from when last in editmode */
|
2019-02-17 18:05:18 +11:00
|
|
|
EDBM_selectmode_flush(me->edit_mesh);
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 20:25:05 +11:00
|
|
|
/**
|
2018-09-27 15:54:10 +02:00
|
|
|
* \warning This can invalidate the #Mesh runtime cache of other objects (for linked duplicates).
|
2017-08-16 11:52:27 +02:00
|
|
|
* Most callers should run #DEG_id_tag_update on \a ob->data, see: T46738, T46913
|
2015-11-10 20:25:05 +11:00
|
|
|
*/
|
2018-06-13 16:29:12 +02:00
|
|
|
void EDBM_mesh_load(Main *bmain, Object *ob)
|
2009-05-16 16:18:08 +00:00
|
|
|
{
|
|
|
|
Mesh *me = ob->data;
|
2019-02-17 18:05:18 +11:00
|
|
|
BMesh *bm = me->edit_mesh->bm;
|
2009-05-26 04:17:47 +00:00
|
|
|
|
2014-11-10 13:32:39 +01:00
|
|
|
/* Workaround for T42360, 'ob->shapenr' should be 1 in this case.
|
|
|
|
* however this isn't synchronized between objects at the moment. */
|
|
|
|
if (UNLIKELY((ob->shapenr == 0) && (me->key && !BLI_listbase_is_empty(&me->key->block)))) {
|
|
|
|
bm->shapenr = 1;
|
|
|
|
}
|
|
|
|
|
2018-02-19 17:27:01 +11:00
|
|
|
BM_mesh_bm_to_me(
|
2018-06-13 16:29:12 +02:00
|
|
|
bmain, bm, me, (&(struct BMeshToMeshParams){
|
2018-02-19 17:27:01 +11:00
|
|
|
.calc_object_remap = true,
|
|
|
|
}));
|
2012-02-20 00:18:35 +00:00
|
|
|
|
|
|
|
#ifdef USE_TESSFACE_DEFAULT
|
|
|
|
BKE_mesh_tessface_calc(me);
|
|
|
|
#endif
|
2013-05-18 10:24:34 +00:00
|
|
|
|
2016-07-01 14:19:47 +02:00
|
|
|
/* Free derived mesh. usually this would happen through depsgraph but there
|
2013-05-18 10:24:34 +00:00
|
|
|
* are exceptions like file save that will not cause this, and we want to
|
2016-07-01 14:19:47 +02:00
|
|
|
* avoid ending up with an invalid derived mesh then.
|
|
|
|
*
|
|
|
|
* Do it for all objects which shares the same mesh datablock, since their
|
|
|
|
* derived meshes might also be referencing data which was just freed,
|
|
|
|
*
|
|
|
|
* Annoying enough, but currently seems most efficient way to avoid access
|
|
|
|
* of freed data on scene update, especially in cases when there are dependency
|
|
|
|
* cycles.
|
|
|
|
*/
|
2018-01-10 19:57:02 +11:00
|
|
|
#if 0
|
2019-03-08 09:29:17 +11:00
|
|
|
for (Object *other_object = bmain->objects.first;
|
2016-07-01 14:19:47 +02:00
|
|
|
other_object != NULL;
|
|
|
|
other_object = other_object->id.next)
|
|
|
|
{
|
|
|
|
if (other_object->data == ob->data) {
|
|
|
|
BKE_object_free_derived_caches(other_object);
|
|
|
|
}
|
|
|
|
}
|
2018-01-10 19:57:02 +11:00
|
|
|
#endif
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
void EDBM_mesh_clear(BMEditMesh *em)
|
|
|
|
{
|
|
|
|
/* clear bmesh */
|
|
|
|
BM_mesh_clear(em->bm);
|
|
|
|
|
|
|
|
/* free derived meshes */
|
|
|
|
BKE_editmesh_free_derivedmesh(em);
|
|
|
|
|
|
|
|
/* free tessellation data */
|
|
|
|
em->tottri = 0;
|
|
|
|
if (em->looptris) {
|
|
|
|
MEM_freeN(em->looptris);
|
|
|
|
em->looptris = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-23 04:24:11 +00:00
|
|
|
/**
|
2013-04-16 05:59:48 +00:00
|
|
|
* Should only be called on the active editmesh, otherwise call #BKE_editmesh_free
|
2012-04-23 04:24:11 +00:00
|
|
|
*/
|
|
|
|
void EDBM_mesh_free(BMEditMesh *em)
|
2009-05-16 16:18:08 +00:00
|
|
|
{
|
2012-04-23 04:24:11 +00:00
|
|
|
/* These tables aren't used yet, so it's not strictly necessary
|
|
|
|
* to 'end' them (with 'e' param) but if someone tries to start
|
|
|
|
* using them, having these in place will save a lot of pain */
|
Fix T47038: Particles in Particle Edit Mode get added in completely wrong location.
It also fixes another issue (crash) related to symmetric editing.
Quite involved, we (try to!) fix complete broken logic of parts of particle code, which would use poly index
as tessface one (or vice-versa). Issue most probably goes back to BMesh integration time...
This patch mostly fixes particle editing mode:
- Adding/removing particles when using generative modifiers (like subsurf) should now work.
- Adding/removing particles with a non-tessellated mesh (i.e. one having ngons) should also mostly work.
- X-axis-mirror-editing particles over ngons does not really work, not sure why currently.
- All this in both 'modes' (with or without using modifier stack for particles).
Tech side:
- Store a deformed-only DM in particle modifier data.
- Rename existing DM to make it clear it's a final one.
- Use deformed-only DM's tessface2poly mapping to 'solve' poly/tessface mismatches.
- Make (part of) mirror-editing code able to use a DM instead of raw mesh, so that we can mirror based on final DM
when editing particles using modifier stack (mandatory, since there is no way currently to find orig tessface
from an final DM tessface index).
Note that this patch is not really nice and clean (current particles are beyond hope on this side anyway),
it's more like some urgency bandage. Whole crap needs complete rewrite anyway,
BMesh's polygons make it really hard to work with current system (and looptri would not help much here).
Also, did not test everything possibly affected by those changes, so it needs some users' testing & validation too.
Reviewers: psy-fi
Subscribers: dfelinto, eyecandy
Maniphest Tasks: T47038
Differential Revision: https://developer.blender.org/D1685
2016-01-04 12:19:45 +01:00
|
|
|
ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e');
|
|
|
|
ED_mesh_mirror_topo_table(NULL, NULL, 'e');
|
2012-04-23 04:24:11 +00:00
|
|
|
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_free(em);
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Selection Utilities
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
void EDBM_selectmode_to_scene(bContext *C)
|
|
|
|
{
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
|
|
|
|
|
|
if (!em) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scene->toolsettings->selectmode = em->selectmode;
|
|
|
|
|
|
|
|
/* Request redraw of header buttons (to show new select mode) */
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene);
|
|
|
|
}
|
|
|
|
|
2012-07-20 09:49:54 +00:00
|
|
|
void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
|
2009-08-05 02:34:54 +00:00
|
|
|
{
|
2012-07-20 09:49:54 +00:00
|
|
|
BM_mesh_select_mode_flush_ex(em->bm, selectmode);
|
2009-08-05 02:34:54 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 06:16:21 +00:00
|
|
|
void EDBM_selectmode_flush(BMEditMesh *em)
|
|
|
|
{
|
|
|
|
EDBM_selectmode_flush_ex(em, em->selectmode);
|
|
|
|
}
|
|
|
|
|
2012-02-10 03:03:42 +00:00
|
|
|
void EDBM_deselect_flush(BMEditMesh *em)
|
2009-09-16 17:43:09 +00:00
|
|
|
{
|
2018-09-27 15:35:22 +02:00
|
|
|
/* function below doesn't use. just do this to keep the values in sync */
|
2012-02-10 03:03:42 +00:00
|
|
|
em->bm->selectmode = em->selectmode;
|
2012-02-12 10:51:45 +00:00
|
|
|
BM_mesh_deselect_flush(em->bm);
|
2009-09-16 17:43:09 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 03:03:42 +00:00
|
|
|
void EDBM_select_flush(BMEditMesh *em)
|
2009-05-16 16:18:08 +00:00
|
|
|
{
|
2018-09-27 15:35:22 +02:00
|
|
|
/* function below doesn't use. just do this to keep the values in sync */
|
2009-05-18 10:29:37 +00:00
|
|
|
em->bm->selectmode = em->selectmode;
|
2012-02-12 10:51:45 +00:00
|
|
|
BM_mesh_select_flush(em->bm);
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 15:17:16 +01:00
|
|
|
void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
|
2009-08-06 05:06:55 +00:00
|
|
|
{
|
|
|
|
BMOperator bmop;
|
2014-11-06 15:17:16 +01:00
|
|
|
const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
|
2009-08-06 05:06:55 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
BMO_op_initf(
|
|
|
|
em->bm, &bmop, BMO_FLAG_DEFAULTS,
|
|
|
|
"region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
|
|
|
|
BM_ELEM_SELECT, false, use_faces, use_face_step);
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_exec(em->bm, &bmop);
|
2012-03-18 07:38:51 +00:00
|
|
|
/* don't flush selection in edge/vertex mode */
|
2013-03-19 23:17:44 +00:00
|
|
|
BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_finish(em->bm, &bmop);
|
2009-08-06 05:06:55 +00:00
|
|
|
|
2013-01-08 17:40:36 +00:00
|
|
|
EDBM_selectmode_flush(em);
|
2009-08-06 05:06:55 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 15:17:16 +01:00
|
|
|
void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
|
2009-08-06 05:06:55 +00:00
|
|
|
{
|
|
|
|
BMOperator bmop;
|
2014-11-06 15:17:16 +01:00
|
|
|
const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
|
2009-08-06 05:06:55 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
BMO_op_initf(
|
|
|
|
em->bm, &bmop, BMO_FLAG_DEFAULTS,
|
|
|
|
"region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
|
|
|
|
BM_ELEM_SELECT, true, use_faces, use_face_step);
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_exec(em->bm, &bmop);
|
2012-03-18 07:38:51 +00:00
|
|
|
/* don't flush selection in edge/vertex mode */
|
2013-03-19 23:17:44 +00:00
|
|
|
BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
|
2012-02-12 10:51:45 +00:00
|
|
|
BMO_op_finish(em->bm, &bmop);
|
2009-08-06 05:06:55 +00:00
|
|
|
|
2012-02-10 06:16:21 +00:00
|
|
|
EDBM_selectmode_flush(em);
|
2016-03-15 14:17:54 +11:00
|
|
|
|
|
|
|
/* only needed for select less, ensure we don't have isolated elements remaining */
|
|
|
|
BM_mesh_select_mode_clean(em->bm);
|
2009-08-06 05:06:55 +00:00
|
|
|
}
|
2009-05-16 16:18:08 +00:00
|
|
|
|
2012-02-12 18:43:59 +00:00
|
|
|
void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
|
2009-05-18 08:46:04 +00:00
|
|
|
{
|
2013-03-19 23:17:44 +00:00
|
|
|
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, hflag, false);
|
2009-05-18 08:46:04 +00:00
|
|
|
}
|
|
|
|
|
2012-02-12 18:43:59 +00:00
|
|
|
void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
|
2009-05-18 10:29:37 +00:00
|
|
|
{
|
2013-03-19 23:17:44 +00:00
|
|
|
BM_mesh_elem_hflag_enable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, hflag, true);
|
2009-05-16 16:18:08 +00:00
|
|
|
}
|
2009-05-18 14:55:34 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name UV Vertex Map API
|
|
|
|
* \{ */
|
|
|
|
|
2013-01-12 20:01:09 +00:00
|
|
|
/**
|
|
|
|
* Return a new UVVertMap from the editmesh
|
|
|
|
*/
|
2015-05-17 23:04:34 +10:00
|
|
|
UvVertMap *BM_uv_vert_map_create(
|
|
|
|
BMesh *bm,
|
|
|
|
const float limit[2], const bool use_select, const bool use_winding)
|
2009-07-21 08:39:58 +00:00
|
|
|
{
|
|
|
|
BMVert *ev;
|
|
|
|
BMFace *efa;
|
|
|
|
BMLoop *l;
|
|
|
|
BMIter iter, liter;
|
|
|
|
/* vars from original func */
|
|
|
|
UvVertMap *vmap;
|
|
|
|
UvMapVert *buf;
|
|
|
|
MLoopUV *luv;
|
|
|
|
unsigned int a;
|
2015-04-09 16:12:20 +02:00
|
|
|
int totverts, i, totuv, totfaces;
|
2013-10-28 02:05:33 +00:00
|
|
|
const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
|
2015-05-17 23:04:34 +10:00
|
|
|
bool *winding = NULL;
|
2015-04-09 16:12:20 +02:00
|
|
|
BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE);
|
2011-11-01 12:51:38 +00:00
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE);
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
totfaces = bm->totface;
|
2013-10-28 02:05:33 +00:00
|
|
|
totverts = bm->totvert;
|
2009-07-21 08:39:58 +00:00
|
|
|
totuv = 0;
|
|
|
|
|
|
|
|
/* generate UvMapVert array */
|
2013-10-28 02:05:33 +00:00
|
|
|
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
2013-01-12 20:01:09 +00:00
|
|
|
if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
2009-07-21 08:39:58 +00:00
|
|
|
totuv += efa->len;
|
2013-01-12 20:01:09 +00:00
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
if (totuv == 0) {
|
2009-07-21 08:39:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2012-02-10 18:09:19 +00:00
|
|
|
vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap");
|
2009-07-21 08:39:58 +00:00
|
|
|
if (!vmap) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-21 09:10:08 +00:00
|
|
|
vmap->vert = (UvMapVert **)MEM_callocN(sizeof(*vmap->vert) * totverts, "UvMapVert_pt");
|
|
|
|
buf = vmap->buf = (UvMapVert *)MEM_callocN(sizeof(*vmap->buf) * totuv, "UvMapVert");
|
2015-05-17 23:04:34 +10:00
|
|
|
if (use_winding) {
|
|
|
|
winding = MEM_callocN(sizeof(*winding) * totfaces, "winding");
|
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
|
|
|
|
if (!vmap->vert || !vmap->buf) {
|
2012-05-05 21:28:12 +00:00
|
|
|
BKE_mesh_uv_vert_map_free(vmap);
|
2009-07-21 08:39:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
|
2013-01-12 20:01:09 +00:00
|
|
|
if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
2018-05-02 10:32:01 +02:00
|
|
|
float (*tf_uv)[2] = NULL;
|
2015-05-17 23:04:34 +10:00
|
|
|
|
|
|
|
if (use_winding) {
|
2015-10-04 14:14:28 +11:00
|
|
|
tf_uv = (float (*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
|
2015-05-17 23:04:34 +10:00
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
|
|
|
|
BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, i) {
|
2018-07-16 15:37:27 +02:00
|
|
|
buf->loop_of_poly_index = i;
|
|
|
|
buf->poly_index = a;
|
2009-07-21 08:39:58 +00:00
|
|
|
buf->separate = 0;
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
buf->next = vmap->vert[BM_elem_index_get(l->v)];
|
|
|
|
vmap->vert[BM_elem_index_get(l->v)] = buf;
|
2009-07-21 08:39:58 +00:00
|
|
|
buf++;
|
2015-05-17 23:04:34 +10:00
|
|
|
|
|
|
|
if (use_winding) {
|
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
|
|
|
copy_v2_v2(tf_uv[i], luv->uv);
|
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
|
|
|
|
2015-05-17 23:04:34 +10:00
|
|
|
if (use_winding) {
|
2017-10-20 14:03:22 +11:00
|
|
|
winding[a] = cross_poly_v2(tf_uv, efa->len) > 0;
|
2015-05-17 23:04:34 +10:00
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2009-07-21 08:39:58 +00:00
|
|
|
/* sort individual uvs for each vert */
|
2015-04-09 16:12:20 +02:00
|
|
|
BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) {
|
2012-02-10 18:09:19 +00:00
|
|
|
UvMapVert *newvlist = NULL, *vlist = vmap->vert[a];
|
2009-07-21 08:39:58 +00:00
|
|
|
UvMapVert *iterv, *v, *lastv, *next;
|
|
|
|
float *uv, *uv2, uvdiff[2];
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
while (vlist) {
|
|
|
|
v = vlist;
|
|
|
|
vlist = vlist->next;
|
|
|
|
v->next = newvlist;
|
|
|
|
newvlist = v;
|
2009-07-21 08:39:58 +00:00
|
|
|
|
2018-07-16 15:37:27 +02:00
|
|
|
efa = BM_face_at_index(bm, v->poly_index);
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2018-07-16 15:37:27 +02:00
|
|
|
l = BM_iter_at_index(bm, BM_LOOPS_OF_FACE, efa, v->loop_of_poly_index);
|
2013-01-12 20:01:09 +00:00
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
2012-02-10 18:09:19 +00:00
|
|
|
uv = luv->uv;
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
lastv = NULL;
|
|
|
|
iterv = vlist;
|
2009-07-21 08:39:58 +00:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
while (iterv) {
|
|
|
|
next = iterv->next;
|
2018-07-16 15:37:27 +02:00
|
|
|
efa = BM_face_at_index(bm, iterv->poly_index);
|
|
|
|
l = BM_iter_at_index(bm, BM_LOOPS_OF_FACE, efa, iterv->loop_of_poly_index);
|
2013-01-12 20:01:09 +00:00
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
2012-02-10 18:09:19 +00:00
|
|
|
uv2 = luv->uv;
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2009-11-23 14:41:22 +00:00
|
|
|
sub_v2_v2v2(uvdiff, uv2, uv);
|
2009-07-21 08:39:58 +00:00
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
if (fabsf(uvdiff[0]) < limit[0] && fabsf(uvdiff[1]) < limit[1] &&
|
2018-07-16 15:37:27 +02:00
|
|
|
(!use_winding || winding[iterv->poly_index] == winding[v->poly_index]))
|
2015-04-09 16:12:20 +02:00
|
|
|
{
|
2012-02-10 18:09:19 +00:00
|
|
|
if (lastv) lastv->next = next;
|
|
|
|
else vlist = next;
|
|
|
|
iterv->next = newvlist;
|
|
|
|
newvlist = iterv;
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
2012-03-06 17:23:26 +00:00
|
|
|
else {
|
2012-02-10 18:09:19 +00:00
|
|
|
lastv = iterv;
|
2012-03-06 17:23:26 +00:00
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
iterv = next;
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
newvlist->separate = 1;
|
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
vmap->vert[a] = newvlist;
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
2012-12-12 06:53:39 +00:00
|
|
|
|
2015-05-17 23:04:34 +10:00
|
|
|
if (use_winding) {
|
|
|
|
MEM_freeN(winding);
|
|
|
|
}
|
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
BLI_buffer_free(&tf_uv_buf);
|
|
|
|
|
2009-07-21 08:39:58 +00:00
|
|
|
return vmap;
|
|
|
|
}
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, unsigned int v)
|
2009-07-21 08:39:58 +00:00
|
|
|
{
|
|
|
|
return vmap->vert[v];
|
|
|
|
}
|
|
|
|
|
2012-01-17 21:08:25 +00:00
|
|
|
/* A specialized vert map used by stitch operator */
|
2015-06-20 19:28:51 +10:00
|
|
|
UvElementMap *BM_uv_element_map_create(
|
|
|
|
BMesh *bm,
|
|
|
|
const bool selected, const bool use_winding, const bool do_islands)
|
2012-01-17 21:08:25 +00:00
|
|
|
{
|
2012-02-06 19:25:12 +00:00
|
|
|
BMVert *ev;
|
|
|
|
BMFace *efa;
|
|
|
|
BMLoop *l;
|
|
|
|
BMIter iter, liter;
|
|
|
|
/* vars from original func */
|
|
|
|
UvElementMap *element_map;
|
|
|
|
UvElement *buf;
|
2018-05-02 10:32:01 +02:00
|
|
|
bool *winding = NULL;
|
2015-04-09 16:12:20 +02:00
|
|
|
BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE);
|
2012-02-06 19:25:12 +00:00
|
|
|
|
|
|
|
MLoopUV *luv;
|
2015-06-01 13:23:02 +10:00
|
|
|
int totverts, totfaces, i, totuv, j;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
|
2013-01-12 20:01:09 +00:00
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE);
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
totfaces = bm->totface;
|
2013-10-28 02:05:33 +00:00
|
|
|
totverts = bm->totvert;
|
2012-02-06 19:25:12 +00:00
|
|
|
totuv = 0;
|
|
|
|
|
|
|
|
/* generate UvElement array */
|
2013-10-28 02:05:33 +00:00
|
|
|
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
2013-01-12 20:01:09 +00:00
|
|
|
if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
2012-02-06 19:25:12 +00:00
|
|
|
totuv += efa->len;
|
2013-01-12 20:01:09 +00:00
|
|
|
}
|
2012-02-06 19:25:12 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
if (totuv == 0) {
|
2012-02-06 19:25:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-06-01 13:23:02 +10:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
|
2012-02-06 21:00:50 +00:00
|
|
|
element_map->totalUVs = totuv;
|
2012-02-10 18:09:19 +00:00
|
|
|
element_map->vert = (UvElement **)MEM_callocN(sizeof(*element_map->vert) * totverts, "UvElementVerts");
|
|
|
|
buf = element_map->buf = (UvElement *)MEM_callocN(sizeof(*element_map->buf) * totuv, "UvElement");
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2015-06-20 19:28:51 +10:00
|
|
|
if (use_winding) {
|
|
|
|
winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding");
|
|
|
|
}
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
|
2015-06-01 13:23:02 +10:00
|
|
|
|
2015-06-20 19:28:51 +10:00
|
|
|
if (use_winding) {
|
|
|
|
winding[j] = false;
|
|
|
|
}
|
2015-06-01 13:23:02 +10:00
|
|
|
|
2013-01-12 20:01:09 +00:00
|
|
|
if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
2018-05-02 10:32:01 +02:00
|
|
|
float (*tf_uv)[2] = NULL;
|
2015-06-20 19:28:51 +10:00
|
|
|
|
|
|
|
if (use_winding) {
|
2015-10-04 14:14:28 +11:00
|
|
|
tf_uv = (float (*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
|
2015-06-20 19:28:51 +10:00
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
|
2012-02-21 19:41:38 +00:00
|
|
|
buf->l = l;
|
2012-02-06 19:25:12 +00:00
|
|
|
buf->separate = 0;
|
|
|
|
buf->island = INVALID_ISLAND;
|
2018-07-16 15:37:27 +02:00
|
|
|
buf->loop_of_poly_index = i;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
buf->next = element_map->vert[BM_elem_index_get(l->v)];
|
|
|
|
element_map->vert[BM_elem_index_get(l->v)] = buf;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2015-06-20 19:28:51 +10:00
|
|
|
if (use_winding) {
|
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
|
|
|
copy_v2_v2(tf_uv[i], luv->uv);
|
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
|
2012-02-06 19:25:12 +00:00
|
|
|
buf++;
|
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
|
2015-06-20 19:28:51 +10:00
|
|
|
if (use_winding) {
|
2017-10-20 14:03:22 +11:00
|
|
|
winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
|
2015-06-20 19:28:51 +10:00
|
|
|
}
|
2012-02-06 19:25:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sort individual uvs for each vert */
|
2015-04-09 16:12:20 +02:00
|
|
|
BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) {
|
2012-02-10 18:09:19 +00:00
|
|
|
UvElement *newvlist = NULL, *vlist = element_map->vert[i];
|
2012-02-06 19:25:12 +00:00
|
|
|
UvElement *iterv, *v, *lastv, *next;
|
|
|
|
float *uv, *uv2, uvdiff[2];
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
while (vlist) {
|
|
|
|
v = vlist;
|
|
|
|
vlist = vlist->next;
|
|
|
|
v->next = newvlist;
|
|
|
|
newvlist = v;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2012-02-21 19:41:38 +00:00
|
|
|
l = v->l;
|
2013-01-12 20:01:09 +00:00
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
2012-02-06 19:25:12 +00:00
|
|
|
uv = luv->uv;
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
lastv = NULL;
|
|
|
|
iterv = vlist;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
while (iterv) {
|
|
|
|
next = iterv->next;
|
2012-02-06 19:25:12 +00:00
|
|
|
|
2012-02-21 19:41:38 +00:00
|
|
|
l = iterv->l;
|
2013-01-12 20:01:09 +00:00
|
|
|
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
2012-02-06 19:25:12 +00:00
|
|
|
uv2 = luv->uv;
|
|
|
|
|
|
|
|
sub_v2_v2v2(uvdiff, uv2, uv);
|
|
|
|
|
2015-04-09 16:12:20 +02:00
|
|
|
if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT &&
|
2015-06-20 19:28:51 +10:00
|
|
|
(!use_winding || winding[BM_elem_index_get(iterv->l->f)] == winding[BM_elem_index_get(v->l->f)]))
|
2015-04-13 22:08:51 +10:00
|
|
|
{
|
2012-02-10 18:09:19 +00:00
|
|
|
if (lastv) lastv->next = next;
|
|
|
|
else vlist = next;
|
|
|
|
iterv->next = newvlist;
|
|
|
|
newvlist = iterv;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lastv = iterv;
|
2012-02-06 19:25:12 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
iterv = next;
|
2012-02-06 19:25:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
newvlist->separate = 1;
|
|
|
|
}
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
element_map->vert[i] = newvlist;
|
2012-02-06 19:25:12 +00:00
|
|
|
}
|
|
|
|
|
2015-06-20 19:28:51 +10:00
|
|
|
if (use_winding) {
|
|
|
|
MEM_freeN(winding);
|
|
|
|
}
|
2015-06-01 13:23:02 +10:00
|
|
|
|
2012-02-05 15:55:28 +00:00
|
|
|
if (do_islands) {
|
2015-06-01 13:23:02 +10:00
|
|
|
unsigned int *map;
|
|
|
|
BMFace **stack;
|
|
|
|
int stacksize = 0;
|
|
|
|
UvElement *islandbuf;
|
|
|
|
/* island number for faces */
|
|
|
|
int *island_number = NULL;
|
|
|
|
|
|
|
|
int nislands = 0, islandbufsize = 0;
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
/* map holds the map from current vmap->buf to the new, sorted map */
|
2012-03-21 09:10:08 +00:00
|
|
|
map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap");
|
2013-10-28 02:05:33 +00:00
|
|
|
stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack");
|
2012-03-21 09:10:08 +00:00
|
|
|
islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer");
|
2015-06-01 13:23:02 +10:00
|
|
|
island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face");
|
|
|
|
copy_vn_i(island_number, totfaces, INVALID_ISLAND);
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2019-01-15 23:24:20 +11:00
|
|
|
/* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
|
|
|
|
* Now we should sort uv's in islands. */
|
2012-02-10 18:09:19 +00:00
|
|
|
for (i = 0; i < totuv; i++) {
|
2012-02-06 19:25:12 +00:00
|
|
|
if (element_map->buf[i].island == INVALID_ISLAND) {
|
|
|
|
element_map->buf[i].island = nislands;
|
2012-12-17 20:14:07 +00:00
|
|
|
stack[0] = element_map->buf[i].l->f;
|
2012-02-12 10:51:45 +00:00
|
|
|
island_number[BM_elem_index_get(stack[0])] = nislands;
|
2012-02-10 18:09:19 +00:00
|
|
|
stacksize = 1;
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
while (stacksize > 0) {
|
2012-01-17 21:08:25 +00:00
|
|
|
efa = stack[--stacksize];
|
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
|
2012-02-12 10:51:45 +00:00
|
|
|
UvElement *element, *initelement = element_map->vert[BM_elem_index_get(l->v)];
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
for (element = initelement; element; element = element->next) {
|
2012-02-05 15:55:28 +00:00
|
|
|
if (element->separate)
|
2012-01-17 21:08:25 +00:00
|
|
|
initelement = element;
|
|
|
|
|
2012-12-17 20:14:07 +00:00
|
|
|
if (element->l->f == efa) {
|
2019-01-15 23:24:20 +11:00
|
|
|
/* found the uv corresponding to our face and vertex.
|
|
|
|
* Now fill it to the buffer */
|
2012-01-17 21:08:25 +00:00
|
|
|
element->island = nislands;
|
2012-02-06 19:25:12 +00:00
|
|
|
map[element - element_map->buf] = islandbufsize;
|
2012-02-21 19:41:38 +00:00
|
|
|
islandbuf[islandbufsize].l = element->l;
|
2012-01-17 21:08:25 +00:00
|
|
|
islandbuf[islandbufsize].separate = element->separate;
|
2018-07-16 15:37:27 +02:00
|
|
|
islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index;
|
2012-01-17 21:08:25 +00:00
|
|
|
islandbuf[islandbufsize].island = nislands;
|
|
|
|
islandbufsize++;
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
for (element = initelement; element; element = element->next) {
|
2012-02-05 15:55:28 +00:00
|
|
|
if (element->separate && element != initelement)
|
2012-01-17 21:08:25 +00:00
|
|
|
break;
|
|
|
|
|
2012-12-17 20:14:07 +00:00
|
|
|
if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
|
|
|
|
stack[stacksize++] = element->l->f;
|
|
|
|
island_number[BM_elem_index_get(element->l->f)] = nislands;
|
2012-01-17 21:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nislands++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 13:23:02 +10:00
|
|
|
MEM_freeN(island_number);
|
|
|
|
|
2012-01-17 21:08:25 +00:00
|
|
|
/* remap */
|
2013-10-28 02:05:33 +00:00
|
|
|
for (i = 0; i < bm->totvert; i++) {
|
2012-01-17 21:08:25 +00:00
|
|
|
/* important since we may do selection only. Some of these may be NULL */
|
2012-02-10 18:09:19 +00:00
|
|
|
if (element_map->vert[i])
|
2012-02-06 19:25:12 +00:00
|
|
|
element_map->vert[i] = &islandbuf[map[element_map->vert[i] - element_map->buf]];
|
2012-01-17 21:08:25 +00:00
|
|
|
}
|
|
|
|
|
2012-03-21 09:10:08 +00:00
|
|
|
element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands, "UvElementMap_island_indices");
|
2012-01-17 21:08:25 +00:00
|
|
|
j = 0;
|
2012-02-10 18:09:19 +00:00
|
|
|
for (i = 0; i < totuv; i++) {
|
2012-02-06 19:25:12 +00:00
|
|
|
UvElement *element = element_map->buf[i].next;
|
2012-02-05 15:55:28 +00:00
|
|
|
if (element == NULL)
|
2012-01-17 21:08:25 +00:00
|
|
|
islandbuf[map[i]].next = NULL;
|
|
|
|
else
|
2012-02-06 19:25:12 +00:00
|
|
|
islandbuf[map[i]].next = &islandbuf[map[element - element_map->buf]];
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2012-02-05 15:55:28 +00:00
|
|
|
if (islandbuf[i].island != j) {
|
2012-01-17 21:08:25 +00:00
|
|
|
j++;
|
2012-02-06 19:25:12 +00:00
|
|
|
element_map->islandIndices[j] = i;
|
2012-01-17 21:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-06 19:25:12 +00:00
|
|
|
MEM_freeN(element_map->buf);
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2012-02-06 19:25:12 +00:00
|
|
|
element_map->buf = islandbuf;
|
|
|
|
element_map->totalIslands = nislands;
|
2012-01-17 21:08:25 +00:00
|
|
|
MEM_freeN(stack);
|
|
|
|
MEM_freeN(map);
|
|
|
|
}
|
2015-04-09 16:12:20 +02:00
|
|
|
|
|
|
|
BLI_buffer_free(&tf_uv_buf);
|
2012-01-17 21:08:25 +00:00
|
|
|
|
2012-02-06 19:25:12 +00:00
|
|
|
return element_map;
|
2012-01-17 21:08:25 +00:00
|
|
|
}
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
void BM_uv_vert_map_free(UvVertMap *vmap)
|
2009-07-21 08:39:58 +00:00
|
|
|
{
|
|
|
|
if (vmap) {
|
|
|
|
if (vmap->vert) MEM_freeN(vmap->vert);
|
|
|
|
if (vmap->buf) MEM_freeN(vmap->buf);
|
|
|
|
MEM_freeN(vmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
void BM_uv_element_map_free(UvElementMap *element_map)
|
2012-01-17 21:08:25 +00:00
|
|
|
{
|
2012-02-06 19:25:12 +00:00
|
|
|
if (element_map) {
|
|
|
|
if (element_map->vert) MEM_freeN(element_map->vert);
|
|
|
|
if (element_map->buf) MEM_freeN(element_map->buf);
|
|
|
|
if (element_map->islandIndices) MEM_freeN(element_map->islandIndices);
|
|
|
|
MEM_freeN(element_map);
|
2012-01-17 21:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-21 08:39:58 +00:00
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
UvElement *BM_uv_element_get(UvElementMap *map, BMFace *efa, BMLoop *l)
|
2012-10-22 07:29:38 +00:00
|
|
|
{
|
2018-03-16 20:46:14 +11:00
|
|
|
for (UvElement *element = map->vert[BM_elem_index_get(l->v)];
|
|
|
|
element;
|
|
|
|
element = element->next)
|
|
|
|
{
|
|
|
|
if (element->l->f == efa) {
|
2012-10-22 07:29:38 +00:00
|
|
|
return element;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
|
|
|
}
|
2012-10-22 07:29:38 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Data Layer Checks
|
|
|
|
* \{ */
|
|
|
|
|
2019-04-10 00:06:53 +10:00
|
|
|
/**
|
|
|
|
* last_sel, use em->act_face otherwise get the last selected face in the editselections
|
|
|
|
* at the moment, last_sel is mainly useful for making sure the space image doesn't flicker.
|
|
|
|
*/
|
2017-05-25 15:11:00 +10:00
|
|
|
BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
|
2009-07-21 08:39:58 +00:00
|
|
|
{
|
|
|
|
BMFace *efa = NULL;
|
2018-03-16 20:46:14 +11:00
|
|
|
|
2018-03-12 14:51:45 +11:00
|
|
|
if (!EDBM_uv_check(em)) {
|
2009-07-21 08:39:58 +00:00
|
|
|
return NULL;
|
2018-03-12 14:51:45 +11:00
|
|
|
}
|
|
|
|
|
2013-06-24 04:51:56 +00:00
|
|
|
efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
|
2012-08-31 15:01:40 +00:00
|
|
|
|
2009-07-21 08:39:58 +00:00
|
|
|
if (efa) {
|
2017-05-25 00:34:14 +10:00
|
|
|
return efa;
|
2009-07-21 08:39:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* can we edit UV's for this mesh?*/
|
2017-05-25 15:11:00 +10:00
|
|
|
bool EDBM_uv_check(BMEditMesh *em)
|
2009-07-21 08:39:58 +00:00
|
|
|
{
|
|
|
|
/* some of these checks could be a touch overkill */
|
2017-05-25 15:11:00 +10:00
|
|
|
return em && em->bm->totface &&
|
2012-03-26 02:56:48 +00:00
|
|
|
CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV);
|
2009-07-21 11:48:58 +00:00
|
|
|
}
|
2009-08-28 10:17:31 +00:00
|
|
|
|
2013-04-05 14:58:30 +00:00
|
|
|
bool EDBM_vert_color_check(BMEditMesh *em)
|
2009-08-28 10:17:31 +00:00
|
|
|
{
|
|
|
|
/* some of these checks could be a touch overkill */
|
|
|
|
return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_MLOOPCOL);
|
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Mirror Cache API
|
|
|
|
* \{ */
|
|
|
|
|
2011-12-20 22:01:11 +00:00
|
|
|
static BMVert *cache_mirr_intptr_as_bmvert(intptr_t *index_lookup, int index)
|
|
|
|
{
|
2012-02-10 18:09:19 +00:00
|
|
|
intptr_t eve_i = index_lookup[index];
|
2011-12-20 22:01:11 +00:00
|
|
|
return (eve_i == -1) ? NULL : (BMVert *)eve_i;
|
|
|
|
}
|
|
|
|
|
2012-03-27 04:46:52 +00:00
|
|
|
/**
|
2015-05-27 00:00:31 +10:00
|
|
|
* Mirror editing API, usage:
|
2012-03-27 04:46:52 +00:00
|
|
|
*
|
2015-05-27 00:00:31 +10:00
|
|
|
* \code{.c}
|
|
|
|
* EDBM_verts_mirror_cache_begin(em, ...);
|
2012-03-27 04:46:52 +00:00
|
|
|
*
|
2015-05-27 00:00:31 +10:00
|
|
|
* BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
|
|
|
|
* v_mirror = EDBM_verts_mirror_get(em, v);
|
|
|
|
* e_mirror = EDBM_verts_mirror_get_edge(em, e);
|
|
|
|
* f_mirror = EDBM_verts_mirror_get_face(em, f);
|
|
|
|
* }
|
2012-03-27 04:46:52 +00:00
|
|
|
*
|
2015-05-27 00:00:31 +10:00
|
|
|
* EDBM_verts_mirror_cache_end(em);
|
|
|
|
* \endcode
|
2012-03-27 04:46:52 +00:00
|
|
|
*/
|
|
|
|
|
2011-10-26 07:41:56 +00:00
|
|
|
/* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a
|
|
|
|
* preference */
|
|
|
|
#define BM_SEARCH_MAXDIST_MIRR 0.00002f
|
2011-10-28 06:23:12 +00:00
|
|
|
#define BM_CD_LAYER_ID "__mirror_index"
|
2013-06-19 18:22:00 +00:00
|
|
|
/**
|
2018-12-12 12:50:58 +11:00
|
|
|
* \param em: Editmesh.
|
|
|
|
* \param use_self: Allow a vertex to point to its self (middle verts).
|
|
|
|
* \param use_select: Restrict to selected verts.
|
|
|
|
* \param use_topology: Use topology mirror.
|
|
|
|
* \param maxdist: Distance for close point test.
|
|
|
|
* \param r_index: Optional array to write into, as an alternative to a customdata layer (length of total verts).
|
2013-06-19 18:22:00 +00:00
|
|
|
*/
|
2018-03-16 20:46:14 +11:00
|
|
|
void EDBM_verts_mirror_cache_begin_ex(
|
|
|
|
BMEditMesh *em, const int axis, const bool use_self, const bool use_select,
|
|
|
|
/* extra args */
|
|
|
|
const bool use_topology, float maxdist, int *r_index)
|
2010-03-11 05:30:01 +00:00
|
|
|
{
|
2012-09-10 03:42:29 +00:00
|
|
|
Mesh *me = (Mesh *)em->ob->data;
|
2011-12-21 00:09:44 +00:00
|
|
|
BMesh *bm = em->bm;
|
2010-03-11 05:30:01 +00:00
|
|
|
BMIter iter;
|
|
|
|
BMVert *v;
|
2018-05-02 10:32:01 +02:00
|
|
|
int cd_vmirr_offset = 0;
|
2013-06-19 18:22:00 +00:00
|
|
|
int i;
|
2015-05-21 13:01:44 +10:00
|
|
|
const float maxdist_sq = SQUARE(maxdist);
|
2011-10-25 16:17:26 +00:00
|
|
|
|
2011-12-20 22:01:11 +00:00
|
|
|
/* one or the other is used depending if topo is enabled */
|
2019-03-20 00:46:33 +11:00
|
|
|
KDTree_3d *tree = NULL;
|
2012-02-10 18:09:19 +00:00
|
|
|
MirrTopoStore_t mesh_topo_store = {NULL, -1, -1, -1};
|
2011-12-20 22:01:11 +00:00
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
BM_mesh_elem_table_ensure(bm, BM_VERT);
|
2010-03-11 05:30:01 +00:00
|
|
|
|
2013-06-19 18:22:00 +00:00
|
|
|
if (r_index == NULL) {
|
|
|
|
const char *layer_id = BM_CD_LAYER_ID;
|
|
|
|
em->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
|
|
|
|
if (em->mirror_cdlayer == -1) {
|
|
|
|
BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT, layer_id);
|
|
|
|
em->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
|
|
|
|
}
|
2010-03-11 05:30:01 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
cd_vmirr_offset = CustomData_get_n_offset(
|
|
|
|
&bm->vdata, CD_PROP_INT,
|
|
|
|
em->mirror_cdlayer - CustomData_get_layer_index(&bm->vdata, CD_PROP_INT));
|
2011-10-28 06:23:12 +00:00
|
|
|
|
2013-06-19 18:22:00 +00:00
|
|
|
bm->vdata.layers[em->mirror_cdlayer].flag |= CD_FLAG_TEMPORARY;
|
|
|
|
}
|
2010-03-11 05:30:01 +00:00
|
|
|
|
2012-02-12 10:51:45 +00:00
|
|
|
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
2011-10-26 10:04:10 +00:00
|
|
|
|
2013-06-28 17:13:09 +00:00
|
|
|
if (use_topology) {
|
2018-06-21 19:05:10 +02:00
|
|
|
ED_mesh_mirrtopo_init(me, NULL, &mesh_topo_store, true);
|
2011-12-20 22:01:11 +00:00
|
|
|
}
|
|
|
|
else {
|
2019-03-20 00:46:33 +11:00
|
|
|
tree = BLI_kdtree_3d_new(bm->totvert);
|
2015-05-21 13:01:44 +10:00
|
|
|
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
2019-03-20 00:46:33 +11:00
|
|
|
BLI_kdtree_3d_insert(tree, i, v->co);
|
2015-05-21 13:01:44 +10:00
|
|
|
}
|
2019-03-20 00:46:33 +11:00
|
|
|
BLI_kdtree_3d_balance(tree);
|
2011-12-20 22:01:11 +00:00
|
|
|
}
|
|
|
|
|
2019-04-02 17:54:04 +11:00
|
|
|
#define VERT_INTPTR(_v, _i) (r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset))
|
2013-06-19 18:22:00 +00:00
|
|
|
|
|
|
|
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
|
|
|
BLI_assert(BM_elem_index_get(v) == i);
|
2011-10-26 07:41:56 +00:00
|
|
|
|
2011-12-21 00:09:44 +00:00
|
|
|
/* temporary for testing, check for selection */
|
2012-02-12 10:51:45 +00:00
|
|
|
if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
|
2011-12-21 00:09:44 +00:00
|
|
|
/* do nothing */
|
2011-10-26 07:41:56 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-06-05 05:58:51 +00:00
|
|
|
BMVert *v_mirr;
|
2013-06-19 18:22:00 +00:00
|
|
|
int *idx = VERT_INTPTR(v, i);
|
2011-12-21 00:09:44 +00:00
|
|
|
|
2013-06-28 17:13:09 +00:00
|
|
|
if (use_topology) {
|
2013-06-19 18:22:00 +00:00
|
|
|
v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
|
2011-12-21 00:09:44 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-05-21 13:01:44 +10:00
|
|
|
int i_mirr;
|
2013-06-19 21:23:00 +00:00
|
|
|
float co[3];
|
|
|
|
copy_v3_v3(co, v->co);
|
|
|
|
co[axis] *= -1.0f;
|
2015-05-21 13:01:44 +10:00
|
|
|
|
|
|
|
v_mirr = NULL;
|
2019-03-20 00:46:33 +11:00
|
|
|
i_mirr = BLI_kdtree_3d_find_nearest(tree, co, NULL);
|
2015-05-21 13:01:44 +10:00
|
|
|
if (i_mirr != -1) {
|
|
|
|
BMVert *v_test = BM_vert_at_index(bm, i_mirr);
|
|
|
|
if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
|
|
|
|
v_mirr = v_test;
|
|
|
|
}
|
|
|
|
}
|
2011-12-21 00:09:44 +00:00
|
|
|
}
|
|
|
|
|
2013-06-05 05:58:51 +00:00
|
|
|
if (v_mirr && (use_self || (v_mirr != v))) {
|
2013-06-19 18:22:00 +00:00
|
|
|
const int i_mirr = BM_elem_index_get(v_mirr);
|
|
|
|
*idx = i_mirr;
|
|
|
|
idx = VERT_INTPTR(v_mirr, i_mirr);
|
|
|
|
*idx = i;
|
2011-12-21 00:09:44 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
*idx = -1;
|
|
|
|
}
|
2011-10-26 07:41:56 +00:00
|
|
|
}
|
2011-12-21 00:09:44 +00:00
|
|
|
|
2010-03-11 05:30:01 +00:00
|
|
|
}
|
|
|
|
|
2013-06-19 18:22:00 +00:00
|
|
|
#undef VERT_INTPTR
|
2011-12-20 22:01:11 +00:00
|
|
|
|
2013-06-28 17:13:09 +00:00
|
|
|
if (use_topology) {
|
2011-12-20 22:01:11 +00:00
|
|
|
ED_mesh_mirrtopo_free(&mesh_topo_store);
|
|
|
|
}
|
|
|
|
else {
|
2019-03-20 00:46:33 +11:00
|
|
|
BLI_kdtree_3d_free(tree);
|
2011-12-20 22:01:11 +00:00
|
|
|
}
|
2010-03-11 05:30:01 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
void EDBM_verts_mirror_cache_begin(
|
|
|
|
BMEditMesh *em, const int axis,
|
|
|
|
const bool use_self, const bool use_select,
|
|
|
|
const bool use_topology)
|
2013-06-19 18:22:00 +00:00
|
|
|
{
|
2018-03-16 20:46:14 +11:00
|
|
|
EDBM_verts_mirror_cache_begin_ex(
|
|
|
|
em, axis,
|
|
|
|
use_self, use_select,
|
|
|
|
/* extra args */
|
|
|
|
use_topology, BM_SEARCH_MAXDIST_MIRR, NULL);
|
2013-06-19 18:22:00 +00:00
|
|
|
}
|
|
|
|
|
2012-03-27 04:46:52 +00:00
|
|
|
BMVert *EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
|
2010-03-11 05:30:01 +00:00
|
|
|
{
|
2014-04-27 00:22:49 +10:00
|
|
|
const int *mirr = CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer);
|
2011-10-28 06:23:12 +00:00
|
|
|
|
|
|
|
BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
|
|
|
|
|
2012-02-10 18:09:19 +00:00
|
|
|
if (mirr && *mirr >= 0 && *mirr < em->bm->totvert) {
|
2013-10-28 02:05:33 +00:00
|
|
|
if (!em->bm->vtable) {
|
2010-03-11 05:30:01 +00:00
|
|
|
printf("err: should only be called between "
|
2012-03-27 04:46:52 +00:00
|
|
|
"EDBM_verts_mirror_cache_begin and EDBM_verts_mirror_cache_end");
|
2010-03-11 05:30:01 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
return em->bm->vtable[*mirr];
|
2010-03-11 05:30:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-01 21:56:59 +00:00
|
|
|
BMEdge *EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
|
|
|
|
{
|
|
|
|
BMVert *v1_mirr = EDBM_verts_mirror_get(em, e->v1);
|
|
|
|
if (v1_mirr) {
|
|
|
|
BMVert *v2_mirr = EDBM_verts_mirror_get(em, e->v2);
|
|
|
|
if (v2_mirr) {
|
|
|
|
return BM_edge_exists(v1_mirr, v2_mirr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BMFace *EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
|
|
|
|
{
|
|
|
|
BMVert **v_mirr_arr = BLI_array_alloca(v_mirr_arr, f->len);
|
|
|
|
|
|
|
|
BMLoop *l_iter, *l_first;
|
|
|
|
unsigned int i = 0;
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
do {
|
|
|
|
if ((v_mirr_arr[i++] = EDBM_verts_mirror_get(em, l_iter->v)) == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
2016-11-14 04:10:47 +11:00
|
|
|
return BM_face_exists(v_mirr_arr, f->len);
|
2013-07-01 21:56:59 +00:00
|
|
|
}
|
|
|
|
|
2012-03-27 04:46:52 +00:00
|
|
|
void EDBM_verts_mirror_cache_clear(BMEditMesh *em, BMVert *v)
|
2011-11-01 05:43:35 +00:00
|
|
|
{
|
|
|
|
int *mirr = CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer);
|
|
|
|
|
|
|
|
BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
|
|
|
|
|
|
|
|
if (mirr) {
|
2012-02-10 18:09:19 +00:00
|
|
|
*mirr = -1;
|
2011-11-01 05:43:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-27 04:46:52 +00:00
|
|
|
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
|
2010-03-11 05:30:01 +00:00
|
|
|
{
|
2012-02-10 18:09:19 +00:00
|
|
|
em->mirror_cdlayer = -1;
|
2010-03-11 05:30:01 +00:00
|
|
|
}
|
2011-10-26 07:41:56 +00:00
|
|
|
|
2012-03-27 04:46:52 +00:00
|
|
|
void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
|
2011-10-26 07:41:56 +00:00
|
|
|
{
|
|
|
|
BMIter iter;
|
|
|
|
BMVert *v;
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
BLI_assert((em->bm->vtable != NULL) && ((em->bm->elem_table_dirty & BM_VERT) == 0));
|
2011-10-26 07:41:56 +00:00
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
|
2012-02-12 10:51:45 +00:00
|
|
|
if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
|
2012-03-27 04:46:52 +00:00
|
|
|
BMVert *mirr = EDBM_verts_mirror_get(em, v);
|
2011-10-26 07:41:56 +00:00
|
|
|
if (mirr) {
|
2012-02-12 10:51:45 +00:00
|
|
|
if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
|
2011-10-26 07:41:56 +00:00
|
|
|
copy_v3_v3(mirr->co, v->co);
|
|
|
|
mirr->co[0] *= -1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-03-23 23:41:33 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Hide/Reveal API
|
|
|
|
* \{ */
|
2012-03-23 23:41:33 +00:00
|
|
|
|
|
|
|
/* swap is 0 or 1, if 1 it hides not selected */
|
2019-02-14 17:50:41 +11:00
|
|
|
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
|
2012-03-23 23:41:33 +00:00
|
|
|
{
|
|
|
|
BMIter iter;
|
|
|
|
BMElem *ele;
|
|
|
|
int itermode;
|
2013-04-05 14:58:30 +00:00
|
|
|
char hflag_swap = swap ? BM_ELEM_SELECT : 0;
|
2019-02-14 17:50:41 +11:00
|
|
|
bool changed = true;
|
2012-03-23 23:41:33 +00:00
|
|
|
|
|
|
|
if (em->selectmode & SCE_SELECT_VERTEX)
|
|
|
|
itermode = BM_VERTS_OF_MESH;
|
|
|
|
else if (em->selectmode & SCE_SELECT_EDGE)
|
|
|
|
itermode = BM_EDGES_OF_MESH;
|
|
|
|
else
|
|
|
|
itermode = BM_FACES_OF_MESH;
|
|
|
|
|
2012-04-19 12:45:56 +00:00
|
|
|
BM_ITER_MESH (ele, &iter, em->bm, itermode) {
|
2019-02-14 17:50:41 +11:00
|
|
|
if (!BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
|
|
|
|
if (BM_elem_flag_test(ele, BM_ELEM_SELECT) ^ hflag_swap) {
|
|
|
|
BM_elem_hide_set(em->bm, ele, true);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
2012-03-23 23:41:33 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 17:50:41 +11:00
|
|
|
if (changed) {
|
|
|
|
EDBM_selectmode_flush(em);
|
|
|
|
}
|
|
|
|
return changed;
|
2012-03-23 23:41:33 +00:00
|
|
|
|
|
|
|
/* original hide flushing comment (OUTDATED):
|
2019-01-15 23:24:20 +11:00
|
|
|
* hide happens on least dominant select mode, and flushes up, not down!
|
|
|
|
* (helps preventing errors in subsurf) */
|
2012-03-23 23:41:33 +00:00
|
|
|
/* - vertex hidden, always means edge is hidden too
|
|
|
|
* - edge hidden, always means face is hidden too
|
|
|
|
* - face hidden, only set face hide
|
|
|
|
* - then only flush back down what's absolute hidden
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2019-02-14 17:50:41 +11:00
|
|
|
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
|
2012-03-23 23:41:33 +00:00
|
|
|
{
|
2018-03-16 20:46:14 +11:00
|
|
|
const char iter_types[3] = {
|
|
|
|
BM_VERTS_OF_MESH,
|
|
|
|
BM_EDGES_OF_MESH,
|
|
|
|
BM_FACES_OF_MESH,
|
|
|
|
};
|
2012-03-23 23:41:33 +00:00
|
|
|
|
2014-04-27 07:50:08 +10:00
|
|
|
const bool sels[3] = {
|
2018-03-16 20:46:14 +11:00
|
|
|
(em->selectmode & SCE_SELECT_VERTEX) != 0,
|
|
|
|
(em->selectmode & SCE_SELECT_EDGE) != 0,
|
|
|
|
(em->selectmode & SCE_SELECT_FACE) != 0,
|
2014-04-27 07:50:08 +10:00
|
|
|
};
|
2012-03-23 23:41:33 +00:00
|
|
|
int i;
|
2019-02-14 17:50:41 +11:00
|
|
|
bool changed = false;
|
2012-03-23 23:41:33 +00:00
|
|
|
|
|
|
|
/* Use tag flag to remember what was hidden before all is revealed.
|
|
|
|
* BM_ELEM_HIDDEN --> BM_ELEM_TAG */
|
|
|
|
for (i = 0; i < 3; i++) {
|
2012-12-21 06:06:17 +00:00
|
|
|
BMIter iter;
|
|
|
|
BMElem *ele;
|
|
|
|
|
2012-04-19 12:45:56 +00:00
|
|
|
BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
|
2019-02-14 17:50:41 +11:00
|
|
|
if (BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
|
|
|
|
BM_elem_flag_enable(ele, BM_ELEM_TAG);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BM_elem_flag_disable(ele, BM_ELEM_TAG);
|
|
|
|
}
|
2012-03-23 23:41:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 17:50:41 +11:00
|
|
|
if (!changed) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-23 23:41:33 +00:00
|
|
|
/* Reveal everything */
|
|
|
|
EDBM_flag_disable_all(em, BM_ELEM_HIDDEN);
|
|
|
|
|
|
|
|
/* Select relevant just-revealed elements */
|
|
|
|
for (i = 0; i < 3; i++) {
|
2012-12-21 06:06:17 +00:00
|
|
|
BMIter iter;
|
|
|
|
BMElem *ele;
|
|
|
|
|
2012-03-23 23:41:33 +00:00
|
|
|
if (!sels[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-04-19 12:45:56 +00:00
|
|
|
BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
|
2012-03-23 23:41:33 +00:00
|
|
|
if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
|
2017-11-20 02:28:07 +11:00
|
|
|
BM_elem_select_set(em->bm, ele, select);
|
2012-03-23 23:41:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EDBM_selectmode_flush(em);
|
2012-03-30 01:11:11 +00:00
|
|
|
|
|
|
|
/* hidden faces can have invalid normals */
|
|
|
|
EDBM_mesh_normals_update(em);
|
2019-02-14 17:50:41 +11:00
|
|
|
|
|
|
|
return true;
|
2012-03-23 23:41:33 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Update API
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
void EDBM_mesh_normals_update(BMEditMesh *em)
|
|
|
|
{
|
|
|
|
BM_mesh_normals_update(em->bm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EDBM_stats_update(BMEditMesh *em)
|
|
|
|
{
|
|
|
|
const char iter_types[3] = {
|
|
|
|
BM_VERTS_OF_MESH,
|
|
|
|
BM_EDGES_OF_MESH,
|
|
|
|
BM_FACES_OF_MESH,
|
|
|
|
};
|
|
|
|
|
|
|
|
BMIter iter;
|
|
|
|
BMElem *ele;
|
|
|
|
int *tots[3];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tots[0] = &em->bm->totvertsel;
|
|
|
|
tots[1] = &em->bm->totedgesel;
|
|
|
|
tots[2] = &em->bm->totfacesel;
|
|
|
|
|
|
|
|
em->bm->totvertsel = em->bm->totedgesel = em->bm->totfacesel = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
ele = BM_iter_new(&iter, em->bm, iter_types[i], NULL);
|
|
|
|
for ( ; ele; ele = BM_iter_step(&iter)) {
|
|
|
|
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
|
|
|
|
(*tots[i])++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-27 00:01:35 +00:00
|
|
|
/* so many tools call these that we better make it a generic function.
|
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive)
|
2012-03-27 00:01:35 +00:00
|
|
|
{
|
|
|
|
Object *ob = em->ob;
|
|
|
|
/* order of calling isn't important */
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
|
2012-12-12 15:22:54 +00:00
|
|
|
WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data);
|
2012-03-27 00:01:35 +00:00
|
|
|
|
|
|
|
if (do_tessface) {
|
2013-04-16 05:59:48 +00:00
|
|
|
BKE_editmesh_tessface_calc(em);
|
2012-03-27 00:01:35 +00:00
|
|
|
}
|
2012-12-12 06:53:39 +00:00
|
|
|
|
|
|
|
if (is_destructive) {
|
2013-10-28 02:05:33 +00:00
|
|
|
/* TODO. we may be able to remove this now! - Campbell */
|
|
|
|
// BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
|
2012-12-12 06:53:39 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* in debug mode double check we didn't need to recalculate */
|
2013-10-28 02:05:33 +00:00
|
|
|
BLI_assert(BM_mesh_elem_table_check(em->bm) == true);
|
2012-12-12 06:53:39 +00:00
|
|
|
}
|
2018-05-25 22:24:24 +05:30
|
|
|
if (em->bm->spacearr_dirty & BM_SPACEARR_BMO_SET) {
|
|
|
|
BM_lnorspace_invalidate(em->bm, false);
|
|
|
|
em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET;
|
|
|
|
}
|
2014-02-28 20:11:41 +11:00
|
|
|
/* don't keep stale derivedMesh data around, see: [#38872] */
|
|
|
|
BKE_editmesh_free_derivedmesh(em);
|
2014-08-25 16:48:47 +10:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
BMEditSelection *ese;
|
|
|
|
for (ese = em->bm->selected.first; ese; ese = ese->next) {
|
|
|
|
BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2012-03-27 00:01:35 +00:00
|
|
|
}
|
2013-04-01 10:18:01 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Operator Helpers
|
|
|
|
* \{ */
|
|
|
|
|
2013-04-01 10:18:01 +00:00
|
|
|
/* poll call for mesh operators requiring a view3d context */
|
2018-07-02 11:47:00 +02:00
|
|
|
bool EDBM_view3d_poll(bContext *C)
|
2013-04-01 10:18:01 +00:00
|
|
|
{
|
2018-03-16 20:46:14 +11:00
|
|
|
if (ED_operator_editmesh(C) && ED_operator_view3d_active(C)) {
|
2013-04-01 10:18:01 +00:00
|
|
|
return 1;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2013-04-01 10:18:01 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-04-16 05:23:34 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name BMesh Element API
|
|
|
|
* \{ */
|
|
|
|
|
2015-12-28 21:26:02 +11:00
|
|
|
BMElem *EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa)
|
|
|
|
{
|
|
|
|
BMElem *ele = NULL;
|
|
|
|
|
|
|
|
if ((em->selectmode & SCE_SELECT_VERTEX) && eve) {
|
|
|
|
ele = (BMElem *)eve;
|
|
|
|
}
|
|
|
|
else if ((em->selectmode & SCE_SELECT_EDGE) && eed) {
|
|
|
|
ele = (BMElem *)eed;
|
|
|
|
}
|
|
|
|
else if ((em->selectmode & SCE_SELECT_FACE) && efa) {
|
|
|
|
ele = (BMElem *)efa;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ele;
|
|
|
|
}
|
|
|
|
|
2015-12-27 18:03:20 +11:00
|
|
|
/**
|
|
|
|
* Used when we want to store a single index for any vert/edge/face.
|
|
|
|
*
|
|
|
|
* Intended for use with operators.
|
|
|
|
*/
|
|
|
|
int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele)
|
|
|
|
{
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
int index = BM_elem_index_get(ele);
|
|
|
|
|
|
|
|
if (ele->head.htype == BM_VERT) {
|
|
|
|
BLI_assert(!(bm->elem_index_dirty & BM_VERT));
|
|
|
|
}
|
|
|
|
else if (ele->head.htype == BM_EDGE) {
|
|
|
|
BLI_assert(!(bm->elem_index_dirty & BM_EDGE));
|
|
|
|
index += bm->totvert;
|
|
|
|
}
|
|
|
|
else if (ele->head.htype == BM_FACE) {
|
|
|
|
BLI_assert(!(bm->elem_index_dirty & BM_FACE));
|
|
|
|
index += bm->totvert + bm->totedge;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BLI_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
BMElem *EDBM_elem_from_index_any(BMEditMesh *em, int index)
|
|
|
|
{
|
|
|
|
BMesh *bm = em->bm;
|
|
|
|
|
|
|
|
if (index < bm->totvert) {
|
|
|
|
return (BMElem *)BM_vert_at_index_find_or_table(bm, index);
|
|
|
|
}
|
|
|
|
index -= bm->totvert;
|
|
|
|
if (index < bm->totedge) {
|
|
|
|
return (BMElem *)BM_edge_at_index_find_or_table(bm, index);
|
|
|
|
}
|
|
|
|
index -= bm->totedge;
|
|
|
|
if (index < bm->totface) {
|
|
|
|
return (BMElem *)BM_face_at_index_find_or_table(bm, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-04-18 01:20:04 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/** \} */
|
2013-04-16 05:23:34 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name BMesh BVH API
|
|
|
|
* \{ */
|
2013-04-16 05:23:34 +00:00
|
|
|
|
|
|
|
static BMFace *edge_ray_cast(struct BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, BMEdge *e)
|
|
|
|
{
|
2013-08-30 16:34:44 +00:00
|
|
|
BMFace *f = BKE_bmbvh_ray_cast(tree, co, dir, 0.0f, NULL, r_hitout, NULL);
|
2013-04-16 05:23:34 +00:00
|
|
|
|
2018-03-16 20:46:14 +11:00
|
|
|
if (f && BM_edge_in_face(e, f)) {
|
2013-04-16 05:23:34 +00:00
|
|
|
return NULL;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2013-04-16 05:23:34 +00:00
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scale_point(float c1[3], const float p[3], const float s)
|
|
|
|
{
|
|
|
|
sub_v3_v3(c1, p);
|
|
|
|
mul_v3_fl(c1, s);
|
|
|
|
add_v3_v3(c1, p);
|
|
|
|
}
|
|
|
|
|
2018-01-18 15:58:02 +01:00
|
|
|
bool BMBVH_EdgeVisible(struct BMBVHTree *tree, BMEdge *e,
|
2018-04-06 12:07:27 +02:00
|
|
|
struct Depsgraph *depsgraph,
|
2018-01-18 15:58:02 +01:00
|
|
|
ARegion *ar, View3D *v3d, Object *obedit)
|
2013-04-16 05:23:34 +00:00
|
|
|
{
|
|
|
|
BMFace *f;
|
|
|
|
float co1[3], co2[3], co3[3], dir1[3], dir2[3], dir3[3];
|
|
|
|
float origin[3], invmat[4][4];
|
|
|
|
float epsilon = 0.01f;
|
|
|
|
float end[3];
|
2018-03-16 20:46:14 +11:00
|
|
|
const float mval_f[2] = {
|
|
|
|
ar->winx / 2.0f,
|
|
|
|
ar->winy / 2.0f,
|
|
|
|
};
|
2013-04-16 05:23:34 +00:00
|
|
|
|
2018-09-20 12:04:18 +10:00
|
|
|
ED_view3d_win_to_segment_clipped(depsgraph, ar, v3d, mval_f, origin, end, false);
|
2013-04-16 05:23:34 +00:00
|
|
|
|
|
|
|
invert_m4_m4(invmat, obedit->obmat);
|
|
|
|
mul_m4_v3(invmat, origin);
|
|
|
|
|
|
|
|
copy_v3_v3(co1, e->v1->co);
|
|
|
|
mid_v3_v3v3(co2, e->v1->co, e->v2->co);
|
|
|
|
copy_v3_v3(co3, e->v2->co);
|
|
|
|
|
|
|
|
scale_point(co1, co2, 0.99);
|
|
|
|
scale_point(co3, co2, 0.99);
|
|
|
|
|
|
|
|
/* ok, idea is to generate rays going from the camera origin to the
|
|
|
|
* three points on the edge (v1, mid, v2)*/
|
|
|
|
sub_v3_v3v3(dir1, origin, co1);
|
|
|
|
sub_v3_v3v3(dir2, origin, co2);
|
|
|
|
sub_v3_v3v3(dir3, origin, co3);
|
|
|
|
|
2016-07-08 10:14:49 +10:00
|
|
|
normalize_v3_length(dir1, epsilon);
|
|
|
|
normalize_v3_length(dir2, epsilon);
|
|
|
|
normalize_v3_length(dir3, epsilon);
|
2013-04-16 05:23:34 +00:00
|
|
|
|
|
|
|
/* offset coordinates slightly along view vectors, to avoid
|
|
|
|
* hitting the faces that own the edge.*/
|
|
|
|
add_v3_v3v3(co1, co1, dir1);
|
|
|
|
add_v3_v3v3(co2, co2, dir2);
|
|
|
|
add_v3_v3v3(co3, co3, dir3);
|
|
|
|
|
|
|
|
normalize_v3(dir1);
|
|
|
|
normalize_v3(dir2);
|
|
|
|
normalize_v3(dir3);
|
|
|
|
|
|
|
|
/* do three samplings: left, middle, right */
|
|
|
|
f = edge_ray_cast(tree, co1, dir1, NULL, e);
|
2018-03-16 20:46:14 +11:00
|
|
|
if (f && !edge_ray_cast(tree, co2, dir2, NULL, e)) {
|
2013-04-16 05:23:34 +00:00
|
|
|
return true;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
|
|
|
else if (f && !edge_ray_cast(tree, co3, dir3, NULL, e)) {
|
2013-04-16 05:23:34 +00:00
|
|
|
return true;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
|
|
|
else if (!f) {
|
2013-04-16 05:23:34 +00:00
|
|
|
return true;
|
2018-03-16 20:46:14 +11:00
|
|
|
}
|
2013-04-16 05:23:34 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-16 20:46:14 +11:00
|
|
|
|
|
|
|
/** \} */
|