Notifications
Clear all

[Closed] Mini-Challenge #2

my method is incorrect anyway as you can’t trust the max interpolater to be linear when you need it to be. Though my solution on the surface looks correct, but the central ring is shaded as a totally convex surface, but in reality it’s two flat faces with shared normals.

to show what i mean, create a new sphere tesselate once, set the center rings to smoothing group 2 and the rest to smoothing group 1, and you’ll notice 2 lighting “aberrations” on the central ring. If you use any kind of non linear interpolation for creating the new “in between” normals you will in effect be smoothing the object. If you use a linear interpolation do you end up with a weird smoothed & faceted object ?

3 Replies
(@denist)
Joined: 11 months ago

Posts: 0

your method is absolutely correct. you just don’t need to detach faces. there is no reason for that.

performance is all about setting normal using Edit_Normals modifier and I will show how to do it fast.

(@klunk1)
Joined: 11 months ago

Posts: 0

no its not, it works for where there would be no difference between smoothed and tessellated faces. But in some areas max’s uv interpolation is incorrect for the situation.

(@denist)
Joined: 11 months ago

Posts: 0

you need to modify your algorithm a little to use GetFaceDegree and GetNormalID methods.

can you post an image of the lighting of the result of your script ?

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

i can post encrypted tessellater if you want and show the code later.

i would just like to see the result of the lighting on the central ring of your script. that’s all.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

well… is it good view?

go for it, sounds interesting

5 Replies
(@denist)
Joined: 11 months ago

Posts: 0

give some time to other people to find their own solution.

You can do yourself. I give you a hint.
Why settings normals using Edit_Normals modifier is slow? Is it a big deal to put some number in some piece of memory? What is really causing slow down?

(@matanh)
Joined: 11 months ago

Posts: 0

I guess you are talking about the modify panel. I tried to switch to the create panel but it causes the setnormal method to fail.
Can I have another hint please?

(@denist)
Joined: 11 months ago

Posts: 0

it’s not modifier panel. unfortunately we can’t do anything with it. Edit_Normal interface works when the modifier is current modifier panel object. It doesn’t work in any mode other than #modify.

well… another hint.
what Edit_Normals modifier actually does do? It modifies normals of mesh below. Every time when the modifier changes the mesh it calls to update the mesh. Every change! Do we need it?

(@matanh)
Joined: 11 months ago

Posts: 0

I guess you are talking about disabling the modifier.
When I do that I gain a couple of seconds, but I can only disable it for the setNormal part of the code.

here is my updated version:

