Varregion

Varregion

"Varregion" is a feature of OpenSimulator that enables region sizes larger than 256x256. The region is just larger so it acts like a regular region but with borders farther apart.

The implementation uses the Aurora large region protocol extensions so the existing Firestorm and Singularity Aurora support will now work for OpenSimulator.

This is different from the older Megaregions large region feature which does not require extra viewer support (above that already implemented by Linden Lab) but which relies on a number of fragile hacks to make a viewer work in a way that Linden Lab never supported. Megaregions also contain a number of extant simulator-side bugs.

A growing list of protocol changes to implement "varregion" is at Varregion Protocol.

Restrictions
Some restrictions apply:
 * The dimensions must be a multiple of 256 and less than or equal to 4096.
 * The dimensions must be equal so regions are square
 * One region can only have one adjacent region per side, excluding corners or viewers may crash. (so at most 4 regions touching at corners plus other 4, one touching each side)
 * Adjacent regions must be the same size unless they are all version 0.9 or above. Even then expect glitches from time to time, especially with physics and crossings.
 * With versions prior to 0.9 you must use BulletSim (0.9 ODE engines support varregions).
 * You must use BulletSim's height map terrain implementation. As of 20140128, BulletSim has been modified to force heightmap terrain implementation if region size is greater than 256 on any side. The setting can be forced by adding to your INI files:


 * If you were previously using megaregions you must ensure that CombineContiguousRegions is now set to false.

Configuration
The size is be specified in the Region.ini file:
 * If size is not specified it will default to the legacy size of 256.
 * If the given dimensions do not fit the restrictions, acceptable values are computed and warning and error messages are output into the log.
 * If you leave ExternalHostName at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.
 * If converting from a mega-region, remember to set:
 * For llRezObject to work beyond 256m set in the [Xengine] section of OpenSim.ini (for 512mx512m) :
 * If you have a very large region you might need to set the database maximum size to a larger value. For instance, a 2816x2816 region (11x11 legacy regions) turns into a 56 megabyte heightmap and, for MySQL, you would need to set max_allowed_packet to something like 64M. Version 0.9 stores terrain in compressed format so this depends on terrain complexity.

Loading Terrain
The usual terrain console commands work with varregions. If you have set up a larger region, you can load BMP/RAW/PNG heightmap files of the dimensions of the region and fill the whole region. For instance, a 1024x1024 region terrain would be completely initialized by "terrain load 1024.bmp" if '1024.bmp is a 1024x1024 bitmap.

Varregions and OAR Files
The objects and terrain are stored in an OAR file as if it was one region. That is, if you save a 1024x1024 region in an OAR file, you can later restore the objects and terrain into a 1024x1024 sized region. If you load an OAR file from a smaller region into a larger region, the unspecified terrain space will default to 25m.

To make conversion to varregions easier, load oar now has a '--displacement ""' parameter. This displaces all of the objects and the terrain from the oar file when loading them into the new region. For instance, say you have four OAR files from four adjacent 256x256 regions (oar00.oar, oar01.oar, oar10.oar, and oar11.oar). You create a new 512x512 varregion named 'bigregion'. The following commands place the four regions of objects, terrains and parcels into the new larger region:

change region bigregion load oar oar00.oar load oar oar01.oar --displacement "<0,256,0>" --merge --force-terrain --force-parcels load oar oar10.oar --displacement "<256,0,0>" --merge --force-terrain --force-parcels load oar oar11.oar --displacement "<256,256,0>" --merge --force-terrain --force-parcels

Note the new "--force-terrain" and "--force-parcels" parameters. "--merge", used by itself, is for merging together the objects from multiple OARs. Merging also suppresses the loading of terrain and parcel data which is just what you want when merging objects. But, if loading multiple OARs to create a new, larger region, the terrain and parcel information must be loaded. Thus the new parameters.

Implementation Discussion (Obsolete)
Since this will be a major change to OpenSimulator that touches a lot of different parts, subsequent posts, will discuss the changes I'm making.

TerrainData
(note that following comments may not apply to version 0.9) One major problem is passing the terrain data from the region to the protocol stack. The existing implementation passed an array of floats that were presumed to be a 256x256 array of region terrain heights. The TerrainChannel class is an attempt to hide the terrain implementation from TerrainModule . TerrainChannel can't be passed into the protocol stack (LLClientView) because TerrainChannel is defined as part of OpenSim.Region.Framework which is not visible to the protocol code.

My solution is to create the TerrainData</tt> class in OpenSim.Framework.</tt> TerrainData</tt> just wraps the data structure for the terrain and additionally has the attributes giving X and Y size.

I didn't want to change the signature of IClientAPI since so many external modules rely on it. It should be changed to pass TerrainData</tt> rather than a float[]</tt>. I decided to not change IClientAPI but rather have LLClientView</tt> ignore the passed array and instead reach back into the associated scene and fetch the TerrainData</tt> instance.

There is one subclass of TerrainData</tt>: HeightmapTerrainData</tt> which keeps the terrain as a compressed heightmap. The height of each point is stored as a short</tt> which is height * compressionFactor</tt> where compressionFactor</tt> is usually "100". This creates a compact storage of the terrain heights with two decimal points of resolution.

The LLLP ("Linden Lab Legacy Protocol") sends terrain height data in compressed "patches" of 16x16 areas of the terrain height. This is a protocol feature that is implemented in TerrainChannel</tt>. I feel that underlying protocol optimizations shouldn't appear up the stack so, in creating TerrainData</tt>, I tried to hide terrain patches. I mean, someday terrain will be generalized meshes. Right? (wrong :p )

