Getting started

Your best bet would be to start with a blank scene to experiment with and get used to Clockworks before attempting to apply it to a mature scene. Start by putting two simple objects into the scene. These objects will ultimately not need to be visible to users, not need to be dynamic, and not need to have collision volumes. Put the "Clockworks - Controller" script into one of them. Edit its settings.

To start with, set the ListenToChat setting to "On". You can turn this off later, but it will be important for early experimentation via live programming while you work on your scene.

Leave the other settings as their default values (0). UnitChannel defaults to 19730002. Although you probably will never have reason to, if you ever decide to choose a different value, be sure to use that same value with every unit script, too.

The default ProgramChannel value is 19730003. If you change it, you will need your custom control scripts to emit script messages on that new channel. You should not ever need to, however.

The second object you created will house your custom scene script, once you start writing it.

Now start putting other objects into your scene that you intend to animate. Put a copy of the "Clockworks - Unit" script in each. You should only need to name each, because your scene script is going to do all the rest of the configuration work.

Every object you want to animate must be marked with an IsDynamic = On setting. This means it must have a collision volume. If you have an object without a collision volume, don't worry. You can add one in the Scene Objects panel. Right-click on the object and choose Add and then Volume.

If you are designing your own machine parts, please consider carefully your collision volumes. For various reasons, you do not want your adjacent parts to bump into each other. Moreover, you generally cannot physically interact with these parts, so there's no reason to design collision volumes with that in mind. For example, you can't fight with a robot or walk on a floating platform. One good option is to make a small cube that you will place at your pivot point. You can even made just one and reuse it for each 3D model you import for use as Clockworks units.

Naming units

Every object you choose to animate in a scene needs its own unique name so you can give it its own unique configuration. So it's tempting to think you need a simple name, like "A", "B", "C", etc. But you really, really need to get a clear understanding of how to properly name units in order to save yourself a lot of needless work down the road. Here's an example of a well-named unit:

To explain why it's named well, consider what you are ultimately trying to do with Clockworks and what kinds of units you might employ. Each machine in your scene may have multiple parts (segments), so each of those needs to be named. Your machine might be duplicated several times and placed around your scene, so those need names, too. You may even have several different sets of machines all following their own tempos or triggered by different events. So let's pick all this apart.

Consider a single machine, such as a lamp. Our lamp will have a base, two arm segments, and a light at the end. We might logically name these units as "Base", "Arm1", "Arm2", "Light". So what if we want to have two identical lamps in the scene, but with different locations for their bases? We'd want all the other configuration elements to be the same, right? The answer is that we need to name each copy of our lamp (machine). Maybe we call them "1" and "2". And we introduce the variable "$" into our unit name. So in this example, we have "Lamp.$.Light" and "1" as our unit name and machine name, respectively.

So now we will effectively have "Lamp.1.Light" and "Lamp.2.Light" to name two different units. So why not just name each unit directly like this instead of using the "$" variable in the name? The reason is that we can refer to all the copies of a machine part in our control scripts. So when we call a command to attach "Light.$.Arm1" to "Light.$.Base", the two different copies of the machine will not get their parts crossed. Light.1.Arm1 will connect to Light.1.Base and Light.2.Arm1 will connect to Light.2.Base.

Note that the "$" variable is only valid in the UnitName setting. Used in any other setting, it will be treated as a literal character. You should just avoid using it outside the unit name, though.

Did we need to have "Light." as a unit name prefix? Technically, no, but what if you had another machine that had a base, too? The best general pattern is to use UnitName values in the form "<machine type>.$.<part>".

MachineName can have whatever format you wish, but the easiest option is usually to just use sequential numbers. If you string them up in rows and columns, you might use something like "A1", "B1", "A2", "B2", etc. to make your custom configuration easier.

SetName can be a little confusing. It most directly relates to use of the Start and Bpm commands to get all the machines and parts that should be operating in concert to run together. To illustrate this, consider a dance club. You would want one set to represent all the lights and other dynamic art that needs to dance to the beat of the current song. But then you would want another set for a fancy door that is not timed to the beat, but instead responds to someone walking near it. And if you had a second identical door, you would want it to have its own set because it gets triggered separately from the first. And maybe you have an advertising display that loops at its own speed, separate from the music; it needs its own set. So the rule of thumb here is to ask yourself: what machines need to start and run together? Each set of machines that fits this description deserves its own SetName value.

