0
$\begingroup$

Here's my mesh. It has some interior faces (it's made out of merged cubes) that I want to remove. I've highlighted two of them for example:

I want to remove

When I run Select Interior Faces, it selects them, but also erroneously selects the large middle face:

wrong interior

I believe I understand why this is happening, due to the nature of the vertices at the edge of this face and how the "interior" faces algorithm works.

Is there an automatable way using Blender scripting to ignore faces that are visible to the camera, or achieve a result similar to that? I'm writing a Blender script to do some automatic mesh cleanup. Removing interior faces is a required part of the process, but I'm not sure how to achieve it in a script.

$\endgroup$
3
  • $\begingroup$ Use limit selection to visible related: blender.stackexchange.com/questions/14764/… $\endgroup$ Commented Jul 9, 2016 at 19:28
  • $\begingroup$ @cegaton I'm trying to do this in a script. Can bounding box selection be scripted? I can't find any resources on it. $\endgroup$ Commented Jul 9, 2016 at 19:28
  • $\begingroup$ select_interior_faces() from the docs: Select faces where all edges have more than 2 face users. also the "use_occlude_geometry" is situated in bpy.types.spaceView3D If you want to see how they are done just right click on the icons button and select "edit source", it will open the code in the text editor, but without enough knowledge of bpy it might be really hard to understand. did you get to this link ? blender.org/api/blender_python_api_2_77a_release $\endgroup$ Commented Jul 10, 2016 at 9:08

1 Answer 1

2
$\begingroup$

Here's the solution I came up with:

  1. Run Blender's built in "select interior face" algorithm
  2. Note which faces are selected
  3. Loop over every face, and cast a ray from the "camera" position to the face.
  4. If the ray hits (the center of) the face, we know it's visible to the camera, so don't delete it.
  5. If the ray doesn't hit the face (hits another face) it means it's probably interior.

This is a bit confusing, so I made two images to show more detail:

no hit example

hit example

Commented code is below. Please note this is taken from a large file so I might have forgot to declare some variable references. Happy to clarify if anyone has questions in comments.

import bpy import bmesh from mathutils import Vector cameraOrigin = Vector( ( 0, 0, 10 ) ) ops = bpy.ops scene = bpy.context.scene mesh = bpy.ops.mesh current_mesh = scene.objects[ 0 ] current_mesh_data = current_mesh.data scene.objects.active = current_mesh # Blender's "select interior faces" doesn't actually select interior faces, it # selects faces that share edges with 2 or more other faces. In my case this # erroneously selects some faces that are visible but connect to multiple other # faces. The solution is to select interior faces, then ignore any faces that # the camera (positioned at 0,0,10) can see, and delete the remaining ones # See http//blender.stackexchange.com/questions/57540/automated-way-to-make-select-interior-faces-ignore-select-faces-that-are-visib def removeInteriorFaces( mesh_data ): # First do the built in selection mesh.select_interior_faces() # And store all faces inside that selection indices = [] bm = bmesh.from_edit_mesh( mesh_data ) for index, face in enumerate( bm.faces ): if face.select: indices.append( ( index, face.calc_center_median_weighted() ) ) # Deselect everything... mesh.select_all() # Switch to object mode to do scene raycasting (doesn't work in edit mode # I don't think, got error "has no mesh data to be used for ray casting" ops.object.mode_set( mode = 'OBJECT' ) outside = [] for index_data in indices: index = index_data[ 0 ] center = index_data[ 1 ] direction = center - cameraOrigin; direction.normalize() # Cast a ray from the "camera" position to the face we think is interior result, location, normal, faceIndex, object, matrix = scene.ray_cast( cameraOrigin, direction ) # If the ray actually hit the face, as in the face index from the # selection matches the face index from the raycast, then this face # *is* visible to the camera, so don't remove it! if faceIndex == index: outside.append( faceIndex ) # Build a list of the "true" interior face indices, which is the original # indices from Blender's built in "select interior faces", but without the # faces we know the camera can see invisible_interior_faces = [ data[ 0 ] for data in indices if data[ 0 ] not in outside ] print( 'Removing ',len( invisible_interior_faces ),'invisible faces' ) # Select the faces (in object mode this is easy, strangely)... if len( invisible_interior_faces ) > 0: for index in invisible_interior_faces: mesh_data.polygons[ index ].select = True ops.object.mode_set( mode = 'EDIT' ) # Then delete them if len( invisible_interior_faces ) > 0: mesh.delete( type = 'FACE' ) ops.object.mode_set( mode = 'EDIT' ) removeInteriorFaces( current_mesh_data ) 
$\endgroup$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.