Notifications
Clear all

[Closed] Calculating Surface Area

Hi folks,

I stumbled accross a problem in MXS and I’d like to know if anyone has ever faced this:

I would like to calculate the surface area of an editable poly’s face in respect to the size of the current view.
What I did so far is the following:

  1. Get the face
  2. Get the points of that face
  3. Transform the points into screen space
  4. Calculate the surface area in screen space for each triangle of the face which is building up the face

This methods works buuuuuut it’s slowish on meshes that exceed 20k polygons.

Has anyone an approach that is a bit faster? For example a build in function?
I know that there is a polyOp.function that calculates the face-area but this value is in respect to the scene not to the view, right?

Any help would be appreciated.

-ko’gen

20 Replies
 lo1

Is the purpose of the algorithm to determine the precentage of the viewport filled by an object?
Because measuring all the faces does not achieve that, unless you are filtering out obscured faces.

Yen it is. Andi you’Re right. I allready do that. But since this was not important for the speed problem i left it at this point. The core Problem is the slow calulation of the triangles for every face.

 lo1

I don’t know a faster method to do this but if you share your code I can attempt to optimize.

Perhaps it might be faster to just grab the viewport and count bitmap pixels.

If accuracy isn’t very important, shoot some rays in a grid from the camera and see how many hit the object, you should get a rough percentage.

how slow is it? to process 20K faces should take ~1 sec. more than that is slow.

1 Reply
(@jonadb)
Joined: 11 months ago

Posts: 0

Are you proposing a new maxscript challenge?

here is an idea, but I’m not sure if it will work faster.
I’m talking about a situation where you want to know what is the portion of the view that an object covers, taking into account obscured faces.

  1. save the materials of all visible objects in memory
  2. assign a standard material with a black diffuse color to all visible objects accept the one you want to test.
  3. assign a standard material with a white diffuse color and self illumination at 100% to the object you want to test.
  4. render the scene from the view you want to test.
  5. count the number of pixels in the image that are white.

you can control the precision by increasing or decreasing the resolution of the render.
you can also make a version of this algorithm that tests a couple of objects simultaneously by assigning different random colors to these objects and counting how many pixels get’s each color (or a color that is closest to it).

Hi back,

thank you so much for your thoughts.
I’ve measured the time and I guess denisT you’d consider it “fast” since a 11k Mesh took 0.3 seconds to be calculated and 200k were around 2 seconds. But to me it seems still slow.

Basically the ray idea sounds useful I guess I would need to store the hits for every piece in the scene within an array and than calculate the surface areas as approximated squares. The problem is if I remember correctly shooting 1 Million hits is pretty slow in Max as well and if I want to use it for an image size of 2k x 4k it’s not even half of the amount of rays I’d need to get an accurate result.

When it comes to calculate the Pixels I sense a problem about Maxscript: The image-processing functionality is even slower than calulating triangles.

If you want, take a look into my code, maybe you’ll get some more ideas:


