1 Tutorials LightWave 3D Layout commands and UDF (User Defined Functions) Qua Jan 26, 2011 3:13 am
Admin
Admin
by Emanuele Salvucci, Revolution Software Ltd. |
Overview In this basic tutorial we'll learn how to use Layout Commands in LScript and we'll define our custom function (UDF) in the script as an exercise. There's no real need to call a UDF in this example, but you'll generally have "clean" code, easier to debug, and you can also create a sort of library with your custom functions. The script will show and hide some elements in Layout whenever it's executed. It'll act like a switch so that we can bind it to a hotkey. Write down the logic first It's certainly not a complicated script but I think it's good habit to write down a logic path of what your script will do. So let's decide which elements we're going to show/hide:
The logic to show/hide these components is pretty simple, and it'll be entirely covered by the CommandInput ( ) command. Syntax: CommandInput ( "Layout_command_name arguments") Example: CommandInput ("ShowMotionPaths"); //no arguments needed for this command This will show/hide Motion Paths in Layout. "ShowMotionPaths" is a Layout Command you can find in the command list. To obtain a command list simply type SaveCommandList in LS Commander , execute and save the .txt file. If we want to show/hide nulls as well things are a bit less linear. Nulls are included in the Mesh Object Agent (Chapter 6, LScript Reference Manual) and since normal meshes are also identified with the same Agent we have to search for nulls and show/hide just them. Logic:
Since our script is a switch it's based on initial conditions of the components we're working on. So, for instance, if you set in your scene "Show Cages = off" and "on" for all the other options, when you run the script you'll have "Show Cages = on" and "off" for all the other options. The code Here we'll translate our logic in LScript language. This first version is the easiest way to do what we want, but after this we'll try using a UDF. Let's start with some statements for the preprocessor: @version 2.3 @warnings @name ShowHide These statements are called pragma directives and you can learn more about it in Chapter 9 of your LScript User Manual. For the moment we just need them to make our script work. Our script is a "generic" script so: @script generic Generic scripts are executed by Layout exclusively in the main generic { } function: @version 2.3 @warnings @script generic @name ShowHide generic { // code here } Now we have to check if there're meshes in the scene and in order to do that we have to create a Mesh Object Agent: mesh = Mesh (); This line assigns the representation (agent) of the object in the variable "mesh". When you call the Mesh () agent without arguments (void) it'll return the agent of the first object in the scene. If there're no objects in your scene the variable "mesh" will be equal to "nil", so it'll be "empty". if (mesh) { // code } That means "If the variable mesh exists (is not "nil") do ..." This is just to check that we're doing operations on something that exists. Outside our condition we'll write the code we're sure we have to execute: the CommandInput sequence: CommandInput ("ShowMotionPaths"); CommandInput ("ShowHandles"); CommandInput ("ShowIKChains"); CommandInput ("ShowCages"); So now we have: @version 2.3 @warnings @script generic @name ShowHide generic { mesh = Mesh (); if (mesh != nil) { // code } CommandInput ("ShowMotionPaths"); CommandInput ("ShowHandles"); CommandInput ("ShowIKChains"); CommandInput ("ShowCages"); } Now we have to cycle through objects in the scene in our condition: for (i=1; mesh != nil; i++) { // code mesh = mesh.next(); } This is a pretty standard way to cycle through objects with a "for" loop. The next() method returns the next object in the scene and we store it in the variable "mesh". When the next() method reaches the last object, another next() method will return "nil" in our "mesh" variable and the loop will stop. Since we're evaluating every object in the scene one by one in the "for" loop we can also check if the current object is a Null object: for (i=1; mesh != nil; i++) { if (mesh.null) { // code } mesh = mesh.next(); } The ".null" data member returns a boolean true if the current object agent ("mesh") is a Null, a false if it's not. We'll use another Layout Command to show/hide Nulls, but we have to select the current Null first: for (i=1; mesh != nil; i++) { if (mesh.null) { mesh.select(); // code } mesh = mesh.next(); } We used another method, select() on our Object Agent to select the object it represents. This method selects an object in Layout as we selected it manually through the interface. Now we've just check the visibility status of our current Null and decide if hide it or show it: for (i=1; mesh != nil; i++) { if (mesh.null) { mesh.select(); if (mesh.visibility == VIS_HIDDEN) CommandInput ("ItemVisibility 3"); else CommandInput ("ItemVisibility 0"); } mesh = mesh.next(); } The ".visibility" data member contains the visibility status for the current Object Agent. "VIS_HIDDEN" is a Layout constant and it equals 2, so you could also write: "if (mesh.visibility == 2)" But it's less readable of course. Other constants for the ".visibility" data member can be found reading the ".visibility()" method for the Scene Object Agent in Chapter 11 / Methods of your Reference Manual. The "ItemVisibility" command sets the status for the current object. The argument equals to the position in the Popup Menu in the SceneEditor: 0 = Hidden 1 = Bounding Box 2 = Vertices 3 = Wireframe etc. For the "ItemVisibility" command the argument can only be a numeric value. So, now our script looks like this: @version 2.3 @warnings @script generic @name ShowHide generic { mesh = Mesh (); if (mesh != nil) { for (i=1; mesh != nil; i++) { if (mesh.null == true) { mesh.select(); if (mesh.visibility == VIS_HIDDEN) CommandInput ("ItemVisibility 3"); else CommandInput ("ItemVisibility 0"); } mesh = mesh.next(); } } CommandInput ("ShowMotionPaths"); CommandInput ("ShowHandles"); CommandInput ("ShowIKChains"); CommandInput ("ShowCages"); } Save it as a ".ls" file and add it as a plugin in Layout. Assign it to a hotkey and switch visibility on the fly! User Defined Functions Just to demonstrate the use of UDF we'll create a function to show/hide Nulls inside the script. A UDF looks just like any other LScript function (CommandInput() for instance) but it's defined and called within the script. In our script the code that show/hide Nulls is: if (mesh.null == true) { mesh.select(); if (mesh.visibility == VIS_HIDDEN) CommandInput ("ItemVisibility 3"); else CommandInput ("ItemVisibility 0"); } So we define our UDF as ShowHideNulls ("NULLS"): ShowHideNulls: mesh { if (mesh.null == true) { mesh.select(); if (mesh.visibility == VIS_HIDDEN) CommandInput ("ItemVisibility 3"); else CommandInput ("ItemVisibility 0"); } } Our UDF is asking for the variable "mesh" when it's called from within another function (generic{} in our script). Remember that "mesh" contains the Object Agent of the current mesh. What should we do now is call our function and pass it the variable "mesh" as requested and the script will become: @version 2.3 @warnings @script generic @name ShowHide generic { mesh = Mesh (); if (mesh != nil) { for (i=1; mesh != nil; i++) { ShowHideNulls (mesh); mesh = mesh.next(); } } CommandInput ("ShowMotionPaths"); CommandInput ("ShowHandles"); CommandInput ("ShowIKChains"); CommandInput ("ShowCages"); } ShowHideNulls: mesh { if (mesh.null == true) { mesh.select(); if (mesh.visibility == VIS_HIDDEN) CommandInput ("ItemVisibility 3"); else CommandInput ("ItemVisibility 0"); } } It is obviously exactly the same, apart from the fact that we're calling one function in our "main" generic{} function, instead of doing there all the operations. If it was a more complicated script and you had to do the same operations many times within different parts of the script, you just had to call your UDF when you needed it. Useless in this simple script but useful to know if you want to experiment with more complicated scripts. Emanuele Salvucci @ Revolution Software Ltd. contact me at [Tens de ter uma conta e sessão iniciada para poderes visualizar este link] |