-- ANIMATED VEGETATION GENERATOR BY MATTIAS "MAVCART" VAN CAMP
-- protected by creative copyright
-- personal use is permitted, do not resell this script or code without express written permission by the owner
-- if you want to use this script for a personal project or otherwise, please contact me at mattias.van.camp@howest.be and at least tell me. also credit me in your project's credits if you use it.
-- this script was written for educational purposes
-- written in maxcscript editor of Autodesk 3D Studio Max 2011 and 3D Studio Max 2012 educational license

MacroScript AnimatedVegetationGenerator
Category: "DAE"
tooltip: "Generates Animated Grass according to user input"
buttontext:"AVG"
(	
	UnitSetupString = "Warning: running the script will change the unit setup of this scene to Metric. Do you want to continue?"
	if (units.DisplayType != #Metric or units.MetricType != #Meters) then
	(
		if QueryBox UnitSetupString beep:true then
		(
			units.DisplayType = #Metric
			units.MetricType = #Meters
		)
	)
	
	floater = newrolloutfloater "Animated Grass Generator by MaVCArt" 580 707
	
	rollout AVGScriptbyMaVCArt "AVG Script by MaVCArt" width:560 height:680
	(
		button btnExecute "Grow that grass!" pos:[416,608] width:135 height:64 enabled:false toolTip:"Create the Grass Meshes"
		progressBar pbProgressStrands "" pos:[8,592] width:400 height:8 color:(color 255 249 69)
		GroupBox grpStrandSettings "Grass Strand Settings" pos:[8,8] width:280 height:136
		button btnToggleRealtime "Toggle Realtime Mode" pos:[416,480] width:135 height:64 toolTip:"Toggles Reatime Mode"
		GroupBox grpAnimationSettings "Animation Settings" pos:[8,152] width:280 height:120
		checkbox chkToggleLifeTime "Toggle LifeTime" pos:[16,176] width:120 height:16 checked:false
		dropdownList ddlStrandType "Strand Type" pos:[16,88] width:264 height:43 items:#("High and Wavy", "Dry and dying", "Golfing Grass (trimmed green)", "Standardized Field Grass") selection:0
		dropdownList ddlDetail "Strand Detail" pos:[16,32] width:264 height:43 items:#("High and procedural (very slow)", "Mid-range: high detail in model, performant texture", "Low: mid-range detail in model, highly performant texture", "Game resolution: useable in-game, higly performant") selection:0
		checkbox chkEternal "Strands don't die" pos:[16,200] width:120 height:16 checked:true
		checkbox chkFlowerMixture "Flower Mixture" pos:[16,224] width:112 height:16
		checkbox chkFootstep "Footstep Mode" pos:[16,248] width:112 height:16
		GroupBox grpGrowSetting "Grow Settings" pos:[8,280] width:544 height:184
		dropdownList ddlGrowSpeed "Grow Speed" pos:[16,304] width:528 height:43 items:#("Very Fast", "Fast", "Average Speed", "Slow") selection:0
		button btnDummyGrowMode "Enter Dummy Mode" pos:[16,408] width:120 height:48 enabled:false
		dropdownList ddlGrowMode "Grow Mode" pos:[16,360] width:528 height:43 items:#("Dummy", "Follow Spline Path", "Surface", "Use Spline as surface limitation") selection:0 toolTip:"select the vegetation grow mode here, going from high range but extremely slow realistic procedural grass to textured grass planes useable in-game."
		button btnSplinePathMode "Enter Spline Path Mode" pos:[152,408] width:120 height:48 enabled:false
		button btnPickSplineLimitationMode "Enter Spline Limit Mode" pos:[424,408] width:120 height:48 enabled:false
		button btnPickSurfaceMode "Enter Surface Mode" pos:[288,408] width:120 height:48 enabled:false
		edittext edtPickedGrowSource "Grow Point List" pos:[8,472] width:400 height:64
		checkbox chkAllowTimeLineChange "Allow TimeLine Editing" pos:[144,176] width:136 height:16 checked:true
		checkbox chkUseForce "Use User-Defined Force" pos:[144,200] width:136 height:16
		button btnPickForceMode "Pick Force" pos:[144,224] width:136 height:40 enabled:false
		spinner spnNrStrands "" pos:[136,544] width:272 height:16 range:[0,5e+008,0] type:#integer scale:1
		label lblNrStrands "Number Of Strands" pos:[8,544] width:100 height:16
		spinner spnMaximumAnimationRange "" pos:[136,568] width:272 height:16 range:[0,5e+008,0] type:#integer scale:1
		label lblAnimationOffset "Maximum animation end" pos:[8,568] width:120 height:16
		progressBar pbProgressTotal "" pos:[8,608] width:400 height:8 color:(color 255 139 69)
		dropdownList ddlPresets "Presets" pos:[8,624] width:400 height:43 items:#("--------------------------------------------------------------------------------------------", "Animation", "Performance", "Dummy Mode","Use Custom Geometry","Video Mode") selection:1
		button btnClearScene "Clean Scene" pos:[416,552] width:135 height:48 toolTip:"Toggles Reatime Mode"
		pickbutton btnPickGeometry "Pick Custom Geometry" pos:[304,104] width:240 height:32 enabled:false
		checkbox chkUseCustomGeometry "Use Custom Geometry" pos:[304,32] width:144 height:16
		GroupBox grpCustomGeometry "Custom Geometry" pos:[296,8] width:256 height:136
		edittext edtCustomGeometry "" pos:[303,56] width:240 height:40 enabled:false
		GroupBox grpVideoMode "Video Mode" pos:[296,152] width:256 height:120
		checkbox chkEnterVideoAnimation "use video file for animation" pos:[304,168] width:240 height:16
		editText edtVideoFile "" pos:[304,192] width:240 height:32 enabled: false
		button btnPickVideoFile "Pick Video File" pos:[304,232] width:240 height:32 enabled:false
		
		--selected storage
		local SelectedDummies = #()
		local SelectedSplinePaths = #()
		local SelectedSurfaces = #()
		local SelectedSplineLimits = #()
		local SelectedGrowSpeed
		local SelectedDetail
		local SelectedType
		local SelectedForces = #()
		local SelectedGrowMode
		
		-- working storage
		local PreviewStrand = #()
		local WorkingSelection = #()
		local WorkingPositions = #()
		
		--geometry storage
		local GrassObject
		local GrassStrands = #()
		local CustomGeo
		
		-- dropdownlist selection storage
		local StrandType
		local StrandDetail
		
		--booleans
		local LifeTime = false-- allow for the strands to grow -> gradual lifetime depending on the length of the current animation timeline
		local Eternal = true-- don't allow for the strands/plants to die of old age -> gradual lifetime with eventual death depending on the length of the current animation timeline
		local FlowerMixture = false-- allow for flowers to be put in the mix of strands -> increases realism/changes feel/increases feel of detail
		local FootstepMode = false -- enable footstep mode: creates grass with sources at the footsteps of animated object: for now, let's keep at biped. to do this, create splines shaped like feet where biped's feet touch the selected ground plane. also, create pick button to select ground plane to intersect with.
		local TimeLineEditing = true -- allows the script to edit the length of the timeline, changes FPS to 25. create queryBox to ask user if they are okay with this; warn them to remove any other animated objects if the FPS is not currently 25.
		local UseCustomForce = false -- allows the user to pick a user-defined space warp to influence the spring controllers on the grass
		local UseRandomGrowth = false -- enables the grass growth to grow randomly, enabling the user to grow grass not only from a central point, but randomly across the surface, too.
		local UseCustomGeometry = false -- enables the user to pick self-defined geometry instead of scripted geometry. this makes the script faster, but the geometry needs to be correct.
		local UseVideoFile = false -- enables the user to load a video file for animation (MUSIC VIDEO)
		local AnimateAlongPath = false -- animates scale along path instead of from each point out
		-- warning: check for editable poly, and check number of polygons in combination with nrstrands at execution. if the nr of polygons is higher than a certain number (say 10), give a warning message
		-- that this might slow down the user's system
		
		-- splineshapes
		local RadiusCircle
		local NameString
		local RadiusCircleRealtime = #()
		local WorkingRadius
		
		-- internal calculation storage
		-- maximumanimationoffset veranderbaar maken, door extra parameter te introduceren. doe dit bij de animation parameters.
		local maximumanimationoffset = 500
		local StrandSpacing = 0
		local StrandScaleValue
		local ForceStrength
		local ForceDirection
		local ForceTurbulence
		local NrStrands = 1
		local RealtimePosition = #(#(),#())
		local dhelper
		local SplineDummy=#(#(),#())
		
		-- video file storage
		local videofile
		
		--internal notices
		local HighDetailString = "Warning: generating highly detailed animated grass will significantly lower the performance of your system, and is likely to cause a program crash and/or freeze if you don't have the CPU required. Do you wish to continue?"
		
		on btnExecute pressed do
		(	
			setCommandPanelTaskMode #create
			if (WorkingPositions.count > 0) then
			(
				GrassMaterial = getMeditMaterial 1
				GrassMaterial.diffuse = green
				
				-- failsafe for workingradius and the sorts
				if WorkingRadius == 0 or WorkingRadius == undefined then
				(
					WorkingRadius = 50
				)
				if StrandScaleValue == 0 or StrandScaleValue == undefined then
				(
					StrandScaleValue = 1
				)
				if SelectedGrowSpeed == 0 or SelectedGrowSpeed == undefined then
				(
					SelectedGrowSpeed = 1
				)
				if SelectedType == 0 or SelectedType == undefined then
				(
					SelectedType = 1
				)
				if SelectedDetail == 0 or SelectedDetail == undefined then
				(
					SelectedDetail = 1
				)
				--
				-- start to generate grass, grow grass from the grow source out
				--
				-- create new grass layer, keeping everything organised -- 
				fn creategrasslayer = 
				(
					try
					(
						grasslayer = LayerManager.newLayer()
						grasslayer.name = "Grass Strands"
					)
					catch
					(
						messagebox "layer name already exists or the layer manager crashed for some reason."
					)
				)
				--creategrasslayer()
				--
				if UseCustomGeometry == false then
				(
				if SelectedType == 1 then
					(
						if SelectedDetail == 1 then
						(
							-- placeholder, high and wavy grass, very detailed goes here
							GrassObject = Cone smooth:on heightsegs:5 capsegs:1 sides:3 height:(random 100 200) radius1:(random 1 10) radius2:(random 0.01 1) isSelected:on
							ConvertTo GrassObject Editable_Poly
							$.EditablePoly.SetSelection #Face #{1, 3..4, 6..7, 9..10, 12..13, 15..16}
							$.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 2 then
						(
							-- placeholder, high and wavy grass goes here
							GrassObject = Cone smooth:on heightsegs:5 capsegs:1 sides:3 height:140 radius1:1 radius2:0.1 isSelected:on
							ConvertTo GrassObject Editable_Poly
							$.EditablePoly.SetSelection #Face #{1, 3..4, 6..7, 9..10, 12..13, 15..16}
							$.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 3 then
						(
							-- placeholder, high and wavy grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 4 then
						(
							-- placeholder, high and wavy grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
					)
					else if SelectedType == 2 then
					(
						if SelectedDetail == 1 then
						(
							-- placeholder, dry and dying grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 2 then
						(
							-- placeholder, dry and dying grass goes here
							GrassObject = Cone smooth:on heightsegs:6 capsegs:1 sides:6 height:100 radius1:20 radius2:3 isSelected:on
							convertTo GrassObject Editable_Poly
							subobjectLevel = 4
							$.EditablePoly.SetSelection #Face #{1..3, 5..10, 12..17, 19..24, 26..31, 33..38, 40..44}
							$.EditablePoly.delete #Face
							$.EditablePoly.SetSelection #Face #{1..6}
							$.EditablePoly.MakePlanarIn #X
							$.EditablePoly.SetSelection #Face #{}
							subobjectLevel = 2
							$.EditablePoly.SetSelection #Edge #{2, 4, 7, 10, 13, 16, 19}
							$.EditablePoly.ConnectEdges ()
							move $.selectedEdges [-1,0,0]
							subobjectLevel = 0
						)
						else if SelectedDetail == 3 then
						(
							-- placeholder, dry and dying grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 4 then
						(
							-- placeholder, dry and dying grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
					)
					else if SelectedType == 3 then
					(
						if SelectedDetail == 1 then
						(
							-- placeholder, golfing trimmed green grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 2 then
						(
							-- placeholder, golfing trimmed green grass goes here
							GrassObject = Cone smooth:on heightsegs:6 capsegs:1 sides:6 height:100 radius1:20 radius2:3 isSelected:on
							convertTo GrassObject Editable_Poly
							subobjectLevel = 4
							$.EditablePoly.SetSelection #Face #{1..3, 5..10, 12..17, 19..24, 26..31, 33..38, 40..44}
							$.EditablePoly.delete #Face
							$.EditablePoly.SetSelection #Face #{1..6}
							$.EditablePoly.MakePlanarIn #X
							$.EditablePoly.SetSelection #Face #{}
							subobjectLevel = 2
							$.EditablePoly.SetSelection #Edge #{2, 4, 7, 10, 13, 16, 19}
							$.EditablePoly.ConnectEdges ()
							move $.selectedEdges [-1,0,0]
							subobjectLevel = 0
						)
						else if SelectedDetail == 3 then
						(
							-- placeholder, golfing trimmed green grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 4 then
						(
							-- placeholder, golfing trimmed green grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
					)
					else if SelectedType == 4 then
					(
						if SelectedDetail == 1 then
						(
							-- placeholder, standardized field grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 2 then
						(
							-- placeholder, standardized field grass goes here
							GrassObject = Cone smooth:on heightsegs:6 capsegs:1 sides:6 height:100 radius1:20 radius2:3 isSelected:on
							convertTo GrassObject Editable_Poly
							subobjectLevel = 4
							$.EditablePoly.SetSelection #Face #{1..3, 5..10, 12..17, 19..24, 26..31, 33..38, 40..44}
							$.EditablePoly.delete #Face
							$.EditablePoly.SetSelection #Face #{1..6}
							$.EditablePoly.MakePlanarIn #X
							$.EditablePoly.SetSelection #Face #{}
							subobjectLevel = 2
							$.EditablePoly.SetSelection #Edge #{2, 4, 7, 10, 13, 16, 19}
							$.EditablePoly.ConnectEdges ()
							move $.selectedEdges [-1,0,0]
							subobjectLevel = 0
						)
						else if SelectedDetail == 3 then
						(
							-- placeholder, standardized field grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
						else if SelectedDetail == 4 then
						(
							-- placeholder, standardized field grass goes here
							GrassObject = cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
							ConvertTo GrassObject Editable_Poly
							select GrassObject
							subobjectlevel = 4
							GrassObject.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
							GrassObject.EditablePoly.delete #Face
							subobjectLevel = 0
						)
					)
				)
				else if UseCustomGeometry == true then
				(
					if CustomGeo != undefined do
					(
						if edtCustomGeometry.text != "" or edtCustomGeometry.text != undefined do
						(
							GrassObject = copy CustomGeo
						)
					)
				)
			for j = 1 to WorkingPositions.count do
			(
				if UseVideoFile == false then
				(
				for i = 1  to NrStrands do
				(
					NameStringStrand = "GrassObject_" + i as String
					GrassStrands[i] = copy GrassObject
					GrassStrands[i].name = NameStringStrand
					if UseCustomGeometry == false do
					(
						local bendmodifier = bend()
						local bendmodifier2 = bend()
						addmodifier GrassStrands[i] bendmodifier
						addmodifier GrassStrands[i] bendmodifier2
						bendmodifier.angle= random -(120/SelectedDetail) (120/SelectedDetail)
						bendmodifier2.angle = 0.0f
						bendmodifier2.axis = 0
						Grassrotation = Eulerangles (random -25 25) 0 0
						GrassStrands[i].material = GrassMaterial
						rotate GrassStrands[i] Grassrotation
					)
					--GrassStrands[i].boxmode = on
					-- generate random position, later crop to circle
					local randomposx = random -WorkingRadius WorkingRadius
					local randomposy = random -WorkingRadius WorkingRadius
					local randompos = [randomposx+WorkingPositions[j].x,randomposy + WorkingPositions[j].y,0]
					local distancebetween = distance WorkingPositions[j] randompos
					-- insert an editable parameter here: how long does the user want the animation to last? and does the user choose wind animation, grow animation, or both?
					local animationoffset = (distancebetween) as integer
					-- check is the position of the grass strand is within the radius of the circle
					while (distance WorkingPositions[j] randompos) > WorkingRadius do
					(
						-- if the position of the grass strand is not within the circle, generate new position
						randomposx = random -WorkingRadius WorkingRadius
						randomposy = random -WorkingRadius WorkingRadius
						randompos = [randomposx+WorkingPositions[j].x,randomposy + WorkingPositions[j].y,0]
					)
					-- give randomly generated position to the grass strand
					GrassStrands[i].pos = randompos
					-- 
					--  BEGIN ANIMATING HERE  --
					--
					with animate on
					(
						-- voor de moment nog werken met scale, maar is het mogelijk om ook met morph targets te werken?
						at time animationrange.start GrassStrands[i].scale = [(0),(0),(0)]
						at time (animationrange.start + animationoffset) GrassStrands[i].scale = [(0),(0),(0)]
						if UseCustomGeometry == false do
						(
							at time (animationrange.start + animationoffset) GrassStrands[i].modifiers[1].direction = random -50 50
						)
						if TimeLineEditing == false then
						(
							finalscale = random 0.01 (StrandScaleValue)
							at time (animationrange.end) GrassStrands[i].scale = [finalscale, finalscale, finalscale]
						)
						-- timeline editing is necessary for wind animation, which only starts after final scale has been reached. wind animation can theoretically
						-- be done while growing the grass, but that's not really realistic, neither is it a wanted combination.
						-- maybe a combination can be optional, that's a path to explore...
						else if TimeLineEditing == true then
						(
							animationRange = interval 0 maximumanimationoffset
							-- SelectedGrowSpeed enables the user to influence the grow speed, not the wind speed.
							-- the calculation makes sure the SelectedGrowSpeed option the user defined speeds up or slows down the animation of the inition growth.
							finalscale = random 0.1 (StrandScaleValue/(distancebetween))
							at time ((animationrange.start + animationoffset)) GrassStrands[i].scale = [finalscale,finalscale,finalscale]
							at time ((animationrange.start + animationoffset)) GrassStrands[i].scale = [GrassStrands[i].scale.x*10,GrassStrands[i].scale.y*10,GrassStrands[i].scale.z*10]
							-- make the user able to influence the amount of bend and direction change, and link the change to the user-defined force, too.
							-- question: either the user-defined force directly influences the change, or the user defines rollout parameters that get integrated into the calculation.
							-- make the animation interval an editable parameter, good for user input.
							randomanimationinterval = random 10 90
							randomanimationintervaloffset = random 1 5
							for i = 0 to randomanimationinterval do
							(
								if UseCustomGeometry == false do
								(
									at time ((animationrange.end/randomanimationinterval)*i) bendmodifier2.direction = random -90 90/(randomanimationinterval/randomanimationintervaloffset)
									at time ((animationrange.end/randomanimationinterval)*i) bendmodifier2.angle = random -90 90/(randomanimationinterval/randomanimationintervaloffset)
								)
							)
						)
						-- make this more compatible with all sorts of animation timelines: right now, short animations will give problems.
						if (100 + animationoffset) > maximumanimationoffset then
						(
							maximumanimationoffset = 100 + animationoffset
						)
					)
					--
					--  END ANIMATING HERE  --
					--
					-- TAG PROGRESS BARS
					pbProgressStrands.value = i*100.0 / NrStrands
					pbProgressTotal.value = j*100.0 / WorkingPositions.count
					--
					--  ADD GRASS STRANDS TO THEIR SEPERATE LAYER HERE  --
					--
					--for i = 1 to GrassStrands[i].count do
					--(
					--	grasslayer.addNode GrassStrands[i]
					--)
				)
			)
			else if UseVideoFile == true then
			(
				WorkingRadius = videofile.width/2
				-- animate  using video file, sample each frame seperately, set radius acoording to the width of the video file. tell the user the video file must be square (width = height)
				-- generate the grass strands in the same way as before, sampling pixels on the video file corresponding to the positions of the grass
				for i = 1  to NrStrands do
				(
					NameStringStrand = "GrassObject_" + i as String
					GrassStrands[i] = copy GrassObject
					GrassStrands[i].name = NameStringStrand
					if UseCustomGeometry == false do
					(
						local bendmodifier = bend()
						local bendmodifier2 = bend()
						addmodifier GrassStrands[i] bendmodifier
						addmodifier GrassStrands[i] bendmodifier2
						bendmodifier.angle= random -(120/SelectedDetail) (120/SelectedDetail)
						bendmodifier2.angle = 0.0f
						bendmodifier2.axis = 0
						Grassrotation = Eulerangles (random -25 25) 0 0
						GrassStrands[i].material = GrassMaterial
						rotate GrassStrands[i] Grassrotation
					)
					--GrassStrands[i].boxmode = on
					-- generate random position, later crop to circle
					local randomposx = random -WorkingRadius WorkingRadius
					local randomposy = random -WorkingRadius WorkingRadius
					local randompos = [randomposx+WorkingPositions[j].x,randomposy + WorkingPositions[j].y,0]
					local distancebetween = distance WorkingPositions[j] randompos
					-- insert an editable parameter here: how long does the user want the animation to last? and does the user choose wind animation, grow animation, or both?
					local animationoffset = (distancebetween) as integer
					-- check is the position of the grass strand is within the radius of the circle
					while (distance WorkingPositions[j] randompos) > WorkingRadius do
					(
						-- if the position of the grass strand is not within the circle, generate new position
						randomposx = random -WorkingRadius WorkingRadius
						randomposy = random -WorkingRadius WorkingRadius
						randompos = [randomposx+WorkingPositions[j].x,randomposy + WorkingPositions[j].y,0]
					)
					-- give randomly generated position to the grass strand
					GrassStrands[i].pos = randompos
					-- 
					--  BEGIN ANIMATING HERE  --
					--
					with animate on
					(
						if UseCustomGeometry == false do
						(
							at time (animationrange.start + animationoffset) GrassStrands[i].modifiers[1].direction = random -50 50
						)
						-- start sampling
						-- remember: [0,0] is the upper left corner of the image
						-- remember: the positions of the grass strands don't always start at [0,0,0], so first substract the grow source position before sampling
						for framenr = 0 to videofile.numframes do
						(
							gotoframe videofile framenr
							tempgrassposition = randompos
							tempgrassposition.x -= WorkingPositions[j].x
							tempgrassposition.y -= WorkingPositions[j].y
							samplecolor = getpixels videofile [videofile.width/2+tempgrassposition.x,videofile.height/2+tempgrassposition.y] 1
							scalesample = samplecolor[1].value
							scalesample = scalesample / 100
							at time framenr GrassStrands[i].scale = [scalesample,scalesample,scalesample]
						)
						randomanimationinterval = random 10 90
						randomanimationintervaloffset = random 1 5
						for i = 0 to randomanimationinterval do
						(
							if UseCustomGeometry == false do
							(
								at time ((animationrange.end/randomanimationinterval)*i) bendmodifier2.direction = random -90 90/(randomanimationinterval/randomanimationintervaloffset)
								at time ((animationrange.end/randomanimationinterval)*i) bendmodifier2.angle = random -90 90/(randomanimationinterval/randomanimationintervaloffset)
							)
						)
					)
					--  END ANIMATING HERE  --
					--
					-- TAG PROGRESS BARS
					pbProgressStrands.value = i*100.0 / NrStrands
					pbProgressTotal.value = j*100.0 / WorkingPositions.count
					--
					--  ADD GRASS STRANDS TO THEIR SEPERATE LAYER HERE  --
					--
					--for i = 1 to GrassStrands[i].count do
					--(
					--	grasslayer.addNode GrassStrands[i]
					--)
				)
			)
				-- vraag: is het performanter alle grassprietjes aan elkaar vast te maken?
				-- misschien gebruik maken van groups?
				-- performance : gebruik boxmode voor de strands.
				-- EXTRA OPTIE!!
				-- tooltje schrijven dat de gebruiker in staat stelt de animatie na het gras gemaakt is nog bij te stellen.
				-- makkelijk te schrijven, de grassprieten krijgen toch allemaal een naam, en de modifiers blijven bestaan.
				-- dit tooltje krijgt dan ook de optie om de animatie te baken, doe dit door de animatie te collapsen, staat normaal bij collapse in het utilities panel.
			
			-- end create grass
			completeRedraw()
			setCommandPanelTaskMode #modify
			)
		)
			else
			(
				messagebox "This shouldn't be happening. If it does, please send me a mail at mattias.van.camp@howest.be with description of the situation the crash occured in."
			)
			
			-- delete leftovers from realtime preview
			fn cleanupscene=
			(
				try
					(
						for i = 1 to PreviewStrand.count do
						(
							if PreviewStrand[i] != undefined do
							(
								delete PreviewStrand[i]
							)
						)
						for i = 1 to RadiusCircleRealtime.count do
						(
							if RadiusCircleRealtime[i] != undefined do
							(
								delete RadiusCircleRealtime[i]
							)
						)
						for i = 1 to SplineDummy.count do
						(
							if SplineDummy[i] != undefined do
							(
								delete SplineDummy[i]
							)
						)
						if dhelper != undefined do
						(
							delete dhelper
						)
						if GrassObject != undefined do
						(
							delete GrassObject
						)
					)
				catch
					(
						messageBox "something went wrong during cleanup, error due to double deletion of objects"
					)
			)
		cleanupscene()
		)
		on btnToggleRealtime pressed do
		(
			local floater6 = newRolloutFloater "Animated Grass Generator by MaVCArt - Realtime Preview - Settings" 300 212
			rollout settingsrollout "AVG Script by MaVCArt - settings" width:280 height:184
			(
				GroupBox grpSettings "Grow Settings" pos:[8,0] width:264 height:128
				spinner spnGrowRadius "" pos:[104,24] width:160 height:16 enabled:false range:[0,5e+008,0] scale:1
				spinner spnStrandSize "" pos:[104,48] width:160 height:16 enabled:false range:[0,5e+008,0] scale:1
				label lblGrowRadius "Grow Radius" pos:[16,24] width:88 height:16
				label lblStrandSize "Strand Size" pos:[16,48] width:88 height:16
				button btnPreparePreview "Prepare Preview" pos:[8,136] width:264 height:40
				spinner spnRandomSeed "" pos:[104,96] width:160 height:16 enabled:false range:[0,5e+008,0] scale:1
				label lblRandomSeed "Random Seed" pos:[16,96] width:88 height:16 enabled:false
				checkbox chkUseRandomGrowth "Use Random Growth" pos:[16,72] width:192 height:16
				
				local randomposx
				local randomposy
				
				on spnGrowRadius changed val do
				(
					for j = 1 to RadiusCircleRealtime.count do
					(
						if spnGrowRadius.value == 0 then
						(
							RadiusCircleRealtime[j].radius = 50.0f
							WorkingRadius = 50.0f
						)
						else
						(
							RadiusCircleRealtime[j].radius = spnGrowRadius.value
							WorkingRadius = spnGrowRadius.value
							if (WorkingRadius == 0) then
							(
								WorkingRadius = 1
							)
							if WorkingRadius > 0 then
							(
								for i = 1 to RealtimePosition[j].count do
								(
									PreviewStrand[j][i].pos.x -= WorkingPositions[j].x
									PreviewStrand[j][i].pos.y -= WorkingPositions[j].y
									PreviewStrand[j][i].pos.x = RealtimePosition[j][i].x * (WorkingRadius/50)
									PreviewStrand[j][i].pos.y = RealtimePosition[j][i].y * (WorkingRadius/50)
									PreviewStrand[j][i].pos.x += WorkingPositions[j].x
									PreviewStrand[j][i].pos.y += WorkingPositions[j].y
								)
							)
						)
					)
				)
				on spnStrandSize changed val do
				(
					for i = 1 to PreviewStrand.count do
					(
						StrandScaleValue = spnStrandSize.value/50 as Float
						PreviewStrand[i].scale = [StrandScaleValue,StrandScaleValue,StrandScaleValue]
					)
				)
				on spnRandomSeed changed val do
				(
					seed = spnRandomSeed.value
					for i = 1 to RadiusCircleRealtime.count do
					(
						for j = 1 to PreviewStrand[i].count do
						(
							randomposx = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
							randomposy = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
							randompos = [randomposx+WorkingPositions[i].x,randomposy + WorkingPositions[i].y,0]
							distancebetween = distance WorkingPositions[i] randompos
							-- check is the position of the grass strand is within the radius of the circle
							while (distance WorkingPositions[i] randompos as integer) > RadiusCircleRealtime[i].radius do
							(
								-- if the position of the grass strand is not within the circle, generate new position
								randomposx = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
								randomposy = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
								randompos = [randomposx+WorkingPositions[i].x,randomposy + WorkingPositions[i].y,0]
							)
							-- give randomly generated position to the grass strand
							PreviewStrand[i][j].pos = randompos
							RealtimePosition[i][j] = randompos
						)
					)
				)
				on btnPreparePreview pressed do
				(
					if (WorkingPositions.count > 0) do
					(
						for i = 1 to WorkingPositions.count do
						(
							SetCommandPanelTaskMode #create
							
							NameString = "RadiusCircle_" + i as String
							RadiusCircle = Circle radius:50
							RadiusCircle.Name = NameString
							RadiusCircle.pos = WorkingPositions[i]
							RadiusCircleRealtime[i] = RadiusCircle
							-- make empty arrays, sort of prepare them for data receiving
							PreviewStrand[i] = #()
							RealtimePosition[i] = #()
							-- generate strands, store them in a subarray of each realtime radius circle
							for j = 1 to 10 do
							(
								-- generate geometry
								-- explore the viability of using proxys instead of actual geometry
								NameStringStrand = "PreviewStrand_" + i as String + "_" + j as String
								StrandPreview = Cone height:80 radius1:3 radius2:1 sides:3 heightsegs:3
								ConvertTo StrandPreview Editable_Poly
								select StrandPreview
								subobjectlevel = 4
								StrandPreview.EditablePoly.setSelection #Face #{1, 3..4, 6..7, 9..11}
								StrandPreview.EditablePoly.delete #Face
								realtimebendmodifier = bend()
								realtimebendmodifier2 = bend()
								addmodifier StrandPreview realtimebendmodifier
								addmodifier StrandPreview realtimebendmodifier2
								-- first bend modifier is for the bend angle of the strands,
								-- the second one if for the wind turbulence effect.
								realtimebendmodifier.angle=45.0f
								realtimebendmodifier2.axis = 0
								realtimebendmodifier2.angle=15.0f
								-- give position to the grass strands
								Grassrotation = Eulerangles (random -25 25) 0 0
								rotate StrandPreview Grassrotation
								-- generate random position, later crop to circle
								randomposx = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
								randomposy = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
								randompos = [randomposx+WorkingPositions[i].x,randomposy + WorkingPositions[i].y,0]
								distancebetween = distance WorkingPositions[i] randompos
								-- check is the position of the grass strand is within the radius of the circle
								while (distance WorkingPositions[i] randompos as integer) > RadiusCircleRealtime[i].radius do
								(
									-- if the position of the grass strand is not within the circle, generate new position
									randomposx = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
									randomposy = random -RadiusCircleRealtime[i].radius RadiusCircleRealtime[i].radius
									randompos = [randomposx+WorkingPositions[i].x,randomposy + WorkingPositions[i].y,0]
								)
								-- give randomly generated position to the grass strand
								StrandPreview.pos = randompos
								StrandPreview.Name = NameStringStrand
								PreviewStrand[i][j] = StrandPreview
								RealtimePosition[i][j] = randompos - WorkingPositions[i]
							)
							
							btnPreparePreview.enabled = false
						)
						
						spnGrowRadius.enabled = true
						spnStrandSize.enabled = true
						btnExecute.enabled = true
						btnToggleRealtime.enabled = false
					)
				)
				on chkUseRandomGrowth changed state do
				(
					if chkUseRandomGrowth.checked == true then
					(
						spnRandomSeed.enabled = true
						lblRandomSeed.enabled = true
					)
					else if chkUseRandomGrowth.checked == false then
					(
						spnRandomSeed.enabled = false
						lblRandomSeed.enabled = false
					)
				)
			)
			addRollout settingsrollout floater6
		)
		on chkToggleLifeTime changed state do
		(
			if chkToggleLifeTime.checked == true then
			(
				LifeTime = true
				chkEternal.checked = false
			)
			else if chkToggleLifeTime.checked == false then
			(
				LifeTime = false
				chkEternal.checked = true
			)
		)
		on ddlStrandType selected obj do
		(
			if (ddlStrandType.selection == 1) do
			(
				SelectedType = 1
			)
			if (ddlStrandType.selection == 2) do
			(
				SelectedType = 2
			)
			if (ddlStrandType.selection == 3) do
			(
				SelectedType = 3
			)
			if (ddlStrandType.selection == 4) do
			(
				SelectedType = 4
			)
		)
		on ddlDetail selected obj do
		(
			if (ddlDetail.selection == 1) do
			(
				if queryBox HighDetailString beep:true then
				(
					SelectedDetail = 1
				)
				else
				(
					ddlDetail.selection = 0
				)
			)
			if (ddlDetail.selection == 2) do
			(
				SelectedDetail = 2
			)
			if (ddlDetail.selection == 3) do
			(
				SelectedDetail = 3
			)
			if (ddlDetail.selection == 4) do
			(
				SelectedDetail = 4
			)
		)
		on chkEternal changed state do
		(
			if chkEternal.checked == true then
			(
				Eternal = true
				chkToggleLifeTime.checked = false
			)
			else if chkEternal.checked ==  false then
			(
				Eternal = false
				chkToggleLifeTime.checked = true
			)
		)
		on chkFlowerMixture changed state do
		(
			if chkFlowerMixture.checked == true then
			(
				FlowerMixture = true
			)
			else if chkFlowerMixture.checked == false then
			(
				FlowerMixture = false
			)
		)
		on chkFootstep changed state do
		(
			if chkFootstep.checked == true then
			(
				FootstepMode = true
			)
			else if chkFootstep.checked == false then
			(
				FootstepMode = false
			)
		)
		on ddlGrowSpeed selected obj do
		(
			if(ddlGrowSpeed.selection == 1) do
			(
				SelectedGrowSpeed = 0.5f
			)
			if(ddlGrowSpeed.selection == 2) do
			(
				SelectedGrowSpeed = 1
			)
			if(ddlGrowSpeed.selection == 3) do
			(
				SelectedGrowSpeed = 2
			)
			if(ddlGrowSpeed.selection == 4) do
			(
				SelectedGrowSpeed = 3
			)
		)
		on btnDummyGrowMode pressed do
		(
			local floater2 = newRolloutFloater "Animated Grass Generator by MaVCArt - Pick Dummies" 340 307
			rollout PickDummiesRollout "Animated Grass Generator by MaVCArt - Pick Dummies" width:328 height:280
			(
				GroupBox grpEverything "Settings" pos:[8,8] width:312 height:264
				edittext txtSelection "Selection" pos:[16,24] width:296 height:184 readOnly:true
				button btnStoreSelection "Store Selection" pos:[16,216] width:296 height:48
				
				on btnStoreSelection pressed do
				(
					txtSelection.text = ""
					for i = 1 to selection.count do
					(
						if iskindof selection[i] Dummy then
						(
							SelectedDummies[i] = selection[i]
							txtSelection.text = txtSelection.text + SelectedDummies[i].Name as String + "\n"
							WorkingSelection = SelectedDummies
							for i = 1 to WorkingSelection.count do 
							(
								WorkingPositions[i] = WorkingSelection[i].pos
								edtPickedGrowSource.text = txtSelection.text
							)
						)
					)
				)
			)
			addRollout PickDummiesRollout floater2
		)
		on ddlGrowMode selected obj do
		(
			if (ddlGrowMode.selection == 1) do
			(
				btnDummyGrowMode.enabled = true
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 1
			)
			if (ddlGrowMode.selection == 2) do
			(
				btnDummyGrowMode.enabled = false
				btnSplinePathMode.enabled = true
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 2
			)
			if (ddlGrowMode.selection == 3) do
			(
				btnDummyGrowMode.enabled = false
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = true
				SelectedGrowMode = 3
			)
			if (ddlGrowMode.selection == 4) do
			(
				btnDummyGrowMode.enabled = false
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = true
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 4
			)
		)
		on btnSplinePathMode pressed do
		(
			local floater3 = newRolloutFloater "Animated Grass Generator by MaVCArt - Pick Splines" 340 355
			rollout PickSplinesRollout "Animated Grass Generator by MaVCArt - Pick Splines" width:328 height:328
			(
				GroupBox grpEverything "Settings" pos:[8,8] width:312 height:312
				edittext txtSelection "Selection" pos:[16,48] width:296 height:184 readOnly:true
				button btnStoreSelection "Store Selection" pos:[16,240] width:296 height:48
				spinner spnSamples "" pos:[64,24] width:248 height:16 enabled:false range:[2,100,2] scale:1
				label lblSamples "Samples" pos:[16,24] width:40 height:16
				checkbox chkAnimateAlongPath "Animate Along Path" pos:[16,296] width:296 height:16
				
				local nrSamples
				
				on btnStoreSelection pressed do
				(
					txtSelection.text = ""
					for i = 1 to selection.count do
					(
						if ((iskindof selection[i] editable_poly) or (iskindof selection[i] Force)) then
						(
						)
						else
						(
							SelectedSplinePaths[i] = selection[i]
							txtSelection.text = txtSelection.text + SelectedSplinePaths[i].Name as String + "\n"
							WorkingSelection = SelectedSplinePaths
							spnSamples.enabled = true
						)
					)
				)
				on spnSamples changed val do
				(
					for i = 1 to SplineDummy.count do
					(
						for j = 1 to SPlineDummy[i].count do
						(
							if not isDeleted SplineDummy[i][j] do
							(
								delete SplineDummy[i][j]
							)
						)
					)
					if dhelper != undefined do
					(
						delete dhelper
					)
					if spnSamples.value == 0 do
					(
						spnSamples.value = 2
					)
					nrSamples = spnSamples.value
					-- safeguard, just in case
					if nrSamples == 0 do
					(
						nrSamples = 2
					)
					for i = 1 to SelectedSplinePaths.count do
					(
						dhelper = point size:20
						dhelper.wirecolor = color 255 7 7
						pc = Path_Constraint()
						dhelper.position.controller = pc
						pc.path = selectedSplinePaths[i]
						tempPositions = #(#(),#())
						
						for j = 1 to nrSamples do
						(
							dhelper.pos.controller.PERCENT = (100.0f/nrSamples)*j
							tempPositions[i][j] = dhelper.pos
						)
						for f = 1 to tempPositions.count do
						(
							SplineDummy[i][f] = point size:20
							SplineDummy[i][f].name = "Dummy_" + i as string + "_" + f as string
							SplineDummy[i][f].wirecolor = color 8 8 136
							SplineDummy[i][f].pos = tempPositions[i][f]
						)
						-- store the final positions in the workingpositions array
						if i == 1 then
						(
							WorkingPositions = tempPositions[i]
						)
						else if i > 1 then
						(
							WorkingPositions = WorkingPositions + tempPositions[i]
						)
					)
				)
				on chkAnimateAlongPath changed state do
				(
					if chkAnimateAlongPath.checked == true then
					(
						AnimateAlongPath = true
					)
					else if chkAnimateAlongPath.checked == false then
					(
						AnimateAlongPath = false
					)
				)
			)
			addRollout PickSplinesRollout floater3
		)
		on btnPickSplineLimitationMode pressed do
		(
			local floater4 = newRolloutFloater "Animated Grass Generator by MaVCArt - Pick Spline Limits" 340 307
			Rollout PickSplineLimitsRollout "Animated Grass Generator by MaVCArt - Pick Spline Limits" width:328 height:280
			(
				groupBox grpEverything "Settings" pos:[8,8] width:312 height:264
				edittext txtSelection "Selection" pos:[16,24] width:296 height:184 readOnly:true
				button btnStoreSelection "Store Selection" pos:[16,216] width:296 height:48
				
				on btnStoreSelection pressed do
				(
					txtSelection.text = ""
					for i = 1 to selection.count do
					(
						if iskindof selection[i] SplineShape then
						(
							SelectedSplineLimits[i] = selection[i]
							txtSelection.text = txtSelection.text + SelectedSplineLimits[i].Name as String + "\n"
							WorkingSelection = SelectedSplineLimits
							-- in here, use a spline as a surface limit.
							-- how to calculate whether or not a point is inside a random surface?
							-- convex concave method
							for i = 1 to WorkingSelection.count do
							(
								WorkingPositions[i] = WorkingSelection[i].pos
							)
						)
					)
				)
			)
			addRollout PickSplineLimitsRollout floater4
		)
		on btnPickSurfaceMode pressed do
		(
			local floater5 = newRolloutFloater "Animated Grass Generator by MaVCArt - Pick Surfaces" 340 307
			Rollout PickSurfaceRollout "Animated Grass Generator by MaVCArt - Pick Surfaces" width:328 height:280
			(
				groupBox grpEverything "Settings" pos:[8,8] width:312 height:264
				edittext txtSelection "Selection" pos:[16,24] width:296 height:184 readOnly:true
				button btnStoreSelection "Store Selection" pos:[16,216] width:296 height:48
				
				on btnStoreSelection pressed do
				(
					txtSelection.text = ""
					for i = 1 to selection.count do
					(
						if not iskindof selection[i] Editable_Poly then
						(
							if querybox "This converts every object in the selection to an editable poly object. (splines can give unpredictable results) do you want to continue?" beep:true then
							(
								convertTo selection[i] Editable_Poly
							)
						)
						if iskindof selection[i] Editable_Poly then
						(
							SelectedSurfaces[i] = selection[i]
							txtSelection.text = txtSelection.text + SelectedSurfaces[i].Name as String + "\n"
							WorkingSelection = SelectedSurfaces
							-- in here, it gets tricky:
							-- in order to grow grass on a surface, we need to access that surface's normal, and orientate the grass accordingly.
							-- first, though, we need to position the seperate grass strands.
							-- question: is it better to just immedeatly position the grass on the surfaces themselves somehow, or just sample each position and 
							-- check collision/proximity to the position of the nearest polygon?
							for i = 0 to WorkingSelection.count do
							(
								for j = 1 to WorkingSelection[i].NumberPolygons do
								(
									
								)
							)
						)
					)
				)
			)
			AddRollout PickSurfaceRollout floater5
		)
		on chkAllowTimeLineChange changed state do
		(
			if chkAllowTimeLineChange.checked == true then
			(
				TimeLineEditing = true
				spnMaximumAnimationRange.enabled = true
			)
			else if chkAllowTimeLineChange.checked == false then
			(
				TimeLineEditing = false
				spnMaximumAnimationRange.enabled = false
			)
		)
		on chkUseForce changed state do
		(
			if chkUseForce.checked == true then
			(
				UseCustomForce = true
				btnPickForceMode.enabled = true
			)
			else if chkUseForce.checked == false then
			(
				UseCustomForce = false
				btnPickForceMode.enabled = false
			)
		)
		on btnPickForceMode pressed do
		(
			floater1 = newrolloutfloater "Animated Grass Generator by MaVCArt - Pick Forces" 268 427
			rollout PickForcesRollout "Animated Grass Generator by MaVCArt - Pick Forces" width:256 height:400
			(
				GroupBox grpSelection "User-Defined Forces" pos:[8,8] width:240 height:232
				edittext txtSelection "Selection" pos:[16,24] width:224 height:10
				button btnStoreSelection "Store Selection" pos:[16,184] width:224 height:48
				
				-- no workaround needed, as spring controllers are no longer used.
				-- in this rollout, specify parameters used in vertex animation by bend modifiers.
				-- OR, let the user directly influence specific parameters used in the animation of the bend modifiers.
				-- also let user define self-defined forces, and fill in the parameters according to their selection automatically.
				
				GroupBox grpForceSettings "Force Settings" pos:[8,248] width:240 height:144
				label lblForceStrength "Force Strength" pos:[16,272] width:128 height:16
				label lblForceDirection "Force Direction (Degrees)" pos:[16,296] width:126 height:16
				spinner spnForceStrength "" pos:[152,272] width:88 height:16 range:[0,5e+008,0] scale:1
				spinner spnForceDirection "" pos:[152,296] width:88 height:16 range:[0,5e+008,0] scale:1
				label lblForceTurbulence "Force Turbulence" pos:[16,320] width:126 height:16
				spinner spnForceTurbulence "" pos:[152,320] width:88 height:16 range:[0,5e+008,0] scale:1
				button btnPreview "Make Preview" pos:[16,344] width:224 height:40
				
				-- all variables to be changed are stored in the master variable storage area, for use in the master calculations.
				
				on btnStoreSelection pressed do
				(
					-- sample selection type, filter out anything that is not a wind spacewarp.
					-- maybe warning message of some kind before opening rollout floater that only wind spacewarps can be stored?
					-- also, when this button is pressed, store values of the force in the spinners and variables.
					-- if the selection contains more than one force, take middle value.
					-- perhaps support for animated forces, so users can animate the strength of the force? after all, this script IS focused on animation!
					for i = 0 to selection.count do
					(
						txtSelection = ""
						if iskindof selection[i] Wind then
						(
							SelectedForces[i] = selection[i]
							txtSelection.text = txtSelection.text + SelectedForces[i].Name as String + "\n"
						)
					)
					if SelectedForces.count > 0 do
					(
						local TotalForceStrength
						local TotalForceTurbulence
						local TotalForceDirection
						for j = 0 to SelectedForces.count do
						(
							TotalForceStrength += SelectedForces[i].Strength
							TotalForceTurbulence += SelectedForces[i].Turbulence
							-- voor direction, eerst de richtingsvector berekenen.
							
							-- gemiddelde nemen
							TotalForceStrength = TotalForceStrength/SelectedForces.count
							TotalForceTurbulence = TotalForceTurbulence/SelectedForces.count
							-- zelfde voor vector, want daar zijn meerdere parameters in.
						)
						ForceStrength = TotalForceStrength
						ForceDirection = TotalForceDirection
						ForceTurbulence = TotalForceTurbulence
						
						spnForceStrength.value = ForceStrength
						spnForceDirection.value = ForceDirection
						spnForceTurbulence.value = ForceTurbulence
					)
				)
				on btnPreview pressed do
				(
					-- make a preview, just like in the realtime grass preview
					-- make a preview of the force direction by making a scripted spline shaped like a compass with an arrow that rotates around the middle of the origin of the world. 
					-- WARNING don't use grow source position here, because in this part of the settings the user usually will not have defined the grow sources yet. the world origin will do just fine.
				)
				on spnForceStrength changed val do
				(
					ForceStrength = spnForceStrength.value
				)
				on spnForceDirection changed val do
				(
					ForceDirection = spnForceDirection
				)
				on spnForceTurbulence changed val do
				(
					ForceTurbulence = spnForceTurbulence
				)
			)
			addRollout PickForcesRollout floater1
		)
		on spnNrStrands changed val do
		(
			NrStrands = spnNrStrands.value as Integer
		)
		on spnMaximumAnimationRange changed val do
		(
			maximumanimationoffset = spnMaximumAnimationRange.value
			animationRange = interval 0 maximumanimationoffset
		)
		on ddlPresets selected obj do
		(
			if ddlPresets.selection == 1 do
			(
				-- set all selections to 0
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlDetail.selection = 0
				ddlStrandType.selection = 0
				ddlGrowMode.selection = 0
				ddlGrowSpeed.selection = 0
				SelectedDetail = 0
				SelectedType = 0
				SelectedGrowSpeed = 0
				btnDummyGrowMode.enabled = false
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 0
				btnPickGeometry.enabled = false
				edtCustomGeometry.enabled = false
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
			)
			if ddlPresets.selection == 2 do
			(
				-- animation settings
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlDetail.selection = 1
				ddlStrandType.selection = 1
				ddlGrowMode.selection = 1
				ddlGrowSpeed.selection = 1
				SelectedDetail = 1
				SelectedType = 1
				SelectedGrowSpeed = 0.5
				btnDummyGrowMode.enabled = true
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 1
				btnPickGeometry.enabled = false
				edtCustomGeometry.enabled = false
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
			)
			if ddlPresets.selection == 3 do
			(
				-- performance settings
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlDetail.selection = 1
				ddlStrandType.selection = 1
				ddlGrowMode.selection = 1
				ddlGrowSpeed.selection = 1
				SelectedDetail = 1
				SelectedType = 1
				SelectedGrowSpeed = 0.5
				btnDummyGrowMode.enabled = true
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 1
				btnPickGeometry.enabled = false
				edtCustomGeometry.enabled = false
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
			)
			if ddlPresets.selection == 4 do
			(
				-- quick results settings, with dummies, depends on what the user uses most (try to find out from community)
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlDetail.selection = 1
				ddlStrandType.selection = 1
				ddlGrowMode.selection = 1
				ddlGrowSpeed.selection = 1
				SelectedDetail = 1
				SelectedType = 1
				SelectedGrowSpeed = 0.5
				btnDummyGrowMode.enabled = true
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 1
				btnPickGeometry.enabled = false
				edtCustomGeometry.enabled = false
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
			)
			if ddlPresets.selection == 5 do
			(
				-- custom geometry
				ddlDetail.selection = 0
				ddlStrandType.selection = 0
				ddlGrowMode.selection = 1
				ddlGrowSpeed.selection = 1
				SelectedDetail = 0
				SelectedGrowSpeed = 0.5
				btnDummyGrowMode.enabled = true
				btnSplinePathMode.enabled = false
				btnPickSplineLimitationMode.enabled = false
				btnPickSurfaceMode.enabled = false
				SelectedGrowMode = 1
				chkUseCustomGeometry.checked = true
				ddlDetail.enabled = false
				ddlStrandType.enabled = false
				btnPickGeometry.enabled = true
				edtCustomGeometry.enabled = true
				ddlDetail.enabled = true
				ddlStrandType.enabled = true
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
				chkEnterVideoAnimation.checked = false
				UseVideoFile = false
			)
			if ddlPresets.selection == 6 do
			(
				-- video file
				if chkUseCustomGeometry.checked == false then
				(
					ddlDetail.enabled = true
					ddlStrandType.enabled = true
				)
				ddlGrowSpeed.enabled = false
				spnMaximumAnimationRange.enabled = false
				chkAllowTimeLineChange.enabled = false
				chkEternal.enabled = false
				chkFlowerMixture.enabled = false
				chkFootstep.enabled = false
				chkToggleLifeTime.enabled = false
				chkUseForce.enabled = false
				chkToggleLifeTime.enabled = false
				btnPickForceMode.enabled = false
				chkEnterVideoAnimation.checked = true
				edtVideoFile.enabled = true
				btnPickVideoFile.enabled = true
				UseVideoFile = true
			)
		)
		on btnClearScene pressed do
		(
			-- clear the scene
			-- this means: clear realtime leftovers
			-- clear grass strands that are already in the scene
			-- clear anything that might have been left behind
			-- try not to delete anything already in the scene that has nothing to do with the grass script
			animationrange = interval 0 100
			sliderTime = 0
		)
		on btnPickGeometry picked obj do
		(
			if obj != undefined and iskindof obj Editable_Poly == true then
			(
				CustomGeo = obj
				edtCustomGeometry.text = obj.name
			)
			else if obj == undefined or iskindof obj Editable_Poly == false then
			(
				messagebox "please select editable poly object for custom geometry"
			)
		)
		on chkUseCustomGeometry changed state do
		(
			if chkUseCustomGeometry.checked == true then
			(
				btnPickGeometry.enabled = true
				edtCustomGeometry.enabled = true
				ddlDetail.enabled = false
				ddlStrandType.enabled = false
				--
				UseCustomGeometry = true
			)
			else if chkUseCustomGeometry.checked == false then
			(
				btnPickGeometry.enabled = false
				edtCustomGeometry.enabled = false
				if UseVideoFile == false then
				(
					ddlDetail.enabled = true
					ddlStrandType.enabled = true
				)
				--
				UseCustomGeometry = false
			)
		)
		on chkEnterVideoAnimation changed state do
		(
			-- disable almost everything, just leave nr of strands (even disable maximum animation range)
			if chkEnterVideoAnimation.checked == true then
			(
				if chkUseCustomGeometry.checked == false then
				(
					ddlDetail.enabled = true
					ddlStrandType.enabled = true
				)
				ddlGrowSpeed.enabled = false
				spnMaximumAnimationRange.enabled = false
				chkAllowTimeLineChange.enabled = false
				chkEternal.enabled = false
				chkFlowerMixture.enabled = false
				chkFootstep.enabled = false
				chkToggleLifeTime.enabled = false
				chkUseForce.enabled = false
				chkToggleLifeTime.enabled = false
				btnPickForceMode.enabled = false
				edtVideoFile.enabled = true
				btnPickVideoFile.enabled = true
				UseVideoFile = true
				messagebox "Video File height must be equal to its width for the animation to come out exactly as the video file heightmap specifies."
				-- account for maximum animation range if already entered, make sure the boolean of UseVideoFile is calculated into the animation script
			)
			else if chkEnterVideoAnimation.checked == false then
			(
				if chkUseCustomGeometry.checked == false then
				(
					ddlDetail.enabled = true
					ddlStrandType.enabled = true
				)
				ddlGrowSpeed.enabled = true
				spnMaximumAnimationRange.enabled = true
				chkAllowTimeLineChange.enabled = true
				chkEternal.enabled = true
				chkFlowerMixture.enabled = true
				chkFootstep.enabled = true
				chkToggleLifeTime.enabled = true
				chkUseForce.enabled = true
				chkToggleLifeTime.enabled = true
				if UseCustomForce == true then
				(
					btnPickForceMode.enabled = true
				)
				edtVideoFile.enabled = false
				btnPickVideoFile.enabled = false
				UseVideoFile = false
			)
		)
		on btnPickVideoFile pressed do
		(
			videofile = SelectBitmap()
			fn samplevideofile videofile = 
			(
				try
				(
					videofileviable = false
					if videofile.width == videofile.height then
					(
						WorkingRadius = videofile.width
						if videofile.numframes > 1 then
						(
							animationRange = interval 0 videofile.numframes
						)
						videofileviable = true
					)
					else if videofile.width != videofile.height then
					(
						messagebox "video file width does not correspond with video file height"
						videofileviable = false
					)
					else if videofile.numframes <= 1 then
					(
						messagebox "video file has only one frame (or less)"
						videofileviable = false
					)
				)
				catch
				(
					messagebox "something went wrong when sampling the video file. check if the video file's width is equal to its height, or if the video file has more than one frame"
				)
				return videofileviable
			)
			if samplevideofile(videofile) then
			(
				edtVideoFile.text = videofile.filename
			)
		)
	)
addRollout AVGScriptbyMaVCArt floater
)

