Rewriting Mesh to BMesh

I’m having trouble understanding some things about Mesh vs Bmesh. Trying to update a selection iterator type of thing to use BMesh data and make it faster, because it was written for Mesh data that requires BKE_mesh_wrapper_ensure_mdata(mesh); which duplicates the entire mesh and takes twice as long.

I’ve made some progress changing Mverts to BMverts, and Mloops to BMloops, but I’m a bit stuck now, and had some questions.

What is the BMesh equivalent for Mpoly?

Why can I assign a float to something like this, using Mloop, but not if I change it to BMloop?

This works fine:

Mesh *mesh = editbmesh_get_eval_cage_from_orig(
      vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
const MLoop *ml;
const MPoly *mp = mesh->mpoly;
float(*face_screen_verts)[2] = MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__);
float(*screen_coords)[2] = MEM_mallocN(sizeof(int) * 2 * bm->totvert, __func__);

ml = &mesh->mloop[mp->loopstart];
face_screen_verts[j][0] = screen_coords[ml->v][0];

But if I change mloop to bmloop, like this:

const BMLoop *ml;
ml = BM_FACE_FIRST_LOOP(efa);

face_screen_verts[j][0] = screen_coords[ml->v][0];

It tells me that the expression screen_coords[ml->v][0]; must have an integral type? I read that this is because you can’t add 2 pointers, but I don’t get it. I can’t get past how Mloop works, and BMloop doesn’t, aren’t they basically the same thing?

I only have a very vague understanding of any of this or what is going on in the script, but I can get it working with everything changed into BMesh data except for that one part, where it wants screen_coords[ml->v][0] but wont let me change it to BMloop.

Here’s the whole thing, I didn’t write this, I’m just using it and trying to make it faster

Original using Mesh:

void mesh_foreachScreenFaceVerts(ViewContext *vc,
                                 void (*func)(void *userData,
                                              BMFace *efa,
                                              const float screen_co[][2],
                                              int total_count,
                                              rctf *screen_rect,
                                              bool *face_hit),
                                 void *userData,
                                 const eV3DProjTest clip_flag)
{
  Mesh *mesh = editbmesh_get_eval_cage_from_orig(
      vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
  BKE_mesh_wrapper_ensure_mdata(mesh);
  ED_view3d_check_mats_rv3d(vc->rv3d);

  BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE);

  const MVert *mvert = mesh->mvert;
  const MPoly *mp = mesh->mpoly;
  const MLoop *ml;
  const BMFace *efa;

  float temp_screen_co[2];
  int total_length = 0;

  float(*screen_coords)[2] = MEM_mallocN(sizeof(int) * 2 * mesh->totvert, __func__);
  int face_screen_verts_size = 4;
  float(*face_screen_verts)[2] = MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__);

  /* This makes only sense on subdivided meshes.*/
  BLI_bitmap *faces_visited;
  int cage_index = BKE_modifiers_get_cage_index(vc->scene, vc->obedit, NULL, 1);
  const bool cage_display = cage_index != -1;
  if (cage_display) {
    faces_visited = BLI_BITMAP_NEW((size_t)mesh->totpoly, __func__);
  }

  /* Transform and store all visible verts into screen coords. */
  for (int i = 0; i < mesh->totvert; i, mvert) {

    if (mvert->flag & ME_HIDE) {
      continue;
    }

    if (ED_view3d_project_float_object(vc->region, mvert->co, &temp_screen_co, clip_flag) ==
        V3D_PROJ_RET_OK) {
      screen_coords[i][0] = temp_screen_co[0];
      screen_coords[i][1] = temp_screen_co[1];
    }
    else {
      screen_coords[i][0] = 0.0f;
      screen_coords[i][1] = 0.0f;
    }
  }

  const int *poly_index = CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX);
  const bool use_original_index = poly_index != 0;

  rctf poly_rect_data;
  rctf *poly_rect = &poly_rect_data;
  bool face_hit = false;

  /* Collect polygon verts and send off per poly callback. */
  for (int i = 0; i < mesh->totpoly; i, mp) {
    int original_index = i;
    if (use_original_index) {
      original_index = *poly_index;
      if (original_index == ORIGINDEX_NONE) {
        continue;
      }
    }

    if (cage_display && BLI_BITMAP_TEST(faces_visited, original_index)) {
      continue;
    }

    efa = BM_face_at_index(vc->em->bm, original_index);
    if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {

      if (mp->totloop > face_screen_verts_size) {
        face_screen_verts_size = mp->totloop;
        MEM_freeN(face_screen_verts);
        face_screen_verts = MEM_mallocN(sizeof(float) * 2 * face_screen_verts_size, __func__);
      }

      total_length = 0;
      BLI_rctf_init_minmax(poly_rect);
      ml = &mesh->mloop[mp->loopstart];

      bool skip = false;

      for (int j = 0; j < mp->totloop; j, ml) {
        face_screen_verts[j][0] = screen_coords[ml->v][0];
        face_screen_verts[j][1] = screen_coords[ml->v][1];

        /* Ignore polygons with invalid screen coords. */
        if (face_screen_verts[j][0] == 0.0f && face_screen_verts[j][1] == 0.0f) {
          skip = true;
          break;
        }

        total_length;

        BLI_rctf_do_minmax_v(poly_rect, screen_coords[ml->v]);
      }

      if (skip) {
        continue;
      }

      face_hit = false;

      func(
          userData, efa, (const float(*)[2])face_screen_verts, total_length, poly_rect, &face_hit);

      if (cage_display && face_hit) {
        BLI_BITMAP_ENABLE(faces_visited, original_index);
      }
    }
  }

  if (cage_display) {
    MEM_freeN(faces_visited);
   }
  MEM_freeN(screen_coords);
  MEM_freeN(face_screen_verts);
 }

