Help / tips with new modifier

OK, been trying for like figure this out for like 2 hours and not getting anywhere. How can I delete the “object”. Right now it’s basically doing like “intersect knife” in edit mode ,but now I need to delete the object and leave just the “cut” it made.

Edit:

So this is what I’m currently trying to achieve.

Using python I’m doing something like this ,but don’t know how you would kinda convert to C.

#Select the cube and make sure everything is deselected
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')

#Select the sphere and make sure everything is selected
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.object.mode_set(mode='OBJECT')

#Join the cube and sphere for making cut
bpy.ops.object.join()
bpy.ops.object.mode_set(mode='EDIT')

#Make the "cut"
bpy.ops.mesh.intersect()

#Delete the faces, leaving just the cut
bpy.ops.mesh.delete(type='FACE')

I’m not one to talk about C, but I don’t think adding a modifier to one object can or should alter a different object. What’s the use case that you’re trying to cover?

It doesn’t alter a different object. It only alters the object with modifier.

1 Like

If you post the code you have so far you’re more likely to get a useful answer, it’s difficult to guess what might be wrong with the implementation.

In general edit mode mesh operators will call bmesh functions, and those bmesh functions can also be used in modifiers, so you might be able to piece things together from there.

@brecht

For a test, I tried adding a fourth option to the boolean modifier. I found out it gave the results I wanted and so then I tried to only keep what was needed for the same results.

I added a fourth option here.

https://developer.blender.org/diffusion/B/browse/master/source/blender/modifiers/intern/MOD_boolean.c$112

case eBooleanModifierOp_Cut:
  result = NULL;
  break;

In my mod.c file, that is like this.

static Mesh *get_quick_mesh(
    Object *ob_self, Mesh *mesh_self, Object *ob_other, Mesh *mesh_other, int operation)
{
  Mesh *result = NULL;

  result = NULL;

  return result;
}

Since the eBooleanModifierOp_Cut is 4 option for the enum property. I did something like this.

	static void initData(ModifierData *md)
	{
	  BooleanModifierData *bmd = (BooleanModifierData *)md;
	
	  bmd->double_threshold = 1e-6f;
	  bmd->operation = 3;
	}

Prolly a better way to do this ,but this gives the results of the Intersect(Knife) in edit mode.

Now, I’m stuck at figuring out how to get the faces of the bmd->object and delete em and I’m assuming that code would then go after the BM_mesh_intersect.

https://developer.blender.org/diffusion/B/browse/master/source/blender/modifiers/intern/MOD_boolean.c$334

OK, getting closer.

I added this below the BM_mesh_intersect. Sometimes it deletes the faces on the cube after the cut though.

BM_mesh_delete_hflag_context(bm, BM_FACE_TAG, DEL_FACES);

How do I figure out what triggers a crash? Trying to now bevel the “cut” and it crashes.

In python I’m doing like this.

bvl_wght = bm.edges.layers.bevel_weight.verify()

#Get weighted edges
wght_ed = [e for e in bm.edges if e[bvl_wght] == 1.0]

#Bevel weighted edges
bmesh.ops.bevel(
		bm, 
		geom=wght_ed, 
		offset=0.02, 
		offset_type='WIDTH', 
		segments=1, 
		profile=0.5, 
		clamp_overlap=False, 
		loop_slide=False
		)

I have looked at the MOD_bevel.c , bmesh_bevel.h and the bmesh_bevel.c and can’t tell if I’m missing something simple that’s causing the crash.


static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
  TestModifierData *smd = (TestModifierData *)md;
  Mesh *result = mesh;

  Mesh *mesh_other;
  BMVert *v;

  MDeformVert *dvert = NULL;
  CurveProfile *custom_profile = NULL;

  float weight;
  const int vgroup = -1;
  const int res = 4;
  const float profile = 0.5f;
  const float bevel_angle = DEG2RADF(30.0f); 
  const int offset_type = BEVEL_AMT_WIDTH;
  const int affect_type = BEVEL_AFFECT_EDGES;
  const int profile_type = BEVEL_PROFILE_SUPERELLIPSE;
  const float value = 0.02f;
  const int mat = -1;
  const bool use_weights = false;  
  const bool do_clamp = false;  
  const bool loop_slide = false;
  const bool mark_seam = false;
  const bool mark_sharp = false;
  const bool harden_normals = false;
  const bool invert_vgroup = false;  
  const int face_strength_mode = BEVEL_FACE_STRENGTH_NONE;
  const int miter_outer = BEVEL_MITER_SHARP;
  const int miter_inner = BEVEL_MITER_SHARP;
  const float spread = 0.1f;
  const float smoothresh = 0.1f;
  const int vmesh_method = BEVEL_VMESH_ADJ;