(
	struct s_VertData
	(
		index,
		OriginalNormals = #()
	)
	
	struct s_EdgeData
	(
		owner,
		index,
		verts,
		midVert,
		
		fn divide =
		(
			midVert = owner.divideEdge index 0.5
		--	format "Edge % divided
" index
		),
		
		fn init =
		(
			verts = polyop.getEdgeVerts owner index
			not polyop.isEdgeDead owner index
		),
		
		valid = init()
	)
	
	struct s_NormalData
	(
		owner,
		faceIndex,
		Vert,
		index,
		dir,
		pos,
		
		fn init =
		(
			local faceNorms = #{}
			local vertNorms = #{}
			
			owner.ConvertFaceSelection #{faceIndex} faceNorms
			owner.ConvertVertexSelection #{Vert.index} vertNorms
			
			faceNorms = faceNorms as array
			vertNorms = vertNorms as array
			
			for i in faceNorms where findItem vertNorms i > 0 do exit with index = i
			
			if index != undefined then (
				dir = owner.getNormal index
				pos = owner.getVertex Vert.index
				true
			) else
				false
		),
		
		fn reconstruct  =
		(
			dir = [0,0,0]
			for N in Vert.OriginalNormals do
				dir += N.dir
			dir = normalize dir
			owner.setNormal index dir
		),
		
		valid = init()
	)
	
	struct s_FaceData
	(
		owner,
		index,
		center,
		MidVert,
		originalVerts = #(),
		OriginalVertsData = #(),
		originalEdges = #(),
		OriginalNormals = #(),
		EdgesData = #(),
		faceChildren = #{},
		midEdgeVerts = #(),
		MidEdgeVertsData = #(),
		
		fn getFaceNormals face:index =
		(
			local faceVerts = polyop.getFaceVerts owner face
			local OVerts = for i = 1 to originalVerts.count where findItem faceVerts originalVerts[i] > 0 collect OriginalVertsData[i]
			local EVerts = for i = 1 to midEdgeVerts.count where findItem faceVerts midEdgeVerts[i] > 0 collect MidEdgeVertsData[i]
			
			local Verts = #()
			join Verts OVerts
			join Verts EVerts
			if MidVert != undefined then
				append Verts MidVert
			
			local NormsData = #()
			for V in Verts do (
				local NewNormData = s_NormalData owner:owner.norms faceIndex:face Vert:V
				if NewNormData.valid then
					append NormsData NewNormData
			)
			
			NormsData
		),
		
		fn getNormalByVert v =
		(
			for N in OriginalNormals where N.Vert.index == v do
				exit with N
		),
		
		fn init =
		(
			if not polyop.isFaceDead owner index then (
				center = polyop.getFaceCenter owner index
				originalVerts = polyop.getFaceVerts owner index
				originalEdges = polyop.getFaceEdges owner index
				OriginalVertsData = for v in originalVerts collect s_VertData index:v
				OriginalNormals = getFaceNormals()
				for V in OriginalVertsData do
					V.OriginalNormals = #(getNormalByVert V.index)
				faceChildren[index] = true
				
			--	format "Face % initialized
" index
				true
			) else
				false
		),
		
		fn setEdgesData Edges =
		(
			EdgesData = for i in originalEdges collect Edges[i]
		),
		
		fn divide =
		(
			local bit1 = bit.set 0 1 true
			local numFacesBefore = polyop.getNumFaces owner
			
			for D in EdgesData do (
				local OrigNorms = for v in D.verts collect OriginalNormals[findItem originalVerts v]
				append midEdgeVerts D.midVert
				append MidEdgeVertsData (s_VertData index:D.midVert OriginalNormals:OrigNorms)
			)
			
			polyop.setVertSelection owner #{midEdgeVerts[1], midEdgeVerts[2]}
			owner.ConnectVertices vertexFlag:bit1
			
			local newEdge = polyop.getNumEdges owner
			MidVert = s_VertData index:(owner.divideEdge newEdge 0.5) OriginalNormals:OriginalNormals
			polyop.setVert owner MidVert.index center
			
			for i = 3 to MidEdgeVerts.count do (
				polyop.setVertSelection owner #{midEdgeVerts[i], MidVert.index}
				owner.ConnectVertices vertexFlag:bit1
			)
			
			local numFacesAfter = polyop.getNumFaces owner
			for i = numFacesBefore + 1 to numFacesAfter do
				faceChildren[i] = true
			
		--	format "Face % divided
" index
		),
		
		fn reconstructNormals =
		(
			for f in faceChildren do (
				local NewFaceNormals = getFaceNormals face:f
				
				for N in NewFaceNormals do
					N.reconstruct()
			)
			
		--	format "Face % normals reconstructed
" index
		),
		
		valid = init()
	)
	
	fn tessellateObject obj =
	(
		if canConvertTo obj Editable_Poly then (
			local ts = timeStamp()
			
			max modify mode
			obj = convertToPoly (copy obj)
			addModifier obj (edit_normals name:"norms")
			modPanel.setCurrentObject obj.norms
			obj.norms.SelLevel = #Object
			
			local EdgesData = #()
			local FacesData = #()
			
			/* Edge Data collection loop */
			for i = 1 to polyop.getNumEdges obj do (
				local NewEdgeData = s_EdgeData owner:obj index:i
				if NewEdgeData.valid then
					append EdgesData NewEdgeData
			)
			
			/* Face Data collection loop */
			for i = 1 to polyop.getNumFaces obj do (
				local NewFaceData = s_FaceData owner:obj index:i
				if NewFaceData.valid then (
					NewFaceData.setEdgesData EdgesData
					append FacesData NewFaceData
				)
			)
			
			/* Edges Subdivision loop */
			for D in EdgesData do
				D.divide()
			
			/* Faces Subdivision loop */
			for F in FacesData do
				F.divide()
			
			/* Normals loop */
			obj.norms.enabled = false
			for F in FacesData do
				F.reconstructNormals()
			obj.norms.enabled = true
			
			obj.norms.MakeExplicit()
			collapseStack obj
			
			format "% tessellation took %
" obj.name (timeStamp() - ts)
			gc()
			
			obj
		) else
			undefined
	)
	
	local newObjs = #()
	
	for o in selection do (
		local newObj = tessellateObject o
		if newObj != undefined then
			append newObjs newObj
	)
	
	select newObjs
	redrawViews()
)
(@denist)
Joined: 11 months ago

