Revert "Cleanup: remove legacy mesh save support"
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Fri, 22 Mar 2019 17:16:46 +0000 (18:16 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Fri, 22 Mar 2019 17:16:46 +0000 (18:16 +0100)
Fixes T62793. Leave this in the blender2.7 branch for those that still rely
on it, but it will remain removed in master.

source/blender/blenkernel/BKE_global.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/mesh_evaluate.c
source/blender/blenloader/intern/writefile.c
source/blender/makesdna/DNA_mesh_types.h
source/blender/windowmanager/intern/wm_files.c

index 5bff532..35e8081 100644 (file)
@@ -182,12 +182,12 @@ enum {
 /* On write, make backup `.blend1`, `.blend2` ... files, when the users preference is enabled */
 #define G_FILE_HISTORY           (1 << 25)
 /* BMesh option to save as older mesh format */
-// #define G_FILE_MESH_COMPAT       (1 << 26)
+#define G_FILE_MESH_COMPAT       (1 << 26)
 /* On write, restore paths after editing them (G_FILE_RELATIVE_REMAP) */
 #define G_FILE_SAVE_COPY         (1 << 27)
 #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28)
 
-#define G_FILE_FLAGS_RUNTIME (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_SAVE_COPY)
+#define G_FILE_FLAGS_RUNTIME (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_MESH_COMPAT | G_FILE_SAVE_COPY)
 
 /* ENDIAN_ORDER: indicates what endianness the platform where the file was
  * written had. */
index bd14fd7..62625a9 100644 (file)
@@ -339,6 +339,9 @@ void BKE_mesh_recalc_looptri(
         const struct MVert *mvert,
         int totloop, int totpoly,
         struct MLoopTri *mlooptri);
+int BKE_mesh_mpoly_to_mface(
+        struct CustomData *fdata, struct CustomData *ldata,
+        struct CustomData *pdata, int totface, int totloop, int totpoly);
 void BKE_mesh_convert_mfaces_to_mpolys(struct Mesh *mesh);
 void BKE_mesh_do_versions_convert_mfaces_to_mpolys(struct Mesh *mesh);
 void BKE_mesh_convert_mfaces_to_mpolys_ex(
index 5402569..707c462 100644 (file)
@@ -3165,9 +3165,130 @@ void BKE_mesh_recalc_looptri(
 #undef ML_TO_MLT
 }
 
-static void bm_corners_to_loops_ex(
-        ID *id, CustomData *fdata, CustomData *ldata, CustomData *pdata,
-        MFace *mface, int totloop, int findex, int loopstart, int numTex, int numCol)
+/* -------------------------------------------------------------------- */
+
+
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+
+/**
+ * This function recreates a tessellation.
+ * returns number of tessellation faces.
+ *
+ * for forwards compat only quad->tri polys to mface, skip ngons.
+ */
+int BKE_mesh_mpoly_to_mface(struct CustomData *fdata, struct CustomData *ldata,
+                            struct CustomData *pdata, int totface, int UNUSED(totloop), int totpoly)
+{
+       MLoop *mloop;
+
+       unsigned int lindex[4];
+       int i;
+       int k;
+
+       MPoly *mp, *mpoly;
+       MFace *mface, *mf;
+
+       const int numTex = CustomData_number_of_layers(pdata, CD_MTEXPOLY);
+       const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL);
+       const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL);
+       const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP);
+       const bool hasLNor = CustomData_has_layer(ldata, CD_NORMAL);
+
+       /* over-alloc, ngons will be skipped */
+       mface = MEM_malloc_arrayN((size_t)totpoly, sizeof(*mface), __func__);
+
+       mpoly = CustomData_get_layer(pdata, CD_MPOLY);
+       mloop = CustomData_get_layer(ldata, CD_MLOOP);
+
+       mp = mpoly;
+       k = 0;
+       for (i = 0; i < totpoly; i++, mp++) {
+               if (ELEM(mp->totloop, 3, 4)) {
+                       const unsigned int mp_loopstart = (unsigned int)mp->loopstart;
+                       mf = &mface[k];
+
+                       mf->mat_nr = mp->mat_nr;
+                       mf->flag = mp->flag;
+
+                       mf->v1 = mp_loopstart + 0;
+                       mf->v2 = mp_loopstart + 1;
+                       mf->v3 = mp_loopstart + 2;
+                       mf->v4 = (mp->totloop == 4) ? (mp_loopstart + 3) : 0;
+
+                       /* abuse edcode for temp storage and clear next loop */
+                       mf->edcode = (char)mp->totloop; /* only ever 3 or 4 */
+
+                       k++;
+               }
+       }
+
+       CustomData_free(fdata, totface);
+
+       totface = k;
+
+       CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface);
+
+       CustomData_from_bmeshpoly(fdata, pdata, ldata, totface);
+
+       mp = mpoly;
+       k = 0;
+       for (i = 0; i < totpoly; i++, mp++) {
+               if (ELEM(mp->totloop, 3, 4)) {
+                       mf = &mface[k];
+
+                       if (mf->edcode == 3) {
+                               /* sort loop indices to ensure winding is correct */
+                               /* NO SORT - looks like we can skip this */
+
+                               lindex[0] = mf->v1;
+                               lindex[1] = mf->v2;
+                               lindex[2] = mf->v3;
+                               lindex[3] = 0; /* unused */
+
+                               /* transform loop indices to vert indices */
+                               mf->v1 = mloop[mf->v1].v;
+                               mf->v2 = mloop[mf->v2].v;
+                               mf->v3 = mloop[mf->v3].v;
+
+                               BKE_mesh_loops_to_mface_corners(fdata, ldata, pdata,
+                                                               lindex, k, i, 3,
+                                                               numTex, numCol, hasPCol, hasOrigSpace, hasLNor);
+                               test_index_face(mf, fdata, k, 3);
+                       }
+                       else {
+                               /* sort loop indices to ensure winding is correct */
+                               /* NO SORT - looks like we can skip this */
+
+                               lindex[0] = mf->v1;
+                               lindex[1] = mf->v2;
+                               lindex[2] = mf->v3;
+                               lindex[3] = mf->v4;
+
+                               /* transform loop indices to vert indices */
+                               mf->v1 = mloop[mf->v1].v;
+                               mf->v2 = mloop[mf->v2].v;
+                               mf->v3 = mloop[mf->v3].v;
+                               mf->v4 = mloop[mf->v4].v;
+
+                               BKE_mesh_loops_to_mface_corners(fdata, ldata, pdata,
+                                                               lindex, k, i, 4,
+                                                               numTex, numCol, hasPCol, hasOrigSpace, hasLNor);
+                               test_index_face(mf, fdata, k, 4);
+                       }
+
+                       mf->edcode = 0;
+
+                       k++;
+               }
+       }
+
+       return k;
+}
+#endif /* USE_BMESH_SAVE_AS_COMPAT */
+
+
+static void bm_corners_to_loops_ex(ID *id, CustomData *fdata, CustomData *ldata, CustomData *pdata,
+                                   MFace *mface, int totloop, int findex, int loopstart, int numTex, int numCol)
 {
        MTFace *texface;
        MTexPoly *texpoly;
index 0d064d5..1ee39a1 100644 (file)
@@ -332,6 +332,10 @@ typedef struct {
         * Will be NULL for UNDO.
         */
        WriteWrap *ww;
+
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+       bool use_mesh_compat; /* option to save with older mesh format */
+#endif
 } WriteData;
 
 static WriteData *writedata_new(WriteWrap *ww)
@@ -2181,6 +2185,12 @@ static void write_customdata(
 
 static void write_mesh(WriteData *wd, Mesh *mesh)
 {
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+       const bool save_for_old_blender = wd->use_mesh_compat;  /* option to save with older mesh format */
+#else
+       const bool save_for_old_blender = false;
+#endif
+
        CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE];
        CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE];
        CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE];
