'##########################################################################################################################################################
'
' TWIG GEN
' VERSION 1.0
' Vincent Fortin [vfortin3d@hotmail.com]
' LAST MODIFIED 2002/10/23
'
' DESCRIPTION:	This script generates extruded models along one or more specified curve(s).
'		It can be used to produce various effects like roots, twigs, hay, cables & wires.
' USAGE:
'	//// Draw a spline and run the script \\\\
'	NUMBER................Defines the number of models to generate
'	POINTS................Defines the number of points. The more points you set, the more ripples each model will have.
'	VAR...................Controls the degree of variability in the POINTS value of each generated curves.
'	MAX CHAOS.............Defines the maximum amplitude (negative to positive) of each ripple. Value is in Softimage|XSI Units.
'	SEED..................Random seed value for the point generation.
'	ORGANIZED CHAOS.......Used to give more similarity to the ripples. Useful for hair or wires.
'	FREEZE ALL............Freezes extrusion operators. You won't have any further control over shape, animation & modeling relation.
'	Linear................If checked, will create linear curves.
'	CURVES ONLY...........Doesn't create any geometry, only splines with proper modeling relation.
'
'##########################################################################################################################################################
Option Explicit
TwiGgen

'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' MAIN TWIGGEN
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sub TwigGen

	Dim oRoot, Sel, oSel, pCurve, oCircle, oTwigParent
	Dim nCurves, Freq, FreqVar, Chaos, ChaosSeed, OrgChaos, FreezeAll, Linear, CurvesOnly, Valid
	Dim GeometryDialogue
	
	Set oRoot = Application.ActiveProject.ActiveScene.Root
	Set Sel = GetValue("SelectionList")
	If Sel.Count < 1 Then Exit Sub
	Set oSel = SIFilter(Sel,"curve")														' FILTER SELECTION TO ONLY KEEP CURVES
	If TypeName(oSel) = "Nothing" Then Exit Sub

	GetDialogue nCurves, Freq, FreqVar, Chaos, ChaosSeed, OrgChaos, FreezeAll, Linear, CurvesOnly, Valid			' CALL MAIN MENU PPG & RETRIEVE VALUES
	If Valid = False Then Exit Sub

	If CurvesOnly = False Then															' NO NEED FOR A CIRCLE IF "CURVES ONLY" IS ON
		Set oCircle = CreatePrim("Circle","NurbsCurve")
		SetValue oCircle & ".circle.radius",0.1
		If FreezeAll = False Then														' NO NEED FOR EXTRA EXTRUDE OPTIONS IF
			ExtrudeOptions GeometryDialogue, Linear											'       "FREEZE ALL" IS CHECKED
		End If
	End If

	For Each pCurve in oSel																' FOR EACH SELECTED CURVE, START DRAWING
		Set oTwigParent = pCurve.AddPrimitive("Null", "TwigGen")
		DrawCurves pCurve, nCurves, Freq, FreqVar, Chaos, ChaosSeed, OrgChaos, FreezeAll, Linear, CurvesOnly, oCircle, GeometryDialogue, oTwigParent
		SelectObj pCurve
	Next

	If FreezeAll = True Then
		DeleteObj oCircle
	Else
		If CurvesOnly = False Then
			CopyPaste oCircle, , oTwigParent, 1
			ToggleVisibility oCircle
			InspectObj GeometryDialogue, "All"
		End If
	End If

End Sub

