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.
This commit is contained in:
2020-03-03 12:35:51 +01:00
parent b0a1af4eb1
commit bc0a0cdf17
15 changed files with 2828 additions and 1251 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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__ */

View File

@@ -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);
}

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;