How to get vertex normal using python

How to get vertex normal using python

Anonymous
Not applicable
4,322 Views
20 Replies
Message 1 of 21

How to get vertex normal using python

Anonymous
Not applicable

I drag a box into an empty scene, then using the following python script to get vertex normal. However, the result is not what i expected.

 

From the output, we can see there are just two kind of normals, which is obviously wrong.

 

I know the face normal can be retrieved with Mesh.GetFaceNormal, but what I really want to get is the vertex normal taking smooth group into account(That means every vertex has three normals). I searched the python API docs, but can't find the right way to do that.

 

Can somebody help me?

 

  python script:

 

n=list(MaxPlus.Core.GetRootNode().Children)[0]
obj=n.EvalWorldState(0).Getobj()
tri_obj = MaxPlus.TriObject._CastFrom(obj.ConvertToType(MaxPlus.ClassIds.TriMeshGeometry, 0))
mesh=tri_obj.GetMesh()
for i in xrange(mesh.GetNumVertices()): print mesh.GetRenderedVertexNormal(i)

  output:

Point3(0, 0, -1.5708)
Point3(0, 0, -1.5708)
Point3(0, 0, -1.5708)
Point3(0, 0, -1.5708)
Point3(0, 0, 1.5708)
Point3(0, 0, 1.5708)
Point3(0, 0, 1.5708)
Point3(0, 0, 1.5708)

max_normalpng.png

0 Likes
4,323 Views
20 Replies
Replies (20)
Message 2 of 21

Anonymous
Not applicable

If i set three adjacent face have the same smooth group, then the shared vertex has only one normal.

 

In C++ SDK, I can get the right normal using RVertex. Is there something like that in Python.

0 Likes
Message 3 of 21

Swordslayer
Advisor
Advisor

Looks like neither Face::getAllVerts nor Mesh::getRVert are exposed in python. As a workaround, you can find the smoothing group borders and for verts that share two or more calculate the normals separately.

Message 4 of 21

Anonymous
Not applicable

Thanks for your reply.

 

Do you mean there is no way in python to get the internal rnormal of 3ds max, and we need to calculate the normals by ourselves?

0 Likes
Message 5 of 21

Swordslayer
Advisor
Advisor
0 Likes
Message 6 of 21

drew_avis
Autodesk
Autodesk

Hi there, here is an example of how to use this API:

 

'''
An example of how to populate RNormals and get them with GetRenderedVertexNormals()
'''
import MaxPlus

obj = MaxPlus.Factory.CreateGeomObject(MaxPlus.ClassIds.Box)
n = MaxPlus.Factory.CreateNode(obj, "myBox")

print "node name: %s" % n.Name
n.Convert(MaxPlus.ClassIds.TriMeshGeometry)
object_state = n.EvalWorldState()
obj_original = object_state.Getobj()
tri_obj = MaxPlus.TriObject._CastFrom(obj_original)
tri_mesh = tri_obj.GetMesh()
print "normals built? ", tri_mesh.GetNormalsBuilt() # will return 0 if normals are not yet built
tri_mesh.CheckNormals(True) # less expensive than BuildNormals(), because it will only build if normals
# do not yet exist
print "normals built? ", tri_mesh.GetNormalsBuilt() # will now return non-0 because CheckNormals() ensures
# we've built them
normal_count = tri_mesh.GetNormalCount()
vertex_count = tri_mesh.GetNumVertices()
print " normals: ", normal_count
print " verts: ", vertex_count

for i in range(0, vertex_count):
	print "vertex: " , tri_mesh.GetVertex(i)
	print "RNormal: ", tri_mesh.GetRenderedVertexNormal(i)

 

This is the script "demoGetRenderedNormals.py" under <3dsmax>\Scripts\Python\. 

 

Drew



Drew Avis
Content Experience Designer
0 Likes
Message 7 of 21

drew_avis
Autodesk
Autodesk

Hi there, here is an example of how to use this API:

 

'''
An example of how to populate RNormals and get them with GetRenderedVertexNormals()
'''
import MaxPlus

obj = MaxPlus.Factory.CreateGeomObject(MaxPlus.ClassIds.Box)
n = MaxPlus.Factory.CreateNode(obj, "myBox")

