PDA

View Full Version : Controllers and their values?


Bercon
12-06-2006, 10:13 AM
Okey, so here is a little test. Open new max scene, open maxscript listener and here is the syntax:


myCont = bezier_float()
Controller:Bezier_Float
myCont.value = 10
10
setPropertyController meditMaterials[1] #opacity myCont
Controller:Bezier_Float
myCont.value
1000.0

Value somehow magically jumps from 10 to 1000 when I assign the controller to Standard material.

Could somebody explain my why does max do this and how to avoid it? I'm trying to code schematic material editor and if connecting controller to material results this kind of errors its hopeless.

- Jerry

josh3light
12-06-2006, 11:02 AM
That could be one serious serious bug. Hope that Bobo can be asked about this one.
I thought it was multiplying it by the current value, but if the controller value is 0 and the opacity value is 20, then the controller value jumps to 20 after connecting.

Strange strange.

Edit: Though there's no excuse for that bug, there's a way to temporarily work around it (maybe not very efficient, but it gets the job done)

Store the current mycont.value in a temporary variable, make the controller connection, then put the variable back in the value. Will it work for your purpose?

Also really looking forward to the SME, keep up the good work! I'd also like to see a schematic controller/ list controller setup, then we'd have something closer to houdini CHOPS :)

Bercon
12-06-2006, 01:27 PM
Heh, I wish it was that easy. Unfortunately it does it for script controllers too. So if I have script controller which calculates sin() based on time, its output value is multiplied by 100 too! I mean.... come on. How the hell I'm supposed to get correct results with this when it messes up even my script controllers?

Bobo
12-06-2006, 03:08 PM
That could be one serious serious bug. Hope that Bobo can be asked about this one.


First of all, it is not a bug, it is how Max works.
When you assign a controller to a track, Max does everything possible to KEEP the existing value. So there is no need to try to preserve it unless you are assigning a script or expression controller and want to include the original value in some form. The script and expression controllers will still preserve the existing value as the default expression if the controller is "virgin", but you might want to overwrite it with your own. In versions prior to Max 8, the script controller would always try to keep the old value. Now the controller has a flag that tells the system whether the controller is new or not. There must be a note in the Reference somewhere about it ;)

Now in this case, these tracks in the material editor are NOT Float tracks.
Run this:

str = stringstream ""
showProperties meditmaterials[1] to:str
seek str 0
while not (eof str) do
(
txt = readline str
if matchPattern txt pattern:"*percent*" do format "%\n" txt
)

Result:

.opacity : percent
.opacityFallOff (Falloff) : percent
.mapAmounts (Map_Amounts) : percent array
.selfIllumAmount (Self_Illumination) : percent
.specularLevel (Specular_Level) : percent
.glossiness : percent



As you can see, opacity, glossiness and a couple others are PERCENT tracks.
Percent tracks generally have an internal multiplier to turn a value of 1.0 to 100.0. This is mostly because the controller should be able to convert an On/Off or Boolean track which run between -1 and 1 resp. 0.0 and 1.0 into valid percentage values.

When a controller is created via MAXScript, its value is 0.0. Max takes the existing value in the percentage track and assigns it as the default value to your new controller. If you do

theMtl = meditmaterials[2]
theMtl.opacity = 50.0
theCtrl = bezier_float()
theMtl.opacity.controller = theCtrl
theMtl.opacity.controller.value

you will get 50.0 in the end because Max knows what the value was, sees that the incoming controller has no value (it is 0.0) and tries to keep the existing one.

If you do

theMtl = meditmaterials[2]
theMtl.opacity = 50.0
theCtrl = bezier_float()
theCtrl.value = 1
theMtl.opacity.controller = theCtrl
theMtl.opacity.controller.value

The result will be 100.0 because Max sees an incoming controller that DOES HAVE a value (you could be pasting an existing keyframed track from some other scene object or other matarial) and decides to use that value. But it has to make sure the value of 1.0 is converted to the PERCENTAGE range of 0.0 and 1.0 so it multiplies it with 100.0 and then OVERWRITES the existing value instead of keeping it.

Similarly, let's create a box, animate its height from 0.0 to 1.0 and instance that track's controller to a material's opacity:

theBox = box()
theBox.height = 0
at time 100 with animate on theBox.height = 1
theMtl = meditmaterials[2]
theMtl.opacity.controller = theBox.height.controller
theMtl.opacity.controller.value

RESULT: While the box grows from 0 to 1, the opacity changes from 0 to 100, USING AN INSTANCE (!!!) OF THE CONTROLLER, which means the same controller appears with two different values in the same scene thanks to the conversion to Percent!


Now try this:

theMtl = meditmaterials[2]
theMtl.opacity = 50.0
theCtrl = on_off()
theMtl.opacity.controller = theCtrl
theMtl.opacity.controller.value

The result is once again 100.0 because an On/Off controller can be either -1 or 1. The material has a non-zero opacity, the controller can only be on or off, so it assumes it means On. On would be 1.0, but for a percentage track, it gets multiplied by 100.0 and the result is 100.0.

theMtl = meditmaterials[2]
theMtl.opacity = 0.0
theCtrl = on_off()
theMtl.opacity.controller = theCtrl
theMtl.opacity.controller.value

Now the opacity is set to 0 which for an On/Off means it should be OFF. Guess the result?
It will be -100.0, because On/Off controllers in TrackView would have the value of -1, but the track is a percentage so it gets multiplied by 100.0 and the result is -100.0.

