ZBrushCentral

Code Sample: Suggested Zscript Functionalities

This is a thread to discuss and show various zscript code implementations that will assist the users general workflow and/or zscript inter-compatibility.

None of the following 5 suggestions will restrict the functionality of existing zscripts, or future zscripts for that matter. So take a look and consider implementing these functionalities in your zscripts.

If you have suggestions of your own then, if possible, please submit them in a similar format, i.e What, Why, When, Description and Code.

Whenever I mention Zscripts I am referring to Zscripts as well as Zplugins. When Zplugins are mentioned Zplugins is the focus.

                                <b>[color=Orange]1.    :white_small_square:The Rebound Effect:white_small_square:</span></b>
                                
                                <b>What:</b> Switches to the previously active zscript after execution of the current zscript.
                                
                                <b>Why:</b> Assists users workflow and allows for a modular construction of zplugins.
                                
                                <b>When:</b> Whenever a zplugin's function does not require it to remain the active zscript.
                                
                                <b>Description:</b>

Zplugins that are imbedded in the Zbrush interface or use Note windows for their interface, should not remain active after completing their function but instead return to the previously active zscript. For example, the currently active zscript has its interface in the Zscript Window and then the Transform > Zspinner > SpinIt button is pressed. The SpinIt button is a zplugin and therefore becomes the active zscript but it has no function after it is run and so should return to the previously active zscript.

Projection Master, XYZadjust and the Zbrush Help button are all examples of zplugins that should implement the rebound effect. Svengali first came up with this simple but excellent idea.

                                <b>Code:</b> 
                                
                      The code is very simple. All that is required is to press the <b>Zscript &gt; Previous</b> button. After pressing the button the current zscript looses control, so you want to press it when your zscript has completed its function.
[[color=PaleGreen]Ibutton, "</span>[color=Orange]Zplugin:Misc Utilities :</span>[color=Orange]Show Polygon Count",  "Displays the number of polygons currently visible",</span>
      [[color=PaleGreen]Vardef, Polygons, 0]</span> // Variable for Mesh3DGet
      [[color=PaleGreen]If, [Iexists, Tool:Geometry:Cage],</span> //Make sure a PolyMesh tool is selected
   	  [[color=PaleGreen]Mesh3DGet, 1, , , Polygons]</span> //Get polygon count
   	  [[color=PaleGreen]Note, [StrMerge, "Number of Polygons : \CEE1E1E", Polygons]]</span> //Display polygon count
   	  [[color=PaleGreen]IshowActions, 0] // Hide the following Ipress from the user</span>
   	  [[color=PaleGreen]Ipress, Zscript:Previous]</span> // Return to previous Zscript
      ] [color=Black]// End of If statement
 </span>] // End of Ibutton
 
                                <b>Code Addendum:</b>

Zplugins that use Note Window interfaces are a problem when implementing the Rebound Effect. If the user closes the Note Window by pressing the corresponding Note Window button there is no problem, it can be detected and the previous zscript can be reloaded. But if the user presses the ā€˜Escapeā€™ key we loose all control as the zscript has ended. Until we can detect and handle a user initiated zscript abortion this will remain a problem.

To take full advantage of the Rebound Effect, zscripts must be able to remember their state during a zbrush session. See the Clever Zscript functionality below.

                                <b>2.    :white_small_square:Memory Block Naming:white_small_square:</b>
                                
                                <b>What:</b> Naming convention for memory blocks defined by any zscript.
                           
                                <b>Why:</b> To avoid memory block conflicts. 
                                
                                <b>When:</b> Whenever a memory block is defined.
                           </span>
                                <b>Description:</b>

Memory blocks are an important and very useful new addition to Zbrush. They open up the ability to quickly save settings of your zscript, parse and modify text files and load/save binary files. Memory Blocks remain active across an entire Zbrush session and as such offer a way to keep track of data from within any zscript. One zscript can keep track of where the other is by analyzing its memory block. That will of course only be true for zscripts were the author has implemented such functionality and shares that info. However this can also cause problems

If everybody uses a memory block to store their settings and calls it ā€œOptionsā€ there will be a conflict. A zscript might fail. A zscript might become buggy because another zscript is modifying a memory block with the same name. A very simple solution is to base all memory block IDs on a simple nomenclature.

                                For example:
                                
                                <b>FSoptions</b> is NOT a good Memory Block ID.
                                <b>
                           TV6_NMFS_options</b> would be an ok Memory Block ID.
                                
                                So lets examine that nomenclature.
                                
                                <b>TV</b> is based on the 2 first characters of my ZBC userid. There might be more people with the same first 2 initials but few who write zscripts.
        
        <b>6</b> is because this is the sixth zscript I made.
        
        <b>_</b> is just for readability. Include as many as you want in your memory block ID. 
        
        <b>NMFS</b> is the shortname of the zscript in question, in this case it stands for "Normal Map Flip/Swap". <b>
       
       options</b> is what you originaly wanted to assign your memory block as an ID.
                                
         The above nomenclature would pretty much elimenate any Memory Block ID conflicts. 
        
       
                                <b>Code:</b>

