Notifications
Clear all

[Closed] Holey Tube

This a geo plugin I made for fun. Feel free to check it out. It’s free and I’d love to see what ways we could improve upon this and features we could possibly add. It was a fun one to make.

Still in its rough draft stages.

https://vimeo.com/69252424


--c/pi/2

plugin simpleObject HoleyTube 
name:"HoleyTube"
category:"Standard Primitives"
classID:#(0x512e8ff9, 0x57aefc78)
(

	parameters main rollout:rltParams
	(		
		holeRadius type:#worldUnits default:3 ui:uiHoleRadius
		holesPerSection type:#integer default:1 ui:uiHolesPerSection
		holeSegs type:#integer default:8
		
		XPadding type:#worldUnits default:6 ui:uiXPadding	
		YPadding type:#worldUnits default:6 ui:uiYPadding	
		XSegs type:#integer default:2 ui:uiXSegs
		--YSegs type:#integer default:0 ui:uiYSegs
		numHoles type:#integer default:8 ui:uiNumHoles
		numRings type:#integer default:2 ui:uiNumRings
		ringOffset type:#integer default:0 ui:uiRingOffset
	)
	
	rollout rltParams "Parameters"
	(
		group "Section Settings:"
		(
			spinner uiHoleRadius "Hole Radius:" range:[.001,1e9,0] type:#worldUnits
			spinner uiHolesPerSection "Holes/Section:" range:[1,1e9,1] type:#integer 
			spinner uiXPadding "X Padding:" range:[0,1e9,0] type:#worldunits offset:[0,6]
			spinner uiYPadding "Y Padding:" range:[0,1e9,0] type:#worldunits
			spinner uiXSegs "X Segments:" range:[0,1e9,0] type:#integer 
			--spinner uiYSegs "Y Segments:" range:[0,1e9,0] type:#integer
		)
		group "Tube Settings:"
		(
			spinner uiNumHoles "Holes/Ring:" range:[1,1e9,1] type:#integer 
			spinner uiNumRings "N° of Rings:" range:[1,1e9,1] type:#integer offset:[0,6]
			spinner uiRingOffset "Ring Offset:" range:[0,1e9,0] type:#integer
		)
		
		fn checkRadius = 
		(
			if uiHoleRadius.value > uiXPadding.value do uiXPadding.value = uiHoleRadius.value
			if uiHoleRadius.value > uiYPadding.value do uiYPadding.value = uiHoleRadius.value
		)
		fn checkPadding = 
		(
			if uiXPadding.value < uiHoleRadius.value do uiHoleRadius.value = uiXPadding.value
			if uiYPadding.value < uiHoleRadius.value do uiHoleRadius.value = uiYPadding.value	
		)
		
		on uiHoleRadius changed val do checkRadius()
		on uiYPadding changed val do checkPadding()
		on uiXPadding changed val do checkPadding()
	)
	
	fn FlipNodeNormals obj =
	(
		for f =1 to obj.numFaces do --for each face in the mesh
		(
			verts=getface obj f -- get its 3 vertices as a point3
			local tmp=verts.x -- swap the first and third vertices
			verts.x=verts.z -- which flips the normal
			verts.z=tmp -- (right-hand rule), and 
			setface obj f verts -- store the new vertex order for the face
		)
		update obj -- update object so 3ds Max sees the changes
	)
	
 	fn BendPoint p angle:0.0 height:0.0 direction:0.0 =
	(
		if angle != 0.0 then
		(
  			factor = angle/height
  			radius = (180.0/pi/factor)
			
			dirMatrix = rotateXMatrix -direction
			value = [0,0,-radius]*dirMatrix 
			rotateMatrix = rotateYMatrix
			
			tm = translate ((rotateMatrix (factor*p)[1]) * dirMatrix) value
			p[1] = 0
			p = (p - value)*(inverse dirMatrix)*tm
			
			return p
		)else(
			return p
		)
	) 
	
	fn transformVerts mesh offset:0.0 angle:0.0 =
	(
		for v = 1 to mesh.numVerts do
		(
			vPos = meshop.getVert mesh v
			tm = transmatrix (vPos + offset)
			vPos = (rotateY tm angle).pos
			meshop.setVert mesh #{v} vPos
		)
		mesh
	)
	
	fn genHoles =
	(
		local tmpVerts = #()
		local holeAngle = 360.0 / holeSegs
		
		for h = 0 to holesPerSection-1 do
		(
			for c = 1 to holeSegs do
			(									
				local x = holeRadius * cos((c-1) * -holeAngle)
				local y = holeRadius * sin((c-1) * -holeAngle)
				tm = rotateZ (transmatrix [x,y,0]) -90 --used to offset so frist vert is in the corner
				
				vPos = (tm * (matrix3 1)).pos
				offset = h * (XPadding * (2)) + XPadding
				vPos+= [offset,YPadding,0]--offset to make it start at [0,0,0]
					
				append tmpVerts vPos
			)
					
			/* Create Frame Points */
			--if its the 1st hole then run this for the frame creation
			if h == 0 then
			(
				for c = 1 to holeSegs do
				(	
					vPos = case c of
					(
						1: [0,-YPadding,0]
						2: [-XPadding,-YPadding,0]
						3: [-XPadding,0,0]
						4: [-XPadding,YPadding,0]
						5: [0,YPadding,0]
						6: [XPadding,YPadding,0]
						7: [XPadding,0,0]
						8: [XPadding,-YPadding,0]
					)
					vPos+= [XPadding,YPadding,0] --offset to make it start at [0,0,0]
					append tmpVerts vPos
				)
			)else(
				for c = 1 to holeSegs do
				(	
					offset = h * (XPadding * (2)) + XPadding
					vPos = case c of
					(
						1: [0,-YPadding,0]
						2: [-XPadding,-YPadding,0]
						3: [-XPadding,0,0]
						4: [-XPadding,YPadding,0]
						5: [0,YPadding,0]
						6: [XPadding,YPadding,0]
						7: [XPadding,0,0]
						8: [XPadding,-YPadding,0]
					)
					vPos+= [offset,YPadding,0] --offset to make it start at [0,0,0]
					if c != 2 AND c != 3 AND c != 4 do
					(
						append tmpVerts vPos
					)
				)
				
			)
		)
		
		/* Create Side Seg Points */
		for i = 1 to XSegs do
		(
			local xStartPos = (holesPerSection*2)*XPadding
 			local xOffset = xStartPos + (XPadding*i)
			
			for s = 0 to 2 do --3 verts for each seg
			(
				offset = YPadding*s
				vPos = [xOffset,(-offset+(YPadding*2)),0]
				append tmpVerts vPos
			)
		)
		
		/* Offset for radius */
		partLength = (XPadding * XSegs) + (holesPerSection * 2 * XPadding)
		circum = ((XPadding * XSegs) + (holesPerSection * 2 * XPadding))*numHoles
		radius = circum/pi/2
		ang = 360.0/numHoles
		
		tmpVerts = for v in tmpVerts collect (
			v = BendPoint v angle:ang height:partLength
			v += [0,0,radius] --offset for radius
			v
		)
		
		return tmpVerts		
	)
	
	fn genMesh = 
	(	
 		/*Set Vertices*/
 		local verticesArr = #()
		
		/* Create Hole Points */	
		join verticesArr (genHoles())
			
		/* Set Faces */
		local facesArr = #()
			
		/*Set Hole Faces for linear holes*/
		local loops = 8 --number of loops to make section (usually half of the ver count since each poly is made of 2 tris

		for s = 0 to holesPerSection-1 do
		(
			start = if s > 0 then (16+((s-1)*13)+1) else 1 --starting vert 
			end = if s > 0 then (start +7) else s*(loops*2)+loops --end vert 
			
			check = 1
			for v = start to end do --each disc
			(
				if v < end then --less then
				(
					if s != 0 then
					(
						case check of
						(
							1: (append facesArr [v+1, v, v+loops]; append facesArr [v+loops ,  v+loops+1-10 , v+1 ])
							2: (append facesArr[v+1, v, v+loops-10]; append facesArr [v+loops-10 ,  v+loops+1-12 , v+1 ])
							3: (append facesArr[v+1, v, v+loops-12]; append facesArr [v+loops-12 ,  v+loops+1-14 , v+1 ])
							4: (append facesArr[v+1, v, v+loops-14]; append facesArr [v+loops-14 ,  v+loops+1-3 , v+1 ])
							5: (append facesArr[v+1, v, v+loops-3]; append facesArr [v+loops-3 ,  v+loops+1-3 , v+1 ])
							6: (append facesArr[v+1, v, v+loops-3]; append facesArr [v+loops-3 ,  v+loops+1-3 , v+1 ])
							7: (append facesArr[v+1, v, v+loops-3]; append facesArr [v+loops-3 ,  v+loops+1-3 , v+1 ])
						)
					)
					else
					(
						append facesArr [v+1, v, v+loops]
						append facesArr [v+loops ,  v+loops+1 , v+1 ]
					)
				)else(
					--makes a complete loop like a disc
					if s != 0 then
					(
						append facesArr [ 1+v-loops , v , v+loops-3]
						append facesArr [v+loops-3 , v+1, 1+v-loops ] 
					)else(
						append facesArr [ 1+v-loops , v , v+loops]
						append facesArr [v+loops , v+1, 1+v-loops ] 
					)
				)
				check += 1
			)
			format "
"
		)	

		/* X-PADDING FACES */
 		if XSegs != 0 do
 		(
			local sVert = 16 + (holesPerSection-1)*(16-3)-3+1 --first vert which will be used to create x-pad faces
			local eVert = sVert + (XSegs)*3-1 --total vert count including the verts used to create x-pad faces - minus last set
			local endCount = sVert+2 --segs of the framed circle
			local count = 3
			
 			for l = sVert to eVert do
			(
				if l < endCount then --less then
				(
					--format "% - % 
"  [l+1, l, l+count] [l+count ,  l+count+1 , l+1 ] 
					append facesArr [l+1, l, l+count]
					append facesArr [l+count ,  l+count+1 , l+1 ] 
				)else(
					--only needed if planning on connecting start/end edges like a tube
					endCount += count --reset strip of verts
				)
			)
 		)
		
		/*Set Mesh*/
 		mainMesh = setMesh mesh verts:verticesArr faces:facesArr
  		triCount = (holesPerSection*16)+(XSegs*2*2) -- total tri count
   		for f in #{1..triCount} do setEdgeVis mesh f 3 false
			
		
		/*Dupe Meshes to make complete ring*/
		local meshes = #()
		for i = 1 to (numHoles-1) do 
		(
			local newMesh = copy mesh
			local angleOffset = (360.0/numHoles)*i
			newMesh = transformVerts newMesh angle:angleOffset
			append meshes newMesh
		)
		/*Attach Duped Meshes*/
		for m in meshes do 	
		(
			meshop.attach mesh m
			free m
		)	
		free meshes
		
		/*Dupe Meshes to make complete ring*/
		local ringMeshes = #()
		for i = 1 to (numRings-1) do 
		(
			local newMesh = copy mesh
			local twistOffset = ( 360.0 / (numHoles * (holesPerSection*2 + XSegs )) ) * i
			local posOffset = (YPadding*2)*i
			newMesh = transformVerts newMesh offset:[0,posOffset,0] angle:(twistOffset*ringOffset)
			append ringMeshes newMesh
		)
		/*Attach Duped Meshes*/
		for m in ringMeshes do 	
		(
			meshop.attach mesh m
			free m
		)	
		free ringMeshes
		
		/*Weld all the clones*/
		meshop.weldVertsByThreshold mesh #{1..(mesh.numverts)} (units.decodeValue ".0001cm")
		
		/*Smooth Normals*/
		meshop.autoSmooth mesh #all 360.0
		
		 			
		/*Flip Normals*/
		FlipNodeNormals mesh
		
		update mesh
	)
	
	on buildMesh do (genMesh())
	
	tool create
	(
		on mousePoint click do case click of
		(
			1: (numHoles = 4; coordSys grid (nodeTM.translation = first_pos = gridPoint))
			2: #stop
		)
		
		on mouseMove click do case click of
		(
			2: (
					numHoles = amax (abs gridDist.x) (abs gridDist.y)
					if altKey do
					(
						nodeTM.translation = [0,0,0]
					)
				)
		)
	)
)
-- delete objects
-- clearlistener()
-- m = HoleyTube()
-- m.vertexticks = true
-- select m

-- for o in selection do
-- (
-- 	convertTo o editable_poly
-- 	for v = 1 to o.numverts do
-- 	(
-- 		vPos = polyop.getVert o v
-- 		t = text size:2.0 pos:vPos text:(v as string) wirecolor:yellow
-- 	)
-- )

1 Reply

Great stuff.

I get an error in 3dsmax 2014×64. My System Unit Setup is set to Meters.

Maxscript Scripted Plugin Handler Exception…
– Runtime error: Unable to decode value: “.0001cm”


		/*Weld all the clones*/
		meshop.weldVertsByThreshold mesh #{1..(mesh.numverts)} (units.decodeValue ".0001cm")