This has been the way Max was designed in version 1.0 (and before that ;)). When MAXScript came along in 2.0, it had to play along with the existing system.

Interesting, eh?

It is a BAD IDEA to assign a value to a controller BEFORE assigning it to a percentage track, because incoming values between 0.0 and 1.0 will be automatically multiplied by 100.0 to ensure they mean percents. The UI will clamp the result to 0.0 to 100.0, but the internal values can be anything.

In short, if you want a Bezier_Float() to not be multiplied by 100.0 when assigned to a Percentage track, don't change its value before assigning. You can change the value AFTER the assignment and it will use that.


Hope this helps.

PiXeL_MoNKeY
12-06-2006, 03:11 PM
It's not a bug. It is doing the conversion from a float to a percentage. <Standard>.opacity Float default: 100.0 -- animatable, percentageThis is all documented in the MaxScript Reference under the topic of "Working with MAXKey Values".

-Eric

Bercon
12-06-2006, 03:41 PM
Thanks for the explanation Bobo! :thumbsup: That explains it. I'll probably make some sort of percent node then to convert floats and stuff to percents. :)

PiXeL_MoNKeY
12-06-2006, 03:47 PM
Or can you convert it once you connect it to a percentage input? That way any channels that uses scaling can be handled without having to ad a specific node.

-Eric

Bobo
12-06-2006, 03:52 PM
Thanks for the explanation Bobo! :thumbsup: That explains it. I'll probably make some sort of percent node then to convert floats and stuff to percents. :)

When writing a Script controller for a sine curve to assign to a percentage track, just use a range of -1.0 to 1.0. The track will do the translation for you:
theCtrl = float_script()
meditmaterials[1].opacity.controller = theCtrl
theCtrl.script = "sin F"

If you want to move this to 0.0 to 100.0, you can say

theCtrl = float_script()
meditmaterials[1].opacity.controller = theCtrl
theCtrl.script = "(1.0 + sin F)/2.0"



meditmaterials[1].opacity --> returns 50.0 on frame 0
meditmaterials[1].opacity.controller.value -->returns 0.5 on frame 0,


When reading from the Scripted controller, you will actually get the correct value calculated by it, but reading the opacity itself will be scaled by 100.0. This is because the Script controller is a procedural one and it does not have keys to be scaled, so Max cannot really change it. It only changes the value passed to the property to make it a percentage.

With keyframeable controllers, Max applies the scaling to the keys so in the TrackView you would see a key at value 100.0 where the internal value of the controller was 1.0. When you move the key up and down, you are setting percentages but the controller sees them scaled DOWN internally (divided by 100.0) so in the example with the box, the box' height would still be between 0.0 and 1.0 while the Opacity would be between 0.0 and 100.0...

josh3light
12-07-2006, 04:24 AM
wow, thanks for clarifying that Bobo

Bercon
12-07-2006, 10:41 AM
I think it still works incorrectly with script controllers. Really weird. Here is an image to explain my problem.

http://www.niksula.cs.hut.fi/%7Ejylilamm/graphics/BugOrNot.jpg

So. The script output is pretty distrubing. IF returns TRUE for smeOps.nodeData[4] == rootNode.controllers[8], so its logical to think that they are the same object? No?

But, how come they can have different values if they truly are the same object?

Bobo
12-07-2006, 03:11 PM
So. The script output is pretty distrubing. IF returns TRUE for smeOps.nodeData[4] == rootNode.controllers[8], so its logical to think that they are the same object? No?

But, how come they can have different values if they truly are the same object?



Did you see my example with the Box Height? Same controller, different values.
Are you reading the track value or the controller value from the script controller? It appears that the track will be *100, but the .controller.value from a scripted controller should be unchanged...

Bercon
12-07-2006, 07:21 PM
Okey, I get it. But I think its still kinda upsidedown. If I call myCont.value I'll get the value multiplied by 100.0, but if I call the actual value of the opacity track I get the real value of the script controller. Like this:

myCont = float_script()
myCont.script = "1"
setPropertyController meditMaterials[1] #opacity myCont
format "myCont.value: %\n" myCont.value
format "meditMaterials[1].opacity.controller.value: %\n" meditMaterials[1].opacity.controller.value
myNull = bezier_float()
myNull.value = 5.0
setPropertyController meditMaterials[1] #opacity myNull
format "Second round:\n"
format "myCont.value: %\n" myCont.value
format "meditMaterials[1].opacity.controller.value: %\n" meditMaterials[1].opacity.controller.value

Output will be:
myCont.value: 100.0
meditMaterials[1].opacity.controller.value: 1.0
Second round:
myCont.value: 100.0
meditMaterials[1].opacity.controller.value: 500.0

So it doesn't just the change output value of the script controller when it is connected to opacity track, but it changes it permanently! The script controller never seems to be the same again :sad:

Is there any way around this? So I could keep my original script controller intact.

josh3light
12-07-2006, 09:37 PM
when you need to plug any controller into any slot that requires percent, could you plug in a secondary node in it's place on the fly to absorb the conversion, then plug your original controller into that secondary node? That should keep the original value and if done elegantly could be transparent to the user.

Bercon
12-08-2006, 11:34 AM
Yeah, I think thats the best way. Now that I store the cont in rootNode and to the nodaData array and then use nodeData arrays "version" to connect it to opacity track the rootNode "version" seems to retain its original value and is unaffected by the change. So I'll just use rootNode's "version" everywhere else but when I wish to connect the controller somewhere (map/material) I'll use the nodeData version.

CGTalk Moderation
12-08-2006, 11:34 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.