2011-02-23 10:52:22 +00:00
|
|
|
/*
|
2008-04-25 18:22:20 +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,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2008-04-25 18:22:20 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
2011-02-27 20:40:57 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup bke
|
2011-02-27 20:40:57 +00:00
|
|
|
*/
|
|
|
|
|
2008-04-25 18:22:20 +00:00
|
|
|
#include <float.h>
|
2008-04-27 19:29:40 +00:00
|
|
|
#include <math.h>
|
2008-06-17 19:00:21 +00:00
|
|
|
#include <memory.h>
|
2008-04-27 19:29:40 +00:00
|
|
|
#include <stdio.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <string.h>
|
2008-05-01 01:00:01 +00:00
|
|
|
#include <time.h>
|
2008-04-25 18:22:20 +00:00
|
|
|
|
2008-08-14 03:05:13 +00:00
|
|
|
#include "DNA_mesh_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "DNA_modifier_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
2008-04-25 18:22:20 +00:00
|
|
|
|
2011-01-07 18:36:47 +00:00
|
|
|
#include "BLI_math.h"
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#include "BLI_math_solvers.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_task.h"
|
|
|
|
#include "BLI_utildefines.h"
|
2011-01-07 18:36:47 +00:00
|
|
|
|
2008-04-25 18:22:20 +00:00
|
|
|
#include "BKE_DerivedMesh.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_cdderivedmesh.h"
|
|
|
|
#include "BKE_context.h"
|
2008-11-03 23:17:36 +00:00
|
|
|
#include "BKE_lattice.h"
|
2020-02-10 12:58:59 +01:00
|
|
|
#include "BKE_lib_id.h"
|
2018-05-09 12:51:03 +02:00
|
|
|
#include "BKE_modifier.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_shrinkwrap.h"
|
2011-01-07 19:18:31 +00:00
|
|
|
|
2008-04-25 18:22:20 +00:00
|
|
|
#include "BKE_deform.h"
|
2015-09-10 18:27:50 +10:00
|
|
|
#include "BKE_editmesh.h"
|
2014-07-28 16:13:47 +06:00
|
|
|
#include "BKE_mesh.h" /* for OMP limits. */
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#include "BKE_mesh_runtime.h"
|
2020-06-15 23:17:42 +10:00
|
|
|
#include "BKE_mesh_wrapper.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_subsurf.h"
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
|
2018-12-07 15:45:53 +01:00
|
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#include "MEM_guardedalloc.h"
|
2008-04-25 18:22:20 +00:00
|
|
|
|
2014-02-03 18:55:59 +11:00
|
|
|
#include "BLI_strict_flags.h"
|
|
|
|
|
2012-11-09 04:01:19 +00:00
|
|
|
/* for timing... */
|
|
|
|
#if 0
|
2016-05-30 17:30:06 +02:00
|
|
|
# include "PIL_time_utildefines.h"
|
2008-05-01 01:00:01 +00:00
|
|
|
#else
|
2012-11-09 04:01:19 +00:00
|
|
|
# define TIMEIT_BENCH(expr, id) (expr)
|
2008-05-01 01:00:01 +00:00
|
|
|
#endif
|
|
|
|
|
2012-11-09 04:01:19 +00:00
|
|
|
/* Util macros */
|
|
|
|
#define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n"))
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
typedef struct ShrinkwrapCalcData {
|
2019-05-01 11:09:22 +10:00
|
|
|
ShrinkwrapModifierData *smd; /* shrinkwrap modifier data */
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2019-05-01 11:09:22 +10:00
|
|
|
struct Object *ob; /* object we are applying shrinkwrap to */
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2019-05-01 11:09:22 +10:00
|
|
|
struct MVert *vert; /* Array of verts being projected (to fetch normals or other data) */
|
|
|
|
float (*vertexCos)[3]; /* vertexs being shrinkwraped */
|
2018-10-03 19:09:43 +03:00
|
|
|
int numVerts;
|
|
|
|
|
2019-05-01 11:09:22 +10:00
|
|
|
struct MDeformVert *dvert; /* Pointer to mdeform array */
|
|
|
|
int vgroup; /* Vertex group num */
|
2018-10-03 19:09:43 +03:00
|
|
|
bool invert_vgroup; /* invert vertex group influence */
|
|
|
|
|
2019-05-01 11:09:22 +10:00
|
|
|
struct Mesh *target; /* mesh we are shrinking to */
|
|
|
|
struct SpaceTransform local2target; /* transform to move between local and target space */
|
2018-10-03 19:09:43 +03:00
|
|
|
struct ShrinkwrapTreeData *tree; /* mesh BVH tree data */
|
|
|
|
|
2018-12-07 15:45:53 +01:00
|
|
|
struct Object *aux_target;
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2019-05-01 11:09:22 +10:00
|
|
|
float keepDist; /* Distance to keep above target surface (units are in local space) */
|
2018-10-03 19:09:43 +03:00
|
|
|
} ShrinkwrapCalcData;
|
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
typedef struct ShrinkwrapCalcCBData {
|
|
|
|
ShrinkwrapCalcData *calc;
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
ShrinkwrapTreeData *tree;
|
|
|
|
ShrinkwrapTreeData *aux_tree;
|
2016-05-30 17:30:06 +02:00
|
|
|
|
|
|
|
float *proj_axis;
|
|
|
|
SpaceTransform *local2aux;
|
|
|
|
} ShrinkwrapCalcCBData;
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* Checks if the modifier needs target normals with these settings. */
|
|
|
|
bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
|
|
|
|
{
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
return (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) ||
|
|
|
|
(shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX &&
|
|
|
|
shrinkMode == MOD_SHRINKWRAP_ABOVE_SURFACE);
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initializes the mesh data structure from the given mesh and settings. */
|
|
|
|
bool BKE_shrinkwrap_init_tree(
|
|
|
|
ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
|
|
|
|
{
|
|
|
|
memset(data, 0, sizeof(*data));
|
|
|
|
|
2020-06-15 23:17:42 +10:00
|
|
|
if (mesh == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We could create a BVH tree from the edit mesh,
|
|
|
|
* however accessing normals from the face/loop normals gets more involved.
|
|
|
|
* Convert mesh data since this isn't typically used in edit-mode. */
|
|
|
|
BKE_mesh_wrapper_ensure_mdata(mesh);
|
|
|
|
|
|
|
|
if (mesh->totvert <= 0) {
|
2018-10-03 19:09:43 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->mesh = mesh;
|
|
|
|
|
|
|
|
if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
|
|
|
|
data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2);
|
|
|
|
|
|
|
|
return data->bvh != NULL;
|
|
|
|
}
|
|
|
|
|
2020-08-07 12:30:43 +02:00
|
|
|
if (mesh->totpoly <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2020-08-07 12:30:43 +02:00
|
|
|
data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_LOOPTRI, 4);
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2020-08-07 12:30:43 +02:00
|
|
|
if (data->bvh == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-03 19:09:43 +03:00
|
|
|
|
2020-08-07 12:30:43 +02:00
|
|
|
if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) {
|
|
|
|
data->pnors = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
|
|
|
|
if ((mesh->flag & ME_AUTOSMOOTH) != 0) {
|
|
|
|
data->clnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
}
|
2020-08-07 12:30:43 +02:00
|
|
|
}
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
|
2020-08-07 12:30:43 +02:00
|
|
|
if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
|
|
|
data->boundary = mesh->runtime.shrinkwrap_data;
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
2020-08-07 12:30:43 +02:00
|
|
|
|
|
|
|
return true;
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Frees the tree data if necessary. */
|
|
|
|
void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data)
|
|
|
|
{
|
|
|
|
free_bvhtree_from_mesh(&data->treeData);
|
|
|
|
}
|
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Free boundary data for target project */
|
|
|
|
void BKE_shrinkwrap_discard_boundary_data(struct Mesh *mesh)
|
|
|
|
{
|
|
|
|
struct ShrinkwrapBoundaryData *data = mesh->runtime.shrinkwrap_data;
|
|
|
|
|
|
|
|
if (data != NULL) {
|
2018-11-07 06:53:29 +11:00
|
|
|
MEM_freeN((void *)data->edge_is_boundary);
|
|
|
|
MEM_freeN((void *)data->looptri_has_boundary);
|
|
|
|
MEM_freeN((void *)data->vert_boundary_id);
|
|
|
|
MEM_freeN((void *)data->boundary_verts);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
|
|
|
|
MEM_freeN(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
mesh->runtime.shrinkwrap_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Accumulate edge for average boundary edge direction. */
|
|
|
|
static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
|
|
|
|
signed char *status,
|
|
|
|
int index,
|
|
|
|
const float edge_dir[3],
|
|
|
|
signed char side)
|
|
|
|
{
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
float *direction = vdata[index].direction;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Invert the direction vector if either:
|
|
|
|
* - This is the second edge and both edges have the vertex as start or end.
|
|
|
|
* - For third and above, if it points in the wrong direction.
|
|
|
|
*/
|
|
|
|
if (status[index] >= 0 ? status[index] == side : dot_v3v3(direction, edge_dir) < 0) {
|
|
|
|
sub_v3_v3(direction, edge_dir);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
add_v3_v3(direction, edge_dir);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
status[index] = (status[index] == 0) ? side : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(struct Mesh *mesh)
|
|
|
|
{
|
|
|
|
const MLoop *mloop = mesh->mloop;
|
|
|
|
const MEdge *medge = mesh->medge;
|
|
|
|
const MVert *mvert = mesh->mvert;
|
|
|
|
|
|
|
|
/* Count faces per edge (up to 2). */
|
|
|
|
char *edge_mode = MEM_calloc_arrayN((size_t)mesh->totedge, sizeof(char), __func__);
|
|
|
|
|
|
|
|
for (int i = 0; i < mesh->totloop; i++) {
|
|
|
|
unsigned int eidx = mloop[i].e;
|
|
|
|
|
|
|
|
if (edge_mode[eidx] < 2) {
|
|
|
|
edge_mode[eidx]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build the boundary edge bitmask. */
|
|
|
|
BLI_bitmap *edge_is_boundary = BLI_BITMAP_NEW(mesh->totedge,
|
|
|
|
"ShrinkwrapBoundaryData::edge_is_boundary");
|
|
|
|
unsigned int num_boundary_edges = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < mesh->totedge; i++) {
|
|
|
|
edge_mode[i] = (edge_mode[i] == 1);
|
|
|
|
|
|
|
|
if (edge_mode[i]) {
|
|
|
|
BLI_BITMAP_ENABLE(edge_is_boundary, i);
|
|
|
|
num_boundary_edges++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If no boundary, return NULL. */
|
|
|
|
if (num_boundary_edges == 0) {
|
|
|
|
MEM_freeN(edge_is_boundary);
|
|
|
|
MEM_freeN(edge_mode);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the data object. */
|
|
|
|
ShrinkwrapBoundaryData *data = MEM_callocN(sizeof(ShrinkwrapBoundaryData),
|
|
|
|
"ShrinkwrapBoundaryData");
|
|
|
|
|
|
|
|
data->edge_is_boundary = edge_is_boundary;
|
|
|
|
|
|
|
|
/* Build the boundary looptri bitmask. */
|
|
|
|
const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
|
|
|
|
int totlooptri = BKE_mesh_runtime_looptri_len(mesh);
|
|
|
|
|
|
|
|
BLI_bitmap *looptri_has_boundary = BLI_BITMAP_NEW(totlooptri,
|
|
|
|
"ShrinkwrapBoundaryData::looptri_is_boundary");
|
|
|
|
|
|
|
|
for (int i = 0; i < totlooptri; i++) {
|
|
|
|
int edges[3];
|
|
|
|
BKE_mesh_looptri_get_real_edges(mesh, &mlooptri[i], edges);
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
if (edges[j] >= 0 && edge_mode[edges[j]]) {
|
|
|
|
BLI_BITMAP_ENABLE(looptri_has_boundary, i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->looptri_has_boundary = looptri_has_boundary;
|
|
|
|
|
|
|
|
/* Find boundary vertices and build a mapping table for compact storage of data. */
|
|
|
|
int *vert_boundary_id = MEM_calloc_arrayN(
|
|
|
|
(size_t)mesh->totvert, sizeof(int), "ShrinkwrapBoundaryData::vert_boundary_id");
|
|
|
|
|
|
|
|
for (int i = 0; i < mesh->totedge; i++) {
|
|
|
|
if (edge_mode[i]) {
|
|
|
|
const MEdge *edge = &medge[i];
|
|
|
|
|
|
|
|
vert_boundary_id[edge->v1] = 1;
|
|
|
|
vert_boundary_id[edge->v2] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int num_boundary_verts = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < mesh->totvert; i++) {
|
|
|
|
vert_boundary_id[i] = (vert_boundary_id[i] != 0) ? (int)num_boundary_verts++ : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->vert_boundary_id = vert_boundary_id;
|
|
|
|
data->num_boundary_verts = num_boundary_verts;
|
|
|
|
|
|
|
|
/* Compute average directions. */
|
|
|
|
ShrinkwrapBoundaryVertData *boundary_verts = MEM_calloc_arrayN(
|
|
|
|
num_boundary_verts, sizeof(*boundary_verts), "ShrinkwrapBoundaryData::boundary_verts");
|
|
|
|
|
|
|
|
signed char *vert_status = MEM_calloc_arrayN(num_boundary_verts, sizeof(char), __func__);
|
|
|
|
|
|
|
|
for (int i = 0; i < mesh->totedge; i++) {
|
|
|
|
if (edge_mode[i]) {
|
|
|
|
const MEdge *edge = &medge[i];
|
|
|
|
|
|
|
|
float dir[3];
|
|
|
|
sub_v3_v3v3(dir, mvert[edge->v2].co, mvert[edge->v1].co);
|
|
|
|
normalize_v3(dir);
|
|
|
|
|
|
|
|
merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge->v1], dir, 1);
|
|
|
|
merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge->v2], dir, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(vert_status);
|
|
|
|
|
|
|
|
/* Finalize average direction and compute normal. */
|
|
|
|
for (int i = 0; i < mesh->totvert; i++) {
|
|
|
|
int bidx = vert_boundary_id[i];
|
|
|
|
|
|
|
|
if (bidx >= 0) {
|
|
|
|
ShrinkwrapBoundaryVertData *vdata = &boundary_verts[bidx];
|
|
|
|
float no[3], tmp[3];
|
|
|
|
|
|
|
|
normalize_v3(vdata->direction);
|
|
|
|
|
|
|
|
normal_short_to_float_v3(no, mesh->mvert[i].no);
|
|
|
|
cross_v3_v3v3(tmp, no, vdata->direction);
|
|
|
|
cross_v3_v3v3(vdata->normal_plane, tmp, no);
|
|
|
|
normalize_v3(vdata->normal_plane);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->boundary_verts = boundary_verts;
|
|
|
|
|
|
|
|
MEM_freeN(edge_mode);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BKE_shrinkwrap_compute_boundary_data(struct Mesh *mesh)
|
|
|
|
{
|
|
|
|
BKE_shrinkwrap_discard_boundary_data(mesh);
|
|
|
|
|
|
|
|
mesh->runtime.shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
|
|
|
|
}
|
|
|
|
|
2020-10-14 14:43:54 +11:00
|
|
|
/**
|
|
|
|
* Shrink-wrap to the nearest vertex
|
2008-05-02 00:16:48 +00:00
|
|
|
*
|
2020-10-14 14:43:54 +11:00
|
|
|
* it builds a #BVHTree of vertices we can attach to and then
|
2008-08-12 20:43:10 +00:00
|
|
|
* for each vertex performs a nearest vertex search on the tree
|
2008-05-02 00:16:48 +00:00
|
|
|
*/
|
2016-05-30 17:30:06 +02:00
|
|
|
static void shrinkwrap_calc_nearest_vertex_cb_ex(void *__restrict userdata,
|
2018-01-10 12:49:51 +01:00
|
|
|
const int i,
|
2019-07-30 14:56:47 +02:00
|
|
|
const TaskParallelTLS *__restrict tls)
|
2008-05-01 01:00:01 +00:00
|
|
|
{
|
2016-05-30 17:30:06 +02:00
|
|
|
ShrinkwrapCalcCBData *data = userdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
ShrinkwrapCalcData *calc = data->calc;
|
2018-10-03 19:09:43 +03:00
|
|
|
BVHTreeFromMesh *treeData = &data->tree->treeData;
|
2018-01-05 16:33:13 +01:00
|
|
|
BVHTreeNearest *nearest = tls->userdata_chunk;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
float *co = calc->vertexCos[i];
|
|
|
|
float tmp_co[3];
|
2020-03-06 12:50:56 +11:00
|
|
|
float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (calc->invert_vgroup) {
|
|
|
|
weight = 1.0f - weight;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (weight == 0.0f) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Convert the vertex to tree coordinates */
|
|
|
|
if (calc->vert) {
|
|
|
|
copy_v3_v3(tmp_co, calc->vert[i].co);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(tmp_co, co);
|
|
|
|
}
|
|
|
|
BLI_space_transform_apply(&calc->local2target, tmp_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Use local proximity heuristics (to reduce the nearest search)
|
|
|
|
*
|
2019-04-27 12:07:07 +10:00
|
|
|
* If we already had an hit before.. we assume this vertex is going to have a close hit to that
|
|
|
|
* other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
|
2016-05-30 17:30:06 +02:00
|
|
|
* This will lead in pruning of the search tree. */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (nearest->index != -1) {
|
2016-05-30 17:30:06 +02:00
|
|
|
nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
else {
|
2016-05-30 17:30:06 +02:00
|
|
|
nearest->dist_sq = FLT_MAX;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Found the nearest vertex */
|
|
|
|
if (nearest->index != -1) {
|
|
|
|
/* Adjusting the vertex weight,
|
|
|
|
* so that after interpolating it keeps a certain distance from the nearest position */
|
|
|
|
if (nearest->dist_sq > FLT_EPSILON) {
|
|
|
|
const float dist = sqrtf(nearest->dist_sq);
|
|
|
|
weight *= (dist - calc->keepDist) / dist;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Convert the coordinates back to mesh coordinates */
|
|
|
|
copy_v3_v3(tmp_co, nearest->co);
|
|
|
|
BLI_space_transform_invert(&calc->local2target, tmp_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
|
|
|
|
}
|
|
|
|
}
|
2008-05-01 01:00:01 +00:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc)
|
|
|
|
{
|
2012-05-11 08:05:47 +00:00
|
|
|
BVHTreeNearest nearest = NULL_BVHTreeNearest;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Setup nearest */
|
2008-05-25 15:43:18 +00:00
|
|
|
nearest.index = -1;
|
2014-02-03 02:46:45 +11:00
|
|
|
nearest.dist_sq = FLT_MAX;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-07 00:06:58 +11:00
|
|
|
ShrinkwrapCalcCBData data = {
|
|
|
|
.calc = calc,
|
|
|
|
.tree = calc->tree,
|
|
|
|
};
|
2019-07-30 14:56:47 +02:00
|
|
|
TaskParallelSettings settings;
|
2018-01-08 11:35:48 +01:00
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
|
|
|
|
settings.userdata_chunk = &nearest;
|
|
|
|
settings.userdata_chunk_size = sizeof(nearest);
|
|
|
|
BLI_task_parallel_range(
|
2018-01-21 11:41:28 +11:00
|
|
|
0, calc->numVerts, &data, shrinkwrap_calc_nearest_vertex_cb_ex, &settings);
|
2008-05-01 01:00:01 +00:00
|
|
|
}
|
2008-04-30 17:55:26 +00:00
|
|
|
|
2008-07-18 22:24:20 +00:00
|
|
|
/*
|
|
|
|
* This function raycast a single vertex and updates the hit if the "hit" is considered valid.
|
2014-04-01 11:34:00 +11:00
|
|
|
* Returns true if "hit" was updated.
|
2008-07-18 22:24:20 +00:00
|
|
|
* Opts control whether an hit is valid or not
|
|
|
|
* Supported options are:
|
2018-11-14 12:53:15 +11:00
|
|
|
* - MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored)
|
|
|
|
* - MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored)
|
2008-07-18 22:24:20 +00:00
|
|
|
*/
|
2014-03-20 22:56:28 +11:00
|
|
|
bool BKE_shrinkwrap_project_normal(char options,
|
2018-05-04 07:39:07 -03:00
|
|
|
const float vert[3],
|
|
|
|
const float dir[3],
|
|
|
|
const float ray_radius,
|
|
|
|
const SpaceTransform *transf,
|
2018-10-03 19:09:43 +03:00
|
|
|
ShrinkwrapTreeData *tree,
|
|
|
|
BVHTreeRayHit *hit)
|
2008-07-18 22:24:20 +00:00
|
|
|
{
|
2012-11-05 05:07:57 +00:00
|
|
|
/* don't use this because this dist value could be incompatible
|
2020-05-09 17:15:25 +10:00
|
|
|
* this value used by the callback for comparing previous/new dist values.
|
2012-11-05 05:07:57 +00:00
|
|
|
* also, at the moment there is no need to have a corrected 'dist' value */
|
|
|
|
// #define USE_DIST_CORRECT
|
|
|
|
|
2008-07-18 22:24:20 +00:00
|
|
|
float tmp_co[3], tmp_no[3];
|
2008-07-19 15:22:38 +00:00
|
|
|
const float *co, *no;
|
2008-07-18 22:24:20 +00:00
|
|
|
BVHTreeRayHit hit_tmp;
|
2008-08-04 14:27:25 +00:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Copy from hit (we need to convert hit rays from one space coordinates to the other */
|
2012-04-29 17:11:40 +00:00
|
|
|
memcpy(&hit_tmp, hit, sizeof(hit_tmp));
|
2008-07-18 22:24:20 +00:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Apply space transform (TODO readjust dist) */
|
2012-04-28 06:31:57 +00:00
|
|
|
if (transf) {
|
2012-04-29 17:11:40 +00:00
|
|
|
copy_v3_v3(tmp_co, vert);
|
2014-08-01 16:28:31 +02:00
|
|
|
BLI_space_transform_apply(transf, tmp_co);
|
2008-07-19 15:22:38 +00:00
|
|
|
co = tmp_co;
|
2008-07-18 22:24:20 +00:00
|
|
|
|
2012-04-29 17:11:40 +00:00
|
|
|
copy_v3_v3(tmp_no, dir);
|
2014-08-01 16:28:31 +02:00
|
|
|
BLI_space_transform_apply_normal(transf, tmp_no);
|
2008-07-19 15:22:38 +00:00
|
|
|
no = tmp_no;
|
|
|
|
|
2012-11-05 05:07:57 +00:00
|
|
|
#ifdef USE_DIST_CORRECT
|
2012-05-11 08:05:47 +00:00
|
|
|
hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target);
|
2012-11-05 05:07:57 +00:00
|
|
|
#endif
|
2008-07-19 15:22:38 +00:00
|
|
|
}
|
2012-03-06 18:40:15 +00:00
|
|
|
else {
|
2008-07-19 15:22:38 +00:00
|
|
|
co = vert;
|
|
|
|
no = dir;
|
2008-07-18 22:24:20 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-07-18 22:24:20 +00:00
|
|
|
hit_tmp.index = -1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
BLI_bvhtree_ray_cast(
|
|
|
|
tree->bvh, co, no, ray_radius, &hit_tmp, tree->treeData.raycast_callback, &tree->treeData);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (hit_tmp.index != -1) {
|
2011-02-19 09:01:28 +00:00
|
|
|
/* invert the normal first so face culling works on rotated objects */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (transf) {
|
2014-08-01 16:28:31 +02:00
|
|
|
BLI_space_transform_invert_normal(transf, hit_tmp.no);
|
2011-02-19 09:01:28 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-08 13:47:26 +03:00
|
|
|
if (options & MOD_SHRINKWRAP_CULL_TARGET_MASK) {
|
2011-02-19 09:01:28 +00:00
|
|
|
/* apply backface */
|
2012-05-11 08:05:47 +00:00
|
|
|
const float dot = dot_v3v3(dir, hit_tmp.no);
|
|
|
|
if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) ||
|
2012-11-05 04:19:30 +00:00
|
|
|
((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f)) {
|
2014-03-20 22:56:28 +11:00
|
|
|
return false; /* Ignore hit */
|
2011-02-19 09:01:28 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (transf) {
|
2011-02-19 09:01:28 +00:00
|
|
|
/* Inverting space transform (TODO make coeherent with the initial dist readjust) */
|
2014-08-01 16:28:31 +02:00
|
|
|
BLI_space_transform_invert(transf, hit_tmp.co);
|
2012-11-05 05:07:57 +00:00
|
|
|
#ifdef USE_DIST_CORRECT
|
2012-11-05 04:19:30 +00:00
|
|
|
hit_tmp.dist = len_v3v3(vert, hit_tmp.co);
|
2012-11-05 05:07:57 +00:00
|
|
|
#endif
|
2008-07-18 22:24:20 +00:00
|
|
|
}
|
|
|
|
|
2012-11-05 05:07:57 +00:00
|
|
|
BLI_assert(hit_tmp.dist <= hit->dist);
|
|
|
|
|
2012-04-29 17:55:54 +00:00
|
|
|
memcpy(hit, &hit_tmp, sizeof(hit_tmp));
|
2014-03-20 22:56:28 +11:00
|
|
|
return true;
|
2008-07-18 22:24:20 +00:00
|
|
|
}
|
2014-03-20 22:56:28 +11:00
|
|
|
return false;
|
2008-07-18 22:24:20 +00:00
|
|
|
}
|
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
|
2018-01-10 12:49:51 +01:00
|
|
|
const int i,
|
2019-07-30 14:56:47 +02:00
|
|
|
const TaskParallelTLS *__restrict tls)
|
2016-05-30 17:30:06 +02:00
|
|
|
{
|
|
|
|
ShrinkwrapCalcCBData *data = userdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
ShrinkwrapCalcData *calc = data->calc;
|
2018-10-03 19:09:43 +03:00
|
|
|
ShrinkwrapTreeData *tree = data->tree;
|
|
|
|
ShrinkwrapTreeData *aux_tree = data->aux_tree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
float *proj_axis = data->proj_axis;
|
|
|
|
SpaceTransform *local2aux = data->local2aux;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-01-05 16:33:13 +01:00
|
|
|
BVHTreeRayHit *hit = tls->userdata_chunk;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
|
|
|
|
float *co = calc->vertexCos[i];
|
|
|
|
float tmp_co[3], tmp_no[3];
|
2020-03-06 12:50:56 +11:00
|
|
|
float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (calc->invert_vgroup) {
|
|
|
|
weight = 1.0f - weight;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (weight == 0.0f) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-27 20:23:22 +01:00
|
|
|
if (calc->vert != NULL && calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
|
2018-06-22 16:56:49 +02:00
|
|
|
/* calc->vert contains verts from evaluated mesh. */
|
2019-04-27 12:07:07 +10:00
|
|
|
/* These coordinates are deformed by vertexCos only for normal projection
|
|
|
|
* (to get correct normals) for other cases calc->verts contains undeformed coordinates and
|
|
|
|
* vertexCos should be used */
|
2018-11-27 20:23:22 +01:00
|
|
|
copy_v3_v3(tmp_co, calc->vert[i].co);
|
|
|
|
normal_short_to_float_v3(tmp_no, calc->vert[i].no);
|
2016-05-30 17:30:06 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(tmp_co, co);
|
|
|
|
copy_v3_v3(tmp_no, proj_axis);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
hit->index = -1;
|
2019-04-27 12:07:07 +10:00
|
|
|
|
|
|
|
/* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */
|
|
|
|
hit->dist = BVH_RAYCAST_DIST_MAX;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
bool is_aux = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Project over positive direction of axis */
|
|
|
|
if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) {
|
|
|
|
if (aux_tree) {
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, 0.0, local2aux, aux_tree, hit)) {
|
|
|
|
is_aux = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_project_normal(
|
2018-10-18 12:03:04 +11:00
|
|
|
calc->smd->shrinkOpts, tmp_co, tmp_no, 0.0, &calc->local2target, tree, hit)) {
|
2018-10-03 19:09:43 +03:00
|
|
|
is_aux = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Project over negative direction of axis */
|
|
|
|
if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) {
|
|
|
|
float inv_no[3];
|
|
|
|
negate_v3_v3(inv_no, tmp_no);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-08 13:47:26 +03:00
|
|
|
char options = calc->smd->shrinkOpts;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-08 13:47:26 +03:00
|
|
|
if ((options & MOD_SHRINKWRAP_INVERT_CULL_TARGET) &&
|
|
|
|
(options & MOD_SHRINKWRAP_CULL_TARGET_MASK)) {
|
|
|
|
options ^= MOD_SHRINKWRAP_CULL_TARGET_MASK;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (aux_tree) {
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, 0.0, local2aux, aux_tree, hit)) {
|
|
|
|
is_aux = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_project_normal(
|
2018-10-18 12:03:04 +11:00
|
|
|
options, tmp_co, inv_no, 0.0, &calc->local2target, tree, hit)) {
|
2018-10-03 19:09:43 +03:00
|
|
|
is_aux = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* don't set the initial dist (which is more efficient),
|
2016-05-30 17:30:06 +02:00
|
|
|
* because its calculated in the targets space, we want the dist in our own space */
|
|
|
|
if (proj_limit_squared != 0.0f) {
|
|
|
|
if (hit->index != -1 && len_squared_v3v3(hit->co, co) > proj_limit_squared) {
|
2018-06-22 12:07:48 +02:00
|
|
|
hit->index = -1;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 12:07:48 +02:00
|
|
|
if (hit->index != -1) {
|
2016-05-30 17:30:06 +02:00
|
|
|
if (is_aux) {
|
|
|
|
BKE_shrinkwrap_snap_point_to_surface(aux_tree,
|
2018-10-03 19:09:43 +03:00
|
|
|
local2aux,
|
2018-10-18 12:03:04 +11:00
|
|
|
calc->smd->shrinkMode,
|
|
|
|
hit->index,
|
|
|
|
hit->co,
|
|
|
|
hit->no,
|
|
|
|
calc->keepDist,
|
|
|
|
tmp_co,
|
|
|
|
hit->co);
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
BKE_shrinkwrap_snap_point_to_surface(tree,
|
2018-10-18 12:03:04 +11:00
|
|
|
&calc->local2target,
|
|
|
|
calc->smd->shrinkMode,
|
|
|
|
hit->index,
|
|
|
|
hit->co,
|
|
|
|
hit->no,
|
|
|
|
calc->keepDist,
|
|
|
|
tmp_co,
|
|
|
|
hit->co);
|
2018-10-03 19:09:43 +03:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
interp_v3_v3v3(co, co, hit->co, weight);
|
|
|
|
}
|
|
|
|
}
|
2008-05-02 00:16:48 +00:00
|
|
|
|
2018-05-30 11:34:08 +02:00
|
|
|
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
|
2008-07-09 19:43:09 +00:00
|
|
|
{
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Options about projection direction */
|
2012-05-11 08:05:47 +00:00
|
|
|
float proj_axis[3] = {0.0f, 0.0f, 0.0f};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Raycast and tree stuff */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-11-05 05:07:57 +00:00
|
|
|
/** \note 'hit.dist' is kept in the targets space, this is only used
|
|
|
|
* for finding the best hit, to get the real dist,
|
|
|
|
* measure the len_v3v3() from the input coord to hit.co */
|
2008-08-12 20:43:10 +00:00
|
|
|
BVHTreeRayHit hit;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* auxiliary target */
|
2018-05-09 12:51:03 +02:00
|
|
|
Mesh *auxMesh = NULL;
|
2018-10-03 19:09:43 +03:00
|
|
|
ShrinkwrapTreeData *aux_tree = NULL;
|
|
|
|
ShrinkwrapTreeData aux_tree_stack;
|
2008-08-12 20:43:10 +00:00
|
|
|
SpaceTransform local2aux;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* If the user doesn't allows to project in any direction of projection axis
|
2018-09-24 17:27:41 +02:00
|
|
|
* then there's nothing todo. */
|
2014-02-03 18:55:59 +11:00
|
|
|
if ((calc->smd->shrinkOpts &
|
2019-04-22 09:39:35 +10:00
|
|
|
(MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) {
|
2009-05-23 03:24:15 +00:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Prepare data to retrieve the direction in which we should project each vertex */
|
2012-04-28 06:31:57 +00:00
|
|
|
if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (calc->vert == NULL) {
|
2012-03-24 06:18:31 +00:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2008-08-12 20:43:10 +00:00
|
|
|
}
|
2012-03-06 18:40:15 +00:00
|
|
|
else {
|
2012-10-20 20:20:02 +00:00
|
|
|
/* The code supports any axis that is a combination of X,Y,Z
|
|
|
|
* although currently UI only allows to set the 3 different axis */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) {
|
2012-03-24 06:18:31 +00:00
|
|
|
proj_axis[0] = 1.0f;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) {
|
2012-03-24 06:18:31 +00:00
|
|
|
proj_axis[1] = 1.0f;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) {
|
2012-03-24 06:18:31 +00:00
|
|
|
proj_axis[2] = 1.0f;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-10 20:43:45 +00:00
|
|
|
normalize_v3(proj_axis);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Invalid projection direction */
|
2012-11-05 04:19:30 +00:00
|
|
|
if (len_squared_v3(proj_axis) < FLT_EPSILON) {
|
|
|
|
return;
|
2008-07-25 18:48:24 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2018-12-07 15:45:53 +01:00
|
|
|
if (calc->aux_target) {
|
2019-02-11 20:20:12 +01:00
|
|
|
auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target, false);
|
2019-04-22 09:39:35 +10:00
|
|
|
if (!auxMesh) {
|
2010-12-17 20:13:54 +00:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2018-12-07 15:45:53 +01:00
|
|
|
BLI_SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->aux_target);
|
2008-07-18 22:24:20 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_init_tree(
|
|
|
|
&aux_tree_stack, auxMesh, calc->smd->shrinkType, calc->smd->shrinkMode, false)) {
|
|
|
|
aux_tree = &aux_tree_stack;
|
2008-07-09 19:43:09 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* After successfully build the trees, start projection vertices. */
|
|
|
|
ShrinkwrapCalcCBData data = {
|
|
|
|
.calc = calc,
|
|
|
|
.tree = calc->tree,
|
|
|
|
.aux_tree = aux_tree,
|
2019-01-07 00:06:58 +11:00
|
|
|
.proj_axis = proj_axis,
|
|
|
|
.local2aux = &local2aux,
|
2018-10-03 19:09:43 +03:00
|
|
|
};
|
2019-07-30 14:56:47 +02:00
|
|
|
TaskParallelSettings settings;
|
2018-10-03 19:09:43 +03:00
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
|
|
|
|
settings.userdata_chunk = &hit;
|
|
|
|
settings.userdata_chunk_size = sizeof(hit);
|
|
|
|
BLI_task_parallel_range(
|
|
|
|
0, calc->numVerts, &data, shrinkwrap_calc_normal_projection_cb_ex, &settings);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* free data structures */
|
|
|
|
if (aux_tree) {
|
|
|
|
BKE_shrinkwrap_free_tree(aux_tree);
|
2016-05-06 04:49:21 +10:00
|
|
|
}
|
2008-07-09 19:43:09 +00:00
|
|
|
}
|
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/*
|
|
|
|
* Shrinkwrap Target Surface Project mode
|
|
|
|
*
|
|
|
|
* It uses Newton's method to find a surface location with its
|
|
|
|
* smooth normal pointing at the original point.
|
|
|
|
*
|
|
|
|
* The equation system on barycentric weights and normal multiplier:
|
|
|
|
*
|
|
|
|
* (w0*V0 + w1*V1 + w2*V2) + l * (w0*N0 + w1*N1 + w2*N2) - CO = 0
|
|
|
|
* w0 + w1 + w2 = 1
|
|
|
|
*
|
|
|
|
* The actual solution vector is [ w0, w1, l ], with w2 eliminated.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//#define TRACE_TARGET_PROJECT
|
|
|
|
|
|
|
|
typedef struct TargetProjectTriData {
|
|
|
|
const float **vtri_co;
|
|
|
|
const float (*vtri_no)[3];
|
|
|
|
const float *point_co;
|
|
|
|
|
|
|
|
float n0_minus_n2[3], n1_minus_n2[3];
|
|
|
|
float c0_minus_c2[3], c1_minus_c2[3];
|
|
|
|
|
|
|
|
/* Current interpolated position and normal. */
|
|
|
|
float co_interp[3], no_interp[3];
|
|
|
|
} TargetProjectTriData;
|
|
|
|
|
|
|
|
/* Computes the deviation of the equation system from goal. */
|
|
|
|
static void target_project_tri_deviation(void *userdata, const float x[3], float r_delta[3])
|
|
|
|
{
|
|
|
|
TargetProjectTriData *data = userdata;
|
|
|
|
|
2020-08-07 22:36:11 +10:00
|
|
|
const float w[3] = {x[0], x[1], 1.0f - x[0] - x[1]};
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
interp_v3_v3v3v3(data->co_interp, data->vtri_co[0], data->vtri_co[1], data->vtri_co[2], w);
|
|
|
|
interp_v3_v3v3v3(data->no_interp, data->vtri_no[0], data->vtri_no[1], data->vtri_no[2], w);
|
|
|
|
|
|
|
|
madd_v3_v3v3fl(r_delta, data->co_interp, data->no_interp, x[2]);
|
|
|
|
sub_v3_v3(r_delta, data->point_co);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Computes the Jacobian matrix of the equation system. */
|
|
|
|
static void target_project_tri_jacobian(void *userdata, const float x[3], float r_jacobian[3][3])
|
|
|
|
{
|
|
|
|
TargetProjectTriData *data = userdata;
|
|
|
|
|
|
|
|
madd_v3_v3v3fl(r_jacobian[0], data->c0_minus_c2, data->n0_minus_n2, x[2]);
|
|
|
|
madd_v3_v3v3fl(r_jacobian[1], data->c1_minus_c2, data->n1_minus_n2, x[2]);
|
|
|
|
|
|
|
|
copy_v3_v3(r_jacobian[2], data->vtri_no[2]);
|
|
|
|
madd_v3_v3fl(r_jacobian[2], data->n0_minus_n2, x[0]);
|
|
|
|
madd_v3_v3fl(r_jacobian[2], data->n1_minus_n2, x[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clamp barycentric weights to the triangle. */
|
|
|
|
static void target_project_tri_clamp(float x[3])
|
|
|
|
{
|
|
|
|
if (x[0] < 0.0f) {
|
|
|
|
x[0] = 0.0f;
|
|
|
|
}
|
|
|
|
if (x[1] < 0.0f) {
|
|
|
|
x[1] = 0.0f;
|
|
|
|
}
|
|
|
|
if (x[0] + x[1] > 1.0f) {
|
|
|
|
x[0] = x[0] / (x[0] + x[1]);
|
|
|
|
x[1] = 1.0f - x[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Correct the Newton's method step to keep the coordinates within the triangle. */
|
|
|
|
static bool target_project_tri_correct(void *UNUSED(userdata),
|
|
|
|
const float x[3],
|
|
|
|
float step[3],
|
|
|
|
float x_next[3])
|
|
|
|
{
|
|
|
|
/* Insignificant correction threshold */
|
2019-12-25 13:08:06 +03:00
|
|
|
const float epsilon = 1e-5f;
|
|
|
|
/* Dot product threshold for checking if step is 'clearly' pointing outside. */
|
|
|
|
const float dir_epsilon = 0.5f;
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
bool fixed = false, locked = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-12-25 13:08:06 +03:00
|
|
|
/* The barycentric coordinate domain is a triangle bounded by
|
|
|
|
* the X and Y axes, plus the x+y=1 diagonal. First, clamp the
|
|
|
|
* movement against the diagonal. Note that step is subtracted. */
|
|
|
|
float sum = x[0] + x[1];
|
|
|
|
float sstep = -(step[0] + step[1]);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-12-25 13:08:06 +03:00
|
|
|
if (sum + sstep > 1.0f) {
|
|
|
|
float ldist = 1.0f - sum;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-12-25 13:08:06 +03:00
|
|
|
/* If already at the boundary, slide along it. */
|
|
|
|
if (ldist < epsilon * (float)M_SQRT2) {
|
|
|
|
float step_len = len_v2(step);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Abort if the solution is clearly outside the domain. */
|
2019-12-25 13:08:06 +03:00
|
|
|
if (step_len > epsilon && sstep > step_len * dir_epsilon * (float)M_SQRT2) {
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
return false;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-12-25 13:08:06 +03:00
|
|
|
/* Project the new position onto the diagonal. */
|
|
|
|
add_v2_fl(step, (sum + sstep - 1.0f) * 0.5f);
|
|
|
|
fixed = locked = true;
|
|
|
|
}
|
|
|
|
else {
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Scale a significant step down to arrive at the boundary. */
|
2019-12-25 13:08:06 +03:00
|
|
|
mul_v3_fl(step, ldist / sstep);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
fixed = true;
|
|
|
|
}
|
2019-12-25 13:08:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Weight 0 and 1 boundary checks - along axis. */
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
if (step[i] > x[i]) {
|
|
|
|
/* If already at the boundary, slide along it. */
|
|
|
|
if (x[i] < epsilon) {
|
|
|
|
float step_len = len_v2(step);
|
|
|
|
|
|
|
|
/* Abort if the solution is clearly outside the domain. */
|
|
|
|
if (step_len > epsilon && (locked || step[i] > step_len * dir_epsilon)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset precision errors to stay at the boundary. */
|
|
|
|
step[i] = x[i];
|
|
|
|
fixed = true;
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
}
|
|
|
|
else {
|
2019-12-25 13:08:06 +03:00
|
|
|
/* Scale a significant step down to arrive at the boundary. */
|
|
|
|
mul_v3_fl(step, x[i] / step[i]);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
fixed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Recompute and clamp the new coordinates after step correction. */
|
|
|
|
if (fixed) {
|
|
|
|
sub_v3_v3v3(x_next, x, step);
|
|
|
|
target_project_tri_clamp(x_next);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool target_project_solve_point_tri(const float *vtri_co[3],
|
|
|
|
const float vtri_no[3][3],
|
|
|
|
const float point_co[3],
|
|
|
|
const float hit_co[3],
|
|
|
|
float hit_dist_sq,
|
|
|
|
float r_hit_co[3],
|
|
|
|
float r_hit_no[3])
|
|
|
|
{
|
|
|
|
float x[3], tmp[3];
|
|
|
|
float dist = sqrtf(hit_dist_sq);
|
2019-10-20 00:15:10 +03:00
|
|
|
float magnitude_estimate = dist + len_manhattan_v3(vtri_co[0]) + len_manhattan_v3(vtri_co[1]) +
|
|
|
|
len_manhattan_v3(vtri_co[2]);
|
|
|
|
float epsilon = magnitude_estimate * 1.0e-6f;
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
|
|
|
|
/* Initial solution vector: barycentric weights plus distance along normal. */
|
|
|
|
interp_weights_tri_v3(x, UNPACK3(vtri_co), hit_co);
|
|
|
|
|
|
|
|
interp_v3_v3v3v3(r_hit_no, UNPACK3(vtri_no), x);
|
|
|
|
sub_v3_v3v3(tmp, point_co, hit_co);
|
|
|
|
|
|
|
|
x[2] = (dot_v3v3(tmp, r_hit_no) < 0) ? -dist : dist;
|
|
|
|
|
|
|
|
/* Solve the equations iteratively. */
|
2019-01-07 00:06:58 +11:00
|
|
|
TargetProjectTriData tri_data = {
|
|
|
|
.vtri_co = vtri_co,
|
|
|
|
.vtri_no = vtri_no,
|
|
|
|
.point_co = point_co,
|
|
|
|
};
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
|
|
|
|
sub_v3_v3v3(tri_data.n0_minus_n2, vtri_no[0], vtri_no[2]);
|
|
|
|
sub_v3_v3v3(tri_data.n1_minus_n2, vtri_no[1], vtri_no[2]);
|
|
|
|
sub_v3_v3v3(tri_data.c0_minus_c2, vtri_co[0], vtri_co[2]);
|
|
|
|
sub_v3_v3v3(tri_data.c1_minus_c2, vtri_co[1], vtri_co[2]);
|
|
|
|
|
|
|
|
target_project_tri_clamp(x);
|
|
|
|
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
|
|
|
const bool trace = true;
|
|
|
|
#else
|
|
|
|
const bool trace = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool ok = BLI_newton3d_solve(target_project_tri_deviation,
|
|
|
|
target_project_tri_jacobian,
|
|
|
|
target_project_tri_correct,
|
|
|
|
&tri_data,
|
|
|
|
epsilon,
|
|
|
|
20,
|
|
|
|
trace,
|
|
|
|
x,
|
|
|
|
x);
|
|
|
|
|
|
|
|
if (ok) {
|
|
|
|
copy_v3_v3(r_hit_co, tri_data.co_interp);
|
|
|
|
copy_v3_v3(r_hit_no, tri_data.no_interp);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool update_hit(BVHTreeNearest *nearest,
|
|
|
|
int index,
|
|
|
|
const float co[3],
|
|
|
|
const float hit_co[3],
|
|
|
|
const float hit_no[3])
|
|
|
|
{
|
|
|
|
float dist_sq = len_squared_v3v3(hit_co, co);
|
|
|
|
|
|
|
|
if (dist_sq < nearest->dist_sq) {
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
|
|
|
printf(
|
2019-10-20 00:15:10 +03:00
|
|
|
"#=#=#> %d (%.3f,%.3f,%.3f) %g < %g\n", index, UNPACK3(hit_co), dist_sq, nearest->dist_sq);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#endif
|
|
|
|
nearest->index = index;
|
|
|
|
nearest->dist_sq = dist_sq;
|
|
|
|
copy_v3_v3(nearest->co, hit_co);
|
|
|
|
normalize_v3_v3(nearest->no, hit_no);
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-27 12:07:07 +10:00
|
|
|
/* Target projection on a non-manifold boundary edge -
|
|
|
|
* treats it like an infinitely thin cylinder. */
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
static void target_project_edge(const ShrinkwrapTreeData *tree,
|
|
|
|
int index,
|
|
|
|
const float co[3],
|
|
|
|
BVHTreeNearest *nearest,
|
|
|
|
int eidx)
|
|
|
|
{
|
|
|
|
const BVHTreeFromMesh *data = &tree->treeData;
|
|
|
|
const MEdge *edge = &tree->mesh->medge[eidx];
|
|
|
|
const float *vedge_co[2] = {data->vert[edge->v1].co, data->vert[edge->v2].co};
|
|
|
|
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
|
|
|
printf("EDGE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f)\n",
|
|
|
|
eidx,
|
|
|
|
UNPACK3(vedge_co[0]),
|
|
|
|
UNPACK3(vedge_co[1]));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Retrieve boundary vertex IDs */
|
|
|
|
const int *vert_boundary_id = tree->boundary->vert_boundary_id;
|
|
|
|
int bid1 = vert_boundary_id[edge->v1], bid2 = vert_boundary_id[edge->v2];
|
|
|
|
|
|
|
|
if (bid1 < 0 || bid2 < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve boundary vertex normals and align them to direction. */
|
|
|
|
const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts;
|
|
|
|
float vedge_dir[2][3], dir[3];
|
|
|
|
|
|
|
|
copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
|
|
|
|
copy_v3_v3(vedge_dir[1], boundary_verts[bid2].normal_plane);
|
|
|
|
|
|
|
|
sub_v3_v3v3(dir, vedge_co[1], vedge_co[0]);
|
|
|
|
|
|
|
|
if (dot_v3v3(boundary_verts[bid1].direction, dir) < 0) {
|
|
|
|
negate_v3(vedge_dir[0]);
|
|
|
|
}
|
|
|
|
if (dot_v3v3(boundary_verts[bid2].direction, dir) < 0) {
|
|
|
|
negate_v3(vedge_dir[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Solve a quadratic equation: lerp(d0,d1,x) * (co - lerp(v0,v1,x)) = 0 */
|
|
|
|
float d0v0 = dot_v3v3(vedge_dir[0], vedge_co[0]), d0v1 = dot_v3v3(vedge_dir[0], vedge_co[1]);
|
|
|
|
float d1v0 = dot_v3v3(vedge_dir[1], vedge_co[0]), d1v1 = dot_v3v3(vedge_dir[1], vedge_co[1]);
|
|
|
|
float d0co = dot_v3v3(vedge_dir[0], co);
|
|
|
|
|
|
|
|
float a = d0v1 - d0v0 + d1v0 - d1v1;
|
|
|
|
float b = 2 * d0v0 - d0v1 - d0co - d1v0 + dot_v3v3(vedge_dir[1], co);
|
|
|
|
float c = d0co - d0v0;
|
|
|
|
float det = b * b - 4 * a * c;
|
|
|
|
|
|
|
|
if (det >= 0) {
|
|
|
|
const float epsilon = 1e-6f;
|
|
|
|
float sdet = sqrtf(det);
|
|
|
|
float hit_co[3], hit_no[3];
|
|
|
|
|
|
|
|
for (int i = (det > 0 ? 2 : 0); i >= 0; i -= 2) {
|
|
|
|
float x = (-b + ((float)i - 1) * sdet) / (2 * a);
|
|
|
|
|
|
|
|
if (x >= -epsilon && x <= 1.0f + epsilon) {
|
|
|
|
CLAMP(x, 0, 1);
|
|
|
|
|
|
|
|
float vedge_no[2][3];
|
|
|
|
normal_short_to_float_v3(vedge_no[0], data->vert[edge->v1].no);
|
|
|
|
normal_short_to_float_v3(vedge_no[1], data->vert[edge->v2].no);
|
|
|
|
|
|
|
|
interp_v3_v3v3(hit_co, vedge_co[0], vedge_co[1], x);
|
|
|
|
interp_v3_v3v3(hit_no, vedge_no[0], vedge_no[1], x);
|
|
|
|
|
|
|
|
update_hit(nearest, index, co, hit_co, hit_no);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Target normal projection BVH callback - based on mesh_looptri_nearest_point. */
|
|
|
|
static void mesh_looptri_target_project(void *userdata,
|
|
|
|
int index,
|
|
|
|
const float co[3],
|
|
|
|
BVHTreeNearest *nearest)
|
|
|
|
{
|
|
|
|
const ShrinkwrapTreeData *tree = (ShrinkwrapTreeData *)userdata;
|
|
|
|
const BVHTreeFromMesh *data = &tree->treeData;
|
|
|
|
const MLoopTri *lt = &data->looptri[index];
|
|
|
|
const MLoop *loop[3] = {
|
|
|
|
&data->loop[lt->tri[0]], &data->loop[lt->tri[1]], &data->loop[lt->tri[2]]};
|
|
|
|
const MVert *vtri[3] = {
|
|
|
|
&data->vert[loop[0]->v], &data->vert[loop[1]->v], &data->vert[loop[2]->v]};
|
|
|
|
const float *vtri_co[3] = {vtri[0]->co, vtri[1]->co, vtri[2]->co};
|
|
|
|
float raw_hit_co[3], hit_co[3], hit_no[3], dist_sq, vtri_no[3][3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* First find the closest point and bail out if it's worse than the current solution. */
|
|
|
|
closest_on_tri_to_point_v3(raw_hit_co, co, UNPACK3(vtri_co));
|
|
|
|
dist_sq = len_squared_v3v3(co, raw_hit_co);
|
|
|
|
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
|
|
|
printf("TRIANGLE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) %g %g\n",
|
|
|
|
index,
|
|
|
|
UNPACK3(vtri_co[0]),
|
|
|
|
UNPACK3(vtri_co[1]),
|
|
|
|
UNPACK3(vtri_co[2]),
|
|
|
|
dist_sq,
|
|
|
|
nearest->dist_sq);
|
|
|
|
#endif
|
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (dist_sq >= nearest->dist_sq) {
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Decode normals */
|
|
|
|
normal_short_to_float_v3(vtri_no[0], vtri[0]->no);
|
|
|
|
normal_short_to_float_v3(vtri_no[1], vtri[1]->no);
|
|
|
|
normal_short_to_float_v3(vtri_no[2], vtri[2]->no);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
/* Solve the equations for the triangle */
|
|
|
|
if (target_project_solve_point_tri(vtri_co, vtri_no, co, raw_hit_co, dist_sq, hit_co, hit_no)) {
|
|
|
|
update_hit(nearest, index, co, hit_co, hit_no);
|
|
|
|
}
|
|
|
|
/* Boundary edges */
|
|
|
|
else if (tree->boundary && BLI_BITMAP_TEST(tree->boundary->looptri_has_boundary, index)) {
|
|
|
|
const BLI_bitmap *is_boundary = tree->boundary->edge_is_boundary;
|
|
|
|
int edges[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
BKE_mesh_looptri_get_real_edges(tree->mesh, lt, edges);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
if (edges[i] >= 0 && BLI_BITMAP_TEST(is_boundary, edges[i])) {
|
|
|
|
target_project_edge(tree, index, co, nearest, edges[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maps the point to the nearest surface, either by simple nearest, or by target normal projection.
|
|
|
|
*/
|
|
|
|
void BKE_shrinkwrap_find_nearest_surface(struct ShrinkwrapTreeData *tree,
|
|
|
|
BVHTreeNearest *nearest,
|
|
|
|
float co[3],
|
|
|
|
int type)
|
|
|
|
{
|
|
|
|
BVHTreeFromMesh *treeData = &tree->treeData;
|
|
|
|
|
|
|
|
if (type == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
2019-10-20 00:15:10 +03:00
|
|
|
printf("\n====== TARGET PROJECT START ======\n");
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
BLI_bvhtree_find_nearest_ex(
|
|
|
|
tree->bvh, co, nearest, mesh_looptri_target_project, tree, BVH_NEAREST_OPTIMAL_ORDER);
|
|
|
|
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
2019-10-20 00:15:10 +03:00
|
|
|
printf("====== TARGET PROJECT END: %d %g ======\n\n", nearest->index, nearest->dist_sq);
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (nearest->index < 0) {
|
|
|
|
/* fallback to simple nearest */
|
|
|
|
BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-26 21:57:53 +00:00
|
|
|
/*
|
|
|
|
* Shrinkwrap moving vertexs to the nearest surface point on the target
|
|
|
|
*
|
|
|
|
* it builds a BVHTree from the target mesh and then performs a
|
2012-03-01 12:20:18 +00:00
|
|
|
* NN matches for each vertex
|
2008-05-26 21:57:53 +00:00
|
|
|
*/
|
2016-05-30 17:30:06 +02:00
|
|
|
static void shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdata,
|
2018-01-10 12:49:51 +01:00
|
|
|
const int i,
|
2019-07-30 14:56:47 +02:00
|
|
|
const TaskParallelTLS *__restrict tls)
|
2008-05-26 21:57:53 +00:00
|
|
|
{
|
2016-05-30 17:30:06 +02:00
|
|
|
ShrinkwrapCalcCBData *data = userdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
ShrinkwrapCalcData *calc = data->calc;
|
2018-01-05 16:33:13 +01:00
|
|
|
BVHTreeNearest *nearest = tls->userdata_chunk;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
float *co = calc->vertexCos[i];
|
|
|
|
float tmp_co[3];
|
2020-03-06 12:50:56 +11:00
|
|
|
float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (calc->invert_vgroup) {
|
|
|
|
weight = 1.0f - weight;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
if (weight == 0.0f) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Convert the vertex to tree coordinates */
|
|
|
|
if (calc->vert) {
|
|
|
|
copy_v3_v3(tmp_co, calc->vert[i].co);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(tmp_co, co);
|
|
|
|
}
|
|
|
|
BLI_space_transform_apply(&calc->local2target, tmp_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Use local proximity heuristics (to reduce the nearest search)
|
|
|
|
*
|
2019-04-27 12:07:07 +10:00
|
|
|
* If we already had an hit before.. we assume this vertex is going to have a close hit to that
|
|
|
|
* other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
|
2016-05-30 17:30:06 +02:00
|
|
|
* This will lead in pruning of the search tree. */
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
if (nearest->index != -1) {
|
|
|
|
if (calc->smd->shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
|
|
|
/* Heuristic doesn't work because of additional restrictions. */
|
|
|
|
nearest->index = -1;
|
|
|
|
nearest->dist_sq = FLT_MAX;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
else {
|
2016-05-30 17:30:06 +02:00
|
|
|
nearest->dist_sq = FLT_MAX;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
BKE_shrinkwrap_find_nearest_surface(data->tree, nearest, tmp_co, calc->smd->shrinkType);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-30 17:30:06 +02:00
|
|
|
/* Found the nearest vertex */
|
|
|
|
if (nearest->index != -1) {
|
2018-10-03 19:09:43 +03:00
|
|
|
BKE_shrinkwrap_snap_point_to_surface(data->tree,
|
2018-10-18 12:03:04 +11:00
|
|
|
NULL,
|
|
|
|
calc->smd->shrinkMode,
|
|
|
|
nearest->index,
|
|
|
|
nearest->co,
|
|
|
|
nearest->no,
|
|
|
|
calc->keepDist,
|
|
|
|
tmp_co,
|
|
|
|
tmp_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
/* Convert the coordinates back to mesh coordinates */
|
|
|
|
BLI_space_transform_invert(&calc->local2target, tmp_co);
|
|
|
|
interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/**
|
|
|
|
* Compute a smooth normal of the target (if applicable) at the hit location.
|
|
|
|
*
|
2018-10-18 12:03:04 +11:00
|
|
|
* \param tree: information about the mesh
|
|
|
|
* \param transform: transform from the hit coordinate space to the object space; may be null
|
|
|
|
* \param r_no: output in hit coordinate space; may be shared with inputs
|
2018-10-03 19:09:43 +03:00
|
|
|
*/
|
|
|
|
void BKE_shrinkwrap_compute_smooth_normal(const struct ShrinkwrapTreeData *tree,
|
|
|
|
const struct SpaceTransform *transform,
|
|
|
|
int looptri_idx,
|
|
|
|
const float hit_co[3],
|
|
|
|
const float hit_no[3],
|
|
|
|
float r_no[3])
|
|
|
|
{
|
|
|
|
const BVHTreeFromMesh *treeData = &tree->treeData;
|
|
|
|
const MLoopTri *tri = &treeData->looptri[looptri_idx];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* Interpolate smooth normals if enabled. */
|
|
|
|
if ((tree->mesh->mpoly[tri->poly].flag & ME_SMOOTH) != 0) {
|
|
|
|
const MVert *verts[] = {
|
|
|
|
&treeData->vert[treeData->loop[tri->tri[0]].v],
|
|
|
|
&treeData->vert[treeData->loop[tri->tri[1]].v],
|
|
|
|
&treeData->vert[treeData->loop[tri->tri[2]].v],
|
|
|
|
};
|
|
|
|
float w[3], no[3][3], tmp_co[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* Custom and auto smooth split normals. */
|
|
|
|
if (tree->clnors) {
|
|
|
|
copy_v3_v3(no[0], tree->clnors[tri->tri[0]]);
|
|
|
|
copy_v3_v3(no[1], tree->clnors[tri->tri[1]]);
|
|
|
|
copy_v3_v3(no[2], tree->clnors[tri->tri[2]]);
|
|
|
|
}
|
|
|
|
/* Ordinary vertex normals. */
|
|
|
|
else {
|
|
|
|
normal_short_to_float_v3(no[0], verts[0]->no);
|
|
|
|
normal_short_to_float_v3(no[1], verts[1]->no);
|
|
|
|
normal_short_to_float_v3(no[2], verts[2]->no);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* Barycentric weights from hit point. */
|
|
|
|
copy_v3_v3(tmp_co, hit_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (transform) {
|
|
|
|
BLI_space_transform_apply(transform, tmp_co);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
interp_weights_tri_v3(w, verts[0]->co, verts[1]->co, verts[2]->co, tmp_co);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
/* Interpolate using weights. */
|
|
|
|
interp_v3_v3v3v3(r_no, no[0], no[1], no[2], w);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (transform) {
|
|
|
|
BLI_space_transform_invert_normal(transform, r_no);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
normalize_v3(r_no);
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 20:24:05 +03:00
|
|
|
/* Use the polygon normal if flat. */
|
|
|
|
else if (tree->pnors != NULL) {
|
|
|
|
copy_v3_v3(r_no, tree->pnors[tri->poly]);
|
|
|
|
}
|
|
|
|
/* Finally fallback to the looptri normal. */
|
2018-10-03 19:09:43 +03:00
|
|
|
else {
|
|
|
|
copy_v3_v3(r_no, hit_no);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
/* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */
|
|
|
|
static void shrinkwrap_snap_with_side(float r_point_co[3],
|
|
|
|
const float point_co[3],
|
|
|
|
const float hit_co[3],
|
|
|
|
const float hit_no[3],
|
|
|
|
float goal_dist,
|
|
|
|
float forcesign,
|
|
|
|
bool forcesnap)
|
|
|
|
{
|
2019-10-20 00:15:10 +03:00
|
|
|
float delta[3];
|
|
|
|
sub_v3_v3v3(delta, point_co, hit_co);
|
|
|
|
|
|
|
|
float dist = len_v3(delta);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
/* If exactly on the surface, push out along normal */
|
|
|
|
if (dist < FLT_EPSILON) {
|
2019-01-10 11:55:09 +03:00
|
|
|
if (forcesnap || goal_dist > 0) {
|
|
|
|
madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist * forcesign);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(r_point_co, hit_co);
|
|
|
|
}
|
2018-07-07 18:39:45 +03:00
|
|
|
}
|
|
|
|
/* Move to the correct side if needed */
|
|
|
|
else {
|
2019-10-20 00:15:10 +03:00
|
|
|
float dsign = signf(dot_v3v3(delta, hit_no));
|
|
|
|
|
|
|
|
if (forcesign == 0.0f) {
|
|
|
|
forcesign = dsign;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
/* If on the wrong side or too close, move to correct */
|
2019-10-20 00:15:10 +03:00
|
|
|
if (forcesnap || dsign * dist * forcesign < goal_dist) {
|
|
|
|
mul_v3_fl(delta, dsign / dist);
|
|
|
|
|
|
|
|
/* At very small distance, blend in the hit normal to stabilize math. */
|
|
|
|
float dist_epsilon = (fabsf(goal_dist) + len_manhattan_v3(hit_co)) * 1e-4f;
|
|
|
|
|
|
|
|
if (dist < dist_epsilon) {
|
|
|
|
#ifdef TRACE_TARGET_PROJECT
|
|
|
|
printf("zero_factor %g = %g / %g\n", dist / dist_epsilon, dist, dist_epsilon);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
interp_v3_v3v3(delta, hit_no, delta, dist / dist_epsilon);
|
|
|
|
}
|
|
|
|
|
|
|
|
madd_v3_v3v3fl(r_point_co, hit_co, delta, goal_dist * forcesign);
|
2016-05-30 17:30:06 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-07-07 18:39:45 +03:00
|
|
|
copy_v3_v3(r_point_co, point_co);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply the shrink to surface modes to the given original coordinates and nearest point.
|
2018-10-03 19:09:43 +03:00
|
|
|
*
|
2018-10-18 12:03:04 +11:00
|
|
|
* \param tree: mesh data for smooth normals
|
|
|
|
* \param transform: transform from the hit coordinate space to the object space; may be null
|
|
|
|
* \param r_point_co: may be the same memory location as point_co, hit_co, or hit_no.
|
2018-07-07 18:39:45 +03:00
|
|
|
*/
|
|
|
|
void BKE_shrinkwrap_snap_point_to_surface(const struct ShrinkwrapTreeData *tree,
|
2018-10-03 19:09:43 +03:00
|
|
|
const struct SpaceTransform *transform,
|
|
|
|
int mode,
|
|
|
|
int hit_idx,
|
|
|
|
const float hit_co[3],
|
|
|
|
const float hit_no[3],
|
|
|
|
float goal_dist,
|
2018-07-07 18:39:45 +03:00
|
|
|
const float point_co[3],
|
|
|
|
float r_point_co[3])
|
|
|
|
{
|
2019-10-20 00:15:10 +03:00
|
|
|
float tmp[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
switch (mode) {
|
|
|
|
/* Offsets along the line between point_co and hit_co. */
|
|
|
|
case MOD_SHRINKWRAP_ON_SURFACE:
|
2019-10-20 00:15:10 +03:00
|
|
|
if (goal_dist != 0) {
|
|
|
|
shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, 0, true);
|
2016-05-30 17:30:06 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-07-07 18:39:45 +03:00
|
|
|
copy_v3_v3(r_point_co, hit_co);
|
2016-05-30 17:30:06 +02:00
|
|
|
}
|
2018-07-07 18:39:45 +03:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
case MOD_SHRINKWRAP_INSIDE:
|
|
|
|
shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, -1, false);
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
case MOD_SHRINKWRAP_OUTSIDE:
|
|
|
|
shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, false);
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
case MOD_SHRINKWRAP_OUTSIDE_SURFACE:
|
2019-01-10 11:55:09 +03:00
|
|
|
if (goal_dist != 0) {
|
2018-07-07 18:39:45 +03:00
|
|
|
shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(r_point_co, hit_co);
|
|
|
|
}
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
/* Offsets along the normal */
|
|
|
|
case MOD_SHRINKWRAP_ABOVE_SURFACE:
|
2019-01-10 11:55:09 +03:00
|
|
|
if (goal_dist != 0) {
|
2018-10-03 19:09:43 +03:00
|
|
|
BKE_shrinkwrap_compute_smooth_normal(tree, transform, hit_idx, hit_co, hit_no, tmp);
|
|
|
|
madd_v3_v3v3fl(r_point_co, hit_co, tmp, goal_dist);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
copy_v3_v3(r_point_co, hit_co);
|
|
|
|
}
|
2018-07-07 18:39:45 +03:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-07 18:39:45 +03:00
|
|
|
default:
|
|
|
|
printf("Unknown Shrinkwrap surface snap mode: %d\n", mode);
|
|
|
|
copy_v3_v3(r_point_co, hit_co);
|
2016-05-30 17:30:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
|
|
|
|
{
|
2012-05-11 08:05:47 +00:00
|
|
|
BVHTreeNearest nearest = NULL_BVHTreeNearest;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Setup nearest */
|
2008-05-26 21:57:53 +00:00
|
|
|
nearest.index = -1;
|
2014-02-03 02:46:45 +11:00
|
|
|
nearest.dist_sq = FLT_MAX;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Find the nearest vertex */
|
2019-01-07 00:06:58 +11:00
|
|
|
ShrinkwrapCalcCBData data = {
|
|
|
|
.calc = calc,
|
|
|
|
.tree = calc->tree,
|
|
|
|
};
|
2019-07-30 14:56:47 +02:00
|
|
|
TaskParallelSettings settings;
|
2018-01-08 11:35:48 +01:00
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
|
|
|
|
settings.userdata_chunk = &nearest;
|
|
|
|
settings.userdata_chunk_size = sizeof(nearest);
|
|
|
|
BLI_task_parallel_range(
|
|
|
|
0, calc->numVerts, &data, shrinkwrap_calc_nearest_surface_point_cb_ex, &settings);
|
2008-05-26 21:57:53 +00:00
|
|
|
}
|
2008-05-02 00:16:48 +00:00
|
|
|
|
2009-01-04 14:14:06 +00:00
|
|
|
/* Main shrinkwrap function */
|
2018-12-07 15:45:53 +01:00
|
|
|
void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
|
|
|
|
const ModifierEvalContext *ctx,
|
|
|
|
struct Scene *scene,
|
|
|
|
Object *ob,
|
|
|
|
Mesh *mesh,
|
|
|
|
MDeformVert *dvert,
|
|
|
|
const int defgrp_index,
|
|
|
|
float (*vertexCos)[3],
|
|
|
|
int numVerts)
|
2009-01-04 14:14:06 +00:00
|
|
|
{
|
2009-05-23 03:24:15 +00:00
|
|
|
|
2012-05-11 08:05:47 +00:00
|
|
|
DerivedMesh *ss_mesh = NULL;
|
2009-01-04 14:14:06 +00:00
|
|
|
ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-18 18:29:16 +11:00
|
|
|
/* remove loop dependencies on derived meshes (TODO should this be done elsewhere?) */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (smd->target == ob) {
|
2017-03-18 18:29:16 +11:00
|
|
|
smd->target = NULL;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
if (smd->auxTarget == ob) {
|
2012-03-24 06:18:31 +00:00
|
|
|
smd->auxTarget = NULL;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Configure Shrinkwrap calc data */
|
2009-01-04 14:14:06 +00:00
|
|
|
calc.smd = smd;
|
|
|
|
calc.ob = ob;
|
|
|
|
calc.numVerts = numVerts;
|
|
|
|
calc.vertexCos = vertexCos;
|
2018-11-26 20:43:35 +01:00
|
|
|
calc.dvert = dvert;
|
|
|
|
calc.vgroup = defgrp_index;
|
2016-03-07 11:24:03 +11:00
|
|
|
calc.invert_vgroup = (smd->shrinkOpts & MOD_SHRINKWRAP_INVERT_VGROUP) != 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-07 15:45:53 +01:00
|
|
|
if (smd->target != NULL) {
|
|
|
|
Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
|
2019-02-11 20:20:12 +01:00
|
|
|
calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-09-07 21:08:20 +10:00
|
|
|
/* TODO there might be several "bugs" with non-uniform scales matrices
|
2012-10-20 20:20:02 +00:00
|
|
|
* because it will no longer be nearest surface, not sphere projection
|
|
|
|
* because space has been deformed */
|
2018-12-07 15:45:53 +01:00
|
|
|
BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob, ob_target);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* TODO: smd->keepDist is in global units.. must change to local */
|
2009-05-23 03:24:15 +00:00
|
|
|
calc.keepDist = smd->keepDist;
|
|
|
|
}
|
2018-12-07 15:45:53 +01:00
|
|
|
calc.aux_target = DEG_get_evaluated_object(ctx->depsgraph, smd->auxTarget);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-05-08 19:04:12 +02:00
|
|
|
if (mesh != NULL && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Setup arrays to get vertexs positions, normals and deform weights */
|
2018-11-26 20:43:35 +01:00
|
|
|
calc.vert = mesh->mvert;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Using vertexs positions/normals as if a subsurface was applied */
|
2012-04-28 06:31:57 +00:00
|
|
|
if (smd->subsurfLevels) {
|
2012-05-11 08:05:47 +00:00
|
|
|
SubsurfModifierData ssmd = {{NULL}};
|
2012-10-20 20:20:02 +00:00
|
|
|
ssmd.subdivType = ME_CC_SUBSURF; /* catmull clark */
|
|
|
|
ssmd.levels = smd->subsurfLevels; /* levels */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-05-08 19:04:12 +02:00
|
|
|
/* TODO to be moved to Mesh once we are done with changes in subsurf code. */
|
|
|
|
DerivedMesh *dm = CDDM_from_mesh(mesh);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-18 11:51:02 +02:00
|
|
|
ss_mesh = subsurf_make_derived_from_derived(
|
|
|
|
dm, &ssmd, scene, NULL, (ob->mode & OB_MODE_EDIT) ? SUBSURF_IN_EDIT_MODE : 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 06:31:57 +00:00
|
|
|
if (ss_mesh) {
|
2009-05-23 03:24:15 +00:00
|
|
|
calc.vert = ss_mesh->getVertDataArray(ss_mesh, CD_MVERT);
|
2012-04-28 06:31:57 +00:00
|
|
|
if (calc.vert) {
|
|
|
|
/* TRICKY: this code assumes subsurface will have the transformed original vertices
|
|
|
|
* in their original order at the end of the vert array. */
|
2009-05-23 03:24:15 +00:00
|
|
|
calc.vert = calc.vert + ss_mesh->getNumVerts(ss_mesh) - dm->getNumVerts(dm);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Just to make sure we are not leaving any memory behind */
|
2018-05-08 19:04:12 +02:00
|
|
|
BLI_assert(ssmd.emCache == NULL);
|
|
|
|
BLI_assert(ssmd.mCache == NULL);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-05-08 19:04:12 +02:00
|
|
|
dm->release(dm);
|
2009-01-04 14:14:06 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* Projecting target defined - lets work! */
|
2018-10-03 19:09:43 +03:00
|
|
|
ShrinkwrapTreeData tree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
if (BKE_shrinkwrap_init_tree(&tree, calc.target, smd->shrinkType, smd->shrinkMode, false)) {
|
|
|
|
calc.tree = &tree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 06:31:57 +00:00
|
|
|
switch (smd->shrinkType) {
|
2009-01-04 14:14:06 +00:00
|
|
|
case MOD_SHRINKWRAP_NEAREST_SURFACE:
|
Shrinkwrap: new mode that projects along the target normal.
The Nearest Surface Point shrink method, while fast, is neither
smooth nor continuous: as the source point moves, the projected
point can both stop and jump. This causes distortions in the
deformation of the shrinkwrap modifier, and the motion of an
animated object with a shrinkwrap constraint.
This patch implements a new mode, which, instead of using the simple
nearest point search, iteratively solves an equation for each triangle
to find a point which has its interpolated normal point to or from the
original vertex. Non-manifold boundary edges are treated as infinitely
thin cylinders that cast normals in all perpendicular directions.
Since this is useful for the constraint, and having multiple
objects with constraints targeting the same guide mesh is a quite
reasonable use case, rather than calculating the mesh boundary edge
data over and over again, it is precomputed and cached in the mesh.
Reviewers: mont29
Differential Revision: https://developer.blender.org/D3836
2018-11-06 21:04:53 +03:00
|
|
|
case MOD_SHRINKWRAP_TARGET_PROJECT:
|
2012-11-09 04:01:19 +00:00
|
|
|
TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), deform_surface);
|
2012-05-11 08:05:47 +00:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-04 14:14:06 +00:00
|
|
|
case MOD_SHRINKWRAP_PROJECT:
|
2018-05-30 11:34:08 +02:00
|
|
|
TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
|
2012-05-11 08:05:47 +00:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-04 14:14:06 +00:00
|
|
|
case MOD_SHRINKWRAP_NEAREST_VERTEX:
|
2012-11-09 04:01:19 +00:00
|
|
|
TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), deform_vertex);
|
2012-05-11 08:05:47 +00:00
|
|
|
break;
|
2009-01-04 14:14:06 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-10-03 19:09:43 +03:00
|
|
|
BKE_shrinkwrap_free_tree(&tree);
|
2009-01-04 14:14:06 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* free memory */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ss_mesh) {
|
2009-05-23 03:24:15 +00:00
|
|
|
ss_mesh->release(ss_mesh);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2009-01-04 14:14:06 +00:00
|
|
|
}
|
2019-09-10 15:18:51 +02:00
|
|
|
|
|
|
|
void BKE_shrinkwrap_mesh_nearest_surface_deform(struct bContext *C,
|
|
|
|
Object *ob_source,
|
|
|
|
Object *ob_target)
|
|
|
|
{
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
struct Scene *sce = CTX_data_scene(C);
|
2020-01-26 16:35:58 +01:00
|
|
|
ShrinkwrapModifierData ssmd = {{0}};
|
2019-09-10 15:18:51 +02:00
|
|
|
ModifierEvalContext ctx = {depsgraph, ob_source, 0};
|
|
|
|
int totvert;
|
|
|
|
|
|
|
|
ssmd.target = ob_target;
|
|
|
|
ssmd.shrinkType = MOD_SHRINKWRAP_NEAREST_SURFACE;
|
|
|
|
ssmd.shrinkMode = MOD_SHRINKWRAP_ON_SURFACE;
|
|
|
|
ssmd.keepDist = 0.0f;
|
|
|
|
|
|
|
|
Mesh *src_me = ob_source->data;
|
|
|
|
float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
|
|
|
|
|
|
|
|
shrinkwrapModifier_deform(&ssmd, &ctx, sce, ob_source, src_me, NULL, -1, vertexCos, totvert);
|
|
|
|
|
|
|
|
BKE_mesh_vert_coords_apply(src_me, vertexCos);
|
|
|
|
|
|
|
|
MEM_freeN(vertexCos);
|
|
|
|
}
|
2019-09-26 16:28:56 +02:00
|
|
|
|
|
|
|
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
|
|
|
|
{
|
2020-01-26 16:35:58 +01:00
|
|
|
ShrinkwrapModifierData ssmd = {{0}};
|
2019-09-26 16:28:56 +02:00
|
|
|
int totvert;
|
|
|
|
|
|
|
|
ssmd.target = ob_target;
|
2019-11-05 15:01:51 +01:00
|
|
|
ssmd.shrinkType = MOD_SHRINKWRAP_PROJECT;
|
2019-09-26 16:28:56 +02:00
|
|
|
ssmd.shrinkMode = MOD_SHRINKWRAP_ON_SURFACE;
|
2019-11-05 15:01:51 +01:00
|
|
|
ssmd.shrinkOpts = MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR;
|
2019-09-26 16:28:56 +02:00
|
|
|
ssmd.keepDist = 0.0f;
|
2020-10-22 17:08:37 -04:00
|
|
|
|
|
|
|
/* Tolerance value to prevent artifacts on sharp edges of a mesh.
|
2020-10-15 18:48:18 +02:00
|
|
|
* This constant and based on experimenting with different values. */
|
|
|
|
const float projLimitTolerance = 5.0f;
|
|
|
|
ssmd.projLimit = target_me->remesh_voxel_size * projLimitTolerance;
|
2019-09-26 16:28:56 +02:00
|
|
|
|
|
|
|
float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
|
|
|
|
|
|
|
|
ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
|
|
|
|
|
|
|
|
calc.smd = &ssmd;
|
|
|
|
calc.numVerts = src_me->totvert;
|
|
|
|
calc.vertexCos = vertexCos;
|
|
|
|
calc.vgroup = -1;
|
|
|
|
calc.target = target_me;
|
|
|
|
calc.keepDist = ssmd.keepDist;
|
2019-11-05 15:01:51 +01:00
|
|
|
calc.vert = src_me->mvert;
|
2019-09-26 16:28:56 +02:00
|
|
|
BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob_target, ob_target);
|
|
|
|
|
|
|
|
ShrinkwrapTreeData tree;
|
|
|
|
if (BKE_shrinkwrap_init_tree(&tree, calc.target, ssmd.shrinkType, ssmd.shrinkMode, false)) {
|
|
|
|
calc.tree = &tree;
|
2019-11-05 15:01:51 +01:00
|
|
|
TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
|
2019-09-26 16:28:56 +02:00
|
|
|
BKE_shrinkwrap_free_tree(&tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
BKE_mesh_vert_coords_apply(src_me, vertexCos);
|
|
|
|
|
|
|
|
MEM_freeN(vertexCos);
|
|
|
|
}
|