30
$\begingroup$

I added a cylinder to my scene, rotated, resized, like this:
default cylinder

I moved it up, because I want to make a model from this and I want it to stand on the ground:
cylinder moved up

I zoomed in and learned, that this object is not on the grid floor, it floats above it:
cylinder not alined with grid floor

My question is: how can I put any object on the floor? (Yes, right, I could move it up half it's size, but think on more complex objects, like a terrain.)

$\endgroup$

11 Answers 11

28
$\begingroup$

You could select a vertex, edge or face in Edit Mode and snap the 3D cursor to the selection Shift-S Cursor to Selection

enter image description here

In object mode set the object origin to 3D-cursor.

enter image description here

After that you can easily position the object to the 3D cursors position which can be adjusted in the proerties panel.

enter image description here

Followed by Snap Shift-S Selection to Cursor

$\endgroup$
2
  • $\begingroup$ Move the cursor to selection, move the object origin to cursor, then move object so it's origin is at Z zero. Makes perfect sense. So why do you then say "Snap Selection to Cursor" which undoes what you just did? The 3D cursor is still where it was when you moved it to the selected geometry with "Cursor to Selection". So why would you want to move the object back to the 3D cursor? $\endgroup$
    – Glazed
    Commented Sep 1, 2021 at 1:18
  • $\begingroup$ @Glazed - Typo I think... in Object mode, I think the next steps would be "Object > Set Origin > Origin to Center of Mass (volume)", then [Shift] + [S], then "Cursor to Selected" $\endgroup$ Commented Jun 22 at 7:17
15
$\begingroup$

The script posted earlier is overkill IMHO, and inefficient. The following is all you need. This doesn't mess with object origins and doesn't require you to apply rotations. No need for bmesh either.

import bpy
context = bpy.context

for obj in context.selected_objects:
    mx = obj.matrix_world
    minz = min((mx @ v.co)[2] for v in obj.data.vertices)
    mx.translation.z -= minz
$\endgroup$
6
  • $\begingroup$ Oh, very interesting @ mx.translation, thanks! $\endgroup$
    – MACHIN3
    Commented Aug 29, 2018 at 18:44
  • $\begingroup$ Done, thanks. I've also removed the list comprehension. $\endgroup$
    – MACHIN3
    Commented Aug 31, 2018 at 10:13
  • 1
    $\begingroup$ Thankyou. Prefer (mx * v.co).z but that is only aesthetics. IMO simplest answer to implement, and best answer here. Hopefully someone else will upvote too. $\endgroup$
    – batFINGER
    Commented Aug 31, 2018 at 15:41
  • $\begingroup$ How to integrate these python code into blender as a button? $\endgroup$
    – chaosink
    Commented Oct 19, 2018 at 8:58
  • $\begingroup$ Where do you want the button? $\endgroup$
    – MACHIN3
    Commented Oct 20, 2018 at 11:44
12
$\begingroup$

The smaller script posted by MACHIN3 is perfect, however blender 2.8x has changed the way it multiplies matrices and you now need to use the @ symbl instead of * or you will get:

TypeError: Element-wise multiplication: not supported between 'Matrix' and 'Vector'

import bpy
context = bpy.context

for obj in context.selected_objects:
    mx = obj.matrix_world
    minz = min((mx @ v.co)[2] for v in obj.data.vertices)
    mx.translation.z -= minz

I would comment on the solution but I don't have enough reputation :(

$\endgroup$
2
  • 3
    $\begingroup$ Welcome to BSE. Not a fan of 1 character edits becoming new answers. Soon you will have rep required to make a suggested edit, comment or upvote. $\endgroup$
    – batFINGER
    Commented Mar 21, 2020 at 12:25
  • $\begingroup$ @SeanPuller Could you delete this duplicate answer, now that the change you mentioned has been integrated into MACHIN3's entry? $\endgroup$
    – Venryx
    Commented Apr 25, 2021 at 23:41
6
$\begingroup$

Surely the simplest method is to use Rigid Body physics sim. Simply set the 'floor' as Passive and the object as Active and then run the simulation. The object will fall (assuming you haven't messed with gravity) until it comes to rest naturally on the floor.

Once your object has settled into position, stop the animation, select it and press Ctrl+A and select 'Visual Transform' to apply the current location and rotation to the object. You can then safely remove the rigid body physics if no longer required.

$\endgroup$
4
$\begingroup$

Normally I just rotate and translate it into position manually (perhaps with snapping), which is good enough in most cases.

There is also an addon bundled with blender called Drop to ground which is useful for placing one or many objects on a ground object quickly.

To use it, enable it ⎈ Ctrl⎇ AltU> User preferences > addons > Drop to Ground. Now you'll have a Drop to Ground button in 3D view > Tool Shelf (T) > Addons tab > Drop to Ground.

$\endgroup$
5
  • 2
    $\begingroup$ It is not possible to "enable" that py, because of an error: attributeError: RNA_Types object has no attribute VIEW3D_PT_tools_objectmode. :( $\endgroup$
    – Zéiksz
    Commented Jan 8, 2015 at 11:09
  • $\begingroup$ I also tried snapping. This did not work in this case, because there was no other object on the scene. Will check with a plane as floor too. $\endgroup$
    – Zéiksz
    Commented Jan 8, 2015 at 11:13
  • 1
    $\begingroup$ You can also use dynamics to do this. Make the ground a passive physics object. Set the other objects to be active. Now run the animation and everything should fall to the ground. There's a bit more to it than that if you want things to stay where they are when the timeline moves but to start you off that should work. $\endgroup$ Commented Jan 8, 2015 at 12:57
  • 1
    $\begingroup$ Using Blender 2.7.6, I was able to fix the RNA_Types error by changing bpy.types.VIEW3D_PT_tools_objectmode to bpy.types.VIEW3D_PT_tools_object. I put the fixed code in a Gist. $\endgroup$
    – mhulse
    Commented Oct 25, 2015 at 0:40
  • 1
    $\begingroup$ This addon is not bundled with Blender 3.2. However, MACHIN3's script worked well. $\endgroup$
    – user31389
    Commented Jun 15, 2022 at 14:57
4
$\begingroup$

You'll need this script:

import bpy
from mathutils import Vector
import bmesh

context = bpy.context

def global_origin_to_bottom(obj):
    mw = obj.matrix_world

    bm = bmesh.new()
    bm.from_mesh(obj.data)
    global_verts = [mw * Vector(v[:]) for v in obj.bound_box]
    print(global_verts)

    l = len(global_verts)
    x = sum([v.x for v in global_verts]) / l
    y = sum([v.y for v in global_verts]) / l
    z = min([v.z for v in global_verts])
    global_origin = Vector([x, y, z])

    local_origin = mw.inverted_safe() * global_origin

    for v in bm.verts:
        v.co = v.co - local_origin

    bm.to_mesh(obj.data)

    #move the mesh back

    mw.translation = global_origin

def origin_to_bottom(obj):
    mw = obj.matrix_world
    local_verts = [Vector(v[:]) for v in obj.bound_box]
    bm = bmesh.new()
    bm.from_mesh(obj.data)
    x, y, z = 0, 0, 0

    l = len(local_verts)
    '''
    # will give a modified center
    # eg if array modifier will be centre of array
    y = sum([v.y for v in local_verts]) / l
    x = sum([v.x for v in local_verts]) / l
    '''
    z = min([v.z for v in local_verts])

    local_origin = Vector((x, y, z))
    global_origin = mw * local_origin

    for v in bm.verts:
        v.co = v.co - local_origin

    bm.to_mesh(obj.data)

    #move the mesh back

    mw.translation = global_origin

#test

mesh_objs = [mo for mo in context.selected_objects if mo.type == 'MESH']
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')

for o in mesh_objs:
    origin_to_bottom(o)
    #global_origin_to_bottom(o)

Save it as a txt file. Open the txt file with Blender Script Editor, select your objects and hit "Run Script".

enter image description here

Make sure to apply Rotation to the objects first if you have rotated them. Rotation X, Y and Z should be zero.

enter image description here

If not select your objects, press Ctrl + A and select Rotation.

enter image description here

To set all these objects to a specific Location Alt + Click into the Location Value, type your desired value and hit enter.

enter image description here

If you want to move the objects to the height of a different object you can snap your cursor to other Vertices or Objects and copy the location of your 3D Cursor to your objects that you wish to move.

If you use it as often as I do save it into your start-up file. Right click on the Menu Bar of the Text Editor, select collapse menus and tuck it away to the side like I did:

enter image description here

$\endgroup$
3
$\begingroup$

This is my Addon, based on the MACHIN3 code, the help of many people and a lot of research. Issues: only works on objects with vertices, surely it will have many programming flaws, an apology for that. However it seems to work properly with its limitations

bl_info = {
"name": "Aterrizar",
"author": "Flobo02",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > Object > Snap",
"description": "Move object to floor",
"warning": "",
"wiki_url": "",
"category": "Object",
}

import bpy

#funcion
def main(context):
    for obj in context.selected_objects:
        mx = obj.matrix_world
        minz = min((mx @ v.co)[2] for v in obj.data.vertices)
        mx.translation.z -= minz
#operador
class ATERRIZAR(bpy.types.Operator):

    bl_idname = "snap.aterrizar"
    bl_label = "Aterrizar"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        main(context)
        return {'FINISHED'}
#boton
def boton(self, context):
    self.layout.operator( 
        ATERRIZAR.bl_idname,
        text="To floor",
        icon='TRIA_DOWN_BAR')

# ubicacion
bpy.types.VIEW3D_MT_snap.append(boton)

def register():
    bpy.utils.register_class(ATERRIZAR)

def unregister():
    bpy.utils.unregister_class(ATERRIZAR)

if __name__ == "__main__":
    register()

test in Blender 2.82a

 

$\endgroup$
2
$\begingroup$

Placing an object on the ground.

  • In Edit Mode select the bottom vertex, edge or face.
  • From the ShiftS menu, choose 'Cursor to Selected'.
  • In Object Mode, ShiftCtrlAltC, 'Set Origin to Cursor'
  • Still in Object Mode, in the properties region of the 3D View (toggled by N,) in the 'Transform' panel, set Location Z to 0.
$\endgroup$
1
$\begingroup$

I don't have enough reputation to comment. But flobo2's addon has an indentation error on line 29. Just add four spaces at the beginning of that line, and you're good.

It's really handy.

Here's the fixed code, if you're too lazy to add the spaces yourself.

bl_info = {
"name": "Aterrizar",
"author": "Flobo02",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > Object > Snap",
"description": "Move object to floor",
"warning": "",
"wiki_url": "",
"category": "Object",
}

import bpy

#funcion
def main(context):
    for obj in context.selected_objects:
        mx = obj.matrix_world
        minz = min((mx @ v.co)[2] for v in obj.data.vertices)
        mx.translation.z -= minz
#operador
class ATERRIZAR(bpy.types.Operator):

    bl_idname = "snap.aterrizar"
    bl_label = "Aterrizar"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        main(context)
        return {'FINISHED'}
#boton
def boton(self, context):
    self.layout.operator( 
        ATERRIZAR.bl_idname,
        text="To floor",
        icon='TRIA_DOWN_BAR')

# ubicacion
bpy.types.VIEW3D_MT_snap.append(boton)

def register():
    bpy.utils.register_class(ATERRIZAR)

def unregister():
    bpy.utils.unregister_class(ATERRIZAR)

if __name__ == "__main__":
    register()
$\endgroup$
1
  • 1
    $\begingroup$ I've made this change for you, thanks. For future reference I think you can propose an edit to a post and one of us can approve it. $\endgroup$ Commented Jan 17, 2021 at 2:14
0
$\begingroup$

The answer by @MACHIN3 is good, but I disliked how it would snap each object individually to the ground, instead of snapping the whole selected group to the ground (ie. a shared offset amount).

For this "group shift" functionality, I used this code:

import bpy
context = bpy.context

minz = min(min((obj2.matrix_world @ v.co).z for v in obj2.data.vertices) for obj2 in context.selected_objects)
for obj in context.selected_objects:
    obj.matrix_world.translation.z -= minz
$\endgroup$
0
$\begingroup$

Currently, the actual simplest answer is described in https://blender.stackexchange.com/a/79655/151054 , i.e. (to reiterate):

Enable the align tools Add-On, and use Z-Axis Align Location - if you have the cursor in 0,0,0 - just set the "Min" reference and "Cursor" destination.

No need to use external scripts or manual labor anymore in 2022'.

Alternatively: select any of the bottom vertices, take the Z value, paste it inverted as the object Z translation value.

$\endgroup$

You must log in to answer this question.

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