--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
--@@@@@@@@@@@@@@@_Morph Field v.1.0_@@@@@@@@@@@@@@@@
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
-- Written by dimitris gourdoukis.
-- object-e architecture 2010.
--------------------------------------------------------------------------------------------
-- http://object-e.net/
-- object.e.architecture@gmail.com
--------------------------------------------------------------------------------------------
-- released under the creative commons license "share alike unported 3.0"
-- http://creativecommons.org/licenses/by-sa/3.0/
--------------------------------------------------------------------------------------------
-- v1.0 Last Modified 10/08/10. Tested on 3DStudio Max 2010.
-- Use and modify at your own risk.
--------------------------------------------------------------------------------------------
-- Description:
--	This script generates a 3-dimentional field of custom objects and several
--  attractors inside the field. Each object has a custom number of morph targets
--	selected by the user loaded. The weight of each target every object is related to
--  the distance between object and attractor through a float script installed by
--  this script. The attractors have also a range property that affects the area
--  inside which they are active.
--------------------------------------------------------------------------------------------

global rltMorphField
try destroydialog rltMorphField catch()

--make sure there are no targets selected from any previous use
theTargets = undefined
rollout rltMorphField "Morph Field v1.0" width:259 height:490
(
	fn containerFilter baseObj = superClassOf baseObj == GeometryClass
	group "About:"
	(
		label ca_label01 "Morph Field v1.0" 
		label ca_label02 "dimitris gourdoukis, 2010"
		hyperlink hl_hp "object-e architecture" color:(color 0 0 255) address:"http://object-e.net" align:#center
	)
	
	group "Base Object:"
	(
		pickbutton theBase "Pick Base Object" width:140 height:25 filter: containerFilter tooltip:"select the base object"
	)
	
	group "Target Objects:"
	(
		button theTarget "Pick Target Objects" width:140 height:25 tooltip:"select the Target objects"
		label ca_label_inst_01 "--Select the targets" align:#left
		label ca_label_inst_02 "--Right click when done selecting" align:#left
		label ca_label_inst_03 "--Make sure you select each target once." align:#left
	)
	
	group "Field Sizes:"
	(
		spinner numXobj "# of objects along X:" range:[1,300,10] type:#integer fieldwidth:40 align:#right
		spinner numYobj "# of objects along Y:" range:[1,300,10] type:#integer fieldwidth:40 align:#right
		spinner numZobj "# of objects along Z:" range:[1,300,2] type:#integer fieldwidth:40 align:#right
		spinner stepAlX "X step:" range:[0,100,10] type:#float fieldwidth:40 align:#right
		spinner stepAlY "Y step:" range:[0,100,10] type:#float fieldwidth:40 align:#right
		spinner stepAlZ "Z step:" range:[0,100,10] type:#float fieldwidth:40 align:#right
	)

	group "Generate Objects:"
	(
		--generate the main button
		button start_process "BUILD FIELD!" width:140 height:30
	)
	
	on theBase picked baseObj do
	(
		--see if the user did not cancel the picking...
		if baseObj != undefined do
		(
			--if he did not, make the box's wireframe red:
			baseObj.wirecolor = red
			clearSelection()
			--and display the name of the object on the button:
			theBase.text = baseObj.name
		)
	)	-- end on theBase picked
	
	on theTarget pressed do
	(
		--see if a base object is selected
		if theBase.object == undefined then
		(
			messageBox "Select a base object first" beep:false
		)
		else
		(
			--note: the tag select: true that would highlight the selection of the targets in the viewport seems broken in max 2010 and 2011, while it seems to work on 2012. uncomment if you are using 2012.
			theTargets = pickObject count:#multiple message:"select target objects" prompt:"select target objects" filter:containerFilter --select: true
			--and display the number of selected objects on the button:
			theTarget.text = theTargets.count as string + " objects selected"
			--clearSelection()
		)
	)	-- end on theTarget pressed
	
	on start_process pressed do
	(
		if (theBase.object == undefined or theTargets == undefined) then
		(
			messageBox "Select a base object and the targets" beep:false
		)
		else
		(
			--start a progress indicator
			progressstart "building objects..."
			
			nXElem = numXobj.value --number of objects in the x ditections
			nYElem = numYobj.value --number of objects in the y ditections
			nZElem = numZobj.value --number of objects in the z ditections
			
			stepX = stepAlX.value --stepsize for the x direction
			stepY = stepAlY.value --stepsize for the y direction
			stepZ = stepAlZ.value --stepsize for the z direction
			
			nAttr = theTargets.count --number of attractor points
			
			bObj = theBase.object --select the base object
			----------------------------------------------------------------------
			--create the locators
			--add an attribute holder modifier
			--add a range custom attribute
			def = attributes range
			(
				parameters main rollout:params
				(
					range type:#float ui:Range default:100
				)
				rollout params "Range"
				(
					spinner Range "range" type:#float range:[0,1000,200]
				)
			)

			myAttractors = #()
			for i = 1 to nAttr do
			(
				--find the max corner of the bounting box that will contain all the objects (their pivot points)
				bbLimit = [nXElem*stepX,nYElem*stepY,nZElem*stepZ]
				pointPos = random [0,0,0] bbLimit
				myPoint = point()
				myPoint.name = uniqueName "Attractor_"
				myPoint.pos = pointPos
				myPoint.Box = true
				addModifier myPoint (EmptyModifier ())
				custAttributes.add myPoint.modifiers[#Attribute_Holder] def
				--add beziers to the parameters that will be used in the expression
				myPoint.modifiers[#Attribute_Holder].range.range.controller = Bezier_Float()
				myPoint.pos.controller = bezier_position ()
				append myAttractors myPoint
			) --end for i loop
			
			----------------------------------------------------------------------
			
			for x = 1 to nXElem do
			(
				--update the progress indicator
				progressupdate (x as float /nXElem *100)
				for y = 1 to nYElem do
				(
					for z = 1 to nZElem do
					(
						obj = copy bObj
						addModifier obj (Morpher())
						obj.pos = [x*stepX,y*stepY,z*stepZ]
						obj.wireColor = [128,128,128]
						b = 0
						obj.pos.controller = bezier_position ()
						for myTarg in theTargets do 
						(
							b = b + 1
							WM3_MC_BuildFromNode obj.morpher b myTarg
							ctrl = float_expression()
							--assign the float_expression controller to morpher value
							obj.modifiers[#Morpher][b].controller = ctrl
							--create the objPos variable in the expression controller
							ctrl.AddVectorTarget "objPos" obj.pos.controller
							theRange = "range" + b as string
							theVec = "Point" + b as string + "Pos"
							theExpr = "max((100-100*length(objPos - " + theVec + ")/" + theRange + "),0)"
							myAttr = myAttractors[b]
							ctrl.AddscalarTarget theRange myAttr.modifiers[#Attribute_Holder].range.range.controller
							ctrl.AddVectorTarget theVec myAttr.pos.controller
							ctrl.SetExpression theExpr
						)--*/
					)--end for z loop
				)--end for y loop
			)--end for x loop
			progressend ()
		)
	)--end on start_process do
)--end rollout

createDialog rltMorphField
