Search code examples
pythontranslationscaleblendermesh

Blender - Move mesh so that smallest Z point is on the Z = 0 plane


I am trying to move a mesh in blender so that the lowest z-point is z=0. This would make the lowest point be on the Z = 0 plane. This is more difficult since I first scale the model by its largest axis. This is not just a single case I am working with so I am trying to make this work for any single mesh model.

Here is my current attempt:

mesh_obj = bpy.context.scene.objects[0]
# I first find the min and max of the mesh. The largest and smallest points on the Z-axis
max_floats = [mesh_obj.data.vertices[0].co[0], mesh_obj.data.vertices[0].co[1], mesh_obj.data.vertices[0].co[2]]
min_floats = [mesh_obj.data.vertices[0].co[0], mesh_obj.data.vertices[0].co[1], mesh_obj.data.vertices[0].co[2]]

for vertex in mesh_obj.data.vertices:
    if vertex.co[0] > max_floats[0]:
        max_floats[0] = vertex.co[0]
    if vertex.co[0] < min_floats[0]:
        min_floats[0] = vertex.co[0]

    if vertex.co[1] > max_floats[1]:
        max_floats[1] = vertex.co[1]
    if vertex.co[1] < min_floats[1]:
        min_floats[1] = vertex.co[1]

    if vertex.co[2] > max_floats[2]:
        max_floats[2] = vertex.co[2]
    if vertex.co[2] < min_floats[2]:
        min_floats[2] = vertex.co[2]

max_float = max(max_floats)
min_float = min(min_floats)
# I then get the the point with the biggest magnitude
if max_float < math.fabs(min_float):
    max_float = math.fabs(min_float)


recip = 1.0 / max_float
# And use that point to scale the model
# This needs to be taken into account when moving the model by its min z_point
# since that point is now smaller
mesh_obj.scale.x = recip
mesh_obj.scale.y = recip
mesh_obj.scale.z = recip

# naive attempt at moving the model so the lowest z-point = 0
mesh_obj.location.z = -(min_floats[2] * recip / 2.0)

Solution

  • The first thing you should know is that the vertex positions are in object space - as in distance from the objects origin. You need to use the objects matrix_world to translate that into world space.

    If you only want the lowest z location to be equal to zero then you can ignore the x and y values.

    import bpy
    import mathutils
    
    mesh_obj = bpy.context.active_object
    
    minz = 999999.0
    
    for vertex in mesh_obj.data.vertices:
        # object vertices are in object space, translate to world space
        v_world = mesh_obj.matrix_world * mathutils.Vector((vertex.co[0],vertex.co[1],vertex.co[2]))
    
        if v_world[2] < minz:
            minz = v_world[2]
    
    mesh_obj.location.z = mesh_obj.location.z - minz