diff options
author | Andrew Wiggin <ender79bl@gmail.com> | 2011-11-13 19:13:59 +0400 |
---|---|---|
committer | Andrew Wiggin <ender79bl@gmail.com> | 2011-11-13 19:13:59 +0400 |
commit | 2266c7fc1cc675fe7465459e31d434bcd15e3d14 (patch) | |
tree | 7440bdeba9149f672a2aa726894dc3f190b9043e /source | |
parent | c0934795cb50d107a769e258b122c4c5b7cbad69 (diff) |
Add CD_POLYINDEX layer to reduce need for retesselations
Diffstat (limited to 'source')
21 files changed, 176 insertions, 154 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 0b1f6d7d9b0..a8a936e0bab 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -129,11 +129,14 @@ struct DerivedMesh { struct GPUDrawObject *drawObject; DerivedMeshType type; - /* Misc. Queries */ - - /*recalculates mesh tesselation*/ + /* calculate vert and face normals */ + void (*calcNormals)(DerivedMesh *dm); + + /* recalculates mesh tesselation */ void (*recalcTesselation)(DerivedMesh *dm); + /* Misc. Queries */ + /* Also called in Editmode */ int (*getNumVerts)(DerivedMesh *dm); int (*getNumEdges)(DerivedMesh *dm); @@ -166,8 +169,8 @@ struct DerivedMesh { void (*copyVertArray)(DerivedMesh *dm, struct MVert *vert_r); void (*copyEdgeArray)(DerivedMesh *dm, struct MEdge *edge_r); void (*copyTessFaceArray)(DerivedMesh *dm, struct MFace *face_r); - void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *loop_r); - void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *poly_r); + void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *loop_r); + void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *poly_r); /* return a copy of all verts/edges/faces from the derived mesh * it is the caller's responsibility to free the returned pointer @@ -175,8 +178,8 @@ struct DerivedMesh { struct MVert *(*dupVertArray)(DerivedMesh *dm); struct MEdge *(*dupEdgeArray)(DerivedMesh *dm); struct MFace *(*dupTessFaceArray)(DerivedMesh *dm); - struct MLoop *(*dupLoopArray)(DerivedMesh *dm); - struct MPoly *(*dupPolyArray)(DerivedMesh *dm); + struct MLoop *(*dupLoopArray)(DerivedMesh *dm); + struct MPoly *(*dupPolyArray)(DerivedMesh *dm); /* return a pointer to a single element of vert/edge/face custom data * from the derived mesh (this gives a pointer to the actual data, not diff --git a/source/blender/blenkernel/BKE_cdderivedmesh.h b/source/blender/blenkernel/BKE_cdderivedmesh.h index affeca2b007..c24db5dd1f8 100644 --- a/source/blender/blenkernel/BKE_cdderivedmesh.h +++ b/source/blender/blenkernel/BKE_cdderivedmesh.h @@ -111,11 +111,8 @@ void CDDM_calc_edges(struct DerivedMesh *dm); faces*/ void CDDM_calc_edges_poly(struct DerivedMesh *dm); -/*reconstitute face triangulation. if orig_use_polyorig is nonzero, sets - the mface origindex layer to copy to the origindex values of the - parent mpolys; otherwise the mface origindex will point to the index of - the parent mpoly*/ -void CDDM_recalc_tesselation(struct DerivedMesh *dm, int orig_use_polyorig); +/* reconstitute face triangulation */ +void CDDM_recalc_tesselation(struct DerivedMesh *dm); /* lowers the number of vertices/edges/faces in a CDDerivedMesh * the layer data stays the same size diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 3bca29771d8..1e175185b33 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -75,7 +75,7 @@ struct BMesh *BKE_mesh_to_bmesh(struct Mesh *me, struct Object *ob); */ int mesh_recalcTesselation(struct CustomData *fdata, struct CustomData *ldata, struct CustomData *pdata, struct MVert *mvert, int totface, - int totloop, int totpoly, int use_poly_origindex, int use_face_origindex); + int totloop, int totpoly); /*calculates a face normal.*/ void mesh_calc_poly_normal(struct MPoly *mpoly, struct MLoop *loopstart, diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index deb201793b8..68aa5f883f4 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -365,13 +365,11 @@ void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob) totvert = tmp.totvert = dm->getNumVerts(dm); totedge = tmp.totedge = dm->getNumEdges(dm); - totface = tmp.totface = dm->getNumTessFaces(dm); totpoly = tmp.totpoly = dm->getNumFaces(dm); totloop = tmp.totloop = dm->numLoopData; CustomData_copy(&dm->vertData, &tmp.vdata, CD_MASK_MESH, CD_DUPLICATE, totvert); CustomData_copy(&dm->edgeData, &tmp.edata, CD_MASK_MESH, CD_DUPLICATE, totedge); - CustomData_copy(&dm->faceData, &tmp.fdata, CD_MASK_MESH, CD_DUPLICATE, totface); CustomData_copy(&dm->loopData, &tmp.ldata, CD_MASK_MESH, CD_DUPLICATE, totloop); CustomData_copy(&dm->polyData, &tmp.pdata, CD_MASK_MESH, CD_DUPLICATE, totpoly); @@ -406,8 +404,6 @@ void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob) CustomData_add_layer(&tmp.vdata, CD_MVERT, CD_ASSIGN, dm->dupVertArray(dm), totvert); if(!CustomData_has_layer(&tmp.edata, CD_MEDGE)) CustomData_add_layer(&tmp.edata, CD_MEDGE, CD_ASSIGN, dm->dupEdgeArray(dm), totedge); - if(!CustomData_has_layer(&tmp.fdata, CD_MFACE)) - CustomData_add_layer(&tmp.fdata, CD_MFACE, CD_ASSIGN, dm->dupTessFaceArray(dm), totface); if(!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { tmp.mloop = dm->dupLoopArray(dm); tmp.mpoly = dm->dupPolyArray(dm); @@ -425,6 +421,7 @@ void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob) } } + tmp.totface = mesh_recalcTesselation(&tmp.fdata, &tmp.ldata, &tmp.pdata, tmp.mvert, tmp.totface, tmp.totloop, tmp.totpoly); mesh_update_customdata_pointers(&tmp); CustomData_free(&me->vdata, me->totvert); @@ -530,7 +527,7 @@ void *DM_get_edge_data_layer(DerivedMesh *dm, int type) void *DM_get_tessface_data_layer(DerivedMesh *dm, int type) { - if(type == CD_MFACE) + if (type == CD_MFACE) return dm->getTessFaceArray(dm); return CustomData_get_layer(&dm->faceData, type); @@ -1457,6 +1454,8 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos } #endif /* WITH_GAMEENGINE */ + finaldm->calcNormals(finaldm); + *final_r = finaldm; if(orcodm) @@ -1508,7 +1507,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D ModifierData *md; float (*deformedVerts)[3] = NULL; CustomDataMask mask; - DerivedMesh *dm = NULL, *orcodm = NULL; + DerivedMesh *dm = NULL, *orcodm = NULL, *finaldm = NULL; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(scene, ob, NULL, 1); LinkNode *datamasks, *curr; int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; @@ -1660,21 +1659,24 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D * then we need to build one. */ if(dm && deformedVerts) { - *final_r = CDDM_copy(dm, 0); + finaldm = CDDM_copy(dm, 0); if(!(cage_r && dm == *cage_r)) dm->release(dm); CDDM_apply_vert_coords(*final_r, deformedVerts); - CDDM_calc_normals(*final_r); } else if (dm) { - *final_r = dm; + finaldm = dm; } else if (!deformedVerts && cage_r && *cage_r) { - *final_r = *cage_r; + finaldm = *cage_r; } else { - *final_r = getEditDerivedBMesh(em, ob, deformedVerts); + finaldm = getEditDerivedBMesh(em, ob, deformedVerts); deformedVerts = NULL; } + finaldm->calcNormals(finaldm); + + *final_r = finaldm; + /* add an orco layer if needed */ if(dataMask & CD_MASK_ORCO) add_orco_dm(ob, em, *final_r, orcodm, CD_ORCO); diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 0247dee07e9..cd608968f93 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -1580,37 +1580,17 @@ static void cdDM_foreachMappedFaceCenter( } -static void cdDM_recalcTesselation(DerivedMesh *dm) +void CDDM_recalc_tesselation(DerivedMesh *dm) { CDDerivedMesh *cddm = (CDDerivedMesh*)dm; dm->numFaceData = mesh_recalcTesselation(&dm->faceData, &dm->loopData, &dm->polyData, cddm->mvert, dm->numFaceData, dm->numLoopData, - dm->numPolyData, 1, 0); + dm->numPolyData); cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE); } -/*ignores original poly origindex layer*/ -static void cdDM_recalcTesselation2(DerivedMesh *dm) -{ - CDDerivedMesh *cddm = (CDDerivedMesh*)dm; - - dm->numFaceData = mesh_recalcTesselation(&dm->faceData, &dm->loopData, - &dm->polyData, cddm->mvert, dm->numFaceData, dm->numLoopData, - dm->numPolyData, 0, 0); - - cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE); -} - -void CDDM_recalc_tesselation(DerivedMesh *dm, int orig_use_polyorig) -{ - if (orig_use_polyorig) - cdDM_recalcTesselation(dm); - else - cdDM_recalcTesselation2(dm); -} - static void cdDM_free_internal(CDDerivedMesh *cddm) { if(cddm->fmap) MEM_freeN(cddm->fmap); @@ -1662,9 +1642,10 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->getVertDataArray = DM_get_vert_data_layer; dm->getEdgeDataArray = DM_get_edge_data_layer; dm->getTessFaceDataArray = DM_get_tessface_data_layer; - + + dm->calcNormals = CDDM_calc_normals; //doesn't work yet for all cases - //dm->recalcTesselation = cdDM_recalcTesselation; + //dm->recalcTesselation = CDDM_recalc_tesselation; dm->getVertCos = cdDM_getVertCos; dm->getVertCo = cdDM_getVertCo; @@ -1708,6 +1689,7 @@ DerivedMesh *CDDM_new(int numVerts, int numEdges, int numFaces, int numLoops, in CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, numVerts); CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numFaces); + CustomData_add_layer(&dm->faceData, CD_POLYINDEX, CD_CALLOC, NULL, numFaces); CustomData_add_layer(&dm->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, numPolys); CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, numVerts); @@ -1731,6 +1713,7 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *UNUSED(ob)) DerivedMesh *dm = &cddm->dm; CustomDataMask mask = CD_MASK_MESH & (~CD_MASK_MDISPS); int alloctype; + int *polyindex = NULL; /* this does a referenced copy, with an exception for fluidsim */ @@ -1745,7 +1728,7 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *UNUSED(ob)) mesh->totvert); CustomData_merge(&mesh->edata, &dm->edgeData, mask, alloctype, mesh->totedge); - CustomData_merge(&mesh->fdata, &dm->faceData, mask|CD_MASK_ORIGINDEX, alloctype, + CustomData_merge(&mesh->fdata, &dm->faceData, mask|CD_MASK_POLYINDEX, alloctype, mesh->totface); CustomData_merge(&mesh->ldata, &dm->loopData, mask, alloctype, mesh->totloop); @@ -1758,8 +1741,12 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *UNUSED(ob)) cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE); - if (!CustomData_has_layer(&cddm->dm.faceData, CD_ORIGINDEX)) - CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, mesh->totface); + BLI_assert(CustomData_has_layer(&cddm->dm.faceData, CD_POLYINDEX)); + + polyindex = CustomData_get_layer(&dm->faceData, CD_POLYINDEX); + if (!CustomData_has_layer(&cddm->dm.faceData, CD_ORIGINDEX)) { + CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_REFERENCE, polyindex, mesh->totface); + } return dm; } @@ -1841,7 +1828,7 @@ static DerivedMesh *disabled__CDDM_from_editmesh(EditMesh *em, Mesh *UNUSED(me)) /* CustomData_from_em_block(&em->edata, &dm->edgeData, eed->data, i); */ } - index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + index = dm->getTessFaceDataArray(dm, CD_POLYINDEX); for(i = 0, efa = em->faces.first; i < dm->numFaceData; i++, efa = efa->next, index++) { MFace *mf = &mface[i]; @@ -1980,7 +1967,7 @@ DerivedMesh *CDDM_from_BMEditMesh(BMEditMesh *em, Mesh *UNUSED(me), int use_mdis MPoly *mpoly = cddm->mpoly; int numCol = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPCOL); int numTex = CustomData_number_of_layers(&em->bm->pdata, CD_MTEXPOLY); - int i, j, *index, add_orig; + int i, j, *index, *polyindex, add_orig; int has_crease, has_edge_bweight, has_vert_bweight; int flag; @@ -2063,6 +2050,7 @@ DerivedMesh *CDDM_from_BMEditMesh(BMEditMesh *em, Mesh *UNUSED(me), int use_mdis BM_SetIndex(efa, i); } + polyindex = dm->getTessFaceDataArray(dm, CD_POLYINDEX); index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); for(i = 0; i < dm->numFaceData; i++, index++) { MFace *mf = &mface[i]; @@ -2077,6 +2065,7 @@ DerivedMesh *CDDM_from_BMEditMesh(BMEditMesh *em, Mesh *UNUSED(me), int use_mdis mf->flag = BMFlags_To_MEFlags(efa); *index = add_orig ? BM_GetIndex(efa) : *(int*)CustomData_bmesh_get(&bm->pdata, efa->head.data, CD_ORIGINDEX); + *polyindex = BM_GetIndex(efa); loops_to_customdata_corners(bm, &dm->faceData, i, l, numCol, numTex); test_index_face(mf, &dm->faceData, i, 3); @@ -2151,8 +2140,10 @@ DerivedMesh *CDDM_copy(DerivedMesh *source, int faces_from_tessfaces) cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP); cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); - - cdDM_recalcTesselation((DerivedMesh *)cddm); + + /* BMESH_TODO: Find out why this is necessary (or else find a way to remove + it). If it is necessary, add a comment explaining why. */ + CDDM_recalc_tesselation((DerivedMesh *)cddm); return dm; } @@ -2187,6 +2178,8 @@ DerivedMesh *CDDM_from_template(DerivedMesh *source, CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); if(!CustomData_get_layer(&dm->faceData, CD_ORIGINDEX)) CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numFaces); + if(!CustomData_get_layer(&dm->faceData, CD_POLYINDEX)) + CustomData_add_layer(&dm->faceData, CD_POLYINDEX, CD_CALLOC, NULL, numFaces); cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE); @@ -2234,21 +2227,23 @@ void CDDM_calc_normals(DerivedMesh *dm) /* we don't want to overwrite any referenced layers */ cddm->mvert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT); - - /*set tesselation origindex values to map to poly indices, rather then poly - poly origindex values*/ - cdDM_recalcTesselation2(dm); - + + if (dm->numFaceData == 0) { + /* No tesselation on this mesh yet, need to calculate one */ + CDDM_recalc_tesselation(dm); + } + else { + /* A tesselation already exists, it should always have a CD_POLYINDEX */ + BLI_assert(CustomData_has_layer(&dm->faceData, CD_POLYINDEX)); + } + face_nors = MEM_mallocN(sizeof(float)*3*dm->numFaceData, "face_nors"); /* calculate face normals */ mesh_calc_normals(cddm->mvert, dm->numVertData, CDDM_get_loops(dm), CDDM_get_polys(dm), dm->numLoopData, dm->numPolyData, NULL, cddm->mface, dm->numFaceData, - CustomData_get_layer(&dm->faceData, CD_ORIGINDEX), face_nors); + CustomData_get_layer(&dm->faceData, CD_POLYINDEX), face_nors); - /*restore tesselation origindex indices to poly origindex indices*/ - cdDM_recalcTesselation(dm); - CustomData_add_layer(&dm->faceData, CD_NORMAL, CD_ASSIGN, face_nors, dm->numFaceData); } @@ -2424,7 +2419,7 @@ DerivedMesh *CDDM_merge_verts(DerivedMesh *dm, int *vtargetmap) memcpy(cddm2->mpoly, mpoly, sizeof(MPoly)*BLI_array_count(mpoly)); BLI_array_free(mvert); BLI_array_free(medge); BLI_array_free(mloop); BLI_array_free(mpoly); - CDDM_recalc_tesselation((DerivedMesh*)cddm2, 1); + CDDM_recalc_tesselation((DerivedMesh*)cddm2); if (newv) MEM_freeN(newv); @@ -2657,7 +2652,7 @@ void CDDM_tessfaces_to_faces(DerivedMesh *dm) MLoop *ml; MPoly *mp; EdgeHash *eh = BLI_edgehash_new(); - int i, l, totloop, *index1, *index2; + int i, l, totloop, *polyindex; /*ensure we have all the edges we need*/ CDDM_calc_edges(dm); @@ -2690,8 +2685,7 @@ void CDDM_tessfaces_to_faces(DerivedMesh *dm) CustomData_merge(&cddm->dm.faceData, &cddm->dm.polyData, CD_MASK_ORIGINDEX, CD_DUPLICATE, cddm->dm.numFaceData); - index1 = CustomData_get_layer(&cddm->dm.faceData, CD_ORIGINDEX); - index2 = CustomData_get_layer(&cddm->dm.polyData, CD_ORIGINDEX); + polyindex = CustomData_get_layer(&cddm->dm.faceData, CD_POLYINDEX); mf = cddm->mface; mp = cddm->mpoly; @@ -2721,7 +2715,7 @@ void CDDM_tessfaces_to_faces(DerivedMesh *dm) ml++, l++; } - *index2 = *index1; + *polyindex = i; } BLI_edgehash_free(eh, NULL); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 7fd03267ab9..f005ed4a2a3 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1048,8 +1048,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 8: CD_NORMAL */ /* 3 floats per normal vector */ {sizeof(float)*3, "vec3f", 1, NULL, NULL, NULL, NULL, NULL, NULL}, - /* 9: CD_FLAGS */ - {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, + /* 9: CD_POLYINDEX */ + {sizeof(int), "MIntProperty", 1, NULL, NULL, NULL, NULL, NULL, NULL}, /* 10: CD_PROP_FLT */ {sizeof(MFloatProperty), "MFloatProperty",1,"Float", layerCopy_propFloat,NULL,NULL,NULL}, /* 11: CD_PROP_INT */ @@ -1103,7 +1103,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { static const char *LAYERTYPENAMES[CD_NUMTYPES] = { /* 0-4 */ "CDMVert", "CDMSticky", "CDMDeformVert", "CDMEdge", "CDMFace", "CDMTFace", - /* 5-9 */ "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty", + /* 5-9 */ "CDMCol", "CDOrigIndex", "CDNormal", "CDPolyIndex","CDMFloatProperty", /* 10-14 */ "CDMIntProperty","CDMStringProperty", "CDOrigSpace", "CDOrco", "CDMTexPoly", "CDMLoopUV", /* 15-19 */ "CDMloopCol", "CDTangent", "CDMDisps", "CDWeightMCol", "CDMPoly", /* 20-24 */ "CDMLoop", "CDMClothOrco", "CDMLoopCol", "CDIDCol", "CDTextureCol", @@ -1126,10 +1126,11 @@ const CustomDataMask CD_MASK_EDITMESH = CD_MASK_MDISPS | CD_MASK_SHAPEKEY | CD_MASK_RECAST; const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | - CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO | + CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_WEIGHT_MLOOPCOL | CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORCO | CD_MASK_TANGENT | - CD_MASK_WEIGHT_MCOL | CD_MASK_NORMAL | CD_MASK_SHAPEKEY | CD_MASK_RECAST; + CD_MASK_WEIGHT_MCOL | CD_MASK_NORMAL | CD_MASK_SHAPEKEY | CD_MASK_RECAST | + CD_MASK_ORIGINDEX | CD_MASK_POLYINDEX; const CustomDataMask CD_MASK_BMESH = CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS; diff --git a/source/blender/blenkernel/intern/editderivedbmesh.c b/source/blender/blenkernel/intern/editderivedbmesh.c index 1c95bc7d683..d0db7c82ce6 100644 --- a/source/blender/blenkernel/intern/editderivedbmesh.c +++ b/source/blender/blenkernel/intern/editderivedbmesh.c @@ -362,6 +362,12 @@ static void bmdm_recalc_lookups(EditDerivedBMesh *bmdm) } } +static void bmDM_calcNormals(DerivedMesh *UNUSED(dm)) +{ + /* Nothing to do: normals are already calculated and stored on the + BMVerts and BMFaces */ +} + static void bmDM_recalcTesselation(DerivedMesh *UNUSED(dm)) { //EditDerivedBMesh *bmdm= (EditDerivedBMesh*) dm; @@ -1668,6 +1674,7 @@ DerivedMesh *getEditDerivedBMesh(BMEditMesh *em, Object *UNUSED(ob), bmdm->dm.getTessFaceDataArray = bmDM_getFaceDataArray; + bmdm->dm.calcNormals = bmDM_calcNormals; bmdm->dm.recalcTesselation = bmDM_recalcTesselation; bmdm->dm.foreachMappedVert = bmDM_foreachMappedVert; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 3aa35bed4e3..e106ebae63d 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1142,6 +1142,12 @@ void mball_to_mesh(ListBase *lb, Mesh *me) make_edges(me, 0); // all edges convert_mfaces_to_mpolys(me); + + me->totface = mesh_recalcTesselation( + &me->fdata, &me->ldata, &me->pdata, + me->mvert, me->totface, me->totloop, me->totpoly); + + mesh_update_customdata_pointers(me); } } @@ -1968,6 +1974,8 @@ void convert_mfaces_to_mpolys(Mesh *mesh) /* note, we dont convert FGons at all, these are not even real ngons, * they have their own UV's, colors etc - its more an editing feature. */ + mesh_update_customdata_pointers(mesh); + BLI_edgehash_free(eh, NULL); } @@ -1978,7 +1986,7 @@ float (*mesh_getVertexCos(Mesh *me, int *numVerts_r))[3] if (numVerts_r) *numVerts_r = numVerts; for (i=0; i<numVerts; i++) - VECCOPY(cos[i], me->mvert[i].co); + copy_v3_v3(cos[i], me->mvert[i].co); return cos; } @@ -2260,21 +2268,11 @@ void mesh_loops_to_tri_corners(CustomData *fdata, CustomData *ldata, /* this function recreates a tesselation. returns number of tesselation faces. - - use_poly_origindex sets whether or not the tesselation faces' origindex - layer should point to original poly indices or real poly indices. - - use_face_origindex sets the tesselation faces' origindex layer - to point to the tesselation faces themselves, not the polys. - - if both of the above are 0, it'll use the indices of the mpolys of the MPoly - data in pdata, and ignore the origindex layer altogether. */ int mesh_recalcTesselation(CustomData *fdata, CustomData *ldata, CustomData *pdata, MVert *mvert, int totface, int UNUSED(totloop), - int totpoly, int use_poly_origindex, - int use_face_origindex) + int totpoly) { MPoly *mp, *mpoly; MLoop *ml, *mloop; @@ -2282,8 +2280,11 @@ int mesh_recalcTesselation(CustomData *fdata, BLI_array_declare(mf); EditVert *v, *lastv, *firstv; EditFace *f; + int *origIndex = NULL; BLI_array_declare(origIndex); - int i, j, k, lindex[4], *origIndex = NULL, *polyorigIndex; + int *polyIndex = NULL; + BLI_array_declare(polyIndex); + int i, j, k, lindex[4], *polyorigIndex; int numTex, numCol; mpoly = CustomData_get_layer(pdata, CD_MPOLY); @@ -2294,7 +2295,7 @@ int mesh_recalcTesselation(CustomData *fdata, k = 0; mp = mpoly; - polyorigIndex = use_poly_origindex? CustomData_get_layer(pdata, CD_ORIGINDEX) : NULL; + polyorigIndex = CustomData_get_layer(pdata, CD_ORIGINDEX); for (i=0; i<totpoly; i++, mp++) { if (mp->totloop > 2) { ml = mloop + mp->loopstart; @@ -2304,10 +2305,6 @@ int mesh_recalcTesselation(CustomData *fdata, lastv = NULL; for (j=0; j<mp->totloop; j++, ml++) { v = BLI_addfillvert(mvert[ml->v].co); - if (polyorigIndex && use_poly_origindex) - v->hash = polyorigIndex[i]; - else - v->hash = i; v->keyindex = mp->loopstart + j; @@ -2323,20 +2320,21 @@ int mesh_recalcTesselation(CustomData *fdata, BLI_edgefill(2); for (f=fillfacebase.first; f; f=f->next) { BLI_array_growone(mf); - BLI_array_growone(origIndex); + BLI_array_append(polyIndex, i); /*these are loop indices, they'll be transformed into vert indices later.*/ mf[k].v1 = f->v1->keyindex; mf[k].v2 = f->v2->keyindex; mf[k].v3 = f->v3->keyindex; - - /*put poly index in mf->v4*/ - mf[k].v4 = f->v1->hash; + mf[k].v4 = 0; mf[k].mat_nr = mp->mat_nr; mf[k].flag = mp->flag; - origIndex[k] = use_face_origindex ? k : f->v1->hash; + + if (polyorigIndex) { + BLI_array_append(origIndex, polyorigIndex[polyIndex[k]]); + } k++; } @@ -2350,9 +2348,23 @@ int mesh_recalcTesselation(CustomData *fdata, totface = k; CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mf, totface); - CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, origIndex, totface); + CustomData_add_layer(fdata, CD_POLYINDEX, CD_ASSIGN, polyIndex, totface); + if (origIndex) { + CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, origIndex, totface); + } + CustomData_from_bmeshpoly(fdata, pdata, ldata, totface); + /* If polys have a normals layer, copying that to faces can help + avoid the need to recalculate normals later */ + if (CustomData_has_layer(pdata, CD_NORMAL)) { + float *pnors = CustomData_get_layer(pdata, CD_NORMAL); + float *fnors = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); + for (i=0; i<totface; i++, fnors++) { + copy_v3_v3(fnors, &pnors[polyIndex[i]]); + } + } + mface = mf; for (i=0; i<totface; i++, mf++) { /*sort loop indices to ensure winding is correct*/ @@ -2374,9 +2386,7 @@ int mesh_recalcTesselation(CustomData *fdata, mf->v3 = mloop[mf->v3].v; mesh_loops_to_tri_corners(fdata, ldata, pdata, - lindex, i, mf->v4); - - mf->v4 = 0; + lindex, i, polyIndex[i]); } return totface; @@ -2450,7 +2460,6 @@ static void mesh_calc_ngon_normal(MPoly *mpoly, MLoop *loopstart, normal[0] = (float) n[0]; normal[1] = (float) n[1]; normal[2] = (float) n[2]; - } void mesh_calc_poly_normal(MPoly *mpoly, MLoop *loopstart, diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 810f9d00d87..9e82bae9195 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -2758,6 +2758,11 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm) return ccgdm->pbvh; } +static void ccgDM_calcNormals(DerivedMesh *UNUSED(dm)) +{ + /* Nothing to do: CCG calculates normals during drawing */ +} + static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, int drawInteriorEdges, int useSubsurfUv, @@ -2847,6 +2852,8 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.getEdgeData = DM_get_edge_data; ccgdm->dm.getTessFaceData = DM_get_face_data; + ccgdm->dm.calcNormals = ccgDM_calcNormals; + ccgdm->dm.getVertCos = cgdm_getVertCos; ccgdm->dm.foreachMappedVert = cgdm_foreachMappedVert; ccgdm->dm.foreachMappedEdge = cgdm_foreachMappedEdge; @@ -2867,7 +2874,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.drawMappedEdgesInterp = cgdm_drawMappedEdgesInterp; ccgdm->dm.drawMappedEdges = cgdm_drawMappedEdges; - + ccgdm->dm.release = cgdm_release; ccgdm->ss = ss; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 547e782e872..357cbffc717 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3657,6 +3657,22 @@ static void lib_link_mesh(FileData *fd, Main *main) convert_mfaces_to_mpolys(me); } + /* + * Re-tesselate, even if the polys were just created from tessfaces, this + * is important because it: + * - fill the CD_POLYINDEX layer + * - gives consistency of tessface between loading from a file and + * converting an edited BMesh back into a mesh (i.e. it replaces + * quad tessfaces in a loaded mesh immediately, instead of lazily + * waiting until edit mode has been entered/exited, making it easier + * to recognize problems that would otherwise only show up after edits). + */ + me->totface = mesh_recalcTesselation( + &me->fdata, &me->ldata, &me->pdata, + me->mvert, me->totface, me->totloop, me->totpoly); + + mesh_update_customdata_pointers(me); + me->id.flag -= LIB_NEEDLINK; } me= me->id.next; diff --git a/source/blender/bmesh/operators/mesh_conv.c b/source/blender/bmesh/operators/mesh_conv.c index cdf0eb06348..bc5106dcaba 100644 --- a/source/blender/bmesh/operators/mesh_conv.c +++ b/source/blender/bmesh/operators/mesh_conv.c @@ -428,7 +428,7 @@ void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) BMLoop *l; BMFace *f; BMIter iter, liter; float *facenors = NULL; - int i, j, *keyi, ototvert, totloop, totface, numTex, numCol; + int i, j, *keyi, ototvert, totloop, totface, numTex, numCol, *polyindex = NULL; int dotess = !BMO_Get_Int(op, "notesselation"); numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); @@ -574,16 +574,18 @@ void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) else { mface= MEM_callocN(totface*sizeof(MFace), "loadeditbMesh face"); facenors = MEM_callocN(totface*sizeof(float)*3, "facenors"); + polyindex = MEM_callocN(totface*sizeof(int), "polyindex"); } CustomData_add_layer(&me->fdata, CD_MFACE, CD_ASSIGN, mface, me->totface); + CustomData_add_layer(&me->fdata, CD_POLYINDEX, CD_ASSIGN, polyindex, me->totface); CustomData_add_layer(&me->fdata, CD_NORMAL, CD_ASSIGN, facenors, me->totface); CustomData_from_bmeshpoly(&me->fdata, &bm->pdata, &bm->ldata, totface); mesh_update_customdata_pointers(me); i = 0; - BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_ITER_INDEX(f, &iter, bm, BM_FACES_OF_MESH, NULL, j) { EditVert *eve, *lasteve = NULL, *firsteve = NULL; EditFace *efa; BMLoop *ls[3]; @@ -633,8 +635,11 @@ void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) loops_to_corners(bm, me, i, f, ls, numTex, numCol); copy_v3_v3(facenors, ls[0]->f->no); + *polyindex = j; + mface++; facenors += 3; + polyindex++; i++; } BLI_end_edgefill(); diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 8083c141c5c..9c39a744db7 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -83,18 +83,19 @@ void paintface_flush_flags(Object *ob) } /* - * Try to push updated mesh poly flags to two other data sets: + * Try to push updated mesh poly flags to three other data sets: * - Mesh polys => Mesh tess faces - * - Mesh polys => Final derived mesh polys + * - Mesh polys => Final derived polys + * - Final derived polys => Final derived tessfaces */ - if ((index_array = CustomData_get_layer(&me->fdata, CD_ORIGINDEX))) { + if ((index_array = CustomData_get_layer(&me->fdata, CD_POLYINDEX))) { faces = me->mface; totface = me->totface; /* loop over tessfaces */ for (i= 0; i<totface; i++) { - /* Copy flags onto the tessface from its poly */ + /* Copy flags onto the original tessface from its original poly */ mp_orig = me->mpoly + index_array[i]; faces[i].flag = mp_orig->flag; } @@ -106,11 +107,24 @@ void paintface_flush_flags(Object *ob) /* loop over final derived polys */ for (i= 0; i<totpoly; i++) { - /* Copy flags onto the mesh poly from its final derived poly */ + /* Copy flags onto the final derived poly from the original mesh poly */ mp_orig = me->mpoly + index_array[i]; polys[i].flag = mp_orig->flag; } } + + if ((index_array = CustomData_get_layer(&dm->faceData, CD_POLYINDEX))) { + polys = dm->getPolyArray(dm); + faces = dm->getTessFaceArray(dm);; + totface = dm->getNumTessFaces(dm); + + /* loop over tessfaces */ + for (i= 0; i<totface; i++) { + /* Copy flags onto the final tessface from its final poly */ + mp_orig = polys + index_array[i]; + faces[i].flag = mp_orig->flag; + } + } } /* returns 0 if not found, otherwise 1 */ @@ -123,14 +137,6 @@ static int facesel_face_pick(struct bContext *C, Mesh *me, Object *ob, const int if (!me || me->totpoly==0) return 0; - /*we can't assume mfaces have a correct origindex layer that indices to mpolys. - so instead we have to regenerate the tesselation faces altogether. - - the final 0, 0 paramters causes it to use the index of each mpoly, instead - of reading from the origindex layer.*/ - me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, &me->pdata, - me->mvert, me->totface, me->totloop, me->totpoly, 0, 0); - mesh_update_customdata_pointers(me); makeDerivedMesh(scene, ob, NULL, CD_MASK_BAREMESH, 0); // XXX if (v3d->flag & V3D_INVALID_BACKBUF) { diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index f84b295f2e2..8aa38fdc182 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -664,15 +664,11 @@ void ED_mesh_update(Mesh *mesh, bContext *C, int calc_edges) mesh->mvert, mesh->totface, mesh->totloop, - mesh->totpoly, - 0, - 0); + mesh->totpoly); mesh_update_customdata_pointers(mesh); - /* origindex for tesselated faces currently holds indices of the poly - the face was tesselated from */ - polyindex = CustomData_get_layer(&mesh->fdata, CD_ORIGINDEX); + polyindex = CustomData_get_layer(&mesh->fdata, CD_POLYINDEX); /* add a normals layer for tesselated faces, a tessface normal will contain the normal of the poly the face was tesselated from. */ face_nors = CustomData_add_layer(&mesh->fdata, CD_NORMAL, CD_CALLOC, NULL, mesh->totface); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index e2702c1df43..f07dc3b02af 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1328,7 +1328,7 @@ static int convert_exec(bContext *C, wmOperator *op) /* re-tesselation doesn't happen automatic, calling like this is */ me= newob->data; - me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, &me->pdata, me->mvert, me->totface, me->totloop, me->totpoly, 0, 0); + me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, &me->pdata, me->mvert, me->totface, me->totloop, me->totpoly); dm->release(dm); object_free_modifiers(newob); /* after derivedmesh calls! */ diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 7266f75cb93..316ae756188 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -4811,15 +4811,6 @@ static int texture_paint_init(bContext *C, wmOperator *op) me = pop->s.me; - /*recalc mesh tesselation so the face origindex values point - to the tesselation faces themselves, instead of polys*/ - me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, - &me->pdata, me->mvert, me->totface, me->totloop, me->totpoly, 0, 1); - mesh_update_customdata_pointers(me); - - /*force customdata update*/ - makeDerivedMesh(scene, pop->ps.ob, NULL, CD_MASK_BAREMESH, 0); - /* Dont allow brush size below 2 */ if (pop->ps.brush && pop->ps.brush->size<=1) pop->ps.brush->size = 2; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 04239ab9508..d20ae89d31f 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1948,13 +1948,6 @@ static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *UNUSED me= get_mesh(ob); if(me==NULL || me->totface==0) return OPERATOR_PASS_THROUGH; - /*we can't assume mfaces have a correct origindex layer that indices to mpolys. - so instead we have to regenerate the tesselation faces altogether.*/ - me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, &me->pdata, - me->mvert, me->totface, me->totloop, me->totpoly, 1, 0); - mesh_update_customdata_pointers(me); - makeDerivedMesh(scene, ob, NULL, CD_MASK_BAREMESH, 0); - /* if nothing was added yet, we make dverts and a vertex deform group */ if (!me->dvert) { ED_vgroup_data_create(&me->id); @@ -2420,14 +2413,7 @@ static void vpaint_build_poly_facemap(struct VPaintData *vd, Mesh *me, vd->polyfacemap = BLI_memarena_alloc(vd->arena, sizeof(ListBase)*me->totpoly); - /*we can't assume mfaces have a correct origindex layer that indices to mpolys. - so instead we have to regenerate the tesselation faces altogether.*/ - me->totface = mesh_recalcTesselation(&me->fdata, &me->ldata, &me->pdata, - me->mvert, me->totface, me->totloop, me->totpoly, 1, 0); - mesh_update_customdata_pointers(me); - makeDerivedMesh(scene, ob, NULL, CD_MASK_BAREMESH, 0); - - origIndex = CustomData_get_layer(&me->fdata, CD_ORIGINDEX); + origIndex = CustomData_get_layer(&me->fdata, CD_POLYINDEX); mf = me->mface; if (!origIndex) diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 2a430f4c520..2d65a8155d0 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -512,6 +512,8 @@ static int draw_tface_mapped__set_draw(void *userData, int index) MTFace mtf; int matnr = me->mpoly[index].mat_nr; + BLI_assert(index >= 0 && index < me->totpoly); + if (mpoly && mpoly->flag&ME_HIDE) return 0; memset(&mtf, 0, sizeof(mtf)); diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index b5666a4dc37..db81e09d107 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -76,7 +76,7 @@ typedef struct CustomData { #define CD_MCOL 6 #define CD_ORIGINDEX 7 #define CD_NORMAL 8 -#define CD_FLAGS 9 +#define CD_POLYINDEX 9 #define CD_PROP_FLT 10 #define CD_PROP_INT 11 #define CD_PROP_STR 12 @@ -111,7 +111,7 @@ typedef struct CustomData { #define CD_MASK_MCOL (1 << CD_MCOL) #define CD_MASK_ORIGINDEX (1 << CD_ORIGINDEX) #define CD_MASK_NORMAL (1 << CD_NORMAL) -#define CD_MASK_FLAGS (1 << CD_FLAGS) +#define CD_MASK_POLYINDEX (1 << CD_POLYINDEX) #define CD_MASK_PROP_FLT (1 << CD_PROP_FLT) #define CD_MASK_PROP_INT (1 << CD_PROP_INT) #define CD_MASK_PROP_STR (1 << CD_PROP_STR) diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index d2d1185eb73..8177e9dbb08 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -236,7 +236,7 @@ DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, ml->e += dm->numEdgeData; } - CDDM_recalc_tesselation(cddm, 1); + CDDM_recalc_tesselation(cddm); /*handle vgroup stuff*/ if ((mmd->flag & MOD_MIR_VGROUP) && CustomData_has_layer(&cddm->vertData, CD_MDEFORMVERT)) { diff --git a/source/blender/modifiers/intern/MOD_ngoninterp.c b/source/blender/modifiers/intern/MOD_ngoninterp.c index ece247647be..2f06bb5cea4 100644 --- a/source/blender/modifiers/intern/MOD_ngoninterp.c +++ b/source/blender/modifiers/intern/MOD_ngoninterp.c @@ -98,7 +98,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), dm = copy = CDDM_copy(dm, 0); } - CDDM_recalc_tesselation(dm, 1); + CDDM_recalc_tesselation(dm); mf = dm->getTessFaceArray(dm); of = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 62ee904c293..d01117aa4bd 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -722,7 +722,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, CDDM_calc_normals(result); } else { - CDDM_recalc_tesselation(result, 1); + CDDM_recalc_tesselation(result); } if (dm != odm) { |