Terrain in the Database
Previously, terrain height maps were saved in the database as a blob of 256x256 doubles. To have different region sizes, that format had to change. There is an existing database field revision</tt> that stored the time the terrain was saved. This revision information wasn't used for anything so this field was co-opted to contain a revision code for the height field blob.

There are three forms for the height map blob: legacy, compressed2D and regular2D. "legacy" is, of course, the previous 256x256 collections of doubles. "regular2D" contains the X, Y dimensions and enough floats for that area's heights. "compressed2D" contains the X,Y and compressionFactor followed by enough shorts for the height map.

The database readers and writers default to the legacy format and, if the region happens to have the dimensions 256x256, it is stored using the legacy format. This is an attempt to keep downward compatibility.

All of the database modules (MySQL, SQLite, MSSQL and PSQL) have been modified to store terrain this new way.


 * some of this was removed in 0.9. Terrain is stored as a compressed array of floats (format V2DGzip).

Sensing Border Crossing
0.9 regions require 0.9 grid services. They can work on 0.8 grids, but keep view range above 256m

older versions:

Most of the ‘move to new region’ code is based on checking boundaries. There is much code related to computing if an object or avatar has crossed a region boundary and then computing the address of the next region from same. Introducing variable sized regions messes a lot of this computation up. That is, the code doing the arithmetic usually assumes it knows the address of the next region based on a known region size and then can compute the location of the next region based on that. With varregions those assumptions no longer hold. Varregion implementation means that the computation of region base locations and border locations moves to the GridService who is the entity who really knows the size of all the regions and what is adjacent to what.

The realization that location to region computation is really a GridService operation lead to a total rip apart of the grid boundary checking code and replacement of it with two functions: Scene.PositionIsInCurrentRegion(Vector3 pos)</tt> and then EntityTransferModule.GetRegionContainingWorldLocation(double X, double Y)</tt>. The former function tests to see if the object/avatar has moved out of the current region and the latter gets the region moved into. (Side note: <tt>GetRegionContainingWorldLocation</tt> should really be a function on <tt>IGridService</tt> but that exercise is left for future hacking).

These changes leave all the 'border' code in limbo -- the generation of the border lists is still there but it is not being used. This should eventually be cleaned up. Also, the computation of neighbor regions is scattered around with routines on <tt>IGridService</tt>, in <tt>EntityTransferModule</tt> and a bunch of bookkeeping in <tt>Scene</tt>. This too should be cleaned up.

Implementation Notes
Following is outdated for 0.9.

As of January 27, 2014, varregion is a feature of the 'master' repository branch.

Stuff to Work On
What follows are notes of things that might need work in OpenSimulator.


 * ITerrainLoader implementations (mostly done)
 * How to handle tiles.
 * How to handle large regions sizes.
 * Terrain/FileLoaders/*.cs all return an ITerrainChannel
 * Need to fix all the file reader/writers
 * FileLoaders/LLRAW.cs has several "256"s rather than constant references
 * Teleporting: should be able to teleport to anywhere in a large region
 * "MyRegion/550/687/40"
 * HG code needs to allow addresses anywhere into large regions
 * Need to look through RegionCombinerModule.cs and see what safety checks are needed
 * Maybe just prevent combination if not legacy region size
 * In LSL_Api.cs, llEdgeOfWorld does some neighbor computation. Check for ok'ness.
 * LandManagementModule.SendParcelOverlay sends land sale/ownership info for 4x4m areas
 * Looks like it expects to send exactly one 1024 byte block (for the 256x256 legacy region
 * Check viewer code for what will happen for larger regions
 * New overlay types added
 * LLClientView.SendMapBlockSplit needs to have region size to send with the map info
 * Region coords are sometimes 'int' and sometimes 'uint' with conversions EVERYWHERE
 * pass over everything and convert region coordinates it 'uint's
 * consider creating structures for world and region coordinates so there can be compile time checking

Viewer
<Aleric> radams1, SianaGearz, frnic : I couldn't help myself doing a profile anyway.. The reason the viewer is slow is because it's busy "drawing" the terrain. "drawing" between quotes because my guess is that it is trying to draw ALL terrain and then clip that afterwards to the frustrum... <Aleric> So, this could be greatly improved by disregarding terrain way way earlier when it is beyond the drawing range anyway. <SianaGearz> OK <Aleric> I disabled all rendering, even added a 'return' in terrain render <Aleric> but.. no improvement of FPS! oops <Aleric> LLSurfacePatch::updateVisibility is still eating a lot of cpu. <Aleric> LLSurface::updatePatchVisibilities does 97% of the calls to LLSurfacePatch::updateVisibility <Aleric> which then spends most of it's time calling LLCamera::AABBInFrustumNoFarClip
 * LLSurface is wired to expect adjacent regions of same size
 * Terrain surface image is sized with a 'static": "   static S32  sTextureSize;               // Size of the surface texture"
 * From FreeNode:#SingularityViewer 20131125:
 * Singularity commit for varregion: https://github.com/singularity-viewer/SingularityViewer/commit/ad8ea07a

Testing

 * Terrain/parcel operations
 * Default 256 standalone region
 * Clean database. Start single standalone region. Verify 'pimple' region. Verify one parcel. Flatten region.
 * Create parcels adjacent to each edge and standalone in middle. Change names of each parcel and verify. Restart simulator and verify parcels exist and work correctly.
 * Save five parcel region as OAR. Clear database. Restart region. Load oar. Verify 5 parcels of correct location, name and features.
 * Adjacent 256 standalone regions
 * 768x768 standalone region
 * Changing land from 256 to 768
 * Teleporting
 * Adjacency
 * Hypergrid