'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' GET USER INPUT FROM DIALOGUE
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Function GetDialogue(nCurves, Freq, FreqVar, Chaos, ChaosSeed, OrgChaos, FreezeAll, Linear, CurvesOnly, Valid)		' THE MAIN OPTION PANEL.

	Dim oRoot, oDial, OptionSliders
	Valid = False

	Set oRoot = Application.ActiveProject.ActiveScene.Root
	Set oDial = AddProp("Custom_Parameter_List", oRoot, , "General_Options").Value("Value")
	OptionSliders = oRoot & "." & oDial

	SIAddCustomParameter OptionSliders, "nCurves", siInt4, 20.000, 1.000, 999.000,, siPersistable+siSilent  , 1.000, 50.000,, "Objects"
	SIAddCustomParameter OptionSliders, "Freq", siInt4, 15.000, 3.000, 999.000,, siPersistable+siSilent, 3.000, 100.000,, "Points"
	SIAddCustomParameter OptionSliders, "FreqVar", siInt4, 0.000, 0.000, 999.000,, siPersistable+siSilent, 0.000, 100.000,, "Var"
	SIAddCustomParameter OptionSliders, "Chaos",, 5.000, 0.000, 10.000,, siPersistable+siSilent, 0.000, 10.000,, "Max Chaos"
	SIAddCustomParameter OptionSliders, "ChaosSeed", siInt4, 17.000, 1.000, 999.000,, siPersistable+siSilent, 1.000, 999.000,, "Seed"
	SIAddCustomParameter OptionSliders, "OrgChaos", siBool, 0.000, 0.000, 1.000,, siPersistable+siSilent, 0.000, 1.000,, "Organized Chaos"
	SIAddCustomParameter OptionSliders, "FreezeAll", siBool, 0.000, 0.000, 1.000,, siPersistable+siSilent, 0.000, 1.000,, "Freeze All"
	SIAddCustomParameter OptionSliders, "Linear", siBool, 0.000, 0.000, 1.000,, siPersistable+siSilent, 0.000, 1.000,, "Linear"
	SIAddCustomParameter OptionSliders, "CurvesOnly", siBool, 0.000, 0.000, 1.000,, siPersistable+siSilent, 0.000, 1.000,, "Curves Only"
	
	On Error Resume Next
	InspectObj OptionSliders ,,,SIModal

	If Err.Number <> 0 Then
		DeleteObj OptionSliders
		Exit Function
	End If

	nCurves = GetValue(OptionSliders & ".nCurves")
	Freq = GetValue(OptionSliders & ".Freq")
	FreqVar = GetValue(OptionSliders & ".FreqVar")
	Chaos = GetValue(OptionSliders & ".Chaos")
	ChaosSeed = GetValue(OptionSliders & ".ChaosSeed")
	OrgChaos = GetValue(OptionSliders & ".OrgChaos")
	FreezeAll = GetValue(OptionSliders & ".FreezeAll")
	Linear = GetValue(OptionSliders & ".Linear")													' COLLECT USER INPUTS...
	CurvesOnly = GetValue(OptionSliders & ".CurvesOnly")

	DeleteObj OptionSliders																	' THEN DELETE CUSTOM PARAM FROM SCENE
	Valid = True

End Function

'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' CREATE OPTIONAL GEOMETRY PPG
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Function ExtrudeOptions(GeometryDialogue, Linear)													' WHILE THIS PPG IS CREATED AT THE BEGINING
																					' OF THE SCRIPT, IT WILL BE CALLED BACK AT
	Dim oRoot, oDial																		' THE END FOR MORE FUN...

	Set oRoot = Application.ActiveProject.ActiveScene.Root
	Set oDial = AddProp("Custom_Parameter_List", oRoot,, "TwigGen_Options").Value("Value")
	GeometryDialogue = oRoot & "." & oDial

	If Linear = True Then
		SIAddCustomParameter GeometryDialogue, "uSteps",siInt4, 4.000, 1.000, 100.000,, siReadOnly, 1.000, 20.000,, "U Steps"
		SIAddCustomParameter GeometryDialogue, "vSteps",siInt4, 10.000, 1.000, 999.000,, siReadOnly, 1.000, 50.00,, "V Steps"
	Else
		SIAddCustomParameter GeometryDialogue, "uSteps",siInt4, 3.000, 1.000, 100.000,, siPersistable+siAnimatable, 1.000, 20.000,, "U Steps"
		SIAddCustomParameter GeometryDialogue, "vSteps",siInt4, 10.000, 1.000, 999.000,, siPersistable+siAnimatable, 1.000, 50.00,, "V Steps"
	End If
	
	SIAddCustomParameter GeometryDialogue, "StartPos",, 0.000, 0.000, 99.900,, siPersistable+siAnimatable, 0.000, 99.900,, "Start Pos"
	SIAddCustomParameter GeometryDialogue, "EndPos",, 100.000, 0.010, 100.000,, siPersistable+siAnimatable, 0.010, 100.000,, "End Pos"
	SIAddCustomParameter GeometryDialogue, "Radius",, 0.100, 0.010, 999.000,, siPersistable+siAnimatable, 0.010, 10.000,, "Radius"