WIP Modified to use BMesh:

void mesh_foreachScreenFaceVerts(ViewContext *vc,
                                 void (*func)(void *userData,
                                              BMFace *efa,
                                              const float screen_co[][2],
                                              int total_count,
                                              rctf *screen_rect,
                                              bool *face_hit),
                                 void *userData,
                                 const eV3DProjTest clip_flag)
{
  Mesh *mesh = editbmesh_get_eval_cage_from_orig(
      vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
  BKE_mesh_wrapper_ensure_mdata(mesh);
  ED_view3d_check_mats_rv3d(vc->rv3d);
  BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE);

  const BMLoop *ml;
  const BMFace *efa;
  const BMesh *bm = vc->em->bm;
  const MPoly *mp = mesh->mpoly;
  const BMLoop *tot = bm->totloop;

  float temp_screen_co[2];
  int total_length = 0;

  float(*screen_coords)[2] = MEM_mallocN(sizeof(int) * 2 * bm->totvert, __func__);
  int face_screen_verts_size = 4;
  float(*face_screen_verts)[2] = MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__);

  /* This makes only sense on subdivided meshes.*/
  BLI_bitmap *faces_visited;
  int cage_index = BKE_modifiers_get_cage_index(vc->scene, vc->obedit, NULL, 1);
  const bool cage_display = cage_index != -1;
  if (cage_display) {
    faces_visited = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
  }

  /* Transform and store all visible verts into screen coords. */
  for (int i = 0; i < bm->totvert; i++) {
    BMVert *bmvert = BM_vert_at_index(bm, i);

    if (BM_elem_flag_test_bool(bmvert, BM_ELEM_HIDDEN)) {
      continue;
    }

    if (ED_view3d_project_float_object(vc->region, bmvert->co, &temp_screen_co, clip_flag) ==
        V3D_PROJ_RET_OK) {
      screen_coords[i][0] = temp_screen_co[0];
      screen_coords[i][1] = temp_screen_co[1];
    }
    else {
      screen_coords[i][0] = 0.0f;
      screen_coords[i][1] = 0.0f;
    }
  }

  const int *poly_index = CustomData_get_layer(&bm->pdata, CD_ORIGINDEX);
  const bool use_original_index = poly_index != 0;

  rctf poly_rect_data;
  rctf *poly_rect = &poly_rect_data;
  bool face_hit = false;



  /* Collect polygon verts and send off per poly callback. */
  for (int i = 0; i < bm->totface; i++, mp++) {
    int original_index = i;
    if (use_original_index) {
      original_index = *poly_index++;
      if (original_index == ORIGINDEX_NONE) {
        continue;
      }
    }

    if (cage_display && BLI_BITMAP_TEST(faces_visited, original_index)) {
      continue;
    }

    efa = BM_face_at_index(vc->em->bm, original_index);
    if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {

      if (bm->totloop > face_screen_verts_size) {
        face_screen_verts_size = bm->totloop;
        MEM_freeN(face_screen_verts);
        face_screen_verts = MEM_mallocN(sizeof(float) * 2 * face_screen_verts_size, __func__);
      }

      total_length = 0;
      BLI_rctf_init_minmax(poly_rect);
      ml = BM_FACE_FIRST_LOOP(efa);

      bool skip = false;

      for (int j = 0; j < efa->len; j++, ml++) {
        face_screen_verts[j][0] = screen_coords[ml->v][0];
        face_screen_verts[j][1] = screen_coords[ml->v][1];

         /* Ignore polygons with invalid screen coords.*/ 
        if (face_screen_verts[j][0] == 0.0f && face_screen_verts[j][1] == 0.0f) {
          skip = true;
          break;
        }

        total_length++;

        BLI_rctf_do_minmax_v(poly_rect, screen_coords[ml->v]);
      }

      if (skip) {
        continue;
      }

      face_hit = false;

      func(
          userData, efa, (const float(*)[2])face_screen_verts, total_length, poly_rect, &face_hit);

      if (cage_display && face_hit) {
        BLI_BITMAP_ENABLE(faces_visited, original_index);
      }
    }
  }

  if (cage_display) {
    MEM_freeN(faces_visited);
  }
  MEM_freeN(screen_coords);
  MEM_freeN(face_screen_verts);
}

I tried a handful of different things just to see, and I got it to where it wasn’t using mesh data at all, without crashing, but it wouldn’t select anything either.

I’m pretty sure this whole thing could be structured differently, and maybe it has to be in order to work right. But I am so close to done with it, I wanted to know if it can at least work as is, and then if necessary make it right once I get a better idea what/why things are doing what they are doing.

I don’t know what to do from here other than continue flailing around trying things randomly based on hints and examples inside of different bmesh .h and .c files. Thanks so much for your time.

1 Like

you need to have a closer look at the bmloop struct as you apparently need to acess the data differently. I think its easiest to just navigate to the struct, and then search the codebase on how its used elsewhere. Then you can try accessing it in the same way :slight_smile:

2 Likes

I love this feeling. Finally progress.

The key to it all was the following:
BM_elem_index_get(l_iter->v);

I had gone through a handful of different rewrites of your work. The closest I had gotten until now was looping through the verts of each face, which is kinda neat but also really weird. Basically, it would allow the user to be in face mode, and drag select over a mesh, and if you have any verts selected, it would then give you the faces that have those verts in them. Like a select faces by vertex.

Anyways, I’m going to go back through and make sure of everything that I can, and clean things up, what a relief. Was really making me wonder why I couldn’t figure out what seemed so simple.

1 Like