[Closed] Change face normals
I’m trying to take a selection of planar faces and rotate them around the selection center so that they all point towards a given normal.
Here is my code, adapted from someone elses.
I can’t get it to work quite right, the normals become non-planar and don’t point towards the specified normal.
fn setFaceNorm dir obj facelist =
(
for f in facelist do
(
center_face = meshop.getFaceCenter obj f
transform_mat = matrixFromNormal (getFaceNormal obj f) --create a matrix using the normal
transform_mat.row4 = center_face --move matrix to center of polygon
face_vertices = (meshop.getVertsUsingFace obj f) as array --get the verts of polygon 1
for v in (meshop.getVertsUsingFace obj f) do --for every vertex in the first polygon,
(
in coordsys transform_mat vertex_position = (meshop.getVert obj v) --get vertex in the matix coordinates
RotateXYZ = (matrixFromNormal dir) * transform_mat -- create a Rotation matrix, transform in the space of the Normal's matrix
meshop.setVert obj v (vertex_position * RotateXYZ) --transform the vertex using the new matrix
)--for v
)
)--fn rotateface
Do you want that the faces turn around the center of the selection or
although each face turns around its center independently of the
others? (it is what you seem to do in your program)
I want all the faces to turn as a group. I’m not doing that now, that’s the point – I want to
Ok, here is what to do (no code):
Currently, your code goes through each face, collects its center, normal and vertices and rotates the vertices about the face center to match the desired direction. Problem is, the vertices will belong to OTHER faces, so rotating one face alone changes its neighbours, then the neighbours get rotated about their centers and change the first one etc. A mess.
*You should first go through all faces and collect all their vertices into a single array, with each vertex featured just once.
*Then you can loop through all vertices, collect their positions, divide by the number of vertices and thus find the center of the selection to rotate about.
*Since you said that all faces are coplanar already, you can pick the normal of the first face. If they are not, you could collect all normals and average them to find a common normal.
*Now you can go through all vertices and apply the code as seen in your example, but using the common center and the common normal.
*Now you can go through all vertices and apply the code as seen in your example, but using the common center and the common normal.
but won’t I still be rotating each face individually which will mess everything up?
No, you will be rotating all VERTICES shared by those faces, not the faces themselves.
It is like converting your face selection to vertex selection, then working with the vertices…
Ok I’ve rewritten the function like this. Everything moves as a group now, but for some reason the normal that it goes to is not what I am specifying. For instance, if I use dir=[1,0,0] then instead of having those faces point towards the global X axis, they point off in some random direction.
fn setFaceNorm dir obj facelist =
(
--make sure the faces are planar first
max modify mode
select obj
setSelectionLevel obj #face
obj.selectedFaces = facelist
meshOps.makePlanar obj
--make array of all the verts to transform
vertIndices = (meshop.getVertsUsingFace obj facelist) as array
--find the center of mass
com = [0,0,0]
for i in vertIndices do
com = com + (meshop.getVert obj i)
com = com / vertIndices.count
--apply the transformation
transform_mat = matrixFromNormal (getFaceNormal obj facelist[1])
for i in vertIndices do
(
vpos = (meshop.getVert obj i)
in coordsys transform_mat vpos
--in coordsys world
RotateXYZ = (matrixFromNormal dir) * transform_mat
meshop.setVert obj i (vpos * RotateXYZ)
)
)--fn rotateface
In order to better understand handling in a space 3d, I amused myself in writing several functions which manipulate faces with the vectors. My intention is to rewrite these functions by using the matrices (what I cannot do for the moment).
That’s related to this subject because I programmed the function which you ask (and more), but I did it with a technique based on the vectors. For me, that is much simpler.
I am also interested by a speed test between the vectors and the matrices.
the “vector based” code:
fn getNormalOnFaceSelection obj facesSel =
(
theNormal=[0,0,0]
for face in facesSel do (theNormal+=polyop.getFaceNormal obj face)
return (normalize theNormal)
)
fn getCenterOnFaceSelection obj facesSel =
(
theCenter=[0,0,0]
for face in facesSel do (theCenter+=polyop.getFaceCenter obj face)
return (theCenter/facesSel.numberSet)
)
fn moveAlongVector obj facesSel vector =
(
vertsSel=polyOp.getVertsUsingFace obj facesSel
for vert in vertsSel do (
polyOp.setVert obj vert ((polyOp.getVert obj vert)+vector)
)
)
fn rotateAroundVector obj facesSel vector rotDeg theCenter =
(
vertsSel=polyOp.getVertsUsingFace obj facesSel
q=quat rotDeg (normalize vector)
for vert in vertsSel do (
pos=polyOp.getVert obj vert
polyOp.setVert obj vert (((pos-theCenter)*q)+theCenter)
)
)
fn getAngle vector1 vector2 = ( return (acos (dot (normalize vector1) (normalize vector2))) )
fn alignFacesNormalToVector obj facesSel vector =
(
theFacesNormal=getNormalOnFaceSelection obj facesSel
theFacesCenter=getCenterOnFaceSelection obj facesSel
theRotAngle=getAngle theFacesNormal vector
theRotAxis=cross vector theFacesNormal
rotateAroundVector obj facesSel theRotAxis theRotAngle theFacesCenter
)
obj=selection[1]
if classof obj==Editable_poly then (
facesSel=polyOp.getFaceSelection obj
if facesSel.numberSet!=0 then (
--move the faces along a vector, here his normal
facesNormal=getNormalOnFaceSelection obj facesSel
moveAlongVector obj facesSel (facesNormal*30.0)
--rotate the faces around an axis, here his normal
theCenter=getCenterOnFaceSelection obj facesSel
rotateAroundVector obj facesSel facesNormal 30 theCenter
-- align the orientation of faces to a vector
alignFacesNormalToVector obj facesSel [0,0,1]
) else messageBox("no face(s) selected")
) else messageBox("not an poly object")
Thanks, this is certainly a lot simpler…I haven’t checked but I bet you can only use this on edit polys. The align face func works nicely…I’m also very interested in your getAngle() function.
It looks like you’re doing the reverse of the dot-product…but I’m not sure what this really means in 3d space? I mean, what plane are they projected onto that this angle is relevant for?
The reason I’m so curious is because my other function I’m trying to get working is basically to take an object, give it 2 normals…and performs a rotation on the object such that if the object was pointing in the direction of the first normal, it will now be pointing in the direction of the second normal. This will be very helpful for aligning objects to point in various directions.
Really ? Oh well, you will find all explanations in the Maxscript Reference
Section: How do I find the angle between 3 vertices?
lollll
Seriouly, I used this function before (I had programmed it myself!! hours of efforts…)
fn angleAbs2vecteurs a b =
(
ps=(a.x*b.x)+(a.y*b.y)+(a.z*b.z)
La=sqrt((a.x^2)+(a.y^2)+(a.z^2))
Lb=sqrt((b.x^2)+(b.y^2)+(b.z^2))
pL=La*Lb
f=(ps/pL)
if f<-1.0 then f=-1.0
if f>1.0 then f=1.0
if pL!=0 then (angle=acos f) else angle=0.0
return angle
)--fn