Notifications
Clear all

[Closed] bend modifier limit

How can add implement the ‘limits’ part of the modifier so it does the same thing that the basic Bend modifier does?

I’ve tried several ways to try and find a solution but I’m a bit confused on how to approach this properly. Hope someone can help.

Thanks.

The code below is from Bobo’s snippets here on cgtalk.


plugin simpleMod MyBend
name:"MyBend"
classID:#(0x2b215c83, 0x6d7189d3)
version:1
( 
	parameters main rollout:params
	(
		amount type:#float ui:spn_amount default:0
		direction type:#float ui:spn_direction default:0	
		axis type:#integer ui:rad_axis default:3
		LimitEnabled type:#boolean ui:uiLimitEnabled default:false
		LimitMax type:#worldUnits ui:uiLimitMax default:0
		LimitMin type:#worldUnits ui:uiLimitMin default:0
	)
 
	rollout params "MyBend Parameters"
	(
		spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1
		spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1
		radiobuttons rad_axis "Axis:" labels:#("X","Y","Z")
		group "Limits"
		(
			checkbox uiLimitEnabled "Enabled" align:#right
			spinner uiLimitMax "Max" range:[0,1e9,0] type:#worldUnits
			spinner uiLimitMin "Min" range:[-1e9,0,0] type:#worldUnits
		)
	)
 
	on map i p do
	(
		if amount != 0 then
		(
			case axis of
			(
				1: (
					theDirMatrix = rotateXMatrix -direction
					theRadius = 180/amount*extent.x/Pi
					theRadiusOffset = [0,0,-theRadius] * theDirMatrix 				
					TM = rotateYMatrix (amount*p.x/extent.x) * theDirMatrix
					TM.row4 = theRadiusOffset
					(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 			
				)
				2: (
					theDirMatrix = rotateYMatrix -direction
					theRadius = 180/amount*extent.y/Pi
					theRadiusOffset = [theRadius,0,0] * theDirMatrix 				
					TM = rotateZMatrix (-amount*p.y/extent.y) * theDirMatrix
					TM.row4 = theRadiusOffset
					(([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 
				)
				default: (
					theDirMatrix = rotateZMatrix -direction
					theRadius = 180/amount*extent.z/Pi
					theRadiusOffset = [theRadius,0,0] * theDirMatrix 				
					TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix
					TM.row4 = theRadiusOffset
					(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM 
				)	
			)	
		)
		else p
	)
)

6 Replies

I have yet to find an actual example of this working online. I don’t even know if this is possible via mxs.

1 Reply
(@bobo)
Joined: 10 months ago

Posts: 0

This is one of those funny cases… I have never seen the SDK code. A few years ago I wanted to recreate the Bend in Genome, went searching online and found the example you posted. Imagine the surprise when I looked at the author!
Turned out I had already solved it once, and completely forgotten about it.

I never looked into the limits until today, here is what the C++ plugin might be doing:

plugin simpleMod MyBend
name:"MyBend"
classID:#(0x2b215c83, 0x6d7189d3)
version:1
( 
	parameters main rollout:params
	(
		amount type:#float ui:spn_amount default:0
		direction type:#float ui:spn_direction default:0	
		axis type:#integer ui:rad_axis default:3
		LimitEnabled type:#boolean ui:uiLimitEnabled default:false
		LimitMax type:#worldUnits ui:uiLimitMax default:0
		LimitMin type:#worldUnits ui:uiLimitMin default:0
	)
 
	rollout params "MyBend Parameters"
	(
		group "Bend:"
		(
			spinner spn_amount "Angle: " range:[-100000,100000,0] scale:1
			spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1
		)
		group "Bend Axis:"
		(
			radiobuttons rad_axis labels:#("X","Y","Z") offsets:#([-10,0],[0,0],[10,0])
		)
		group "Limits"
		(
			checkbox uiLimitEnabled "Limit Effect" align:#right
			spinner uiLimitMax "Upper Limit" range:[0,1e9,0] type:#worldUnits
			spinner uiLimitMin "Lower Limit" range:[-1e9,0,0] type:#worldUnits
		)
	)
 
	on map i p do
	(
		if amount != 0 then
		(
			case axis of
			(
				1: (
					theDirMatrix = rotateXMatrix -direction
					if LimitEnabled then 
					(
						theRadius = 180/amount*(LimitMax-LimitMin)/Pi
						theRadiusOffset = [0,0,-theRadius] * theDirMatrix 				
						if p.x > LimitMax then
						(
							TM = rotateYMatrix (amount*(LimitMax)/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							newPoint = ((([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
							TM.row4 = [0,0,0]			
							newPoint += [p.x-LimitMax,0,0] * TM							
						)
						else if p.x < LimitMin then
						(
							TM = rotateYMatrix (amount*(LimitMin)/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							newPoint = ((([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
							TM.row4 = [0,0,0]			
							newPoint += [p.x-LimitMin,0,0] * TM	
						)
						else
						(
							TM = rotateYMatrix (amount*p.x/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 
						)
					)
					else
					(
						theRadius = 180/amount*extent.x/Pi
						theRadiusOffset = [0,0,-theRadius] * theDirMatrix 				
						TM = rotateYMatrix (amount*p.x/extent.x) * theDirMatrix
						TM.row4 = theRadiusOffset
						(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 			
					)
				)
				
				2: (
						theDirMatrix = rotateYMatrix -direction
						if LimitEnabled then 
						(
							theRadius = 180/amount*(LimitMax-LimitMin)/Pi
							theRadiusOffset = [theRadius,0,0] * theDirMatrix 				
							if p.y > LimitMax then
							(
								TM = rotateZMatrix (-amount*(LimitMax)/(LimitMax-LimitMin)) * theDirMatrix
								TM.row4 = theRadiusOffset
								newPoint = ((([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
								TM.row4 = [0,0,0]			
								newPoint += [0,p.y-LimitMax,0] * TM							
							)
							else if p.y < LimitMin then
							(
								TM = rotateZMatrix (-amount*(LimitMin)/(LimitMax-LimitMin)) * theDirMatrix
								TM.row4 = theRadiusOffset
								newPoint = ((([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
								TM.row4 = [0,0,0]			
								newPoint += [0,p.y-LimitMin,0] * TM	
							)
							else
							(
								TM = rotateZMatrix (-amount*p.y/(LimitMax-LimitMin)) * theDirMatrix
								TM.row4 = theRadiusOffset
								(([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 
							)
						)
						else
						(
							theRadius = 180/amount*extent.y/Pi
							theRadiusOffset = [theRadius,0,0] * theDirMatrix 				
							TM = rotateZMatrix (-amount*p.y/extent.y) * theDirMatrix
							TM.row4 = theRadiusOffset
							(([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 
						)
					)
				
				default: (
					theDirMatrix = rotateZMatrix -direction
					if LimitEnabled then 
					(
						theRadius = 180/amount*(LimitMax-LimitMin)/Pi
						theRadiusOffset = [theRadius,0,0] * theDirMatrix 
						if p.z > LimitMax then
						(
							TM = rotateYMatrix (amount*LimitMax/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							newPoint = ((([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM)
							TM.row4 = [0,0,0]
							newPoint += [0,0,p.z-LimitMax] * TM
						)
						else if p.z < LimitMin then
						(
							TM = rotateYMatrix (amount*LimitMin/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							newPoint = ((([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM)
							TM.row4 = [0,0,0]
							newPoint += [0,0,p.z-LimitMin] * TM							
						)
						else						
						(
							TM = rotateYMatrix (amount*p.z/(LimitMax-LimitMin)) * theDirMatrix
							TM.row4 = theRadiusOffset
							(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM 
						)
					)
					else
					(
						theRadius = 180/amount*extent.z/Pi
						theRadiusOffset = [theRadius,0,0] * theDirMatrix 				
						TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix
						TM.row4 = theRadiusOffset
						(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM 
					)
				)	
			)	
		)
		else p
	)
)

The basic idea is this:

Let’s say we are looking at the Z axis option.
For each vertex, we take the XY projection with Z zero (this puts the vertex at the ground plane in local space). We then rotate that vertex at an angle proportional to the Z position of the vertex within the bounding box. When Limit Effect is checked, we have to take the Min and Max values as the bounding box instead of the actual modifier context (bend gizmo). So the complete angle is applied within this custom range between the Lower and Upper limits, and the function is exactly the same. We just need to handle two other cases – when the Z is above the Upper limit, and when it is below the Lower limit.

In those cases, we apply the same rotation as any point that would be exactly at the Upper or Lower limit, and then shift the Z by the amount that is the difference between its actual Z position and the respective limit. However we transform the Z offset vector into the same bent space defined by the TM, but with zero translation. So instead of moving the vertex up in object space, we move it perpendicular to the rotated limit plane. As result, any vertices outside the bent region continue straight using the angle at the limit, and keep their correct offset from the limit plane regardless of the angle.

The code could certainly be optimized, it is a bit too verbose. It is also probably full with errors.

Also, when both the Upper and Lower limits are set to 0.0, the C++ plugin seems to apply a different rule as I see a deformation that my scripted version does not apply. I will investigate further, but I like my behavior better. I believe it is due to a possible division by zero when the two limits are zero…

Hope this helps.

I attempted to take your example and demonstrate the same thing in the Skew mod…it’s prove to be much more difficult than i expected. I still haven’t got a working version in the skew mod.

What am I doing wrong? Here is a basic SkewModifier working correctly but without limits.


delete objects
plugin simpleMod skewy
name:"skewy"
classID:#(0x6b15953d, 0xb0cf3d07)
version:1.1
( 
	parameters main rollout:main
	(
		skewAxis type:#integer ui:uiSkewAxis default:3
		skewAmount type:#worldUnits ui:uiSkewAmount default:0
		skewDirection type:#float ui:uiSkewDirection default:0
		LimitEnabled type:#boolean ui:uiLimitEnabled default:false
		LimitMax type:#worldUnits ui:uiLimitMax default:0
		LimitMin type:#worldUnits ui:uiLimitMin default:0
	)

	rollout main "MyBend Parameters"
	(
		group"Skew"
		(
			radiobuttons uiSkewAxis "" labels:#("X","Y","Z") columns:3 align:#center offset:[0,0]
			spinner uiSkewAmount "Amount: " type:#worldUnits range:[-1e9,1e9,0] scale:.01 offset:[0,4]
			spinner uiSkewDirection "Direction: " range:[-1e9,1e9,0] scale:1
		)
		group "Limits"
		(
			checkbox uiLimitEnabled "Enabled" align:#right
			spinner uiLimitMax "Max" range:[0,1e9,0] type:#worldUnits
			spinner uiLimitMin "Min" range:[-1e9,0,0] type:#worldUnits
		)
	)

	on map i p do
	(
 		skewVal = (skewAmount*p/extent)
		p += (case skewAxis of
			(
				1:	([0,0,skewVal[skewAxis]] * (rotateXMatrix -skewDirection))
				2:	([0,0,skewVal[skewAxis]]  * (rotateYMatrix -skewDirection))
				3:	([0,skewVal[skewAxis],0] * (rotateZMatrix -skewDirection))
			)
		)
	)
)

m = plane radius:10-- height:60 heightsegs:20 radius:5 sides:4 wirecolor:yellow
addmodifier m (Skewy skewAxis:1 skewAmount:10) 
select m

Here is my first attempt. It appears to be acting funky when the max value is to high. its moving on Y and not just the Z which it should be doing…?


delete objects
plugin simpleMod skewy
name:"skewy"
classID:#(0x6b15953d, 0xb0cf3d07)
version:1.1
( 
	parameters main rollout:main
	(
		Axis type:#integer ui:uiAxis default:3
		Amount type:#worldUnits ui:uiAmount default:0
		Direction type:#float ui:uiDirection default:0
		LimitEnabled type:#boolean ui:uiLimitEnabled default:false
		LimitMax type:#worldUnits ui:uiLimitMax default:0
		LimitMin type:#worldUnits ui:uiLimitMin default:0
	)

	rollout main "MyBend Parameters"
	(
		group"Skew"
		(
			radiobuttons uiAxis "" labels:#("X","Y","Z") columns:3 align:#center offset:[0,0]
			spinner uiAmount "Amount: " type:#worldUnits range:[-1e9,1e9,0] scale:.01 offset:[0,4]
			spinner uiDirection "Direction: " range:[-1e9,1e9,0] scale:1
		)
		group "Limits"
		(
			checkbox uiLimitEnabled "Enabled" align:#right
			spinner uiLimitMax "Max" range:[0,1e9,0] type:#worldUnits
			spinner uiLimitMin "Min" range:[-1e9,0,0] type:#worldUnits
		)
	)

	on map i p do
	(
		-- original working code without LIMITS
--  	skewVal = (Amount*p/extent)
-- 		p += (case Axis of
-- 			(
-- 				1:	([0,0,skewVal[Axis]] * (rotateXMatrix -Direction))
-- 				2:	([0,0,skewVal[Axis]]  * (rotateYMatrix -Direction))
-- 				3:	([0,skewVal[Axis],0] * (rotateZMatrix -Direction))
-- 			)
-- 		)

		case Axis of
		(
			1: (
				theDirMatrix = rotateXMatrix -direction
				if LimitEnabled then 
				(
					theRadius = (Amount*p.z)*(LimitMax-LimitMin)
					theRadiusOffset = [0,0,-theRadius] * theDirMatrix 				
					if p.x > LimitMax then
					(
						TM = rotateYMatrix (Amount*(LimitMax)/(LimitMax-LimitMin)) * theDirMatrix
						TM.row4 = theRadiusOffset
						newPoint = ((([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
						TM.row4 = [0,0,0]			
						newPoint += [p.x-LimitMax,0,0] * TM							
					)
					else if p.x < LimitMin then
					(
						TM = rotateYMatrix (Amount*(LimitMin)/(LimitMax-LimitMin)) * theDirMatrix
						TM.row4 = theRadiusOffset
						newPoint = ((([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM)
						TM.row4 = [0,0,0]			
						newPoint += [p.x-LimitMin,0,0] * TM	
					)
					else
					(
						TM = rotateYMatrix (Amount*p.x/(LimitMax-LimitMin)) * theDirMatrix
						TM.row4 = theRadiusOffset
						(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 
					)
				)
				else
				(
					val = (Amount * p / extent)
					p += [0,0,val[Axis]] * (rotateXMatrix -Direction)
-- 					theRadius = 180/Amount*extent.x/Pi
-- 					theRadiusOffset = [0,0,-theRadius] * theDirMatrix 				
-- 					TM = rotateYMatrix (Amount*p.x/extent.x) * theDirMatrix
-- 					TM.row4 = theRadiusOffset
-- 					(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM 			
				)
			)
		)
	)
)

m = plane radius:10-- height:60 heightsegs:20 radius:5 sides:4 wirecolor:yellow
addmodifier m (Skewy Axis:1 Amount:-100 LimitEnabled:true LimitMax:50) 
select m

Here it the simplest implementation I could come up with:

plugin simpleMod skewy
name:"skewy"
classID:#(0x6b15953d, 0xb0cf3d07)
version:1.1
( 
	parameters main rollout:main
	(
		skewAxis type:#integer ui:uiSkewAxis default:3
		skewAmount type:#worldUnits ui:uiSkewAmount default:0
		skewDirection type:#float ui:uiSkewDirection default:0
		LimitEnabled type:#boolean ui:uiLimitEnabled default:false
		LimitMax type:#worldUnits ui:uiLimitMax default:0
		LimitMin type:#worldUnits ui:uiLimitMin default:0
	)

	rollout main "MyBend Parameters"
	(
		group "Skew:"
		(
			spinner uiSkewAmount "Amount: " type:#worldUnits range:[-1e9,1e9,0] scale:.01 offset:[0,4]
			spinner uiSkewDirection "Direction: " range:[-1e9,1e9,0] scale:1
		)
		group "Skew Axis:"
		(
			radiobuttons uiSkewAxis "" labels:#("X","Y","Z") columns:3 align:#center offsets:#([-7,0],[0,0],[7,0])
		)
		group "Limits"
		(
			checkbox uiLimitEnabled "Limit Effect" align:#right offset:[9,0]
			spinner uiLimitMax "Upper Limit" range:[0,1e9,0] type:#worldUnits
			spinner uiLimitMin "Lower Limit" range:[-1e9,0,0] type:#worldUnits
		)
	)

	on map i p do
	(
		if LimitEnabled then
		(
			case of
			(
				(p[skewAxis] > LimitMax): skewVal = (skewAmount*LimitMax/(LimitMax-LimitMin))
				(p[skewAxis] < LimitMin):  skewVal = (skewAmount*LimitMin/(LimitMax-LimitMin))
				default: skewVal = (skewAmount*p[skewAxis]/(LimitMax-LimitMin))
			)
		)
		else
			skewVal = (skewAmount*p/extent)[skewAxis]
		case skewAxis of
		(
			1: (p + ([0,0,skewVal] * (rotateXMatrix (180-skewDirection))))
			2: (p + ([0,0,skewVal] * (rotateYMatrix (-skewDirection+90))))
			default: (p + ([0,skewVal,0] * (rotateZMatrix (-skewDirection-90))))
		)		
	)
)

moved to another thread …

deleted …