Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2020-03-03 14:35:51 +0300
committerSergey Sharybin <sergey.vfx@gmail.com>2020-03-13 16:14:56 +0300
commitbc0a0cdf171037cba4076c796e9adb2769382561 (patch)
tree518cf488b585fa0440989e756db10d5c3cd3f6a1 /source/blender
parentb0a1af4eb10f6c771a49401e9d0c4c56f6f6d008 (diff)
Multires: Fix Subdivide, Reshape and Apply Base
This change fixes artifacts produced by these operations. On a technical aspect this is done by porting all of the operations to the new subdivision surface implementation which ensures that tangent space used to evaluate modifier and those operations is exactly the same (before modifier will use new code and the operations will still use an old one). The next step is to get sculpting on a non-top level to work, and that actually requires fixes in the undo system.
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_multires.h37
-rw-r--r--source/blender/blenkernel/BKE_object.h4
-rw-r--r--source/blender/blenkernel/CMakeLists.txt8
-rw-r--r--source/blender/blenkernel/intern/multires.c171
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.c233
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.h302
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_apply_base.c158
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_ccg.c72
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_legacy.c1056
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c1064
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_util.c727
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_vertcos.c210
-rw-r--r--source/blender/blenkernel/intern/object.c4
-rw-r--r--source/blender/editors/object/object_modifier.c31
-rw-r--r--source/blender/editors/object/object_relations.c2
15 files changed, 2828 insertions, 1251 deletions
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index c663ae76564..941675489c1 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -98,16 +98,15 @@ void multiresModifier_del_levels(struct MultiresModifierData *mmd,
struct Scene *scene,
struct Object *object,
int direction);
-void multiresModifier_base_apply(struct MultiresModifierData *mmd,
- struct Scene *scene,
- struct Object *ob);
-void multiresModifier_subdivide(struct MultiresModifierData *mmd,
- struct Scene *scene,
- struct Object *ob,
- int updateblock,
- int simple);
-void multiresModifier_sync_levels_ex(struct Scene *scene,
- struct Object *ob_dst,
+void multiresModifier_base_apply(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd);
+void multiresModifier_subdivide_legacy(struct MultiresModifierData *mmd,
+ struct Scene *scene,
+ struct Object *ob,
+ int updateblock,
+ int simple);
+void multiresModifier_sync_levels_ex(struct Object *ob_dst,
struct MultiresModifierData *mmd_src,
struct MultiresModifierData *mmd_dst);
@@ -145,18 +144,32 @@ int mdisp_rot_face_to_crn(struct MVert *mvert,
/* Reshaping, define in multires_reshape.c */
+bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ const float (*vert_coords)[3],
+ const int num_vert_coords);
bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph,
struct MultiresModifierData *mmd,
struct Object *dst,
struct Object *src);
bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph,
- struct MultiresModifierData *mmd,
struct Object *ob,
- struct ModifierData *md);
+ struct MultiresModifierData *mmd,
+ struct ModifierData *deform_md);
bool multiresModifier_reshapeFromCCG(const int tot_level,
struct Mesh *coarse_mesh,
struct SubdivCCG *subdiv_ccg);
+/* Subdivide multires displacement once. */
+void multiresModifier_subdivide(struct Object *object, struct MultiresModifierData *mmd);
+
+/* Subdivide displacement to the given level.
+ * If level is lower than the current top level nothing happens. */
+void multiresModifier_subdivide_to_level(struct Object *object,
+ struct MultiresModifierData *mmd,
+ const int top_level);
+
/* Subdivision integration, defined in multires_subdiv.c */
struct SubdivSettings;
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index a8ebd32ad4d..886591d7728 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -76,9 +76,7 @@ bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md);
bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type);
-void BKE_object_link_modifiers(struct Scene *scene,
- struct Object *ob_dst,
- const struct Object *ob_src);
+void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src);
void BKE_object_free_modifiers(struct Object *ob, const int flag);
void BKE_object_free_shaderfx(struct Object *ob, const int flag);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 112933e40be..923108240dd 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -168,7 +168,12 @@ set(SRC
intern/modifier.c
intern/movieclip.c
intern/multires.c
- intern/multires_reshape_legacy.c
+ intern/multires_reshape.c
+ intern/multires_reshape_apply_base.c
+ intern/multires_reshape_ccg.c
+ intern/multires_reshape_smooth.c
+ intern/multires_reshape_util.c
+ intern/multires_reshape_vertcos.c
intern/multires_subdiv.c
intern/nla.c
intern/node.c
@@ -378,6 +383,7 @@ set(SRC
intern/data_transfer_intern.h
intern/lib_intern.h
intern/multires_inline.h
+ intern/multires_reshape.h
intern/pbvh_intern.h
intern/subdiv_converter.h
intern/subdiv_inline.h
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index f3d65f584d1..70061c155d3 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -57,6 +57,8 @@
#include "DEG_depsgraph_query.h"
+#include "multires_reshape.h"
+
#include <math.h>
#include <string.h>
@@ -789,158 +791,7 @@ static DerivedMesh *subsurf_dm_create_local(Scene *scene,
return subsurf_make_derived_from_derived(dm, &smd, scene, NULL, flags);
}
-/* assumes no is normalized; return value's sign is negative if v is on
- * the other side of the plane */
-static float v3_dist_from_plane(float v[3], float center[3], float no[3])
-{
- float s[3];
- sub_v3_v3v3(s, v, center);
- return dot_v3v3(s, no);
-}
-
-void multiresModifier_base_apply(MultiresModifierData *mmd, Scene *scene, Object *ob)
-{
- DerivedMesh *cddm, *dispdm, *origdm;
- Mesh *me;
- const MeshElemMap *pmap;
- float(*origco)[3];
- int i, j, k, offset, totlvl;
-
- multires_force_sculpt_rebuild(ob);
-
- me = BKE_mesh_from_object(ob);
- totlvl = mmd->totlvl;
-
- /* nothing to do */
- if (!totlvl) {
- return;
- }
-
- /* XXX - probably not necessary to regenerate the cddm so much? */
-
- /* generate highest level with displacements */
- cddm = CDDM_from_mesh(me);
- DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
- dispdm = multires_dm_create_local(
- scene, ob, cddm, totlvl, totlvl, 0, 0, MULTIRES_IGNORE_SIMPLIFY);
- cddm->release(cddm);
-
- /* copy the new locations of the base verts into the mesh */
- offset = dispdm->getNumVerts(dispdm) - me->totvert;
- for (i = 0; i < me->totvert; i++) {
- dispdm->getVertCo(dispdm, offset + i, me->mvert[i].co);
- }
-
- /* heuristic to produce a better-fitting base mesh */
-
- cddm = CDDM_from_mesh(me);
- pmap = cddm->getPolyMap(ob, cddm);
- origco = MEM_calloc_arrayN(me->totvert, 3 * sizeof(float), "multires apply base origco");
- for (i = 0; i < me->totvert; i++) {
- copy_v3_v3(origco[i], me->mvert[i].co);
- }
-
- for (i = 0; i < me->totvert; i++) {
- float avg_no[3] = {0, 0, 0}, center[3] = {0, 0, 0}, push[3];
- float dist;
- int tot = 0;
-
- /* don't adjust verts not used by at least one poly */
- if (!pmap[i].count) {
- continue;
- }
-
- /* find center */
- for (j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &me->mpoly[pmap[i].indices[j]];
-
- /* this double counts, not sure if that's bad or good */
- for (k = 0; k < p->totloop; k++) {
- int vndx = me->mloop[p->loopstart + k].v;
- if (vndx != i) {
- add_v3_v3(center, origco[vndx]);
- tot++;
- }
- }
- }
- mul_v3_fl(center, 1.0f / tot);
-
- /* find normal */
- for (j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &me->mpoly[pmap[i].indices[j]];
- MPoly fake_poly;
- MLoop *fake_loops;
- float(*fake_co)[3];
- float no[3];
-
- /* set up poly, loops, and coords in order to call
- * BKE_mesh_calc_poly_normal_coords() */
- fake_poly.totloop = p->totloop;
- fake_poly.loopstart = 0;
- fake_loops = MEM_malloc_arrayN(p->totloop, sizeof(MLoop), "fake_loops");
- fake_co = MEM_malloc_arrayN(p->totloop, 3 * sizeof(float), "fake_co");
-
- for (k = 0; k < p->totloop; k++) {
- int vndx = me->mloop[p->loopstart + k].v;
-
- fake_loops[k].v = k;
-
- if (vndx == i) {
- copy_v3_v3(fake_co[k], center);
- }
- else {
- copy_v3_v3(fake_co[k], origco[vndx]);
- }
- }
-
- BKE_mesh_calc_poly_normal_coords(&fake_poly, fake_loops, (const float(*)[3])fake_co, no);
- MEM_freeN(fake_loops);
- MEM_freeN(fake_co);
-
- add_v3_v3(avg_no, no);
- }
- normalize_v3(avg_no);
-
- /* push vertex away from the plane */
- dist = v3_dist_from_plane(me->mvert[i].co, center, avg_no);
- copy_v3_v3(push, avg_no);
- mul_v3_fl(push, dist);
- add_v3_v3(me->mvert[i].co, push);
- }
-
- MEM_freeN(origco);
- cddm->release(cddm);
-
- /* Vertices were moved around, need to update normals after all the vertices are updated
- * Probably this is possible to do in the loop above, but this is rather tricky because
- * we don't know all needed vertices' coordinates there yet.
- */
- BKE_mesh_calc_normals(me);
-
- /* subdivide the mesh to highest level without displacements */
- cddm = CDDM_from_mesh(me);
- DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
- origdm = subsurf_dm_create_local(scene,
- ob,
- cddm,
- totlvl,
- 0,
- 0,
- mmd->uv_smooth == SUBSURF_UV_SMOOTH_NONE,
- 0,
- false,
- SUBSURF_IGNORE_SIMPLIFY);
- cddm->release(cddm);
-
- /* calc disps */
- multiresModifier_disp_run(
- dispdm, me, NULL, CALC_DISPLACEMENTS, origdm->getGridData(origdm), totlvl);
-
- origdm->release(origdm);
- dispdm->release(dispdm);
-}
-
-static void multires_subdivide(
+static void multires_subdivide_legacy(
MultiresModifierData *mmd, Scene *scene, Object *ob, int totlvl, int updateblock, int simple)
{
Mesh *me = ob->data;
@@ -1038,10 +889,10 @@ static void multires_subdivide(
multires_set_tot_level(ob, mmd, totlvl);
}
-void multiresModifier_subdivide(
+void multiresModifier_subdivide_legacy(
MultiresModifierData *mmd, Scene *scene, Object *ob, int updateblock, int simple)
{
- multires_subdivide(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple);
+ multires_subdivide_legacy(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple);
}
static void grid_tangent(const CCGKey *key, int x, int y, int axis, CCGElem *grid, float t[3])
@@ -2277,7 +2128,8 @@ void multires_load_old(Object *ob, Mesh *me)
multires_load_old_vcols(me);
multires_load_old_face_flags(me);
- /* multiresModifier_subdivide (actually, multires_subdivide) expects polys, not tessfaces! */
+ /* multiresModifier_subdivide_legacy (actually, multires_subdivide_legacy) expects polys, not
+ * tessfaces! */
BKE_mesh_convert_mfaces_to_mpolys(me);
/* Add a multires modifier to the object */
@@ -2289,7 +2141,7 @@ void multires_load_old(Object *ob, Mesh *me)
BLI_insertlinkbefore(&ob->modifiers, md, mmd);
for (i = 0; i < me->mr->level_count - 1; i++) {
- multiresModifier_subdivide(mmd, NULL, ob, 1, 0);
+ multiresModifier_subdivide_legacy(mmd, NULL, ob, 1, 0);
}
mmd->lvl = mmd->totlvl;
@@ -2314,8 +2166,7 @@ void multires_load_old(Object *ob, Mesh *me)
/* If 'ob_src' and 'ob_dst' both have multires modifiers, synchronize them
* such that 'ob_dst' has the same total number of levels as 'ob_src'. */
-void multiresModifier_sync_levels_ex(Scene *scene,
- Object *ob_dst,
+void multiresModifier_sync_levels_ex(Object *ob_dst,
MultiresModifierData *mmd_src,
MultiresModifierData *mmd_dst)
{
@@ -2324,7 +2175,7 @@ void multiresModifier_sync_levels_ex(Scene *scene,
}
if (mmd_src->totlvl > mmd_dst->totlvl) {
- multires_subdivide(mmd_dst, scene, ob_dst, mmd_src->totlvl, false, mmd_dst->simple);
+ multiresModifier_subdivide_to_level(ob_dst, mmd_dst, mmd_src->totlvl);
}
else {
multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl);
@@ -2346,7 +2197,7 @@ static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst)
}
if (mmd_src && mmd_dst) {
- multiresModifier_sync_levels_ex(scene, ob_dst, mmd_src, mmd_dst);
+ multiresModifier_sync_levels_ex(ob_dst, mmd_src, mmd_dst);
}
}
diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c
new file mode 100644
index 00000000000..82f6e70692b
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape.c
@@ -0,0 +1,233 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_math_vector.h"
+
+#include "BKE_customdata.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "multires_reshape.h"
+
+/* ================================================================================================
+ * Reshape from object.
+ */
+
+bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ const float (*vert_coords)[3],
+ const int num_vert_coords)
+{
+ MultiresReshapeContext reshape_context;
+ if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) {
+ return false;
+ }
+ multires_reshape_store_original_grids(&reshape_context);
+ multires_reshape_ensure_grids(object->data, reshape_context.top.level);
+ if (!multires_reshape_assign_final_coords_from_vertcos(
+ &reshape_context, vert_coords, num_vert_coords)) {
+ multires_reshape_context_free(&reshape_context);
+ return false;
+ }
+ multires_reshape_smooth_object_grids_with_details(&reshape_context);
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_reshape_context_free(&reshape_context);
+ return true;
+}
+
+/* Returns truth on success, false otherwise.
+ *
+ * This function might fail in cases like source and destination not having
+ * matched amount of vertices. */
+bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph,
+ struct MultiresModifierData *mmd,
+ struct Object *dst,
+ struct Object *src)
+{
+ struct Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ struct Object *src_eval = DEG_get_evaluated_object(depsgraph, src);
+ Mesh *src_mesh_eval = mesh_get_eval_final(depsgraph, scene_eval, src_eval, &CD_MASK_BAREMESH);
+
+ int num_deformed_verts;
+ float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(src_mesh_eval, &num_deformed_verts);
+
+ const bool result = multiresModifier_reshapeFromVertcos(
+ depsgraph, dst, mmd, deformed_verts, num_deformed_verts);
+
+ MEM_freeN(deformed_verts);
+
+ return result;
+}
+
+/* ================================================================================================
+ * Reshape from modifier.
+ */
+
+bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ struct ModifierData *deform_md)
+{
+ MultiresModifierData highest_mmd = *mmd;
+ highest_mmd.sculptlvl = highest_mmd.totlvl;
+ highest_mmd.lvl = highest_mmd.totlvl;
+ highest_mmd.renderlvl = highest_mmd.totlvl;
+
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+
+ /* Create mesh for the multires, ignoring any further modifiers (leading
+ * deformation modifiers will be applied though). */
+ Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, scene_eval, &highest_mmd, object);
+ int num_deformed_verts;
+ float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts);
+
+ /* Apply deformation modifier on the multires, */
+ const ModifierEvalContext modifier_ctx = {
+ .depsgraph = depsgraph,
+ .object = object,
+ .flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY,
+ };
+ modwrap_deformVerts(
+ deform_md, &modifier_ctx, multires_mesh, deformed_verts, multires_mesh->totvert);
+ BKE_id_free(NULL, multires_mesh);
+
+ /* Reshaping */
+ bool result = multiresModifier_reshapeFromVertcos(
+ depsgraph, object, &highest_mmd, deformed_verts, num_deformed_verts);
+
+ /* Cleanup */
+ MEM_freeN(deformed_verts);
+
+ return result;
+}
+
+/* ================================================================================================
+ * Reshape from grids.
+ */
+
+bool multiresModifier_reshapeFromCCG(const int tot_level,
+ Mesh *coarse_mesh,
+ struct SubdivCCG *subdiv_ccg)
+{
+ MultiresReshapeContext reshape_context;
+ if (!multires_reshape_context_create_from_ccg(
+ &reshape_context, subdiv_ccg, coarse_mesh, tot_level)) {
+ return false;
+ }
+ multires_reshape_store_original_grids(&reshape_context);
+ multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level);
+ if (!multires_reshape_assign_final_coords_from_ccg(&reshape_context, subdiv_ccg)) {
+ multires_reshape_context_free(&reshape_context);
+ return false;
+ }
+ multires_reshape_smooth_object_grids_with_details(&reshape_context);
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_reshape_context_free(&reshape_context);
+ return true;
+}
+
+/* ================================================================================================
+ * Subdivision.
+ */
+
+void multiresModifier_subdivide(Object *object, MultiresModifierData *mmd)
+{
+ const int top_level = mmd->totlvl + 1;
+ multiresModifier_subdivide_to_level(object, mmd, top_level);
+}
+
+void multiresModifier_subdivide_to_level(struct Object *object,
+ struct MultiresModifierData *mmd,
+ const int top_level)
+{
+ if (top_level <= mmd->totlvl) {
+ return;
+ }
+
+ Mesh *coarse_mesh = object->data;
+ MultiresReshapeContext reshape_context;
+
+ /* There was no multires at all, all displacement is at 0. Can simply make sure all mdisps grids
+ * are allocated at a proper level and return. */
+ const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS);
+ if (!has_mdisps) {
+ CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop);
+ }
+ if (!has_mdisps || top_level == 1) {
+ multires_reshape_ensure_grids(coarse_mesh, top_level);
+ multires_set_tot_level(object, mmd, top_level);
+ return;
+ }
+
+ multires_flush_sculpt_updates(object);
+
+ if (!multires_reshape_context_create_from_subdivide(&reshape_context, object, mmd, top_level)) {
+ return;
+ }
+ multires_reshape_store_original_grids(&reshape_context);
+ multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level);
+ multires_reshape_assign_final_coords_from_orig_mdisps(&reshape_context);
+ multires_reshape_smooth_object_grids(&reshape_context);
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+ multires_reshape_context_free(&reshape_context);
+
+ multires_set_tot_level(object, mmd, top_level);
+}
+
+/* ================================================================================================
+ * Apply base.
+ */
+
+void multiresModifier_base_apply(struct Depsgraph *depsgraph,
+ Object *object,
+ MultiresModifierData *mmd)
+{
+ multires_force_sculpt_rebuild(object);
+
+ MultiresReshapeContext reshape_context;
+ if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) {
+ return;
+ }
+
+ multires_reshape_assign_final_coords_from_mdisps(&reshape_context);
+ multires_reshape_apply_base_update_mesh_coords(&reshape_context);
+ multires_reshape_apply_base_refit_base_mesh(&reshape_context);
+ multires_reshape_apply_base_refine_subdiv(&reshape_context);
+ multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
+
+ multires_reshape_context_free(&reshape_context);
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h
new file mode 100644
index 00000000000..6a51c1112cb
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape.h
@@ -0,0 +1,302 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifndef __BKE_INTERN_MULTIRES_RESHAPE_H__
+#define __BKE_INTERN_MULTIRES_RESHAPE_H__
+
+#include "BLI_sys_types.h"
+
+struct Depsgraph;
+struct GridPaintMask;
+struct MDisps;
+struct Mesh;
+struct MultiresModifierData;
+struct Object;
+struct Subdiv;
+struct SubdivCCG;
+
+typedef struct MultiresReshapeContext {
+ /* Base mesh from original object.
+ * NOTE: Does NOT include any leading modifiers in it. */
+ struct Mesh *base_mesh;
+
+ /* Subdivision surface created for multires modifier.
+ *
+ * The coarse mesh of this subdivision surface is a base mesh with all deformation modifiers
+ * leading multires applied on it. */
+ struct Subdiv *subdiv;
+ bool need_free_subdiv;
+
+ struct {
+ /* Level at which displacement is being assigned to.
+ * It will be propagated up from this level to top.level. */
+ int level;
+
+ /* Grid size for reshape.level. */
+ int grid_size;
+ } reshape;
+
+ struct {
+ /* Top level of the displacement grids.
+ * The displacement will be propagated up to this level. */
+ int level;
+
+ /* Grid size for top.level. */
+ int grid_size;
+ } top;
+
+ struct {
+ /* Copy of original displacement and painting masks. */
+ struct MDisps *mdisps;
+ struct GridPaintMask *grid_paint_masks;
+ } orig;
+
+ /* Number of grids which are required for base_mesh. */
+ int num_grids;
+
+ /* Destination displacement and mask.
+ * Points to a custom data on a destination mesh. */
+ struct MDisps *mdisps;
+ struct GridPaintMask *grid_paint_masks;
+
+ /* Indexed by face index, gives first grid index of the face. */
+ int *face_start_grid_index;
+
+ /* Indexed by grid index, contains face (poly) index in the base mesh from which the grid has
+ * been created (in other words, index of a poly which contains loop corresponding to the grid
+ * index). */
+ int *grid_to_face_index;
+
+ /* Indexed by ptex face index, gives first grid index of the ptex face.
+ *
+ * For non-quad base faces ptex face is created for every face corner, so it's similar to a
+ * grid in this case. In this case start grid index will be the only one for this ptex face.
+ *
+ * For quad base faces there is a single ptex face but 4 grids. So in this case there will be
+ * 4 grids for the ptex, starting at a value stored in this mapping. */
+ int *ptex_start_grid_index;
+
+ /* Indexed by base face index, returns first ptex face index corresponding
+ * to that base face. */
+ int *face_ptex_offset;
+} MultiresReshapeContext;
+
+/* Coordinate which identifies element of a grid.
+ * This is directly related on how CD_MDISPS stores dispalcement.
+ */
+typedef struct GridCoord {
+ int grid_index;
+ float u, v;
+} GridCoord;
+
+/* COordinate within ptex, which is what OpenSubdiv API operates on. */
+typedef struct PTexCoord {
+ int ptex_face_index;
+ float u, v;
+} PTexCoord;
+
+/* Element of a grid data stored in the destination mesh.
+ * This is where reshaped coordinates and mask values will be written to. */
+typedef struct ReshapeGridElement {
+ float *displacement;
+ float *mask;
+} ReshapeGridElement;
+
+typedef struct ReshapeConstGridElement {
+ float displacement[3];
+ float mask;
+} ReshapeConstGridElement;
+
+/* ================================================================================================
+ * Construct/destruct reshape context.
+ */
+
+/* Create subdivision surface descriptor which is configured for surface evaluation at a given
+ * multires modifier. */
+struct Subdiv *multires_reshape_create_subdiv(struct Depsgraph *depsgraph,
+ struct Object *object,
+ const struct MultiresModifierData *mmd);
+
+bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context,
+ struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd);
+
+bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context,
+ struct SubdivCCG *subdiv_ccg,
+ struct Mesh *base_mesh,
+ int top_level);
+
+bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *reshape_context,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ int top_level);
+
+void multires_reshape_context_free(MultiresReshapeContext *reshape_context);
+
+/* ================================================================================================
+ * Helper accessors.
+ */
+
+/* For the given grid index get index of face it was created for. */
+int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context,
+ int grid_index);
+
+/* For the given grid index get corner of a face it was created for. */
+int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_context, int grid_index);
+
+bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index);
+
+/* For the given grid index get index of corresponding ptex face. */
+int multires_reshape_grid_to_ptex_index(const MultiresReshapeContext *reshape_context,
+ int grid_index);
+
+/* Convert normalized coordinate within a grid to a normalized coordinate within a ptex face. */
+PTexCoord multires_reshape_grid_coord_to_ptex(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord);
+
+/* Convert a normalized coordinate within a ptex face to a normalized coordinate within a grid. */
+GridCoord multires_reshape_ptex_coord_to_grid(const MultiresReshapeContext *reshape_context,
+ const PTexCoord *ptex_coord);
+
+/* Calculate tangent matrix which converts displacement to a object vector.
+ * Is calculated for the given surface derivatives at a given base face corner. */
+void multires_reshape_tangent_matrix_for_corner(const MultiresReshapeContext *reshape_context,
+ const int face_index,
+ const int corner,
+ const float dPdu[3],
+ const float dPdv[3],
+ float r_tangent_matrix[3][3]);
+
+/* Get grid elements which are to be reshaped at a given or ptex coordinate.
+ * The data is coming from final custom mdata layers. */
+ReshapeGridElement multires_reshape_grid_element_for_grid_coord(
+ const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord);
+ReshapeGridElement multires_reshape_grid_element_for_ptex_coord(
+ const MultiresReshapeContext *reshape_context, const PTexCoord *ptex_coord);
+
+/* Get original grid element for the given coordinate. */
+ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord(
+ const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord);
+
+/* ================================================================================================
+ * Sample limit surface of the base mesh.
+ */
+
+/* Evaluate limit surface created from base mesh.
+ * This is the limit surface which defines tangent space for MDisps. */
+void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ float r_P[3],
+ float r_tangent_matrix[3][3]);
+
+/* ================================================================================================
+ * Custom data preparation.
+ */
+
+/* Make sure custom data is allocated for the given level. */
+void multires_reshape_ensure_grids(struct Mesh *mesh, const int level);
+
+/* ================================================================================================
+ * Functions specific to reshaping from a set of vertices in a object position.
+ */
+
+/* Returns truth if all coordinates were assigned.
+ *
+ * False will be returned if the number of vertex coordinates did not match required number of
+ * vertices at a reshape level. */
+bool multires_reshape_assign_final_coords_from_vertcos(
+ const MultiresReshapeContext *reshape_context,
+ const float (*vert_coords)[3],
+ const int num_vert_coords);
+
+/* ================================================================================================
+ * Functions specific to reshaping from CCG.
+ */
+
+/* NOTE: Displacement grids to be at least at a reshape level.
+ *
+ * Return truth if all coordinates have been updated. */
+bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context,
+ struct SubdivCCG *subdiv_ccg);
+
+/* ================================================================================================
+ * Functions specific to reshaping from MDISPS.
+ */
+
+/* Reads and writes to the current mesh CD_MDISPS. */
+void multires_reshape_assign_final_coords_from_mdisps(
+ const MultiresReshapeContext *reshape_context);
+
+/* Reads from original CD_MIDTSPS, writes to the current mesh CD_MDISPS. */
+void multires_reshape_assign_final_coords_from_orig_mdisps(
+ const MultiresReshapeContext *reshape_context);
+
+/* ================================================================================================
+ * Displacement smooth.
+ */
+
+/* Operates on a displacement grids (CD_MDISPS) which contains object space coordinates stopred for
+ * the reshape level.
+ *
+ * The result is grids which are defining mesh with a smooth surface and details starting from
+ * reshape level up to top level added back from original displacement grids. */
+void multires_reshape_smooth_object_grids_with_details(
+ const MultiresReshapeContext *reshape_context);
+
+/* Operates on a displacement grids (CD_MDISPS) which contains object spacecoordinates stopred for
+ * the reshape level.
+ *
+ * Makes it so surface on top level looks smooth. Details are not preserved
+ */
+void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context);
+
+/* ================================================================================================
+ * Displacement, space conversion.
+ */
+
+/* Store original grid data, so then it's possible to calculate delta from it and add
+ * high-frequency content on top of reshaped grids. */
+void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context);
+
+void multires_reshape_object_grids_to_tangent_displacement(
+ const MultiresReshapeContext *reshape_context);
+
+/* ================================================================================================
+ * Apply base.
+ */
+
+/* Update mesh coordinates to the final positions of displacement in object space.
+ * This is effectively desired position of base mesh vertices after caneling out displacement.
+ *
+ * NOTE: Expects that mesh's CD_MDISPS has been set ot object space positions. */
+void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context);
+
+/* Perform better fitting of the base mesh so its subdivided version brings vertices to their
+ * desired locations. */
+void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context);
+
+/* Refine subdivision surface to the new positions of the base mesh. */
+void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context);
+
+#endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */
diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
new file mode 100644
index 00000000000..e05b5bb3179
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math_vector.h"
+
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_subdiv_eval.h"
+
+void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context)
+{
+ Mesh *base_mesh = reshape_context->base_mesh;
+ const int grid_size = reshape_context->top.grid_size;
+ const int grid_index = grid_size * grid_size - 1;
+ for (int i = 0; i < base_mesh->totloop; ++i) {
+ MDisps *displacement_grid = &reshape_context->mdisps[i];
+ const MLoop *loop = &base_mesh->mloop[i];
+ MVert *vert = &base_mesh->mvert[loop->v];
+ copy_v3_v3(vert->co, displacement_grid->disps[grid_index]);
+ }
+}
+
+/* Assumes no is normalized; return value's sign is negative if v is on the other side of the
+ * plane. */
+static float v3_dist_from_plane(float v[3], float center[3], float no[3])
+{
+ float s[3];
+ sub_v3_v3v3(s, v, center);
+ return dot_v3v3(s, no);
+}
+
+void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context)
+{
+ Mesh *base_mesh = reshape_context->base_mesh;
+
+ MeshElemMap *pmap;
+ int *pmap_mem;
+ BKE_mesh_vert_poly_map_create(&pmap,
+ &pmap_mem,
+ base_mesh->mpoly,
+ base_mesh->mloop,
+ base_mesh->totvert,
+ base_mesh->totpoly,
+ base_mesh->totloop);
+
+ float(*origco)[3] = MEM_calloc_arrayN(
+ base_mesh->totvert, 3 * sizeof(float), "multires apply base origco");
+ for (int i = 0; i < base_mesh->totvert; i++) {
+ copy_v3_v3(origco[i], base_mesh->mvert[i].co);
+ }
+
+ for (int i = 0; i < base_mesh->totvert; i++) {
+ float avg_no[3] = {0, 0, 0}, center[3] = {0, 0, 0}, push[3];
+
+ /* Don't adjust vertices not used by at least one poly. */
+ if (!pmap[i].count) {
+ continue;
+ }
+
+ /* Find center. */
+ int tot = 0;
+ for (int j = 0; j < pmap[i].count; j++) {
+ const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]];
+
+ /* This double counts, not sure if that's bad or good. */
+ for (int k = 0; k < p->totloop; k++) {
+ const int vndx = base_mesh->mloop[p->loopstart + k].v;
+ if (vndx != i) {
+ add_v3_v3(center, origco[vndx]);
+ tot++;
+ }
+ }
+ }
+ mul_v3_fl(center, 1.0f / tot);
+
+ /* Find normal. */
+ for (int j = 0; j < pmap[i].count; j++) {
+ const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]];
+ MPoly fake_poly;
+ MLoop *fake_loops;
+ float(*fake_co)[3];
+ float no[3];
+
+ /* Set up poly, loops, and coords in order to call BKE_mesh_calc_poly_normal_coords(). */
+ fake_poly.totloop = p->totloop;
+ fake_poly.loopstart = 0;
+ fake_loops = MEM_malloc_arrayN(p->totloop, sizeof(MLoop), "fake_loops");
+ fake_co = MEM_malloc_arrayN(p->totloop, 3 * sizeof(float), "fake_co");
+
+ for (int k = 0; k < p->totloop; k++) {
+ const int vndx = base_mesh->mloop[p->loopstart + k].v;
+
+ fake_loops[k].v = k;
+
+ if (vndx == i) {
+ copy_v3_v3(fake_co[k], center);
+ }
+ else {
+ copy_v3_v3(fake_co[k], origco[vndx]);
+ }
+ }
+
+ BKE_mesh_calc_poly_normal_coords(&fake_poly, fake_loops, (const float(*)[3])fake_co, no);
+ MEM_freeN(fake_loops);
+ MEM_freeN(fake_co);
+
+ add_v3_v3(avg_no, no);
+ }
+ normalize_v3(avg_no);
+
+ /* Push vertex away from the plane. */
+ const float dist = v3_dist_from_plane(base_mesh->mvert[i].co, center, avg_no);
+ copy_v3_v3(push, avg_no);
+ mul_v3_fl(push, dist);
+ add_v3_v3(base_mesh->mvert[i].co, push);
+ }
+
+ MEM_freeN(origco);
+ MEM_freeN(pmap);
+ MEM_freeN(pmap_mem);
+
+ /* Vertices were moved around, need to update normals after all the vertices are updated
+ * Probably this is possible to do in the loop above, but this is rather tricky because
+ * we don't know all needed vertices' coordinates there yet. */
+ BKE_mesh_calc_normals(base_mesh);
+}
+
+void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context)
+{
+ BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, reshape_context->base_mesh, NULL);
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape_ccg.c b/source/blender/blenkernel/intern/multires_reshape_ccg.c
new file mode 100644
index 00000000000..1f8c782ed46
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_ccg.c
@@ -0,0 +1,72 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include <string.h>
+
+#include "BLI_utildefines.h"
+
+#include "BKE_ccg.h"
+#include "BKE_subdiv_ccg.h"
+
+bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context,
+ struct SubdivCCG *subdiv_ccg)
+{
+ CCGKey reshape_level_key;
+ BKE_subdiv_ccg_key(&reshape_level_key, subdiv_ccg, reshape_context->reshape.level);
+
+ const int reshape_grid_size = reshape_context->reshape.grid_size;
+ const float reshape_grid_size_1_inv = 1.0f / (((float)reshape_grid_size) - 1.0f);
+
+ int num_grids = subdiv_ccg->num_grids;
+ for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
+ CCGElem *ccg_grid = subdiv_ccg->grids[grid_index];
+ for (int y = 0; y < reshape_grid_size; ++y) {
+ const float v = (float)y * reshape_grid_size_1_inv;
+ for (int x = 0; x < reshape_grid_size; ++x) {
+ const float u = (float)x * reshape_grid_size_1_inv;
+
+ GridCoord grid_coord;
+ grid_coord.grid_index = grid_index;
+ grid_coord.u = u;
+ grid_coord.v = v;
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(
+ reshape_context, &grid_coord);
+
+ BLI_assert(grid_element.displacement != NULL);
+ memcpy(grid_element.displacement,
+ CCG_grid_elem_co(&reshape_level_key, ccg_grid, x, y),
+ sizeof(float) * 3);
+
+ if (reshape_level_key.has_mask) {
+ BLI_assert(grid_element.mask != NULL);
+ *grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y);
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape_legacy.c b/source/blender/blenkernel/intern/multires_reshape_legacy.c
deleted file mode 100644
index 0a8248b25f1..00000000000
--- a/source/blender/blenkernel/intern/multires_reshape_legacy.c
+++ /dev/null
@@ -1,1056 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2018 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup bke
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_scene_types.h"
-
-#include "BLI_utildefines.h"
-#include "BLI_math_vector.h"
-#include "BLI_task.h"
-
-#include "BKE_ccg.h"
-#include "BKE_lib_id.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
-#include "BKE_modifier.h"
-#include "BKE_multires.h"
-#include "BKE_subdiv.h"
-#include "BKE_subdiv_ccg.h"
-#include "BKE_subdiv_eval.h"
-#include "BKE_subdiv_foreach.h"
-#include "BKE_subdiv_mesh.h"
-
-#include "DEG_depsgraph_query.h"
-
-static void multires_reshape_init_mmd(MultiresModifierData *reshape_mmd,
- const MultiresModifierData *mmd)
-{
- *reshape_mmd = *mmd;
-}
-
-static void multires_reshape_init_mmd_top_level(MultiresModifierData *reshape_mmd,
- const MultiresModifierData *mmd)
-{
- *reshape_mmd = *mmd;
- reshape_mmd->lvl = reshape_mmd->totlvl;
-}
-
-/* =============================================================================
- * General reshape implementation, reused by all particular cases.
- */
-
-typedef struct MultiresReshapeContext {
- Subdiv *subdiv;
- const Mesh *coarse_mesh;
- MDisps *mdisps;
- GridPaintMask *grid_paint_mask;
- int top_grid_size;
- int top_level;
- /* Indexed by coarse face index, returns first ptex face index corresponding
- * to that coarse face. */
- int *face_ptex_offset;
-} MultiresReshapeContext;
-
-static void multires_reshape_allocate_displacement_grid(MDisps *displacement_grid, const int level)
-{
- const int grid_size = BKE_subdiv_grid_size_from_level(level);
- const int grid_area = grid_size * grid_size;
- float(*disps)[3] = MEM_calloc_arrayN(grid_area, 3 * sizeof(float), "multires disps");
- if (displacement_grid->disps != NULL) {
- MEM_freeN(displacement_grid->disps);
- }
- displacement_grid->disps = disps;
- displacement_grid->totdisp = grid_area;
- displacement_grid->level = level;
-}
-
-static void multires_reshape_ensure_displacement_grid(MDisps *displacement_grid, const int level)
-{
- if (displacement_grid->disps != NULL && displacement_grid->level == level) {
- return;
- }
- multires_reshape_allocate_displacement_grid(displacement_grid, level);
-}
-
-static void multires_reshape_ensure_displacement_grids(Mesh *mesh, const int grid_level)
-{
- const int num_grids = mesh->totloop;
- MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- multires_reshape_ensure_displacement_grid(&mdisps[grid_index], grid_level);
- }
-}
-
-static void multires_reshape_ensure_mask_grids(Mesh *mesh, const int grid_level)
-{
- GridPaintMask *grid_paint_masks = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK);
- if (grid_paint_masks == NULL) {
- return;
- }
- const int num_grids = mesh->totloop;
- const int grid_size = BKE_subdiv_grid_size_from_level(grid_level);
- const int grid_area = grid_size * grid_size;
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- GridPaintMask *grid_paint_mask = &grid_paint_masks[grid_index];
- if (grid_paint_mask->level == grid_level) {
- continue;
- }
- grid_paint_mask->level = grid_level;
- if (grid_paint_mask->data) {
- MEM_freeN(grid_paint_mask->data);
- }
- grid_paint_mask->data = MEM_calloc_arrayN(grid_area, sizeof(float), "gpm.data");
- }
-}
-
-static void multires_reshape_ensure_grids(Mesh *mesh, const int grid_level)
-{
- multires_reshape_ensure_displacement_grids(mesh, grid_level);
- multires_reshape_ensure_mask_grids(mesh, grid_level);
-}
-
-/* Convert normalized coordinate within a grid to a normalized coordinate within
- * a ptex face. */
-static void multires_reshape_corner_coord_to_ptex(const MPoly *coarse_poly,
- const int corner,
- const float corner_u,
- const float corner_v,
- float *r_ptex_face_u,
- float *r_ptex_face_v)
-{
- if (coarse_poly->totloop == 4) {
- float grid_u, grid_v;
- BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v);
- BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, r_ptex_face_u, r_ptex_face_v);
- }
- else {
- *r_ptex_face_u = corner_u;
- *r_ptex_face_v = corner_v;
- }
-}
-
-/* NOTE: The tangent vectors are measured in ptex face normalized coordinates,
- * which is different from grid tangent. */
-static void multires_reshape_sample_surface(Subdiv *subdiv,
- const MPoly *coarse_poly,
- const int corner,
- const float corner_u,
- const float corner_v,
- const int ptex_face_index,
- float r_P[3],
- float r_dPdu[3],
- float r_dPdv[3])
-{
- float ptex_face_u, ptex_face_v;
- multires_reshape_corner_coord_to_ptex(
- coarse_poly, corner, corner_u, corner_v, &ptex_face_u, &ptex_face_v);
- BKE_subdiv_eval_limit_point_and_derivatives(
- subdiv, ptex_face_index, ptex_face_u, ptex_face_v, r_P, r_dPdu, r_dPdv);
-}
-
-static void multires_reshape_tangent_matrix_for_corner(const MPoly *coarse_poly,
- const int coarse_corner,
- const float dPdu[3],
- const float dPdv[3],
- float r_tangent_matrix[3][3])
-{
- /* For a quad faces we would need to flip the tangent, since they will use
- * use different coordinates within displacement grid comparent to ptex
- * face. */
- const bool is_quad = (coarse_poly->totloop == 4);
- const int tangent_corner = is_quad ? coarse_corner : 0;
- BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, tangent_corner);
-}
-
-static void multires_reshape_vertex_from_final_data(MultiresReshapeContext *ctx,
- const int ptex_face_index,
- const float corner_u,
- const float corner_v,
- const int coarse_poly_index,
- const int coarse_corner,
- const float final_P[3],
- const float final_mask)
-{
- Subdiv *subdiv = ctx->subdiv;
- const int grid_size = ctx->top_grid_size;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- const int loop_index = coarse_poly->loopstart + coarse_corner;
- /* Evaluate limit surface. */
- float P[3], dPdu[3], dPdv[3];
- multires_reshape_sample_surface(
- subdiv, coarse_poly, coarse_corner, corner_u, corner_v, ptex_face_index, P, dPdu, dPdv);
- /* Construct tangent matrix which matches orientation of the current
- * displacement grid. */
- float tangent_matrix[3][3], inv_tangent_matrix[3][3];
- multires_reshape_tangent_matrix_for_corner(
- coarse_poly, coarse_corner, dPdu, dPdv, tangent_matrix);
- invert_m3_m3(inv_tangent_matrix, tangent_matrix);
- /* Convert object coordinate to a tangent space of displacement grid. */
- float D[3];
- sub_v3_v3v3(D, final_P, P);
- float tangent_D[3];
- mul_v3_m3v3(tangent_D, inv_tangent_matrix, D);
- /* Calculate index of element within the grid. */
- float grid_u, grid_v;
- BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v);
- const int grid_x = (grid_u * (grid_size - 1) + 0.5f);
- const int grid_y = (grid_v * (grid_size - 1) + 0.5f);
- const int index = grid_y * grid_size + grid_x;
- /* Write tangent displacement. */
- MDisps *displacement_grid = &ctx->mdisps[loop_index];
- copy_v3_v3(displacement_grid->disps[index], tangent_D);
- /* Write mask grid. */
- if (ctx->grid_paint_mask != NULL) {
- GridPaintMask *grid_paint_mask = &ctx->grid_paint_mask[loop_index];
- BLI_assert(grid_paint_mask->level == displacement_grid->level);
- grid_paint_mask->data[index] = final_mask;
- }
-}
-
-/* =============================================================================
- * Helpers to propagate displacement to higher levels.
- */
-
-typedef struct MultiresPropagateData {
- /* Number of displacement grids. */
- int num_grids;
- /* Resolution level up to which displacement is known. */
- int reshape_level;
- /* Resolution up to which propagation is happening, affecting all the
- * levels in [reshape_level + 1, top_level]. */
- int top_level;
- /* Grid sizes at the corresponding levels. */
- int reshape_grid_size;
- int top_grid_size;
- /* Keys to access CCG at different levels. */
- CCGKey reshape_level_key;
- CCGKey top_level_key;
- /* Original grid data, before any updates for reshape.
- * Contains data at the reshape_level resolution level. */
- CCGElem **orig_grids_data;
- /* Custom data layers from a coarse mesh. */
- MDisps *mdisps;
- GridPaintMask *grid_paint_mask;
-} MultiresPropagateData;
-
-static CCGElem **allocate_grids(CCGKey *key, int num_grids)
-{
- CCGElem **grids = MEM_calloc_arrayN(num_grids, sizeof(CCGElem *), "reshape grids*");
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- grids[grid_index] = MEM_calloc_arrayN(
- key->elem_size, key->grid_area, "reshape orig_grids_data elems");
- }
- return grids;
-}
-
-static void free_grids(CCGElem **grids, int num_grids)
-{
- if (grids == NULL) {
- return;
- }
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- MEM_freeN(grids[grid_index]);
- }
- MEM_freeN(grids);
-}
-
-/* Initialize element sizes and offsets. */
-static void multires_reshape_init_key_layers(CCGKey *key, const MultiresPropagateData *data)
-{
- key->elem_size = 3 * sizeof(float);
- if (data->grid_paint_mask != NULL) {
- key->mask_offset = 3 * sizeof(float);
- key->elem_size += sizeof(float);
- key->has_mask = true;
- }
- else {
- key->mask_offset = -1;
- key->has_mask = false;
- }
- /* We never have normals in original grids. */
- key->normal_offset = -1;
- key->has_normals = false;
-}
-
-/* Initialize key used to access reshape grids at given level. */
-static void multires_reshape_init_level_key(CCGKey *key,
- const MultiresPropagateData *data,
- const int level)
-{
- key->level = level;
- /* Init layers. */
- multires_reshape_init_key_layers(key, data);
- /* By default, only 3 floats for coordinate, */
- key->grid_size = BKE_subdiv_grid_size_from_level(key->level);
- key->grid_area = key->grid_size * key->grid_size;
- key->grid_bytes = key->elem_size * key->grid_area;
-}
-
-static void multires_reshape_store_original_grids(MultiresPropagateData *data)
-{
- const int num_grids = data->num_grids;
- /* Original data to be backed up. */
- const MDisps *mdisps = data->mdisps;
- const GridPaintMask *grid_paint_mask = data->grid_paint_mask;
- /* Allocate grids for backup. */
- CCGKey *orig_key = &data->reshape_level_key;
- CCGElem **orig_grids_data = allocate_grids(orig_key, num_grids);
- /* Fill in grids. */
- const int orig_grid_size = data->reshape_grid_size;
- const int top_grid_size = data->top_grid_size;
- const int skip = (top_grid_size - 1) / (orig_grid_size - 1);
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- CCGElem *orig_grid = orig_grids_data[grid_index];
- for (int y = 0; y < orig_grid_size; y++) {
- const int top_y = y * skip;
- for (int x = 0; x < orig_grid_size; x++) {
- const int top_x = x * skip;
- const int top_index = top_y * top_grid_size + top_x;
- memcpy(CCG_grid_elem_co(orig_key, orig_grid, x, y),
- mdisps[grid_index].disps[top_index],
- sizeof(float) * 3);
- if (orig_key->has_mask) {
- *CCG_grid_elem_mask(
- orig_key, orig_grid, x, y) = grid_paint_mask[grid_index].data[top_index];
- }
- }
- }
- }
- /* Store in the context. */
- data->orig_grids_data = orig_grids_data;
-}
-
-static void multires_reshape_propagate_prepare(MultiresPropagateData *data,
- Mesh *coarse_mesh,
- const int reshape_level,
- const int top_level)
-{
- BLI_assert(reshape_level <= top_level);
- memset(data, 0, sizeof(*data));
- data->num_grids = coarse_mesh->totloop;
- data->reshape_level = reshape_level;
- data->top_level = top_level;
- if (reshape_level == top_level) {
- /* Nothing to do, reshape will happen on the whole grid content. */
- return;
- }
- data->mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS);
- data->grid_paint_mask = CustomData_get_layer(&coarse_mesh->ldata, CD_GRID_PAINT_MASK);
- data->top_grid_size = BKE_subdiv_grid_size_from_level(top_level);
- data->reshape_grid_size = BKE_subdiv_grid_size_from_level(reshape_level);
- /* Initialize keys to access CCG at different levels. */
- multires_reshape_init_level_key(&data->reshape_level_key, data, data->reshape_level);
- multires_reshape_init_level_key(&data->top_level_key, data, data->top_level);
- /* Make a copy of grids before reshaping, so we can calculate deltas
- * later on. */
- multires_reshape_store_original_grids(data);
-}
-
-static void multires_reshape_propagate_prepare_from_mmd(MultiresPropagateData *data,
- struct Depsgraph *depsgraph,
- Object *object,
- const MultiresModifierData *mmd,
- const int top_level,
- const bool use_render_params)
-{
- /* TODO(sergey): Find mode reliable way of getting current level. */
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Mesh *mesh = object->data;
- const int level = multires_get_level(scene_eval, object, mmd, use_render_params, true);
- multires_reshape_propagate_prepare(data, mesh, level, top_level);
-}
-
-/* Calculate delta of changed reshape level data layers. Delta goes to a
- * grids at top level (meaning, the result grids are only partially filled
- * in). */
-static void multires_reshape_calculate_delta(MultiresPropagateData *data,
- CCGElem **delta_grids_data)
-{
- const int num_grids = data->num_grids;
- /* At this point those custom data layers has updated data for the
- * level we are propagating from. */
- const MDisps *mdisps = data->mdisps;
- const GridPaintMask *grid_paint_mask = data->grid_paint_mask;
- CCGKey *reshape_key = &data->reshape_level_key;
- CCGKey *delta_level_key = &data->top_level_key;
- /* Calculate delta. */
- const int top_grid_size = data->top_grid_size;
- const int reshape_grid_size = data->reshape_grid_size;
- const int delta_grid_size = data->top_grid_size;
- const int skip = (top_grid_size - 1) / (reshape_grid_size - 1);
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- /*const*/ CCGElem *orig_grid = data->orig_grids_data[grid_index];
- CCGElem *delta_grid = delta_grids_data[grid_index];
- for (int y = 0; y < reshape_grid_size; y++) {
- const int top_y = y * skip;
- for (int x = 0; x < reshape_grid_size; x++) {
- const int top_x = x * skip;
- const int top_index = top_y * delta_grid_size + top_x;
- sub_v3_v3v3(CCG_grid_elem_co(delta_level_key, delta_grid, top_x, top_y),
- mdisps[grid_index].disps[top_index],
- CCG_grid_elem_co(reshape_key, orig_grid, x, y));
- if (delta_level_key->has_mask) {
- const float old_mask_value = *CCG_grid_elem_mask(reshape_key, orig_grid, x, y);
- const float new_mask_value = grid_paint_mask[grid_index].data[top_index];
- *CCG_grid_elem_mask(delta_level_key, delta_grid, top_x, top_y) = new_mask_value -
- old_mask_value;
- }
- }
- }
- }
-}
-
-/* Makes it so delta is propagated onto all the higher levels, but is also
- * that this delta is smoothed in a way that it does not cause artifacts on
- * boundaries. */
-
-typedef struct MultiresPropagateCornerData {
- float coord_delta[3];
- float mask_delta;
-} MultiresPropagateCornerData;
-
-BLI_INLINE void multires_reshape_propagate_init_patch_corners(
- MultiresPropagateData *data,
- CCGElem *delta_grid,
- const int patch_x,
- const int patch_y,
- MultiresPropagateCornerData r_corners[4])
-{
- CCGKey *delta_level_key = &data->top_level_key;
- const int orig_grid_size = data->reshape_grid_size;
- const int top_grid_size = data->top_grid_size;
- const int skip = (top_grid_size - 1) / (orig_grid_size - 1);
- const int x = patch_x * skip;
- const int y = patch_y * skip;
- /* Store coordinate deltas. */
- copy_v3_v3(r_corners[0].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x, y));
- copy_v3_v3(r_corners[1].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x + skip, y));
- copy_v3_v3(r_corners[2].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x, y + skip));
- copy_v3_v3(r_corners[3].coord_delta,
- CCG_grid_elem_co(delta_level_key, delta_grid, x + skip, y + skip));
- if (delta_level_key->has_mask) {
- r_corners[0].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x, y);
- r_corners[1].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x + skip, y);
- r_corners[2].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x, y + skip);
- r_corners[3].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x + skip, y + skip);
- }
-}
-
-BLI_INLINE void multires_reshape_propagate_interpolate_coord(
- float delta[3], const MultiresPropagateCornerData corners[4], const float weights[4])
-{
- interp_v3_v3v3v3v3(delta,
- corners[0].coord_delta,
- corners[1].coord_delta,
- corners[2].coord_delta,
- corners[3].coord_delta,
- weights);
-}
-
-BLI_INLINE float multires_reshape_propagate_interpolate_mask(
- const MultiresPropagateCornerData corners[4], const float weights[4])
-{
- return corners[0].mask_delta * weights[0] + corners[1].mask_delta * weights[1] +
- corners[2].mask_delta * weights[2] + corners[3].mask_delta * weights[3];
-}
-
-BLI_INLINE void multires_reshape_propagate_and_smooth_delta_grid_patch(MultiresPropagateData *data,
- CCGElem *delta_grid,
- const int patch_x,
- const int patch_y)
-{
- CCGKey *delta_level_key = &data->top_level_key;
- const int orig_grid_size = data->reshape_grid_size;
- const int top_grid_size = data->top_grid_size;
- const int skip = (top_grid_size - 1) / (orig_grid_size - 1);
- const float skip_inv = 1.0f / (float)skip;
- MultiresPropagateCornerData corners[4];
- multires_reshape_propagate_init_patch_corners(data, delta_grid, patch_x, patch_y, corners);
- const int start_x = patch_x * skip;
- const int start_y = patch_y * skip;
- for (int y = 0; y <= skip; y++) {
- const float v = (float)y * skip_inv;
- const int final_y = start_y + y;
- for (int x = 0; x <= skip; x++) {
- const float u = (float)x * skip_inv;
- const int final_x = start_x + x;
- const float linear_weights[4] = {
- (1.0f - u) * (1.0f - v), u * (1.0f - v), (1.0f - u) * v, u * v};
- multires_reshape_propagate_interpolate_coord(
- CCG_grid_elem_co(delta_level_key, delta_grid, final_x, final_y),
- corners,
- linear_weights);
- if (delta_level_key->has_mask) {
- float *mask = CCG_grid_elem_mask(delta_level_key, delta_grid, final_x, final_y);
- *mask = multires_reshape_propagate_interpolate_mask(corners, linear_weights);
- }
- }
- }
-}
-
-BLI_INLINE void multires_reshape_propagate_and_smooth_delta_grid(MultiresPropagateData *data,
- CCGElem *delta_grid)
-{
- const int orig_grid_size = data->reshape_grid_size;
- for (int patch_y = 0; patch_y < orig_grid_size - 1; patch_y++) {
- for (int patch_x = 0; patch_x < orig_grid_size - 1; patch_x++) {
- multires_reshape_propagate_and_smooth_delta_grid_patch(data, delta_grid, patch_x, patch_y);
- }
- }
-}
-
-/* Entry point to propagate+smooth. */
-static void multires_reshape_propagate_and_smooth_delta(MultiresPropagateData *data,
- CCGElem **delta_grids_data)
-{
- const int num_grids = data->num_grids;
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- CCGElem *delta_grid = delta_grids_data[grid_index];
- multires_reshape_propagate_and_smooth_delta_grid(data, delta_grid);
- }
-}
-
-/* Apply smoothed deltas on the actual data layers. */
-static void multires_reshape_propagate_apply_delta(MultiresPropagateData *data,
- CCGElem **delta_grids_data)
-{
- const int num_grids = data->num_grids;
- /* At this point those custom data layers has updated data for the
- * level we are propagating from. */
- MDisps *mdisps = data->mdisps;
- GridPaintMask *grid_paint_mask = data->grid_paint_mask;
- CCGKey *orig_key = &data->reshape_level_key;
- CCGKey *delta_level_key = &data->top_level_key;
- CCGElem **orig_grids_data = data->orig_grids_data;
- const int orig_grid_size = data->reshape_grid_size;
- const int top_grid_size = data->top_grid_size;
- const int skip = (top_grid_size - 1) / (orig_grid_size - 1);
- /* Restore grid values at the reshape level. Those values are to be changed
- * to the accommodate for the smooth delta. */
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- CCGElem *orig_grid = orig_grids_data[grid_index];
- for (int y = 0; y < orig_grid_size; y++) {
- const int top_y = y * skip;
- for (int x = 0; x < orig_grid_size; x++) {
- const int top_x = x * skip;
- const int top_index = top_y * top_grid_size + top_x;
- copy_v3_v3(mdisps[grid_index].disps[top_index],
- CCG_grid_elem_co(orig_key, orig_grid, x, y));
- if (grid_paint_mask != NULL) {
- grid_paint_mask[grid_index].data[top_index] = *CCG_grid_elem_mask(
- orig_key, orig_grid, x, y);
- }
- }
- }
- }
- /* Add smoothed delta to all the levels. */
- for (int grid_index = 0; grid_index < num_grids; grid_index++) {
- CCGElem *delta_grid = delta_grids_data[grid_index];
- for (int y = 0; y < top_grid_size; y++) {
- for (int x = 0; x < top_grid_size; x++) {
- const int top_index = y * top_grid_size + x;
- add_v3_v3(mdisps[grid_index].disps[top_index],
- CCG_grid_elem_co(delta_level_key, delta_grid, x, y));
- if (delta_level_key->has_mask) {
- grid_paint_mask[grid_index].data[top_index] += *CCG_grid_elem_mask(
- delta_level_key, delta_grid, x, y);
- }
- }
- }
- }
-}
-
-static void multires_reshape_propagate(MultiresPropagateData *data)
-{
- if (data->reshape_level == data->top_level) {
- return;
- }
- const int num_grids = data->num_grids;
- /* Calculate delta made at the reshape level. */
- CCGKey *delta_level_key = &data->top_level_key;
- CCGElem **delta_grids_data = allocate_grids(delta_level_key, num_grids);
- multires_reshape_calculate_delta(data, delta_grids_data);
- /* Propagate deltas to the higher levels. */
- multires_reshape_propagate_and_smooth_delta(data, delta_grids_data);
- /* Finally, apply smoothed deltas. */
- multires_reshape_propagate_apply_delta(data, delta_grids_data);
- /* Cleanup. */
- free_grids(delta_grids_data, num_grids);
-}
-
-static void multires_reshape_propagate_free(MultiresPropagateData *data)
-{
- free_grids(data->orig_grids_data, data->num_grids);
-}
-
-/* =============================================================================
- * Reshape from deformed vertex coordinates.
- */
-
-typedef struct MultiresReshapeFromDeformedVertsContext {
- MultiresReshapeContext reshape_ctx;
- const float (*deformed_verts)[3];
- int num_deformed_verts;
-} MultiresReshapeFromDeformedVertsContext;
-
-static bool multires_reshape_topology_info(const SubdivForeachContext *foreach_context,
- const int num_vertices,
- const int UNUSED(num_edges),
- const int UNUSED(num_loops),
- const int UNUSED(num_polygons))
-{
- MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
- if (num_vertices != ctx->num_deformed_verts) {
- return false;
- }
- return true;
-}
-
-/* Will run reshaping for all grid elements which are adjacent to the given
- * one. This is the way to ensure continuity of displacement stored in the
- * grids across the inner boundaries of the grids. */
-static void multires_reshape_neighour_boundary_vertices(MultiresReshapeContext *ctx,
- const int UNUSED(ptex_face_index),
- const float corner_u,
- const float corner_v,
- const int coarse_poly_index,
- const int coarse_corner,
- const float final_P[3],
- const float final_mask)
-{
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- const int num_corners = coarse_poly->totloop;
- const int start_ptex_face_index = ctx->face_ptex_offset[coarse_poly_index];
- const bool is_quad = (coarse_poly->totloop == 4);
- if (corner_u == 1.0f && corner_v == 1.0f) {
- for (int current_corner = 0; current_corner < num_corners; current_corner++) {
- if (current_corner == coarse_corner) {
- continue;
- }
- const int current_ptex_face_index = is_quad ? start_ptex_face_index :
- start_ptex_face_index + current_corner;
- multires_reshape_vertex_from_final_data(ctx,
- current_ptex_face_index,
- 1.0f,
- 1.0f,
- coarse_poly_index,
- current_corner,
- final_P,
- final_mask);
- }
- }
- else if (corner_u == 1.0f) {
- const float next_corner_index = (coarse_corner + 1) % num_corners;
- const float next_corner_u = corner_v;
- const float next_corner_v = 1.0f;
- const int next_ptex_face_index = is_quad ? start_ptex_face_index :
- start_ptex_face_index + next_corner_index;
- multires_reshape_vertex_from_final_data(ctx,
- next_ptex_face_index,
- next_corner_u,
- next_corner_v,
- coarse_poly_index,
- next_corner_index,
- final_P,
- final_mask);
- }
- else if (corner_v == 1.0f) {
- const float prev_corner_index = (coarse_corner + num_corners - 1) % num_corners;
- const float prev_corner_u = 1.0f;
- const float prev_corner_v = corner_u;
- const int prev_ptex_face_index = is_quad ? start_ptex_face_index :
- start_ptex_face_index + prev_corner_index;
- multires_reshape_vertex_from_final_data(ctx,
- prev_ptex_face_index,
- prev_corner_u,
- prev_corner_v,
- coarse_poly_index,
- prev_corner_index,
- final_P,
- final_mask);
- }
-}
-
-static void multires_reshape_vertex(MultiresReshapeFromDeformedVertsContext *ctx,
- const int ptex_face_index,
- const float u,
- const float v,
- const int coarse_poly_index,
- const int coarse_corner,
- const int subdiv_vertex_index)
-{
- const float *final_P = ctx->deformed_verts[subdiv_vertex_index];
- const Mesh *coarse_mesh = ctx->reshape_ctx.coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- const bool is_quad = (coarse_poly->totloop == 4);
- float corner_u, corner_v;
- int actual_coarse_corner;
- if (is_quad) {
- actual_coarse_corner = BKE_subdiv_rotate_quad_to_corner(u, v, &corner_u, &corner_v);
- }
- else {
- actual_coarse_corner = coarse_corner;
- corner_u = u;
- corner_v = v;
- }
- multires_reshape_vertex_from_final_data(&ctx->reshape_ctx,
- ptex_face_index,
- corner_u,
- corner_v,
- coarse_poly_index,
- actual_coarse_corner,
- final_P,
- 0.0f);
- multires_reshape_neighour_boundary_vertices(&ctx->reshape_ctx,
- ptex_face_index,
- corner_u,
- corner_v,
- coarse_poly_index,
- actual_coarse_corner,
- final_P,
- 0.0f);
-}
-
-static void multires_reshape_vertex_inner(const SubdivForeachContext *foreach_context,
- void *UNUSED(tls_v),
- const int ptex_face_index,
- const float u,
- const float v,
- const int coarse_poly_index,
- const int coarse_corner,
- const int subdiv_vertex_index)
-{
- MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
- multires_reshape_vertex(
- ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index);
-}
-
-static void multires_reshape_vertex_every_corner(
- const struct SubdivForeachContext *foreach_context,
- void *UNUSED(tls_v),
- const int ptex_face_index,
- const float u,
- const float v,
- const int UNUSED(coarse_vertex_index),
- const int coarse_poly_index,
- const int coarse_corner,
- const int subdiv_vertex_index)
-{
- MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
- multires_reshape_vertex(
- ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index);
-}
-
-static void multires_reshape_vertex_every_edge(const struct SubdivForeachContext *foreach_context,
- void *UNUSED(tls_v),
- const int ptex_face_index,
- const float u,
- const float v,
- const int UNUSED(coarse_edge_index),
- const int coarse_poly_index,
- const int coarse_corner,
- const int subdiv_vertex_index)
-{
- MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
- multires_reshape_vertex(
- ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index);
-}
-
-static Subdiv *multires_create_subdiv_for_reshape(struct Depsgraph *depsgraph,
- /*const*/ Object *object,
- const MultiresModifierData *mmd)
-{
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
- Mesh *deformed_mesh = mesh_get_eval_deform(
- depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
- SubdivSettings subdiv_settings;
- BKE_multires_subdiv_settings_init(&subdiv_settings, mmd);
- Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, deformed_mesh);
- if (!BKE_subdiv_eval_update_from_mesh(subdiv, deformed_mesh, NULL)) {
- BKE_subdiv_free(subdiv);
- return NULL;
- }
- return subdiv;
-}
-
-static bool multires_reshape_from_vertcos(struct Depsgraph *depsgraph,
- Object *object,
- const MultiresModifierData *mmd,
- const float (*deformed_verts)[3],
- const int num_deformed_verts,
- const bool use_render_params)
-{
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Mesh *coarse_mesh = object->data;
- MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS);
- /* Pick maximum between multires level and displacement level.
- * This is because mesh can be used by objects with multires at different
- * levels.
- *
- * TODO(sergey): At this point it should be possible to always use
- * mdisps->level. */
- const int top_level = max_ii(mmd->totlvl, mdisps->level);
- /* Make sure displacement grids are ready. */
- multires_reshape_ensure_grids(coarse_mesh, top_level);
- /* Initialize subdivision surface. */
- Subdiv *subdiv = multires_create_subdiv_for_reshape(depsgraph, object, mmd);
- if (subdiv == NULL) {
- return false;
- }
- /* Construct context. */
- MultiresReshapeFromDeformedVertsContext reshape_deformed_verts_ctx = {
- .reshape_ctx =
- {
- .subdiv = subdiv,
- .coarse_mesh = coarse_mesh,
- .mdisps = mdisps,
- .grid_paint_mask = NULL,
- .top_grid_size = BKE_subdiv_grid_size_from_level(top_level),
- .top_level = top_level,
- .face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv),
- },
- .deformed_verts = deformed_verts,
- .num_deformed_verts = num_deformed_verts,
- };
- SubdivForeachContext foreach_context = {
- .topology_info = multires_reshape_topology_info,
- .vertex_inner = multires_reshape_vertex_inner,
- .vertex_every_edge = multires_reshape_vertex_every_edge,
- .vertex_every_corner = multires_reshape_vertex_every_corner,
- .user_data = &reshape_deformed_verts_ctx,
- };
- /* Initialize mesh rasterization settings. */
- SubdivToMeshSettings mesh_settings;
- BKE_multires_subdiv_mesh_settings_init(
- &mesh_settings, scene_eval, object, mmd, use_render_params, true);
- /* Initialize propagation to higher levels. */
- MultiresPropagateData propagate_data;
- multires_reshape_propagate_prepare_from_mmd(
- &propagate_data, depsgraph, object, mmd, top_level, use_render_params);
- /* Run all the callbacks. */
- BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, &mesh_settings, coarse_mesh);
- BKE_subdiv_free(subdiv);
- /* Update higher levels if needed. */
- multires_reshape_propagate(&propagate_data);
- multires_reshape_propagate_free(&propagate_data);
- return true;
-}
-
-/* =============================================================================
- * Reshape from object.
- */
-
-/* Returns truth on success, false otherwise.
- *
- * This function might fail in cases like source and destination not having
- * matched amount of vertices. */
-bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph,
- MultiresModifierData *mmd,
- Object *dst,
- Object *src)
-{
- /* Would be cool to support this eventually, but it is very tricky to match
- * vertices order even for meshes, when mixing meshes and other objects it's
- * even more tricky. */
- if (src->type != OB_MESH) {
- return false;
- }
- MultiresModifierData reshape_mmd;
- multires_reshape_init_mmd(&reshape_mmd, mmd);
- /* Get evaluated vertices locations to reshape to. */
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- Object *src_eval = DEG_get_evaluated_object(depsgraph, src);
- Mesh *src_mesh_eval = mesh_get_eval_final(depsgraph, scene_eval, src_eval, &CD_MASK_BAREMESH);
- int num_deformed_verts;
- float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(src_mesh_eval, &num_deformed_verts);
- bool result = multires_reshape_from_vertcos(
- depsgraph, dst, &reshape_mmd, deformed_verts, num_deformed_verts, false);
- MEM_freeN(deformed_verts);
- return result;
-}
-
-/* =============================================================================
- * Reshape from modifier.
- */
-
-bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph,
- MultiresModifierData *mmd,
- Object *object,
- ModifierData *md)
-{
- MultiresModifierData highest_mmd;
- /* It is possible that the current subdivision level of multires is lower
- * that it's maximum possible one (i.e., viewport is set to a lower level
- * for the performance purposes). But even then, we want all the multires
- * levels to be reshaped. Most accurate way to do so is to ignore all
- * simplifications and calculate deformation modifier for the highest
- * possible multires level.
- * Alternative would be propagate displacement from current level to a
- * higher ones, but that is likely to cause artifacts. */
- multires_reshape_init_mmd_top_level(&highest_mmd, mmd);
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- /* Perform sanity checks and early output. */
- if (multires_get_level(scene_eval, object, &highest_mmd, false, true) == 0) {
- return false;
- }
- /* Create mesh for the multires, ignoring any further modifiers (leading
- * deformation modifiers will be applied though). */
- Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, scene_eval, &highest_mmd, object);
- int num_deformed_verts;
- float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts);
- /* Apply deformation modifier on the multires, */
- const ModifierEvalContext modifier_ctx = {
- .depsgraph = depsgraph,
- .object = object,
- .flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY,
- };
- modwrap_deformVerts(md, &modifier_ctx, multires_mesh, deformed_verts, multires_mesh->totvert);
- BKE_id_free(NULL, multires_mesh);
- /* Reshaping */
- bool result = multires_reshape_from_vertcos(
- depsgraph, object, &highest_mmd, deformed_verts, num_deformed_verts, false);
- /* Cleanup */
- MEM_freeN(deformed_verts);
- return result;
-}
-
-/* =============================================================================
- * Reshape from grids.
- */
-
-typedef struct ReshapeFromCCGTaskData {
- MultiresReshapeContext reshape_ctx;
- const CCGKey *key;
- /*const*/ CCGElem **grids;
-} ReshapeFromCCGTaskData;
-
-static void reshape_from_ccg_task(void *__restrict userdata,
- const int coarse_poly_index,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- ReshapeFromCCGTaskData *data = userdata;
- const CCGKey *key = data->key;
- /*const*/ CCGElem **grids = data->grids;
- const Mesh *coarse_mesh = data->reshape_ctx.coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- const int key_grid_size = key->grid_size;
- const int key_grid_size_1 = key_grid_size - 1;
- const int resolution = key_grid_size;
- const float resolution_1_inv = 1.0f / (float)(resolution - 1);
- const int start_ptex_face_index = data->reshape_ctx.face_ptex_offset[coarse_poly_index];
- const bool is_quad = (coarse_poly->totloop == 4);
- for (int corner = 0; corner < coarse_poly->totloop; corner++) {
- for (int y = 0; y < resolution; y++) {
- const float corner_v = y * resolution_1_inv;
- for (int x = 0; x < resolution; x++) {
- const float corner_u = x * resolution_1_inv;
- /* Quad faces consists of a single ptex face. */
- const int ptex_face_index = is_quad ? start_ptex_face_index :
- start_ptex_face_index + corner;
- float grid_u, grid_v;
- BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v);
- /*const*/ CCGElem *grid = grids[coarse_poly->loopstart + corner];
- /*const*/ CCGElem *grid_element = CCG_grid_elem(
- key, grid, key_grid_size_1 * grid_u, key_grid_size_1 * grid_v);
- const float *final_P = CCG_elem_co(key, grid_element);
- float final_mask = 0.0f;
- if (key->has_mask) {
- final_mask = *CCG_elem_mask(key, grid_element);
- }
- multires_reshape_vertex_from_final_data(&data->reshape_ctx,
- ptex_face_index,
- corner_u,
- corner_v,
- coarse_poly_index,
- corner,
- final_P,
- final_mask);
- }
- }
- }
-}
-
-bool multiresModifier_reshapeFromCCG(const int tot_level, Mesh *coarse_mesh, SubdivCCG *subdiv_ccg)
-{
- CCGKey key;
- BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
- /* Sanity checks. */
- if (coarse_mesh->totloop != subdiv_ccg->num_grids) {
- /* Grids are supposed to eb created for each face-cornder (aka loop). */
- return false;
- }
- MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS);
- if (mdisps == NULL) {
- /* Multires displacement has been removed before current changes were
- * applies to all the levels. */
- return false;
- }
- GridPaintMask *grid_paint_mask = CustomData_get_layer(&coarse_mesh->ldata, CD_GRID_PAINT_MASK);
- Subdiv *subdiv = subdiv_ccg->subdiv;
- /* Pick maximum between multires level and displacement level.
- * This is because mesh can be used by objects with multires at different
- * levels.
- *
- * TODO(sergey): At this point it should be possible to always use
- * mdisps->level. */
- const int top_level = max_ii(tot_level, mdisps->level);
- /* Make sure displacement grids are ready. */
- multires_reshape_ensure_grids(coarse_mesh, top_level);
- /* Construct context. */
- ReshapeFromCCGTaskData data = {
- .reshape_ctx =
- {
- .subdiv = subdiv,
- .coarse_mesh = coarse_mesh,
- .mdisps = mdisps,
- .grid_paint_mask = grid_paint_mask,
- .top_grid_size = BKE_subdiv_grid_size_from_level(top_level),
- .top_level = top_level,
- .face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv),
- },
- .key = &key,
- .grids = subdiv_ccg->grids,
- };
- /* Initialize propagation to higher levels. */
- MultiresPropagateData propagate_data;
- multires_reshape_propagate_prepare(&propagate_data, coarse_mesh, key.level, top_level);
- /* Threaded grids iteration. */
- TaskParallelSettings parallel_range_settings;
- BLI_parallel_range_settings_defaults(&parallel_range_settings);
- BLI_task_parallel_range(
- 0, coarse_mesh->totpoly, &data, reshape_from_ccg_task, &parallel_range_settings);
- /* Update higher levels if needed. */
- multires_reshape_propagate(&propagate_data);
- multires_reshape_propagate_free(&propagate_data);
- return true;
-}
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
new file mode 100644
index 00000000000..0a9a17d7bd8
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -0,0 +1,1064 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math_vector.h"
+#include "BLI_task.h"
+
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_eval.h"
+#include "BKE_subdiv_foreach.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "opensubdiv_converter_capi.h"
+#include "opensubdiv_evaluator_capi.h"
+#include "opensubdiv_topology_refiner_capi.h"
+
+#include "subdiv_converter.h"
+
+typedef struct SurfacePoint {
+ float P[3];
+ float tangent_matrix[3][3];
+} SurfacePoint;
+
+typedef struct SurfaceGrid {
+ SurfacePoint *points;
+} SurfaceGrid;
+
+typedef struct Vertex {
+ /* All grid coordinates which the vertex corresponding to.
+ * For a vertices which are created from inner points of grids there is always one coordinate. */
+ int num_grid_coords;
+ GridCoord *grid_coords;
+
+ bool is_infinite_sharp;
+} Vertex;
+
+typedef struct Corner {
+ const Vertex *vertex;
+ int grid_index;
+} Corner;
+
+typedef struct Dace {
+ int start_corner_index;
+ int num_corners;
+} Dace;
+
+typedef struct MultiresReshapeSmoothContext {
+ const MultiresReshapeContext *reshape_context;
+
+ // Geometry at a reshape multires level.
+ struct {
+ int num_vertices;
+ Vertex *vertices;
+
+ int num_corners;
+ Corner *corners;
+
+ int num_faces;
+ Dace *faces;
+ } geometry;
+
+ /* Subdivision surface created for geometry at a reshape level. */
+ Subdiv *reshape_subdiv;
+
+ SurfaceGrid *base_surface_grids;
+} MultiresReshapeSmoothContext;
+
+/* ================================================================================================
+ * Masks.
+ */
+
+/* Interpolate mask grid at a reshape level.
+ * Will return 0 if there is no masks custom data layer. */
+static float interpolate_masks_grid(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const GridCoord *grid_coord)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ if (reshape_context->grid_paint_masks == NULL) {
+ return 0.0f;
+ }
+
+ const GridPaintMask *grid = &reshape_context->orig.grid_paint_masks[grid_coord->grid_index];
+ const int grid_size = BKE_subdiv_grid_size_from_level(grid->level);
+ const int grid_size_1 = grid_size - 1;
+ const float grid_size_1_inv = 1.0f / (float)(grid_size_1);
+
+ const float x_f = grid_coord->u * grid_size_1;
+ const float y_f = grid_coord->v * grid_size_1;
+
+ const int x_i = x_f;
+ const int y_i = y_f;
+ const int x_n_i = (x_i == grid_size - 1) ? (x_i) : (x_i + 1);
+ const int y_n_i = (y_i == grid_size - 1) ? (y_i) : (y_i + 1);
+
+ const int corners[4][2] = {{x_i, y_i}, {x_n_i, y_i}, {x_n_i, y_n_i}, {x_i, y_n_i}};
+ float mask_elements[4];
+ for (int i = 0; i < 4; ++i) {
+ GridCoord corner_grid_coord;
+ corner_grid_coord.grid_index = grid_coord->grid_index;
+ corner_grid_coord.u = corners[i][0] * grid_size_1_inv;
+ corner_grid_coord.v = corners[i][1] * grid_size_1_inv;
+
+ ReshapeConstGridElement element = multires_reshape_orig_grid_element_for_grid_coord(
+ reshape_context, &corner_grid_coord);
+ mask_elements[i] = element.mask;
+ }
+
+ const float u = x_f - x_i;
+ const float v = y_f - y_i;
+ const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), (1.0f - u) * v, u * v};
+
+ return mask_elements[0] * weights[0] + mask_elements[1] * weights[1] +
+ mask_elements[2] * weights[2] + mask_elements[3] * weights[3];
+}
+
+/* ================================================================================================
+ * Surface.
+ */
+
+static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int num_grids = reshape_context->num_grids;
+ const int grid_size = reshape_context->top.grid_size;
+ const int grid_area = grid_size * grid_size;
+
+ SurfaceGrid *surface_grid = MEM_malloc_arrayN(num_grids, sizeof(SurfaceGrid), "delta grids");
+
+ for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
+ surface_grid[grid_index].points = MEM_calloc_arrayN(
+ sizeof(SurfacePoint), grid_area, "delta grid dispalcement");
+ }
+
+ reshape_smooth_context->base_surface_grids = surface_grid;
+}
+
+static void base_surface_grids_free(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->base_surface_grids == NULL) {
+ return;
+ }
+
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int num_grids = reshape_context->num_grids;
+ for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
+ MEM_freeN(reshape_smooth_context->base_surface_grids[grid_index].points);
+ }
+ MEM_freeN(reshape_smooth_context->base_surface_grids);
+}
+
+static SurfacePoint *base_surface_grids_read(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const GridCoord *grid_coord)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int grid_index = grid_coord->grid_index;
+ const int grid_size = reshape_context->top.grid_size;
+ const int grid_x = lround(grid_coord->u * (grid_size - 1));
+ const int grid_y = lround(grid_coord->v * (grid_size - 1));
+ const int grid_element_index = grid_y * grid_size + grid_x;
+
+ SurfaceGrid *surface_grid = &reshape_smooth_context->base_surface_grids[grid_index];
+ return &surface_grid->points[grid_element_index];
+}
+
+static void base_surface_grids_write(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const GridCoord *grid_coord,
+ float P[3],
+ float tangent_matrix[3][3])
+{
+ SurfacePoint *point = base_surface_grids_read(reshape_smooth_context, grid_coord);
+ copy_v3_v3(point->P, P);
+ copy_m3_m3(point->tangent_matrix, tangent_matrix);
+}
+
+/* ================================================================================================
+ * Evaluation of subdivision surface at a reshape level.
+ */
+
+typedef void (*ForeachTopLevelGridCoordCallback)(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *userdata_v);
+
+typedef struct ForeachTopLevelGridCoordTaskData {
+ const MultiresReshapeSmoothContext *reshape_smooth_context;
+
+ int inner_grid_size;
+ float inner_grid_size_1_inv;
+
+ ForeachTopLevelGridCoordCallback callback;
+ void *callback_userdata_v;
+} ForeachHighLevelCoordTaskData;
+
+/* Find grid index which given face was created for. */
+static int get_face_grid_index(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Dace *face)
+{
+ const Corner *first_corner = &reshape_smooth_context->geometry.corners[face->start_corner_index];
+ const int grid_index = first_corner->grid_index;
+
+#ifndef NDEBUG
+ for (int face_corner = 0; face_corner < face->num_corners; ++face_corner) {
+ const int corner_index = face->start_corner_index + face_corner;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ BLI_assert(corner->grid_index == grid_index);
+ }
+#endif
+
+ return grid_index;
+}
+
+static GridCoord *vertex_grid_coord_with_grid_index(const Vertex *vertex, const int grid_index)
+{
+ for (int i = 0; i < vertex->num_grid_coords; ++i) {
+ if (vertex->grid_coords[i].grid_index == grid_index) {
+ return &vertex->grid_coords[i];
+ }
+ }
+ return NULL;
+}
+
+/* Get grid coordinates which correspond to corners of the given face.
+ * All the grid coordinates will be from the same grid index. */
+static void grid_coords_from_face_vertices(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Dace *face,
+ const GridCoord *grid_coords[])
+{
+ BLI_assert(face->num_corners == 4);
+
+ const int grid_index = get_face_grid_index(reshape_smooth_context, face);
+ BLI_assert(grid_index != -1);
+
+ for (int i = 0; i < face->num_corners; ++i) {
+ const int corner_index = face->start_corner_index + i;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ grid_coords[i] = vertex_grid_coord_with_grid_index(corner->vertex, grid_index);
+ BLI_assert(grid_coords[i] != NULL);
+ }
+}
+
+static float lerp(float t, float a, float b)
+{
+ return (a + t * (b - a));
+}
+
+static void interpolate_grid_coord(GridCoord *result,
+ const GridCoord *face_grid_coords[4],
+ const float u,
+ const float v)
+{
+ /*
+ * v
+ * ^
+ * | (3) -------- (2)
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * | (0) -------- (1)
+ * *--------------------------> u
+ */
+
+ const float u01 = lerp(u, face_grid_coords[0]->u, face_grid_coords[1]->u);
+ const float u32 = lerp(u, face_grid_coords[3]->u, face_grid_coords[2]->u);
+
+ const float v03 = lerp(v, face_grid_coords[0]->v, face_grid_coords[3]->v);
+ const float v12 = lerp(v, face_grid_coords[1]->v, face_grid_coords[2]->v);
+
+ result->grid_index = face_grid_coords[0]->grid_index;
+ result->u = lerp(v, u01, u32);
+ result->v = lerp(u, v03, v12);
+}
+
+static void foreach_toplevel_grid_coord_task(void *__restrict userdata_v,
+ const int face_index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ ForeachHighLevelCoordTaskData *data = userdata_v;
+
+ const MultiresReshapeSmoothContext *reshape_smooth_context = data->reshape_smooth_context;
+ const int inner_grid_size = data->inner_grid_size;
+ const float inner_grid_size_1_inv = data->inner_grid_size_1_inv;
+
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+ const GridCoord *face_grid_coords[4];
+ grid_coords_from_face_vertices(reshape_smooth_context, face, face_grid_coords);
+
+ for (int y = 0; y < inner_grid_size; ++y) {
+ const float ptex_v = (float)y * inner_grid_size_1_inv;
+ for (int x = 0; x < inner_grid_size; ++x) {
+ const float ptex_u = (float)x * inner_grid_size_1_inv;
+
+ PTexCoord ptex_coord;
+ ptex_coord.ptex_face_index = face_index;
+ ptex_coord.u = ptex_u;
+ ptex_coord.v = ptex_v;
+
+ GridCoord grid_coord;
+ interpolate_grid_coord(&grid_coord, face_grid_coords, ptex_u, ptex_v);
+
+ data->callback(reshape_smooth_context, &ptex_coord, &grid_coord, data->callback_userdata_v);
+ }
+ }
+}
+
+static void foreach_toplevel_grid_coord(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ ForeachTopLevelGridCoordCallback callback,
+ void *callback_userdata_v)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+
+ ForeachHighLevelCoordTaskData data;
+ data.reshape_smooth_context = reshape_smooth_context;
+ data.inner_grid_size = (1 << level_difference) + 1;
+ data.inner_grid_size_1_inv = 1.0f / (float)(data.inner_grid_size - 1);
+ data.callback = callback;
+ data.callback_userdata_v = callback_userdata_v;
+
+ TaskParallelSettings parallel_range_settings;
+ BLI_parallel_range_settings_defaults(&parallel_range_settings);
+ parallel_range_settings.min_iter_per_thread = 1;
+
+ const int num_faces = reshape_smooth_context->geometry.num_faces;
+ BLI_task_parallel_range(
+ 0, num_faces, &data, foreach_toplevel_grid_coord_task, &parallel_range_settings);
+}
+
+/* ================================================================================================
+ * Generation of a topology information for OpenSubdiv converter.
+ *
+ * Calculates vertices, their coordinates in the original grids, and connections of them so then
+ * it's easy to create OpenSubdiv's topology refiner. */
+
+static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
+ const MultiresReshapeContext *reshape_context)
+{
+ reshape_smooth_context->reshape_context = reshape_context;
+
+ reshape_smooth_context->geometry.num_vertices = 0;
+ reshape_smooth_context->geometry.vertices = NULL;
+ reshape_smooth_context->geometry.num_corners = 0;
+ reshape_smooth_context->geometry.corners = NULL;
+
+ reshape_smooth_context->reshape_subdiv = NULL;
+ reshape_smooth_context->base_surface_grids = NULL;
+}
+
+static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->geometry.vertices != NULL) {
+ for (int i = 0; i < reshape_smooth_context->geometry.num_vertices; ++i) {
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices[i].grid_coords);
+ }
+ }
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices);
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.corners);
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.faces);
+}
+
+static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->reshape_subdiv == NULL) {
+ return;
+ }
+ BKE_subdiv_free(reshape_smooth_context->reshape_subdiv);
+}
+
+static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ context_free_geometry(reshape_smooth_context);
+ context_free_subdiv(reshape_smooth_context);
+ base_surface_grids_free(reshape_smooth_context);
+}
+
+static bool foreach_topology_info(const SubdivForeachContext *foreach_context,
+ const int num_vertices,
+ const int UNUSED(num_edges),
+ const int num_loops,
+ const int num_polygons)
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ /* NOTE: Calloc so the counters are re-set to 0 "for free". */
+ reshape_smooth_context->geometry.num_vertices = num_vertices;
+ reshape_smooth_context->geometry.vertices = MEM_calloc_arrayN(
+ sizeof(Vertex), num_vertices, "smooth vertices");
+
+ reshape_smooth_context->geometry.num_corners = num_loops;
+ reshape_smooth_context->geometry.corners = MEM_malloc_arrayN(
+ sizeof(Corner), num_loops, "smooth corners");
+
+ reshape_smooth_context->geometry.num_faces = num_polygons;
+ reshape_smooth_context->geometry.faces = MEM_malloc_arrayN(
+ sizeof(Dace), num_polygons, "smooth faces");
+
+ return true;
+}
+
+static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
+ const GridCoord *grid_coord,
+ const int subdiv_vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ BLI_assert(subdiv_vertex_index < reshape_smooth_context->geometry.num_vertices);
+
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index];
+
+ vertex->grid_coords = MEM_reallocN(vertex->grid_coords,
+ sizeof(Vertex) * (vertex->num_grid_coords + 1));
+ vertex->grid_coords[vertex->num_grid_coords] = *grid_coord;
+ ++vertex->num_grid_coords;
+}
+
+/* TODO(sergey): De-duplicate with similar function in multires_reshape_vertcos.c */
+static void foreach_vertex(const SubdivForeachContext *foreach_context,
+ const PTexCoord *ptex_coord,
+ const int subdiv_vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord);
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context,
+ grid_coord.grid_index);
+
+ const Mesh *base_mesh = reshape_context->base_mesh;
+ const MPoly *base_poly = &base_mesh->mpoly[face_index];
+ const int num_corners = base_poly->totloop;
+ const int start_grid_index = reshape_context->face_start_grid_index[face_index];
+ const int corner = grid_coord.grid_index - start_grid_index;
+
+ if (grid_coord.u == 0.0f && grid_coord.v == 0.0f) {
+ for (int current_corner = 0; current_corner < num_corners; ++current_corner) {
+ GridCoord corner_grid_coord = grid_coord;
+ corner_grid_coord.grid_index = start_grid_index + current_corner;
+ foreach_single_vertex(foreach_context, &corner_grid_coord, subdiv_vertex_index);
+ }
+ return;
+ }
+
+ foreach_single_vertex(foreach_context, &grid_coord, subdiv_vertex_index);
+
+ if (grid_coord.u == 0.0f) {
+ GridCoord prev_grid_coord;
+ prev_grid_coord.grid_index = start_grid_index + ((corner + num_corners - 1) % num_corners);
+ prev_grid_coord.u = grid_coord.v;
+ prev_grid_coord.v = 0.0f;
+
+ foreach_single_vertex(foreach_context, &prev_grid_coord, subdiv_vertex_index);
+ }
+
+ if (grid_coord.v == 0.0f) {
+ GridCoord next_grid_coord;
+ next_grid_coord.grid_index = start_grid_index + ((corner + 1) % num_corners);
+ next_grid_coord.u = 0.0f;
+ next_grid_coord.v = grid_coord.u;
+
+ foreach_single_vertex(foreach_context, &next_grid_coord, subdiv_vertex_index);
+ }
+}
+
+static void foreach_vertex_inner(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_poly_index),
+ const int UNUSED(coarse_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_vertex_every_corner(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_vertex_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_vertex_every_edge(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_edge_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_loop(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(ptex_face_index),
+ const float UNUSED(ptex_face_u),
+ const float UNUSED(ptex_face_v),
+ const int UNUSED(coarse_loop_index),
+ const int coarse_poly_index,
+ const int coarse_corner,
+ const int subdiv_loop_index,
+ const int subdiv_vertex_index,
+ const int UNUSED(subdiv_edge_index))
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ BLI_assert(subdiv_loop_index < reshape_smooth_context->geometry.num_corners);
+
+ Corner *corner = &reshape_smooth_context->geometry.corners[subdiv_loop_index];
+ corner->vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index];
+
+ const int first_grid_index = reshape_context->face_start_grid_index[coarse_poly_index];
+ corner->grid_index = first_grid_index + coarse_corner;
+}
+
+static void foreach_poly(const SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(coarse_poly_index),
+ const int subdiv_poly_index,
+ const int start_loop_index,
+ const int num_loops)
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ BLI_assert(subdiv_poly_index < reshape_smooth_context->geometry.num_faces);
+
+ Dace *face = &reshape_smooth_context->geometry.faces[subdiv_poly_index];
+ face->start_corner_index = start_loop_index;
+ face->num_corners = num_loops;
+}
+
+static void foreach_vertex_of_loose_edge(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(coarse_edge_index),
+ const float UNUSED(u),
+ const int vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index];
+
+ if (vertex->num_grid_coords != 0) {
+ vertex->is_infinite_sharp = true;
+ }
+}
+
+static void geometry_create(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ SubdivForeachContext foreach_context = {
+ .topology_info = foreach_topology_info,
+ .vertex_inner = foreach_vertex_inner,
+ .vertex_every_corner = foreach_vertex_every_corner,
+ .vertex_every_edge = foreach_vertex_every_edge,
+ .loop = foreach_loop,
+ .poly = foreach_poly,
+ .vertex_of_loose_edge = foreach_vertex_of_loose_edge,
+ .user_data = reshape_smooth_context,
+ };
+
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << reshape_context->reshape.level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ /* TODO(sergey): Tell the foreach() to ignore loose vertices. */
+ BKE_subdiv_foreach_subdiv_geometry(
+ reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh);
+}
+
+/* ================================================================================================
+ * Generation of OpenSubdiv evaluator for topology created form reshape level.
+ */
+
+static OpenSubdiv_SchemeType get_scheme_type(const OpenSubdiv_Converter *UNUSED(converter))
+{
+ return OSD_SCHEME_CATMARK;
+}
+
+static OpenSubdiv_VtxBoundaryInterpolation get_vtx_boundary_interpolation(
+ const struct OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ return BKE_subdiv_converter_vtx_boundary_interpolation_from_settings(settings);
+}
+
+static OpenSubdiv_FVarLinearInterpolation get_fvar_linear_interpolation(
+ const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ return BKE_subdiv_converter_fvar_linear_from_settings(settings);
+}
+
+static bool specifies_full_topology(const OpenSubdiv_Converter *UNUSED(converter))
+{
+ return false;
+}
+
+static int get_num_faces(const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+
+ return reshape_smooth_context->geometry.num_faces;
+}
+
+static int get_num_vertices(const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+
+ return reshape_smooth_context->geometry.num_vertices;
+}
+
+static int get_num_face_vertices(const OpenSubdiv_Converter *converter, int face_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+
+ return face->num_corners;
+}
+
+static void get_face_vertices(const OpenSubdiv_Converter *converter,
+ int face_index,
+ int *face_vertices)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+
+ for (int i = 0; i < face->num_corners; ++i) {
+ const int corner_index = face->start_corner_index + i;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ face_vertices[i] = corner->vertex - reshape_smooth_context->geometry.vertices;
+ }
+}
+
+static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, int vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index];
+
+ return vertex->is_infinite_sharp;
+}
+
+static void converter_init(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ OpenSubdiv_Converter *converter)
+{
+ converter->getSchemeType = get_scheme_type;
+ converter->getVtxBoundaryInterpolation = get_vtx_boundary_interpolation;
+ converter->getFVarLinearInterpolation = get_fvar_linear_interpolation;
+ converter->specifiesFullTopology = specifies_full_topology;
+
+ converter->getNumFaces = get_num_faces;
+ converter->getNumEdges = NULL;
+ converter->getNumVertices = get_num_vertices;
+
+ converter->getNumFaceVertices = get_num_face_vertices;
+ converter->getFaceVertices = get_face_vertices;
+ converter->getFaceEdges = NULL;
+
+ converter->getEdgeVertices = NULL;
+ converter->getNumEdgeFaces = NULL;
+ converter->getEdgeFaces = NULL;
+ converter->getEdgeSharpness = NULL;
+
+ converter->getNumVertexEdges = NULL;
+ converter->getVertexEdges = NULL;
+ converter->getNumVertexFaces = NULL;
+ converter->getVertexFaces = NULL;
+ converter->isInfiniteSharpVertex = is_infinite_sharp_vertex;
+ converter->getVertexSharpness = NULL;
+
+ converter->getNumUVLayers = NULL;
+ converter->precalcUVLayer = NULL;
+ converter->finishUVLayer = NULL;
+ converter->getNumUVCoordinates = NULL;
+ converter->getFaceCornerUVIndex = NULL;
+
+ converter->freeUserData = NULL;
+
+ converter->user_data = (void *)reshape_smooth_context;
+}
+
+/* Create subdiv descriptor created for topology at a reshape level, */
+static void reshape_subdiv_create(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ OpenSubdiv_Converter converter;
+ converter_init(reshape_smooth_context, &converter);
+
+ Subdiv *reshape_subdiv = BKE_subdiv_new_from_converter(settings, &converter);
+ BKE_subdiv_eval_begin(reshape_subdiv);
+
+ reshape_smooth_context->reshape_subdiv = reshape_subdiv;
+
+ BKE_subdiv_converter_free(&converter);
+}
+
+/* Callback to provide coarse position for subdivision surface topology at a reshape level. */
+typedef void(ReshapeSubdivCoarsePositionCb)(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Vertex *vertex,
+ float r_P[3]);
+
+/* Refine subdivision surface topology at a reshape level for new coarse verticies positions. */
+static void reshape_subdiv_refine(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ ReshapeSubdivCoarsePositionCb coarse_position_cb)
+{
+ Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv;
+
+ /* TODO(sergey): For non-trivial coarse_position_cb we should multi-thread this loop. */
+
+ const int num_vertices = reshape_smooth_context->geometry.num_vertices;
+ for (int i = 0; i < num_vertices; ++i) {
+ const Vertex *vertex = &reshape_smooth_context->geometry.vertices[i];
+ float P[3];
+ coarse_position_cb(reshape_smooth_context, vertex, P);
+ reshape_subdiv->evaluator->setCoarsePositions(reshape_subdiv->evaluator, P, i, 1);
+ }
+ reshape_subdiv->evaluator->refine(reshape_subdiv->evaluator);
+}
+
+BLI_INLINE const GridCoord *reshape_subdiv_refine_vertex_grid_coord(const Vertex *vertex)
+{
+ if (vertex->num_grid_coords == 0) {
+ /* This is a loose vertex, the coordinate is not important. */
+ /* TODO(sergey): Once the subdiv_foreach() supports properly ignoring loose elements this
+ * should become an assert instead. */
+ return NULL;
+ }
+ /* NOTE: All grid coordinates will point to the same object position, so can be simple and use
+ * first grid coordinate. */
+ return &vertex->grid_coords[0];
+}
+
+/* Version of reshape_subdiv_refine() which uses coarse position from original grids. */
+static void reshape_subdiv_refine_orig_P(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex);
+
+ /* Check whether this is a loose vertex. */
+ if (grid_coord == NULL) {
+ zero_v3(r_P);
+ return;
+ }
+
+ float limit_P[3];
+ float tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, limit_P, tangent_matrix);
+
+ const ReshapeConstGridElement orig_grid_element =
+ multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
+
+ float D[3];
+ mul_v3_m3v3(D, tangent_matrix, orig_grid_element.displacement);
+
+ add_v3_v3v3(r_P, limit_P, D);
+}
+static void reshape_subdiv_refine_orig(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_orig_P);
+}
+
+/* Version of reshape_subdiv_refine() which uses coarse position from final grids. */
+static void reshape_subdiv_refine_final_P(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex);
+
+ /* Check whether this is a loose vertex. */
+ if (grid_coord == NULL) {
+ zero_v3(r_P);
+ return;
+ }
+
+ const ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(
+ reshape_context, grid_coord);
+
+ /* NOTE: At this point in reshape/propagate pipeline grid displacement is actually storing object
+ * vertices coordinates. */
+ copy_v3_v3(r_P, grid_element.displacement);
+}
+static void reshape_subdiv_refine_final(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_final_P);
+}
+
+static void reshape_subdiv_evaluate_limit_at_grid(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ float limit_P[3],
+ float r_tangent_matrix[3][3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ float dPdu[3], dPdv[3];
+ BKE_subdiv_eval_limit_point_and_derivatives(reshape_smooth_context->reshape_subdiv,
+ ptex_coord->ptex_face_index,
+ ptex_coord->u,
+ ptex_coord->v,
+ limit_P,
+ dPdu,
+ dPdv);
+
+ const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index);
+ BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, corner);
+}
+
+/* ================================================================================================
+ * Evaluation of base surface.
+ */
+
+static void evaluate_base_surface_grids_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ float limit_P[3];
+ float tangent_matrix[3][3];
+ reshape_subdiv_evaluate_limit_at_grid(
+ reshape_smooth_context, ptex_coord, grid_coord, limit_P, tangent_matrix);
+
+ base_surface_grids_write(reshape_smooth_context, grid_coord, limit_P, tangent_matrix);
+}
+
+static void evaluate_base_surface_grids(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_base_surface_grids_callback, NULL);
+}
+
+/* ================================================================================================
+ * Evaluation of new surface.
+ */
+
+/* Evaluate final position of the original (pre-sculpt-edit) point position at a given grid
+ * coordinate. */
+static void evaluate_final_original_point(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const GridCoord *grid_coord,
+ float r_orig_final_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ /* Element of an original MDISPS grid) */
+ const ReshapeConstGridElement orig_grid_element =
+ multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
+
+ /* Limit surface of the base mesh. */
+ float base_mesh_limit_P[3];
+ float base_mesh_tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(
+ reshape_context, grid_coord, base_mesh_limit_P, base_mesh_tangent_matrix);
+
+ /* Convert original displacement from tangent space to object space. */
+ float orig_displacement[3];
+ mul_v3_m3v3(orig_displacement, base_mesh_tangent_matrix, orig_grid_element.displacement);
+
+ /* Final point = limit surface + displacement. */
+ add_v3_v3v3(r_orig_final_P, base_mesh_limit_P, orig_displacement);
+}
+
+static void evaluate_higher_grid_positions_with_details_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ /* Position of the original veretx at top level. */
+ float orig_final_P[3];
+ evaluate_final_original_point(reshape_smooth_context, grid_coord, orig_final_P);
+
+ /* Original surface point on sculpt level (sculpt level before edits in sculpt mode). */
+ const SurfacePoint *orig_sculpt_point = base_surface_grids_read(reshape_smooth_context,
+ grid_coord);
+
+ /* Difference between original top level and original sculpt level in object space. */
+ float original_detail_delta[3];
+ sub_v3_v3v3(original_detail_delta, orig_final_P, orig_sculpt_point->P);
+
+ /* Difference between original top level and original sculpt level in tangent space of original
+ * sculpt level. */
+ float original_detail_delta_tangent[3];
+ float original_sculpt_tangent_matrix_inv[3][3];
+ invert_m3_m3(original_sculpt_tangent_matrix_inv, orig_sculpt_point->tangent_matrix);
+ mul_v3_m3v3(
+ original_detail_delta_tangent, original_sculpt_tangent_matrix_inv, original_detail_delta);
+
+ /* Limit surface of smoothed (subdivided) edited sculpt level. */
+ float smooth_limit_P[3];
+ float smooth_tangent_matrix[3][3];
+ reshape_subdiv_evaluate_limit_at_grid(
+ reshape_smooth_context, ptex_coord, grid_coord, smooth_limit_P, smooth_tangent_matrix);
+
+ /* Add original detail to the smoothed surface. */
+ float smooth_delta[3];
+ mul_v3_m3v3(smooth_delta, smooth_tangent_matrix, original_detail_delta_tangent);
+
+ /* Grid element of the result.
+ *
+ * NOTE: Displacement is storing object space coordinate. */
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+
+ add_v3_v3v3(grid_element.displacement, smooth_limit_P, smooth_delta);
+}
+static void evaluate_higher_grid_positions_with_details(
+ const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(
+ reshape_smooth_context, evaluate_higher_grid_positions_with_details_callback, NULL);
+}
+
+static void evaluate_higher_grid_positions_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv;
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+
+ /* Surface. */
+
+ float P[3];
+ BKE_subdiv_eval_limit_point(
+ reshape_subdiv, ptex_coord->ptex_face_index, ptex_coord->u, ptex_coord->v, P);
+
+ copy_v3_v3(grid_element.displacement, P);
+
+ /* Masks. */
+ if (grid_element.mask != NULL) {
+ *grid_element.mask = interpolate_masks_grid(reshape_smooth_context, grid_coord);
+ }
+}
+
+static void evaluate_higher_grid_positions(
+ const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(
+ reshape_smooth_context, evaluate_higher_grid_positions_callback, NULL);
+}
+/* ================================================================================================
+ * Entry point.
+ */
+
+void multires_reshape_smooth_object_grids_with_details(
+ const MultiresReshapeContext *reshape_context)
+{
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+ if (level_difference == 0) {
+ /* Early output. */
+ return;
+ }
+
+ MultiresReshapeSmoothContext reshape_smooth_context;
+ context_init(&reshape_smooth_context, reshape_context);
+
+ geometry_create(&reshape_smooth_context);
+
+ reshape_subdiv_create(&reshape_smooth_context);
+
+ base_surface_grids_allocate(&reshape_smooth_context);
+ reshape_subdiv_refine_orig(&reshape_smooth_context);
+ evaluate_base_surface_grids(&reshape_smooth_context);
+
+ reshape_subdiv_refine_final(&reshape_smooth_context);
+ evaluate_higher_grid_positions_with_details(&reshape_smooth_context);
+
+ context_free(&reshape_smooth_context);
+}
+
+void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context)
+{
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+ if (level_difference == 0) {
+ /* Early output. */
+ return;
+ }
+
+ MultiresReshapeSmoothContext reshape_smooth_context;
+ context_init(&reshape_smooth_context, reshape_context);
+
+ geometry_create(&reshape_smooth_context);
+
+ reshape_subdiv_create(&reshape_smooth_context);
+
+ reshape_subdiv_refine_final(&reshape_smooth_context);
+ evaluate_higher_grid_positions(&reshape_smooth_context);
+
+ context_free(&reshape_smooth_context);
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c
new file mode 100644
index 00000000000..1ebaf743858
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_util.c
@@ -0,0 +1,727 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_task.h"
+
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_eval.h"
+#include "BKE_subdiv_ccg.h"
+#include "BKE_subdiv_foreach.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "DEG_depsgraph_query.h"
+
+/* ================================================================================================
+ * Construct/destruct reshape context.
+ */
+
+/* Create subdivision surface descriptor which is configured for surface evaluation at a given
+ * multires modifier. */
+Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph,
+ /*const*/ Object *object,
+ const MultiresModifierData *mmd)
+{
+ Mesh *coarse_mesh;
+
+ if (depsgraph != NULL) {
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
+ coarse_mesh = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
+ }
+ else {
+ coarse_mesh = (Mesh *)object->data;
+ }
+
+ SubdivSettings subdiv_settings;
+ BKE_multires_subdiv_settings_init(&subdiv_settings, mmd);
+ Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, coarse_mesh);
+ if (!BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh, NULL)) {
+ BKE_subdiv_free(subdiv);
+ return NULL;
+ }
+ return subdiv;
+}
+
+static void context_zero(MultiresReshapeContext *reshape_context)
+{
+ memset(reshape_context, 0, sizeof(*reshape_context));
+}
+
+static void context_init_lookup(MultiresReshapeContext *reshape_context)
+{
+ const Mesh *base_mesh = reshape_context->base_mesh;
+ const MPoly *mpoly = base_mesh->mpoly;
+ const int num_faces = base_mesh->totpoly;
+
+ reshape_context->face_start_grid_index = MEM_malloc_arrayN(
+ sizeof(int), num_faces, "face_start_grid_index");
+ int num_grids = 0;
+ int num_ptex_faces = 0;
+ for (int face_index = 0; face_index < num_faces; ++face_index) {
+ const int num_corners = mpoly[face_index].totloop;
+ reshape_context->face_start_grid_index[face_index] = num_grids;
+ num_grids += num_corners;
+ num_ptex_faces += (num_corners == 4) ? 1 : num_corners;
+ }
+
+ reshape_context->grid_to_face_index = MEM_malloc_arrayN(
+ sizeof(int), num_grids, "grid_to_face_index");
+ reshape_context->ptex_start_grid_index = MEM_malloc_arrayN(
+ sizeof(int), num_ptex_faces, "ptex_start_grid_index");
+ for (int face_index = 0, grid_index = 0, ptex_index = 0; face_index < num_faces; ++face_index) {
+ const int num_corners = mpoly[face_index].totloop;
+ const int num_face_ptex_faces = (num_corners == 4) ? 1 : num_corners;
+ for (int i = 0; i < num_face_ptex_faces; ++i) {
+ reshape_context->ptex_start_grid_index[ptex_index + i] = grid_index + i;
+ }
+ for (int corner = 0; corner < num_corners; ++corner, ++grid_index) {
+ reshape_context->grid_to_face_index[grid_index] = face_index;
+ }
+ ptex_index += num_face_ptex_faces;
+ }
+
+ /* Store number of grids, which will be used for sanity checks. */
+ reshape_context->num_grids = num_grids;
+}
+
+static void context_init_grid_pointers(MultiresReshapeContext *reshape_context)
+{
+ Mesh *base_mesh = reshape_context->base_mesh;
+ reshape_context->mdisps = CustomData_get_layer(&base_mesh->ldata, CD_MDISPS);
+ reshape_context->grid_paint_masks = CustomData_get_layer(&base_mesh->ldata, CD_GRID_PAINT_MASK);
+}
+
+static void context_init_commoon(MultiresReshapeContext *reshape_context)
+{
+ BLI_assert(reshape_context->subdiv != NULL);
+ BLI_assert(reshape_context->base_mesh != NULL);
+
+ reshape_context->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(reshape_context->subdiv),
+
+ context_init_lookup(reshape_context);
+ context_init_grid_pointers(reshape_context);
+}
+
+static bool context_is_valid(MultiresReshapeContext *reshape_context)
+{
+ if (reshape_context->mdisps == NULL) {
+ /* Multires displacement has been removed before current changes were applies. */
+ return false;
+ }
+ return true;
+}
+
+static bool context_verify_or_free(MultiresReshapeContext *reshape_context)
+{
+ const bool is_valid = context_is_valid(reshape_context);
+ if (!is_valid) {
+ multires_reshape_context_free(reshape_context);
+ }
+ return is_valid;
+}
+
+bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context,
+ Depsgraph *depsgraph,
+ Object *object,
+ MultiresModifierData *mmd)
+{
+ context_zero(reshape_context);
+
+ const bool use_render_params = false;
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Mesh *base_mesh = (Mesh *)object->data;
+
+ reshape_context->base_mesh = base_mesh;
+
+ reshape_context->subdiv = multires_reshape_create_subdiv(depsgraph, object, mmd);
+ reshape_context->need_free_subdiv = true;
+
+ reshape_context->reshape.level = multires_get_level(
+ scene_eval, object, mmd, use_render_params, true);
+ reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level(
+ reshape_context->reshape.level);
+
+ reshape_context->top.level = mmd->totlvl;
+ reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
+
+ context_init_commoon(reshape_context);
+
+ return context_verify_or_free(reshape_context);
+}
+
+bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context,
+ SubdivCCG *subdiv_ccg,
+ Mesh *base_mesh,
+ int top_level)
+{
+ context_zero(reshape_context);
+
+ reshape_context->base_mesh = base_mesh;
+
+ reshape_context->subdiv = subdiv_ccg->subdiv;
+ reshape_context->need_free_subdiv = false;
+
+ reshape_context->reshape.level = subdiv_ccg->level;
+ reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level(
+ reshape_context->reshape.level);
+
+ reshape_context->top.level = top_level;
+ reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
+
+ context_init_commoon(reshape_context);
+
+ return context_verify_or_free(reshape_context);
+}
+
+bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *reshape_context,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ int top_level)
+{
+ context_zero(reshape_context);
+
+ Mesh *base_mesh = (Mesh *)object->data;
+
+ reshape_context->base_mesh = base_mesh;
+
+ reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd);
+ reshape_context->need_free_subdiv = true;
+
+ reshape_context->reshape.level = mmd->totlvl;
+ reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level(
+ reshape_context->reshape.level);
+
+ reshape_context->top.level = top_level;
+ reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
+
+ context_init_commoon(reshape_context);
+
+ return context_verify_or_free(reshape_context);
+}
+
+static void free_original_grids(MultiresReshapeContext *reshape_context)
+{
+ MDisps *orig_mdisps = reshape_context->orig.mdisps;
+ GridPaintMask *orig_grid_paint_masks = reshape_context->orig.grid_paint_masks;
+
+ if (orig_mdisps == NULL && orig_grid_paint_masks == NULL) {
+ return;
+ }
+
+ const int num_grids = reshape_context->num_grids;
+ for (int grid_index = 0; grid_index < num_grids; grid_index++) {
+ if (orig_mdisps != NULL) {
+ MDisps *orig_grid = &orig_mdisps[grid_index];
+ MEM_SAFE_FREE(orig_grid->disps);
+ }
+ if (orig_grid_paint_masks != NULL) {
+ GridPaintMask *orig_paint_mask_grid = &orig_grid_paint_masks[grid_index];
+ MEM_SAFE_FREE(orig_paint_mask_grid->data);
+ }
+ }
+
+ MEM_SAFE_FREE(orig_mdisps);
+ MEM_SAFE_FREE(orig_grid_paint_masks);
+}
+
+void multires_reshape_context_free(MultiresReshapeContext *reshape_context)
+{
+ if (reshape_context->need_free_subdiv) {
+ BKE_subdiv_free(reshape_context->subdiv);
+ }
+
+ free_original_grids(reshape_context);
+
+ MEM_freeN(reshape_context->face_start_grid_index);
+ MEM_freeN(reshape_context->ptex_start_grid_index);
+ MEM_freeN(reshape_context->grid_to_face_index);
+}
+
+/* ================================================================================================
+ * Helper accessors.
+ */
+
+/* For the given grid index get index of face it was created for. */
+int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context,
+ int grid_index)
+{
+ BLI_assert(grid_index >= 0);
+ BLI_assert(grid_index < reshape_context->num_grids);
+
+ /* TODO(sergey): Optimization: when SubdivCCG is known we can calculate face index using
+ * SubdivCCG::grid_faces and SubdivCCG::faces, saving memory used by grid_to_face_index. */
+
+ return reshape_context->grid_to_face_index[grid_index];
+}
+
+/* For the given grid index get corner of a face it was created for. */
+int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_context, int grid_index)
+{
+ BLI_assert(grid_index >= 0);
+ BLI_assert(grid_index < reshape_context->num_grids);
+
+ /* TODO(sergey): Optimization: when SubdivCCG is known we can calculate face index using
+ * SubdivCCG::grid_faces and SubdivCCG::faces, saving memory used by grid_to_face_index. */
+
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context, grid_index);
+ return grid_index - reshape_context->face_start_grid_index[face_index];
+}
+
+bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index)
+{
+ const MPoly *coarse_poly = &reshape_context->base_mesh->mpoly[face_index];
+ return (coarse_poly->totloop == 4);
+}
+
+/* For the given grid index get index of corresponding ptex face. */
+int multires_reshape_grid_to_ptex_index(const MultiresReshapeContext *reshape_context,
+ int grid_index)
+{
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context, grid_index);
+ const int corner = multires_reshape_grid_to_corner(reshape_context, grid_index);
+ const bool is_quad = multires_reshape_is_quad_face(reshape_context, face_index);
+ return reshape_context->face_ptex_offset[face_index] + (is_quad ? 0 : corner);
+}
+
+/* Convert normalized coordinate within a grid to a normalized coordinate within a ptex face. */
+PTexCoord multires_reshape_grid_coord_to_ptex(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord)
+{
+ PTexCoord ptex_coord;
+
+ ptex_coord.ptex_face_index = multires_reshape_grid_to_ptex_index(reshape_context,
+ grid_coord->grid_index);
+
+ float corner_u, corner_v;
+ BKE_subdiv_grid_uv_to_ptex_face_uv(grid_coord->u, grid_coord->v, &corner_u, &corner_v);
+
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context,
+ grid_coord->grid_index);
+ const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index);
+ if (multires_reshape_is_quad_face(reshape_context, face_index)) {
+ float grid_u, grid_v;
+ BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v);
+ BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, &ptex_coord.u, &ptex_coord.v);
+ }
+ else {
+ ptex_coord.u = corner_u;
+ ptex_coord.v = corner_v;
+ }
+
+ return ptex_coord;
+}
+
+/* Convert a normalized coordinate within a ptex face to a normalized coordinate within a grid. */
+GridCoord multires_reshape_ptex_coord_to_grid(const MultiresReshapeContext *reshape_context,
+ const PTexCoord *ptex_coord)
+{
+ GridCoord grid_coord;
+
+ const int start_grid_index = reshape_context->ptex_start_grid_index[ptex_coord->ptex_face_index];
+ const int face_index = reshape_context->grid_to_face_index[start_grid_index];
+
+ int corner_delta;
+ if (multires_reshape_is_quad_face(reshape_context, face_index)) {
+ corner_delta = BKE_subdiv_rotate_quad_to_corner(
+ ptex_coord->u, ptex_coord->v, &grid_coord.u, &grid_coord.v);
+ }
+ else {
+ corner_delta = 0;
+ grid_coord.u = ptex_coord->u;
+ grid_coord.v = ptex_coord->v;
+ }
+ grid_coord.grid_index = start_grid_index + corner_delta;
+
+ BKE_subdiv_ptex_face_uv_to_grid_uv(grid_coord.u, grid_coord.v, &grid_coord.u, &grid_coord.v);
+
+ return grid_coord;
+}
+
+void multires_reshape_tangent_matrix_for_corner(const MultiresReshapeContext *reshape_context,
+ const int face_index,
+ const int corner,
+ const float dPdu[3],
+ const float dPdv[3],
+ float r_tangent_matrix[3][3])
+{
+ /* For a quad faces we would need to flip the tangent, since they will use
+ * use different coordinates within displacement grid comparent to ptex
+ * face. */
+ const bool is_quad = multires_reshape_is_quad_face(reshape_context, face_index);
+ const int tangent_corner = is_quad ? corner : 0;
+ BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, tangent_corner);
+}
+
+ReshapeGridElement multires_reshape_grid_element_for_grid_coord(
+ const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord)
+{
+ ReshapeGridElement grid_element = {NULL, NULL};
+
+ const int grid_size = reshape_context->top.grid_size;
+ const int grid_x = lround(grid_coord->u * (grid_size - 1));
+ const int grid_y = lround(grid_coord->v * (grid_size - 1));
+ const int grid_element_index = grid_y * grid_size + grid_x;
+
+ if (reshape_context->mdisps != NULL) {
+ MDisps *displacement_grid = &reshape_context->mdisps[grid_coord->grid_index];
+ grid_element.displacement = displacement_grid->disps[grid_element_index];
+ }
+
+ if (reshape_context->grid_paint_masks != NULL) {
+ GridPaintMask *grid_paint_mask = &reshape_context->grid_paint_masks[grid_coord->grid_index];
+ grid_element.mask = &grid_paint_mask->data[grid_element_index];
+ }
+
+ return grid_element;
+}
+
+ReshapeGridElement multires_reshape_grid_element_for_ptex_coord(
+ const MultiresReshapeContext *reshape_context, const PTexCoord *ptex_coord)
+{
+ GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord);
+ return multires_reshape_grid_element_for_grid_coord(reshape_context, &grid_coord);
+}
+
+ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord(
+ const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord)
+{
+ ReshapeConstGridElement grid_element = {{0.0f, 0.0f, 0.0f}, 0.0f};
+
+ const MDisps *mdisps = reshape_context->orig.mdisps;
+ if (mdisps != NULL) {
+ const MDisps *displacement_grid = &mdisps[grid_coord->grid_index];
+ if (displacement_grid->disps != NULL) {
+ const int grid_size = BKE_subdiv_grid_size_from_level(displacement_grid->level);
+ const int grid_x = lround(grid_coord->u * (grid_size - 1));
+ const int grid_y = lround(grid_coord->v * (grid_size - 1));
+ const int grid_element_index = grid_y * grid_size + grid_x;
+ copy_v3_v3(grid_element.displacement, displacement_grid->disps[grid_element_index]);
+ }
+ }
+
+ const GridPaintMask *grid_paint_masks = reshape_context->orig.grid_paint_masks;
+ if (grid_paint_masks != NULL) {
+ const GridPaintMask *paint_mask_grid = &grid_paint_masks[grid_coord->grid_index];
+ if (paint_mask_grid->data != NULL) {
+ const int grid_size = BKE_subdiv_grid_size_from_level(paint_mask_grid->level);
+ const int grid_x = lround(grid_coord->u * (grid_size - 1));
+ const int grid_y = lround(grid_coord->v * (grid_size - 1));
+ const int grid_element_index = grid_y * grid_size + grid_x;
+ grid_element.mask = paint_mask_grid->data[grid_element_index];
+ }
+ }
+
+ return grid_element;
+}
+
+/* ================================================================================================
+ * Sample limit surface of the base mesh.
+ */
+
+void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ float r_P[3],
+ float r_tangent_matrix[3][3])
+{
+ float dPdu[3], dPdv[3];
+ const PTexCoord ptex_coord = multires_reshape_grid_coord_to_ptex(reshape_context, grid_coord);
+ Subdiv *subdiv = reshape_context->subdiv;
+ BKE_subdiv_eval_limit_point_and_derivatives(
+ subdiv, ptex_coord.ptex_face_index, ptex_coord.u, ptex_coord.v, r_P, dPdu, dPdv);
+
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context,
+ grid_coord->grid_index);
+ const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index);
+ multires_reshape_tangent_matrix_for_corner(
+ reshape_context, face_index, corner, dPdu, dPdv, r_tangent_matrix);
+}
+
+/* ================================================================================================
+ * Custom data preparation.
+ */
+
+static void allocate_displacement_grid(MDisps *displacement_grid, const int level)
+{
+ const int grid_size = BKE_subdiv_grid_size_from_level(level);
+ const int grid_area = grid_size * grid_size;
+ float(*disps)[3] = MEM_calloc_arrayN(grid_area, 3 * sizeof(float), "multires disps");
+ if (displacement_grid->disps != NULL) {
+ MEM_freeN(displacement_grid->disps);
+ }
+ /* TODO(sergey): Preserve data on the old level. */
+ displacement_grid->disps = disps;
+ displacement_grid->totdisp = grid_area;
+ displacement_grid->level = level;
+}
+
+static void ensure_displacement_grid(MDisps *displacement_grid, const int level)
+{
+ if (displacement_grid->disps != NULL && displacement_grid->level >= level) {
+ return;
+ }
+ allocate_displacement_grid(displacement_grid, level);
+}
+
+static void ensure_displacement_grids(Mesh *mesh, const int grid_level)
+{
+ const int num_grids = mesh->totloop;
+ MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
+ for (int grid_index = 0; grid_index < num_grids; grid_index++) {
+ ensure_displacement_grid(&mdisps[grid_index], grid_level);
+ }
+}
+
+static void ensure_mask_grids(Mesh *mesh, const int level)
+{
+ GridPaintMask *grid_paint_masks = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK);
+ if (grid_paint_masks == NULL) {
+ return;
+ }
+ const int num_grids = mesh->totloop;
+ const int grid_size = BKE_subdiv_grid_size_from_level(level);
+ const int grid_area = grid_size * grid_size;
+ for (int grid_index = 0; grid_index < num_grids; grid_index++) {
+ GridPaintMask *grid_paint_mask = &grid_paint_masks[grid_index];
+ if (grid_paint_mask->level >= level) {
+ continue;
+ }
+ grid_paint_mask->level = level;
+ if (grid_paint_mask->data) {
+ MEM_freeN(grid_paint_mask->data);
+ }
+ /* TODO(sergey): Preserve data on the old level. */
+ grid_paint_mask->data = MEM_calloc_arrayN(grid_area, sizeof(float), "gpm.data");
+ }
+}
+
+void multires_reshape_ensure_grids(Mesh *mesh, const int level)
+{
+ ensure_displacement_grids(mesh, level);
+ ensure_mask_grids(mesh, level);
+}
+
+/* ================================================================================================
+ * Displacement, space conversion.
+ */
+
+void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context)
+{
+ const MDisps *mdisps = reshape_context->mdisps;
+ const GridPaintMask *grid_paint_masks = reshape_context->grid_paint_masks;
+
+ MDisps *orig_mdisps = MEM_dupallocN(mdisps);
+ GridPaintMask *orig_grid_paint_masks = NULL;
+ if (grid_paint_masks != NULL) {
+ orig_grid_paint_masks = MEM_dupallocN(grid_paint_masks);
+ }
+
+ const int num_grids = reshape_context->num_grids;
+ for (int grid_index = 0; grid_index < num_grids; grid_index++) {
+ MDisps *orig_grid = &orig_mdisps[grid_index];
+ /* Ignore possibly invalid/non-allocated original grids. They will be replaced with 0 original
+ * data when accessed during reshape process.
+ * Reshape process will ensure all grids are on top level, but that happens on separate set of
+ * grids which eventually replaces original one. */
+ if (orig_grid->disps != NULL) {
+ orig_grid->disps = MEM_dupallocN(orig_grid->disps);
+ }
+ if (orig_grid_paint_masks != NULL) {
+ GridPaintMask *orig_paint_mask_grid = &orig_grid_paint_masks[grid_index];
+ if (orig_paint_mask_grid->data != NULL) {
+ orig_paint_mask_grid->data = MEM_dupallocN(orig_paint_mask_grid->data);
+ }
+ }
+ }
+
+ reshape_context->orig.mdisps = orig_mdisps;
+ reshape_context->orig.grid_paint_masks = orig_grid_paint_masks;
+}
+
+typedef void (*ForeachGridCoordinateCallback)(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ void *userdata_v);
+
+typedef struct ForeachGridCoordinateTaskData {
+ const MultiresReshapeContext *reshape_context;
+
+ int grid_size;
+ float grid_size_1_inv;
+
+ ForeachGridCoordinateCallback callback;
+ void *callback_userdata_v;
+} ForeachGridCoordinateTaskData;
+
+static void foreach_grid_face_coordinate_task(void *__restrict userdata_v,
+ const int face_index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ ForeachGridCoordinateTaskData *data = userdata_v;
+
+ const MultiresReshapeContext *reshape_context = data->reshape_context;
+
+ const Mesh *base_mesh = data->reshape_context->base_mesh;
+ const MPoly *mpoly = base_mesh->mpoly;
+ const int grid_size = data->grid_size;
+ const float grid_size_1_inv = 1.0f / (((float)grid_size) - 1.0f);
+
+ const int num_corners = mpoly[face_index].totloop;
+ int grid_index = reshape_context->face_start_grid_index[face_index];
+ for (int corner = 0; corner < num_corners; ++corner, ++grid_index) {
+ for (int y = 0; y < grid_size; ++y) {
+ const float v = (float)y * grid_size_1_inv;
+ for (int x = 0; x < grid_size; ++x) {
+ const float u = (float)x * grid_size_1_inv;
+
+ GridCoord grid_coord;
+ grid_coord.grid_index = grid_index;
+ grid_coord.u = u;
+ grid_coord.v = v;
+
+ data->callback(data->reshape_context, &grid_coord, data->callback_userdata_v);
+ }
+ }
+ }
+}
+
+/* Run given callback for every grid coordinate at a given level. */
+static void foreach_grid_coordinate(const MultiresReshapeContext *reshape_context,
+ const int level,
+ ForeachGridCoordinateCallback callback,
+ void *userdata_v)
+{
+ ForeachGridCoordinateTaskData data;
+ data.reshape_context = reshape_context;
+ data.grid_size = BKE_subdiv_grid_size_from_level(level);
+ data.grid_size_1_inv = 1.0f / (((float)data.grid_size) - 1.0f);
+ data.callback = callback;
+ data.callback_userdata_v = userdata_v;
+
+ TaskParallelSettings parallel_range_settings;
+ BLI_parallel_range_settings_defaults(&parallel_range_settings);
+ parallel_range_settings.min_iter_per_thread = 1;
+
+ const Mesh *base_mesh = reshape_context->base_mesh;
+ const int num_faces = base_mesh->totpoly;
+ BLI_task_parallel_range(
+ 0, num_faces, &data, foreach_grid_face_coordinate_task, &parallel_range_settings);
+}
+
+static void object_grid_element_to_tangent_displacement(
+ const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ float P[3];
+ float tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix);
+
+ float inv_tangent_matrix[3][3];
+ invert_m3_m3(inv_tangent_matrix, tangent_matrix);
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+
+ float D[3];
+ sub_v3_v3v3(D, grid_element.displacement, P);
+
+ float tangent_D[3];
+ mul_v3_m3v3(tangent_D, inv_tangent_matrix, D);
+
+ copy_v3_v3(grid_element.displacement, tangent_D);
+}
+
+void multires_reshape_object_grids_to_tangent_displacement(
+ const MultiresReshapeContext *reshape_context)
+{
+ foreach_grid_coordinate(reshape_context,
+ reshape_context->top.level,
+ object_grid_element_to_tangent_displacement,
+ NULL);
+}
+
+/* ================================================================================================
+ * MDISPS
+ *
+ * TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to
+ * own file. */
+
+static void assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ float P[3];
+ float tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix);
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+ float D[3];
+ mul_v3_m3v3(D, tangent_matrix, grid_element.displacement);
+
+ add_v3_v3v3(grid_element.displacement, P, D);
+}
+
+void multires_reshape_assign_final_coords_from_mdisps(
+ const MultiresReshapeContext *reshape_context)
+{
+ foreach_grid_coordinate(
+ reshape_context, reshape_context->top.level, assign_final_coords_from_mdisps, NULL);
+}
+
+static void assign_final_coords_from_orig_mdisps(const MultiresReshapeContext *reshape_context,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ float P[3];
+ float tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix);
+
+ const ReshapeConstGridElement orig_grid_element =
+ multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
+
+ float D[3];
+ mul_v3_m3v3(D, tangent_matrix, orig_grid_element.displacement);
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+ add_v3_v3v3(grid_element.displacement, P, D);
+}
+
+void multires_reshape_assign_final_coords_from_orig_mdisps(
+ const MultiresReshapeContext *reshape_context)
+{
+ foreach_grid_coordinate(
+ reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL);
+}
diff --git a/source/blender/blenkernel/intern/multires_reshape_vertcos.c b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
new file mode 100644
index 00000000000..c47b50faa03
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
@@ -0,0 +1,210 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math_vector.h"
+
+#include "BKE_subdiv_foreach.h"
+#include "BKE_subdiv_mesh.h"
+
+typedef struct MultiresReshapeAssignVertcosContext {
+ const MultiresReshapeContext *reshape_context;
+
+ const float (*vert_coords)[3];
+ const int num_vert_coords;
+} MultiresReshapeAssignVertcosContext;
+
+/* Set single displacement grid value at a reshape level to a correspnding vertex coordinate.
+ * This function will be called for every side of a boundary grid points for inner coordinates. */
+static void multires_reshape_vertcos_foreach_single_vertex(
+ const SubdivForeachContext *foreach_context,
+ const GridCoord *grid_coord,
+ const int subdiv_vertex_index)
+{
+ MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data;
+ const float *coordinate = reshape_vertcos_context->vert_coords[subdiv_vertex_index];
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(
+ reshape_vertcos_context->reshape_context, grid_coord);
+ BLI_assert(grid_element.displacement != NULL);
+ copy_v3_v3(grid_element.displacement, coordinate);
+}
+
+/* TODO(sergey): De-duplicate with similar function in multires_reshape_smooth.c */
+static void multires_reshape_vertcos_foreach_vertex(const SubdivForeachContext *foreach_context,
+ const PTexCoord *ptex_coord,
+ const int subdiv_vertex_index)
+{
+ const MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_vertcos_context->reshape_context;
+
+ const GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord);
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context,
+ grid_coord.grid_index);
+
+ const Mesh *base_mesh = reshape_context->base_mesh;
+ const MPoly *base_poly = &base_mesh->mpoly[face_index];
+ const int num_corners = base_poly->totloop;
+ const int start_grid_index = reshape_context->face_start_grid_index[face_index];
+ const int corner = grid_coord.grid_index - start_grid_index;
+
+ if (grid_coord.u == 0.0f && grid_coord.v == 0.0f) {
+ for (int current_corner = 0; current_corner < num_corners; ++current_corner) {
+ GridCoord corner_grid_coord = grid_coord;
+ corner_grid_coord.grid_index = start_grid_index + current_corner;
+ multires_reshape_vertcos_foreach_single_vertex(
+ foreach_context, &corner_grid_coord, subdiv_vertex_index);
+ }
+ return;
+ }
+
+ multires_reshape_vertcos_foreach_single_vertex(
+ foreach_context, &grid_coord, subdiv_vertex_index);
+
+ if (grid_coord.u == 0.0f) {
+ GridCoord prev_grid_coord;
+ prev_grid_coord.grid_index = start_grid_index + ((corner + num_corners - 1) % num_corners);
+ prev_grid_coord.u = grid_coord.v;
+ prev_grid_coord.v = 0.0f;
+
+ multires_reshape_vertcos_foreach_single_vertex(
+ foreach_context, &prev_grid_coord, subdiv_vertex_index);
+ }
+
+ if (grid_coord.v == 0.0f) {
+ GridCoord next_grid_coord;
+ next_grid_coord.grid_index = start_grid_index + ((corner + 1) % num_corners);
+ next_grid_coord.u = 0.0f;
+ next_grid_coord.v = grid_coord.u;
+
+ multires_reshape_vertcos_foreach_single_vertex(
+ foreach_context, &next_grid_coord, subdiv_vertex_index);
+ }
+}
+
+/* SubdivForeachContext::topology_info() */
+static bool multires_reshape_vertcos_foreach_topology_info(
+ const SubdivForeachContext *foreach_context,
+ const int num_vertices,
+ const int UNUSED(num_edges),
+ const int UNUSED(num_loops),
+ const int UNUSED(num_polygons))
+{
+ MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data;
+ if (num_vertices != reshape_vertcos_context->num_vert_coords) {
+ return false;
+ }
+ return true;
+}
+
+/* SubdivForeachContext::vertex_inner() */
+static void multires_reshape_vertcos_foreach_vertex_inner(
+ const SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+/* SubdivForeachContext::vertex_every_corner() */
+static void multires_reshape_vertcos_foreach_vertex_every_corner(
+ const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_vertex_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+/* SubdivForeachContext::vertex_every_edge() */
+static void multires_reshape_vertcos_foreach_vertex_every_edge(
+ const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_edge_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+/* Set displacement grids values at a reshape level to a object coordinates of the the given
+ * source. */
+bool multires_reshape_assign_final_coords_from_vertcos(
+ const MultiresReshapeContext *reshape_context,
+ const float (*vert_coords)[3],
+ const int num_vert_coords)
+{
+ MultiresReshapeAssignVertcosContext reshape_vertcos_context = {
+ .reshape_context = reshape_context,
+ .vert_coords = vert_coords,
+ .num_vert_coords = num_vert_coords,
+ };
+
+ SubdivForeachContext foreach_context = {
+ .topology_info = multires_reshape_vertcos_foreach_topology_info,
+ .vertex_inner = multires_reshape_vertcos_foreach_vertex_inner,
+ .vertex_every_edge = multires_reshape_vertcos_foreach_vertex_every_edge,
+ .vertex_every_corner = multires_reshape_vertcos_foreach_vertex_every_corner,
+ .user_data = &reshape_vertcos_context,
+ };
+
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << reshape_context->reshape.level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ return BKE_subdiv_foreach_subdiv_geometry(
+ reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh);
+}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 41f7531ed85..d7247518f9e 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -524,7 +524,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return true;
}
-void BKE_object_link_modifiers(Scene *scene, struct Object *ob_dst, const struct Object *ob_src)
+void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src)
{
ModifierData *md;
BKE_object_free_modifiers(ob_dst, 0);
@@ -562,7 +562,7 @@ void BKE_object_link_modifiers(Scene *scene, struct Object *ob_dst, const struct
if (md->type == eModifierType_Multires) {
/* Has to be done after mod creation, but *before* we actually copy its settings! */
multiresModifier_sync_levels_ex(
- scene, ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd);
+ ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd);
}
modifier_copyData(md, nmd);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index bac40b35102..31c4f96693c 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -686,7 +686,7 @@ static int modifier_apply_obdata(
}
if (mmd && mmd->totlvl && mti->type == eModifierTypeType_OnlyDeform) {
- if (!multiresModifier_reshapeFromDeformModifier(depsgraph, mmd, ob, md_eval)) {
+ if (!multiresModifier_reshapeFromDeformModifier(depsgraph, ob, mmd, md_eval)) {
BKE_report(reports, RPT_ERROR, "Multires modifier returned error, skipping apply");
return 0;
}
@@ -1375,26 +1375,25 @@ void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot)
static int multires_subdivide_exec(bContext *C, wmOperator *op)
{
- Scene *scene = CTX_data_scene(C);
- Object *ob = ED_object_active_context(C);
+ Object *object = ED_object_active_context(C);
MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(
- op, ob, eModifierType_Multires);
+ op, object, eModifierType_Multires);
if (!mmd) {
return OPERATOR_CANCELLED;
}
- multiresModifier_subdivide(mmd, scene, ob, 0, mmd->simple);
+ multiresModifier_subdivide(object, mmd);
ED_object_iter_other(
- CTX_data_main(C), ob, true, ED_object_multires_update_totlevels_cb, &mmd->totlvl);
+ CTX_data_main(C), object, true, ED_object_multires_update_totlevels_cb, &mmd->totlvl);
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
- if (ob->mode & OB_MODE_SCULPT) {
+ if (object->mode & OB_MODE_SCULPT) {
/* ensure that grid paint mask layer is created */
- BKE_sculpt_mask_layers_ensure(ob, mmd);
+ BKE_sculpt_mask_layers_ensure(object, mmd);
}
return OPERATOR_FINISHED;
@@ -1614,19 +1613,19 @@ void OBJECT_OT_multires_external_pack(wmOperatorType *ot)
/********************* multires apply base ***********************/
static int multires_base_apply_exec(bContext *C, wmOperator *op)
{
- Scene *scene = CTX_data_scene(C);
- Object *ob = ED_object_active_context(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *object = ED_object_active_context(C);
MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(
- op, ob, eModifierType_Multires);
+ op, object, eModifierType_Multires);
if (!mmd) {
return OPERATOR_CANCELLED;
}
- multiresModifier_base_apply(mmd, scene, ob);
+ multiresModifier_base_apply(depsgraph, object, mmd);
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index c7bba43758f..29bf9e88853 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1586,7 +1586,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE);
break;
case MAKE_LINKS_MODIFIERS:
- BKE_object_link_modifiers(scene, ob_dst, ob_src);
+ BKE_object_link_modifiers(ob_dst, ob_src);
DEG_id_tag_update(&ob_dst->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
break;