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:
Diffstat (limited to 'source/blender/blenkernel/intern/multires.c')
-rw-r--r--source/blender/blenkernel/intern/multires.c983
1 files changed, 894 insertions, 89 deletions
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 4b54114505e..f2c5a9cbf37 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -37,6 +37,8 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_pbvh.h"
+#include "BLI_editVert.h"
+#include "BLI_utildefines.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_mesh.h"
@@ -45,7 +47,9 @@
#include "BKE_paint.h"
#include "BKE_scene.h"
#include "BKE_subsurf.h"
-#include "BKE_utildefines.h"
+#include "BKE_tessmesh.h"
+
+#include "BKE_object.h"
#include "CCGSubSurf.h"
@@ -89,6 +93,36 @@ MultiresModifierData *find_multires_modifier_before(Scene *scene, ModifierData *
return NULL;
}
+/* used for applying scale on mdisps layer and syncing subdivide levels when joining objects
+ use_first - return first multires modifier if all multires'es are disabled
+*/
+MultiresModifierData *get_multires_modifier(Scene *scene, Object *ob, int use_first)
+{
+ ModifierData *md;
+ MultiresModifierData *mmd= NULL, *firstmmd= NULL;
+
+ /* find first active multires modifier */
+ for(md = ob->modifiers.first; md; md = md->next) {
+ if(md->type == eModifierType_Multires) {
+ if(!firstmmd)
+ firstmmd= (MultiresModifierData*)md;
+
+ if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) {
+ mmd= (MultiresModifierData*)md;
+ break;
+ }
+ }
+ }
+
+ if(!mmd && use_first) {
+ /* active multires have not been found
+ try to use first one */
+ return firstmmd;
+ }
+
+ return mmd;
+}
+
static int multires_get_level(Object *ob, MultiresModifierData *mmd, int render)
{
if(render)
@@ -151,68 +185,6 @@ void multires_force_render_update(Object *ob)
multires_force_update(ob);
}
-/* XXX */
-#if 0
-void multiresModifier_join(Object *ob)
-{
- Base *base = NULL;
- int highest_lvl = 0;
-
- /* First find the highest level of subdivision */
- base = FIRSTBASE;
- while(base) {
- if(TESTBASELIB_BGMODE(v3d, scene, base) && base->object->type==OB_MESH) {
- ModifierData *md;
- for(md = base->object->modifiers.first; md; md = md->next) {
- if(md->type == eModifierType_Multires) {
- int totlvl = ((MultiresModifierData*)md)->totlvl;
- if(totlvl > highest_lvl)
- highest_lvl = totlvl;
-
- /* Ensure that all updates are processed */
- multires_force_update(base->object);
- }
- }
- }
- base = base->next;
- }
-
- /* No multires meshes selected */
- if(highest_lvl == 0)
- return;
-
- /* Subdivide all the displacements to the highest level */
- base = FIRSTBASE;
- while(base) {
- if(TESTBASELIB_BGMODE(v3d, scene, base) && base->object->type==OB_MESH) {
- ModifierData *md = NULL;
- MultiresModifierData *mmd = NULL;
-
- for(md = base->object->modifiers.first; md; md = md->next) {
- if(md->type == eModifierType_Multires)
- mmd = (MultiresModifierData*)md;
- }
-
- /* If the object didn't have multires enabled, give it a new modifier */
- if(!mmd) {
- md = base->object->modifiers.first;
-
- while(md && modifierType_getInfo(md->type)->type == eModifierTypeType_OnlyDeform)
- md = md->next;
-
- mmd = (MultiresModifierData*)modifier_new(eModifierType_Multires);
- BLI_insertlinkbefore(&base->object->modifiers, md, mmd);
- modifier_unique_name(&base->object->modifiers, mmd);
- }
-
- if(mmd)
- multiresModifier_subdivide(mmd, base->object, highest_lvl - mmd->totlvl, 0, 0);
- }
- base = base->next;
- }
-}
-#endif
-
int multiresModifier_reshapeFromDM(Scene *scene, MultiresModifierData *mmd,
Object *ob, DerivedMesh *srcdm)
{
@@ -229,7 +201,7 @@ int multiresModifier_reshapeFromDM(Scene *scene, MultiresModifierData *mmd,
return 1;
}
- mrdm->release(mrdm);
+ if(mrdm) mrdm->release(mrdm);
return 0;
}
@@ -275,6 +247,60 @@ int multiresModifier_reshapeFromDeformMod(Scene *scene, MultiresModifierData *mm
return result;
}
+/* reset the multires levels to match the number of mdisps */
+static int get_levels_from_disps(Object *ob)
+{
+ Mesh *me = ob->data;
+ MDisps *mdisp, *md;
+ int i, j, totlvl= 0;
+
+ mdisp = CustomData_get_layer(&me->fdata, CD_MDISPS);
+
+ for(i = 0; i < me->totpoly; ++i) {
+ int S = me->mpoly[i].totloop;
+
+ md = mdisp + me->mpoly[i].loopstart;
+ for (j=0; j<me->mpoly[i].totloop; j++, md++) {
+ if(md->totdisp == 0) continue;
+
+ while(1) {
+ int side = (1 << (totlvl-1)) + 1;
+ int lvl_totdisp = side*side*S;
+ if(md->totdisp == lvl_totdisp)
+ break;
+ else if(md->totdisp < lvl_totdisp)
+ --totlvl;
+ else
+ ++totlvl;
+
+ }
+
+ break;
+ }
+ }
+
+ return totlvl;
+}
+
+/* reset the multires levels to match the number of mdisps */
+void multiresModifier_set_levels_from_disps(MultiresModifierData *mmd, Object *ob)
+{
+ Mesh *me = ob->data;
+ MDisps *mdisp;
+
+ if(me->edit_btmesh)
+ mdisp = CustomData_get_layer(&me->edit_btmesh->bm->ldata, CD_MDISPS);
+ else
+ mdisp = CustomData_get_layer(&me->ldata, CD_MDISPS);
+
+ if(mdisp) {
+ mmd->totlvl = get_levels_from_disps(ob);
+ mmd->lvl = MIN2(mmd->sculptlvl, mmd->totlvl);
+ mmd->sculptlvl = MIN2(mmd->sculptlvl, mmd->totlvl);
+ mmd->renderlvl = MIN2(mmd->renderlvl, mmd->totlvl);
+ }
+}
+
static void multires_set_tot_mdisps(Mesh *me, int lvl)
{
MDisps *mdisps= CustomData_get_layer(&me->ldata, CD_MDISPS);
@@ -351,11 +377,9 @@ static void multires_copy_dm_grid(DMGridData *gridA, DMGridData *gridB, int size
}
}
-/* direction=1 for delete higher, direction=0 for lower (not implemented yet) */
-void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int direction)
+static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
{
- Mesh *me = get_mesh(ob);
- int lvl = multires_get_level(ob, mmd, 0);
+ Mesh *me = (Mesh*)ob->data;
int levels = mmd->totlvl - lvl;
MDisps *mdisps;
@@ -365,7 +389,7 @@ void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int dire
multires_force_update(ob);
- if(mdisps && levels > 0 && direction == 1) {
+ if(mdisps && levels > 0) {
if(lvl > 0) {
MLoop *ml = me->mloop;
int nsize = multires_side_tot[lvl];
@@ -403,11 +427,31 @@ void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int dire
multires_set_tot_level(ob, mmd, lvl);
}
+/* direction=1 for delete higher, direction=0 for lower (not implemented yet) */
+void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int direction)
+{
+ Mesh *me = get_mesh(ob);
+ int lvl = multires_get_level(ob, mmd, 0);
+ int levels = mmd->totlvl - lvl;
+ MDisps *mdisps;
+
+ multires_set_tot_mdisps(me, mmd->totlvl);
+ CustomData_external_read(&me->fdata, &me->id, CD_MASK_MDISPS, me->totface);
+ mdisps= CustomData_get_layer(&me->fdata, CD_MDISPS);
+
+ multires_force_update(ob);
+
+ if(mdisps && levels > 0 && direction == 1) {
+ multires_del_higher(mmd, ob, lvl);
+ }
+
+ multires_set_tot_level(ob, mmd, lvl);
+}
+
static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int totlvl, int simple)
{
- MultiresModifierData mmd;
+ MultiresModifierData mmd= {{NULL}};
- memset(&mmd, 0, sizeof(MultiresModifierData));
mmd.lvl = lvl;
mmd.sculptlvl = lvl;
mmd.renderlvl = lvl;
@@ -417,11 +461,10 @@ static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm, int lv
return multires_dm_create_from_derived(&mmd, 1, dm, ob, 0, 0);
}
-static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int simple, int optimal)
+static DerivedMesh *subsurf_dm_create_local(Object *UNUSED(ob), DerivedMesh *dm, int lvl, int simple, int optimal)
{
- SubsurfModifierData smd;
+ SubsurfModifierData smd= {{NULL}};
- memset(&smd, 0, sizeof(SubsurfModifierData));
smd.levels = smd.renderLevels = lvl;
smd.flags |= eSubsurfModifierFlag_SubsurfUv;
if(simple)
@@ -432,12 +475,133 @@ static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm, int lvl
return subsurf_make_derived_from_derived(dm, &smd, 0, NULL, 0, 0);
}
-void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updateblock, int simple)
+
+
+/* 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, Object *ob)
+{
+ DerivedMesh *cddm, *dispdm, *origdm;
+ Mesh *me;
+ ListBase *fmap;
+ float (*origco)[3];
+ int i, j, offset, totlvl;
+
+ multires_force_update(ob);
+
+ me = get_mesh(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, NULL);
+ DM_set_only_copy(cddm, CD_MASK_BAREMESH);
+ dispdm = multires_dm_create_local(ob, cddm, totlvl, totlvl, 0);
+ 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, NULL);
+ fmap = cddm->getFaceMap(ob, cddm);
+ origco = MEM_callocN(sizeof(float)*3*me->totvert, "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) {
+ IndexNode *n;
+ float avg_no[3] = {0,0,0}, center[3] = {0,0,0}, push[3];
+ float dist;
+ int tot;
+
+ /* don't adjust verts not used by at least one face */
+ if(!fmap[i].first)
+ continue;
+
+ /* find center */
+ for(n = fmap[i].first, tot = 0; n; n = n->next) {
+ MFace *f = &me->mface[n->index];
+ int S = f->v4 ? 4 : 3;
+
+ /* this double counts, not sure if that's bad or good */
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+ if(vndx != i) {
+ add_v3_v3(center, origco[vndx]);
+ ++tot;
+ }
+ }
+ }
+ mul_v3_fl(center, 1.0f / tot);
+
+ /* find normal */
+ for(n = fmap[i].first; n; n = n->next) {
+ MFace *f = &me->mface[n->index];
+ int S = f->v4 ? 4 : 3;
+ float v[4][3], no[3];
+
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+ if(vndx == i)
+ copy_v3_v3(v[j], center);
+ else
+ copy_v3_v3(v[j], origco[vndx]);
+ }
+
+ if(S == 4)
+ normal_quad_v3(no, v[0], v[1], v[2], v[3]);
+ else
+ normal_tri_v3(no, v[0], v[1], v[2]);
+ 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);
+
+ /* subdivide the mesh to highest level without displacements */
+ cddm = CDDM_from_mesh(me, NULL);
+ DM_set_only_copy(cddm, CD_MASK_BAREMESH);
+ origdm = subsurf_dm_create_local(ob, cddm, totlvl, 0, 0);
+ cddm->release(cddm);
+
+ /* calc disps */
+ multiresModifier_disp_run(dispdm, me, 1, 0, origdm->getGridData(origdm), totlvl);
+
+ origdm->release(origdm);
+ dispdm->release(dispdm);
+}
+
+static void multires_subdivide(MultiresModifierData *mmd, Object *ob, int totlvl, int updateblock, int simple)
{
Mesh *me = ob->data;
MDisps *mdisps;
int lvl= mmd->totlvl;
- int totlvl= mmd->totlvl+1;
if(totlvl > multires_max_levels)
return;
@@ -510,6 +674,11 @@ void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updat
multires_set_tot_level(ob, mmd, totlvl);
}
+void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updateblock, int simple)
+{
+ multires_subdivide(mmd, ob, mmd->totlvl+1, updateblock, simple);
+}
+
static void grid_tangent(int gridSize, int index, int x, int y, int axis, DMGridData **gridData, float t[3])
{
if(axis == 0) {
@@ -541,7 +710,7 @@ static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int
MPoly *mpoly = me->mpoly;
MDisps *mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
int *gridOffset;
- int i, k, numGrids, gridSize, dGridSize, dSkip;
+ int i, k, /*numGrids,*/ gridSize, dGridSize, dSkip;
if(!mdisps) {
if(invert)
@@ -550,7 +719,7 @@ static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int
return;
}
- numGrids = dm->getNumGrids(dm);
+ /*numGrids = dm->getNumGrids(dm);*/ /*UNUSED*/
gridSize = dm->getGridSize(dm);
gridData = dm->getGridData(dm);
gridOffset = dm->getGridOffset(dm);
@@ -747,7 +916,7 @@ void multires_stitch_grids(Object *ob)
}
DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int local_mmd, DerivedMesh *dm, Object *ob,
- int useRenderParams, int isFinalCalc)
+ int useRenderParams, int UNUSED(isFinalCalc))
{
Mesh *me= ob->data;
DerivedMesh *result;
@@ -802,7 +971,7 @@ DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int loca
***************************/
/* Adapted from sculptmode.c */
-static void old_mdisps_bilinear(float out[3], float (*disps)[3], int st, float u, float v)
+void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, float v)
{
int x, y, x2, y2;
const int st_max = st - 1;
@@ -843,7 +1012,7 @@ static void old_mdisps_bilinear(float out[3], float (*disps)[3], int st, float u
add_v3_v3v3(out, d2[0], d2[1]);
}
-static void old_mdisps_rotate(int S, int newside, int oldside, int x, int y, float *u, float *v)
+static void old_mdisps_rotate(int S, int UNUSED(newside), int oldside, int x, int y, float *u, float *v)
{
float offset = oldside*0.5f - 0.5f;
@@ -998,7 +1167,12 @@ static void create_old_vert_edge_map(ListBase **map, IndexNode **mem, const Mult
static MultiresFace *find_old_face(ListBase *map, MultiresFace *faces, int v1, int v2, int v3, int v4)
{
IndexNode *n1;
- int v[4] = {v1, v2, v3, v4}, i, j;
+ int v[4], i, j;
+
+ v[0]= v1;
+ v[1]= v2;
+ v[2]= v3;
+ v[3]= v4;
for(n1 = map[v1].first; n1; n1 = n1->next) {
int fnd[4] = {0, 0, 0, 0};
@@ -1145,18 +1319,18 @@ static void multires_load_old_dm(DerivedMesh *dm, Mesh *me, int totlvl)
MultiresLevel *lvl, *lvl1;
Multires *mr= me->mr;
MVert *vsrc, *vdst;
- int src, dst;
+ unsigned int src, dst;
int st = multires_side_tot[totlvl - 1] - 1;
int extedgelen = multires_side_tot[totlvl] - 2;
int *vvmap; // inorder for dst, map to src
int crossedgelen;
- int i, j, s, x, totvert, tottri, totquad;
+ int s, x, tottri, totquad;
+ unsigned int i, j, totvert;
src = 0;
- dst = 0;
vsrc = mr->verts;
vdst = dm->getVertArray(dm);
- totvert = dm->getNumVerts(dm);
+ totvert = (unsigned int)dm->getNumVerts(dm);
vvmap = MEM_callocN(sizeof(int) * totvert, "multires vvmap");
lvl1 = mr->levels.first;
@@ -1247,7 +1421,7 @@ static void multires_load_old_dm(DerivedMesh *dm, Mesh *me, int totlvl)
fmem = MEM_callocN(sizeof(IndexNode*) * (mr->level_count-1), "multires fmem");
emem = MEM_callocN(sizeof(IndexNode*) * (mr->level_count-1), "multires emem");
lvl = lvl1;
- for(i = 0; i < mr->level_count - 1; ++i) {
+ for(i = 0; i < (unsigned int)mr->level_count - 1; ++i) {
create_old_vert_face_map(fmap + i, fmem + i, lvl->faces, lvl->totvert, lvl->totface);
create_old_vert_edge_map(emap + i, emem + i, lvl->edges, lvl->totvert, lvl->totedge);
lvl = lvl->next;
@@ -1282,9 +1456,9 @@ static void multires_load_old_dm(DerivedMesh *dm, Mesh *me, int totlvl)
dst = ldst;
}
- lvl = lvl->next;
+ /*lvl = lvl->next;*/ /*UNUSED*/
- for(i = 0; i < mr->level_count - 1; ++i) {
+ for(i = 0; i < (unsigned int)(mr->level_count - 1); ++i) {
MEM_freeN(fmap[i]);
MEM_freeN(fmem[i]);
MEM_freeN(emap[i]);
@@ -1306,6 +1480,52 @@ static void multires_load_old_dm(DerivedMesh *dm, Mesh *me, int totlvl)
multires_mvert_to_ss(dm, vdst);
}
+/* Copy the first-level vcol data to the mesh, if it exists */
+/* Warning: higher-level vcol data will be lost */
+static void multires_load_old_vcols(Mesh *me)
+{
+ MultiresLevel *lvl;
+ MultiresColFace *colface;
+ MCol *mcol;
+ int i, j;
+
+ if(!(lvl = me->mr->levels.first))
+ return;
+
+ if(!(colface = lvl->colfaces))
+ return;
+
+ /* older multires format never supported multiple vcol layers,
+ so we can assume the active vcol layer is the correct one */
+ if(!(mcol = CustomData_get_layer(&me->fdata, CD_MCOL)))
+ return;
+
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < 4; ++j) {
+ mcol[i*4 + j].a = colface[i].col[j].a;
+ mcol[i*4 + j].r = colface[i].col[j].r;
+ mcol[i*4 + j].g = colface[i].col[j].g;
+ mcol[i*4 + j].b = colface[i].col[j].b;
+ }
+ }
+}
+
+/* Copy the first-level face-flag data to the mesh */
+static void multires_load_old_face_flags(Mesh *me)
+{
+ MultiresLevel *lvl;
+ MultiresFace *faces;
+ int i;
+
+ if(!(lvl = me->mr->levels.first))
+ return;
+
+ if(!(faces = lvl->faces))
+ return;
+
+ for(i = 0; i < me->totface; ++i)
+ me->mface[i].flag = faces[i].flag;
+}
void multires_load_old(Object *ob, Mesh *me)
{
@@ -1367,8 +1587,593 @@ void multires_load_old(Object *ob, Mesh *me)
memset(&me->mr->vdata, 0, sizeof(CustomData));
memset(&me->mr->fdata, 0, sizeof(CustomData));
+ multires_load_old_vcols(me);
+ multires_load_old_face_flags(me);
+
/* Remove the old multires */
multires_free(me->mr);
me->mr= NULL;
}
+static void multires_sync_levels(Scene *scene, Object *ob, Object *to_ob)
+{
+ MultiresModifierData *mmd= get_multires_modifier(scene, ob, 1);
+ MultiresModifierData *to_mmd= get_multires_modifier(scene, to_ob, 1);
+
+ if(!mmd) {
+ /* object could have MDISP even when there is no multires modifier
+ this could lead to troubles due to i've got no idea how mdisp could be
+ upsampled correct without modifier data.
+ just remove mdisps if no multires present (nazgul) */
+
+ Mesh *me= (Mesh*)ob->data;
+
+ CustomData_external_remove(&me->fdata, &me->id, CD_MDISPS, me->totface);
+ CustomData_free_layer_active(&me->fdata, CD_MDISPS, me->totface);
+ }
+
+ if(!mmd || !to_mmd) return;
+
+ if(mmd->totlvl>to_mmd->totlvl) multires_del_higher(mmd, ob, to_mmd->totlvl);
+ else multires_subdivide(mmd, ob, to_mmd->totlvl, 0, mmd->simple);
+}
+
+static void multires_apply_smat(Scene *scene, Object *ob, float smat[3][3])
+{
+ DerivedMesh *dm= NULL, *cddm= NULL, *subdm= NULL;
+ DMGridData **gridData, **subGridData;
+ Mesh *me= (Mesh*)ob->data;
+ MFace *mface= me->mface;
+ MDisps *mdisps;
+ int *gridOffset;
+ int i, /*numGrids,*/ gridSize, dGridSize, dSkip, totvert;
+ float (*vertCos)[3] = NULL;
+ MultiresModifierData *mmd= get_multires_modifier(scene, ob, 1);
+ MultiresModifierData high_mmd;
+
+ CustomData_external_read(&me->fdata, &me->id, CD_MASK_MDISPS, me->totface);
+ mdisps= CustomData_get_layer(&me->fdata, CD_MDISPS);
+
+ if(!mdisps || !mmd) return;
+
+ /* we need derived mesh created from highest resolution */
+ high_mmd= *mmd;
+ high_mmd.lvl= high_mmd.totlvl;
+
+ /* unscaled multires with applied displacement */
+ subdm= get_multires_dm(scene, &high_mmd, ob);
+
+ /* prepare scaled CDDM to create ccgDN */
+ cddm= mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
+
+ totvert= cddm->getNumVerts(cddm);
+ vertCos= MEM_mallocN(sizeof(*vertCos) * totvert, "multiresScale vertCos");
+ cddm->getVertCos(cddm, vertCos);
+ for(i=0; i<totvert; i++)
+ mul_m3_v3(smat, vertCos[i]);
+ CDDM_apply_vert_coords(cddm, vertCos);
+ MEM_freeN(vertCos);
+
+ /* scaled ccgDM for tangent space of object with applied scale */
+ dm= subsurf_dm_create_local(ob, cddm, high_mmd.totlvl, high_mmd.simple, 0);
+ cddm->release(cddm);
+
+ /*numGrids= dm->getNumGrids(dm);*/ /*UNUSED*/
+ gridSize= dm->getGridSize(dm);
+ gridData= dm->getGridData(dm);
+ gridOffset= dm->getGridOffset(dm);
+ subGridData= subdm->getGridData(subdm);
+
+ dGridSize= multires_side_tot[high_mmd.totlvl];
+ dSkip= (dGridSize-1)/(gridSize-1);
+
+ #pragma omp parallel for private(i) if(me->totface*gridSize*gridSize*4 >= CCG_OMP_LIMIT)
+ for(i = 0; i < me->totface; ++i) {
+ const int numVerts= mface[i].v4 ? 4 : 3;
+ MDisps *mdisp= &mdisps[i];
+ int S, x, y, gIndex = gridOffset[i];
+
+ for(S = 0; S < numVerts; ++S, ++gIndex) {
+ DMGridData *grid= gridData[gIndex];
+ DMGridData *subgrid= subGridData[gIndex];
+ float (*dispgrid)[3]= &mdisp->disps[S*dGridSize*dGridSize];
+
+ for(y = 0; y < gridSize; y++) {
+ for(x = 0; x < gridSize; x++) {
+ float *co= grid[x + y*gridSize].co;
+ float *sco= subgrid[x + y*gridSize].co;
+ float *no= grid[x + y*gridSize].no;
+ float *data= dispgrid[dGridSize*y*dSkip + x*dSkip];
+ float mat[3][3], tx[3], ty[3], disp[3];
+
+ /* construct tangent space matrix */
+ grid_tangent(gridSize, gIndex, x, y, 0, gridData, tx);
+ normalize_v3(tx);
+
+ grid_tangent(gridSize, gIndex, x, y, 1, gridData, ty);
+ normalize_v3(ty);
+
+ column_vectors_to_mat3(mat, tx, ty, no);
+
+ /* scale subgrid coord and calculate displacement */
+ mul_m3_v3(smat, sco);
+ sub_v3_v3v3(disp, sco, co);
+
+ /* convert difference to tangent space */
+ invert_m3(mat);
+ mul_v3_m3v3(data, mat, disp);
+ }
+ }
+ }
+ }
+
+ dm->release(dm);
+ subdm->release(subdm);
+}
+
+int multires_mdisp_corners(MDisps *s)
+{
+ int lvl= 13;
+
+ while(lvl > 0) {
+ int side = (1 << (lvl-1)) + 1;
+ if ((s->totdisp % (side*side)) == 0) return s->totdisp / (side*side);
+ lvl--;
+ }
+
+ return 0;
+}
+
+void multiresModifier_scale_disp(Scene *scene, Object *ob)
+{
+ float smat[3][3];
+
+ /* object's scale matrix */
+ object_scale_to_mat3(ob, smat);
+
+ multires_apply_smat(scene, ob, smat);
+}
+
+void multiresModifier_prepare_join(Scene *scene, Object *ob, Object *to_ob)
+{
+ float smat[3][3], tmat[3][3], mat[3][3];
+ multires_sync_levels(scene, ob, to_ob);
+
+ /* construct scale matrix for displacement */
+ object_scale_to_mat3(to_ob, tmat);
+ invert_m3(tmat);
+ object_scale_to_mat3(ob, smat);
+ mul_m3_m3m3(mat, smat, tmat);
+
+ multires_apply_smat(scene, ob, mat);
+}
+
+/* update multires data after topology changing */
+void multires_topology_changed(Scene *scene, Object *ob)
+{
+ Mesh *me= (Mesh*)ob->data;
+ MDisps *mdisp= NULL, *cur= NULL;
+ int i, grid= 0, corners;
+ MultiresModifierData *mmd= get_multires_modifier(scene, ob, 1);
+
+ if(mmd)
+ multires_set_tot_mdisps(me, mmd->totlvl);
+
+ CustomData_external_read(&me->fdata, &me->id, CD_MASK_MDISPS, me->totface);
+ mdisp= CustomData_get_layer(&me->fdata, CD_MDISPS);
+
+ if(!mdisp) return;
+
+ cur= mdisp;
+ for(i = 0; i < me->totface; i++, cur++) {
+ if(mdisp->totdisp) {
+ corners= multires_mdisp_corners(mdisp);
+ grid= mdisp->totdisp / corners;
+
+ break;
+ }
+ }
+
+ for(i = 0; i < me->totface; i++, mdisp++) {
+ int nvert= me->mface[i].v4 ? 4 : 3;
+
+ /* allocate memory for mdisp, the whole disp layer would be erased otherwise */
+ if(!mdisp->totdisp) {
+ if(grid) {
+ mdisp->totdisp= nvert*grid;
+ mdisp->disps= MEM_callocN(mdisp->totdisp*sizeof(float)*3, "mdisp topology");
+ }
+
+ continue;
+ }
+
+ corners= multires_mdisp_corners(mdisp);
+
+ if(corners!=nvert) {
+ mdisp->totdisp= (mdisp->totdisp/corners)*nvert;
+
+ if(mdisp->disps)
+ MEM_freeN(mdisp->disps);
+
+ mdisp->disps= MEM_callocN(mdisp->totdisp*sizeof(float)*3, "mdisp topology");
+ }
+ }
+}
+
+/* makes displacement along grid boundary symmetrical */
+void multires_mdisp_smooth_bounds(MDisps *disps)
+{
+ int x, y, side, S, corners;
+ float (*out)[3];
+
+ corners = multires_mdisp_corners(disps);
+ side = sqrt(disps->totdisp / corners);
+
+ out = disps->disps;
+ for(S = 0; S < corners; S++) {
+ for(y = 0; y < side; ++y) {
+ for(x = 0; x < side; ++x, ++out) {
+ float (*dispgrid)[3];
+ float *data;
+
+ if(x != 0 && y != 0) continue;
+
+ if(corners == 4) {
+ if(S == 0) {
+ if(y == 0) {
+ dispgrid = &disps->disps[1*side*side];
+ data = dispgrid[side * x + 0];
+
+ (*out)[0] = (*out)[0] + data[1];
+ (*out)[1] = (*out)[1] - data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = -(*out)[1];
+ data[1] = (*out)[0];
+ data[2] = (*out)[2];
+ } else if (x == 0) {
+ dispgrid = &disps->disps[3 * side * side];
+ data = dispgrid[side * 0 + y];
+
+ (*out)[0] = (*out)[0] - data[1];
+ (*out)[1] = (*out)[1] + data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = (*out)[1];
+ data[1] = -(*out)[0];
+ data[2] = (*out)[2];
+ }
+ } else if (S == 2) {
+ if(y == 0) {
+ dispgrid = &disps->disps[3 * side * side];
+ data = dispgrid[side * x + 0];
+
+ (*out)[0] = (*out)[0] + data[1];
+ (*out)[1] = (*out)[1] - data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = -(*out)[1];
+ data[1] = (*out)[0];
+ data[2] = (*out)[2];
+ } else if(x == 0) {
+ dispgrid = &disps->disps[1 * side * side];
+ data = dispgrid[side * 0 + y];
+
+ (*out)[0] = (*out)[0] - data[1];
+ (*out)[1] = (*out)[1] + data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = (*out)[1];
+ data[1] = -(*out)[0];
+ data[2] = (*out)[2];
+ }
+ }
+ } else if (corners == 3) {
+ if(S == 0) {
+ if(y == 0) {
+ dispgrid = &disps->disps[1*side*side];
+ data = dispgrid[side * x + 0];
+
+ (*out)[0] = (*out)[0] + data[1];
+ (*out)[1] = (*out)[1] - data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = -(*out)[1];
+ data[1] = (*out)[0];
+ data[2] = (*out)[2];
+ } else if (x == 0) {
+ dispgrid = &disps->disps[2 * side * side];
+ data = dispgrid[side * 0 + y];
+
+ (*out)[0] = (*out)[0] - data[1];
+ (*out)[1] = (*out)[1] + data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = (*out)[1];
+ data[1] = -(*out)[0];
+ data[2] = (*out)[2];
+ }
+ } else if (S == 2) {
+ if(x == 0) {
+ dispgrid = &disps->disps[1 * side * side];
+ data = dispgrid[side * 0 + y];
+
+ (*out)[0] = (*out)[0] - data[1];
+ (*out)[1] = (*out)[1] + data[0];
+ (*out)[2] = (*out)[2] + data[2];
+
+ mul_v3_fl(*out, 0.5);
+
+ data[0] = (*out)[1];
+ data[1] = -(*out)[0];
+ data[2] = (*out)[2];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/***************** Multires interpolation stuff *****************/
+
+static void mdisp_get_crn_rect(int face_side, float crn[3][4][2])
+{
+ float offset = face_side*0.5f - 0.5f;
+ float mid[2];
+
+ mid[0] = offset * 4 / 3;
+ mid[1] = offset * 2 / 3;
+
+ crn[0][0][0] = mid[0]; crn[0][0][1] = mid[1];
+ crn[0][1][0] = offset; crn[0][1][1] = 0;
+ crn[0][2][0] = 0; crn[0][2][1] = 0;
+ crn[0][3][0] = offset; crn[0][3][1] = offset;
+
+ crn[1][0][0] = mid[0]; crn[1][0][1] = mid[1];
+ crn[1][1][0] = offset * 2; crn[1][1][1] = offset;
+ crn[1][2][0] = offset * 2; crn[1][2][1] = 0;
+ crn[1][3][0] = offset; crn[1][3][1] = 0;
+
+ crn[2][0][0] = mid[0]; crn[2][0][1] = mid[1];
+ crn[2][1][0] = offset; crn[2][1][1] = offset;
+ crn[2][2][0] = offset * 2; crn[2][2][1] = offset * 2;
+ crn[2][3][0] = offset * 2; crn[2][3][1] = offset;
+}
+
+static int mdisp_pt_in_crn(float p[2], float crn[4][2])
+{
+ float v[2][2];
+ float a[2][2];
+
+ sub_v2_v2v2(v[0], crn[1], crn[0]);
+ sub_v2_v2v2(v[1], crn[3], crn[0]);
+
+ sub_v2_v2v2(a[0], p, crn[0]);
+ sub_v2_v2v2(a[1], crn[2], crn[0]);
+
+ if(cross_v2v2(a[0], v[0]) * cross_v2v2(a[1], v[0]) < 0)
+ return 0;
+
+ if(cross_v2v2(a[0], v[1]) * cross_v2v2(a[1], v[1]) < 0)
+ return 0;
+
+ return 1;
+}
+
+static void face_to_crn_interp(float u, float v, float v1[2], float v2[2], float v3[2], float v4[2], float *x)
+{
+ float a = (v4[1]-v3[1])*v2[0]+(-v4[1]+v3[1])*v1[0]+(-v2[1]+v1[1])*v4[0]+(v2[1]-v1[1])*v3[0];
+ float b = (v3[1]-v)*v2[0]+(v4[1]-2*v3[1]+v)*v1[0]+(-v4[1]+v3[1]+v2[1]-v1[1])*u+(v4[0]-v3[0])*v-v1[1]*v4[0]+(-v2[1]+2*v1[1])*v3[0];
+ float c = (v3[1]-v)*v1[0]+(-v3[1]+v1[1])*u+v3[0]*v-v1[1]*v3[0];
+ float d = b * b - 4 * a * c;
+ float x1, x2;
+
+ if(a == 0) {
+ *x = -c / b;
+ return;
+ }
+
+ x1 = (-b - sqrtf(d)) / (2 * a);
+ x2 = (-b + sqrtf(d)) / (2 * a);
+
+ *x = maxf(x1, x2);
+}
+
+void mdisp_rot_crn_to_face(const int S, const int corners, const int face_side, const float x, const float y, float *u, float *v)
+{
+ float offset = face_side*0.5f - 0.5f;
+
+ if(corners == 4) {
+ if(S == 1) { *u= offset + x; *v = offset - y; }
+ if(S == 2) { *u= offset + y; *v = offset + x; }
+ if(S == 3) { *u= offset - x; *v = offset + y; }
+ if(S == 0) { *u= offset - y; *v = offset - x; }
+ } else {
+ float crn[3][4][2], vec[4][2];
+ float p[2];
+
+ mdisp_get_crn_rect(face_side, crn);
+
+ interp_v2_v2v2(vec[0], crn[S][0], crn[S][1], x / offset);
+ interp_v2_v2v2(vec[1], crn[S][3], crn[S][2], x / offset);
+ interp_v2_v2v2(vec[2], crn[S][0], crn[S][3], y / offset);
+ interp_v2_v2v2(vec[3], crn[S][1], crn[S][2], y / offset);
+
+ isect_seg_seg_v2_point(vec[0], vec[1], vec[2], vec[3], p);
+
+ (*u) = p[0];
+ (*v) = p[1];
+ }
+}
+
+int mdisp_rot_face_to_crn(const int corners, const int face_side, const float u, const float v, float *x, float *y)
+{
+ const float offset = face_side*0.5f - 0.5f;
+ int S = 0;
+
+ if (corners == 4) {
+ if(u <= offset && v <= offset) S = 0;
+ else if(u > offset && v <= offset) S = 1;
+ else if(u > offset && v > offset) S = 2;
+ else if(u <= offset && v >= offset) S = 3;
+
+ if(S == 0) {
+ *y = offset - u;
+ *x = offset - v;
+ } else if(S == 1) {
+ *x = u - offset;
+ *y = offset - v;
+ } else if(S == 2) {
+ *y = u - offset;
+ *x = v - offset;
+ } else if(S == 3) {
+ *x= offset - u;
+ *y = v - offset;
+ }
+ } else {
+ float crn[3][4][2];
+ float p[2] = {u, v};
+
+ mdisp_get_crn_rect(face_side, crn);
+
+ for (S = 0; S < 3; ++S) {
+ if (mdisp_pt_in_crn(p, crn[S]))
+ break;
+ }
+
+ face_to_crn_interp(u, v, crn[S][0], crn[S][1], crn[S][3], crn[S][2], &p[0]);
+ face_to_crn_interp(u, v, crn[S][0], crn[S][3], crn[S][1], crn[S][2], &p[1]);
+
+ *x = p[0] * offset;
+ *y = p[1] * offset;
+ }
+
+ return S;
+}
+
+void mdisp_apply_weight(const int S, const int corners, int x, int y, const int face_side,
+ float crn_weight[4][2], float *u_r, float *v_r)
+{
+ float u, v, xl, yl;
+ float mid1[2], mid2[2], mid3[2];
+
+ mdisp_rot_crn_to_face(S, corners, face_side, x, y, &u, &v);
+
+ if(corners == 4) {
+ xl = u / (face_side - 1);
+ yl = v / (face_side - 1);
+
+ mid1[0] = crn_weight[0][0] * (1 - xl) + crn_weight[1][0] * xl;
+ mid1[1] = crn_weight[0][1] * (1 - xl) + crn_weight[1][1] * xl;
+ mid2[0] = crn_weight[3][0] * (1 - xl) + crn_weight[2][0] * xl;
+ mid2[1] = crn_weight[3][1] * (1 - xl) + crn_weight[2][1] * xl;
+ mid3[0] = mid1[0] * (1 - yl) + mid2[0] * yl;
+ mid3[1] = mid1[1] * (1 - yl) + mid2[1] * yl;
+ } else {
+ yl = v / (face_side - 1);
+
+ if(v == face_side - 1) xl = 1;
+ else xl = 1 - (face_side - 1 - u) / (face_side - 1 - v);
+
+ mid1[0] = crn_weight[0][0] * (1 - xl) + crn_weight[1][0] * xl;
+ mid1[1] = crn_weight[0][1] * (1 - xl) + crn_weight[1][1] * xl;
+ mid3[0] = mid1[0] * (1 - yl) + crn_weight[2][0] * yl;
+ mid3[1] = mid1[1] * (1 - yl) + crn_weight[2][1] * yl;
+ }
+
+ *u_r = mid3[0];
+ *v_r = mid3[1];
+}
+
+void mdisp_flip_disp(const int S, const int corners, const float axis_x[2], const float axis_y[2], float disp[3])
+{
+ float crn_x[2], crn_y[2];
+ float vx[2], vy[2], coord[2];
+
+ if (corners == 4) {
+ float x[4][2] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
+ float y[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
+
+ copy_v2_v2(crn_x, x[S]);
+ copy_v2_v2(crn_y, y[S]);
+
+ mul_v2_v2fl(vx, crn_x, disp[0]);
+ mul_v2_v2fl(vy, crn_y, disp[1]);
+ add_v2_v2v2(coord, vx, vy);
+
+ project_v2_v2v2(vx, coord, axis_x);
+ project_v2_v2v2(vy, coord, axis_y);
+
+ disp[0] = len_v2(vx);
+ disp[1] = len_v2(vy);
+
+ if(dot_v2v2(vx, axis_x) < 0)
+ disp[0] = -disp[0];
+
+ if(dot_v2v2(vy, axis_y) < 0)
+ disp[1] = -disp[1];
+ } else {
+ /* XXX: it was very overhead code to support displacement flipping
+ for case of tris without visible profit.
+ Maybe its not really big limitation? for now? (nazgul) */
+ disp[0] = 0;
+ disp[1] = 0;
+ }
+}
+
+/* Join two triangular displacements into one quad
+ Corners mapping:
+ 2 -------- 3
+ | \ tri2 |
+ | \ |
+ | tri1 \ |
+ 0 -------- 1 */
+void mdisp_join_tris(MDisps *dst, MDisps *tri1, MDisps *tri2)
+{
+ int side, st;
+ int S, x, y, crn;
+ float face_u, face_v, crn_u, crn_v;
+ float (*out)[3];
+ MDisps *src;
+
+ if(dst->disps)
+ MEM_freeN(dst->disps);
+
+ side = sqrt(tri1->totdisp / 3);
+ st = (side<<1)-1;
+
+ dst->totdisp = 4 * side * side;
+ out = dst->disps = MEM_callocN(3*dst->totdisp*sizeof(float), "join disps");
+
+ for(S = 0; S < 4; S++)
+ for(y = 0; y < side; ++y)
+ for(x = 0; x < side; ++x, ++out) {
+ mdisp_rot_crn_to_face(S, 4, st, x, y, &face_u, &face_v);
+ face_u = st - 1 - face_u;
+
+ if(face_v > face_u) {
+ src = tri2;
+ face_u = st - 1 - face_u;
+ face_v = st - 1 - face_v;
+ } else src = tri1;
+
+ crn = mdisp_rot_face_to_crn(3, st, face_u, face_v, &crn_u, &crn_v);
+
+ old_mdisps_bilinear((*out), &src->disps[crn*side*side], side, crn_u, crn_v);
+ (*out)[0] = 0;
+ (*out)[1] = 0;
+ }
+}