Finally, the Flavor piece of a unit's name deals with object swapping. Let's say your lamp can have 6 different colors of light and an "off" option. You would need 7 copies of the "Lamp.$.Light" object, where each has a different spotlight attached to it. You might choose to make the "off" one your default object, in which case you leave its Flavor setting blank. Every other one you might give Flavor names like "Red", "Orange", "Blue", "Purple", "Yellow", and "Red". The program that makes the light move around will also select the currently active flavor by name. Only that object will be visible to the users. All the rest will hide away a kilometer below ground, waiting until it is needed.

Note that you can set the unit's Hideaway setting to control where it gets stashed when another flavor is in use if you wish, but the default is to take the startup position and drop its Z value by 1 km. If the startup position's Z value is already -1000 or lower, then the startup position will actually be up 1 km instead of down. Again, you can override this by setting Hideaway yourself. Be sure not to use the same hideaway location for two objects, as they are likely to burden the physics engine with their collisions.

Remember that you almost always want to have a blank Flavor value for at least one object. That one is the default flavor, and this applies to most of your moving parts. It's only when you want other flavors to replace the default flavor with that you start assigning custom Flavor names. The exception is when you want there to be no object visible by default and only later want some other flavor(s) of object to show up during your program.

And to be clear about this, only one flavor will be visible at a time. All the others will be in their hideaway locations at that time.

Technically speaking, only UnitName is required for a unit name. You can skip all the rest. But the minimum recommended is values for SetName and UnitName. For a simple scene or one where most animated objects are part of one large set, a simple, easy to remember SetName value is "X". Similarly, if you are only going to have a single copy of one machine, you really don't need to have a MachineName setting or use "$" in UnitName to refer to it.

If you plan to sell copies of your machine by selling its parts and a custom control script, be sure to be very careful about naming. Your customer will need to follow your directions. They should be free to control SetName and MachineName. Here's how your custom script's settings might look, as configured by your customer:

Your custom script can demand that they use a fixed set of UnitName and Flavor values. Your UnitName values will need to be unique to avoid name collisions. Here is a good example of one of the units your customer might set up:

In this scenario, a vendor has a unique brand named "TheLampPeople" and a model named "FancyLamp". Note that if they have ten different color variants, the UnitName should not need to be different for each. It will be up to the customer to choose (and thus name) the appropriate parts.

Positions and orientations

In live programming and custom scripts, you find the need to express positions and orientations. Clockworks offers some variety in how you do so for ease of use.

For positions, you use vectors. The most basic vector has three components, as in "10, 1, 0". However, you can omit the Z axis (e.g., "10, 1") if you are expressing vectors in the X,Y plane. You can also use the literal "0" as equivalent to "0, 0, 0".

For orientations, you can directly use a quaternion (e.g., "1, 0, 2, 0") if you are comfortable working with them. Most people prefer to use Euler angles. In that case, you are passing in a vector like "0, 0, -45". All the rules described above for vectors apply here, with one exception. You can also use a single number to represent a rotation around the Z axis. For example, "180" is the same as "0, 0, 180".

Note from the above examples that rotation angles are always expressed in degrees. Quaternions, of course, have their own unitless form.

Live programming

One profound frustration in these early days of Sansar is that scripts must be written offline and then uploaded to your inventory for testing, a laborious process. Clockworks is designed to allow you to play in real time with your machines. As you figure out good settings, you can reproduce them in your custom script and eventually bring the latest script in for further testing and development. This saves a lot of time and encourages you to experiment more.

Practically speaking, you can manually execute all the steps your custom script would to perform the unit assembly and programming process. But also, this is an easy way to choose which custom program to run and what speed to run it at during a live performance.

To enable live programming, you'll need to edit the object that contains your "Clockworks - Controller" script and set ListenToChat to On. When this is enabled, anyone in the scene can see you submitting commands and can submit their own, so use this setting with care.

To execute a command, simply type it into the public chat window and hit Enter. Here's an example:

/cw x start * 140

This can be read as "For all machines in the X set, start running the default program (*) with a pace of 140 beats per minute". All commands begin with "/cw" and a space. The rest of the command is composed of arguments separated by spaces. Multiple spaces are treated as the same as one space, in this sense.

There is one special exception to the use of spaces as argument separators. For convenience, the "Prog" command, for setting the custom program for a segment, treats everything after the initial arguments as one single program argument, so you can use spaces in it. For example:

