[Closed] Smart Normal Flipper
Hi,
I am in need of a MaxScript that will look at the geomtry of an object, and decide if the normals for the mesh should be flipped.
The logic of when it should flip normals is if most (75% >) of the normals point inwards on the object.
Anyone have an ideas on how to start programming such a script?
Thanks,
I started but I have to stop for now, so if someone else wants to take a look, feel free. Problem is that I am not accurately determining when a normal is facing out or in, but I am able to flip normals with this script, so it’s mostly there, just need to figure out how to determine the in out direction of the normal, and then test for it ratehr than what I am testing for.
(--start
local a = undefined
local i = undefined
local n = undefined
local o = undefined
local u = undefined
local po = undefined
local pf = undefined
local puninverted = undefined
local pinverted = undefined
local pg = undefined
po = polyop.getnumfaces $
pf = #()
pf = for i = 1 to po collect ( polyop.getfacenormal $ i )
puninverted = ( for n = 1 to pf.count where ( pf[n].x > 0 == true ) and ( pf[n].y > 0 == true ) collect pf[n] )
pinverted = ( for n = 1 to pf.count where ( pf[n].x < 0 == true ) and ( pf[n].y < 0 == true ) collect pf[n] )
if puninverted.count > pinverted.count then
(--if puninverted >
--pg = #()
--pg = ( for o = 1 to po collect ( polyop.getfacenormal $ o ) )
for u = 1 to pf.count do ( if ( pf[u].x < 0 == true ) and ( pf[u].y < 0 ) do polyop.flipnormals $ u )
)--if puninverted >
else
(--if pinverted >
--pg = #()
--pg = ( for a = 1 to po do ( polyop.getfacenormal $ a ) )
for u = 1 to pf.count do ( if ( pf[u].x > 0 == true ) and ( pf[u].y > 0 ) do polyop.flipnormals $ u )
)--if pinverted >
)--end
Wow! Thanks for the source code.
I had some playing around with MaxScript yesterday, and came up with this function. It calculates the center of the object, and then checks if the average dotproduct of the face normals are mostly pointing inwards.
It works most of the time. I just need to modify it to work on mesh elements.
function flipNormal obj = (
-- compute the center of all the verts
c = getVert obj 1
for vIndx = 2 to obj.numVerts do (
v = getVert obj vIndx
c = c + v
)
c = c / obj.numVerts
-- compute the average dot product between the center and face normals
d = 0.0
for fIndx = 1 to obj.numfaces do (
n1 = getFaceNormal obj fIndx
verts = getface obj fIndx
v1 = getVert obj verts.x
v2 = getVert obj verts.y
v3 = getVert obj verts.z
v = (v1 + v2 + v3) / 3
n2 = normalize(c - v)
d = d + (dot n1 n2)
)
d = d / obj.numfaces
-- if the average dotproduct is to large, then flip the normals
print d
if (d > 0.0) then (
addModifier obj (normalModifier flip:true)
)
)
Mathew… to use the script on Mesh objects, you have to edit some of the calls / commands and their structure for accessing a mesh rather than a poly object. Mesh uses a slightly different approach but if you were to look up mesh / poly methods, you’ll spot the difference right away. Commands are quite similar.
I’ll take a look at what you developed and take a look at trying to convert mine and yours into a mesh interface, then see if I can get it to work for you… tomorrow likely.
Good luck in the meantime. By the way, I did that over an hour or so because I love scripting and I was intrigued to see how this might work in Maxscript .
Maneswar
Thanks,
I have finished the script, and ran it on my problem model.
Here’s the before image of the model.
Here’s the after image of the model after I ran the script.
And I’ve attached the script if anyone wants to improve it.
function flipNormal obj = (
-- compute the center of all the verts
c = getVert obj 1
for vIndx = 2 to obj.numVerts do (
v = getVert obj vIndx
c = c + v
)
c = c / obj.numVerts
-- compute the average dot product between the center and face normals
d = 0.0
for fIndx = 1 to obj.numfaces do (
n1 = getFaceNormal obj fIndx
verts = getface obj fIndx
v1 = getVert obj verts.x
v2 = getVert obj verts.y
v3 = getVert obj verts.z
v = (v1 + v2 + v3) / 3
n2 = normalize(c - v)
d = d + (dot n1 n2)
)
d = d / obj.numfaces
-- if the average dotproduct is to large, then flip the normals
print d
if (d > 0.0) then (
addModifier obj (normalModifier flip:true)
)
)
function explodeObjectElements obj = (
result = #()
-- we will detach all the faces from the object.
-- later we will attach them back so the object appears
-- untouched
while (obj.numFaces > 0) do (
new_mesh = Editable_Mesh()
f = meshop.getElementsUsingFace obj #(1)
new_mesh.mesh = meshop.detachFaces obj f delete:true asMesh:true
new_mesh.pos = obj.pos
new_mesh.rotation = obj.rotation
new_mesh.scale = obj.scale
append result new_mesh
)
return result
)
function implodeObjectElements obj elements = (
for e in elements do meshop.attach obj e deleteSourceNode:true
update obj
)
utility SmartFlipNormals "SmartFlip"
(
button btn1 "run" pos:[16,16] width:128 height:24
on btn1 pressed do
(
progressStart "Smart Flip Normal"
c = 0
for obj in selection do (
if (classof obj == Editable_Mesh) then (
elements = explodeObjectElements obj
for e in elements do (
flipNormal e
)
implodeObjectElements obj elements
)
c = c + 1
progressUpdate (c / selection.count)
if (getProgressCancel()) then
exit
)
progressEnd()
)
)
I think this is a very handy script. We should continue to improve it.
Am I missing something here or is this the same as batching a load of unify normal commands?
The problem with this is precisely that it works on the notional centre of a group of normals so it doesn’t deal well with concave surfaces.
It’s not, because the unify feature (which in the documation states it should work) doesn’t work the same way. It runs alone the surface of geometry flipping normals so that they point in the same direction as their shared faces.
Here’s an image of the tire with the unify modifier applied to it. The modifier fails to use any logic to decide which direction is correct for the normal, but it unifies the normals correctly even if they’re pointing in the wrong direction. Does that make sense?
A concave element will most likely be in error as long as more then half the face normals exist on the wrong side of the center location. This is also true that it will make the same error is there are to many faces on one side of the center that changes the dotproduct average.
So, it still needs more work to be completely error free. But, if you look at my before and after it does work well enough for the artist to work with the model. At least, in my case.
Ah OK, sorry for the misunderstanding. I had a look at this a while back and unfortunately my math wasn’t up to sorting it out. Thanks.
if it is possible to look at the pivot of an object’s ELEMENT, then this could possibly get a better result…?
it’s just an idea.
but anyway, the script is fine! :o)
The script calculates the average center of each element, and uses that. But it’s not the boxes center. So if an element has most vertexs on the left then the center will drift to the left. Maybe that should be fixed?