At the end it’s like this.

  BM_mesh_delete_hflag_context(bm, BM_FACE_TAG, DEL_FACES);
    
  BM_mesh_bevel(bm,
                smd->width,
                offset_type,
                profile_type,
                smd->segments,
                profile,
                affect_type,
                use_weights,
                do_clamp,
                dvert,
                vgroup,
                mat,
                loop_slide,
                mark_seam,
                mark_sharp,
                harden_normals,
                face_strength_mode,
                miter_outer,
                miter_inner,
                spread,
                mesh->smoothresh,
                custom_profile,
                vmesh_method);


  result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);


  BM_mesh_free(bm);

  result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; 

You may have produced an invalid mesh. You might try calling the mesh validate function after your modifier has been applied. In python, https://docs.blender.org/api/current/bpy.types.Mesh.html?highlight=validate#bpy.types.Mesh.validate

By the way, I am probably going to try adding “intersect knife” options to the boolean modifier too, as part of my work on the newboolean project.

2 Likes

Not having any luck. I’m assuming in C it’s BKE_mesh_validate_material_indices(mesh); ? How does BM_mesh_bevel know what to bevel? With bmesh.ops.bevel you have the geom option. Is it the dvert that’s determining the bevel? From what I can see in the MOD_bevel.c that just looks like it’s related to vertex group? Is it crashing because I have MDeformVert *dvert = NULL; I have bevel weights added to my cut ,but don’t see how you would tell it to bevel just those edges.

The C function I would try is BKE_mesh_validate(mesh, do_verbose, cddata_check_mask).

BMesh first of all finds vertices that have the BM_ELEM_TAG set, and then, if it is not in vertex_only mode, it will bevel all edges between those. Except: the beveling can be affected by edge weights, vertex weights, and values in MDeformVert groups: sometimes the bevel amount gets multiplied by those, which can have the effect of making bevel a “no-op” on such edges. But the code should have no problem with a null dvert argument because it checks for NULL before trying to use it.

If you are running the C code, you should be able to run it under a debugger and see where it crashes, which may give you a clue as to where the problem is. I still favor my guess that the mesh is somehow violating some invariant assumed about BMeshes.

Did some more MOD_bevel.c dissecting and found out if I add this before the BM_mesh_bevel it works without crashing. What part of this is preventing the crash? All of it?

      if (affect_type == BEVEL_AFFECT_EDGES) {

        BMIter iter;
        BMEdge *e;

        BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
          if (BM_edge_is_manifold(e)) {
            if (use_weights) {
              weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT);
              if (weight == 0.0f) {
                continue;
            }
          }
          BM_elem_flag_enable(e, BM_ELEM_TAG);
          BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
          BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
        }
      }
    }

I don’t know. If you have a valid Mesh object file that the current Boolean modifier crashes on, then that is a bug that I should fix. If you can make a .blend that doesn’t involve needed your new modifier (that is, you apply your modifier), then make a bug for me and I will look at it.

Yeah, you would prolly have to have the new modifier.

Any idea how to go about doing this? I know there was talk on a thread about using collection instead of object for boolean modifier. I got to figure out how to make sure objects in collection is mesh and do the following in C for the cut.

act_obj = bpy.context.active_object

# Get collection
cut_col = bpy.data.collections["Cut"]
# Get mesh objects in collection
cut_objs = [o for o in cut_col.objects if o.type == 'MESH']

# get selected objects minus active 
sel_not_act = [o for o in cut_objs if act_obj != o]

# deselect objects
for objs in sel_not_act:     
    objs.select_set(state=False)

# make cut for each object
while len(sel_not_act) >= 1:
    objs = sel_not_act.pop()
    objs.select_set(state=True)
                        
    bpy.ops.object.join()

    # make cut
    bpy.ops.object.mode_set(mode='EDIT')

    bpy.ops.mesh.intersect(mode='SELECT_UNSELECT', separate_mode='CUT', threshold=1e-06) 

    bpy.ops.object.mode_set(mode='OBJECT')