@@ -2189,17 +2199,19 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
 
        if (mesh->id.us > 0 || wd->use_memfile) {
                /* write LibData */
-               {
+               if (!save_for_old_blender) {
                        /* write a copy of the mesh, don't modify in place because it is
                         * not thread safe for threaded renders that are reading this */
                        Mesh *old_mesh = mesh;
                        Mesh copy_mesh = *mesh;
                        mesh = &copy_mesh;
 
+#ifdef USE_BMESH_SAVE_WITHOUT_MFACE
                        /* cache only - don't write */
                        mesh->mface = NULL;
                        mesh->totface = 0;
                        memset(&mesh->fdata, 0, sizeof(mesh->fdata));
+#endif /* USE_BMESH_SAVE_WITHOUT_MFACE */
 
                        /**
                         * Those calls:
@@ -2210,7 +2222,11 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
                         */
                        CustomData_file_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
                        CustomData_file_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
+#ifndef USE_BMESH_SAVE_WITHOUT_MFACE  /* Do not copy org fdata in this case!!! */
+                       CustomData_file_write_prepare(&mesh->fdata, &flayers, flayers_buff, ARRAY_SIZE(flayers_buff));
+#else
                        flayers = flayers_buff;
+#endif
                        CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
                        CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
 
@@ -2235,6 +2251,73 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
                        /* restore pointer */
                        mesh = old_mesh;
                }
+               else {
+
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+                       /* write a copy of the mesh, don't modify in place because it is
+                        * not thread safe for threaded renders that are reading this */
+                       Mesh *old_mesh = mesh;
+                       Mesh copy_mesh = *mesh;
+                       mesh = &copy_mesh;
+
+                       mesh->mpoly = NULL;
+                       mesh->mface = NULL;
+                       mesh->totface = 0;
+                       mesh->totpoly = 0;
+                       mesh->totloop = 0;
+                       CustomData_reset(&mesh->fdata);
+                       CustomData_reset(&mesh->pdata);
+                       CustomData_reset(&mesh->ldata);
+                       mesh->edit_btmesh = NULL;
+
+                       /* now fill in polys to mfaces */
+                       /* XXX This breaks writing design, by using temp allocated memory, which will likely generate
+                        *     duplicates in stored 'old' addresses.
+                        *     This is very bad, but do not see easy way to avoid this, aside from generating those data
+                        *     outside of save process itself.
+                        *     Maybe we can live with this, though?
+                        */
+                       mesh->totface = BKE_mesh_mpoly_to_mface(
+                               &mesh->fdata, &old_mesh->ldata, &old_mesh->pdata,
+                               mesh->totface, old_mesh->totloop, old_mesh->totpoly);
+
+                       BKE_mesh_update_customdata_pointers(mesh, false);
+
+                       CustomData_file_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
+                       CustomData_file_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
+                       CustomData_file_write_prepare(&mesh->fdata, &flayers, flayers_buff, ARRAY_SIZE(flayers_buff));
+#if 0
+                       CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
+                       CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+#endif
+
+                       writestruct_at_address(wd, ID_ME, Mesh, 1, old_mesh, mesh);
+                       write_iddata(wd, &mesh->id);
+
+                       /* direct data */
+                       if (mesh->adt) {
+                               write_animdata(wd, mesh->adt);
+                       }
+
+                       writedata(wd, DATA, sizeof(void *) * mesh->totcol, mesh->mat);
+                       /* writedata(wd, DATA, sizeof(MSelect) * mesh->totselect, mesh->mselect); */ /* pre-bmesh NULL's */
+
+                       write_customdata(wd, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, -1, 0);
+                       write_customdata(wd, &mesh->id, mesh->totedge, &mesh->edata, elayers, -1, 0);
+                       write_customdata(wd, &mesh->id, mesh->totface, &mesh->fdata, flayers, -1, 0);
+                       /* harmless for older blender versioins but _not_ writing these keeps file size down */
+#if 0
+                       write_customdata(wd, &mesh->id, mesh->totloop, &mesh->ldata, llayers, -1, 0);
+                       write_customdata(wd, &mesh->id, mesh->totpoly, &mesh->pdata, players, -1, 0);
+#endif
+
+                       CustomData_free(&mesh->fdata, mesh->totface);
+                       flayers = NULL;
+
+                       /* restore pointer */
+                       mesh = old_mesh;
+#endif /* USE_BMESH_SAVE_AS_COMPAT */
+               }
        }
 
        if (vlayers && vlayers != vlayers_buff) {
@@ -3756,6 +3839,10 @@ static bool write_file_handle(
 
        wd = mywrite_begin(ww, compare, current);
 
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+       wd->use_mesh_compat = (write_flags & G_FILE_MESH_COMPAT) != 0;
+#endif
+
 #ifdef USE_NODE_COMPAT_CUSTOMNODES
        /* don't write compatibility data on undo */
        if (!current) {
index 40bfe5a..b0f4a5b 100644 (file)
@@ -224,6 +224,9 @@ enum {
 /* this is so we can save bmesh files that load in trunk, ignoring NGons
  * will eventually be removed */
 
+#define USE_BMESH_SAVE_AS_COMPAT
+#define USE_BMESH_SAVE_WITHOUT_MFACE
+
 /* enable this so meshes get tessfaces calculated by default */
 /* #define USE_TESSFACE_DEFAULT */
 
index 29e72b1..98f3216 100644 (file)
@@ -58,6 +58,7 @@
 
 #include "BLF_api.h"
 
+#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
 #include "DNA_object_types.h"
 #include "DNA_space_types.h"
 #include "DNA_userdef_types.h"
@@ -2127,6 +2128,16 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
                 RNA_boolean_get(op->ptr, "copy")),
                G_FILE_SAVE_COPY);
 
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+       SET_FLAG_FROM_TEST(
+               fileflags,
+               (RNA_struct_find_property(op->ptr, "use_mesh_compat") &&
+                RNA_boolean_get(op->ptr, "use_mesh_compat")),
+               G_FILE_MESH_COMPAT);
+#else
+#  error "don't remove by accident"
+#endif
+
        const bool ok = wm_file_write(C, path, fileflags, op->reports);
 
        if ((op->flag & OP_IS_INVOKE) == 0) {
@@ -2186,6 +2197,11 @@ void WM_OT_save_as_mainfile(wmOperatorType *ot)
        prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy",
                        "Save a copy of the actual working state but does not make saved file active");
        RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+       RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format",
+                       "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will "
+                       "be lost (no implicit triangulation)");
+#endif
 }
 
 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))