/cw x prog lamp.$.arm1 *   S:1/4 | S:-1/4

I like to put extra spaces before the start of the program so I can easily read it. The program part is circled in red. The spaces within it are treated as just extra characters in the program.

To use a blank for one of your arguments, use a single underscore (_) character, as in this example:

/cw x arm lamp.$.arm2 _ 0,0,90

The commands available and their arguments are exactly the same as for custom scripts.

Assembling a machine

Clockworks machines are composed of units arranged in relation to one another. The process of assembling a machine begins with telling each unit what its "parent" segment is. In an example of a lamp, our hierarchy might be like this:

Then each segment gets its own configuration that tells it how it is oriented relative to its parent segment and how it moves relative to it.

The physical objects (units) can be thought of as "attachments" to the logical segments. There often are more segments defined than units visibly representing the machine. For example, a ball and socket shoulder joint may be represented by two segments that reflect the two degrees of freedom it has, while there would only be one mesh object visible.

A segment is defined first by its "arm", a vector representing how far away its pivot point is from its parent segment's own pivot point. In the case of the first segment in a machine (a lamp's base, for example), the arm represents the absolute position of the whole machine, relative to the scene's origin (0, 0, 0). If this segment rotates, it is going to rotate around the endpoint of this arm. Keep in mind that for all segments under a machine's root segment, this is truly an offset and not an absolute position. As the parent rotates, this vector also rotates. As the parent slides, this vector also slides.

The second thing that defines a segment is its axis. This is an orientation represented by a rotation. This orientation affects how the attached unit looks, of course, but it also rotates the arm vectors of any attached child segments. In our lamp example, the machine might be oriented facing east, so if you wanted to rotate the whole thing 90° to initially point north, you might change its axis value from (0, 0, 0) to (0, 0, 90).