Posts: 0

it’s exactly the same place where I put two extra lines of code. But my code is different. Your guess is right, but there is another way to get the result.

since you tessellate the mesh yourself and you exactly know the normal of created vertex you can smartly move the vertex to do tessellation and mesh smoothing at same time (tessellation with tension).

I don’t have a lot of time right now, so I’ll just post my unbuild approach

I’ll use the ‘simpleFaceManager’ to add extra point3 data channels to the faces and store all the face’s vertex normals in there.

“The Per-Face data will survive topology changes and class conversions. For example, if you would convert the plane from the above example to Editable Mesh, the channels will be preserved and each of the four faces in the new mesh will inherit the data from the respective polygons in the Editable Poly. When changing topology by tessellating existing faces, the new faces will inherit the values of the original face. “

So after backing up all the data I’ll just add a tessellate modifier. All the new faces will have the data form their ‘parent’-face. Then it’s just finding a clever way of setting the new normals to the old data.

So the ‘simpleFaceManager’ is the main trick to my approach

TzMtN,
polyop.setVertSelection
is the bottleneck of your Face-divide algorithm. Any updating selection of editable poly structure is slow. (ask lo. lo can double check it for you with lo’s new tool ;)) use polyop.setvertflags to connect vertices with custom flag. it will make your divide algorithm ~4 times faster.

I have modified the divide function as you suggested but gain only ~1 sec in general run time on my machine (which is now aprox 23 sec)
am I doing it as you intended?