Even though proper naming of memory blocks should eliminate most conflicts it is still necessary to perform a standard memory block creation test. I place the following IF statement at then end of my code. You can see why in the 3rd section, Clever Zscripts.

[[color=PaleGreen]If, [MemGetSize, TV6_NMFS_options] = 0, // If size = 0 then memory block does not exist
      [MemCreate, TV6_NMFS_options, 64, 1] // create the memory block
      ,[If, [MemGetSize, TV6_NMFS_options] != 64, // if memory block is not the same size as when it was created report it.
      [Note, "Memory block size has changed. Zscript might not function properly"]
      ]
   ] //End of If statement</span>
                       <b>3.    :white_small_square:Clever Zscripts:white_small_square:</b>
                                 
                                 <b>What:</b> Storing of a zscripts critical variables and current state in a memory block.
                            
                                 <b>Why:</b>  To lessen user interaction when switching zscripts. 
                           </span>
                                 <b>When:</b> Whenever a zscript critical variable is changed.
                           
                           <b>Description:</b>
                           </span>

The Rebound Effect is not much use if the user needs to reapply all his/her settings when a zscript is reloaded. Storing a zscripts critical values in a memory block allows easy restoration of the zscripts previous state when switching between zscripts.

A zscript critical value is one that is either user assignable (a switch status, current tool, a user defined directory etc) or the state of the zscript (Activating Projection Master once enters one state, activating it again enters another).

The state of interface items generated by zplugins need not be stored. They are part of the Zbrush interface and are handled by Zbrush when switching zscripts.

                                 <b>Code:</b>

A memory block is the easiest way to store the state of a Zscript. The following code shows how to store the values of a Zscript generated switch and slider.

	  [[color=PaleGreen]VarDef, Init, 1] // used to stop the If statement below from looping
                         
                         [Ibutton, "Show values", ,
                         	[Note, [StrMerge, "Slider value: ", [Iget, Zscript:TVSlider], "   ", "Switch Status: ", [Iget, Zscript:TVSwitch]]]
                         ]
                         
                         // Define the slider and write to the memory block each time the slider value changes
                         [Islider, TVSlider, 4, 1, 0, 10, "Change fdfdsj ratio", [MemWrite, TV5_example_1, [Iget, Zscript:TVSlider], , 0] , , 100]
                         
                         // Define the switch and write to the memory block each time the switch value changes
                         [ISwitch, TVSwitch, 0, "Switch the effect of efdjsh", [MemWrite, TV5_example_1, [Iget, Zscript:TVSwitch],  , 4],[MemWrite, TV5_example_1, [Iget, Zscript:TVSwitch],  , 4]]
                         
                         [If, Init,
                         	[If, [MemGetSize, TV5_example_1] = 0, // If size = 0 then memory block does not exist
                         		[MemCreate, TV5_example_1, 8, 1] // create a memory block 8 bytes in size
                         		[MemWrite, TV5_example_1, [Iget, Zscript:TVSlider], , 0]
                         		[MemWrite, TV5_example_1, [Iget, Zscript:TVSwitch], , 4]
                         		,
                         		[If, [MemGetSize, TV5_example_1] != 8, // if memory block is not 8 bytes in size then report it.
                         			[Note, "Memory block size has changed. Zscript might not function properly"]
                         		]
                         	]
                         
                      	[Vardef, TempValue] // define dummy/temp value for the MemRead command
                            // Read values from memory block and set zscript interface items
                      	[MemRead, TV5_example_1, TempValue, , 0]
                      	[Iset, Zscript:TVSlider, TempValue]
                         
                      	[MemRead, TV5_example_1, TempValue, , 4]
                      	[Iset, Zscript:TVSwitch, TempValue]
                         
                      	[Varset, Init, 0]
                         ]</span> // End of If statement
                                <b>4.    :white_small_square:Size Matters:white_small_square:</b>
                                
                                <b>What:</b> Adjusts the height of the Zscript Window to fit the active Zscripts interface.
                                
                                <b>Why:</b>  Assists user workflow when switching between Zscripts that utilise the Zscript Window.
                                
                                <b>When:</b> Performed in Zscripts that display in the Zscript Window.
                            
                                <b>Descripton: </b>

Zscripts that display in the Zscript Window have varying interface sizes. Switching between such zscripts usualy requires the user to either increase the height of the zscript Window to view its entire interface or to decrease the height so as to not waste valuable screen space. Automatic adjustment of the Zscript Windowā€™s height would help workflow.

