diff options
Diffstat (limited to 'source/blender/blenkernel/intern/multires.c')
-rw-r--r-- | source/blender/blenkernel/intern/multires.c | 983 |
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; + } +} |