First let’s assume that verts a,b,c,d form a rectangle just to simplify stuff at the beginning. We have something like this:
d----c
| |
| |
a----b
For aligning object with this plane we need at least 2 axes (vectors). Normal vector is not enough. You experienced this already when trying to “rotate” object such that its z axis matches the normal of your plane.
I said we need 2 axes… Well… We need all 3 axes, but when 2 of them are specified - the third one can easily be computed as cross product of the other two.
So say a,b,c,d are coordinates of the plane and this plane IS a rectangle, so all angles between edges of it are right angles.
For rotation matrix we need normalized vectors.
Having coords of verts we can define x and y as for example:
x is vector ab
y is vector ad
We need them normalized, so:
x = (b - a).normalized()
y = (d - a).normalized()
Then we can compute z that should be equal to normal of the plane. To be sure that it’s not flipped we can calculate this as cross product of x and y, so we have:
x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)
In case of z we don’t even have to normalize it, because cross product of two perpendicular unit vectors will always result in unit vector perpendicular to both of them. If however you don’t trust me on that, you can do: z = x.cross(y).normalized()
Now x,y,z can be used as columns of 3x3 rotation matrix.
x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)
mat3x3 = Matrix().to_3x3()
mat3x3.col[0] = x
mat3x3.col[1] = y
mat3x3.col[2] = z
Now we need to apply rotation defined by such matrix onto our object.
First I’d store original scale of our object in a variable, because applying rotation matrix on our object will reset the scale. So the final code would look like this:
import bpy
from mathutils import * # to be able to perform vector math
# a, b, c, d are defined as coords of the verts of rectangle like this:
# d----c
# | |
# | |
# a----b
#
ob = bpy.data.objects['name_of_your_object']
scale = ob.scale.copy()
# Compute the middle of abcd rectangle to be used as new location of ob
loc = 0.25 * (a + b + c + d)
x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)
mat3x3 = Matrix().to_3x3()
mat3x3.col[0] = x
mat3x3.col[1] = y
mat3x3.col[2] = z
mat = mat3x3.to_4x4()
ob.matrix_world = mat
ob.location = loc
ob.scale = scale
Please mark that this will work ONLY when a,b,c,d form a proper rectangle. If not - we’re not lost, there are ways to handle this, but I’ll not dive into it now.
What do you think about such solution? I don’t know if this is the only way, maybe there are simpler solutions.