struct ViewColor 
(
	ArrTexColors = #((Color 128 0 255),(Color 0 0 255),(Color 0 128 255),(Color 0 255 255),(Color 0 255 0),(Color 255 255 0),(Color 255 128 0),(Color 255 0 128),(Color 255 0 0), (Color 0 0 0)),
	
	fn calculateTexSizes texsize: 16384 =
	(
		ArrTemp = #()
		x = 32
		while x < texsize do
		(
			append ArrTemp (x*x)
			x  *= 2
		)
		print ArrTemp
		ArrTemp
	),

	ArrTexSize = calculateTexSizes(),

	fn changeWorld2ScreenPoint pnt =
	(
		thePos = pnt * viewport.getTM() -- multiplies the vertex with the ViewMatrix
		screenOrig = mapScreenToView [0,0] (thePos.z) [renderWidth, renderHeight]
		screenEnd = mapScreenToView [renderWidth,renderHeight] (thePos.z) [renderWidth, renderHeight]
		ScreenSize = screenOrig-screenEnd
		aspX = renderWidth/(abs ScreenSize.x)
		aspY = renderHeight/(abs ScreenSize.y)
		screenCoords = point3 (aspX*(thePos.x-screenOrig.x)) (-(aspY*(thePos.y-screenOrig.y))) 0
		screenCoords 
	),

	fn calculateTriangle pnt1 pnt2 pnt3 =
	(
		v1 = pnt1 - pnt2
		v2 = pnt1 - pnt3
		r = cross v1 v2
		r = (length r)/2.0
	),

	fn colorizeVerts polyobj = 
	(
		coordSysTM = Inverse(getViewTM())
		viewDir = -coordSysTM.row3
		-- get the view vector (inverse of the camera vector)
		Arr = #()
		
		if classof polyobj != Editable_Poly then
		(
			return false
		)
		
		ArrFaces = #{}
		
		SurfArea = 0
		
		for lvj = 1 to (polyOp.getNumFaces polyobj) do
		(
			if (dot viewDir (polyOp.getFaceNormal polyobj lvj) < 0 ) then 
			(
				VertList = (polyOp.getFaceVerts polyobj lvj)
				-- Convert the vertlist in 2D coords
				for lvi =1 to VertList.count do
				(
					VertList[lvi] = changeWorld2ScreenPoint (polyOp.getVert polyobj VertList[lvi])
				)
				
				LocSurfArea  = calculateTriangle VertList[1] VertList[2] VertList[3]

				for lvi = 3 to VertList.count-1 do
				(
					LocSurfArea  += calculateTriangle VertList[lvi] VertList[1] VertList[lvi+1]
				)
				
				SurfArea += LocSurfArea
				append ArrFaces lvj
			)
		)
		
		if ((2*SurfArea) > 0 and (2*SurfArea) <= ArrTexSize[1]) then
		(
			polyobj.wirecolor = ArrTexColors[1]
			--polyop.SetFaceColor polyobj 0 ArrFaces ArrTexColors[1]
		)
		for lvi = 2 to ArrTexSize.count-1 do
		(
			if ((2*SurfArea) > ArrTexSize[lvi-1] and (2*SurfArea) <= ArrTexSize[lvi]) then -- Nyquist Shannon
			(
				polyobj.wirecolor = ArrTexColors[lvi]
				--polyop.SetFaceColor polyobj 0 ArrFaces ArrTexColors[lvi] -- lvi (0 = black)
				exit
			)
			else
			(
				continue
			)
		)			
		if ((2*SurfArea) > ArrTexSize[ArrTexSize.count]) then
		(
			polyobj.wirecolor = ArrTexColors[ArrTexColors.count]
			--polyop.SetFaceColor polyobj 0 ArrFaces ArrTexColors[ArrTexColors.count]
		)
		
		return true
	)
)

Col = ViewColor()
start = timestamp()

for obj in objects do 
(
	Col.colorizeVerts obj
)
end = timestamp()
print ((end-start)/1000.0)

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

when i was answering i thought about 200K (not 20K)
well… here is a sample:


fn getViewFaceArea node view: = if iskindof node GeometryClass do
(
	if viewport.numViews == 1 or view == unsupplied do view = viewport.activeViewport

	if view != viewport.activeViewport do viewport.activeViewport = view
	gw.setTransform (matrix3 1)
	transPoint = gw.hTransPoint

	mesh = snapshotasmesh node
	local tm = node.objecttransform
	vverts = for v=1 to mesh.numverts collect (transPoint (getvert mesh v))
	vareas = for f=1 to mesh.numfaces collect
	(
		vv = getface mesh f
		length (cross (vverts[vv[1]]-vverts[vv[2]]) (vverts[vv[3]]-vverts[vv[2]])) * 0.5  
	)
	free mesh
	vareas
)
/*
delete objects
sp = geosphere segments:100
t1 = timestamp()
data = getViewFaceArea sp
t2 = timestamp()
format "faces:% = % time:%c
" data.count sp.mesh.numfaces ((t2-t1)/1000.)
*/

the result on my machine: faces:200000 time:0.669c

 lo1

I’m incredibly curious to know why you’d need that accurate a result. Could you share the purpose?

Why not. I want to know which surface area of an object covers which part of a final image.
Best example would be the always posed question: Object X is seen that close on screen, what texture size will I need without running in undersampling problems. Well, you can always say: Ahh use 4k maps, but why use more memory when you can achieve the result with much lesser texture space.
The script would color code the object / the visible part of the object so you can tell which map you’d need.

2 Replies
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

I contend you need much less accuracy then you imagine for this purpose.

(@jonadb)
Joined: 11 months ago

Posts: 0

Just curious, how will you account for UV tilling?

Another approach: get a 1,2,3,4K texture map with a 1 pixel black/white grid. Apply to the entire scene. if you can see checkers you know you’ve got an undersampled area.

Page 1 / 2