In the past tutorial we tried to talk about scripting in 3ds max creating a custom scripted shader plug-in, this time we continue our adventure creating a little utility that can select an object and reverse a part or the entire position animation applied. The utility is very simple and we’ll use it to understand how to interact with the scene, with the animation, and to know something about variables, array etc etc. An other simple example that can help to understand scripting in a simple and useful way. Let’s start with this exercise having a little view about the code we’ll going to write.

Step 02

To create a piece of code and to solve some problem, it needs to follow a way of working. To solve the problem and to find the right code to be written, you need to understand well the situation, and understand what you need as final result, then you can try to figure how in the real world you can achieve it, and then you can translate it in real programming language. It’s very simple, if someone will try to solve directly the problem writing code, maybe he’ll be able to achieve the right result but it’s possible to lost more time or to add more bugs. A bug is an error generation created by the software due to a logical or programming error done by the user, so if you spend more time at the beginning you’ll lost less time later to fix it. So let’s start talking about the final result we’d like to achieve. The problem is : “Finding a fast way to select a mesh or a light or a camera or whatever object, and reverse automatically its position animation in a given time range”. So let’s try to subdivide the problem in a step by step list :

  1. we have to find a way to select the object,
  2. we have to store the position animation of the picked object in memory,
  3. then we have to reverse the animation and to save it on the selected object previous animation

As you can see the problem can be solved with no too many steps.

Step 03

Let’s start to create a utility using MAXScript and then step by step we’ll see how every single task will be solved with the code. A utility in MAXScript is a piece of code with its own custom interface, is something like a plug-in, and it can stay opened on the monitor waiting for the user action, and in the meantime that it is opened the user can do whatever he wants, so the utility can’t stop the 3ds max execution and can wait for the users giving no problems. During the tutorial you’ll notice that some parts of it will be very similar to the Xray Scripted Shader, and that’s normal, because we’ll create the interface in the same way, only the typology of the script is different so we have not to write some sentence to declare it, because it will not be visible as a real plug-in. Basically to define a utility you have to create and describe the interface roll out, writing object by object what can happen when the user interact with it, and finally you have to add the roll out so the utility will be created and it will be 100$ working. So the structure will be :

rollout choosen name “rollout title text” width:??? height:???

(

– here you have to put all the interface controls you need, spinner, buttons, etc etc

– and here you need to put all the events can happens to describe what the software will do when the user interact with its controls

)

floater name = newRolloutFloater “floater title bar text” x lenght y height

addRollout name floater name

So this is the basic utility structure, let’s start writing our utility.

Step 04

In the figure you can see the 1st
part of the script, let’s check it line by line

Line 1 : rollout rev “AnimVerse v.0.1 – (C) A.Cangelosi for CGCookie” width:266 height:300

In Line 1 we are creating a roll out called “rev” with the title text and we choosen the width of 266 pixels and the height of 300 pixels

Line 3 : pickButton btn1 “Pick Object” pos:[53,37] width:155 height:20

In Line 3 we are creating a button that gives the possibility to pick an object from the scene, the name of the button is “btn1”, over the button we’ll find written “Pick Object”, then we chosen the position, the width and the height like before

Line 4 : spinner spn_start “Start frame ” pos:[51,68] width:106 height:16 range:[0,10000,0] type:#integer

In Line 4 we are creating a spinner control called “spn_start”, over the interface can be found because its label is “Start frame”, then we set the position, width and height. For the spinner you can set also the value range, in our case 0,10000,0 that means, the minimal value is 0, the maximum value is 10000 and the default value is 0. The value type is integer number and can be set with “type:#integer”. With this spinner we’ll choose the start frame of our reverse action.

Line 5 :  spinner spn_end “End frame ” pos:[51,94] width:97 height:16 range:[0,10000,100] type:#integer

As I wrote for Line 4, here we are creating a spinner to control the end frame of the reverse action. We have only a difference for the default value of the spinner, we choosen frame 100. But it can be changed here or when the utility will be executed.

Line 6 : progressBar pb1 “ProgressBar” pos:[14,255] width:236 height:11

In Line 6 we create a progress bar control. It’s useful to show the percentage done of a given task, literally we chosen the name, “pb1”, its position, width and lenght.

Line 7 : label lbl1 “Reverse progress” pos:[14,237] width:180 height:17

This line is very simple. We put a label with the text “Reverse progress” over the progress bar…nothing much more. You can see it later in the interface.

Line 8 : button btn2 “Acquire it !” pos:[76,144] width:111 height:33

Now it’s time to add some button useful to choose what the utility has to do. With this line we create a button named “btn2”, with inside the text “Acquire it !”, and with a given position, width and height. This button will be used by the user to activate the acquisition of the animation data from the picked object.

Line 9 : button btn3 “Reverse it !” pos:[76,180] width:111 height:33

Like line 8 but this button is for the “reverse animation” action. It will used to reverse the animation after the user used the “Acquire it !” button.

