ZBrushCentral

ZScript Lesson III -- CanvasStroke and Loops

This lesson is the third in an ongoinging series. It’s designed to follow
ZScript Lesson I: IButtons and Formatting and
ZScript Lesson II: ISliders
.

<hr>

By now you know how to create buttons and sliders. Now you’ll add some action – you’ll get a taste of how powerful ZScripts can be when drawing directly on the canvas.


In this lesson, you’ll create a ZScript that takes any stroke you draw, using any tool, and repeats it several times around a central point. It’s perfect for drawing flower shapes, like the one shown above.

You’ll learn a few important concepts: working with Strokes, and using the Loop command. You’ll also work with variables, to which you were introduced in the last section.

<font color="#ffa000" size=“3”>1) Introduction to Strokes</font>
ZBrush can keep track of any brush stroke you draw in the canvas. When I say “brush stroke” I mean any clicking+dragging action in the canvas; any time you click the mouse or pen button within the canvas, drag to another place, and release the button, that counts as a brush stroke. Depending on the tool selected, this brush stroke can draw a line of paint, it can transform a 3D object, it can move the Gyro to another spot, it can transform the Stencil, and so on.

Just so you’re not confused: a brush stroke is simply the action of clicking & dragging within the canvas, even if there’s no painting involved.

ZBrush keeps track of your brush strokes any time you record a ZScript by pressing RECORD in the ZScript palette…

… also any time you record brush strokes by pressing RECORD in the Stroke palette …

Either way, ZBrush precisely records the stroke information, including the starting and ending point plus all points inbetween, the pressure at each point, and any keyboard modifiers which may have been pressed along the way.

Don’t be misled – ZBrush precisely records all movements of the mouse/pen, but doesn’t record any information about the canvas or final result. Let me illustrate so it’s clear:

Here’s a brush stroke made with the Depth (3D) Brush on an empty canvas (with Draw: Depth increased for emphasis):

I’ll change the color to red, and replay the stroke (this can be done by pressing the “REPLAY LAST” button in the Stroke palette). You might expect the second stroke to simply replace the first one, but here’s what happens:

The stroke behaves differently when replayed, because there’s new information on the canvas.

ZBrush stores the stroke information in a text file. Perhaps you’ve been curious and opened up one of these text files; you’ve seen something very cryptic that looks like this:

(ZObjStrokeV02n60=YH3FV60PXH42V62H44V63H46V65…

This strange stream of letters and numbers tells ZBrush exactly where to click and drag when repeating your brush stroke. (For hardcore tinkerers, Zuzu explains what the letters and numbers mean in this thread.)

Fortunately, you don’t have to understand this internal code to work with brush strokes – ZScripts offer you lots of commands to work with them.

For example:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[StrokeGetLast]</font></td></tr></table></blockquote>

… is a command, with no arguments, that simply fetches the last brush stroke drawn. This means the last (single) time the mouse/pen button was clicked, dragged, and released.

What do you do with that information? You put it in a variable! (Did you think variables were only good for holding numbers? :wink: )
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[VarSet,ThisStroke,[StrokeGetLast]]</font></td></tr></table></blockquote>

Remember from lesson II: This tells the ZScript that any time it encounters the variable ThisStroke in a command, substitute it with the value, in this case, the brush stroke.

Note: In this lesson, you’ll be dealing only with single brush strokes – one click, drag and release. ZScripts provide similar ways of dealing with multiple brush strokes, but these will be described in future lessons.

<hr><font color="#ffa000" size=“3”>2) The CanvasStroke Command</font>
In this section you’ll become familiar with the CanvasStroke command and its arguments. If you’re already familiar with the CanvasStroke command, you can skip to section 3.

The CanvasStroke command draws a single brush stroke on the canvas. It uses the tool, color, material, texture, transform and draw settings which are currently applied. Don’t confuse it with the CanvasStrokes command, which draws multiple strokes.

Here is the CanvasStroke command and its arguments:
<blockquote> </blockquote>
Stroke Data is the stroke information, described in the last section. You could copy one of those cryptic strings of letters and numbers from a recorded ZScript, and paste it here if you wanted. Or, more simply, you could use a variable, like ThisStroke, in its place.

Delayed Update can be 0 or 1. If 0 (or empty), the user gets to watch the entire stroke as it takes place; if any number other than 0, the stroke is applied in a single instant.

