5
$\begingroup$

The 3D print toolbox currently only outputs the volume/area of all faces.

How can I get the volume of selected mesh objects, ignoring certain faces?


Asking on behalf of a user who mailed me.

$\endgroup$

1 Answer 1

7
$\begingroup$

This can be done using the Python API to get the mesh with modifiers applied, then applying the transform matrix.

The following script applies to all selected objects, showing a popup with the final volume. Not that it does not use units or scene scale.

See is_face_volume for the method of checking if a face should be included in volume calculations.

import bpy
from bpy import context

# Quick way to test with/without hidden faces
USE_FILTER_FACES = True


def report_info(title="", message=""):
    # show in console
    print(title, message)
    # show in popup
    def draw_cb(self, context):
        self.layout.label(text=message)
    context.window_manager.popup_menu(draw_func=draw_cb, title=title, icon='INFO')


def is_face_skip(f):
    """Ignore faces that pass this test!"""
    return f.hide is False
    # You may want to filter based on material.
    # return f.material_index == 0


def bmesh_from_object_final(ob):
    import bmesh
    matrix = ob.matrix_world
    me = ob.to_mesh()
    me.transform(matrix)
    bm = bmesh.new()
    bm.from_mesh(me)
    if USE_FILTER_FACES:
        faces_remove = [f for f in bm.faces if not is_face_skip(f)]
        for f in faces_remove:
            bm.faces.remove(f)
    return (bm, matrix.is_negative)


def volume_and_area_from_object(ob):
    bm, is_negative = bmesh_from_object_final(ob)
    volume = bm.calc_volume(signed=True)
    area = sum(f.calc_area() for f in bm.faces)
    bm.free()
    if is_negative:
        volume = -volume
    return volume, area


def main():
    volume = 0.0
    area = 0.0
    object_count = 0
    for ob in context.selected_objects:
        if ob.type != 'MESH':
            continue
        volume_single, area_single = volume_and_area_from_object(ob)
        volume += volume_single
        area += area_single
        object_count += 1
    volume = abs(volume)

    report_info(
        title="From %d object(s)" % object_count,
        message="Volume: %.4f, Area: %.4f" % (volume, area),
    )

if __name__ == "__main__":
    main()
$\endgroup$
2
  • $\begingroup$ The Object.to_mesh method only takes in the preserve_all_data_layers and depsgraph parameters on Blender 2.8 with the 3D Printing Toolkit addon installed. Am I missing something still? $\endgroup$
    – Ziad
    Sep 30, 2019 at 9:14
  • $\begingroup$ Updated for Blender 2.93 $\endgroup$
    – ideasman42
    Mar 9, 2021 at 11:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .