Notifications
Clear all

[Closed] Pipes Generator

So I’ve search around on the inter-web and didn’t find to many great pipe generator tools and on top of that I’ve been unable to find any good ones fore free. I’m going to start a thread here on cgtalk where I will begin to build a develop and free and openly coded tool. Anyone is more than welcome to share their input and ideas as I’m always open to making things as good as we can. I’ve started a very rough base so far. Take a look.
The download zip is needed.

Right now each pipe is being randomly selected when being created next which is purely for the purpose of quick testing reasons. I will eventually give the control for users to pick which pipe is to be created next as well as having the option to just randomly grow pipes.


try(destroyDialog rlCreate)catch()
rollout rlCreate "Boxes"
(
	local cs = $SmallCornerCtrl
	local s = $SmallCtrl
	local m = $MediumCtrl
	local t = $LargeCtrl
	
	button btnCreate "Create"
	
	on btnCreate pressed do (
		if selection.count >= 1 then
		(
			ends = #()
			for o in selection where classof o == Point AND findString o.name "End" != undefined do 
			(
				pipes = #(cs,s,m,t)
				p = pipes[random 1 pipes.count]
				
				local newPipes
				if not (maxOps.cloneNodes p expandHierarchy:true newNodes:&newPipes) then throw "Not good!"
				pipeCtrl = newPipes[1]
				
				pipeCtrl.transform = copy o.transform
				
				for c in newPipes where classof c == Point AND findstring c.name "End" != undefined do append ends c
			)
			select ends
		)
		else
		(
			pipes = #(cs,s,m,t)
			p = pipes[random 1 pipes.count]
				
			ends = #()
			local newPipes
			if not (maxOps.cloneNodes p expandHierarchy:true newNodes:&newPipes) then throw "Not good!"
			pipeCtrl = newPipes[1]
			
			pipeCtrl.pos = [0,0,0]
			
			for c in newPipes where classof c == Point AND findstring c.name "End" != undefined do append ends c
			select ends
		)
	)
	
)
createDialog rlCreate

Thanks.

140 Replies

Rough but working!!

Added parenting so you can rotate pipes and effect the pipes which are growing from that particular one.


try(destroyDialog rlCreate)catch()
rollout rlCreate "Boxes"
(
	local cs = $SmallCornerCtrlStart
	local s = $SmallCtrlStart
	local m = $MediumCtrlStart
	local t = $LargeCtrlStart
	local cros = $CrossCtrlStart
	
	button btnCreate "Create"
	
	on btnCreate pressed do (
		if selection.count >= 1 then
		(
			ends = #()
			
			for o in selection where classof o == Point AND findString o.name "End" != undefined do 
			(
				pipes = #(cs,s,m,t,cros)
				p = pipes[random 1 pipes.count]
				
				maxOps.cloneNodes p expandHierarchy:true cloneType:#copy actualNodeList:&refPipes newNodes:&newPipes
				pipeCtrl = newPipes[1]
				
				pipeCtrl.transform = copy o.transform
				
				--link children
				for p in newPipes where classof p == circle AND findString p.name "Start" !=undefined do p.parent = (o.parent.parent)
				
				for c in newPipes where classof c == Point AND findstring c.name "End" != undefined do append ends c
			)
			select ends
		)
		else
		(
			pipes = #(cs,s,m,t)
			p = pipes[random 1 pipes.count]
				
			newPipes = #()
			maxOps.cloneNodes p expandHierarchy:true cloneType:#copy actualNodeList:&refPipes newNodes:&newPipes
			pipeCtrl = newPipes[1]
			
			pipeCtrl.pos = [0,0,0]
			
			ends = for c in newPipes where classof c == Point AND findstring c.name "End" != undefined collect c
			select ends
		)
	)
	
)
createDialog rlCreate

search this forum. I am pretty sure i remember a pipe generator maxscript project some time ago

Yeah I had come across it already but in my mind it was not as good as it could be. That is why I’m going to take the concept of pipe generation and make it more user friendly and much more stable.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