End Function
	
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' DRAW nCURVES
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Function DrawCurves(pCurve, nCurves, Freq, FreqVar, Chaos, ChaosSeed, OrgChaos, FreezeAll, Linear, CurvesOnly, oCircle, GeometryDialogue, oTwigParent)

	Dim Tracer, i, ii, x, y, z, oCurve, oRoot, PathCns, Lin, NewFreq, u_bound, l_bound
	ReDim TwigObject(nCurves)
	If Linear = True Then Lin = 1 Else Lin = 3
	
	Set Tracer = pCurve.parent.AddPrimitive("Null")												' THIS GUY WILL SLIDE ON THE CURVE AND
	Set PathCns = Tracer.Kinematics.AddConstraint("Path", pCurve)									' SEND HIS POSITION TO AN ARRAY
																				' EVERY (ii/Freq * 100) PATH %
	Randomize ChaosSeed
	For i = 0 to nCurves - 1
		l_bound = FreqVar - (FreqVar * 2)
		u_bound = FreqVar
		NewFreq = Freq + Int((u_bound - l_bound + 1) * Rnd + l_bound)
		If NewFreq < 3 Then NewFreq = 3

		ReDim cArray(2, NewFreq)
																				' THANKS TO MICHAEL ISNER FOR THE REF,
		For ii = 0 to NewFreq															' SAVED ME A LOT OF MATH TROUBLES :)
			PathCns.Parameters("perc").value = ii / NewFreq * 100
			x = Tracer.Kinematics.Global.Parameters("posx").Value
			y = Tracer.Kinematics.Global.Parameters("posy").Value
			z = Tracer.Kinematics.Global.Parameters("posz").Value
			cArray(0, ii) = x + VarianceNumber(Chaos, ChaosSeed, OrgChaos, ii)						' VARIANCENUMBER ADDS THE CHAOS TO EACH
			cArray(1, ii) = y + VarianceNumber(Chaos, ChaosSeed, OrgChaos, ii)						' POINT POSITION. ii FOR THE ORGANIZED CHAOS
			cArray(2, ii) = z + VarianceNumber(Chaos, ChaosSeed, OrgChaos, ii)
		Next
		
		Set oCurve = oTwigParent.AddNurbsCurve(cArray,, False, Lin, 0, 0)

		If CurvesOnly = False Then														' USUAL PRECAUTIONS FOR "CURVES ONLY"
			ExtrudeTwigs FreezeAll, GeometryDialogue, oCircle, oCurve, pCurve, oTwigParent				' AND "FREEZE ALL"
			If FreezeAll = False Then ApplyDeformByCage CStr(oCurve) & ";" & CStr(pCurve)					' I FIND IT SLOPPY THE WAY THEY ARE SORTED
		Else																		' BUT IT WORKS FOR NOW...
			ApplyDeformByCage CStr(oCurve) & ";" & CStr(pCurve)
		End If
	Next

	DeleteObj Tracer

End Function

'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' GENERATE A CHAOTIC NUMBER
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Function VarianceNumber(Chaos, ChaosSeed, OrgChaos, ii)
	
	Randomize ChaosSeed
	If OrgChaos = True Then
		If ii Mod 2 = 0 Then															' WHEN ORGANIZED CHAOS IS ON:
			VarianceNumber = Round(((Chaos * Rnd) * Rnd),4)										' CHAOS GOES POSITIVE THEN NEGATIVE THEN
		Else																		' POSITIVE AND NEGATIVE AND ON AND ON...
			VarianceNumber = Round(((Chaos * Rnd - Chaos) * Rnd),4)
		End If
	Else
		If Round(Rnd()) = 0 Then
			VarianceNumber = Round(((Chaos * Rnd) * Rnd),4)										' WHEN ORGANIZED CHAOS IS OFF:
		Else																		' CHAOS IS COMPUTED RANDOMLY
			VarianceNumber = Round(((Chaos * Rnd - Chaos) * Rnd),4)
		End If
	End If
End Function

'-----------------------------------------------------------------------------------------------------------------------------------------------------------
' EXTRUDE THE CREATED CURVE
'-----------------------------------------------------------------------------------------------------------------------------------------------------------
Function ExtrudeTwigs(FreezeAll, GeometryDialogue, oCircle, oCurve, pCurve, oTwigParent)

	Dim oExtrud, Bobo
	Set oExtrud = ApplyOp("Extrusion", oCircle & ";" & oCurve, 3, siPersistentOperation)
	Bobo = Split(Cstr(oExtrud), ".surfmsh.extrusion", -1, 1)
	CopyPaste bobo(0), , oTwigParent, 1
	SetValue oExtrud & ".ignoresub", False													' THIS ALLOWS FURTHER UV STEP INPUTS
	SetValue oExtrud & ".subdivu", 4
	SetValue oExtrud & ".subdivv", 20

	If FreezeAll = False Then
		SetExpr oExtrud & ".subdivu", GeometryDialogue & ".uSteps"									' SET EXPRESSIONS ON OBJECT'S EXTRUSION OP
		SetExpr oExtrud & ".subdivv", GeometryDialogue & ".vSteps"									' SO THEY ARE NOW ALL DRIVEN BY THE FINAL
		SetExpr oExtrud & ".startpos", GeometryDialogue & ".StartPos"								' GEOMETRY OPTIONS PPG
		SetExpr oExtrud & ".length", GeometryDialogue & ".EndPos"
		SetExpr oCircle & ".circle.radius", GeometryDialogue & ".Radius"								' ... AND THE CIRCLE RADIUS ALSO.
		ToggleVisibility oCurve															' HIDE THE CURVES, ITS CLEANER
	Else
		'Refresh
		FreezeObj(oExtrud)
		DeleteObj oCurve
	End If

End Function