print "node name: %s" % n.Name
n.Convert(MaxPlus.ClassIds.TriMeshGeometry)
object_state = n.EvalWorldState()
obj_original = object_state.Getobj()
tri_obj = MaxPlus.TriObject._CastFrom(obj_original)
tri_mesh = tri_obj.GetMesh()
print "normals built? ", tri_mesh.GetNormalsBuilt() # will return 0 if normals are not yet built
tri_mesh.CheckNormals(True) # less expensive than BuildNormals(), because it will only build if normals
# do not yet exist
print "normals built? ", tri_mesh.GetNormalsBuilt() # will now return non-0 because CheckNormals() ensures
# we've built them
normal_count = tri_mesh.GetNormalCount()
vertex_count = tri_mesh.GetNumVertices()
print " normals: ", normal_count
print " verts: ", vertex_count

for i in range(0, vertex_count):
	print "vertex: " , tri_mesh.GetVertex(i)
	print "RNormal: ", tri_mesh.GetRenderedVertexNormal(i)

 

This is the script "demoGetRenderedNormals.py" under <3dsmax>\Scripts\Python\. 

 

Hope that helps,

Drew



Drew Avis
Content Experience Designer
0 Likes
Message 8 of 21

Anonymous
Not applicable
RNormal:  Point3(0, 0, -1.5708)
RNormal:  Point3(0, 0, -1.5708)
RNormal:  Point3(0, 0, -1.5708)
RNormal:  Point3(0, 0, -1.5708)
RNormal:  Point3(0, 0, 1.5708)
RNormal:  Point3(0, -0, 1.5708)
RNormal:  Point3(-0, 0, 1.5708)
RNormal:  Point3(0, 0, 1.5708)

Thanks for your reply:)

 

Following your code, I still get the same result as above:(

 

By the way, I use 3ds max 2017, there is no "demoGetRenderedNormals.py" in the demo script directory.

0 Likes
Message 9 of 21

Anonymous
Not applicable

I guess you use 3ds max 2018, am I right?

 

Is your result correct? Can you post it here.

 

Thanks.

0 Likes
Message 10 of 21

drew_avis
Autodesk
Autodesk

EDIT: Ok, I re-read your first post, you want to take the smoothing group into account - let me look at this again and get back to you.

 

I get the same results:

vertex:  Point3(-12.5, -12.5, 0)
RNormal:  Point3(0, 0, -1.5708)
vertex:  Point3(12.5, -12.5, 0)
RNormal:  Point3(0, 0, -1.5708)
vertex:  Point3(-12.5, 12.5, 0)
RNormal:  Point3(0, 0, -1.5708)
vertex:  Point3(12.5, 12.5, 0)
RNormal:  Point3(0, 0, -1.5708)
vertex:  Point3(-12.5, -12.5, 25)
RNormal:  Point3(0, 0, 1.5708)
vertex:  Point3(12.5, -12.5, 25)
RNormal:  Point3(0, -0, 1.5708)
vertex:  Point3(-12.5, 12.5, 25)
RNormal:  Point3(-0, 0, 1.5708)
vertex:  Point3(12.5, 12.5, 25)
RNormal:  Point3(0, 0, 1.5708)

Is this not what you're expecting?

 

I get the same thing in MAXScript:

b = box()
convertToMesh b
for v = 1 to b.numVerts do
(
	n = getNormal b v
	format "vertex: % normal: %\n" v n
)

-- result:
vertex: 1 normal: [0,0,-1.5708]
vertex: 2 normal: [0,0,-1.5708]
vertex: 3 normal: [0,0,-1.5708]
vertex: 4 normal: [0,0,-1.5708]
vertex: 5 normal: [0,0,1.5708]
vertex: 6 normal: [0,0,1.5708]
vertex: 7 normal: [0,0,1.5708]
vertex: 8 normal: [0,0,1.5708]

 



Drew Avis
Content Experience Designer
0 Likes
Message 11 of 21

drew_avis
Autodesk
Autodesk

Ok, I just tested both the MAXScript and Python code on a box with all faces assigned to a smoothing group, and they appear to return the correct data.

 

mxs:

vertex: 1 normal: [0,-0.785398,-1.5708]
vertex: 2 normal: [0,-1.5708,-1.5708]
vertex: 3 normal: [0,1.5708,-1.5708]
vertex: 4 normal: [1.5708,0,-1.5708]
vertex: 5 normal: [0,-1.5708,1.5708]
vertex: 6 normal: [0,-0.785398,1.5708]
vertex: 7 normal: [0,0.785398,1.5708]
vertex: 8 normal: [0.785398,0,1.5708]

MaxPlus:

vertex:  Point3(-12.5, -12.5, 0)
RNormal:  Point3(0, -0.785398, -1.5708)
vertex:  Point3(12.5, -12.5, 0)
RNormal:  Point3(0, -1.5708, -1.5708)
vertex:  Point3(-12.5, 12.5, 0)
RNormal:  Point3(0, 1.5708, -1.5708)
vertex:  Point3(12.5, 12.5, 0)
RNormal:  Point3(1.5708, 0, -1.5708)
vertex:  Point3(-12.5, -12.5, 25)
RNormal:  Point3(0, -1.5708, 1.5708)
vertex:  Point3(12.5, -12.5, 25)
RNormal:  Point3(0, -0.785398, 1.5708)
vertex:  Point3(-12.5, 12.5, 25)
RNormal:  Point3(0, 0.785398, 1.5708)
vertex:  Point3(12.5, 12.5, 25)
RNormal:  Point3(0.785398, 0, 1.5708)

This is 2018.

 

Drew

 



Drew Avis
Content Experience Designer
0 Likes
Message 12 of 21

Anonymous
Not applicable

That's not correct.  There are just two different normal(positive z, negative z) for the 8 vertex.

 

RNormal:  Point3(0, 0, -1.5708)
RNormal:  Point3(0, 0, 1.5708)
0 Likes
Message 13 of 21

drew_avis
Autodesk
Autodesk

EDIT: Ok, I think I understand now, sorry for the confusion - you want the 3 normals shown in your original diagram because the faces are not in the same smoothing group.  It doesn't look like GetRenderedVertexNormals() returns more than the first vertex normal.

 

Drew

 

 



Drew Avis
Content Experience Designer
0 Likes
Message 14 of 21

Anonymous
Not applicable

Yes. So do you think i have to calculate normals by ourselves in python? I am just wondering whether there is an API

can do that.

0 Likes
Message 15 of 21

drew_avis
Autodesk
Autodesk

Right now you will need to calculate normals yourself, if there's more than 1 normal per vertex.  Thank you for posting this - I've opened a defect on this lack of exposure, though if it does get fixed, it won't be until after the 2019 release.

 

Drew



Drew Avis
Content Experience Designer
0 Likes
Message 16 of 21

Anonymous
Not applicable

Got it. Thanks

0 Likes
Message 17 of 21

denisT.MaxDoctor
Advisor
Advisor

@drew_aviswrote:

Right now you will need to calculate normals yourself, if there's more than 1 normal per vertex. 

Drew


what do mean by "calculate"? average? but it doesn't make sense. Every geometry vertex (by index) might have more than one corresponding RNormal. It's how it is. And you have to live with it...

 

i don't see any bug in code above. I just don't sure that  

GetRenderedVertexNormal 

does do everything right 

0 Likes
Message 18 of 21

Anonymous
Not applicable

Yeah, but the problem is that we can't access the rnormal with Python API (GetRenderedVertexNormal fails to do that).

 

With C++ API, we can.

 

0 Likes
Message 19 of 21

denisT.MaxDoctor
Advisor
Advisor
GetRenderedVertexNormal

there is no this method in c++ api.

 

you can get pointer to the 'i-th' RVertex with mesh->getRVertPtr(int i)

after that you have access to RNormal(s). This is all that you need

0 Likes
Message 20 of 21

Anonymous
Not applicable

I think you misunderstood me. Sorry for the confusion. 

 

1. Of course I know there is no 'GetRenderedVertexNormal' in C++. The problem is that in Python, its result is wrong.

 

2. "With C++ API, we can", I mean in C++ we can get the right rnormal using RVertex like what you stated. 

0 Likes