Detecting faces that are in exactly the same position as other faces (optimizing voxel geometry)

Detecting faces that are in exactly the same position as other faces (optimizing voxel geometry)

mikeZ7BJ8
Participant Participant
1,068 Views
8 Replies
Message 1 of 9

Detecting faces that are in exactly the same position as other faces (optimizing voxel geometry)

mikeZ7BJ8
Participant
Participant

I posted about this here and it was suggested I ask here, in the programming forum. 

 

I'm looking for a way to optimize voxel geometry. The result (as shown in the attached screenshot) is a series of cubes that are all touching. I want to delete ALL the "inside" faces, that are touching other cube faces. I hope that makes sense.

 

Then I should be able to weld all the remaining verts to form the "shell", which I can then optimize. If anyone has any suggestions, it would be appreciated! I'm pretty new to 3ds Max, and am not a programmer. Thanks!

-Mike

0 Likes
Accepted solutions (1)
1,069 Views
8 Replies
Replies (8)
Message 2 of 9

Swordslayer
Advisor
Advisor

Thought I could use the OverlappingFaces xView interface that's already in max but it provided wrong results so here's a simple snippet that uses coords of the center of a face as a key for a dictionary and selects those that have more than one face sharing the same key (bottleneck is the address generation so if you want to improve performance, better use something else than the naive string concatenation):

 

(
	local voxelSize = 0.24
	local obj = $voxel_building

	local round = (dotNetClass "System.Math").Round
	fn posToRoundedStr pos = int (round pos.x) as string + ":" + int (round pos.y) as string + ":" + int (round pos.z) as string

	local halfSize = voxelSize * .5
	local facesByAddr = Dictionary #string
	local faceSel = #{}
	for face = 1 to polyop.getNumFaces obj do
	(
		local addr = posToRoundedStr (polyop.getFaceCenter obj face / halfSize)
		if hasDictValue facesByAddr addr then append facesByAddr[addr] face
		else putDictValue facesByAddr addr #{face}
	)
	for item in facesByAddr where item.value.numberSet > 1 do join faceSel item.value
	polyop.setFaceSelection obj faceSel
)

 

0 Likes
Message 3 of 9

denisT.MaxDoctor
Advisor
Advisor

deleted

0 Likes
Message 4 of 9

mikeZ7BJ8
Participant
Participant

It worked! I went to accept your solution, denisT.MaxDoctor, and now it says it's been deleted!

 

I ran your script and then used the Optimize modifier and got a 48k poly model down to 6.7k.

0 Likes
Message 5 of 9

denisT.MaxDoctor
Advisor
Advisor
Accepted solution

@mikeZ7BJ8 wrote:

It worked! I went to accept your solution, denisT.MaxDoctor, and now it says it's been deleted!


Oh!
I didn't want to steal the better solution from Swordslayer. 

here is the UI'ed Swordslayer's solution:

 

try(destroydialog rol) catch ()
rollout rol "RO:16AB3B8B" width:191
(
 	checkbox delete_ch "Delete Poly Dups" offset:[0,4] 
	checkbox weld_ch "Weld Open Edges"
	
	button optimize_bt "Optimize" width:172 offset:[0,8]
	label info_lb "" enabled:off
	
	local _builtin = python.import #__builtin__
	local _round = _builtin.round
	fn round_pos pos ndigits:2 = [_round pos.x ndigits, _round pos.y ndigits, _round pos.z ndigits]

	on optimize_bt pressed do undo "Optimize" on
	(
		if iskindof (node = selection[1]) editable_poly do
		(
			local threshold = sqrt (polyop.getFaceArea node 1) / 2
			
			n0 = node.numfaces
			info_lb.text = [0, n0] as string
			
			local dict = Dictionary #string
			
			count = polyop.getnumfaces node
			dups = #{}
			
			for k=1 to count do
			(
				c = polyop.getfacecenter node k
				hash = (round_pos c) as string				
				if (i = dict[hash]) != undefined then 
				(
					append dups k
					append dups i
					--removedictvalue dict hash  -- ??? maybe not
				)
				else dict[hash] = k
			)
			free dict
			
			node.selectedfaces = dups
			
			if delete_ch.state do
			(
				polyop.deletefaces node #selection
			)
			if weld_ch.state do 
			(
				node.weldThreshold = threshold
				polyop.weldEdgesByThreshold node (polyop.getOpenEdges node) 
			)
			
			n1 = node.numfaces 
			info_lb.text = [dups.numberset, n0] as string
			
			
		)
	)
	
	on rol open do
	(
		info_lb.text = "select poly object"
	)
)
createdialog rol

 

 

0 Likes
Message 6 of 9

denisT.MaxDoctor
Advisor
Advisor

welding should be improved. It seems that a multi-stage welding is needed - first by material ID ...

0 Likes
Message 7 of 9

denisT.MaxDoctor
Advisor
Advisor

@mikeZ7BJ8 wrote:

I ran your script and then used the Optimize modifier and got a 48k poly model down to 6.7k.


denisTMaxDoctor_0-1689314999884.png

 

You could probably ditch a couple hundred more triangles, easy.

 

0 Likes
Message 8 of 9

denisT.MaxDoctor
Advisor
Advisor

denisTMaxDoctor_0-1689413274045.png

 

this is my best I could get with automatic optimization (keeping original material IDs)

0 Likes
Message 9 of 9

mikeZ7BJ8
Participant
Participant

Oh nice! Is this using swordslayer's code, that you adjusted here?

0 Likes