Line 10 :  groupBox grp1 “Object properties” pos:[10,12] width:242 height:117

With line 10 we are creating a group interface that will contains all the other controls, it’s only a stylish thing.

Line 11 : label lbl2 “AnimVerse v.0.15 – (C) A. Cangelosi for CGCookie” pos:[8,282] width:248 height:12

Line 11 is a simple text put over the interface to show some information about the utility.

Step 05

Let’s talk about the 1st event. In the figure you can see the “btn1” event. Let’s describe it line by line :

Line 15 : on btn1 picked obj do

In Line 15 we start with the event definition, so when the user picked the object do something…you can see “on btn1 picked..” that is the action, “obj” is a variable used by 3ds max to store the picked object name, “do” literally is do the action listed below…

Line 16 : (

Every piece of code needs to be braked by “(“ and “)” so that’s the meaning of line 16.

Line 17 : — Pick the object animation to be reversed

In line 17 we have only a simple comment description

Line 18 : global ob_sel = obj

In line 18 we store the name of the picked object capturing it from “obj” variable and sending it in the “ob_sel” variable. The “global” sentence gives to the variable the possibility to be used by every single event without loosing the name stored inside it.

Line 19 : print ob_sel

In line 19 we encounter a little tricks used to understand if something happens or not when the action has been executed, how you can be sure that the picked object name has been transferred in the “ob_sel” variable ? You can simply print its value ! So in the left/down corner of 3ds max, you can see a white text bar, you’ll find it here ! This line can be removed with no problems, it is useful only for checking stuffs.

Line 20 : )

In line 20 we close the event.

Step 06

Let’s continue with the btn2 press event. What will happen if the user will press the button “Acquire it !” ? Let’s check it line by line :

Line 22 : on btn2 pressed do

As seen before, with this line we open the event, this time it’s not a picking action, but only the “press” event, if the user will press this button all the action listed below will be executed.

Line 23: (

Here we open the event code.

Line 24: – Upload all the position information of the picked object in a array

This line is only for comment purposes.

Line 25 : lbl1.caption = “Capturing animation data…”

If the button will be pressed, we change the text written over the progress bar in “Captuing animation data..” until the task will be over.

Line 26 : for i=0 to spn_end.value do

This is the 1st time we use the “for” cycle. A cycle is a series of action that will be done until a condition will be true. So If we want to do 100 times something, we can write “For i=0 to 100 do”, where “i” is a simple counter that is automatically incremented every single time that the cycle goes in execution, when i=100 the cycle will be over and the software continues with the other actions. In our case we want to acquire the position of the picked object for every single frame of the animation, so starting from frame 0 until the last frame set by the right spinner, (remember that in this test sample we are not using the “start frame” spinner, so we can start acquiring every time from frame 0, it’s done to let you trying to expand the script adding the right event and changing the code to use also this spinner). In line 26 we are saying that the action listed below will be executed until the i value will be the same of the “end frame” spinner.

Line 27 : (

Here we open a new piece of code

Line 28 : sliderTime = i

In line 28, we move the time slider to a given frame number, this number is the “i” value. So every time the cycle will be executed we move frame by frame until the end frame.

Line 29 : j = i + 1

In line 29 we create a personal counter named “j”, it’s the “i” value summed to the number “1”. This is a very simple tricks. We have to store all the position data, so we need a container to do it. To do these kind of stuffs we can use the array. The array can contains data, you can store in it, and you can load data from it. In our case we’ll create 3 array, (X,Y,Z coordinates). You can imagine the array as a train, the train is a collection of wagon with people inside, the people is our data, the train is the array, the wagon is the index position inside the array. You can move in the array choosing the data you need using the index. The frame number in 3ds max, starts from 0, but the array position index starts from 1, so that’s why we are creating “j” value. “j” will be the array index position, so if we start from frame 0 the array index will be 1.

Line 30 : animation_camera_X[j]= ob_sel.position.x

In this like we get the x position of the picked object and we store it in the “j” position of the array containing the X position value.

Line 31 : animation_camera_Y[j]= ob_sel.position.y

In line 31 we do the same operation for the Y position.

Line 32 : animation_camera_Z[j]= ob_sel.position.z

And here for the Z position.

Line 33 : pb1.value = 100.*i/spn_end.value

When the action is done we have to update the progress bar percentage status, so we have to set the percentage status. To do it we have to make a little mathematic operation to find the percentage : 100 is the total percentage, “i” is the actual frame, we have to make (100*actual frame) / total amount of frames, and we can find the actual percentage of task done. Then we have to put this value in the progress bar.

Line 34 : )

Here we close the code of the “For” cycle.

Line 35 : sliderTime = 0

We can move back to the frame 0.

Line 36 : lbl1.caption = “Animation stored…”

And here we write over the progress bar that we finished to acquire the animation.

Line 37 : )

Now we can close the event code.

Step 07

We are near the end of the tutorial. Now we have to reverse the animation and to write it over the picked object. Let’s do it step by step :

