[SOLVED] Scripted controller self reference mystery on animated spline


#1

I am playing with scripted controllers . According to the help files I am able to set the text input of a textobject by adding a scripted controller to the kerning controller of said textobject

b=box name: "ControlBox" wirecolor:blue
t=text name: "ControlledText" wirecolor:red
t.baseobject.renderable=true --set the shape to renderable
theCtrl = float_script() --create a float script controller
theCtrl.addNode "TheText" t --add a variable, connect to theText node
theCtrl.addNode "TheBox" b --add a variable, connect to the Box node
--Set the expression to assign the height of the box as string to the
--.text property of the text shape, then return 0 on the next line:
theCtrl.SetExpression "TheText.text = TheBox.height as string\n0"
t.kerning.controller=theCtrl --assign the controller to the kerning

So far so good, this all works as explained.

However, when I try to control the points of a spline by adding a float_script() to one of the variables, say the angle controller of the spline, and then reference the spline itself in this script to set the points I get an “Illegal self reference in scripted controller” warning. See code and variables below.

knotCount = numknots cableObject 1
for i=1 to knotCount do
(
	knotPos = pointA.pos + (((pointB.pos - pointA.pos)/(knotCount-1))*i-1)
	setknotPoint cableObject 1 i knotPos
)
updateShape cableObject
)

where the variables in the script controller are:

cableObject = my line 
pointA = 1st helper
pointB = 2nd helper

Not very surprising you would say, but why does that message not show up on the textobject example? A similar self reference is made there in the line 5 and 10!

I’ve been playing with this for two hours now, but I cannot seem to get it working properly. I can accept the self reference limitation, and I can work around that I’m sure. But if there is an explanation or a loophole somewhere I would love to know about it


#2

You can get self boject by function, and operate whith them after

fn fn_GetSelf =( (refs.dependentNodes (refs.dependents this)[1])[1] )
_self_ = fn_GetSelf ()


#3

Unfortunately, that is not the case for settings spline knots on a renderable spline. Wrapping up a quick script attached to the angle parameter of the spline will give the illegal self reference warning. I think this is because the value needs to be evaluated for renderable spline radial values to work.

fn fn_GetSelf =( (refs.dependentNodes (refs.dependents this)[1])[1] )
_self_ = fn_GetSelf ()

setknotPoint _self_ 1 1 pointA.pos
setknotPoint _self_ 1 2 pointB.pos
updateShape _self_
0

However, when I set this script to the angle2 variable, which is used by the rectangular renderable spline shape, the script works, but crashes when I add modifiers to the spline.

You see, there seem to be some halfway loopholes, and I am wondering if there is more information about this.


#4

for example… how to use it in scripted controller

controller example - from Autodesk help

plugin Point3Controller Point3Controller_example
name:"Point3Controller Example"
classID:#(0x47db14ff, 0x4e9b5f9f)
(
	parameters pblock 
	(
		offset_value type:#point3 animatable:true 
		valueController type:#maxobject subAnim:true
	)

    fn fn_GetSelf =( (refs.dependentNodes (refs.dependents this)[1])[1] )

	on getValue do 
	(
		valueController.value + offset_value
	)
	on setValue val relVal commit do 
	(
            _self_ = fn_GetSelf ()
		val -= _self_.scale --- this for example
		SetControllerValue valueController val commit #absolute
	)

	on create do
	(
		valueController = NewDefaultPoint3Controller()
		isLeaf=true
	)
)

#5

Ok, I see that working yes. Thanks for replying so far.

Maybe I should have clarified my challenge. I am trying to modify a spline in such a way that it is changing shape according to a scripted controller and remains renderable.
Since this should also work in the renderfarm I am trying to avoid having to use scripted plugins, as they are not saved in the scene and I would have to distribute them across all render slaves. At least, this is what I am assuming.

Are there ways around the illegal self reference issue outside of using scripted plugins?


#6

But why script controller?
There’s MasterPointController in Spline and all verts can and will get (at least) pos. controllers if you decide to animate those.

There’s also Spline IK Control Modifier for animating verts thru other (helper) objects…


#7

Yes, those are valid options too, although animating the spline IK controls would require me to add helpers to the scene which might add up real quick on multiple splines. And as we know, helpers are a bit slower in the viewport.

Never played with MasterPointController though, so that might be interesting.

I still think it doesn’t make sense that, as I mentioned in my first post, the self reference works on the textobject example, but doesn’t on my spline example. What is the difference there?


#8
---- create spline with any number of knots (set tangents to corner)
---- create 2 dummies
---- names "Line001" "Dummy001"  "Dummy002" 



splCA = attributes splCA 
(
	parameters main
	(
		pnts type:#NodeTab TabSize:2
	)

	fn fn_GetSelf =( (refs.dependentNodes (refs.dependents this)[1])[1] )

	fn updSpline =(
		self_ = fn_GetSelf()
		knotCount = numknots self_ 1
		for i=1 to knotCount do
		(
			knotPos = pnts[1].pos + (((pnts[2].pos - pnts[1].pos)/(knotCount-1))*i-1)
			setknotPoint self_ 1 i knotPos
		)
		updateShape self_
	)
	
)



CustAttributes.add $Line001 splCA
$Line001.pnts = #($Dummy001,$Dummy002)