there is a little addition to your code:


     local rots = #(-90,0,90,180)
 	on btnCreate pressed do (
 		if selection.count > 0 then
 		(
 			ends = #()
 			
 			for o in selection where classof o == Point AND findString o.name "End" != undefined do 
 			(
 				pipes = #(cs,s,m,t,cros)
 				p = pipes[random 1 pipes.count]
 				
 				maxOps.cloneNodes p expandHierarchy:true cloneType:#copy actualNodeList:&refPipes newNodes:&newPipes
 				pipeCtrl = newPipes[1]
 				
 				pipeCtrl.transform = copy (prerotatez o.transform rots[random 1 rots.count])
 
 

Very cool.

I was just going to add the random rotation to.
I’m going to start building out a UI and from there figure out a good way to make this more useable and give more options. Such as a preview and also the option of having … auto generate pipes.

Controls?

Does anyone have any particular opinions on what type of controls they would like to have for this.

My ideas were:

  1. Ability to use custom pipes.
  2. Ability to Auto generate pipe system (grow)
  3. Auto generate next pipe (with collision detection)
  4. Option to generate specific pipes at any given time
  5. Rotation Controls

As far as anything else that is all I can think up.
It would be cool to somehow grow pipes around and object but that may be for another day haha.

Hey guys. Things are coming together nicely here. In the newest update you’ll see a UI now.
To try it out download the attached file and run the script. Once the script is up and running select all the objects in the scene and hit ‘Add’. Then clear your selection and hit Generate.

Workings:
Based on the items selected in your list that controls which pipe type will be created. If you have more than one item selected it will randomly pick one of the pipes to create.

Things I’d like to add.
A control which drives the rotation of the pipes to limit their rotation. So users can grow pipes just on the X but with slight rotation say -30 to +30. If users use 360 then it will create it all directions.

Secondly I need to create a script which selects the last children of the entire pipe tree so users can quickly select the ends and continue building. In short a script which selects only children whom don’t have children. But the initial selection before running the script could take place anywhere in the hierarchy.

Lastly I’d like to add some sort of preview option and raycast intersection detection system to help drive which pipe gets randomly generated. Which that would come an option where users could pick whether or not they care if it intersects.

I’d love to get some feedback and ideas from you guys. Thanks again for the help so far guys.


try(destroyDialog rlPipesGenerator)catch()
rollout rlPipesGenerator "Pipes Generator"
(
	local pipeList = #()
	local rots = #(-90,0,90,180)
	
	fn getChildren node = (
		local tmpArr = #()
		for c in node.children do
		(
			append tmpArr c
			join tmpArr (getChildren c)
		)
		tmpArr
	)

	fn checkStart node = (if classof node == Point AND findString node.name "Start" != undefined then true else false)
	
	fn checkEnd node = (
		children = getChildren node
		endNodes = for o in children where classof o == Point AND findString o.name "End" != undefined collect o
		if endNodes.count >= 1 then true else false	
	)
	
	fn getEnds arr = (
		local tmpArr = #()
		tmpArr = if arr.count >= 1 then (for o in arr where classof o == Point AND findString o.name "End" != undefined collect o) else #()
		tmpArr
	)		
	
	fn fnAddItmsLst lst arr = ( -- add Objects to list if they meet requirements
		curSel = getCurrentSelection() as array
		tmpArr = for p in curSel where (checkStart p) AND (checkEnd p) collect p
		
		if tmpArr.count >= 1 do
		(
			for o in tmpArr do (appendIfUnique arr o)
			lst.items = for i in arr collect i.name -- update list with array
		)
	)
		
	fn fnRemoveItmsLst lst arr = ( -- remove objects from list
		local currSel = lst.selection
		for i = lst.items.count to 1 by -1 where currSel[i] do (deleteItem arr i)
		lst.items = for i in arr collect i.name -- update list with array
		lst.selection = #{}
	)
	
	label lbPipes "Available Pipes:" pos:[10,10]	
	multilistbox lstPipes "" items:#() width:140 height:10 pos:[10,30]
	button btnInfo "Info ?" height:20 width:60 pos:[150,30]
	button btnAdd "Add" height:58 width:60 pos:[150,50]
	button btnRemove "Remove" height:58 width:60 pos:[150,108]
	button btnGeneratePipes "Generate Pipes" width:200 height:30
	
	on btnInfo pressed do (
		infoMessage = stringStream ""
		format "%
" "Items which are added to the list must meet the following requirements:" to:infoMessage
		format "
%" "1. The object being added must be a Point Helper." to:infoMessage
		format "
%" "2. The object's name must contain the word 'Start'." to:infoMessage
		format "
%" "3. The object added must contain atleast one children 'Point Helper' object." to:infoMessage
		format "
%" "4. The child object's names must contain the word 'End'." to:infoMessage
		format "

%" "NOTE: There can be more than one child point helper object." to:infoMessage
		messagebox infoMessage
	)
	
	on btnAdd pressed do (fnAddItmsLst lstPipes pipeList)
	on btnRemove pressed do (fnRemoveItmsLst lstPipes pipeList)
	
	on btnGeneratePipes pressed do (
		
		if selection.count >= 1 then
		(
			prevEnds = getEnds (selection as array)
			newEnds = #()
			
			for o in prevEnds do (
				
				--Randomly choose a pipe based on list selection
				items = if (lstPipes.selection) != undefined then (lstPipes.selection) as array else 1
				idx = items[random 1 items.count]
				_pipe = pipeList[idx]
				
				maxOps.cloneNodes _pipe expandHierarchy:true cloneType:#copy actualNodeList:&refPipes newNodes:&newPipes
				pipeCtrl = newPipes[1]
				
				pipeCtrl.transform = copy (prerotatez o.transform rots[random 1 rots.count])
				
  				for p in newPipes where (checkStart p) do p.parent = o --link children
				
 				join newEnds (getEnds newPipes)
			)
			select newEnds
			
		)else(
			--Randomly choose a pipe based on list selection
			items = if (lstPipes.selection) != undefined then (lstPipes.selection) as array else 1
			idx = items[random 1 items.count]
			_pipe = pipeList[idx]
				
			maxOps.cloneNodes _pipe expandHierarchy:true cloneType:#copy actualNodeList:&refPipes newNodes:&newPipes
			pipeCtrl = newPipes[1]
			
			pipeCtrl.pos = [0,0,0]
			
			pipeCtrl.transform = (prerotatez  (matrix3 1) rots[random 1 rots.count])
			
			select (getEnds newPipes)
		)
	)
)
createDialog rlPipesGenerator 220 210 style:#(#style_SysMenu, #style_ToolWindow)

if i would build anything using ‘blocks’ i do it by using containers. that will give me automatic updates and enabling of proxy display.

Making each pipe into a container? Seems like that would make it a bit harder to track the start and ends of the pipes though.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

no difference. you can check selected container and use any object from its hierarchy to get a connection point.

collision detection… you really don’t need it. if you use ‘multiple of N’ block size you can setup the 3d grid, and mark it’s cells as taken every time when you generate a new piece. if cell on the way of growing is taken try to turn on free way or stop growing this branch (set some ‘ending’ piece for example).

Yeah I was thinking about adding an end cap piece to then help make sense of a pipe suddenly ending.

I’ll have to look into the container stuff as its new to me and I’ve never even looked at it before.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

i think that the main goal of the pipe generator for you is to learn anything new. that’s why i throw you containers…

Page 1 / 12