Line 38 : on btn3 pressed do

Like before, we set the type of event, when the user will press this button it will be executed.

Line 39 : (

We can open the event code

Line 40 : – Download all the animation data in reverse mode starting from the end position of the array

And here we can put some comments like always

Line 41 : Animate on

To animate the picked object, we can simulate the manual animation action, so we can turn on the “Animate” button. From now all we do is stored as animation.

Line 42 : lbl1.caption = “Reversing animation data…”

As we done before, we can write what we are doing over the progress bar.

Line 43 : pb1.color = [255,0,0]

When we acquire the animation data the progress bar will be filled of blue color to show the percentage of task done, in this case we are writing animation, so we’d like to change it to “red” as the “Animate” button in 3ds max.

Line 44 : f = spn_end.value + 1

In line 44 we prepare a variable called “f” with inside the “end frame” value with “1” added to it. It’s as we described before. We have to make the same thing we done before for the difference between frame numbers and array position index.

Line 45 : for i = 1 to f do

So we can start with the “for” cycle to write the new animation over the picked object. We have to get the data from the array so as you can see we start the cycle from the 1st array position, (i=1), until we arrive at the end of the array.

Line 46 : (

We open the “for” code

Line 47 : marker = f – i

In line 47, we create a “marker” variable. We need it to move to the right frame, so we start from the “end frame” and go back to the start, to make it we have to do : actual frame number = (number of frames stored – amount of frames loaded by the cycle). So if we read 20 frames from the array, and the end frame is 100, we are storing the frame number 80, and as you can see is right and very simple is like watching the animation starting from frame 100 figuring it will be the frame 0.

Line 48 : sliderTime = marker

We can move to the frame we need using the time slider bar.

Line 49 : – print marker

This like is only for checking purpose to print the number of frame.

Line 50 : with animate on

We have to write the animation just loaded over the picked object, so we have to turn on the animate button, to do it, we have to say that what will do below has to be done with the animate button turned on.

Line 51 : (

We open the code that must be done with the animate button set to on.

Line 52 : ob_sel.position.x = animation_camera_X[i]

We can set the X position getting it from the right array.

Line 53 : ob_sel.position.y = animation_camera_Y[i]

And now the Y position.

Line 54 : ob_sel.position.z = animation_camera_Z[i]

And finally the Z position.

Line 55 : )

So we close the animation writing piece of code.

Line 56 : pb1.value = 100.*i/f

We have to update the progress bar value using the same method used for the acquisition code.

Line 57 : )

We can close the code of the “for” cycle.

Line 58 : –Animate off

If the animate button will not be automatically turned off, you can use this line to do it.

Line 59 : lbl1.caption = “Reverse done.”

The task is finished and we can write it over the progress bar.

Line 60 : pb1.value = 0

And now we can reset the progress bar percentage, to be ready to reverse an other object animation.

Line 61 : )

We can close the event code.

Step 08

We can write the final piece of code to finalize the utility. In the figure you can see the last part, let’s see how it works :

Line 63 : reverseFloater = newRolloutFloater “Animation Reverse v.0.15 – 5 November 2008″ 280 330

In line 63 we create the floater. A floater is the utility interface it’s called “floater” because it can float around the screen during your normal work waiting for you. To create it we have to write : name of the floater = newRolloutFloater “utility title name” X width Y height

Line 64 : — Array initialization

This is a comment line.

Line 65 : animation_camera_X =#()

If we decide to use array in our script we have to declare and initialize it before we can access to it. So we have to write : array name = #()

and we obtain an empty numerical array ready to be used.

Line 66 : animation_camera_Y =#()

We make the same for the Y position data.

Line 67 : animation_camera_Z =#()

And for the Z position data.

Line 68 : — Rollout creation

The last comment line….

Line 69 : addRollout rev reverseFloater

Finally we have to add the roll out we created at the beginning that contains all the controls, to the floater.

Step 09

Now we can test the utility, so we can prepare a simple scene to do it. Please create a scene with a Teapot and a camera as in the figure.

Step 10

Now please move to the frame n.100, move the camera as in the figure, so we have the animation to be reversed

Step 11

Now move to the frame n.50 and set the camera position as in the figure.

Step 12

Now run the script just created, and you’ll see the interface as in the figure, click on “Pick Object” button to pick the camera, then click on “Acquire it !” button, and the utility will do its things to store the entire animation.

Step 13

When the utility finishes to acquire the data, you can click on the “Reverse it !” button to save the reversed animation. As soon as it will be finished, please try to play the animation and you’ll notice that everything has gone in the right way. So, this time we understood new techniques, we talked about array, variables, how to create a utility etc etc. So now if you want, try to add the start frame control, and try to expand it to store rotation and scale animation also, maybe you can add some checker to choose which animation needs to be reversed or not. In the next tutorial we’ll go back to some other CG related theme before working on the Underwater scene again.

purchase_btn_corrected

You must be logged in to upload images. Register

Leave a Comment