You should not worry about implementing this functionality while writing your zscript. You can and should wait until your Zscript Window Interface is finished before thinking of adjusting the ZW height.

                                <b>Code:</b>
                                
                                It might look slightly complicated but trust me, the implementation is easy.

As it needs to run automatically the following code needs to be placed as top level commands. A good idea is to place the variables at the start of your zscript for easy adjustment and the rest wherever you want.

The following code evaluates the height of the Zscript Window(Window ID 1006) and adjusts the position of the Zscript Window Divider(Window ID 1002) accordingly by [Iclick,ā€¦]'ing it. The extra [If,ā€¦] statements are needed to ensure the Zscript Window Divider is not moved less than 4 pixels. Moving the divider 3 pixels or less is the same as a mouse click which hides/unhides the Zscript Window.

[[color=PaleGreen]Vardef, ZwindowHeight, 58] // Set to your desired window height
  [Vardef, Init, 1] // Used for running the code below once, insert your other initialisation code in the following [If,...] statement if you wish.
  </span>[If, Init,
 	  [If, [Iheight, 1006</span>] < ZwindowHeight,
 	  [If, [Iheight, 1006</span>] > (ZwindowHeight-4),
 		 [Iclick, 1002, [IHpos, 1002]+100, [IVpos, 1002], [IHpos, 1002</span>]+100, ([IVPos, 1002]-10)] // Move Zscript Divider down 10 pixels
 	  ]
 	  [Iclick,1002, [IHpos,1002]+100, [IVpos, 1002], [IHpos, 1002]+100, (([IVPos, 1002]+[Iheight, 1006]) - ZwindowHeight)] // Set Zscript Divider to ZwindowHeight
 	  ]
 	  [If, [Iheight, 1006] > ZwindowHeight,
 	  [If, [Iheight, 1006] < (ZwindowHeight+4),
 	  [Iclick, 1002, [IHpos, 1002]+100, [IVpos, 1002], [IHpos, 1002</span>]+100, ([IVPos, 1002]-10)] </span>[color=Black]// Move Zscript Divider down 10 pixels</span>
 	  ]
 	  [[color=PaleGreen]Iclick, 1002, [IHpos, 1002]+100, [IVpos, 1002], [IHpos, 1002]+100, (([IVPos, 1002]+[Iheight, 1006]) - ZwindowHeight)] </span>[color=Black]// Set Zscript Divider to ZwindowHeight</span>
 	  ]
                      
 	  [[color=PaleGreen]Varset, Init, 0] // stops If statement from looping
  ]</span> //End of If statement</span>
  
     <b>Code Addendum:</b>

Do not concern yourself with the Zscript Window height until you are finished with your zscript. When you are finished it is simply a matter of dragging the Zscript Window Divider to your liking and adding a button that displays the current height. Remove the button when you have determined your ideal Zscript Window Heigh and then adjust the ZwindowHeight variable.

[[color=PaleGreen]Ibutton,"ZW height","Displays the current Zscript Window Height", [Note,[Iheight, 1006]], , , 'b']</span>
                                <b>
                           5.    :white_small_square:The Untouched Canvas:white_small_square:</b>
                                
                                <b>What:</b> Restores the Canvas Zoom and Pan settings.
                                
                                <b>Why:</b>  To minimize the zscript's effect on user workflow.
                                
                                <b>When:</b> Whenever the Canvas zoom and pan settings are adjusted by a zscript.
                                
                                <b>Description:</b>

Zscripts sometimes need to change the zoom and pan of the canvas in order to function properly. The most immediate example is when exporting the document( Document>Export ). In order to export an image with the original dimensions the Antialiased Half Size (AAHalf) view mode must be disabled which is usualy done by pressing Actual Size. This recenters the canvas and sets the zoom level to .5.

To make such an action transparent to the user we need to store the Canvas Horizontal and Vertical Pan as well as the Canvas Zoom level before pressing Actual Size. After all actions are performed the Canvas H/V Pan and Zoom level are restored.

This might not seem like an important function but if the user is currently editing a 4096*4096 sized document then the less zooming in/out and scrolling the better.

                                <b>Code:
                          
                          </b> 
[color=Black]//Variable definitions
                 [Vardef, Zoom]
                 [Vardef, CpanH]
                 [Vardef, CpanV]
                 
                 [Ibutton, "Zplugin:Misc Utilities :GrabFulDoc", "Grab a full size canvas texture",
                 
                 //Before performing canvas pan and zoom altering commands
                 [Varset, Zoom, [CanvasZoomGet]] // store zoom level
                 [Varset, CpanH, [CanvasPanGetH]] // store horizontal pan
                 [Varset, CpanV, [CanvasPanGetV]] // store vertical pan
                 
                 	[Ifreeze, //Stops the following commands from updating the interface
                 	[IShowActions, 0] //Stops the following commands from being shown to the user
                 
                 	 //Canvas zoom and pan altering function
                 	[CanvasZoomSet, 1] // Set zoom level for a 1:1 GrabDoc
                 	[Ipress, Texture:GrabDoc] // Grab canvas as a texture
                 
                 	//After canvas pan and zoom altering function(s)
                 	[CanvasZoomSet, Zoom] // restore zoom level
                 	[CanvasPanSet, CpanH, CpanV] // restore horizontal and vertical pan
                 
                 	] // end of Ifreeze
                 
                 [Ishow, 28601, 1] // Notify user of new texture
                 [Iset, Zscript:previous, 1] // Reload previous zscript
                 ] //end of Ibutton</span>

