I am using a Python script to render the below scene in Blender. Before Packing

Now, I am trying to pack them using the piston on the right hand side to make it look like this:

After Packing

The center of mass before packing will be roughly at the center of the box and it will be shifted to a different point after packing the objects. The question I have is, how to do the center of mass drawing in the Blender and how to calculate it in the Blender using a Python script. Does Blender have an inbuilt feature for doing this? I have checked online and the closest I have found is this.


Additional Information:

  • Using Blender 2.74 on OsX
  • Using Blender Game Engine to do the packing

A bit late to answer this, but I recently had to compute the center of gravity of various objects, so here is what I got.

1) "origin to center of mass" does not put the origin to the center of mass

What this command does is to consider the object as an empty shell, i.e. it computes the centroid of all faces. This is not the same thing as the center of mass of a solid object with constant density.

2) as far as I know there is no builtin feature to compute the center of mass of a solid object.

See this answer for a basic script that does the job.

3) to be useful, center of mass computation has to consider various ways of defining actual mass.

Some objects will represent some material with constant density (a bowling ball is indeed a mass of plastic). Some will rather be the abstraction of a complex object of known weight (for instance the engine of a car). Lastly, some curves might represent ropes, rods or wires with a known linear weight (the spring of a clamp)

Based on all this, I designed a small script that does compute the center of mass of a collection of heterogenous objects.

3 kinds of objects can take part in the computation:

  • meshes with a custom weight attribute
  • meshes with a material that has a custom density attribute
  • curves with a material that has a custom linear weight attribute

To use it, you have to define the various attributes and materials required, then you just select all the objects you want (selecting "weightless" objects, empties, whatever will do no harm) and run the script. It will place the 3D cursor at the resulting CG location.

As an illustration, see how the CG moves: battery front Battery placed further back battery back Landing gear down gear down

I used millimeters and grams as units in my case, so if you want to switch to imperial system or change the scale you'll have to tweak a few constants. See the script for details.

import bpy
import mathutils
from mathutils import Vector

def triangles (verts):
    """enumerate triangles in a face"""
    for i in range (1, len(verts)-1):
        yield (verts[0], verts[i], verts[i+1])

def cg_mesh (obj):
    """center of gravity and mass of a mesh"""

    center = Vector()
    volume = 0
    mesh = obj.to_mesh (bpy.context.scene, True, 'PREVIEW')
    for face in mesh.polygons:
        f = face.vertices
        for t in triangles (f):
            a,b,c = (mesh.vertices[v].co for v in t)
            v = a.cross(b).dot(c) / 6
            center += v * (a+b+c) / 4
            volume += v

    if volume == 0: print ("ZERO VOLUME", obj.name)
    else          : center /= volume

    # To make a mesh heavy
    # define a "weight" (in grams) custom property at object level, or
    # define a "density" (in g/cm3) custom property in associated material
    if 'weight' in obj:
        mass = obj['weight']
    elif obj.active_material and 'density' in obj.active_material:
        mass = volume * obj.active_material['density'] / 1000
        mass = 0
    return center, mass

def cg_curve (obj):
    """center of gravity and mass of a curve"""

    # to make a curve heavy,
    # define a "linear weight" (in g/m) custom property in associated material
        linear_weight = obj.active_material['linear weight'] / 1000
        print ("NO MATERIAL FOR CURVE", obj.name)
        return Vector(), 0

    # copy the curve and its modifiers
    bones = obj.copy()
    bones.data = obj.data.copy() # copy curve
    curve = bones.data

    # reduce curve to skeleton
    curve.bevel_object = None
    curve.taper_object = None

    # convert to mesh, applying modifiers
    mesh = bones.to_mesh (bpy.context.scene, True, 'PREVIEW')

    # compute skeleton length and center of mass
    len = 0
    center = Vector()
    for segment in mesh.edges:
        a, b = (mesh.vertices[v].co for v in segment.vertices)
        l = (a-b).length
        center += l * (a+b) # / 2 (median point) taken out of the loop
        len += l
        print (l,a,b)
    bpy.data.meshes.remove (mesh)

    bpy.data.objects.remove (bones)
    bpy.data.curves.remove (curve)

    return center/len/2, len * linear_weight

def cg_switch (obj):
    if   obj.type == 'MESH' : return cg_mesh (obj)
    elif obj.type == 'CURVE': return cg_curve(obj)
    else                    : return Vector(), 0

def cg (obj):
    center, mass = cg_switch (obj)
    return obj.matrix_world * center, mass

center = Vector()
weight = 0
for obj in bpy.context.selected_objects:
    c, w = cg (obj)
    center += c * w
    weight += w
if weight != 0:
    bpy.context.scene.cursor_location = center/weight
    print (center/weight, weight)

In Blender 2.79 you can move the origin of an object to the center of mass, with the choice of computing it from the surface or the volume :


"ORIGIN_CENTER_OF_MASS": Sets object origin to Center of Mass (Surface), Calculate the center of mass from the surface area.

"ORIGIN_CENTER_OF_VOLUME": Sets object origin to Center of Mass (Volume), Calculate the center of mass from the volume (must be manifold geometry with consistent normals).

See the docs here


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.