Sunday, 13 July 2025

The Grand old Duke Of Rustica, he had 10,000 prims.

The Grand old Duke Of Rustica, he had 10,000 prims. He marched them up to the top of the sim and he marched them down again. 

And when they were up they were up, and when they were down they were down, and when they were only half way up, they were quantised into oblivion.

 So goes the tale of the Grand Old Duke of Rustica, the Honourable Maxwell Graf, Esq.

For those not interested in curious, obscure Second Life bugs, go and watch my video instead, just a few minutes of fun watching a large castle be moved (again)

Relocating Rustica - An exploration of terrain export woes in Second Life

It's rare these days for me to write about anything other than Firestorm/viewer stuff. But I thought I'd record a recent adventure in relocation, scripting ingenuity and a smidgen of mathematics (no, no, please stay, I'll keep it easy)

A few years ago, the impressive ancient castle of Rustica found a home attached to the New Babbage estate. Sheltering in our community gave its owner, Maxwell, the community support he needed at that time. The move was non-trivial. New Babbage, to some known for its Steampunk theme and resident-driven community builds, also happens to be one of the few deepwater estates in Second Life. With the sea level set at 100m, we host many a technical scuba diver practising their controlled ascents and decompression stops (yes, really, we do). So what? So.. that meant that the 14000+ meshes/sculpts and prims that combine to form the impressive Rustica Castle and its surrounding land all needed to be lifted, from the shallow grid-default water depth of just 20m, and with high accuracy, ensuring that no cracks appeared and the castle did not crumble. 

I recorded that feat as a record for Max and for Liz, whose LSL-wielding powers had allowed this to happen. You can watch it over on my vimeo account. 

 Poor Max has had a terrible few years. A traumatic brain injury led to a stroke and a bucket list of things you don't want to happen. It has left him broken in several ways, but not in spirit. Doggedly re-learning his skills as an artist, destroyed by the cognitive-loss from the TBI, has shown amazing results, some of his recent creations are some of my favourites, but these things take time and patience, things that don;t sit well with the frenetic pace of running a store in Second Life.

Unable to create new products at the rate and quality that stores today require, but not wishing to give up on Rustica and SecondLife, a place so very dear to him. It was here where, among many other important events in his life, Max met his RL wife so losing SL and Rustica was never really a choice he'd choose to make. Instead, he made the difficult choice to find it a new home where, amongst a dedicated group of role players, his castle can live on without needing the income from his store alone to pay the fees. 

So, where do Beq and Liz come into this? Well, naturally, this means...yep, reversing the process. On the one hand, this is easy enough; we have the technology, or at least Liz does, and with the experience from the Rise, we can handle the Fall. However, it has also given us the need to address one of the problems we faced the first time around. Adjusting the terrain. 

When we raised the terrain, we encountered issues, but given the stress had been over the lift and shift of the stonework, it was mostly accepted as a small price to pay and reworked by hand. 

Why was raising the terrain hard? There is no tool on the viewer or the server to simply add or remove an offset. Arguably, there should be, but we'll come back to that. To adjust the terrain, you need to export the terrain data as a raw file. 99% of Second Life users will never do this because it is a function only available to estate or private region owners. 

Terrain files? Raw? wth?

Terrain files are ... "speshal". They are "raw" images, just bundles of bytes. Consisting of 13 channels of 256x256 8-bit values. 

Regular images have three channels Red, Green and Blue (RGB). In the Terrain files, we have 13 channels, but in reality, all but three of them are ignored these days, and for practical purposes, only the first two of these are used. Each "pixel" is notionally applied to a 1x1 square metre of the region. The Red and the Green Channels encode the height, while the Blue channel encodes the water level. 

The height encoding is where things get interesting. Given that an 8-bit number can only hold whole number (integer) values between 0 and 255, and we need to be able to represent terrain elevations higher than 255 metres, but we also want to be able to represent sub-metre levels (2.3m, 75.7m etc), some magic needs to happen. Two channels are combined. 

The most immediately obvious way to do this would be to combine the two channels in a binary 16-bit value, allowing up to 65535 discrete elevations, where an elevation step could be an arbitrary unit (let's say a centimetre), allowing a little over 655m of elevation. Instead, though, the Terrain format is encoded at a variable precision. The value of the First Channel (Red) is treated as representing the number of height "units". Meanwhile, the value of the Second Channel (Green) is used to define the "unit" measured in 128ths of a metre. 

So if R is 32 and G is 1, then the resulting height is 32 * 1/128 metres, a quarter of a metre, or better known as 25 centimetres. If we want larger jumps from a shallow shelving beach to a towering cliff, then we switch to a large unit size, 128 gives us steps of 1 metre. If we really want, we can go for 255, units of close to 2m jumps. A red value of 32 here, when coupled with a Green of 255, is not 25cm but close to 64m. So this rather obscure way of encoding is there to allow us fine-grained control over the terrain that we work with. Or it should.

And here we finally come to the problem at hand. The Second Life Server does not seem to dynamically tune the unit size. In an ideal world, you'd look to encode each height in the most accurate representation, this would scale the unit size to be smaller when the target height was lower, allowing small tweaks at lower altitudes and coarser ones higher up. 

When Max reached out to Liz and me to "do the drop", he sent me the terrain file, knowing that I would be doing the adjustments to drop the terrain height back to the 20m sea level. However, when I examined the file that the server had exported for him, I immediately saw an issue. The entire region uses a single "unit size" of 140/128ths. That's about 1.1metres. The minimum "step" from one metre to the next is 1.1 metres, which is not going to be able to capture the gentle slopes of the Rustica landscape. In fact, as Max found to his cost, it "Minecraftifies" the whole place. The image below shows how the land looked in Second Life before the export.


Once we export and re-import we get the following rather worrying results. Shown here on my private local instance of OpenSim.


A better export is possible

Interestingly, though, using LSL, we can actually read very detailed height information. With another of Liz's ingenious region crawlers capturing the true elevation data, and publishing that to a Google sheet. 

I was then able to write some code that translated that back into height data, but being careful to properly encode it to the most accurate combination of unit size and unit count (height), allowing us to extract an accurate topology. 

I have placed my python code and a few of the generated RAW files on github in a dedicated repo I have called "Terrain Tools"

Since the first commit I've added, a little extra code to allow land to be raised or lowered by an arbitrary amount, to create a "zero" empty terrain (which used with the raise-by command creates flat terrain of varying heights.

I also added the ability to create a ramp...not all that useful but you'll see why.

OK, export "fixed" does it really work?

Having established that we can import beautiful accurate terrain into Open Sim the remaining question is, what about Second Life will the imports work....

Mostly, but also...no, not really.

The import is as weirdly broken as the export. The imports do look smooth, have all the correct details, but, importantly, the height values are not correct; things are off by peculiar amounts. 

A flat 20m land should be at the same level as the sea, on Open Sim that is exactly what we get.  On SecondLife, however, the land is infuriatingly 1.25 metres above sea level. 1.25m, is not some minor rounding error! Land at zero is however correct, so is there a constant error offset being applied, can we compensate for it? We need to get Max the best land we can.

I created a Ramp using the tools and tested it in OpenSim; sure enough, it was a smooth linear ramp from 0 to 60m in 255 steps.

The same file in Second Life gave a very different result.

Oddly, baking that terrain and exporting it still gave us the smooth ramp. So, whatever it is doing to it, it is doing independently. We naturally tested the Second Life viewer as well as FS, and it was broken in the same way.

I had Liz run a hi-res scan of the imported ramp, and the offsets were reported. More interestingly, the errors all appeared to be 64/ths of the original data. So the suspicion is that the importer is quanitising the data on import as well, yielding very peculiar results. I added support for quantising the G (height unit) channel, and lo-and-behold, when quantised to 4ths, the ramp is "smooth" with small steps across the region like a line with anti-aliasing disabled.

I didn't have time or patience to fully reverse engineer the underlying bug, the quantising solution gives better results but I don't think it actually fixes it (and of course realistically you don't want any of this loss of fidelity), I wonder when this was broken though, I find it hasrd to believe it has always been this way, but I also recall noticing the offset from the water level about  4 years ago when creating terrain for Fantasy Faire builds. One thought is that it might have had to be re-written with the AWS uplift, due to new OS updates leaving whatever the old code was written in non-viable. You might also wonder whether the documentation was mis-read or read too literally? Notice how the examples cite only G values that happen to be quarters of 128, but there is no real value in speculating. A canny issue has been raised to hopefully get this examined more closely.

After many hours iterating over different encodings we decided tat we were not going to hit a perfect match and settled on the Quanta=4 output from the terrain script along with the hi-res captured data as input. This stll gave errors but was closer than any of the other tests.

Yesterday (12th July) we finally did the relocation. Over the course of about 3 hours (due in part to some weirdness with perms and temp-rez objects) the "sim muncher" that Liz wrote chewed its way thourgh Rustica spitting out a perfect recreation at the new sea-level. You can watch the new movie of this task on my Vimeo account

As always, it is possible that I've created a self-imposed wild goose chase, that I mis-understood the docs and none of this is even valid. But I've asked other to review and also, it works fine in OpenSim, which would have bene written using the documentation. All that remains is to raise the issues and see whether there is any substance to my claims.
 
Canny issues related to this post, will be added as edits post publication: