--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
--@@@@@@@@@@@@@@_Orientation 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 objects has a look_at constraint with every
--	attractor. The weight of each look_at constraint for every object is related to
--  the distance between object and attract 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 rltOrientationField
try destroydialog rltOrientationField catch()


rollout rltOrientationField "Orientation Field v1.0" width:259 height:490
(
	fn containerFilter baseObj = superClassOf baseObj == GeometryClass
	group "About:"
	(
		label ca_label01 "Orientation 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:"object to use for the array"
	)
	
	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 "Attractor Settings:"
	(
		spinner numAttr "# of attractors:" range:[1,100,4] type:#integer fieldwidth:40 align:#right
		--spinner dRange "Default Range:" range:[0,1000,100] type:#float fieldwidth:40 align:#right
	)
	
	group "Rotation Settings:"
	(
		spinner vcrLen "Vector Length:" range:[0,200,10] type:#float fieldwidth:40 align:#right 
		label rotationAxis "Select rotation Axis:" align:#right
		radiobuttons rdoBV "" labels:#("X", "Y", "Z") default:2 columns:3 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 start_process pressed do
	(
		if theBase.object == undefined then
		(
			messageBox "Select a base object" 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 = numAttr.value --number of attractor points
			
			bObj = theBase.object --select the base object
			vecLng = vcrLen.value --set the length og the vector
			--dr = dRange.value
			
			case rdoBV.state of
			(
				1:	trgAx = 0
				2:	trgAx = 1
				3:	trgAx = 2
			)	-- end case rdoBV.state
			----------------------------------------------------------------------
			--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
						obj.pos = [(x-1)*stepX,(y-1)*stepY,(z-1)*stepZ]
						obj.wireColor = [128,128,128]
						obj.pos.controller = bezier_position ()
						c = LookAt_Constraint()
						obj.rotation.controller = c
						c.upnode_ctrl = 0
						c.target_axis = trgAx
						c.upnode_axis = 1
						c.StoUP_axis = 1
						c.lookat_vector_length = vecLng
						b = 0
						for myAttr in myAttractors do 
						(
							b = b + 1
							c.appendTarget myAttr 50
							
							--assign a bezier float to the weight
							--c.weight[b].controller = Bezier_Float()
							ctrl = float_script()
							--assign the float_expression controller to the weight
							c.weight[b].controller = ctrl
							--create the objPos variable in the expression controller
							ctrl.AddObject "objPos" obj.pos.controller
							theRange = "range" + b as string
							theVec = "Point" + b as string + "Pos"
							theExpr = "amax ((1-length(objPos.value - " + theVec + ")/" + theRange + ")*100) 0"
							ctrl.AddTarget theRange myAttr.modifiers[#Attribute_Holder].range.range.controller
							ctrl.AddTarget 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 rltOrientationField