Rotation is any angle, in degrees (0-360, or negative). If this is 0 or empty, the stroke is drawn at the same orientation as when it was first defined. If Rotation is 180, for example, it’s as if the canvas is “spun” around a half turn before the stroke is applied. If you think of the canvas being “spun”, also think of a pin being stuck into the canvas at the spot where the mouse/pen button was first clicked (the beginning of the stroke). This is also the spot where the rotated stroke begins drawing.

Horizontal Scale is the scaled width of the stroke. 0 = zero width, 1 = 100% width, 2 = 200% enlarged width; .5 = 50% reduced width, and so on. Caution: if you type commas and leave this blank, i.e. [CanvasStroke,ThisStroke,0,90,10,10], the Horizontal Scale will be 0.

Vertical Scale Similar to Horizontal Scale, this is the scaled height of the stroke.

Horizontal Offset From where the mouse/pen was first clicked when the stroke was first defined, move this many pixels horizontally before drawing this stroke. This value can be positive, meaning “move this many pixels to the right”, or negative, meaning “move this many pixels to the left”.

Vertical Offset Similar to Horizontal Offset, this move this many pixels up or down before drawing the stroke. A positive value means “move down”, a negative value means “move up”.

<hr><font color="#ffa000" size=“3”>3) Trying It Out</font>
Let’s create a simple ZScript so you can get a feel for working with the CanvasStroke command. This ZScript will draw a mirror image of any stroke you draw.

Create an IButton, and leave the Commands Group blank so you can insert it later:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[IButton,“Draw”,“Draw a Mirror Image”,
// leave this blank for now
]
</font></td></tr></table></blockquote>
There’s a very simple way to instruct the CanvasStroke command to draw a mirror image: give the Horizontal Scale a value of -1. Combine this with the VarSet and StrokeGetLast commands from above:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[IButton,“Draw”,“Draw a Mirror Image”,
[VarSet,ThisStroke,[StrokeGetLast]]
[CanvasStroke,ThisStroke,0,0,-1]
]
</font></td></tr></table></blockquote>
The CanvasStroke command has a Delayed Update value of 0, so you can watch it being drawn, a Rotation of 0, and a Horizontal Scale of -1. All other arguments are omitted, so they take default values – Vertical Scale is 1 (100%), Horizontal Offset and Vertical Offset are both 0.

Try this simple ZScript – load it, draw a stroke using any painting tool, size and color …

… and press the Draw IButton

<hr><font color="#ffa000" size=“3”>4) The Loop Command</font>
The Loop command is very simple, but you’ll get lots of use out of it. Simply stated in English, it means “Do this entire set of actions (Commands group), a certain number of times”. It looks like this:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[Loop, number of times ,
Commands group ,

]
</font></td></tr></table></blockquote>

It’s especially powerful when you change the values of variables within the Commands group. For this lesson’s Flower ZScript, you’ll use the Loop command to draw each separate “petal”, a certain number of times.

<hr><font color="#ffa000" size=“3”>5) Creating the Flower ZScript</font>
This ZScript will consist of an ISlider, with which you can specify the number of “petals” on the flower, and a Draw IButton, which does the work.

You already know how to create IButtons and ISliders, so create these two items now, leaving the IButton’s Commands group blank for now:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[ISlider,“Petals count”,8,1,2,50,“Number of separate petals”,150]
[IButton,“Draw”,“Draw the flower”,
// Commands group blank for now
]
</font></td></tr></table></blockquote>

Refresher: the ISlider is called “Petals count”, starts with a value of 8, has a Resolution of 1, can go from 2 to 50, and has a visible width of 150.

Let’s plan out our Commands group. You already know how to take the last stroke drawn, and re-draw it. You’ll use a Loop command to draw it several times, with different Rotation values.

Here’s what you can’t know when writing the ZScript: what the stroke looks like, what number of petals has been chosen by the user, and what angle to use when drawing each stroke. Since you can’t know this information, use variables. You can use any names at all; here are my names:

ThisStroke = what the stroke looks like
Petals = number of petals chosen by the user
Angle = what angle to use when drawing each stroke

At this point, all you know about the Angle variable, is that you’ll change it each time you draw a stroke. So, in the beginning, you should start with this variable set to 0.

Now you know enough to write the Commands group, except for the information inside the Loop command, so leave that part blank for now:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[IButton,“Draw”,“Draw the flower”,
[VarSet,ThisStroke,[StrokeGetLast]]
[VarSet,Petals,[IGet,-1]] //
remember my “neat feature” from Lesson II?
[VarSet,Angle,0]
[Loop,Petals,
// Commands group blank for now
]
]
</font></td></tr></table></blockquote>