That is it. I hope that people will implement most of the above suggestions and hopefully write some useful functionality code themselves. But remember it should be code that does not hinder general zscripting and zscript functionality. Happy zscripting.

   Thanks goes to Svengali for providing me with feedback.

This is a great idea in general and some great individual ideas here as well. I hope this becomes a long standing, well worn thread!!

Some Great Ideas! Your window sizing routine is much more elegant than mine, I am using it now in my scripts, thanks a bunch.

I thought I would contribute a sample script showing how to use the NoteIButton for creating Menus. I have commented the code but feel free to ask questions.

Thanks TVeyesā€¦i have learn a lot from you and from the master Digit.

Thanks again.

cameyo

What a brilliant thread. For someone like me who struggles to grasp the basics of zscripts (but enjoys trying) these examples are invaluable. Thank you TVeyes for starting the thread so generously with your interesting and useful ideas. And thank you Digits for posting a script which sets out stuff Iā€™ve been puzzling over for quite a while! :slight_smile:

Marcus

Thank you for the comments. I hope everybody tries to implement these functionailities in their zscripts where applicable. Of course only if you agree with the reasoning behind it.

#1, The Rebound Effect and #2, Memory Block Naming, are essential in my opinion. #3, Clever Zscripts, is necessary in general and especially so when the Rebound Effect is utilised. #4, Size Matters, is nice to have for the end user but not necessary. But considering how easy it is to implement I think all Zscript Window scripts should include it. #5, The Untouched Canvas, is not needed that often under normal circumstances but for a certain group of zscripts it is vital for the end user (More on that when I get time).

Aminuts : Thank you. We are waiting for you to join the zscriptor's club, you know you want to :)

Digits : Thanks. I guess you know it was you and Cameyo who got me started in the first place so the pleasure is all mine. While writing the Size Matters code, a good while ago, I had the feeling I had seen that functionality before. I guess it was one of yours. Thanks.

Cameyo : Thank you. You know I also learned alot from you so I am glad to return the favour now and then.

Marcus : Cheers, glad this thread is also useful to you codewise. FYI, that Note Window Interface tutorial I once talked about will be up soonish. ā€œSoonishā€ must be such a nice word, since I use it all the time :o

If there are any questions to my first post please ask. There are a couple of minor changes needed in the Memory Block Naming code and I might add some extra description and code commentsā€¦ soonish.

Here is another functionality which, although very simple, offers a nice way to cut down on interface clutter

   <b>6. :white_small_square: Double-Edged Buttons:white_small_square:</b>
                                        
                                        <b>What:</b> Allows a single button to offer two functions.
                                        
                                        <b>Why:</b>  To minimize interface clutter.
                                        
                                        <b>When:</b> Whenever a zscript button uses a shortcut to activate its function(s) and the button's action(s) has optional settings.
                                        
                                        <b>Description:</b>

Some zscripts require a shortcut key to perform properly. For example, the XYZadjust script needs to be triggered by a shortcut as it uses the current cursor position to check for zspheres. So what about the XYZadjust button hidden in the Zplugin palette, feeling all lonely because no one presses it?

If the zscripts function calls for it you can set a different action all depending on if you actually press the zscript button or you press a shortcut on the keyboard that activates that button. This is beneficial in reducing the amount of buttons by allowing either the normal action (the shortcut) or a second action (the button press) which will probably be an options Note Window, but I am sure somebody will think of another use.

   Needless to say this functionality is not suited to all zscript or zplugin buttons.
   
                                        <b>Code:
   
   </b>The code is very simple but you need to be aware off how zbrush recognises interface items in zscript window scripts. The interface item path for a zscript window interface item is the button name appendend to "Zscript:". So, a button labeled "MyFirstZscript" becomes "Zscript:MyFirstZscript". Zplugin buttons always keep their designated interface path and button name.
[[color=PaleGreen]Ibutton, "RenderRegion", "Renders a user defined region.",
       	[If, [Iget, Preferences:Utilities:View Window ID] = [IgetID, "Zscript:RenderRegion"],
       	 // Button was pressed. Insert code here.
       	 ,
       	 // Shortcut was pressed. Insert code here.
       	]
       ,,,'[color=Orange]k'</span>]</span>//button is activated by 'k' on the keyboard
   <b>Code Addendum:</b>
                                     
 [[color=PaleGreen]Iget, Preferences:Utilities:View Window ID] </span>returns a numerical value. To avoid conflicts you should use the [[color=PaleGreen]IgetID,....</span>] command to dereference the Interface Item Path to its numerical value.