(
	struct s_VertData
	(
		index,
		OriginalNormals = #()
	)
	
	struct s_EdgeData
	(
		owner,
		index,
		verts,
		midVert,
		
		fn divide =
		(
			midVert = owner.divideEdge index 0.5
		--	format "Edge % divided
" index
		),
		
		fn init =
		(
			verts = polyop.getEdgeVerts owner index
			not polyop.isEdgeDead owner index
		),
		
		valid = init()
	)
	
	struct s_NormalData
	(
		owner,
		faceIndex,
		Vert,
		index,
		dir,
		pos,
		
		fn init =
		(
			local faceNorms = #{}
			local vertNorms = #{}
			
			owner.ConvertFaceSelection #{faceIndex} faceNorms
			owner.ConvertVertexSelection #{Vert.index} vertNorms
			
			faceNorms = faceNorms as array
			vertNorms = vertNorms as array
			
			for i in faceNorms where findItem vertNorms i > 0 do exit with index = i
			
			if index != undefined then (
				dir = owner.getNormal index
			--	pos = owner.getVertex Vert.index
				true
			) else
				false
		),
		
		fn reconstruct  =
		(
			dir = [0,0,0]
			for N in Vert.OriginalNormals do
				dir += N.dir
			dir = normalize dir
			owner.setNormal index dir
		),
		
		valid = init()
	)
	
	struct s_FaceData
	(
		owner,
		index,
		center,
		MidVert,
		originalVerts = #(),
		OriginalVertsData = #(),
		originalEdges = #(),
		OriginalNormals = #(),
		EdgesData = #(),
		faceChildren = #{},
		midEdgeVerts = #(),
		MidEdgeVertsData = #(),
		
		fn getFaceNormals face:index =
		(
			local faceVerts = polyop.getFaceVerts owner face
			local OVerts = for i = 1 to originalVerts.count where findItem faceVerts originalVerts[i] > 0 collect OriginalVertsData[i]
			local EVerts = for i = 1 to midEdgeVerts.count where findItem faceVerts midEdgeVerts[i] > 0 collect MidEdgeVertsData[i]
			
			local Verts = #()
			join Verts OVerts
			join Verts EVerts
			if MidVert != undefined then
				append Verts MidVert
			
			local NormsData = #()
			for V in Verts do (
				local NewNormData = s_NormalData owner:owner.norms faceIndex:face Vert:V
				if NewNormData.valid then
					append NormsData NewNormData
			)
			
			NormsData
		),
		
		fn getNormalByVert v =
		(
			for N in OriginalNormals where N.Vert.index == v do
				exit with N
		),
		
		fn init =
		(
			if not polyop.isFaceDead owner index then (
				center = polyop.getFaceCenter owner index
				originalVerts = polyop.getFaceVerts owner index
				originalEdges = polyop.getFaceEdges owner index
				OriginalVertsData = for v in originalVerts collect s_VertData index:v
				OriginalNormals = getFaceNormals()
				faceChildren[index] = true
				
				for V in OriginalVertsData do
					V.OriginalNormals = #(getNormalByVert V.index)
				
			--	format "Face % initialized
" index
				true
			) else
				false
		),
		
		fn setEdgesData Edges =
		(
			EdgesData = for i in originalEdges collect Edges[i]
		),
		
		fn divide =
		(
		--	local flag = bit.set 0 1 true
			local flag = bit.set 0 30 true
			local numFacesBefore = polyop.getNumFaces owner
			
			for D in EdgesData do (
				local OrigNorms = for v in D.verts collect OriginalNormals[findItem originalVerts v]
				append midEdgeVerts D.midVert
				append MidEdgeVertsData (s_VertData index:D.midVert OriginalNormals:OrigNorms)
			)
			
		--	polyop.setVertSelection owner #{midEdgeVerts[1], midEdgeVerts[2]}
			polyop.setVertFlags owner #{midEdgeVerts[1], midEdgeVerts[2]} flag mask:flag
			owner.ConnectVertices vertexFlag:flag
			polyop.setVertFlags owner #{midEdgeVerts[1], midEdgeVerts[2]} 0 mask:flag
			
			local newEdge = polyop.getNumEdges owner
			MidVert = s_VertData index:(owner.divideEdge newEdge 0.5) OriginalNormals:OriginalNormals
			polyop.setVert owner MidVert.index center
			
			for i = 3 to MidEdgeVerts.count do (
			--	polyop.setVertSelection owner #{midEdgeVerts[i], MidVert.index}
				polyop.setVertFlags owner #{midEdgeVerts[i], MidVert.index} flag mask:flag
				owner.ConnectVertices vertexFlag:flag
				polyop.setVertFlags owner #{midEdgeVerts[i], MidVert.index} 0 mask:flag
			)
			
			local numFacesAfter = polyop.getNumFaces owner
			for i = numFacesBefore + 1 to numFacesAfter do
				faceChildren[i] = true
			
		--	format "Face % divided
" index
		),
		
		fn reconstructNormals =
		(
			for f in faceChildren do (
				local NewFaceNormals = getFaceNormals face:f
				
				for N in NewFaceNormals do
					N.reconstruct()
			)
			
		--	format "Face % normals reconstructed
" index
		),
		
		valid = init()
	)
	
	fn tessellateObject obj =
	(
		if canConvertTo obj Editable_Poly then (
			local ts = timeStamp()
			
			max modify mode
			obj = convertToPoly (copy obj)
			addModifier obj (edit_normals name:"norms")
			modPanel.setCurrentObject obj.norms
			obj.norms.SelLevel = #Object
			
			local EdgesData = #()
			local FacesData = #()
			
			/* Edge Data collection loop */
			for i = 1 to polyop.getNumEdges obj do (
				local NewEdgeData = s_EdgeData owner:obj index:i
				if NewEdgeData.valid then
					append EdgesData NewEdgeData
			)
			
			/* Face Data collection loop */
			for i = 1 to polyop.getNumFaces obj do (
				local NewFaceData = s_FaceData owner:obj index:i
				if NewFaceData.valid then (
					NewFaceData.setEdgesData EdgesData
					append FacesData NewFaceData
				)
			)
			
			/* Edges Subdivision loop */
			for D in EdgesData do
				D.divide()
			
			/* Faces Subdivision loop */
			for F in FacesData do
				F.divide()
			
			/* Normals loop */
			obj.norms.enabled = false
			for F in FacesData do
				F.reconstructNormals()
			obj.norms.enabled = true
			
			obj.norms.MakeExplicit()
			collapseStack obj
			
			format "% tessellation took %
" obj.name (timeStamp() - ts)
			gc()
			
			obj
		) else
			undefined
	)
	
	local newObjs = #()
	
	for o in selection do (
		local newObj = tessellateObject o
		if newObj != undefined then
			append newObjs newObj
	)
	
	select newObjs
	redrawViews()
)