The ThisStroke variable is set to the stroke information. The Petals variable is set to the number chosen by the user in the “Petals count” slider (using my “neat feature” from Lesson II). The Angle variable is set to 0.

As for the Loop command, it’s instructed to carry out its Commands group a certain number of times, determined by the Petals variable.

So what to do with the Commands group? As I said before, you’ll change the Angle variable each time you draw a stroke.

You already know you’ll be drawing a full circle by the time the Loop command finishes – that’s 360 degrees. You also know you’ll be dividing that circle into pieces – each time the stroke is drawn, you’ll move another step around the circle.

Let’s say the number of Petals is 6 – the circle (360 degrees) will be divided into 6 parts:

Each part represents a certain angle. You don’t know what that angle is, and frankly, you don’t care – you’ll let ZBrush figure it out. You can write this angle as 360/6.

In this case, the number of petals (the Petals variable) will change, so write it as 360/Petals.

Add this number to the Angle variable using the VarAdd command:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[VarAdd,Angle,360/Petals]</font></td></tr></table></blockquote>

Whatever that number – 360/Petals – is, it’ll be added to the Angle variable each time the stroke is drawn.

Plug the Angle variable into the CanvasStroke command, and you can finish the Commands group for the Loop command:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”> [Loop,Petals,
[VarAdd,Angle,360/Petals]
[CanvasStroke,ThisStroke,0,Angle]
]
</font></td></tr></table></blockquote>

One minor change: when you press this IButton there will already be one stroke drawn on the canvas. When the circle is completed, this Loop will draw another stroke on top of the first one. So you should prevent that last stroke from being drawn, by making the Loop command execute one less time. Do this by simply subtracting 1 from the number of times it executes (Petals - 1):
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”> [Loop,Petals - 1,</font></td></tr></table></blockquote>

Here’s the finished IButton:
<blockquote><table border=“0” cellpadding=“6” cellspacing=“0” bgcolor="#cccccc"><tr><td bgcolor="#cccccc"><font color=“black” size=“2”>[IButton,“Draw”,“Draw the flower”,
[VarSet,ThisStroke,[StrokeGetLast]]
[VarSet,Petals,[IGet,-1]]
[VarSet,Angle,0]
[Loop,Petals - 1,
[VarAdd,Angle,360/Petals]
[CanvasStroke,ThisStroke,0,Angle]
]
]
</font></td></tr></table></blockquote>

<hr><font color="#ffa000" size=“3”>6) Using the Flower ZScript</font>
Now your ZScript is finished. Try it out using any paint tool – draw the first stroke:

Press the Draw IButton, and you’ll get this:

Since a brush stroke can also draw a 3D object, you can create a 3D petal. I sculpted this from a Circle3D object:

I used the Tool:Modifiers:Offset deformer to move it away from the center:

Using this petal in combination with this ZScript gives me this:

<hr><font color="#ffa000" size=“3”>Exercises</font>
To get a little more practice, try these challenges on your own:

:white_small_square: Create a button that takes the last stroke drawn and “flips” it horizontally and vertically, for 4-sided mirror symmetry. Try the same trick, but draw each stroke a second time rotated 45 degrees, for 8-sided symmetry.

:white_small_square: Create a “Store” IButton and “Recall” IButton which you can click to save your brush stroke and draw it later. Create several of these button pairs for interesting variations.

Happy ZScripting!
dave

<hr>
Next Lesson:
Lesson IV: TransformSet and CanvasClick

Ahhhh! This one snuck up on me! I wasn’t expecting it till next week!

But … hey!.. it looks cool!!
Thanks Davey!
Upham (Coder 2 be) :slight_smile:

I can’t claim to have even read it all yet as the ink isn’t dry but from my quick look over it, it looks like a winner of a lesson. Very nice work Davey.

Thanks for these lessons. When will your ZScripting book be released as I would like to illustrate it for you? :slight_smile:

Glen

thank davey keepem comin :+1: :+1:

Great, awsome, wonderful!!! Pretty much everything in here was completely new to me! I can’t wait to begin to really use all this! :slight_smile:

upps sorry wrong place

hey davey

This is simply awesome that you are teaching the fundamentals of zscripting…
It is an aspect of zbrush I am just starting to explore…

I would really like the to read your 1 & 2 lessons but the link here is broken.
Could you re-post or point me in the right direction?

cheers

:+1:small_orange_diamond:+1:

EDIT: its cool I found em!