You should not directly reference the Window ID (the numerical value) of a zplugin button as this can and will change all depending on the number of zplugins installed on your system. Zscript buttons can be referenced by their Window ID if necessary as they are assigned IDs within a certain Window ID range and only one zscript can be active at a time. But for readability it is best to reference a button by its full alphanumerical value.

Double-Edged Buttons To add to TVeyes example 6, a method adapted from Pixolatorā€™s ZLauncher script:

What: Allows a single button to have two functions.

Why: Simplify zscript layout

When: Anytime you donā€™t want too many buttons!

Description: By using a modifier key (in the code example the Alt key) to add a second function when a button is pressed

Code:

[VarDef,keyOnMouseDown,0]

[Sleep,0.001,
	[If,SleepResult==4,//waits for keypress
		[VarSet,keyOnMouseDown,
			[IGet,preferences:utilities:viewkeyboardstatus]
		]
	]
	[SleepAgain],4,sleepResult
]

[IButton,"Press","Press",
[If,((KeyOnMouseDown)&1024)==1024,
[Note,"You pressed the Alt key"]//commands for Alt keypress on click button
,//else
[Note,"You didn't press the Alt key"]//commands for straight mousedown on button
]//end if
]

And also:

User Plugins

What: How to make a plugin of a recorded zscript
Why: So as to add a button to your UI
When: To ease loading when thereā€™s a script you use often

Description:

For information about User Plugins see here. Simply putting your ZScript in the ā€¦\ZBrush2\ZStartUp\ZPlugs folder wonā€™t work though. You need to edit the text file version of your script first.