$Dummy001.scale.controller = scale_script()
$Dummy001.scale.controller.addNode "sp" $Line001
$Dummy001.scale.controller.script = "sp.updSpline ()
[1, 1, 1]"
$Dummy002.scale.controller = scale_script()
$Dummy002.scale.controller.addNode "sp" $Line001
$Dummy002.scale.controller.script = "sp.updSpline ()
[1, 1, 1]"

https://youtu.be/Q6WGcEGxol4


#9

what about something like this?

delete objects
gc()

p1 = point name:#pt_1 pos:[50,25,0]
p2 = point name:#pt_2 pos:[-50,25,0]

rect = Rectangle width:100 length:50
convertToSplineShape rect

rect.render_sides.controller = float_script()
rect.render_sides.controller.addobject "owner" (reftargmonitor reftarg:rect)
rect.render_sides.controller.addNode "p1" p1
rect.render_sides.controller.addNode "p2" p2
rect.render_sides.controller.script = "
	self = owner.reftarg
	setKnotPoint self 1 1 p1.pos
	setKnotPoint self 1 2 p2.pos
	updateShape self

12
"

#10

And yet you have this in script:

pointA = 1st helper
pointB = 2nd helper


#11

@domos
Hehe, true true, but that is mainly for testing purposes :wink:

@Serejah
That works great up until you set the spline to rendering “Enable in viewport”. The self reference issue then pops up. However, It did render when I animated the helper and only set “Enable in renderer”, so there is that to play with!

@Eugine_M
I have a very similar solution to what you have made here, using a Custom attribute with functions. However, try to render a sequence of frames and you will notice that the spline is only rendered in it’s initial state. As far as I know, custom attributes are not re-evaluated after render start :frowning: (not sure if that is the right terminology, but it’s something like that)
I know this is another issue I didn’t mention before, but one that I ran into and started this whole self reference exploration. If you know a way to force an update of the spline on each rendered frame, then that would solve a lot of my problems.


#12

I not worked with renders at all. So yes, CA not update by render sequence (some kind of bug)… i think good idea - is bake spline animation when you need render. just put function in CA and call it when you need bake (in prerender callback or by hands). after you can do same to clear keys.


#13

I also tried adding a separate renderable spline modifier to your sollution but that causes a crash.

I think what you suggest is the best solution at the moment yes. I’ll try and see if I can wrap up a bake spline animation into the CA


#14

What if you disable ref messages for a moment of shape update? No errors now, but still not working with renderable spline mod

(
	delete objects
	gc()

	p1 = point name:#pt_1 pos:[50,25,0]
	p2 = point name:#pt_2 pos:[-50,25,0]

	rect = Rectangle width:100 length:50
	convertToSplineShape rect

	animate on
	(	
		at time 30f
		(
			p1.pos   = [100,55,0]
			p2.pos   = [-50,-50,0]		
			rect.pos = [15,25,-50]
		)
	)

	rect.render_displayRenderMesh = true
	rect.render_sides.controller = float_script()
	rect.render_sides.controller.addobject "owner" (reftargmonitor reftarg:rect)
	rect.render_sides.controller.addNode "p1" p1
	rect.render_sides.controller.addNode "p2" p2
	rect.render_sides.controller.script = "
		self = owner.reftarg	
		setKnotPoint self 1 1 p1.pos
		setKnotPoint self 1 2 p2.pos
		disableRefMsgs()
		updateShape self
		enableRefMsgs()

	12
	"

)

#15

Ah, but that is one step closer to what I need. Funny to see the message below it in the help files though, “USE WITH EXTREME CAUTION” :slight_smile:

But that whole chapter in the help file is interesting. I wonder if “notifydependents” could be used to force an update somehow. Not enough time to fiddle at the moment.

I was just now trying to add an attribute holder modifier and set some attributes for the spline animation behaviour, but the original object spline doesn’t update properly then. Just as you mentioned above with the renderable spline modifier.

Still really appreciate you guys thinking along here, I feel like I’m getting close to what I need


#16

This is not the good way to animate splines (bind spline knots).
The best solution is to use the SimpleSpline plugin, but it was not available for earlier versions of MAX.

As an old school solution I would recommend something like this:

delete objects
with redraw off
(
	global sp = line name:#spline wirecolor:green
	sp.vertexTicks = on
	addnewspline sp

	numpts = 3
	
	pp = for k=0.0 to numpts - 1 collect
	(
		pos = [k*40,0,0]
		addknot sp 1 #smooth #curve pos 
		t = 1 - k/(numpts-1)
		col = orange * t + yellow * (1 - t)
		p = point name:(uniquename "p_") pos:pos wirecolor:col	
	)
	
	updateshape sp

	animatevertex sp #all
	
	master = sp.baseobject[#master] 
		
	for k=0 to (numknots sp 1) - 1 do
	(
		c = master[k*3+1].controller = point3_xyz()  
		c = master[k*3+3].controller = point3_xyz()  

		s = master[k*3+2].controller = point3_script() 
		s.addnode "self" sp
		s.addnode "target" pp[k+1]
		s.setexpression "if isvalidnode target and isvalidnode self then (target.pos * self.transform) else [0,0,0]"
	)
)

(as it was already suggested above)


#17

@denisT and @domos

you guys were right, animating the knots is a very good way to properly get an animated spline rendered. I went for a solution with a stored #prerender and #postrender callbacks that will animate the keys upon start of render, and will remove the keys once the render is done.

I might go for a more compacted approach and set the keys directly as you suggest above Denis, but for now I will go with this setup.

Thanks for all the help all of you