and what about your other hint? I can’t think of anything there, it must be something I am not familiar with or am I just stupid?

2 Replies
(@denist)
Joined: 11 months ago

Posts: 0

so… what do you have now? we decreased tessellation time from 32 to 23 sec. which is good!
here is my benchmarking:

Edge Data collection loop: 8
Face Data collection loop: 933
Edges Subdivision loop: 1275
[color=Red]Faces Subdivision loop: 14058
Normals loop: 4538
Tessellation: 21208[/color]

there two things to need improvement. start with faces… let me check what can we do.

(@denist)
Joined: 11 months ago

Posts: 0

i changed the divide function to:


 		fn divide =
 		(
 			local numFacesBefore = owner.numfaces
 			
 			for D in EdgesData do (
 				local OrigNorms = for v in D.verts collect OriginalNormals[findItem originalVerts v]
 				append midEdgeVerts D.midVert
 				append MidEdgeVertsData (s_VertData index:D.midVert OriginalNormals:OrigNorms)
 			)
 			
 			newEdge = polyop.createEdge owner midEdgeVerts[1] midEdgeVerts[2]
 			MidVert = s_VertData index:(polyop.divideEdge owner newEdge 0.5) OriginalNormals:OriginalNormals
 			polyop.setVert owner MidVert.index center
 			
 			for i = 3 to MidEdgeVerts.count do (
 				polyop.createEdge owner midEdgeVerts[i] MidVert.index
 			)
 			
 			local numFacesAfter = owner.numfaces
 			for i = numFacesBefore + 1 to numFacesAfter do
 				faceChildren[i] = true
 		),
 

it saves me ~5.5 sec
Faces Subdivision loop: 8680

the real bottleneck was polyop.getNumEdges

 lo1

I’m sure that storing all the polyop methods in a variable will save you a bit of time.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

i’ve tried. it saves but not too much… ~1s

it’s an interesting situation… there are almost 900 views for this topic but only 4 people are really taking part in it.

is it:
1) too complicated for beginners
2) too trivial for pros
3) not having any practical application
4) just wished to be got than discovered
5) other
?

why would people like to watch but not touch?

1 Reply
(@kameleon)
Joined: 11 months ago

Posts: 0

I’m not having to much time this week, but has I’ve said earlier this is a little bit out of my league, I dont do to many mesh operation scripts

Page 3 / 5