Notifications
Clear all

[Closed] Need help finishing / debugging script

PolyTools3D
OMG)… Man)))… Your code)))…


----------------------------------------------------------------------------------------------
[ 100.00 ]  [  1173218 ] / [  1173218 ]   Removed: [   766519 ]   Percent: [  65.33 ]
Elapsed:      14 secs /    0.24 mins   FPS:   [      81208 ]

This is freaking great MAN!!!

Of course that printing to the listener does slow things down, but that is something that can’t be optimized, as the OP explained, the information needed to be printed out in the Listener.

  Regarding meshop.getFaceArea, it does indeed look like the number of faces in a mesh affects its performance.

  Have you compared your code against mine using only 1 mesh with 1 million faces?

  Here is a quick test which shows it can be over 200 times faster to not use meshop.getFaceArea, and that number should go higher for larger meshes. 
(
 	
 	fn RunTests obj it:10000 =
 	(
 		
 		st = timestamp(); sh = heapfree
 		_getFaceArea = meshop.getFaceArea
 		for x = 1 to it do
 		(
 			area = _getFaceArea obj 1
 		)
 		format "faces:% time:% ram:% 	% 
" obj.numfaces (timestamp()-st) (sh-heapfree) "meshop.getFaceArea"
 
 		st = timestamp(); sh = heapfree
 		for x = 1 to it do
 		(
 			face = getface obj 1
 			v1 = getvert obj face[1]
 			v2 = getvert obj face[2]
 			v3 = getvert obj face[3]
 			area = length (cross (v1-v2) (v1-v3))/2.0
 		)
 		format "faces:% time:% ram:% 	% 
" obj.numfaces (timestamp()-st) (sh-heapfree) "custom area"
 	)
 	
 	gc()
 	delete objects
 
 	obj = snapshotasmesh (box lengthsegs:1 widthsegs:1 heightsegs:1)
 	RunTests obj
 	
 	obj = snapshotasmesh (box lengthsegs:100 widthsegs:100 heightsegs:100)
 	RunTests obj
 	
 )

meshop.getFaceArea is very slow function. this bug was all time of max being. there is no any reason to be so slow. for example plyop.getfacearea hundreds times faster.

PolyTools3D
My previous post, that is info for single object with your code.
My code for that same object took 80+ mins and used over 12GB of ram))))… LAWL…

1 Reply
(@polytools3d)
Joined: 10 months ago

Posts: 0

Yes, in the case of only one object, the slowdown is mostly produced by the buggy meshop.getFaceArea function. As you can see in the following simple test, the time for calculating the face area grows with the amount of faces the object has.

The function calculates 1000 times the area of the face 1 on 5 objects with different number of faces. If the amount of faces wouldn’t affect the performance of the getFaceArea function, then all the times should be the same, but they are not.

(
  gc()
  delete objects
  
  _getFaceArea = meshop.getFaceArea
  
  for x = 0 to 200 by 50 do
  (
  	obj = snapshotasmesh (box lengthsegs:x widthsegs:x heightsegs:x)
     
  	st = timestamp(); sh = heapfree
     
  	for x = 1 to 1000 do area = _getFaceArea obj 1
     
  	format "faces:% 	 time:% 	 ram:% 
" obj.numfaces (timestamp()-st) (sh-heapfree)
  )
  )

faces:12 time:2 ram:64L
faces:30000 time:97 ram:64L
faces:120000 time:382 ram:64L
faces:270000 time:857 ram:64L
faces:480000 time:1524 ram:64L

that means the bug is worth than i’ve thought.

for what it’s worth here the source for it.

Value*
 meshop_getFaceArea_cf(Value** arg_list, int arg_count)
 {
 	check_arg_count(getFaceArea, 2, arg_count);
 	Mesh* mesh = get_meshForValue(arg_list[0], MESH_READ_ACCESS, NULL, getFaceArea);
 	int nFaces=mesh->getNumFaces();
 	BitArray faces(nFaces);
 	ValueToBitArrayM(arg_list[1], faces, nFaces, GetString(IDS_MESH_FACE_INDEX_OUT_OF_RANGE), MESH_FACESEL_ALLOWED, mesh);
 	float area=0.0f;
 	for (int fi = 0; fi < nFaces; fi++) {
 		if (faces[fi]) {
 			area += Length(mesh->FaceNormal(fi));
 		}
 	}
 	return Float::intern(area/2.0f);
 }

it suffers from being a “catch all” as it has to iterate over all the faces just in case say the caller wants the area of the first and last face. So your example shows it in it’s worst possible light. Unfortunately there’s no “quick way” of collecting a bitarray of zero area faces without calling this function !

something like this possibly ?

def_visible_primitive(deleteZeroAreaFaces, "deleteZeroAreaFaces");
         
         Value* deleteZeroAreaFaces_cf(Value** arg_list, int arg_count)
         {
         	check_arg_count(getFaceArea, 1, arg_count);
         	Mesh* mesh = get_meshForValue(arg_list[0], MESH_READ_ACCESS, NULL, deleteZeroAreaFaces);
         	int nFaces = mesh->getNumFaces();
         	BitArray faces(nFaces);
         	faces.ClearAll();
         	for (int fi = 0; fi < nFaces; fi++) 
         		if(LengthSquared(mesh->FaceNormal(fi)) == 0.0f)  // comparing float to zero is bad form
         			faces.Set(fi);
         	BitArray isoverts;
         	mesh->DeleteFaceSet(faces, &isoverts);
         	mesh->DeleteVertSet(isoverts);
         	return &ok;
         }
    or the completely untested 

        fn deleteZeroAreafaces msh = 
        (
        	zfaces = for f=1 to msh.numfaces where length (getfacenormal msh f) == 0.0 collect f;
        	meshop.deleteFaces msh zfaces;
        )
 ^^ takes about 1.5 seconds to process approx half a million faces (on a 6 year old pc). and about 2 seconds if it needs to delete more than a 3rd of the faces.

this has the same performance but should use 32 times less memory.

fn deleteZerofaces2 msh = 
   (
   	zfaces = #{}
   	zfaces.count = msh.numfaces;
   	for f = 1 to msh.numfaces do if length (getfacenormal msh f) == 0.0 then zfaces[f] = true;
   	meshop.deleteFaces msh zfaces;
   )

BTW the sdk mxs extention function will handle half a million faces in about 0.046s FTW

Thank you Klunk for all this information, especially for the “getfacenormal” tip. I didn’t know the face normal would be [0,0,0] if its area is 0.

Using this, the script runs around 2 times faster than manually calculating the area.

Do you know if it is a 100% safe to assume that the face normal would be [0,0,0] for any face with area 0?

 Very clever approach:thumbsup:
 I didn't test this but still ... I not know is it fast
fn deleteZerofaces3 msh = 
  (
  	local snapMsh = snapshotasmesh msh
  	for f = 1 to snapMsh.numfaces where length (getfacenormal snapMsh f) == 0.0 do deleteFaces snapMsh f
  	msh = snapMsh
  )

Do you know if it is a 100% safe to assume that the face normal would be [0,0,0] for any face with area 0?

i guess so, it’s what max uses in the meshop.getfacearea function and using e1 crossed with e2 to generate the face normal should alway produce a zero normal if the verts are in a line eg the edges are parallel or if all the verts are in the same position.

Page 3 / 4