Edit:

This https://developer.blender.org/D184 was using groups, so prolly be a good starting point figuring out collections.

Still no luck after 50+ hours. I keep trying something like this and it must be wrong ,cause I can’t get anything to work.

CollectionObject *cob;
cob = smd->object_collection->gobject.first;

while (cob) {
  Object *other = cob->ob;
  # Do stuff to each object
  cob = cob->next;

Edit 2:
Maybe @brecht

OK, partly solved this. It’s now using “a” object out of the collection. How do I get it to cut all objects? I tried all kinds of things and can’t get all to work? Everything I try, it keeps doing just the last object in collection. I’m assuming the bmesh is creating a new one for each cut and removing previous cut? Also, tried different things with bmesh and no luck.

  } else if (smd->source == MOD_STRIP_SOURCE_COLLECTION){
      if (smd->object_collection == NULL) {
        return result;
      }

      CollectionObject *cob;      
      cob = smd->object_collection->gobject.first;

      while (cob) {
        Object *other = cob->ob;

        mesh_other = BKE_modifier_get_evaluated_mesh_from_evaluated_object(other, false);
      
        if (mesh_other) {
          Object *object = ctx->object;

          BKE_mesh_wrapper_ensure_mdata(mesh_other);         

          const bool is_flip = (is_negative_m4(object->obmat) != is_negative_m4(other->obmat));

          const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_other);

          bm = BM_mesh_create(&allocsize,
                              &((struct BMeshCreateParams){
                                  .use_toolflags = false,
                              }));

          BM_mesh_bm_from_me(bm,
                            mesh_other,
                            &((struct BMeshFromMeshParams){
                                .calc_face_normal = true,
                            }));

          if (UNLIKELY(is_flip)) {
            const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
            BMIter iter;
            BMFace *efa;
            BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
              BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
            }
          }

          bm = BM_mesh_create(&allocsize,
                              &((struct BMeshCreateParams){
                                  .use_toolflags = false,
                              }));

          BM_mesh_bm_from_me(bm,
                            mesh,
                            &((struct BMeshFromMeshParams){
                                .calc_face_normal = true,
                            }));
          // make cut
          mesh_cut_create(bm, md, ctx, mesh_other, object, other, is_flip, cob);

        cob = cob->next;



    result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);

    BM_mesh_free(bm);

  }
    result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
  }

    /* if new mesh returned, return it; otherwise there was
     * an error, so delete the modifier object */
    if (result == NULL) {
      BKE_modifier_set_error(md, "Cannot execute strip operation");
    }
  }

  return result;
}

OK, I started playing around with bmesh in python and I think I know what’s up. How do I apply “All Transforms” bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) in C? The python code gives the same results as modifier, until transforms are applied. The python code looks no different from the C code. This is prolly why I can’t get the right results for modifier.

Edit:
OK , that doesn’t seem to matter.

Here’s python code

import bpy, bmesh

mesh = bpy.context.object.data

col = bpy.data.collections['Collection']
cob = col.objects

for o in cob:
    cob_data = o.data;
     
    bm = bmesh.new()
    bm.from_mesh(cob_data)
    bm.from_mesh(mesh)            
    bm.to_mesh(mesh)
    
    
bm.free()

Result if transforms not applied

If transforms applied

Here’s C code. Looks like it should do the same? It only does one object though.

      Collection *col = smd->object_collection;
      CollectionObject *cob;
     
      for(cob = col->gobject.first; cob; cob = cob->next){
        Mesh *cob_data = cob->ob->data;

        BKE_mesh_wrapper_ensure_mdata(cob_data);

        const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, cob_data);

        BMesh *bm;
        bm = BM_mesh_create(&allocsize,
                            &((struct BMeshCreateParams){
                                .use_toolflags = false,
                            }));

        BM_mesh_bm_from_me(bm,
                          cob_data,
                          &((struct BMeshFromMeshParams){
                              .calc_face_normal = true,
                          }));
        
        BM_mesh_bm_from_me(bm,
                          mesh,
                          &((struct BMeshFromMeshParams){
                              .calc_face_normal = true,
                          }));

        result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);

      BM_mesh_free(bm);

      result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;

Result if transforms not applied

If transforms applied

So, what am I not doing right? Starting to feel pretty dumb right now.