Before the [IButton, bit of your script put this:

[ISubPalette,Zplugin:More Plugs]

This tells ZB to put your plugin button in the ā€˜More Plugsā€™ subpalette. If there is no ā€˜More Plugsā€™ subpalette, it will create one (though look out for naming conflicts).

Then edit the name of the button (immediately after the first IButton comma) so as to include the plugin folder - it should read something like:

[IButton,
ā€œZplugin:More Plugs:My Modelingā€,

Having edited your script save it as a text file to your ā€¦\ZBrush2\ZStartUp\ZPlugs folder and then load it into ZBrush using the ZScript menu load button. It should then appear as a new button in the More Plugs subpalette of the ZPlugin menu.

Code:

[ISubPalette,Zplugin:More Plugs]//This tells ZB where to put your Plugin button

//Now to make your button a Plugin button

[IButton,
"Zplugin:More Plugs:My Modeling",//names the button 'My Modeling'

"Draws ZSphere",//pop-up text

//The Commands go here

,,76,//button width in pixels
"",,]//end of button - put a hotkey between the "" if you want
1 Like

Sweet! Thanks guys! I am as always adding this information to my arsenal of tips so that when I can take some time and actually think a few projects in mind out I will be able to achieve my goals a bit quicker and more efficiently thanks to you! I am sure you are helping a lot more folks than actually post a thank you as well.

I hope to maybe one day hahaha add some insight of my own.

thanks again guys!!

Marcus and TVeyes,

This is going to be a very valuable thread for both new and seasoned scripters. I just learned something that will turn out to be very useful about the Sleep command (thanks Marcus!)

One thing I was thinking of doing was creating note-help popups for each button by letting the user do a control-click on the button in question. No go. Could someone explain to me why the

[IGET, PREFERENCES:Utilities:ViewKeyboardStatus] command

wonā€™t acknowledge that the control key is pressed even though you can plainly see it IS pressed in the ViewKeyboardStatus slider? (I have a suspicion but not really sureā€¦)

One quick tip: For anyone who wants to read the WindowID for various interface elements, Just drag and drop the PREFERENCES:Utilities:GetWindowID slider onto your shelf and update your CustomUI. This can be very handy during script writing/testing.

Like TVeyes says though, using the WindowID number instead of the actual path-plus-itemname for the various elements can make for some pretty UNREADABLE scripts! (Especially if you havenā€™t looked at old code for two or three months :confused: :smiley: )

Tā€™anks guys.

Sven

Sven,
If you substitute 512 for 1024 in my code example you will get the result you are after [Ctrl-clicking]. Modifier key references are added to any other keypress, even the preceding one, so some way is needed to extract the extra key info. Now, I donā€™t know why this works (perhaps someone would explain?) but it does. If you put ā€˜-32ā€™ in the code instead of ā€˜&512ā€™ and press the spacebar before the Ctrl key you will see what I mean.

Edit: on the subject of Auto-Notes it would be very useful to have this functionality for zscripts.

Cheers,

Double-edged buttons : Addendum

The Sleep command can have more than one awakening event specified. This can be useful, as (presumably) you can only have one Sleep command in a zscript.

As a simulation of Auto-Notes for zscripts here is an example that might be useful in certain situations. The note is called by pressing the Ctrl+Shift keys while mousing over the button. In this example, the Alt+click on the button gives an alternative command set. (TVeyes, please excuse direct button reference!)

[VarDef,KeyPressID,0]
[VarDef,buttonID,0]

[Sleep,0.001,
	[If,SleepResult==64,//waits for modifier keypress
			[VarSet,KeyPressID,
			[IGet,Preferences:utilities:viewkeyboardstatus]]			
			[VarSet,buttonID,
			[IGet,Preferences:utilities:view window id]]
			[RoutineCall,popup]
	]
	[If,SleepResult==4,//waits for mousedown
			[VarSet,KeyPressID,
			[IGet,Preferences:utilities:viewkeyboardstatus]]
	]
	[SleepAgain]
	,64|4 //specifies modifier keydown OR left mousedown as awakening event
	,SleepResult
]


[RoutineDef,popup,
[If,((KeyPressID)&768)==768, //Ctrl+Shift keys pressed
[If,buttonID=12808,
[Note,"Some information about this button.",12808,1]]]
]


[IButton,"Press","Press",
[If,((KeyPressID)&1024)==1024,
[Note,"You pressed the Alt key"]//commands for Alt keypress on click button
,//else
[Note,"You didn't press the Alt key"]//commands for straight keypress on button
]//end if
]



It is easy to change the default colors of Zbrushā€™s interface items to use in your zscripts. I am mostly refering to the Slider and Switch controls since you canā€™t assign a image to them, it allows the zscripter the ability to color match the controls of the script for a more pleasing interface. However, when the user selects another Zscript to use the prior color scheme that you have set with your script might not appear to be a fashion winner.

I propose that a small amount of code be used in all your scripts to control how your interface appears in the workspace so that a Zscript loaded prior to yours doesnā€™t interfere.

Here is a small routine that you can call to set the colors of Zbuttons, Sliders and Switches. Call it either in the start of your script or in your initialization routines. Change the RGB values to suit your taste.

[RoutineDef, Interface , [VarDef,RKeep,0] [VarDef,GKeep,0] [VarDef,BKeep,0]

//Settings as shown restore Zbrush Default Colors
// Remember current color selected
[VarSet,RKeep,[Iget,COLOR:REDCOMPONENT]]
[VarSet,GKeep,[Iget,COLOR:GREENCOMPONENT]]
[VarSet,BKeep,[Iget,COLOR:BLUECOMPONENT]]
[ICOLORSET,100,3,3]
[IPRESS,PREFERENCES:ICOLORS:ZSCRIPTBUTTONCOLOR]
[ICOLORSET,85,46,46]
[IPRESS,PREFERENCES:ICOLORS:ZSCRIPTSWITCHCOLOR]
[ICOLORSET,142,142,142]
[IPRESS,PREFERENCES:ICOLORS:TEXTCOLOR]
[ICOLORSET,194,200,201]
[IPRESS,PREFERENCES:ICOLORS:PRESSEDTEXTCOLOR]
[ICOLORSET,255,153,35]
[IPRESS,PREFERENCES:ICOLORS:NUMBERSCOLOR]
// Restore color
[ICOLORSET,RKeep,GKeep,BKeep]
]

Hi Scripters,

An example of a cropping subroutine based on some stuff Iā€™m doing right now in a script. I was always curious how the > > CROPPER < < tool worked. A bit of experimentation and I came up with the following subroutine called CropBox. You can see it work with the attached ā€œselectā€ button.

// -----------------------------------------------------------------------------------
// SUBROUTINE TO CROP AN AREA OF CANVAS
// -----------------------------------------------------------------------------------
// CropBox example submitted by Svengali - June 2005
// Asks User to describe (click-drag) a rectangular area of the canvas
// Returns X, Y for upper left and X, Y for lower right corners
// Routine also Min/Max dimensions and clips to the canvas boundries
// Displays note that visually confirms the selected area to user

[RoutineDef, CropBox,
// Read canvas dimensions
	[VarSet, CXMax, DOCUMENT:Width ]
	[VarSet, CYMax, DOCUMENT:Height ]

// Note prompts user to draw diagonal box using click and drag
	[Note, "\C202020   Draw "Crop-box" Area
\CC02020   Click-and-drag from upper left to lower right." , ButtonLink, , 0xe0e0e0,30,360 ]

// Get X and Y for upper left and X and Y for lower right corners
	[Loop, 999999,												// loop until left mouse button is clicked
		[If, ([MouseLButton ] == 1),
			[VarSet, Cx1, INT([MouseHPos]+.5) ]
			[VarSet, Cy1, INT([MouseVPos]+.5) ]
			[LoopExit ]
		]


	]
	[Loop,999999,												// loop until left mouse button is released
		[If, ([MouseLButton ] == 0),
			[VarSet, Cx2, INT([MouseHPos ]+.5) ]
			[VarSet, Cy2, INT([MouseVPos ]+.5) ]
			[LoopExit ]
		]
	]

// If necessary, reorder the X and Y values
// Dimension results = upper left and lower right pairs
	
	[If,Cx1 > Cx2,									// reorder x dimensions of crop box
		[VarSet, CTemp1 ,Cx1 ]
		[VarSet, Cx1, Cx2 ]
		[VarSet, Cx2, CTemp1 ]
	]
	[If,Cy1 > Cy2,									// reorder y dimensions of crop box
		[VarSet, CTemp1, Cy1 ]
		[VarSet, Cy1, Cy2 ]
		[VarSet, Cy2, CTemp1 ]
	]

// Clip the X and Y values to fall within the canvas

	[If,(Cx1 < 0), [VarSet, Cx1, 0 ] ]				// clip min/max dimensions to canvas size
	[If,(Cx1 > CXMax), [VarSet, Cx1, CXMax] ]

	[If,(Cx2 < 0), [VarSet, Cx2, 0 ] ]
	[If,(Cx2 > CXMax), [VarSet, Cx2, CXMax] ]

	[If,(Cy1 < 0), [VarSet, Cy1, 0 ] ]
	[If,(Cy1 > CYMax), [VarSet, Cy1, CYMax] ]

	[If,(Cy2 < 0), [VarSet, Cy2, 0 ] ]
	[If,(Cy2 > CYMax), [VarSet, Cy2, CYMax] ]

// Display note showing box dimensions
	[VarSet, CTemp1, Cx2 - Cx1 ]
	[VarSet, CTemp2, Cy2 - Cy1 ]
	
	[If, (CTemp1 == 0) || (CTemp2 == 0),
		[Note, "\Cf02020Sorry, selected area is outside the Canvas...", , , 0xd0d0d0 ]
	, // else
		[Note,  , 1004 , -1 , , , , , ((Cx2-Cx1)/CXMax), ((Cy2-Cy1)/CYMax), (Cx1/CXMax), (Cy1/CYMax) ]	
		[Note, [StrMerge,"\Cf02020CropBox corners:
\C202020 x1 = ", Cx1, ", y1 = ", Cy1, "
 x2 = ", Cx2, ", y2 = ",Cy2 ], , -1 ]
		[Note, [StrMerge, "
\Cf02020CropBox dimensions:
\C202020 ", CTemp1 , " by ", CTemp2, " pixols" ] , , , 0xd0d0d0, 20, 170, 0x808080 ]
	]
	
// pass dimensions back to calling button routine
// The incoming argument, ButtonLink, passes the name of the calling button routine -used to "anchor" the first prompt-note to that button

	,Cx1, Cy1, Cx2, Cy2, ButtonLink
]


// --------------------------------------------------------------
// Demo CropBox Routine 
// --------------------------------------------------------------

[VarDef, x1, 0 ]
[VarDef, x2, 0 ]
[VarDef, y1, 0 ]
[VarDef, y2, 0 ]

[IButton, " Select ", "select area of canvas using mouse",
	[Routinecall, CropBox, x1, y1, x2, y2, "ZSCRIPT:Select" ]
]



The comments explain each part. Post any other questions or improvements! Thanks.

Sven

p.s. TVeyes: this might be useful to define a best-render area?
p.p.s. the attachment is a .txt file of the code

Just a small update to the ZScript Interface Color Preference code I posted above. In order not to hijack someoneā€™s custom colors I have modified the code so that the first time a script is loaded it will ask the user for permission to change the interface. Once the choice is made it will apply it when the script is reloaded or when another script with the same functionality is loaded.

here is the code

//***************************************************************
//Zscript Interface Properties
//***************************************************************
[VarDef,RKeep,0]
[VarDef,GKeep,0]
[VarDef,BKeep,0]
[VarDef,Begin,0]
[IF,Begin == 0,
//Check to See if Interface is enabled
[VarSet,tmp,[MemCreate,FBS_INTERFACE,4]]
[if,tmp==-1,
//Memory is already created so Read the Userā€™s choice
[MemRead,FBS_INTERFACE,RKeep]
,//else
//Ask the User
[VarSet,RKeep,[MessageYesNo,Disable Custom Interface Colors?,ZCurve Interface]]
//If the answer is no then RKeep = 0 and the Interface code executes
//If the answer is yes then RKeep = 1 and the Interface code does not execute
[MemWrite,FBS_INTERFACE,RKeep]
]
]
[IF,RKeep == 0,
[VarSet,RKeep,[IGET,COLOR:REDCOMPONENT]]
[VarSet,GKeep,[IGET,COLOR:GREENCOMPONENT]]
[VarSet,BKeep,[IGET,COLOR:BLUECOMPONENT]]
[ICOLORSET,64,64,64 ]
[IPRESS,PREFERENCES:ICOLORS:ZSCRIPTBUTTONCOLOR]
[ICOLORSET,64,64,64 ]
[IPRESS,PREFERENCES:ICOLORS:ZSCRIPTSWITCHCOLOR]
[ICOLORSET,160,160,160 ]
[IPRESS,PREFERENCES:ICOLORS:TEXTCOLOR]
[ICOLORSET,194,200,201 ]
[IPRESS,PREFERENCES:ICOLORS:PRESSEDTEXTCOLOR]
[ICOLORSET,255,153,35 ]
[IPRESS,PREFERENCES:ICOLORS:NUMBERSCOLOR]
[ICOLORSET,RKeep,GKeep,BKeep]
[VarSet,RKeep,1]
]
[VarSet,Begin,1]

This creates a memory block called ā€œFBS_INTERFACEā€ which will store the user choice. If you use the same name your scripts will also remember any other scriptā€™s choice or perform the user interaction.

The ICOLORSET commands contain the color choices for your interface.

I built on Svenā€™s Crop Box code above for my Crop Tool plugin. There are a couple of things that may interest others so I am posting the code here.

Whenever a plugin takes over the ZBrush environment, the script or the user will probably change some of the slider settings, and possibly the current alpha, texture, stroke or material. This routine can be called at the start of the plugin script to save and at the end of the plugin script to restore ZBrush settings to their original state.

// -------------------------------------------------------------------------------
// SUBROUTINE TO SAVE AND RETRIEVE THE STATE OF ZBRUSH
// -------------------------------------------------------------------------------

// At the time a plugin is called, the status of the various
// ZBrush buttons need to be stored so that when the plugin is
// exited they may be restored to their original status

// example call:   [RoutineCall, State, ZState]where ZState is 0 to save state and 1 to restore state

[RoutineDef,State,
	[If, ZStateFlag = 0,	
		// flag zero means save status of following items
		[VarSet, TMat, [IGet,MATERIAL:ItemInfo ] ]
		[VarSet, TObj, [IGet,TOOL:ItemInfo ] ]
		[VarSet, TTex, [IGet,TEXTURE:ItemInfo ] ]
		[VarSet, TMrgb, [IGet,DRAW:Mrgb ] ]
		[VarSet, TRgb, [IGet,DRAW:Rgb ] ]
		[VarSet, Tm, [IGet,DRAW:M ] ]
		[VarSet, TZadd, [IGet,DRAW:Zadd ] ]
		[VarSet, TAlp, [IGet,ALPHA:ItemInfo ] ]
		[VarSet, TStk, [IGet,STROKE:ItemInfo ] ]
		[VarSet, Tdsize, [IGet,DRAW:DrawSize ] ] 
		[VarSet, TZint, [IGet,DRAW:ZIntensity ] ]
		[VarSet, TRGBint, [IGet, DRAW:RGBIntensity ] ]
		[VarSet, TFshift, [IGet, DRAW:FocalShift ] ]
	,// Else 
		// flag one means restore status for following items
		[ISet,MATERIAL:ItemInfo, TMat]
		[ISet, TOOL:ItemInfo, TObj ]
		[ISet, TEXTURE:ItemInfo, TTex ]
		[ISet, DRAW:Mrgb, TMrgb ]
		[ISet, DRAW:Rgb, TRgb ]
		[ISet, DRAW:M, Tm ]
		[ISet, DRAW:Zadd, TZadd ]
		[ISet, ALPHA:ItemInfo, TAlp ]
		[ISet, STROKE:ItemInfo, TStk ]
		[ISet, DRAW:DrawSize, Tdsize ]
		[ISet, DRAW:ZIntensity, TZint ]
		[ISet, DRAW:RGBIntensity, TRGBint ]
		[ISet, DRAW:FocalShift, TFshift ]
	]
	,ZStateFlag // value passed from main script
]

Maybe I am the only one who might find it usefull but I would like to suggest a zscript that could automatize sending every subtool to a different layer.
I prefer to compose my scenes placing the models as subtools rather than doing it layer by layer. Its a lot more preciseā€¦but have them in separate layers is usefulll alsoā€¦

[EDITED] Double edged buttons with modifier keys - example 6 : One can think it should be better to make the trigger event 64|128 (mod key down/up) instead of 4, BUT IT IS NOT, sometimes ViewKeyboardStatus doesnā€™t get updated on key up and also mod key down it gets triggered thousand times per second which is not cool.

Cheers everyone :slight_smile: