[Closed] How to make my function faster – get peer vertex normals
Hi
I wrote function that returns normals for each model’s vertex.
It works fine, but it is so slow. Execution for standad teapot takes 7 to 10 seconds…
I have read tips in max help and also tried trick with disabling command panel, but it no give results.
Maybe I do something totaly wrong? Other parts of my script (geometry exporter) doing much more but takes less time (under one second).
There is the script. I wrote testing interface, so you can copy and run it. Result shape in viewport will be flat shaded, but in render should looks identical as source object (normals returned by my function)
-- get peer-vertex normals using normals source object
-- not exacly working...
-- by Miran - Miran4@op.pl
-- you can use on GNU license terms
-- return average point3 of point3 array
function avg3 point3array =
(
local average = point3 0.0 0.0 0.0
if( point3array.count > 0 ) then
(
for i=1 to point3array.count do
(
average += point3array[i]
)
average /= point3array.count
)
return average
)
-- returns array of face indices which using given vertice's indice
function getFacesByVert object vert =
(
local face
local faces = #()
for i=1 to object.numfaces do
(
face = (getFace object i)
for j=1 to 3 do
(
if( face[j] == vert ) then
(
append faces i
exit
)
)
)
return faces
)
--
function getVertNormalsObj vert_obj normal_obj =
(
print "### getVertNormalsObj started ###"
local tt=timeStamp(), t=tt
local normalMod, localNormalMod = true
-- use already existing normals modifier
for i=1 to normal_obj.modifiers.count do
(
if( (classOf normal_obj.modifiers[i]) == editNormals ) then
(
normalMod = normal_obj.modifiers[i]
localNormalMod = false
exit
)
)
-- create new modifier
if( localNormalMod ) then
(
normalMod = editNormals()
addModifier normal_obj (normalMod)
)
-- for correct working of modifier
select #( normal_obj )
max modify mode
print( " #1 time: " + (((timeStamp()-t)/1000.0) as string) + "s" )
t=timeStamp()
local VertNormals = #()
local faces_vert, vert_normals, face, face_normals
for i=1 to vert_obj.numverts do -- this is most time-wasting loop
(
-- get faces builded on this vert
faces_vert = getFacesByVert vert_obj i
vert_normals = #()
for j=1 to faces_vert.count do
(
-- get this vert corner index in the face
face = getFace vert_obj faces_vert[j]
for k=1 to 3 do
(
if( face[k] == i ) then
(
-- get this face's corner normal
append vert_normals (normalMod.GetNormal (normalMod.GetNormalID faces_vert[j] k))
exit
)
)
)
append VertNormals (avg3 vert_normals)
)
if( localNormalMod ) then
deleteModifier normal_obj normalMod -- remove local created modifier
print( "TOTAL time: " + (((timeStamp()-tt)/1000.0) as string) + "s" )
return VertNormals
)
-- testing interface
rollout VertNormals "VertNormals"
(
button getBtn "Get peer-vertex normals"
on getBtn pressed do
(
undo off
(
disableSceneRedraw()
if( (selection.count > 0) and ((classOf selection[1]) == Editable_mesh) ) then
(
meshop.autoedge selection[1] selection[1].Edges 0 -- make all faces triangles
local broken_mesh = copy selection[1]
broken_mesh.name = "broken"
meshop.breakVerts broken_mesh broken_mesh.verts -- break all verts
local vert_normals = getVertNormalsObj broken_mesh selection[1] -- tested function here
for i=1 to broken_mesh.numverts do
( -- apply calculated peer-vertex normals (visible when render)
setNormal broken_mesh i vert_normals[i]
)
move broken_mesh (point3 (1.5*(broken_mesh.max.x-broken_mesh.min.x)) 0.0 0.0) -- move next to original object
select #( selection[1], broken_mesh ) -- select source and result
max zoomext sel all -- center viewport on selected
)
else
messageBox "No editable mesh selected!"
enableSceneRedraw()
)
)
)
-- create the rollout window and add the rollout
if VertNormalsFloater != undefined do closerolloutfloater VertNormalsFloater
VertNormalsFloater = newRolloutFloater "VertNormals Floater" 200 100
addRollout VertNormals VertNormalsFloater
when you break all vertices in the mesh you can simply get current vertex normal just using getnormal function.
Then I will get normals of flat shaded objects. I want get same normals like in original object.
Break all verts is only example for debuging. My exporter is optimizing the mesh, and breaks only these vertices which need it.
I rewrite the code. Now it no using modifier. But I reduced execute time only from 23 seconds to 17 (tested on teapot with 10 segments).
The most hevy think is part ended by #S 6 print text (17 sec). #S 4 also isn’t preety fast (4 sec).
-- get peer-vertex normals using normals source object
-- by Miran - Miran4@op.pl
-- return average point3 of point3 array
function avg3 point3array =
(
local average = point3 0 0 0
if( point3array.count > 0 ) then
(
for i=1 to point3array.count do
average += point3array[i]
average /= point3array.count
)
return average
)
function getVertNormalsObj vert_obj normal_obj =
(
local t=timeStamp()
-- working on snaps avoid errors and speed up mesh operations
local vert_mesh
if( (classOf vert_obj) == mesh or (classOf vert_obj) == trimesh ) then
vert_mesh = vert_obj
else
vert_mesh = snapshotAsMesh vert_obj
if( (classOf normal_obj) == mesh or (classOf normal_obj) == trimesh ) then
normal_mesh = normal_obj
else
normal_mesh = snapshotAsMesh normal_obj
print( "#S 1: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
-- collect array of coresponding faces
local vert_mesh_centers = (for i=1 to vert_mesh.numfaces collect (meshOp.getFaceCenter vert_mesh i))
local normal_mesh_centers = (for i=1 to normal_mesh.numfaces collect (meshOp.getFaceCenter normal_mesh i))
local normal_mesh_centers_idx = (for i=1 to normal_mesh.numfaces collect i)
print( "#S 2: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
mirror_face = #(); mirror_face.count = vert_mesh.numfaces
for i=1 to vert_mesh.numfaces do
(
for j=1 to normal_mesh_centers.count do
(
if( (distance vert_mesh_centers[i] normal_mesh_centers[j]) < 0.000005 ) then
(
mirror_face[i] = normal_mesh_centers_idx[j]
-- remove mathed face from search
deleteItem normal_mesh_centers_idx j
deleteItem normal_mesh_centers j
exit
)
)
)
print( "#S 3: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
-- free memory
vert_mesh_centers = undefined
normal_mesh_centers = undefined
normal_mesh_centers_idx = undefined
gc light:true
print( "#S 4: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
-- get normals
local normals = (for i=1 to vert_mesh.numverts collect #())
local vert_face, normal_face, faceNormals, vert_v, normal_v, vert, verts_left
print( "#S 5: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
for i=1 to vert_mesh.numfaces do
(
if( mirror_face[i] != undeined ) then
(
vert_face = getFace vert_mesh i
normal_face = getFace normal_mesh mirror_face[i]
faceNormals = meshOp.getFaceRNormals normal_mesh mirror_face[i]
verts_left = #(1,2,3) -- array of yet unmatched face's corners
for j=1 to 3 do -- three face corners
(
if( verts_left.count != 1 ) then -- 2 verts already mathed, so last one has to be correct
vert = getVert vert_mesh vert_face[j] -- store pos of corner vertice
for k=1 to verts_left.count do
(
if( (verts_left.count == 1) or ((distance vert (getVert normal_mesh normal_face[verts_left[k]])) <= 0.000005) ) then
(
--if( (findItem normals[vert_face[j]] faceNormals[verts_left[k]]) == 0 ) then -- this normal isn't already added to this vert (disable to get face count average/enable to get different normals count average, check which result is better for you)
append normals[vert_face[j]] faceNormals[verts_left[k]]
if( verts_left.count != 1 ) then -- don't do for last one
(
deleteItem verts_left k -- matched
exit
)
)
)
)
)
)
print( "#S 6: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
-- average results to get single normal peer vert
local avg_normals = #()
avg_normals.count = vert_mesh.numverts
for i=1 to vert_mesh.numverts do
(
if( normals[i].count > 1 ) then
avg_normals[i] = (avg3 normals[i])
else
if( normals[i].count == 1 ) then
avg_normals[i] = normals[i][1]
else
avg_normals[i] = [0,0,0]
)
print( "#S 7: " + ( (((timeStamp()-t)/1000.0)) as string ) ); t=timeStamp()
return avg_normals
)
-- testing interface
rollout VertNormals_roll "VertNormals"
(
button getBtn "Get peer-vertex normals"
on getBtn pressed do
(
undo off
(
--disableSceneRedraw()
if( (selection.count > 0) and ((classOf selection[1]) == Editable_mesh) ) then
(
meshop.autoedge selection[1] selection[1].Edges 0 -- make all faces triangles
local broken_mesh = copy selection[1]
broken_mesh.name = "broken"
meshop.breakVerts broken_mesh broken_mesh.verts -- break all verts
local t = timeStamp()
local vert_normals = getVertNormalsObj broken_mesh selection[1] -- tested function here
print( "NEW GET NORMALS TIME: " + ( (((timeStamp()-t)/1000.0)) as string ) )
for i=1 to broken_mesh.numverts do
( -- apply calculated peer-vertex normals (visible when render)
setNormal broken_mesh i vert_normals[i]
)
move broken_mesh (point3 (1.5*(broken_mesh.max.x-broken_mesh.min.x)) 0.0 0.0) -- move next to original object
select #( selection[1], broken_mesh ) -- select source and result
max zoomext sel all -- center viewport on selected
)
else
messageBox "No mesh selected!"
--enableSceneRedraw()
)
)
)
-- create the rollout window and add the rollout
if VertNormalsFloater != undefined do closerolloutfloater VertNormalsFloater
VertNormalsFloater = newRolloutFloater "VertNormals Floater" 200 100
addRollout VertNormals_roll VertNormalsFloater
Maye is there better way to match coresponding object’s faces and corners.
there is a topic in maxscript help named “how to make it faster” try to use those tips.
Try first the “do not use return, break, exit or continue”
[color=white][/color]
cheers
I didn’t know that “exit” function works so slow.
After exit replace it executes in less than one second (before 17 seconds).
Thanks!