If you decide to express your axis in Euler angles (how Sansar's object editor represents orientations), keep in mind the ambiguity of (0, 0, 0). Clockworks will resolve that ambiguity by assuming this value means you want rotation in the X,Y plane around the Z axis. So you might need to choose a different initial rotation to indicate the correct rotation plane and adjust the other settings for the unit and child segments accordingly.

A segment is next defined by how it actuates. It either rotates around its pivot point (defined by the arm) or slides linearly along an invisible track that extends out from its pivot point.

If this segment is a rotational actuator, having the unit program shift between 0 and 1 will rotate it through its full 360° range in the rotational plane defined by the axis.

If this segment is instead a linear actuator, you need to also define a track vector. This sets the direction of travel out and back during animation and also how far it can go. If your track vector is 30m long, for example, having the unit program shift between 0 and 1 will slide the unit along that full 30m length.

If you did a perfect job of designing your mesh machine parts around the pivot point and with the correct orientation, then you're all set with your assembly. If, however, you are human, you need to adjust the placement of your parts (units) relative to the segments they are attached to. You do this by setting a unit's offset and tilt. Offset is a vector that is very similar in spirit to the arm vector. Tilt is a rotation that is also similar in spirit to the axis rotation. However, offset and tilt only affect that unit and never affect the arrangement of other segments the way arm and axis do.

Once you've defined your machine's assembly, your next step is to write unit programs to actuate all its joints.

Reflex integration

You can use the "Clockworks - Prog start" script to trigger the start of some Clockworks animation program from some Reflex sensor script such as a People sensor firing off a signal.

Let's say you had a dancing lamp that had a stationary pose as its default program and a little bow as its second program, to be triggered by a person wandering into a People sensor's range. You could put a copy of the program-start script into a static object in your scene and configure it like so:

One important point is that the program generally needs to stop after one program loop or a finite number of them by using the "loop repetition" beat attribute. This is not technically required, though.

One obvious application of the program-start script is a complex door with lots of gears or moving parts that opens with a flourish. Keep in mind that if you want to make an ordinary door or even some fairly sophisticated door mechanisms, the existing Reflex options, including Hinged door, Sliding door, and Position toggle, are loads easier to set up for this application.

We'll illustrate a concrete example of a Clockworks door. Let's say you have worked out the details of your door machine already. You named its set "Door1" and placed the foot of it at (10, 5, 0). And you created a default program that has the door stationary, a second program named "Open", and a third program named "Close" to animate all the machine's parts. Now you want to have it open when someone comes near and close after everyone leaves. Put the following four scripts in a static object to trigger this behavior.

People sensor

People presence

Clockworks - Prog start

Clockworks - Prog start

Collisions and physics

Clockworks is designed to work around several key limitations in Sansar's current Script API and physics engine. Those limitation will likely change in time. One tactic Clockworks uses is to set the mass of every unit it animates to zero. This prevents a visible jitter that dynamic objects under script control can get as they constantly try to fall as the script keeps them aloft. This helps the animations look smooth.

Another tactic is that units do not truly "move" in the physics sense. It's more like they are instantly teleported from one place to another without ever moving from frame to frame. Because these micro-teleports, if you will, happen so fast and in such small increments that your eye typically perceives it as continuously smooth motion.

Both these tactics, however, conspire to make units behave in ways you wouldn't expect from physical objects. You generally can't walk on a moving platform, for example (see below for an exception). You can't use these to easily create door-like barriers to people's movement, for another. Or create reliable "paddles" for physics based games and such.

For another thing, while your machines look to visitors like they are whole objects like in the real world, the parts of a Clockworks machine are all separate and operate independently of one another. You can't just pick one up and move it around.

Moreover, you can't really pick up any of the units reliably. The unit script will typically tear it out of your feeble virtual hands.

Your best bet is to consider Clockworks units to be visually pretty and not physically interactive. Since you must have a collision volume to make your object dynamic, my recommendation is to create as small a cube as you can get away with and use that. It should generally be much smaller than your visible model and will typically reside at the axis that it rotates around (if it is a rotator). This will keep the parts from colliding with one another and make it so they are less likely to get bumped by users and other obstacles as they move.

Regarding floating platforms, if you try to stand on Clockworks-controlled objects, they will sometimes allow it. If you really want to make it so someone can reliably stand on a moving platform, there is a trick you can use. Make an invisible sled for it that rides along a smooth ground below and make it part of the collision mesh. However, keep in mind that when it moves, it will slip right out from under the person standing on it as though they were wearing skates. They will need to walk with it to keep from falling off the moving edge.

I have advocated for LL adding a RigidBodyComponent.SetBuoyancy() method that would be similar in nature to RigidBodyComponent.SetMass() but designed to oppose it. In this scenario and in a simple usage, a perfectly hovering object would have a buoyancy value exactly the same as its mass. One upshot of this would be that Clockworks units would not need to have a mass of zero. Then it would be more practical to interact with them, stand on them, etc.

Optimizing your scene

Let's say, for argument's sake, that you decide to flood your experience (scene) with tons of Clockworks units and other scripted items and physical objects bouncing around. You might start to notice things getting sluggish. Key to this is the computing resources used on the server and the network bandwidth needed to send updates to every connected client. Fortunately, you can throttle these in various ways to make your scene behave better with a large load.

It's important to understand that the movements of dynamic objects within a scene take place on Sansar's own scene server. When you download that scene to visit it on your client, picture all the dynamic objects as being like remote-control cars with their remotes on Sansar's scene server. When that server decides to move that dynamic object, it pushes an update message out to every client viewing the scene at that moment. That means the more often move messages are sent, the more network bandwidth gets used.

One of the easiest ways you can reduce the computing burden on the scene server and the bandwidth burden to clients is to reduce the update rate. See the UpdateRate operation for details on its use. The default rate is 10 ms between updates, or about 100 FPS. Just increasing that delay to 20 ms would reduce the update rate to 50 FPS and should cut the server CPU usage by half and the update messages to clients also by half.

Another trick you can deploy is to turn some Clockworks machines off using the "Ready = No" technique.

The number of segments in a machine matters. The more joints (degrees of freedom) that you want your machine to have, the more segments you need to implement them. And even if you have the same number of visible units moving around, each extra abstract segment adds to the computational burden. To put this in perspective, imagine a snake-like chain of 10 segments attached to the ground and dancing above. The first segment needs only to compute its position and orientation. The second one has to compute both its own and its parent's. The third has to compute all three. Finally, the tenth unit must compute the positions and orientations of all 9 of the segments below it and then its own. These ten units are doing 55 segment computations cumulatively. All this happens 100 times a second by default. That's a lot of computation. So it's worth asking if there are ways to reduce the number of segments or the depth of their attachment hierarchy. Maybe two objects move as though they are one, so you could model them as one single 3D object, for instance.