Compare commits

...

143 Commits

Author SHA1 Message Date
Robert Adams
7fc289c039 Properly restore position on crossing failure for mega-regions.
Fix odd "cannot cross into banned parcel" viewer error message when crossing
into non-existant region. Proper permission failure messages are now returned.
2014-02-15 17:02:53 -08:00
Robert Adams
877bdcdce1 Rewrite of mega-region code to use new form of border checking.
This commit eliminates all of the 'border' class and list code and replaces
it with testing if in the current region.
Impacts: can make a mega-region out of varregions of the same size; and
mega-region combinations must be rectangular (not square but rectangular)
2014-02-15 16:01:43 -08:00
Robert Adams
bdbbeaa494 Non-functional changes of numbers into symbolic references and a few
comments on what variables really mean.
2014-02-15 16:01:01 -08:00
Justin Clark-Casey (justincc)
f6913e911e Merge branch 'justincc-master' 2014-02-15 01:18:10 +00:00
Justin Clark-Casey (justincc)
f74aafaf63 In GridUserService, if a UUID is given consistently use the longest matched entry (as already done by GetGridUserInfo()) in order to avoid problems with multiple entries.
This is to avoid issues where LoggedIn, SetHome, etc were always using the exact UUID match but GetGridUserInfo() would use the longest.
Looks to address http://opensimulator.org/mantis/view.php?id=6986
2014-02-15 01:13:58 +00:00
Justin Clark-Casey (justincc)
4fed301e65 Don't request group information in SP.MakeRootAgent() if the presence belongs to no group (UUID.Zero)
This was trigger the XmlRpcGroups errors described in http://opensimulator.org/mantis/view.php?id=6986
Introduced in commit 5b73b9c4 (Wed Dec 11 01:39:56 2013)
2014-02-14 23:43:07 +00:00
Justin Clark-Casey (justincc)
733e067958 Log information about which function, request data and agent ID triggered an XmlRpcGroupsServiceConnector error 2014-02-14 21:28:45 +00:00
Justin Clark-Casey (justincc)
ffd0da23fb Merge branch 'justincc-master' 2014-02-14 00:16:17 +00:00
Justin Clark-Casey (justincc)
f49d513089 Change warns associated with UserAgentServiceConnector to debugs, as this is not necessarily a problen with the source simulator (e.g. someone else's remote simulator cannot be contacted).
This is Oren Hurvitz's second patch from http://opensimulator.org/mantis/view.php?id=6956 with a small amount of correction
2014-02-14 00:08:13 +00:00
Justin Clark-Casey (justincc)
fc35b45e21 If calls to UserAgentServiceConnector fail then throw an exception. This lets the caller decide whether to discard the error or not.
This is Oren Hurvitz's 0001 patch from http://opensimulator.org/mantis/view.php?id=6956 but I ended up doing some tweaking to resolve patch application issues.
2014-02-14 00:01:12 +00:00
Justin Clark-Casey (justincc)
bc7fda39b4 Merge branch 'justincc-master' 2014-02-12 23:23:56 +00:00
Justin Clark-Casey (justincc)
e10012a7a6 If a caller tries to queue a CAPs message to a scene presence that has no event queue (e.g. an NPC), only warn if event queue debugging is greater than zero.
Removes the spurious log warnings if groups are active when NPCs are used.
Adds more regression tests associated with adding messages to the event queue
2014-02-12 23:18:10 +00:00
Robert Adams
3a7c8d1f32 BulletSim: the minimum vehicle velocity was set too low so moving slow
was getting zeroed too easily. Added VehicleMinVelocity parameter.
2014-02-11 21:07:55 -08:00
Robert Adams
c0cc5e0fa4 varregion: Send large region patches for wind and clouds. 2014-02-11 21:07:55 -08:00
Mic Bowman
b83a224147 Add JsonRezAtRoot script function. Operation is very similar to
llRezAtRoot except that the start parameter is a Json string that will
be unpacked into a json store identified by the objects uuid.  This
makes a much more expressive (and simpler) way of passing initial
parameters to a rezzed object.
2014-02-09 18:07:49 -08:00
Robert Adams
4a1c1fc009 Add zadark to CONTRIBUTORS.txt 2014-02-06 11:47:19 -08:00
Roger Kirkman
cf61cdf58c Fix - Viewer crash during HG Teleport
Signed-off-by: Robert Adams <misterblue@misterblue.com>
2014-02-06 11:33:17 -08:00
Mic Bowman
1913ab5ad5 Update the SimianMaptile uploader to accommodate varregions. 2014-02-05 21:26:39 -08:00
Melanie
29c8461631 Remove the added whitespace, test concluded 2014-02-04 05:54:28 +00:00
Melanie
8f372b8ac8 Bot test 2014-02-04 05:52:22 +00:00
Robert Adams
342be228c6 Remove compile error from returning value in void function 2014-02-03 21:53:14 -08:00
Michael Cerquoni
87abf06956 test again undoing previous changes 2014-02-04 00:32:40 -05:00
Michael Cerquoni
f81841a2c5 this is just a test 2014-02-04 00:30:35 -05:00
Robert Adams
1b41ec0a85 Fix raw32 terrain heightmap reader so it estimates terrain size from
the size of the input stream. This is required since the raw heightmap
format (.r32) does not contain any size information.
The estimation relies on terrain being square.
2014-02-03 21:23:32 -08:00
Melanie
31cba5aa66 Add one check for a blank URL because the module wasn't quite sure not to work when unconfigured :| 2014-02-04 04:20:37 +00:00
Melanie
1aed6567a8 Samle configurations for the baked texture server. 2014-02-04 02:49:13 +00:00
Melanie
f0f852b27f Final code drop for appearance. Adding Avination's baked texture storage server. 2014-02-04 02:29:02 +00:00
Melanie
1197658233 Adding the Avination XBakesModule, the client for the persistent bakes system 2014-02-04 02:09:39 +00:00
Melanie
e1d1c27965 Merge branch 'master' of melanie@opensimulator.org:/var/git/opensim 2014-02-04 01:55:41 +00:00
Melanie
49c2213a01 Dropping the rest of Avination's modified appearance code for core.
Module to follow.
2014-02-04 01:54:16 +00:00
Robert Adams
41b6602a77 Add "--no-objects" parameter to 'load oar'. 2014-02-02 22:16:01 -08:00
Robert Adams
8c6a0cb44a Really add the old parameter names to load oar to keep downward compatibiliy 2014-02-02 17:18:08 -08:00
Robert Adams
2a4dd34616 Change new 'load oar' parameters to be hyphenated to be consistant with
existing parameters. ('--forceterrain' becomes '--force-terrain').
The old forms have been kept for downward compatiblity.
2014-02-02 12:21:18 -08:00
Robert Adams
9c97fb8e12 Implement terrain merging in TerrainChannel.
Modify archiver to use terrain merging when loading oars.
This makes displacement AND rotation properly work on terrain when loading oars.
Especially useful when loading legacy region oars into large varregions.
2014-02-02 11:17:49 -08:00
dahlia
a8e64cd59a Overload INPCModule.CreateNPC() to allow agentID to be specified. Note: this is intended for use in region modules and is not exposed to scripts. 2014-02-01 04:09:20 -08:00
Oren Hurvitz
abb193ec94 In UuidGatherer, gather materials referenced in the prim's TextureEntry
Signed-off-by: dahlia <dahlia@nomail>
2014-02-01 02:56:15 -08:00
Justin Clark-Casey (justincc)
b2878eb773 Merge branch 'justincc-master' 2014-01-31 00:51:22 +00:00
Oren Hurvitz
b8e22f02e7 Make sure Web streams are disposed after use 2014-01-31 00:44:22 +00:00
David Rowe
1380b37d71 Made error messages more consistent 2014-01-31 00:24:52 +00:00
David Rowe
3d62f4369d Replaced throwing exceptions with calls to Error() 2014-01-31 00:24:47 +00:00
David Rowe
d405254971 Replaced llSay()ing LSL errors with calls Error() 2014-01-31 00:24:43 +00:00
David Rowe
9db4090c07 Replaced LSLError() calls with calls to Error() 2014-01-31 00:24:40 +00:00
David Rowe
c9550e473d Updated "deprecated" LSL errors to use Deprecrated() 2014-01-31 00:24:36 +00:00
David Rowe
257f9cec40 Updated "not implemented" LSL errors to use NotImplemented() 2014-01-31 00:24:31 +00:00
David Rowe
13bb9ea682 Updated ShoutError() calls to use new LSL error methods 2014-01-31 00:21:22 +00:00
David Rowe
67ec95bde8 Updated methods for handling LSL script errors, deprecated, and not implemented 2014-01-31 00:20:10 +00:00
Justin Clark-Casey (justincc)
c467dfcd81 Merge branch 'justincc-master' 2014-01-30 00:42:20 +00:00
Justin Clark-Casey (justincc)
b73baeb4a4 Record whether login to home fails because no home set (UUID.Zero) or region not found. 2014-01-30 00:40:56 +00:00
Justin Clark-Casey (justincc)
b50e5704b8 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2014-01-30 00:06:09 +00:00
Justin Clark-Casey (justincc)
7807b19a89 Merge branch 'justincc-master' 2014-01-30 00:05:12 +00:00
Justin Clark-Casey (justincc)
bdab05df0e Add "show grid user" robust/standalone console command for debug purposes.
Shows all data on entries which match or start with a given ID.
This would usually be a UUID.
2014-01-30 00:03:22 +00:00
Robert Adams
0842e2e15b BulletSim: default physical terrain implementation to heightmap.
It originally looked like mesh terrain would perform better for vehicles
but, after much use, heightmap is the clear winner.
Force terrain implementation to heightmap if the physics region is
larger than legacy region size. This solves running out of memory for
very large regions.
2014-01-29 06:44:14 -08:00
Mic Bowman
fbf33ef1de Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2014-01-28 22:42:35 -08:00
Mic Bowman
3f5c6c897f One more run at fixing the sun module. Parameter setting fixed to
work through the OSSL interface. And setting the parameters now
adjusts all the dependent variables correctly so the sun moves
at the modified rate.
2014-01-28 22:40:39 -08:00
Robert Adams
1900254e77 Restore brush tests with correct values and adjustments for new terrain height
rounding characteristics.
2014-01-28 21:43:18 -08:00
Robert Adams
678c107915 Temporarily disable brush test. It will come back when tuning is complete. 2014-01-28 16:58:27 -08:00
Robert Adams
dde0e547a7 Change area of brush test to account for rounding errors in terrain implementation 2014-01-28 16:48:34 -08:00
Robert Adams
226b5e4d75 Increase the strength of brushes in brush test in an attempt to overcome rounding
introduced with new terrain height class.
2014-01-28 16:36:56 -08:00
Mic Bowman
cfe1bced7d Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2014-01-28 16:34:49 -08:00
Mic Bowman
2877c7d94d Actually make the parameter updates change the behavior of
sun movement.
2014-01-28 16:34:22 -08:00
Robert Adams
0c3493f619 clear land tainting when tested. Then testing, pass scene to LandObject as now needed to get region size 2014-01-28 16:25:01 -08:00
Robert Adams
d25265ae82 Fix terrain tests by properly initializing low detail terrain to zero height.
Also remove PI heightmap test as new heightmaps only have two significant digits.
2014-01-28 15:58:45 -08:00
Mic Bowman
1d533b0f01 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2014-01-28 15:47:24 -08:00
Mic Bowman
bfb0011cd3 Some major surgery on the sun module. Updates to the client were gated
by a "mode" check. That mode check has been removed (it didn't result
in any change of behavior anyway). Also added a command line variable
update to set the sun position time offset (offset from the system
clock). Default is no offset to preserve existing behavior.
2014-01-28 15:43:47 -08:00
Robert Adams
e5d59dc696 Repair database routines so they properly return null when asked for
the heighmap of a region that does not exist.
2014-01-28 15:29:06 -08:00
Robert Adams
8eec717f5f Merge branch 'master' into varregion 2014-01-28 08:50:28 -08:00
Robert Adams
4faf11e001 varregion: fix for teleporting by double clicking on a map location.
Thanks Garmin.
2014-01-28 08:49:22 -08:00
Robert Adams
49af6b53e7 varregion: enable teleporting to a varregion by clicking on the map and
pressing the 'teleport' button.
This commit adds returning region map info for all the subregions of a
varregion. This also handles the selection of the extra region and then
the displacement of the postion so the teleport is to the correct location.
2014-01-26 19:32:28 -08:00
Robert Adams
13a9d5409c Merge branch 'master' into varregion
Conflicts:
	OpenSim/Region/CoreModules/World/LegacyMap/MapImageModule.cs
	OpenSim/Region/CoreModules/World/LegacyMap/ShadedMapTileRenderer.cs
	OpenSim/Region/CoreModules/World/LegacyMap/TexturedMapTileRenderer.cs
2014-01-26 07:56:47 -08:00
Robert Adams
6831e58616 varregion: modify MapImageModule structure so it will better merge
with changes made in master.
2014-01-26 07:38:28 -08:00
Robert Adams
afb2e07111 varregion: pass region size in more HG services. 2014-01-24 06:30:38 -08:00
Mic Bowman
674a3a5639 Enable the simulator to handle region size information returned from
a hypergrid gatekeeper. Fields are "size_x" and "size_y". Server side
will be updated separately.
2014-01-23 16:20:18 -08:00
Robert Adams
4c362a83f9 Merge branch 'master' into varregion 2014-01-21 11:31:51 -08:00
Robert Adams
90fa3202c6 varregion: remove debugging splat file saving that fills up the
maptile dir and causes errors.
2014-01-19 13:37:51 -08:00
Robert Adams
5e6a47f13f varregion: remove --noterrain and --noparcel parameters in 'load oar'.
Add --forceterrain and --forceparcel to 'load oar'. In order to not change
the operation of --merge (which does an object merge and suppresses terrain
and parcel information loading), added the --force* parameters to be used
when loading multiple oars to build up a varregion.

Added --rotation and --rotationcenter parameters to 'load oar' which apply a rotation to
the loaded oar objects before displacing. The rotation is in degrees (pos or neg)
and the center defaults to "<128, 128, 0>".
2014-01-19 12:45:16 -08:00
Robert Adams
6fbfb47b92 varregion: add --noterrain and --noparcel to 'load oar'.
--noterrain suppresses the loading of the terrain from the oar.
--noparcels suppresses the loading of parcel information from the oar.
2014-01-19 11:03:08 -08:00
Robert Adams
dd6db72939 varregion: add --displacement parameter to 'load oar'.
Adds displacment to all objects and terrain loaded from the oar.
As an example, if you have a 512x512 region and an old 256x256 oar, doing
  load oar --displacement "<128,128,0>" oarFile.oar
will load the object (and terrain) into the middle of the 512x512 region.
If displacement is not specified, 'load oar' works like it always has.
If you have a 5
2014-01-19 10:09:43 -08:00
Robert Adams
f127e4b4ee Merge branch 'master' into varregion 2014-01-19 07:38:53 -08:00
Robert Adams
54a4b9eab4 varregion: Update Warp3D to properly handle varregions.
This includes additions to Warp3D:
   Optional rendering of mesh and scupltie prims (INI parameter. Off by default)
   Texturing of large prims (INI parameter. On by default)
   Better garbage collection
2014-01-19 07:33:56 -08:00
Robert Adams
813f0da00b Add J2K decoder routine that converts directly to an image. 2014-01-19 07:32:41 -08:00
Robert Adams
08fa0a6a8a Fix casting error for float type INI file parameter parsing. 2014-01-19 07:26:55 -08:00
Robert Adams
60de0bc3c2 varregion: split up generated maptile images for storage in map. This
makes maps for varregions show up properly.
2014-01-19 05:09:03 -08:00
Robert Adams
cc5cffc212 varregion: properly pack the region size parameters so he viewer will parse them.
This gets rid of the viewer crash when teleporting into varregions and allows
multiple, adjacent varregions (of the same size) with border crossings.
2014-01-11 22:00:52 -08:00
Robert Adams
1eea6fd452 varregion: Debug messages for region crossing debugging. 2014-01-11 22:00:24 -08:00
Robert Adams
3760d10cd0 varregion: remove unnecessary border checking code in ScenePresence. 2014-01-11 21:59:22 -08:00
Robert Adams
1cf17a3cf7 Merge branch 'master' into varregion
Conflicts:
	OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
	OpenSim/Region/Framework/Scenes/SceneBase.cs
	OpenSim/Services/Interfaces/IGridService.cs
	OpenSim/Services/LLLoginService/LLLoginResponse.cs
(conflicts were debug statements that are commented out in master branch)
2014-01-11 08:52:23 -08:00
Robert Adams
fd045d520e Merge branch 'master' into varregion
Conflicts:
	OpenSim/Framework/Constants.cs
	OpenSim/Framework/RegionInfo.cs
	OpenSim/Services/GridService/GridService.cs
	OpenSim/Services/Interfaces/IGridService.cs
Most conflicts had to do with Util routines not in master branch yet.
2014-01-04 08:57:51 -08:00
Robert Adams
9984ecf862 varregion: Add region size to teleport event messages (EnableSimulator,
CorssRegion, TeleportFinishEvent).
Have Simian grid service return the region size.
Many teleport related debug log messages. Can be removed when teleport
works (like that's ever going to happen).
2014-01-03 07:41:06 -08:00
Robert Adams
01c0bbf181 varregion: extract banned region logic into a class for cleanlyness.
Add 'not found' caching in EntityTransferModule.GetRegionContainingWorldLocation
so hitting borders and bad teleports do not continuiously hammer on the GridService.
2013-12-27 08:23:37 -08:00
Robert Adams
2d2bea4aa7 varregion: many more updates removing the constant RegionSize and replacing
with a passed region size. This time in the map code and grid services code.
2013-12-26 22:45:59 -08:00
Robert Adams
e5f7c8b6e8 varregion: add lots of DEBUG level log messages. Especially for teleport. 2013-12-24 12:31:26 -08:00
Robert Adams
b40b57776b varregion: remove unused Scene.HaveNeighbor routine. Its computation
was wrong for large regions anyway.
2013-12-24 12:28:54 -08:00
Robert Adams
5c9fa15f30 varregion: fix bug where destination region is not found and object is
not restored to its original location.
2013-12-24 11:51:50 -08:00
Robert Adams
a01862509e Merge branch 'master' into varregion 2013-12-20 06:52:28 -08:00
Robert Adams
6937eec258 Merge branch 'master' into varregion
Add new region crossing code to varregion

Conflicts:
	OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
	OpenSim/Region/Framework/Scenes/ScenePresence.cs
2013-12-17 06:18:13 -08:00
Robert Adams
13a9a4b653 varregion: rename 'LegacyRegionLocX' back to 'RegionLocX' and same for Y and Z.
Rename 'RegionWorldLocX' to 'WorldLocX' and same for Y and Z.
This keeps the downward compatibility and follows the scheme of 'region'
and 'world' location naming that is happening in the Util module.
2013-12-14 07:53:01 -08:00
Robert Adams
4eb52eb19e Merge branch 'master' into varregion 2013-12-05 21:07:44 -08:00
Robert Adams
31bacfbb63 Merge branch 'master' into varregion 2013-12-01 15:51:42 -08:00
Robert Adams
6cd0d7a62b varregion: Add MaxRegionSize constant and enforce in RegionInfo.
Intermediate checkin of changing border cross computation from checking
boundry limits to requests to GridService. Not totally functional.
2013-11-30 15:28:39 -08:00
Robert Adams
109136c074 varregion: add ITerrainChannel.GetHeightAtXYZ() for eventual mesh terrain.
Implementation of same in TerrainChannel.cs.
Check for bounds in TerrainChannel[x,y] to prevent array access exceptions.
2013-11-28 08:22:41 -08:00
Robert Adams
7aa00632b9 varregion: many replacements of in-place arithmetic with calls to
the Util functions for converting world addresses to region addresses
and converting region handles to locations.
2013-11-28 08:20:16 -08:00
Robert Adams
3193bcaae1 Merge branch 'master' into varregion 2013-11-24 07:23:12 -08:00
Robert Adams
604b39cea9 Merge branch 'master' into varregion 2013-11-15 14:56:13 -08:00
Robert Adams
d67236c09d Merge branch 'master' into varregion 2013-11-13 16:10:25 -08:00
Robert Adams
e2a1fa806d varregion: extend TerrainModule to use the region size from RegionInfo
rather than using Constants.RegionSize. This allows loading and saving
of terrain heightmaps that match the size of non-legacy sized regions.
2013-11-10 21:12:17 -08:00
Robert Adams
c12e68e34d varregion: fix GetLandObject error return and initialization of square
land object bitmaps. This fixes creation of child presences and the
editing of parcels.
Also lots of commented out debugging messages.
2013-11-10 19:52:31 -08:00
Robert Adams
7061784cc6 Merge branch 'master' into varregion 2013-11-08 20:55:28 -08:00
Robert Adams
beeec1c467 varregion: elimination of Constants.RegionSize from all over OpenSimulator.
Routines in Util to compute region world coordinates from region coordinates
as well as the conversion to and from region handles. These routines have
replaced a lot of math scattered throughout the simulator.
Should be no functional changes.
2013-11-08 20:53:37 -08:00
Robert Adams
a7a837550e varregion: Massive work to LandManagementModule and LandObject to
handle variable sized regions. Many changes for both the region and parcels.
Most of the constant "4" (for the 4x4 parcel units) have been replaced
with symbols and math.
2013-11-08 20:51:09 -08:00
Robert Adams
f7bd0da026 Merge branch 'master' into varregion 2013-11-06 06:32:11 -08:00
Robert Adams
a75ce7423c Merge branch 'master' into varregion 2013-11-05 21:42:27 -08:00
Robert Adams
d0854e4ace varregion: properly sense size of terrain heightmap and store as
compressed 2D database blob if a varregion.
2013-11-05 21:33:02 -08:00
Robert Adams
c931b16c1f Merge branch 'master' into varregion 2013-11-04 22:12:57 -08:00
Robert Adams
f2810bf03a varregion: add plumbing to pass region size from Scene down to the
physics engine.
Older physics engines will default to the legacy region size.
Update BulletSim to use the new region size information.
2013-11-04 22:10:54 -08:00
Robert Adams
cd1a23fc14 varregion: remove uses of region size constant. In particular, update scene
to check for border crossings based on the size of the region.
2013-11-04 22:09:52 -08:00
Robert Adams
ac94dc8a14 varregion: remove unused terrain serialization code in SQLite and PGSQL modules 2013-11-04 22:06:20 -08:00
Robert Adams
9bf363e9be varregion: send the proper terrain patch layer code for large terrain.
Code cleanups.
2013-11-03 08:14:51 -08:00
Robert Adams
79b031bd0c varregion: send region size in LLLoginResponse. 2013-11-02 15:42:26 -07:00
Robert Adams
6df7d4219d varregion: add linkage for region size in creations and conversions
of GridRegion. New variables for size and code to initialize same.
2013-11-02 15:40:48 -07:00
Robert Adams
f66737fe56 varregion: Enforce the configuration of square regions in RegionInfo.cs. 2013-11-02 15:36:16 -07:00
Robert Adams
92c06a5d0b varregion: fix lawn-mower terrain fill so it works for non-square regions.
Add some debugging logs on region creation to report region size.
2013-11-01 16:37:27 -07:00
Robert Adams
976530569a varregion: enforce multiple of 256 for region size when parameters fetched from user.
Output info and warning messages for non-legacy region sizes.
2013-11-01 16:06:19 -07:00
Robert Adams
ff5885ab23 varregion: push TerrainData implementation up and down the database storage stack.
Implement both LoadTerrain and StoreTerrain for all DBs.
Move all database blob serialization/deserialization into TerrainData.
2013-11-01 11:35:31 -07:00
Robert Adams
39777db8ef varregion: fix problem of X/Y dimensions swapped and incorrect terrain
compression base computation.
Complete replacement of float[] for terrain heightmap with TerrainData instance.
2013-10-31 09:24:06 -07:00
Robert Adams
2be0347f50 Merge branch 'master' into varregion 2013-10-28 09:30:26 -07:00
Robert Adams
c581506058 varregion: update PGSQL driver for storing variable terrain size blobs. 2013-10-16 08:13:06 -07:00
Robert Adams
8937a2244d Merge branch 'master' into varregion 2013-10-16 07:53:44 -07:00
Robert Adams
97bc5263de varregion: move the compressed heighmap compression factor from
Constants into TerrainData.
Save compression factor with the terrain blob in the database.
2013-10-16 07:52:30 -07:00
Robert Adams
86bf79aa2b Merge branch 'master' into varregion 2013-10-07 13:58:17 -07:00
Robert Adams
7416809077 varregion: plug in TerrainData class and modify TerrainModule and LLClientView to use same. This passes a terrain info class around rather than passing a one dimensional array thus allowing variable regions. Update the database storage for variable region sizes. This should be downward compatible (same format for 256x256 regions). 2013-10-07 13:57:40 -07:00
Robert Adams
25ae59b9eb varregion: remove scattered use of Constants.RegionSize by having routines reference RegionInfo.RegionWorldLoc?. 2013-10-07 13:57:30 -07:00
Robert Adams
9b150194f6 varregion: add new TerrainData and TerrainCompressor routines. TerrainCompressor needed to replace the one in libopenmetaverse that doesn't know about the larger terrain packets. 2013-10-07 13:57:16 -07:00
Robert Adams
8c432c7c9b Merge branch 'varregion' of git://opensimulator.org/git/opensim into varregion 2013-09-28 07:35:30 -07:00
Robert Adams
8c1d80fdfd varregion: serious rework of TerrainChannel:
-- addition of varaible region size in X and Y
    -- internal storage of heightmap changed from double[] to short[]
    -- helper routines for handling internal structure while keeping existing API
    -- to and from XML that adds region size information (for downward compatibility,
        output in the legacy XML format if X and Y are 256)
Updated and commented Constants.RegionSize but didn't change the name for compatibility.
2013-09-28 07:33:56 -07:00
Robert Adams
aea5d3a842 Remove time based terrain storage in SQLite so revision number can be used
to denote terrain format revision.
Add terrain DB format revision codes to ISimulationDataStore.cs.
Setup so legacy compatible terrain storage and fetch is possible while
allowing future format extensions.
2013-09-28 07:33:55 -07:00
Robert Adams
fa1c688342 varregion: go back to using Constants.RegionSize so as not to break
external modules. People shouldn't use it but don't want to cause
too much breakage of legacy modules.
2013-09-28 07:33:54 -07:00
Robert Adams
139639d25e VarRegion: add RegionSize[XYZ] to RegionInfo.cs.
Update RegionInfo parameter and serialization routines to serialize
the region size if it is not the LegacyRegionSize.
2013-09-28 07:33:53 -07:00
Robert Adams
317c04fe17 VarRegion: change RegionInfo storage of region coordinates from region
count number to integer world coordinates.
Added new methods RegionWorldLoc[XY].
Refactored name of 'RegionLoc*' to 'LegacyRegionLoc*' throughout OpenSim.
Kept old 'RegionLoc*' entrypoint to RegionInfo for downward compatability
of external region management packages.
2013-09-28 07:33:52 -07:00
Robert Adams
fbc9072a5c varregion: serious rework of TerrainChannel:
-- addition of varaible region size in X and Y
    -- internal storage of heightmap changed from double[] to short[]
    -- helper routines for handling internal structure while keeping existing API
    -- to and from XML that adds region size information (for downward compatibility,
        output in the legacy XML format if X and Y are 256)
Updated and commented Constants.RegionSize but didn't change the name for compatibility.
2013-09-25 17:30:53 -07:00
Robert Adams
7fa64cce7d Remove time based terrain storage in SQLite so revision number can be used
to denote terrain format revision.
Add terrain DB format revision codes to ISimulationDataStore.cs.
Setup so legacy compatible terrain storage and fetch is possible while
allowing future format extensions.
2013-09-25 17:30:51 -07:00
Robert Adams
ab1474b5de varregion: go back to using Constants.RegionSize so as not to break
external modules. People shouldn't use it but don't want to cause
too much breakage of legacy modules.
2013-09-25 17:30:49 -07:00
Robert Adams
0765a83a8c VarRegion: add RegionSize[XYZ] to RegionInfo.cs.
Update RegionInfo parameter and serialization routines to serialize
the region size if it is not the LegacyRegionSize.
2013-09-25 17:30:47 -07:00
Robert Adams
96abbbb6fb VarRegion: change RegionInfo storage of region coordinates from region
count number to integer world coordinates.
Added new methods RegionWorldLoc[XY].
Refactored name of 'RegionLoc*' to 'LegacyRegionLoc*' throughout OpenSim.
Kept old 'RegionLoc*' entrypoint to RegionInfo for downward compatability
of external region management packages.
2013-09-25 17:30:45 -07:00
142 changed files with 6167 additions and 3261 deletions

View File

@@ -145,6 +145,7 @@ what it is today.
* Richard Alimi (IBM)
* Rick Alther (IBM)
* Rob Smart (IBM)
* Roger Kirkman (zadark)
* rtomita
* Ruud Lathorp
* SachaMagne

View File

@@ -1022,7 +1022,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
// Set home position
GridRegion home = scene.GridService.GetRegionByPosition(scopeID,
(int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize));
(int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation));
if (null == home)
{
m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", firstName, lastName);
@@ -1252,7 +1252,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
if ((null != regionXLocation) && (null != regionYLocation))
{
GridRegion home = scene.GridService.GetRegionByPosition(scopeID,
(int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize));
(int)Util.RegionToWorldLoc((uint)regionXLocation), (int)Util.RegionToWorldLoc((uint)regionYLocation));
if (null == home) {
m_log.WarnFormat("[RADMIN]: Unable to set home region for updated user account {0} {1}", firstName, lastName);
} else {
@@ -1484,8 +1484,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
}
IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
Dictionary<string, object> archiveOptions = new Dictionary<string,object>();
if (mergeOar) archiveOptions.Add("merge", null);
if (skipAssets) archiveOptions.Add("skipAssets", null);
if (archiver != null)
archiver.DearchiveRegion(filename, mergeOar, skipAssets, Guid.Empty);
archiver.DearchiveRegion(filename, Guid.Empty, archiveOptions);
else
throw new Exception("Archiver module not present for scene");
@@ -2881,7 +2884,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
// Set home position
GridRegion home = scene.GridService.GetRegionByPosition(scopeID,
(int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize));
(int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation));
if (null == home) {
m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", names[0], names[1]);
} else {

View File

@@ -54,12 +54,12 @@ namespace OpenSim.Data
/// <summary>
/// Return the x-coordinate of this region.
/// </summary>
public int coordX { get { return posX / (int)Constants.RegionSize; } }
public int coordX { get { return (int)Util.WorldToRegionLoc((uint)posX); } }
/// <summary>
/// Return the y-coordinate of this region.
/// </summary>
public int coordY { get { return posY / (int)Constants.RegionSize; } }
public int coordY { get { return (int)Util.WorldToRegionLoc((uint)posY); } }
public Dictionary<string, object> Data;
}

View File

@@ -49,6 +49,7 @@ namespace OpenSim.Data.MSSQL
// private static FileSystemDataStore Instance = new FileSystemDataStore();
private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[REGION DB MSSQL]";
/// <summary>
/// The database manager
@@ -530,43 +531,53 @@ ELSE
/// <returns></returns>
public double[,] LoadTerrain(UUID regionID)
{
double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize];
terrain.Initialize();
double[,] ret = null;
TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
if (terrData != null)
ret = terrData.GetDoubles();
return ret;
}
// Returns 'null' if region not found
public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
{
TerrainData terrData = null;
string sql = "select top 1 RegionUUID, Revision, Heightfield from terrain where RegionUUID = @RegionUUID order by Revision desc";
using (SqlConnection conn = new SqlConnection(m_connectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
// MySqlParameter param = new MySqlParameter();
cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
int rev;
if (reader.Read())
// MySqlParameter param = new MySqlParameter();
cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]);
BinaryReader br = new BinaryReader(str);
for (int x = 0; x < (int)Constants.RegionSize; x++)
int rev;
if (reader.Read())
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
terrain[x, y] = br.ReadDouble();
}
rev = (int)reader["Revision"];
byte[] blob = (byte[])reader["Heightfield"];
terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
}
rev = (int)reader["Revision"];
else
{
_Log.Info("[REGION DB]: No terrain found for region");
return null;
}
_Log.Info("[REGION DB]: Loaded terrain revision r" + rev);
}
else
{
_Log.Info("[REGION DB]: No terrain found for region");
return null;
}
_Log.Info("[REGION DB]: Loaded terrain revision r" + rev);
}
}
return terrain;
return terrData;
}
// Legacy entry point for when terrain was always a 256x256 hieghtmap
public void StoreTerrain(double[,] ter, UUID regionID)
{
StoreTerrain(new HeightmapTerrainData(ter), regionID);
}
/// <summary>
@@ -574,10 +585,8 @@ ELSE
/// </summary>
/// <param name="terrain">terrain map data.</param>
/// <param name="regionID">regionID.</param>
public void StoreTerrain(double[,] terrain, UUID regionID)
public void StoreTerrain(TerrainData terrData, UUID regionID)
{
int revision = Util.UnixTimeSinceEpoch();
//Delete old terrain map
string sql = "delete from terrain where RegionUUID=@RegionUUID";
using (SqlConnection conn = new SqlConnection(m_connectionString))
@@ -590,17 +599,23 @@ ELSE
sql = "insert into terrain(RegionUUID, Revision, Heightfield) values(@RegionUUID, @Revision, @Heightfield)";
int terrainDBRevision;
Array terrainDBblob;
terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
using (SqlConnection conn = new SqlConnection(m_connectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
cmd.Parameters.Add(_Database.CreateParameter("@Revision", revision));
cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", serializeTerrain(terrain)));
conn.Open();
cmd.ExecuteNonQuery();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision));
cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob));
conn.Open();
cmd.ExecuteNonQuery();
}
}
_Log.Info("[REGION DB]: Stored terrain revision r " + revision);
_Log.InfoFormat("{0} Stored terrain revision r={1}", LogHeader, terrainDBRevision);
}
/// <summary>
@@ -1344,30 +1359,6 @@ VALUES
#region Private Methods
/// <summary>
/// Serializes the terrain data for storage in DB.
/// </summary>
/// <param name="val">terrain data</param>
/// <returns></returns>
private static Array serializeTerrain(double[,] val)
{
MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) * sizeof(double));
BinaryWriter bw = new BinaryWriter(str);
// TODO: COMPATIBILITY - Add byte-order conversions
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
double height = val[x, y];
if (height == 0.0)
height = double.Epsilon;
bw.Write(height);
}
return str.ToArray();
}
/// <summary>
/// Stores new regionsettings.
/// </summary>

View File

@@ -48,6 +48,7 @@ namespace OpenSim.Data.MySQL
public class MySQLSimulationData : ISimulationDataStore
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[REGION DB MYSQL]";
private string m_connectionString;
private object m_dbLock = new object();
@@ -91,7 +92,7 @@ namespace OpenSim.Data.MySQL
}
catch (Exception e)
{
m_log.Error("[REGION DB]: MySQL error in ExecuteReader: " + e.Message);
m_log.ErrorFormat("{0} MySQL error in ExecuteReader: {1}", LogHeader, e);
throw;
}
@@ -572,10 +573,14 @@ namespace OpenSim.Data.MySQL
}
}
// Legacy entry point for when terrain was always a 256x256 hieghtmap
public void StoreTerrain(double[,] ter, UUID regionID)
{
m_log.Info("[REGION DB]: Storing terrain");
StoreTerrain(new HeightmapTerrainData(ter), regionID);
}
public void StoreTerrain(TerrainData terrData, UUID regionID)
{
lock (m_dbLock)
{
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
@@ -589,11 +594,18 @@ namespace OpenSim.Data.MySQL
ExecuteNonQuery(cmd);
cmd.CommandText = "insert into terrain (RegionUUID, " +
"Revision, Heightfield) values (?RegionUUID, " +
"1, ?Heightfield)";
cmd.Parameters.AddWithValue("Heightfield", SerializeTerrain(ter));
int terrainDBRevision;
Array terrainDBblob;
terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
m_log.InfoFormat("{0} Storing terrain. X={1}, Y={2}, rev={3}",
LogHeader, terrData.SizeX, terrData.SizeY, terrainDBRevision);
cmd.CommandText = "insert into terrain (RegionUUID, Revision, Heightfield)"
+ "values (?RegionUUID, ?Revision, ?Heightfield)";
cmd.Parameters.AddWithValue("Revision", terrainDBRevision);
cmd.Parameters.AddWithValue("Heightfield", terrainDBblob);
ExecuteNonQuery(cmd);
}
@@ -601,9 +613,20 @@ namespace OpenSim.Data.MySQL
}
}
// Legacy region loading
public double[,] LoadTerrain(UUID regionID)
{
double[,] terrain = null;
double[,] ret = null;
TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
if (terrData != null)
ret = terrData.GetDoubles();
return ret;
}
// Returns 'null' if region not found
public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
{
TerrainData terrData = null;
lock (m_dbLock)
{
@@ -623,32 +646,15 @@ namespace OpenSim.Data.MySQL
while (reader.Read())
{
int rev = Convert.ToInt32(reader["Revision"]);
terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize];
terrain.Initialize();
using (MemoryStream mstr = new MemoryStream((byte[])reader["Heightfield"]))
{
using (BinaryReader br = new BinaryReader(mstr))
{
for (int x = 0; x < (int)Constants.RegionSize; x++)
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
terrain[x, y] = br.ReadDouble();
}
}
}
m_log.InfoFormat("[REGION DB]: Loaded terrain revision r{0}", rev);
}
byte[] blob = (byte[])reader["Heightfield"];
terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
}
}
}
}
}
return terrain;
return terrData;
}
public void RemoveLandObject(UUID globalID)
@@ -1524,30 +1530,6 @@ namespace OpenSim.Data.MySQL
return entry;
}
/// <summary>
///
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
private static Array SerializeTerrain(double[,] val)
{
MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) *sizeof (double));
BinaryWriter bw = new BinaryWriter(str);
// TODO: COMPATIBILITY - Add byte-order conversions
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
double height = val[x, y];
if (height == 0.0)
height = double.Epsilon;
bw.Write(height);
}
return str.ToArray();
}
/// <summary>
/// Fill the prim command with prim values
/// </summary>

View File

@@ -132,15 +132,33 @@ namespace OpenSim.Data.Null
return new List<SceneObjectGroup>();
}
Dictionary<UUID, double[,]> m_terrains = new Dictionary<UUID, double[,]>();
public void StoreTerrain(double[,] ter, UUID regionID)
Dictionary<UUID, TerrainData> m_terrains = new Dictionary<UUID, TerrainData>();
public void StoreTerrain(TerrainData ter, UUID regionID)
{
if (m_terrains.ContainsKey(regionID))
m_terrains.Remove(regionID);
m_terrains.Add(regionID, ter);
}
// Legacy. Just don't do this.
public void StoreTerrain(double[,] ter, UUID regionID)
{
TerrainData terrData = new HeightmapTerrainData(ter);
StoreTerrain(terrData, regionID);
}
// Legacy. Just don't do this.
// Returns 'null' if region not found
public double[,] LoadTerrain(UUID regionID)
{
if (m_terrains.ContainsKey(regionID))
{
return m_terrains[regionID].GetDoubles();
}
return null;
}
public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
{
if (m_terrains.ContainsKey(regionID))
{

View File

@@ -46,6 +46,7 @@ namespace OpenSim.Data.PGSQL
public class PGSQLSimulationData : ISimulationDataStore
{
private const string _migrationStore = "RegionStore";
private const string LogHeader = "[REGION DB PGSQL]";
// private static FileSystemDataStore Instance = new FileSystemDataStore();
private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -523,44 +524,54 @@ namespace OpenSim.Data.PGSQL
/// <returns></returns>
public double[,] LoadTerrain(UUID regionID)
{
double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize];
terrain.Initialize();
double[,] ret = null;
TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
if (terrData != null)
ret = terrData.GetDoubles();
return ret;
}
// Returns 'null' if region not found
public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
{
TerrainData terrData = null;
string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain
where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; ";
using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
// PGSqlParameter param = new PGSqlParameter();
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
conn.Open();
using (NpgsqlDataReader reader = cmd.ExecuteReader())
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
int rev;
if (reader.Read())
// PGSqlParameter param = new PGSqlParameter();
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
conn.Open();
using (NpgsqlDataReader reader = cmd.ExecuteReader())
{
MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]);
BinaryReader br = new BinaryReader(str);
for (int x = 0; x < (int)Constants.RegionSize; x++)
int rev;
if (reader.Read())
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
terrain[x, y] = br.ReadDouble();
}
rev = Convert.ToInt32(reader["Revision"]);
byte[] blob = (byte[])reader["Heightfield"];
terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
}
rev = (int)reader["Revision"];
else
{
_Log.Info("[REGION DB]: No terrain found for region");
return null;
}
_Log.Info("[REGION DB]: Loaded terrain revision r" + rev);
}
else
{
_Log.Info("[REGION DB]: No terrain found for region");
return null;
}
_Log.Info("[REGION DB]: Loaded terrain revision r" + rev);
}
}
return terrain;
return terrData;
}
// Legacy entry point for when terrain was always a 256x256 heightmap
public void StoreTerrain(double[,] terrain, UUID regionID)
{
StoreTerrain(new HeightmapTerrainData(terrain), regionID);
}
/// <summary>
@@ -568,35 +579,43 @@ namespace OpenSim.Data.PGSQL
/// </summary>
/// <param name="terrain">terrain map data.</param>
/// <param name="regionID">regionID.</param>
public void StoreTerrain(double[,] terrain, UUID regionID)
public void StoreTerrain(TerrainData terrData, UUID regionID)
{
int revision = Util.UnixTimeSinceEpoch();
//Delete old terrain map
string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID";
using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
conn.Open();
cmd.ExecuteNonQuery();
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
conn.Open();
cmd.ExecuteNonQuery();
_Log.InfoFormat("{0} Deleted terrain revision id = {1}", LogHeader, regionID);
}
}
_Log.Info("[REGION DB]: Deleted terrain revision r " + revision);
int terrainDBRevision;
Array terrainDBblob;
terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)";
using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
cmd.Parameters.Add(_Database.CreateParameter("Revision", revision));
cmd.Parameters.Add(_Database.CreateParameter("Heightfield", serializeTerrain(terrain)));
conn.Open();
cmd.ExecuteNonQuery();
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
cmd.Parameters.Add(_Database.CreateParameter("Revision", terrainDBRevision));
cmd.Parameters.Add(_Database.CreateParameter("Heightfield", terrainDBblob));
conn.Open();
cmd.ExecuteNonQuery();
_Log.InfoFormat("{0} Stored terrain id = {1}, terrainSize = <{2},{3}>",
LogHeader, regionID, terrData.SizeX, terrData.SizeY);
}
}
_Log.Info("[REGION DB]: Stored terrain revision r " + revision);
}
/// <summary>
@@ -1349,30 +1368,6 @@ namespace OpenSim.Data.PGSQL
#region Private Methods
/// <summary>
/// Serializes the terrain data for storage in DB.
/// </summary>
/// <param name="val">terrain data</param>
/// <returns></returns>
private static Array serializeTerrain(double[,] val)
{
MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) * sizeof(double));
BinaryWriter bw = new BinaryWriter(str);
// TODO: COMPATIBILITY - Add byte-order conversions
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
double height = val[x, y];
if (height == 0.0)
height = double.Epsilon;
bw.Write(height);
}
return str.ToArray();
}
/// <summary>
/// Stores new regionsettings.
/// </summary>

View File

@@ -51,6 +51,7 @@ namespace OpenSim.Data.SQLite
public class SQLiteSimulationData : ISimulationDataStore
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[REGION DB SQLLITE]";
private const string primSelect = "select * from prims";
private const string shapeSelect = "select * from primshapes";
@@ -819,45 +820,44 @@ namespace OpenSim.Data.SQLite
prim.Inventory.RestoreInventoryItems(inventory);
}
// Legacy entry point for when terrain was always a 256x256 hieghtmap
public void StoreTerrain(double[,] ter, UUID regionID)
{
StoreTerrain(new HeightmapTerrainData(ter), regionID);
}
/// <summary>
/// Store a terrain revision in region storage
/// </summary>
/// <param name="ter">terrain heightfield</param>
/// <param name="regionID">region UUID</param>
public void StoreTerrain(double[,] ter, UUID regionID)
public void StoreTerrain(TerrainData terrData, UUID regionID)
{
lock (ds)
{
int revision = Util.UnixTimeSinceEpoch();
// This is added to get rid of the infinitely growing
// terrain databases which negatively impact on SQLite
// over time. Before reenabling this feature there
// needs to be a limitter put on the number of
// revisions in the database, as this old
// implementation is a DOS attack waiting to happen.
using (
SqliteCommand cmd =
new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID and Revision <= :Revision",
m_conn))
SqliteCommand cmd = new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID", m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Revision", revision));
cmd.ExecuteNonQuery();
}
// the following is an work around for .NET. The perf
// issues associated with it aren't as bad as you think.
m_log.Debug("[SQLITE REGION DB]: Storing terrain revision r" + revision.ToString());
String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" +
" values(:RegionUUID, :Revision, :Heightfield)";
int terrainDBRevision;
Array terrainDBblob;
terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
m_log.DebugFormat("{0} Storing terrain revision r {1}", LogHeader, terrainDBRevision);
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Revision", revision));
cmd.Parameters.Add(new SqliteParameter(":Heightfield", serializeTerrain(ter)));
cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision));
cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob));
cmd.ExecuteNonQuery();
}
}
@@ -870,11 +870,20 @@ namespace OpenSim.Data.SQLite
/// <returns>Heightfield data</returns>
public double[,] LoadTerrain(UUID regionID)
{
double[,] ret = null;
TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
if (terrData != null)
ret = terrData.GetDoubles();
return ret;
}
// Returns 'null' if region not found
public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
{
TerrainData terrData = null;
lock (ds)
{
double[,] terret = new double[(int)Constants.RegionSize, (int)Constants.RegionSize];
terret.Initialize();
String sql = "select RegionUUID, Revision, Heightfield from terrain" +
" where RegionUUID=:RegionUUID order by Revision desc";
@@ -887,21 +896,9 @@ namespace OpenSim.Data.SQLite
int rev = 0;
if (row.Read())
{
// TODO: put this into a function
using (MemoryStream str = new MemoryStream((byte[])row["Heightfield"]))
{
using (BinaryReader br = new BinaryReader(str))
{
for (int x = 0; x < (int)Constants.RegionSize; x++)
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
{
terret[x, y] = br.ReadDouble();
}
}
}
}
rev = Convert.ToInt32(row["Revision"]);
byte[] blob = (byte[])row["Heightfield"];
terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
}
else
{
@@ -912,8 +909,8 @@ namespace OpenSim.Data.SQLite
m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString());
}
}
return terret;
}
return terrData;
}
public void RemoveLandObject(UUID globalID)
@@ -2016,40 +2013,6 @@ namespace OpenSim.Data.SQLite
return entry;
}
/// <summary>
///
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
private static Array serializeTerrain(double[,] val)
{
MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) * sizeof(double));
BinaryWriter bw = new BinaryWriter(str);
// TODO: COMPATIBILITY - Add byte-order conversions
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int y = 0; y < (int)Constants.RegionSize; y++)
bw.Write(val[x, y]);
return str.ToArray();
}
// private void fillTerrainRow(DataRow row, UUID regionUUID, int rev, double[,] val)
// {
// row["RegionUUID"] = regionUUID;
// row["Revision"] = rev;
// MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize)*sizeof (double));
// BinaryWriter bw = new BinaryWriter(str);
// // TODO: COMPATIBILITY - Add byte-order conversions
// for (int x = 0; x < (int)Constants.RegionSize; x++)
// for (int y = 0; y < (int)Constants.RegionSize; y++)
// bw.Write(val[x, y]);
// row["Heightfield"] = str.ToArray();
// }
/// <summary>
///
/// </summary>

View File

@@ -27,6 +27,7 @@
using System;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace OpenSim.Framework
{
@@ -40,9 +41,26 @@ namespace OpenSim.Framework
public byte WaterHeight;
public ushort X;
public ushort Y;
public ushort SizeX;
public ushort SizeY;
public MapBlockData()
{
}
public OSDMap ToOSD()
{
OSDMap map = new OSDMap();
map["X"] = X;
map["Y"] = Y;
map["SizeX"] = SizeX;
map["SizeY"] = SizeY;
map["Name"] = Name;
map["Access"] = Access;
map["RegionFlags"] = RegionFlags;
map["WaterHeight"] = WaterHeight;
map["MapImageID"] = MapImageId;
return map;
}
}
}

View File

@@ -26,6 +26,7 @@
*/
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace OpenSim.Framework
{
@@ -37,5 +38,37 @@ namespace OpenSim.Framework
public int Extra;
public int Extra2;
public string name;
public mapItemReply(uint pX, uint pY, UUID pId, string pName, int pExt1, int pExt2)
{
x = pX;
y = pY;
id = pId;
name = pName;
Extra = pExt1;
Extra2 = pExt2;
}
public OSDMap ToOSD()
{
OSDMap map = new OSDMap();
map["X"] = OSD.FromInteger((int)x);
map["Y"] = OSD.FromInteger((int)y);
map["ID"] = OSD.FromUUID(id);
map["Name"] = OSD.FromString(name);
map["Extra"] = OSD.FromInteger(Extra);
map["Extra2"] = OSD.FromInteger(Extra2);
return map;
}
public void FromOSD(OSDMap map)
{
x = (uint) map["X"].AsInteger();
y = (uint) map["Y"].AsInteger();
id = map["ID"].AsUUID();
Extra = map["Extra"].AsInteger();
Extra2 = map["Extra2"].AsInteger();
name = map["Name"].AsString();
}
}
}

View File

@@ -0,0 +1,423 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using OpenMetaverse;
using log4net;
namespace OpenSim.Framework
{
public abstract class TerrainData
{
// Terrain always is a square
public int SizeX { get; protected set; }
public int SizeY { get; protected set; }
public int SizeZ { get; protected set; }
// A height used when the user doesn't specify anything
public const float DefaultTerrainHeight = 21f;
public abstract float this[int x, int y] { get; set; }
// Someday terrain will have caves
public abstract float this[int x, int y, int z] { get; set; }
public bool IsTainted { get; protected set; }
public abstract bool IsTaintedAt(int xx, int yy);
public abstract void ClearTaint();
public abstract void ClearLand();
public abstract void ClearLand(float height);
// Return a representation of this terrain for storing as a blob in the database.
// Returns 'true' to say blob was stored in the 'out' locations.
public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
// Given a revision code and a blob from the database, create and return the right type of TerrainData.
// The sizes passed are the expected size of the region. The database info will be used to
// initialize the heightmap of that sized region with as much data is in the blob.
// Return created TerrainData or 'null' if unsuccessful.
public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob)
{
// For the moment, there is only one implementation class
return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob);
}
// return a special compressed representation of the heightmap in shorts
public abstract short[] GetCompressedMap();
public abstract float CompressionFactor { get; }
public abstract double[,] GetDoubles();
public abstract TerrainData Clone();
}
// The terrain is stored in the database as a blob with a 'revision' field.
// Some implementations of terrain storage would fill the revision field with
// the time the terrain was stored. When real revisions were added and this
// feature removed, that left some old entries with the time in the revision
// field.
// Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
// left over and it is presumed to be 'Legacy256'.
// Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
// If a revision does not match any of these, it is assumed to be Legacy256.
public enum DBTerrainRevision
{
// Terrain is 'double[256,256]'
Legacy256 = 11,
// Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions
// The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
Variable2D = 22,
// Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions
// and third int is the 'compression factor'. The heights are compressed as
// "short compressedHeight = (short)(height * compressionFactor);"
// The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
Compressed2D = 27,
// A revision that is not listed above or any revision greater than this value is 'Legacy256'.
RevisionHigh = 1234
}
// Version of terrain that is a heightmap.
// This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
// of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
// The heighmap is kept as an array of short integers. The integer values are converted to
// and from floats by TerrainCompressionFactor. Shorts are used to limit storage used.
public class HeightmapTerrainData : TerrainData
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[HEIGHTMAP TERRAIN DATA]";
// TerrainData.this[x, y]
public override float this[int x, int y]
{
get { return FromCompressedHeight(m_heightmap[x, y]); }
set {
short newVal = ToCompressedHeight(value);
if (m_heightmap[x, y] != newVal)
{
m_heightmap[x, y] = newVal;
m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
}
}
}
// TerrainData.this[x, y, z]
public override float this[int x, int y, int z]
{
get { return this[x, y]; }
set { this[x, y] = value; }
}
// TerrainData.ClearTaint
public override void ClearTaint()
{
IsTainted = false;
for (int ii = 0; ii < m_taint.GetLength(0); ii++)
for (int jj = 0; jj < m_taint.GetLength(1); jj++)
m_taint[ii, jj] = false;
}
// TerrainData.ClearLand
public override void ClearLand()
{
ClearLand(DefaultTerrainHeight);
}
// TerrainData.ClearLand(float)
public override void ClearLand(float pHeight)
{
short flatHeight = ToCompressedHeight(pHeight);
for (int xx = 0; xx < SizeX; xx++)
for (int yy = 0; yy < SizeY; yy++)
m_heightmap[xx, yy] = flatHeight;
}
public override bool IsTaintedAt(int xx, int yy)
{
int tx = xx / Constants.TerrainPatchSize;
int ty = yy / Constants.TerrainPatchSize;
bool ret = m_taint[tx, ty];
m_taint[tx, ty] = false;
return ret;
}
// TerrainData.GetDatabaseBlob
// The user wants something to store in the database.
public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
{
bool ret = false;
if (SizeX == Constants.RegionSize && SizeY == Constants.RegionSize)
{
DBRevisionCode = (int)DBTerrainRevision.Legacy256;
blob = ToLegacyTerrainSerialization();
ret = true;
}
else
{
DBRevisionCode = (int)DBTerrainRevision.Compressed2D;
blob = ToCompressedTerrainSerialization();
ret = true;
}
return ret;
}
// TerrainData.CompressionFactor
private float m_compressionFactor = 100.0f;
public override float CompressionFactor { get { return m_compressionFactor; } }
// TerrainData.GetCompressedMap
public override short[] GetCompressedMap()
{
short[] newMap = new short[SizeX * SizeY];
int ind = 0;
for (int xx = 0; xx < SizeX; xx++)
for (int yy = 0; yy < SizeY; yy++)
newMap[ind++] = m_heightmap[xx, yy];
return newMap;
}
// TerrainData.Clone
public override TerrainData Clone()
{
HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ);
ret.m_heightmap = (short[,])this.m_heightmap.Clone();
return ret;
}
// TerrainData.GetDoubles
public override double[,] GetDoubles()
{
double[,] ret = new double[SizeX, SizeY];
for (int xx = 0; xx < SizeX; xx++)
for (int yy = 0; yy < SizeY; yy++)
ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]);
return ret;
}
// =============================================================
private short[,] m_heightmap;
// Remember subregions of the heightmap that has changed.
private bool[,] m_taint;
// To save space (especially for large regions), keep the height as a short integer
// that is coded as the float height times the compression factor (usually '100'
// to make for two decimal points).
public short ToCompressedHeight(double pHeight)
{
return (short)(pHeight * CompressionFactor);
}
public float FromCompressedHeight(short pHeight)
{
return ((float)pHeight) / CompressionFactor;
}
// To keep with the legacy theme, create an instance of this class based on the
// way terrain used to be passed around.
public HeightmapTerrainData(double[,] pTerrain)
{
SizeX = pTerrain.GetLength(0);
SizeY = pTerrain.GetLength(1);
SizeZ = (int)Constants.RegionHeight;
m_compressionFactor = 100.0f;
m_heightmap = new short[SizeX, SizeY];
for (int ii = 0; ii < SizeX; ii++)
{
for (int jj = 0; jj < SizeY; jj++)
{
m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]);
}
}
// m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
ClearTaint();
}
// Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that
public HeightmapTerrainData(int pX, int pY, int pZ)
{
SizeX = pX;
SizeY = pY;
SizeZ = pZ;
m_compressionFactor = 100.0f;
m_heightmap = new short[SizeX, SizeY];
m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
// m_log.DebugFormat("{0} new by dimensions. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
ClearTaint();
ClearLand(0f);
}
public HeightmapTerrainData(short[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ)
{
m_compressionFactor = pCompressionFactor;
int ind = 0;
for (int xx = 0; xx < SizeX; xx++)
for (int yy = 0; yy < SizeY; yy++)
m_heightmap[xx, yy] = cmap[ind++];
// m_log.DebugFormat("{0} new by compressed map. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
}
// Create a heighmap from a database blob
public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ)
{
switch ((DBTerrainRevision)pFormatCode)
{
case DBTerrainRevision.Compressed2D:
FromCompressedTerrainSerialization(pBlob);
m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
break;
default:
FromLegacyTerrainSerialization(pBlob);
m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
break;
}
}
// Just create an array of doubles. Presumes the caller implicitly knows the size.
public Array ToLegacyTerrainSerialization()
{
Array ret = null;
using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double)))
{
using (BinaryWriter bw = new BinaryWriter(str))
{
for (int xx = 0; xx < Constants.RegionSize; xx++)
{
for (int yy = 0; yy < Constants.RegionSize; yy++)
{
double height = this[xx, yy];
if (height == 0.0)
height = double.Epsilon;
bw.Write(height);
}
}
}
ret = str.ToArray();
}
return ret;
}
// Just create an array of doubles. Presumes the caller implicitly knows the size.
public void FromLegacyTerrainSerialization(byte[] pBlob)
{
// In case database info doesn't match real terrain size, initialize the whole terrain.
ClearLand();
using (MemoryStream mstr = new MemoryStream(pBlob))
{
using (BinaryReader br = new BinaryReader(mstr))
{
for (int xx = 0; xx < (int)Constants.RegionSize; xx++)
{
for (int yy = 0; yy < (int)Constants.RegionSize; yy++)
{
float val = (float)br.ReadDouble();
if (xx < SizeX && yy < SizeY)
m_heightmap[xx, yy] = ToCompressedHeight(val);
}
}
}
ClearTaint();
}
}
// See the reader below.
public Array ToCompressedTerrainSerialization()
{
Array ret = null;
using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16))))
{
using (BinaryWriter bw = new BinaryWriter(str))
{
bw.Write((Int32)DBTerrainRevision.Compressed2D);
bw.Write((Int32)SizeX);
bw.Write((Int32)SizeY);
bw.Write((Int32)CompressionFactor);
for (int yy = 0; yy < SizeY; yy++)
for (int xx = 0; xx < SizeX; xx++)
{
bw.Write((Int16)m_heightmap[xx, yy]);
}
}
ret = str.ToArray();
}
return ret;
}
// Initialize heightmap from blob consisting of:
// int32, int32, int32, int32, int16[]
// where the first int32 is format code, next two int32s are the X and y of heightmap data and
// the forth int is the compression factor for the following int16s
// This is just sets heightmap info. The actual size of the region was set on this instance's
// creation and any heights not initialized by theis blob are set to the default height.
public void FromCompressedTerrainSerialization(byte[] pBlob)
{
Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor;
using (MemoryStream mstr = new MemoryStream(pBlob))
{
using (BinaryReader br = new BinaryReader(mstr))
{
hmFormatCode = br.ReadInt32();
hmSizeX = br.ReadInt32();
hmSizeY = br.ReadInt32();
hmCompressionFactor = br.ReadInt32();
m_compressionFactor = hmCompressionFactor;
// In case database info doesn't match real terrain size, initialize the whole terrain.
ClearLand();
for (int yy = 0; yy < hmSizeY; yy++)
{
for (int xx = 0; xx < hmSizeX; xx++)
{
Int16 val = br.ReadInt16();
if (xx < SizeX && yy < SizeY)
m_heightmap[xx, yy] = val;
}
}
}
ClearTaint();
m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size=<{3},{4}>. CompFact={5}",
LogHeader, hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor);
}
}
}
}

View File

@@ -160,15 +160,19 @@ namespace OpenSim.Framework
public virtual ulong HomeRegion
{
get
{
return Utils.UIntsToLong(
m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize);
{
return Util.RegionWorldLocToHandle(Util.RegionToWorldLoc(m_homeRegionX), Util.RegionToWorldLoc(m_homeRegionY));
// return Utils.UIntsToLong( m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize);
}
set
{
m_homeRegionX = (uint) (value >> 40);
m_homeRegionY = (((uint) (value)) >> 8);
uint regionWorldLocX, regionWorldLocY;
Util.RegionHandleToWorldLoc(value, out regionWorldLocX, out regionWorldLocY);
m_homeRegionX = Util.WorldToRegionLoc(regionWorldLocX);
m_homeRegionY = Util.WorldToRegionLoc(regionWorldLocY);
// m_homeRegionX = (uint) (value >> 40);
// m_homeRegionY = (((uint) (value)) >> 8);
}
}

View File

@@ -1745,6 +1745,30 @@ namespace OpenSim.Framework
return data;
}
/// <summary>
/// Pretty format the hashtable contents to a single line.
/// </summary>
/// <remarks>
/// Used for debugging output.
/// </remarks>
/// <param name='ht'></param>
public static string PrettyFormatToSingleLine(Hashtable ht)
{
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (string key in ht.Keys)
{
sb.AppendFormat("{0}:{1}", key, ht[key]);
if (++i < ht.Count)
sb.AppendFormat(", ");
}
return sb.ToString();
}
/// <summary>
/// Used to trigger an early library load on Windows systems.
/// </summary>

View File

@@ -1030,7 +1030,7 @@ namespace OpenSim.Framework
finally
{
if (requestStream != null)
requestStream.Close();
requestStream.Dispose();
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
@@ -1183,7 +1183,7 @@ namespace OpenSim.Framework
finally
{
if (requestStream != null)
requestStream.Close();
requestStream.Dispose();
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
@@ -1268,4 +1268,4 @@ namespace OpenSim.Framework
return deserial;
}
}
}
}

View File

@@ -266,10 +266,21 @@ namespace OpenSim
SavePrimsXml2);
m_console.Commands.AddCommand("Archiving", false, "load oar",
"load oar [--merge] [--skip-assets] [<OAR path>]",
"load oar [--merge] [--skip-assets]"
+ " [--force-terrain] [--force-parcels]"
+ " [--no-objects]"
+ " [--rotation degrees] [--rotation-center \"<x,y,z>\"]"
+ " [--displacement \"<x,y,z>\"]"
+ " [<OAR path>]",
"Load a region's data from an OAR archive.",
"--merge will merge the OAR with the existing scene." + Environment.NewLine
"--merge will merge the OAR with the existing scene (suppresses terrain and parcel info loading)." + Environment.NewLine
+ "--skip-assets will load the OAR but ignore the assets it contains." + Environment.NewLine
+ "--displacement will add this value to the position of every object loaded" + Environment.NewLine
+ "--force-terrain forces the loading of terrain from the oar (undoes suppression done by --merge)" + Environment.NewLine
+ "--force-parcels forces the loading of parcels from the oar (undoes suppression done by --merge)" + Environment.NewLine
+ "--rotation specified rotation to be applied to the oar. Specified in degrees." + Environment.NewLine
+ "--rotation-center Location (relative to original OAR) to apply rotation. Default is <128,128,0>" + Environment.NewLine
+ "--no-objects suppresses the addition of any objects (good for loading only the terrain)" + Environment.NewLine
+ "The path can be either a filesystem location or a URI."
+ " If this is not given then the command looks for an OAR named region.oar in the current directory.",
LoadOar);

View File

@@ -690,7 +690,8 @@ namespace OpenSim
clientServer = clientNetworkServers;
scene.LoadWorldMap();
scene.PhysicsScene = GetPhysicsScene(scene.RegionInfo.RegionName);
Vector3 regionExtent = new Vector3(regionInfo.RegionSizeX, regionInfo.RegionSizeY, regionInfo.RegionSizeZ);
scene.PhysicsScene = GetPhysicsScene(scene.RegionInfo.RegionName, regionExtent);
scene.PhysicsScene.RequestAssetMethod = scene.PhysicsRequestAsset;
scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised());
scene.PhysicsScene.SetWaterLevel((float) regionInfo.RegionSettings.WaterHeight);
@@ -752,10 +753,10 @@ namespace OpenSim
# region Setup methods
protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier)
protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier, Vector3 regionExtent)
{
return GetPhysicsScene(
m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, Config, osSceneIdentifier);
m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, Config, osSceneIdentifier, regionExtent);
}
/// <summary>

View File

@@ -59,6 +59,7 @@ namespace OpenSim.Region.ClientStack.Linden
public class EventQueueGetModule : IEventQueue, INonSharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[EVENT QUEUE GET MODULE]";
/// <value>
/// Debug level.
@@ -228,12 +229,18 @@ namespace OpenSim.Region.ClientStack.Linden
lock (queue)
queue.Enqueue(ev);
}
else
else if (DebugLevel > 0)
{
OSDMap evMap = (OSDMap)ev;
m_log.WarnFormat(
"[EVENTQUEUE]: (Enqueue) No queue found for agent {0} when placing message {1} in region {2}",
avatarID, evMap["message"], m_scene.Name);
ScenePresence sp = m_scene.GetScenePresence(avatarID);
// This assumes that an NPC should never have a queue.
if (sp != null && sp.PresenceType != PresenceType.Npc)
{
OSDMap evMap = (OSDMap)ev;
m_log.WarnFormat(
"[EVENTQUEUE]: (Enqueue) No queue found for agent {0} {1} when placing message {2} in region {3}",
sp.Name, sp.UUID, evMap["message"], m_scene.Name);
}
}
}
catch (NullReferenceException e)
@@ -714,7 +721,7 @@ namespace OpenSim.Region.ClientStack.Linden
public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID, int regionSizeX, int regionSizeY)
{
m_log.DebugFormat("{0} EnableSimulator. handle={1}, avatarID={2}, regionSize={3},{4}>",
"[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY);
LogHeader, handle, avatarID, regionSizeX, regionSizeY);
OSD item = EventQueueHelper.EnableSimulator(handle, endPoint, regionSizeX, regionSizeY);
Enqueue(item, avatarID);
@@ -724,7 +731,7 @@ namespace OpenSim.Region.ClientStack.Linden
ulong regionHandle, int regionSizeX, int regionSizeY)
{
m_log.DebugFormat("{0} EstablishAgentCommunication. handle={1}, avatarID={2}, regionSize={3},{4}>",
"[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY);
LogHeader, regionHandle, avatarID, regionSizeX, regionSizeY);
OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath, regionHandle, regionSizeX, regionSizeY);
Enqueue(item, avatarID);
}
@@ -734,8 +741,8 @@ namespace OpenSim.Region.ClientStack.Linden
uint locationID, uint flags, string capsURL,
UUID avatarID, int regionSizeX, int regionSizeY)
{
m_log.DebugFormat("{0} TeleportFinishEvent. handle={1}, avatarID={2}, regionSize={3},{4}>",
"[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY);
m_log.DebugFormat("{0} TeleportFinishEvent. handle={1}, avatarID={2}, regionSize=<{3},{4}>",
LogHeader, regionHandle, avatarID, regionSizeX, regionSizeY);
OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint,
locationID, flags, capsURL, avatarID, regionSizeX, regionSizeY);
@@ -747,7 +754,7 @@ namespace OpenSim.Region.ClientStack.Linden
string capsURL, UUID avatarID, UUID sessionID, int regionSizeX, int regionSizeY)
{
m_log.DebugFormat("{0} CrossRegion. handle={1}, avatarID={2}, regionSize={3},{4}>",
"[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY);
LogHeader, handle, avatarID, regionSizeX, regionSizeY);
OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint,
capsURL, avatarID, sessionID, regionSizeX, regionSizeY);

View File

@@ -77,8 +77,8 @@ namespace OpenSim.Region.ClientStack.Linden
llsdSimInfo.Add("Handle", new OSDBinary(ulongToByteArray(handle)));
llsdSimInfo.Add("IP", new OSDBinary(endPoint.Address.GetAddressBytes()));
llsdSimInfo.Add("Port", new OSDInteger(endPoint.Port));
llsdSimInfo.Add("RegionSizeX", new OSDInteger(regionSizeX));
llsdSimInfo.Add("RegionSizeY", new OSDInteger(regionSizeY));
llsdSimInfo.Add("RegionSizeX", OSD.FromUInteger((uint) regionSizeX));
llsdSimInfo.Add("RegionSizeY", OSD.FromUInteger((uint) regionSizeY));
OSDArray arr = new OSDArray(1);
arr.Add(llsdSimInfo);
@@ -138,8 +138,8 @@ namespace OpenSim.Region.ClientStack.Linden
regionDataMap.Add("SeedCapability", OSD.FromString(capsURL));
regionDataMap.Add("SimIP", OSD.FromBinary(newRegionExternalEndPoint.Address.GetAddressBytes()));
regionDataMap.Add("SimPort", OSD.FromInteger(newRegionExternalEndPoint.Port));
regionDataMap.Add("RegionSizeX", new OSDInteger(regionSizeX));
regionDataMap.Add("RegionSizeY", new OSDInteger(regionSizeY));
regionDataMap.Add("RegionSizeX", OSD.FromUInteger((uint)regionSizeX));
regionDataMap.Add("RegionSizeY", OSD.FromUInteger((uint)regionSizeY));
OSDArray regionDataArr = new OSDArray(1);
regionDataArr.Add(regionDataMap);
@@ -166,8 +166,8 @@ namespace OpenSim.Region.ClientStack.Linden
info.Add("SimIP", OSD.FromBinary(regionExternalEndPoint.Address.GetAddressBytes()));
info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port));
info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation
info.Add("RegionSizeX", new OSDInteger(regionSizeX));
info.Add("RegionSizeY", new OSDInteger(regionSizeY));
info.Add("RegionSizeX", OSD.FromUInteger((uint)regionSizeX));
info.Add("RegionSizeY", OSD.FromUInteger((uint)regionSizeY));
OSDArray infoArr = new OSDArray();
infoArr.Add(info);

View File

@@ -26,6 +26,7 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using log4net.Config;
@@ -33,11 +34,14 @@ using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.ClientStack.Linden;
using OpenSim.Region.CoreModules.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
@@ -47,6 +51,8 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
public class EventQueueTests : OpenSimTestCase
{
private TestScene m_scene;
private EventQueueGetModule m_eqgMod;
private NPCModule m_npcMod;
[SetUp]
public override void SetUp()
@@ -69,10 +75,15 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
config.Configs["Startup"].Set("EventQueue", "true");
CapabilitiesModule capsModule = new CapabilitiesModule();
EventQueueGetModule eqgModule = new EventQueueGetModule();
m_eqgMod = new EventQueueGetModule();
// For NPC test support
config.AddConfig("NPC");
config.Configs["NPC"].Set("Enabled", "true");
m_npcMod = new NPCModule();
m_scene = new SceneHelpers().SetupScene();
SceneHelpers.SetupSceneModules(m_scene, config, capsModule, eqgModule);
SceneHelpers.SetupSceneModules(m_scene, config, capsModule, m_eqgMod, m_npcMod);
}
[Test]
@@ -101,5 +112,80 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
// TODO: Add more assertions for the other aspects of event queues
Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0));
}
[Test]
public void TestEnqueueMessage()
{
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, TestHelpers.ParseTail(0x1));
string messageName = "TestMessage";
m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), sp.UUID);
Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, sp.UUID);
Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.OK));
// Console.WriteLine("Response [{0}]", (string)eventsResponse["str_response_string"]);
OSDMap rawOsd = (OSDMap)OSDParser.DeserializeLLSDXml((string)eventsResponse["str_response_string"]);
OSDArray eventsOsd = (OSDArray)rawOsd["events"];
bool foundUpdate = false;
foreach (OSD osd in eventsOsd)
{
OSDMap eventOsd = (OSDMap)osd;
if (eventOsd["message"] == messageName)
foundUpdate = true;
}
Assert.That(foundUpdate, Is.True, string.Format("Did not find {0} in response", messageName));
}
/// <summary>
/// Test an attempt to put a message on the queue of a user that is not in the region.
/// </summary>
[Test]
public void TestEnqueueMessageNoUser()
{
TestHelpers.InMethod();
TestHelpers.EnableLogging();
string messageName = "TestMessage";
m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), TestHelpers.ParseTail(0x1));
Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, TestHelpers.ParseTail(0x1));
Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.BadGateway));
}
/// <summary>
/// NPCs do not currently have an event queue but a caller may try to send a message anyway, so check behaviour.
/// </summary>
[Test]
public void TestEnqueueMessageToNpc()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
UUID npcId
= m_npcMod.CreateNPC(
"John", "Smith", new Vector3(128, 128, 30), UUID.Zero, true, m_scene, new AvatarAppearance());
ScenePresence npc = m_scene.GetScenePresence(npcId);
string messageName = "TestMessage";
m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), npc.UUID);
Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, npc.UUID);
Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.BadGateway));
}
}
}

View File

@@ -27,6 +27,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
@@ -53,8 +54,8 @@ namespace OpenSim.Region.ClientStack.Linden
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UploadBakedTextureModule")]
public class UploadBakedTextureModule : INonSharedRegionModule
{
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// For historical reasons this is fixed, but there
@@ -63,39 +64,211 @@ namespace OpenSim.Region.ClientStack.Linden
private Scene m_scene;
private bool m_persistBakedTextures;
private string m_URL;
private IBakedTextureModule m_BakedTextureModule;
public void Initialise(IConfigSource source)
{
IConfig config = source.Configs["ClientStack.LindenCaps"];
if (config == null)
return;
m_URL = config.GetString("Cap_UploadBakedTexture", string.Empty);
IConfig appearanceConfig = source.Configs["Appearance"];
if (appearanceConfig != null)
m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures);
}
public void AddRegion(Scene s)
{
m_scene = s;
}
public void RemoveRegion(Scene s)
{
s.EventManager.OnRegisterCaps -= RegisterCaps;
s.EventManager.OnNewPresence -= RegisterNewPresence;
s.EventManager.OnRemovePresence -= DeRegisterPresence;
m_BakedTextureModule = null;
m_scene = null;
}
public void RegionLoaded(Scene s)
{
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
m_scene.EventManager.OnNewPresence += RegisterNewPresence;
m_scene.EventManager.OnRemovePresence += DeRegisterPresence;
}
private void DeRegisterPresence(UUID agentId)
{
ScenePresence presence = null;
if (m_scene.TryGetScenePresence(agentId, out presence))
{
presence.ControllingClient.OnSetAppearance -= CaptureAppearanceSettings;
}
}
private void RegisterNewPresence(ScenePresence presence)
{
presence.ControllingClient.OnSetAppearance += CaptureAppearanceSettings;
}
private void CaptureAppearanceSettings(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems)
{
int maxCacheitemsLoop = cacheItems.Length;
if (maxCacheitemsLoop > AvatarWearable.MAX_WEARABLES)
{
maxCacheitemsLoop = AvatarWearable.MAX_WEARABLES;
m_log.WarnFormat("[CACHEDBAKES]: Too Many Cache items Provided {0}, the max is {1}. Truncating!", cacheItems.Length, AvatarWearable.MAX_WEARABLES);
}
m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
if (cacheItems.Length > 0)
{
m_log.Debug("[Cacheitems]: " + cacheItems.Length);
for (int iter = 0; iter < maxCacheitemsLoop; iter++)
{
m_log.Debug("[Cacheitems] {" + iter + "/" + cacheItems[iter].TextureIndex + "}: c-" + cacheItems[iter].CacheId + ", t-" +
cacheItems[iter].TextureID);
}
ScenePresence p = null;
if (m_scene.TryGetScenePresence(remoteClient.AgentId, out p))
{
WearableCacheItem[] existingitems = p.Appearance.WearableCacheItems;
if (existingitems == null)
{
if (m_BakedTextureModule != null)
{
WearableCacheItem[] savedcache = null;
try
{
if (p.Appearance.WearableCacheItemsDirty)
{
savedcache = m_BakedTextureModule.Get(p.UUID);
p.Appearance.WearableCacheItems = savedcache;
p.Appearance.WearableCacheItemsDirty = false;
}
}
/*
* The following Catch types DO NOT WORK with m_BakedTextureModule.Get
* it jumps to the General Packet Exception Handler if you don't catch Exception!
*
catch (System.Net.Sockets.SocketException)
{
cacheItems = null;
}
catch (WebException)
{
cacheItems = null;
}
catch (InvalidOperationException)
{
cacheItems = null;
} */
catch (Exception)
{
// The service logs a sufficient error message.
}
if (savedcache != null)
existingitems = savedcache;
}
}
// Existing items null means it's a fully new appearance
if (existingitems == null)
{
for (int i = 0; i < maxCacheitemsLoop; i++)
{
if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex)
{
Primitive.TextureEntryFace face = textureEntry.FaceTextures[cacheItems[i].TextureIndex];
if (face == null)
{
textureEntry.CreateFace(cacheItems[i].TextureIndex);
textureEntry.FaceTextures[cacheItems[i].TextureIndex].TextureID =
AppearanceManager.DEFAULT_AVATAR_TEXTURE;
continue;
}
cacheItems[i].TextureID =face.TextureID;
if (m_scene.AssetService != null)
cacheItems[i].TextureAsset =
m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString());
}
else
{
m_log.WarnFormat("[CACHEDBAKES]: Invalid Texture Index Provided, Texture doesn't exist or hasn't been uploaded yet {0}, the max is {1}. Skipping!", cacheItems[i].TextureIndex, textureEntry.FaceTextures.Length);
}
}
}
else
{
// for each uploaded baked texture
for (int i = 0; i < maxCacheitemsLoop; i++)
{
if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex)
{
Primitive.TextureEntryFace face = textureEntry.FaceTextures[cacheItems[i].TextureIndex];
if (face == null)
{
textureEntry.CreateFace(cacheItems[i].TextureIndex);
textureEntry.FaceTextures[cacheItems[i].TextureIndex].TextureID =
AppearanceManager.DEFAULT_AVATAR_TEXTURE;
continue;
}
cacheItems[i].TextureID =
face.TextureID;
}
else
{
m_log.WarnFormat("[CACHEDBAKES]: Invalid Texture Index Provided, Texture doesn't exist or hasn't been uploaded yet {0}, the max is {1}. Skipping!", cacheItems[i].TextureIndex, textureEntry.FaceTextures.Length);
}
}
for (int i = 0; i < maxCacheitemsLoop; i++)
{
if (cacheItems[i].TextureAsset == null)
{
cacheItems[i].TextureAsset =
m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString());
}
}
}
p.Appearance.WearableCacheItems = cacheItems;
if (m_BakedTextureModule != null)
{
m_BakedTextureModule.Store(remoteClient.AgentId, cacheItems);
p.Appearance.WearableCacheItemsDirty = true;
}
}
}
}
public void PostInitialise()
{
}
public void Close() { }
public string Name { get { return "UploadBakedTextureModule"; } }
@@ -107,26 +280,23 @@ namespace OpenSim.Region.ClientStack.Linden
public void RegisterCaps(UUID agentID, Caps caps)
{
// UUID capID = UUID.Random();
UploadBakedTextureHandler avatarhandler = new UploadBakedTextureHandler(
caps, m_scene.AssetService, m_persistBakedTextures);
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
if (m_URL == "localhost")
{
caps.RegisterHandler(
caps.RegisterHandler(
"UploadBakedTexture",
new RestStreamHandler(
"POST",
"/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath,
avatarhandler.UploadBakedTexture,
"UploadBakedTexture",
new RestStreamHandler(
"POST",
"/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath,
new UploadBakedTextureHandler(
caps, m_scene.AssetService, m_persistBakedTextures).UploadBakedTexture,
"UploadBakedTexture",
agentID.ToString()));
}
else
{
caps.RegisterHandler("UploadBakedTexture", m_URL);
}
agentID.ToString()));
}
}
}
}

View File

@@ -34,11 +34,13 @@ using System.Text;
using System.Threading;
using System.Timers;
using System.Xml;
using log4net;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.Messages.Linden;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Client;
using OpenSim.Framework.Monitoring;
@@ -48,7 +50,6 @@ using OpenSim.Services.Interfaces;
using Timer = System.Timers.Timer;
using AssetLandmark = OpenSim.Framework.AssetLandmark;
using RegionFlags = OpenMetaverse.RegionFlags;
using Nini.Config;
using System.IO;
using PermissionMask = OpenSim.Framework.PermissionMask;
@@ -307,6 +308,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[LLCLIENTVIEW]";
protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
/// <summary>
@@ -323,6 +325,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private readonly byte[] m_channelVersion = Utils.EmptyBytes;
private readonly IGroupsModule m_GroupsModule;
private int m_cachedTextureSerial;
private PriorityQueue m_entityUpdates;
private PriorityQueue m_entityProps;
private Prioritizer m_prioritizer;
@@ -447,7 +450,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// ~LLClientView()
// {
// m_log.DebugFormat("[LLCLIENTVIEW]: Destructor called for {0}, circuit code {1}", Name, CircuitCode);
// m_log.DebugFormat("{0} Destructor called for {1}, circuit code {2}", LogHeader, Name, CircuitCode);
// }
/// <summary>
@@ -513,9 +516,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// there is some unidentified connection problem, not where we have issues due to deadlock
if (!IsActive && !force)
{
m_log.DebugFormat(
"[CLIENT]: Not attempting to close inactive client {0} in {1} since force flag is not set",
Name, m_scene.Name);
m_log.DebugFormat( "{0} Not attempting to close inactive client {1} in {2} since force flag is not set",
LogHeader, Name, m_scene.Name);
return;
}
@@ -1153,7 +1155,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="map">heightmap</param>
public virtual void SendLayerData(float[] map)
{
Util.FireAndForget(DoSendLayerData, map);
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
}
/// <summary>
@@ -1162,10 +1164,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="o"></param>
private void DoSendLayerData(object o)
{
float[] map = LLHeightFieldMoronize((float[])o);
TerrainData map = (TerrainData)o;
try
{
// Send LayerData in typerwriter pattern
//for (int y = 0; y < 16; y++)
//{
// for (int x = 0; x < 16; x++)
@@ -1175,7 +1178,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//}
// Send LayerData in a spiral pattern. Fun!
SendLayerTopRight(map, 0, 0, 15, 15);
SendLayerTopRight(map, 0, 0, map.SizeX/Constants.TerrainPatchSize-1, map.SizeY/Constants.TerrainPatchSize-1);
}
catch (Exception e)
{
@@ -1183,7 +1186,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
private void SendLayerTopRight(float[] map, int x1, int y1, int x2, int y2)
private void SendLayerTopRight(TerrainData map, int x1, int y1, int x2, int y2)
{
// Row
for (int i = x1; i <= x2; i++)
@@ -1193,11 +1196,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int j = y1 + 1; j <= y2; j++)
SendLayerData(x2, j, map);
if (x2 - x1 > 0)
if (x2 - x1 > 0 && y2 - y1 > 0)
SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2);
}
void SendLayerBottomLeft(float[] map, int x1, int y1, int x2, int y2)
void SendLayerBottomLeft(TerrainData map, int x1, int y1, int x2, int y2)
{
// Row in reverse
for (int i = x2; i >= x1; i--)
@@ -1207,7 +1210,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int j = y2 - 1; j >= y1; j--)
SendLayerData(x1, j, map);
if (x2 - x1 > 0)
if (x2 - x1 > 0 && y2 - y1 > 0)
SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1);
}
@@ -1229,22 +1232,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// OutPacket(layerpack, ThrottleOutPacketType.Land);
// }
// Legacy form of invocation that passes around a bare data array.
// Just ignore what was passed and use the real terrain info that is part of the scene.
public void SendLayerData(int px, int py, float[] map)
{
SendLayerData(px, py, m_scene.Heightmap.GetTerrainData());
}
/// <summary>
/// Sends a specified patch to a client
/// Sends a terrain packet for the point specified.
/// This is a legacy call that has refarbed the terrain into a flat map of floats.
/// We just use the terrain from the region we know about.
/// </summary>
/// <param name="px">Patch coordinate (x) 0..15</param>
/// <param name="py">Patch coordinate (y) 0..15</param>
/// <param name="map">heightmap</param>
public void SendLayerData(int px, int py, float[] map)
public void SendLayerData(int px, int py, TerrainData terrData)
{
try
{
int[] patches = new int[] { py * 16 + px };
float[] heightmap = (map.Length == 65536) ?
map :
LLHeightFieldMoronize(map);
LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, px, py);
// When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience.
// To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain.
@@ -1262,14 +1269,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_justEditedTerrain)
{
layerpack.Header.Reliable = false;
OutPacket(layerpack,
ThrottleOutPacketType.Unknown );
OutPacket(layerpack, ThrottleOutPacketType.Unknown );
}
else
{
layerpack.Header.Reliable = true;
OutPacket(layerpack,
ThrottleOutPacketType.Land);
OutPacket(layerpack, ThrottleOutPacketType.Land);
}
}
catch (Exception e)
@@ -1278,38 +1283,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
/// <summary>
/// Munges heightfield into the LLUDP backed in restricted heightfield.
/// </summary>
/// <param name="map">float array in the base; Constants.RegionSize</param>
/// <returns>float array in the base 256</returns>
internal float[] LLHeightFieldMoronize(float[] map)
{
if (map.Length == 65536)
return map;
else
{
float[] returnmap = new float[65536];
if (map.Length < 65535)
{
// rebase the vector stride to 256
for (int i = 0; i < Constants.RegionSize; i++)
Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize);
}
else
{
for (int i = 0; i < 256; i++)
Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256);
}
//Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536);
return returnmap;
}
}
/// <summary>
/// Send the wind matrix to the client
/// </summary>
@@ -1350,7 +1323,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Wind);
byte layerType = (byte)TerrainPatch.LayerType.Wind;
if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
layerType = (byte)TerrainPatch.LayerType.WindExtended;
LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
(int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
layerpack.Header.Zerocoded = true;
OutPacket(layerpack, ThrottleOutPacketType.Wind);
}
@@ -1374,7 +1352,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Cloud);
byte layerType = (byte)TerrainPatch.LayerType.Cloud;
if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
layerType = (byte)TerrainPatch.LayerType.CloudExtended;
LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType,
(int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY);
layerpack.Header.Zerocoded = true;
OutPacket(layerpack, ThrottleOutPacketType.Cloud);
}
@@ -2785,8 +2768,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (req.AssetInf.Data == null)
{
m_log.ErrorFormat("Cannot send asset {0} ({1}), asset data is null",
req.AssetInf.ID, req.AssetInf.Metadata.ContentType);
m_log.ErrorFormat("{0} Cannot send asset {1} ({2}), asset data is null",
LogHeader, req.AssetInf.ID, req.AssetInf.Metadata.ContentType);
return;
}
@@ -8970,6 +8953,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest;
if (handlerTeleportLocationRequest != null)
{
// Adjust teleport location to base of a larger region if requested to teleport to a sub-region
uint locX, locY;
Util.RegionHandleToWorldLoc(tpLocReq.Info.RegionHandle, out locX, out locY);
if ((locX >= m_scene.RegionInfo.WorldLocX)
&& (locX < (m_scene.RegionInfo.WorldLocX + m_scene.RegionInfo.RegionSizeX))
&& (locY >= m_scene.RegionInfo.WorldLocY)
&& (locY < (m_scene.RegionInfo.WorldLocY + m_scene.RegionInfo.RegionSizeY)) )
{
tpLocReq.Info.RegionHandle = m_scene.RegionInfo.RegionHandle;
tpLocReq.Info.Position.X += locX - m_scene.RegionInfo.WorldLocX;
tpLocReq.Info.Position.Y += locY - m_scene.RegionInfo.WorldLocY;
}
handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position,
tpLocReq.Info.LookAt, 16);
}
@@ -11757,36 +11753,158 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <returns></returns>
protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet)
{
//m_log.Debug("texture cached: " + packet.ToString());
AgentCachedTexturePacket cachedtex = (AgentCachedTexturePacket)packet;
AgentCachedTextureResponsePacket cachedresp = (AgentCachedTextureResponsePacket)PacketPool.Instance.GetPacket(PacketType.AgentCachedTextureResponse);
if (cachedtex.AgentData.SessionID != SessionId)
return false;
List<CachedTextureRequestArg> requestArgs = new List<CachedTextureRequestArg>();
for (int i = 0; i < cachedtex.WearableData.Length; i++)
{
CachedTextureRequestArg arg = new CachedTextureRequestArg();
arg.BakedTextureIndex = cachedtex.WearableData[i].TextureIndex;
arg.WearableHashID = cachedtex.WearableData[i].ID;
requestArgs.Add(arg);
}
// TODO: don't create new blocks if recycling an old packet
cachedresp.AgentData.AgentID = AgentId;
cachedresp.AgentData.SessionID = m_sessionId;
cachedresp.AgentData.SerialNum = m_cachedTextureSerial;
m_cachedTextureSerial++;
cachedresp.WearableData =
new AgentCachedTextureResponsePacket.WearableDataBlock[cachedtex.WearableData.Length];
try
//IAvatarFactoryModule fac = m_scene.RequestModuleInterface<IAvatarFactoryModule>();
// var item = fac.GetBakedTextureFaces(AgentId);
//WearableCacheItem[] items = fac.GetCachedItems(AgentId);
IAssetService cache = m_scene.AssetService;
IBakedTextureModule bakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
//bakedTextureModule = null;
int maxWearablesLoop = cachedtex.WearableData.Length;
if (maxWearablesLoop > AvatarWearable.MAX_WEARABLES)
maxWearablesLoop = AvatarWearable.MAX_WEARABLES;
if (bakedTextureModule != null && cache != null)
{
CachedTextureRequest handlerCachedTextureRequest = OnCachedTextureRequest;
if (handlerCachedTextureRequest != null)
// We need to make sure the asset stored in the bake is available on this server also by it's assetid before we map it to a Cacheid
WearableCacheItem[] cacheItems = null;
ScenePresence p = m_scene.GetScenePresence(AgentId);
if (p.Appearance != null)
if (p.Appearance.WearableCacheItems == null || p.Appearance.WearableCacheItemsDirty)
{
try
{
cacheItems = bakedTextureModule.Get(AgentId);
p.Appearance.WearableCacheItems = cacheItems;
p.Appearance.WearableCacheItemsDirty = false;
}
/*
* The following Catch types DO NOT WORK, it jumps to the General Packet Exception Handler if you don't catch Exception!
*
catch (System.Net.Sockets.SocketException)
{
cacheItems = null;
}
catch (WebException)
{
cacheItems = null;
}
catch (InvalidOperationException)
{
cacheItems = null;
} */
catch (Exception)
{
cacheItems = null;
}
}
else if (p.Appearance.WearableCacheItems != null)
{
cacheItems = p.Appearance.WearableCacheItems;
}
if (cache != null && cacheItems != null)
{
handlerCachedTextureRequest(simclient,cachedtex.AgentData.SerialNum,requestArgs);
foreach (WearableCacheItem item in cacheItems)
{
if (cache.GetCached(item.TextureID.ToString()) == null)
{
item.TextureAsset.Temporary = true;
cache.Store(item.TextureAsset);
}
}
}
if (cacheItems != null)
{
for (int i = 0; i < maxWearablesLoop; i++)
{
WearableCacheItem item =
WearableCacheItem.SearchTextureIndex(cachedtex.WearableData[i].TextureIndex,cacheItems);
cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
cachedresp.WearableData[i].TextureIndex= cachedtex.WearableData[i].TextureIndex;
cachedresp.WearableData[i].HostName = new byte[0];
if (item != null && cachedtex.WearableData[i].ID == item.CacheId)
{
cachedresp.WearableData[i].TextureID = item.TextureID;
}
else
{
cachedresp.WearableData[i].TextureID = UUID.Zero;
}
}
}
else
{
for (int i = 0; i < maxWearablesLoop; i++)
{
cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex;
cachedresp.WearableData[i].TextureID = UUID.Zero;
//UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46");
cachedresp.WearableData[i].HostName = new byte[0];
}
}
}
catch (Exception e)
else
{
m_log.ErrorFormat("[CLIENT VIEW]: AgentTextureCached packet handler threw an exception, {0}", e);
return false;
if (cache == null)
{
for (int i = 0; i < maxWearablesLoop; i++)
{
cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex;
cachedresp.WearableData[i].TextureID = UUID.Zero;
//UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46");
cachedresp.WearableData[i].HostName = new byte[0];
}
}
else
{
for (int i = 0; i < maxWearablesLoop; i++)
{
cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex;
if (cache.GetCached(cachedresp.WearableData[i].TextureID.ToString()) == null)
cachedresp.WearableData[i].TextureID = UUID.Zero;
//UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46");
else
cachedresp.WearableData[i].TextureID = UUID.Zero;
// UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46");
cachedresp.WearableData[i].HostName = new byte[0];
}
}
}
cachedresp.Header.Zerocoded = true;
OutPacket(cachedresp, ThrottleOutPacketType.Task);
return true;
}

View File

@@ -69,7 +69,7 @@ namespace OpenSim.Region.ClientStack
/// The name of the OpenSim scene this physics scene is serving. This will be used in log messages.
/// </param>
/// <returns></returns>
protected abstract PhysicsScene GetPhysicsScene(string osSceneIdentifier);
protected abstract PhysicsScene GetPhysicsScene(string osSceneIdentifier, Vector3 regionExtent);
protected abstract ClientStackManager CreateClientStackManager();
protected abstract Scene CreateScene(RegionInfo regionInfo, ISimulationDataService simDataService, IEstateDataService estateDataService, AgentCircuitManager circuitManager);
@@ -123,13 +123,13 @@ namespace OpenSim.Region.ClientStack
/// </param>
/// <returns></returns>
protected PhysicsScene GetPhysicsScene(
string engine, string meshEngine, IConfigSource config, string osSceneIdentifier)
string engine, string meshEngine, IConfigSource config, string osSceneIdentifier, Vector3 regionExtent)
{
PhysicsPluginManager physicsPluginManager;
physicsPluginManager = new PhysicsPluginManager();
physicsPluginManager.LoadPluginsFromAssemblies("Physics");
return physicsPluginManager.GetPhysicsScene(engine, meshEngine, config, osSceneIdentifier);
return physicsPluginManager.GetPhysicsScene(engine, meshEngine, config, osSceneIdentifier, regionExtent);
}
}
}

View File

@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
@@ -182,6 +183,25 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
return DoJ2KDecode(assetID, j2kData, out layers, out components);
}
public Image DecodeToImage(byte[] j2kData)
{
if (m_useCSJ2K)
return J2kImage.FromBytes(j2kData);
else
{
ManagedImage mimage;
Image image;
if (OpenJPEG.DecodeToImage(j2kData, out mimage, out image))
{
mimage = null;
return image;
}
else
return null;
}
}
#endregion IJ2KDecoder
/// <summary>

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using OpenMetaverse;
using Nini.Config;
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using Mono.Addins;
namespace OpenSim.Region.CoreModules.Avatar.BakedTextures
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XBakes.Module")]
public class XBakesModule : INonSharedRegionModule, IBakedTextureModule
{
protected Scene m_Scene;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private UTF8Encoding enc = new UTF8Encoding();
private string m_URL = String.Empty;
private static XmlSerializer m_serializer = new XmlSerializer(typeof(AssetBase));
public void Initialise(IConfigSource configSource)
{
IConfig config = configSource.Configs["XBakes"];
if (config == null)
return;
m_URL = config.GetString("URL", String.Empty);
}
public void AddRegion(Scene scene)
{
// m_log.InfoFormat("[XBakes]: Enabled for region {0}", scene.RegionInfo.RegionName);
m_Scene = scene;
scene.RegisterModuleInterface<IBakedTextureModule>(this);
}
public void RegionLoaded(Scene scene)
{
}
public void RemoveRegion(Scene scene)
{
}
public void Close()
{
}
public string Name
{
get { return "XBakes.Module"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public WearableCacheItem[] Get(UUID id)
{
if (m_URL == String.Empty)
return null;
int size = 0;
RestClient rc = new RestClient(m_URL);
List<WearableCacheItem> ret = new List<WearableCacheItem>();
rc.AddResourcePath("bakes");
rc.AddResourcePath(id.ToString());
rc.RequestMethod = "GET";
try
{
Stream s = rc.Request();
XmlTextReader sr = new XmlTextReader(s);
sr.ReadStartElement("BakedAppearance");
while (sr.LocalName == "BakedTexture")
{
string sTextureIndex = sr.GetAttribute("TextureIndex");
int lTextureIndex = Convert.ToInt32(sTextureIndex);
string sCacheId = sr.GetAttribute("CacheId");
UUID lCacheId = UUID.Zero;
if (!(UUID.TryParse(sCacheId, out lCacheId)))
{
// ?? Nothing here
}
++size;
sr.ReadStartElement("BakedTexture");
AssetBase a = (AssetBase)m_serializer.Deserialize(sr);
ret.Add(new WearableCacheItem() { CacheId = lCacheId, TextureIndex = (uint)lTextureIndex, TextureAsset = a, TextureID = a.FullID });
sr.ReadEndElement();
}
m_log.DebugFormat("[XBakes]: Ended reading");
sr.Close();
s.Close();
return ret.ToArray();
}
catch (XmlException e)
{
return null;
}
}
public void Store(UUID agentId, WearableCacheItem[] data)
{
if (m_URL == String.Empty)
return;
MemoryStream bakeStream = new MemoryStream();
XmlTextWriter bakeWriter = new XmlTextWriter(bakeStream, null);
bakeWriter.WriteStartElement(String.Empty, "BakedAppearance", String.Empty);
for (int i = 0; i < data.Length; i++)
{
if (data[i] != null)
{
bakeWriter.WriteStartElement(String.Empty, "BakedTexture", String.Empty);
bakeWriter.WriteAttributeString(String.Empty, "TextureIndex", String.Empty, data[i].TextureIndex.ToString());
bakeWriter.WriteAttributeString(String.Empty, "CacheId", String.Empty, data[i].CacheId.ToString());
if (data[i].TextureAsset != null)
m_serializer.Serialize(bakeWriter, data[i].TextureAsset);
bakeWriter.WriteEndElement();
}
}
bakeWriter.WriteEndElement();
bakeWriter.Flush();
RestClient rc = new RestClient(m_URL);
rc.AddResourcePath("bakes");
rc.AddResourcePath(agentId.ToString());
rc.RequestMethod = "POST";
MemoryStream reqStream = new MemoryStream(bakeStream.ToArray());
Util.FireAndForget(
delegate
{
rc.Request(reqStream);
}
);
}
}
}

View File

@@ -189,8 +189,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
string message = c.Message;
Scene scene = (Scene)c.Scene;
Vector3 fromPos = c.Position;
Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize,
scene.RegionInfo.RegionLocY * Constants.RegionSize, 0);
Vector3 regionPos = new Vector3(scene.RegionInfo.WorldLocX, scene.RegionInfo.WorldLocY, 0);
if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel;
@@ -342,8 +341,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
{
Vector3 fromRegionPos = fromPos + regionPos;
Vector3 toRegionPos = presence.AbsolutePosition +
new Vector3(presence.Scene.RegionInfo.RegionLocX * Constants.RegionSize,
presence.Scene.RegionInfo.RegionLocY * Constants.RegionSize, 0);
new Vector3(presence.Scene.RegionInfo.WorldLocX, presence.Scene.RegionInfo.WorldLocY, 0);
int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos);

View File

@@ -282,7 +282,17 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
string uasURL = circuit.ServiceURLs["HomeURI"].ToString();
m_log.DebugFormat("[HG MESSAGE TRANSFER]: getting UUI of user {0} from {1}", toAgent, uasURL);
UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uasURL);
return uasConn.GetUUI(fromAgent, toAgent);
string agentUUI = string.Empty;
try
{
agentUUI = uasConn.GetUUI(fromAgent, toAgent);
}
catch (Exception e) {
m_log.Debug("[HG MESSAGE TRANSFER]: GetUUI call failed ", e);
}
return agentUUI;
}
}
}

View File

@@ -667,8 +667,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
Vector3 avaPos = p.AbsolutePosition;
// Getting the global position for the Avatar
Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.RegionLocX*Constants.RegionSize + avaPos.X,
remoteClient.Scene.RegionInfo.RegionLocY*Constants.RegionSize + avaPos.Y,
Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.WorldLocX + avaPos.X,
remoteClient.Scene.RegionInfo.WorldLocY + avaPos.Y,
avaPos.Z);
string landOwnerName = string.Empty;
@@ -1164,7 +1164,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
UserAgentServiceConnector uConn = new UserAgentServiceConnector(home_url);
Dictionary<string, object> account = uConn.GetUserInfo(userID);
Dictionary<string, object> account;
try
{
account = uConn.GetUserInfo(userID);
}
catch (Exception e)
{
m_log.Debug("[PROFILES]: GetUserInfo call failed ", e);
account = new Dictionary<string, object>();
}
if (account.Count > 0)
{
@@ -1290,9 +1299,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
webRequest.ContentType = "application/json-rpc";
webRequest.Method = "POST";
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(content, 0, content.Length);
dataStream.Close();
using (Stream dataStream = webRequest.GetRequestStream())
dataStream.Write(content, 0, content.Length);
WebResponse webResponse = null;
try
@@ -1306,26 +1314,18 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
return false;
}
Stream rstream = webResponse.GetResponseStream();
OSDMap mret = new OSDMap();
try
using (webResponse)
using (Stream rstream = webResponse.GetResponseStream())
{
mret = (OSDMap)OSDParser.DeserializeJson(rstream);
}
catch (Exception e)
{
m_log.DebugFormat("[PROFILES]: JsonRpcRequest Error {0} - remote user with legacy profiles?", e.Message);
return false;
}
OSDMap mret = (OSDMap)OSDParser.DeserializeJson(rstream);
if (mret.ContainsKey("error"))
return false;
if (mret.ContainsKey("error"))
return false;
// get params...
OSD.DeserializeMembers(ref parameters, (OSDMap) mret["result"]);
return true;
// get params...
OSD.DeserializeMembers(ref parameters, (OSDMap)mret["result"]);
return true;
}
}
/// <summary>
@@ -1366,9 +1366,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
webRequest.ContentType = "application/json-rpc";
webRequest.Method = "POST";
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(content, 0, content.Length);
dataStream.Close();
using (Stream dataStream = webRequest.GetRequestStream())
dataStream.Write(content, 0, content.Length);
WebResponse webResponse = null;
try
@@ -1382,29 +1381,32 @@ namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles
return false;
}
Stream rstream = webResponse.GetResponseStream();
OSDMap response = new OSDMap();
try
using (webResponse)
using (Stream rstream = webResponse.GetResponseStream())
{
response = (OSDMap)OSDParser.DeserializeJson(rstream);
}
catch (Exception e)
{
m_log.DebugFormat("[PROFILES]: JsonRpcRequest Error {0} - remote user with legacy profiles?", e.Message);
return false;
}
OSDMap response = new OSDMap();
try
{
response = (OSDMap)OSDParser.DeserializeJson(rstream);
}
catch (Exception e)
{
m_log.DebugFormat("[PROFILES]: JsonRpcRequest Error {0} - remote user with legacy profiles?", e.Message);
return false;
}
if(response.ContainsKey("error"))
{
data = response["error"];
return false;
if (response.ContainsKey("error"))
{
data = response["error"];
return false;
}
data = response;
return true;
}
data = response;
return true;
}
#endregion Web Util
}
}
}

View File

@@ -269,9 +269,7 @@ namespace OpenSim.Region.CoreModules.Framework
foreach (KeyValuePair<ulong, string> kvp in m_childrenSeeds[agentID])
{
uint x, y;
Utils.LongToUInts(kvp.Key, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(kvp.Key, out x, out y);
m_log.Info(" >> "+x+", "+y+": "+kvp.Value);
}
}

View File

@@ -51,7 +51,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "EntityTransferModule")]
public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]";
public const int DefaultMaxTransferDistance = 4095;
@@ -121,8 +121,53 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// </summary>
private EntityTransferStateMachine m_entityTransferStateMachine;
private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
// For performance, we keed a cached of banned regions so we don't keep going
// to the grid service.
private class BannedRegionCache
{
private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
ExpiringCache<ulong, DateTime> m_idCache;
DateTime m_banUntil;
public BannedRegionCache()
{
}
// Return 'true' if there is a valid ban entry for this agent in this region
public bool IfBanned(ulong pRegionHandle, UUID pAgentID)
{
bool ret = false;
if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
{
if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil))
{
if (DateTime.Now < m_banUntil)
{
ret = true;
}
}
}
return ret;
}
// Add this agent in this region as a banned person
public void Add(ulong pRegionHandle, UUID pAgentID)
{
if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
{
m_idCache = new ExpiringCache<ulong, DateTime>();
m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(45));
}
m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
}
// Remove the agent from the region's banned list
public void Remove(ulong pRegionHandle, UUID pAgentID)
{
if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
{
m_idCache.Remove(pRegionHandle);
}
}
}
private BannedRegionCache m_bannedRegionCache = new BannedRegionCache();
private IEventQueue m_eqModule;
private IRegionCombinerModule m_regionCombinerModule;
@@ -337,6 +382,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
"[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name);
}
// Attempt to teleport the ScenePresence to the specified position in the specified region (spec'ed by its handle).
public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags)
{
if (sp.Scene.Permissions.IsGridGod(sp.UUID))
@@ -418,7 +464,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
sp.Name, position, sp.Scene.RegionInfo.RegionName);
// Teleport within the same region
if (IsOutsideRegion(sp.Scene, position) || position.Z < 0)
if (!sp.Scene.PositionIsInCurrentRegion(position) || position.Z < 0)
{
Vector3 emergencyPos = new Vector3(128, 128, 128);
@@ -434,10 +480,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
float posZLimit = 22;
// TODO: Check other Scene HeightField
if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <= (int)Constants.RegionSize)
{
posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y];
}
posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y];
float newPosZ = posZLimit + localAVHeight;
if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ)))
@@ -480,9 +523,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
ScenePresence sp, ulong regionHandle, Vector3 position,
Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination)
{
uint x = 0, y = 0;
Utils.LongToUInts(regionHandle, out x, out y);
GridRegion reg = Scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y);
// Get destination region taking into account that the address could be an offset
// region inside a varregion.
GridRegion reg = GetTeleportDestinationRegion(sp.Scene.GridService, sp.Scene.RegionInfo.ScopeID, regionHandle, ref position);
if (reg != null)
{
@@ -490,9 +533,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (finalDestination == null)
{
m_log.WarnFormat(
"[ENTITY TRANSFER MODULE]: Final destination is having problems. Unable to teleport {0} {1}",
sp.Name, sp.UUID);
m_log.WarnFormat( "{0} Final destination is having problems. Unable to teleport {1} {2}",
LogHeader, sp.Name, sp.UUID);
sp.ControllingClient.SendTeleportFailed("Problem at destination");
return;
@@ -533,12 +575,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// and set the map-tile to '(Offline)'
uint regX, regY;
Utils.LongToUInts(regionHandle, out regX, out regY);
Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY);
MapBlockData block = new MapBlockData();
block.X = (ushort)(regX / Constants.RegionSize);
block.Y = (ushort)(regY / Constants.RegionSize);
block.Access = 254; // == not there
block.X = (ushort)regX;
block.Y = (ushort)regY;
block.Access = (byte)SimAccess.Down;
List<MapBlockData> blocks = new List<MapBlockData>();
blocks.Add(block);
@@ -546,6 +588,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
// The teleport address could be an address in a subregion of a larger varregion.
// Find the real base region and adjust the teleport location to account for the
// larger region.
private GridRegion GetTeleportDestinationRegion(IGridService gridService, UUID scope, ulong regionHandle, ref Vector3 position)
{
uint x = 0, y = 0;
Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
// Compute the world location we're teleporting to
double worldX = (double)x + position.X;
double worldY = (double)y + position.Y;
// Find the region that contains the position
GridRegion reg = GetRegionContainingWorldLocation(gridService, scope, worldX, worldY);
if (reg != null)
{
// modify the position for the offset into the actual region returned
position.X += x - reg.RegionLocX;
position.Y += y - reg.RegionLocY;
}
return reg;
}
// Nothing to validate here
protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
{
@@ -646,10 +713,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
uint newRegionX = (uint)(reg.RegionHandle >> 40);
uint newRegionY = (((uint)(reg.RegionHandle)) >> 8);
uint oldRegionX = (uint)(sp.Scene.RegionInfo.RegionHandle >> 40);
uint oldRegionY = (((uint)(sp.Scene.RegionInfo.RegionHandle)) >> 8);
uint newRegionX, newRegionY, oldRegionX, oldRegionY;
Util.RegionHandleToRegionLoc(reg.RegionHandle, out newRegionX, out newRegionY);
Util.RegionHandleToRegionLoc(sp.Scene.RegionInfo.RegionHandle, out oldRegionX, out oldRegionY);
ulong destinationHandle = finalDestination.RegionHandle;
@@ -1266,6 +1332,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return region;
}
// This returns 'true' if the new region already has a child agent for our
// incoming agent. The implication is that, if 'false', we have to create the
// child and then teleport into the region.
protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY)
{
if (m_regionCombinerModule != null && m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID))
@@ -1290,20 +1359,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY);
}
protected virtual bool IsOutsideRegion(Scene s, Vector3 pos)
{
if (s.TestBorderCross(pos, Cardinals.N))
return true;
if (s.TestBorderCross(pos, Cardinals.S))
return true;
if (s.TestBorderCross(pos, Cardinals.E))
return true;
if (s.TestBorderCross(pos, Cardinals.W))
return true;
return false;
}
#endregion
#region Landmark Teleport
@@ -1349,6 +1404,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (uinfo.HomeRegionID == UUID.Zero)
{
// can't find the Home region: Tell viewer and abort
m_log.ErrorFormat("{0} No grid user info found for {1} {2}. Cannot send home.",
LogHeader, client.Name, client.AgentId);
client.SendTeleportFailed("You don't have a home position set.");
return false;
}
@@ -1382,139 +1439,73 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
#region Agent Crossings
public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, out uint xDest, out uint yDest, out string version, out Vector3 newpos)
// Given a position relative to the current region (which has previously been tested to
// see that it is actually outside the current region), find the new region that the
// point is actually in.
// Returns the coordinates and information of the new region or 'null' of it doesn't exist.
public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos,
out string version, out Vector3 newpos, out string failureReason)
{
version = String.Empty;
newpos = pos;
failureReason = string.Empty;
// m_log.DebugFormat(
// "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name);
uint neighbourx = scene.RegionInfo.RegionLocX;
uint neighboury = scene.RegionInfo.RegionLocY;
const float boundaryDistance = 1.7f;
Vector3 northCross = new Vector3(0, boundaryDistance, 0);
Vector3 southCross = new Vector3(0, -1 * boundaryDistance, 0);
Vector3 eastCross = new Vector3(boundaryDistance, 0, 0);
Vector3 westCross = new Vector3(-1 * boundaryDistance, 0, 0);
// Compute world location of the object's position
double presenceWorldX = (double)scene.RegionInfo.WorldLocX + pos.X;
double presenceWorldY = (double)scene.RegionInfo.WorldLocY + pos.Y;
// distance into new region to place avatar
const float enterDistance = 0.5f;
// Call the grid service to lookup the region containing the new position.
GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
presenceWorldX, presenceWorldY,
Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY));
if (scene.TestBorderCross(pos + westCross, Cardinals.W))
if (neighbourRegion != null)
{
if (scene.TestBorderCross(pos + northCross, Cardinals.N))
// Compute the entity's position relative to the new region
newpos = new Vector3((float)(presenceWorldX - (double)neighbourRegion.RegionLocX),
(float)(presenceWorldY - (double)neighbourRegion.RegionLocY),
pos.Z);
if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID))
{
Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N);
neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize);
failureReason = "Cannot region cross into banned parcel";
neighbourRegion = null;
}
else if (scene.TestBorderCross(pos + southCross, Cardinals.S))
else
{
neighboury--;
newpos.Y = Constants.RegionSize - enterDistance;
// If not banned, make sure this agent is not in the list.
m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID);
}
neighbourx--;
newpos.X = Constants.RegionSize - enterDistance;
}
else if (scene.TestBorderCross(pos + eastCross, Cardinals.E))
{
Border b = scene.GetCrossedBorder(pos + eastCross, Cardinals.E);
neighbourx += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize);
newpos.X = enterDistance;
if (scene.TestBorderCross(pos + southCross, Cardinals.S))
// Check to see if we have access to the target region.
if (neighbourRegion != null
&& !scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out failureReason))
{
neighboury--;
newpos.Y = Constants.RegionSize - enterDistance;
}
else if (scene.TestBorderCross(pos + northCross, Cardinals.N))
{
Border c = scene.GetCrossedBorder(pos + northCross, Cardinals.N);
neighboury += (uint)(int)(c.BorderLine.Z / (int)Constants.RegionSize);
newpos.Y = enterDistance;
}
}
else if (scene.TestBorderCross(pos + southCross, Cardinals.S))
{
Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S);
neighboury--;
newpos.Y = Constants.RegionSize - enterDistance;
}
else if (scene.TestBorderCross(pos + northCross, Cardinals.N))
{
Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N);
neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize);
newpos.Y = enterDistance;
}
/*
if (pos.X < boundaryDistance) //West
{
neighbourx--;
newpos.X = Constants.RegionSize - enterDistance;
}
else if (pos.X > Constants.RegionSize - boundaryDistance) // East
{
neighbourx++;
newpos.X = enterDistance;
}
if (pos.Y < boundaryDistance) // South
{
neighboury--;
newpos.Y = Constants.RegionSize - enterDistance;
}
else if (pos.Y > Constants.RegionSize - boundaryDistance) // North
{
neighboury++;
newpos.Y = enterDistance;
}
*/
xDest = neighbourx;
yDest = neighboury;
int x = (int)(neighbourx * Constants.RegionSize), y = (int)(neighboury * Constants.RegionSize);
ulong neighbourHandle = Utils.UIntsToLong((uint)x, (uint)y);
ExpiringCache<ulong, DateTime> r;
DateTime banUntil;
if (m_bannedRegions.TryGetValue(agentID, out r))
{
if (r.TryGetValue(neighbourHandle, out banUntil))
{
if (DateTime.Now < banUntil)
return null;
r.Remove(neighbourHandle);
// remember banned
m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID);
neighbourRegion = null;
}
}
else
{
r = null;
// The destination region just doesn't exist
failureReason = "Cannot cross into non-existant region";
}
GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y);
string reason;
if (!scene.SimulationService.QueryAccess(neighbourRegion, agentID, newpos, out version, out reason))
{
if (r == null)
{
r = new ExpiringCache<ulong, DateTime>();
r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
m_bannedRegions.Add(agentID, r, TimeSpan.FromSeconds(45));
}
else
{
r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
}
return null;
}
if (neighbourRegion == null)
m_log.DebugFormat("{0} GetDestination: region not found. Old region name={1} at <{2},{3}> of size <{4},{5}>. Old pos={6}",
LogHeader, scene.RegionInfo.RegionName,
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY,
pos);
else
m_log.DebugFormat("{0} GetDestination: new region={1} at <{2},{3}> of size <{4},{5}>, newpos=<{6},{7}>",
LogHeader, neighbourRegion.RegionName,
neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY,
newpos.X, newpos.Y);
return neighbourRegion;
}
@@ -1525,11 +1516,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
uint y;
Vector3 newpos;
string version;
string failureReason;
GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, agent.AbsolutePosition, out x, out y, out version, out newpos);
GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, agent.AbsolutePosition,
out version, out newpos, out failureReason);
if (neighbourRegion == null)
{
agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel");
agent.ControllingClient.SendAlertMessage(failureReason);
return false;
}
@@ -1568,7 +1561,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
agent.Scene.RequestTeleportLocation(
agent.ControllingClient,
Utils.UIntsToLong(regionX * (uint)Constants.RegionSize, regionY * (uint)Constants.RegionSize),
Util.RegionLocToHandle(regionX, regionY),
position,
agent.Lookat,
(uint)Constants.TeleportFlags.ViaLocation);
@@ -1578,11 +1571,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (im != null)
{
UUID gotoLocation = Util.BuildFakeParcelID(
Util.UIntsToLong(
(regionX *
(uint)Constants.RegionSize),
(regionY *
(uint)Constants.RegionSize)),
Util.RegionLocToHandle(regionX, regionY),
(uint)(int)position.X,
(uint)(int)position.Y,
(uint)(int)position.Z);
@@ -1635,17 +1624,22 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// Calls an asynchronous method to do so.. so it doesn't lag the sim.
/// </summary>
public ScenePresence CrossAgentToNewRegionAsync(
ScenePresence agent, Vector3 pos, GridRegion neighbourRegion,
bool isFlying, string version)
ScenePresence agent, Vector3 pos, GridRegion neighbourRegion,
bool isFlying, string version)
{
m_log.DebugFormat("{0} CrossAgentToNewRegionAsync: new region={1} at <{2},{3}>. newpos={4}",
LogHeader, neighbourRegion.RegionName, neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, pos);
if (!CrossAgentToNewRegionPrep(agent, neighbourRegion))
{
m_log.DebugFormat("{0} CrossAgentToNewRegionAsync: prep failed. Resetting transfer state", LogHeader);
m_entityTransferStateMachine.ResetFromTransit(agent.UUID);
return agent;
}
if (!CrossAgentIntoNewRegionMain(agent, pos, neighbourRegion, isFlying))
{
m_log.DebugFormat("{0} CrossAgentToNewRegionAsync: cross main failed. Resetting transfer state", LogHeader);
m_entityTransferStateMachine.ResetFromTransit(agent.UUID);
return agent;
}
@@ -2038,15 +2032,195 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
// Computes the difference between two region bases.
// Returns a vector of world coordinates (meters) from base of first region to the second.
// The first region is the home region of the passed scene presence.
Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour)
{
int rRegionX = (int)sp.Scene.RegionInfo.RegionLocX;
int rRegionY = (int)sp.Scene.RegionInfo.RegionLocY;
/*
int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX;
int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY;
int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize;
int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize;
int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize;
int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize;
return new Vector3(shiftx, shifty, 0f);
*/
return new Vector3( sp.Scene.RegionInfo.WorldLocX - neighbour.RegionLocX,
sp.Scene.RegionInfo.WorldLocY - neighbour.RegionLocY,
0f);
}
public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py)
{
// Since we don't know how big the regions could be, we have to search a very large area
// to find possible regions.
return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize);
}
#region NotFoundLocationCache class
// A collection of not found locations to make future lookups 'not found' lookups quick.
// A simple expiring cache that keeps not found locations for some number of seconds.
// A 'not found' location is presumed to be anywhere in the minimum sized region that
// contains that point. A conservitive estimate.
private class NotFoundLocationCache
{
private struct NotFoundLocation
{
public double minX, maxX, minY, maxY;
public DateTime expireTime;
}
private List<NotFoundLocation> m_notFoundLocations = new List<NotFoundLocation>();
public NotFoundLocationCache()
{
}
// Add an area to the list of 'not found' places. The area is the snapped region
// area around the added point.
public void Add(double pX, double pY)
{
lock (m_notFoundLocations)
{
if (!LockedContains(pX, pY))
{
NotFoundLocation nfl = new NotFoundLocation();
// A not found location is not found for at least a whole region sized area
nfl.minX = pX - (pX % (double)Constants.RegionSize);
nfl.minY = pY - (pY % (double)Constants.RegionSize);
nfl.maxX = nfl.minX + (double)Constants.RegionSize;
nfl.maxY = nfl.minY + (double)Constants.RegionSize;
nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30);
m_notFoundLocations.Add(nfl);
}
}
}
// Test to see of this point is in any of the 'not found' areas.
// Return 'true' if the point is found inside the 'not found' areas.
public bool Contains(double pX, double pY)
{
bool ret = false;
lock (m_notFoundLocations)
ret = LockedContains(pX, pY);
return ret;
}
private bool LockedContains(double pX, double pY)
{
bool ret = false;
this.DoExpiration();
foreach (NotFoundLocation nfl in m_notFoundLocations)
{
if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY)
{
ret = true;
break;
}
}
return ret;
}
private void DoExpiration()
{
List<NotFoundLocation> m_toRemove = null;
DateTime now = DateTime.Now;
foreach (NotFoundLocation nfl in m_notFoundLocations)
{
if (nfl.expireTime < now)
{
if (m_toRemove == null)
m_toRemove = new List<NotFoundLocation>();
m_toRemove.Add(nfl);
}
}
if (m_toRemove != null)
{
foreach (NotFoundLocation nfl in m_toRemove)
m_notFoundLocations.Remove(nfl);
m_toRemove.Clear();
}
}
}
#endregion // NotFoundLocationCache class
private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache();
// Given a world position (fractional meter coordinate), get the GridRegion info for
// the region containing that point.
// Someday this should be a method on GridService.
// 'pSizeHint' is the size of the source region but since the destination point can be anywhere
// the size of the target region is unknown thus the search area might have to be very large.
// Return 'null' if no such region exists.
public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID,
double px, double py, uint pSizeHint)
{
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py);
GridRegion ret = null;
const double fudge = 2.0;
// One problem with this routine is negative results. That is, this can be called lots of times
// for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they
// will be quick 'not found's next time.
// NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and
// thus re-ask the GridService about the location.
if (m_notFoundLocationCache.Contains(px, py))
{
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py);
return null;
}
// As an optimization, since most regions will be legacy sized regions (256x256), first try to get
// the region at the appropriate legacy region location.
uint possibleX = (uint)Math.Floor(px);
possibleX -= possibleX % Constants.RegionSize;
uint possibleY = (uint)Math.Floor(py);
possibleY -= possibleY % Constants.RegionSize;
ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY);
if (ret != null)
{
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}",
LogHeader, possibleX, possibleY, ret.RegionName);
}
if (ret == null)
{
// If the simple lookup failed, search the larger area for a region that contains this point
double range = (double)pSizeHint + fudge;
while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize))
{
// Get from the grid service a list of regions that might contain this point.
// The region origin will be in the zero direction so only subtract the range.
List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID,
(int)(px - range), (int)(px),
(int)(py - range), (int)(py));
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}",
LogHeader, possibleRegions.Count, range);
if (possibleRegions != null && possibleRegions.Count > 0)
{
// If we found some regions, check to see if the point is within
foreach (GridRegion gr in possibleRegions)
{
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>",
LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY);
if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX)
&& py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY))
{
// Found a region that contains the point
ret = gr;
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName);
break;
}
}
}
// Larger search area for next time around if not found
range *= 2;
}
}
if (ret == null)
{
// remember this location was not found so we can quickly not find it next time
m_notFoundLocationCache.Add(px, py);
m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py);
}
return ret;
}
private void InformClientOfNeighbourCompleted(IAsyncResult iar)
@@ -2128,22 +2302,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// <param name='neCorner'></param>
private void GetMegaregionViewRange(out Vector2 swCorner, out Vector2 neCorner)
{
Border[] northBorders = Scene.NorthBorders.ToArray();
Border[] eastBorders = Scene.EastBorders.ToArray();
Vector2 extent = Vector2.Zero;
for (int i = 0; i < eastBorders.Length; i++)
{
extent.X = (eastBorders[i].BorderLine.Z > extent.X) ? eastBorders[i].BorderLine.Z : extent.X;
}
for (int i = 0; i < northBorders.Length; i++)
{
extent.Y = (northBorders[i].BorderLine.Z > extent.Y) ? northBorders[i].BorderLine.Z : extent.Y;
}
// Loss of fraction on purpose
extent.X = ((int)extent.X / (int)Constants.RegionSize);
extent.Y = ((int)extent.Y / (int)Constants.RegionSize);
if (m_regionCombinerModule != null)
{
Vector2 megaRegionSize = m_regionCombinerModule.GetSizeOfMegaregion(Scene.RegionInfo.RegionID);
extent.X = (float)Util.WorldToRegionLoc((uint)megaRegionSize.X);
extent.Y = (float)Util.WorldToRegionLoc((uint)megaRegionSize.Y);
}
swCorner.X = Scene.RegionInfo.RegionLocX - 1;
swCorner.Y = Scene.RegionInfo.RegionLocY - 1;
@@ -2168,16 +2334,19 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// view to include everything in the megaregion
if (m_regionCombinerModule == null || !m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID))
{
int dd = avatar.DrawDistance < Constants.RegionSize ? (int)Constants.RegionSize : (int)avatar.DrawDistance;
// The area to check is as big as the current region.
// We presume all adjacent regions are the same size as this region.
uint dd = Math.Max((uint)avatar.DrawDistance,
Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY));
int startX = (int)pRegionLocX * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2);
int startY = (int)pRegionLocY * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2);
uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2;
uint startY = Util.RegionToWorldLoc(pRegionLocY) - dd + Constants.RegionSize/2;
int endX = (int)pRegionLocX * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2);
int endY = (int)pRegionLocY * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2);
uint endX = Util.RegionToWorldLoc(pRegionLocX) + dd + Constants.RegionSize/2;
uint endY = Util.RegionToWorldLoc(pRegionLocY) + dd + Constants.RegionSize/2;
List<GridRegion> neighbours =
avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY);
avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, (int)startX, (int)endX, (int)startY, (int)endY);
neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; });
return neighbours;
@@ -2190,10 +2359,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
List<GridRegion> neighbours
= pScene.GridService.GetRegionRange(
m_regionInfo.ScopeID,
(int)swCorner.X * (int)Constants.RegionSize,
(int)neCorner.X * (int)Constants.RegionSize,
(int)swCorner.Y * (int)Constants.RegionSize,
(int)neCorner.Y * (int)Constants.RegionSize);
(int)Util.RegionToWorldLoc((uint)swCorner.X), (int)Util.RegionToWorldLoc((uint)neCorner.X),
(int)Util.RegionToWorldLoc((uint)swCorner.Y), (int)Util.RegionToWorldLoc((uint)neCorner.Y) );
neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; });
@@ -2256,10 +2423,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// Move the given scene object into a new region depending on which region its absolute position has moved
/// into.
///
/// This method locates the new region handle and offsets the prim position for the new region
/// Using the objects new world location, ask the grid service for a the new region and adjust the prim
/// position to be relative to the new region.
/// </summary>
/// <param name="attemptedPosition">the attempted out of region position of the scene object</param>
/// <param name="grp">the scene object that we're crossing</param>
/// <param name="attemptedPosition">the attempted out of region position of the scene object. This position is
/// relative to the region the object currently is in.</param>
/// <param name="silent">if 'true', the deletion of the client from the region is not broadcast to the clients</param>
public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent)
{
if (grp == null)
@@ -2285,208 +2455,49 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
int thisx = (int)scene.RegionInfo.RegionLocX;
int thisy = (int)scene.RegionInfo.RegionLocY;
Vector3 EastCross = new Vector3(0.1f, 0, 0);
Vector3 WestCross = new Vector3(-0.1f, 0, 0);
Vector3 NorthCross = new Vector3(0, 0.1f, 0);
Vector3 SouthCross = new Vector3(0, -0.1f, 0);
// use this if no borders were crossed!
ulong newRegionHandle
= Util.UIntsToLong((uint)((thisx) * Constants.RegionSize),
(uint)((thisy) * Constants.RegionSize));
Vector3 pos = attemptedPosition;
int changeX = 1;
int changeY = 1;
if (scene.TestBorderCross(attemptedPosition + WestCross, Cardinals.W))
{
if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint)((thisy - changeY) * Constants.RegionSize));
// x - 1
// y - 1
}
else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N))
{
Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint)((thisy + changeY) * Constants.RegionSize));
// x - 1
// y + 1
}
else
{
Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint)(thisy * Constants.RegionSize));
// x - 1
}
}
else if (scene.TestBorderCross(attemptedPosition + EastCross, Cardinals.E))
{
if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
pos.X = ((pos.X - Constants.RegionSize));
Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint)((thisy - changeY) * Constants.RegionSize));
// x + 1
// y - 1
}
else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N))
{
pos.X = ((pos.X - Constants.RegionSize));
pos.Y = ((pos.Y - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint)((thisy + changeY) * Constants.RegionSize));
// x + 1
// y + 1
}
else
{
pos.X = ((pos.X - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint)(thisy * Constants.RegionSize));
// x + 1
}
}
else if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize));
// y - 1
}
else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N))
{
pos.Y = ((pos.Y - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize));
// y + 1
}
// Offset the positions for the new region across the border
// Remember the old group position in case the region lookup fails so position can be restored.
Vector3 oldGroupPosition = grp.RootPart.GroupPosition;
// If we fail to cross the border, then reset the position of the scene object on that border.
uint x = 0, y = 0;
Utils.LongToUInts(newRegionHandle, out x, out y);
GridRegion destination = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y);
// Compute the absolute position of the object.
double objectWorldLocX = (double)scene.RegionInfo.WorldLocX + attemptedPosition.X;
double objectWorldLocY = (double)scene.RegionInfo.WorldLocY + attemptedPosition.Y;
// Ask the grid service for the region that contains the passed address
GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
objectWorldLocX, objectWorldLocY);
Vector3 pos = Vector3.Zero;
if (destination != null)
{
if (CrossPrimGroupIntoNewRegion(destination, pos, grp, silent))
return; // we did it
// Adjust the object's relative position from the old region (attemptedPosition)
// to be relative to the new region (pos).
pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX),
(float)(objectWorldLocY - (double)destination.RegionLocY),
attemptedPosition.Z);
}
// no one or failed lets go back and tell physics to go on
oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X, 0.5f, (float)Constants.RegionSize - 0.5f);
oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y, 0.5f, (float)Constants.RegionSize - 0.5f);
oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z, 0.5f, 4096.0f);
if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent))
{
m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID);
grp.AbsolutePosition = oldGroupPosition;
grp.Velocity = Vector3.Zero;
// We are going to move the object back to the old position so long as the old position
// is in the region
oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1));
oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1));
oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z, 1.0f, Constants.RegionHeight);
if (grp.RootPart.PhysActor != null)
grp.RootPart.PhysActor.CrossingFailure();
grp.AbsolutePosition = oldGroupPosition;
grp.Velocity = Vector3.Zero;
if (grp.RootPart.PhysActor != null)
grp.RootPart.PhysActor.CrossingFailure();
if (grp.RootPart.KeyframeMotion != null)
grp.RootPart.KeyframeMotion.CrossingFailure();
if (grp.RootPart.KeyframeMotion != null)
grp.RootPart.KeyframeMotion.CrossingFailure();
grp.ScheduleGroupForFullUpdate();
grp.ScheduleGroupForFullUpdate();
}
}
/// <summary>
/// Move the given scene object into a new region
/// </summary>

View File

@@ -77,6 +77,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
public class EntityTransferStateMachine
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[ENTITY TRANSFER STATE MACHINE]";
/// <summary>
/// If true then on a teleport, the source region waits for a callback from the destination region. If
@@ -100,6 +101,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// <returns>true if the agent was not already in transit, false if it was</returns>
internal bool SetInTransit(UUID id)
{
m_log.DebugFormat("{0} SetInTransit. agent={1}, newState=Preparing", LogHeader, id);
lock (m_agentsInTransit)
{
if (!m_agentsInTransit.ContainsKey(id))
@@ -121,6 +123,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
internal bool UpdateInTransit(UUID id, AgentTransferState newState)
{
m_log.DebugFormat("{0} UpdateInTransit. agent={1}, newState={2}", LogHeader, id, newState);
bool transitionOkay = false;
// We don't want to throw an exception on cancel since this can come it at any time.
@@ -193,6 +197,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
else if (failIfNotOkay)
{
m_log.DebugFormat("{0} UpdateInTransit. Throwing transition failure = {1}", LogHeader, failureMessage);
throw new Exception(failureMessage);
}
// else

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
@@ -462,7 +462,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
IUserAgentService userAgentService = new UserAgentServiceConnector(aCircuit.ServiceURLs["HomeURI"].ToString());
Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY;
GridRegion finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt);
GridRegion finalDestination = null;
try
{
finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt);
}
catch (Exception e)
{
m_log.Debug("[HG ENTITY TRANSFER MODULE]: GetHomeRegion call failed ", e);
}
if (finalDestination == null)
{
client.SendTeleportFailed("Your home region could not be found");

View File

@@ -130,7 +130,17 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
}
UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr);
UUID userID = uasConn.GetUUID(names[0], names[1]);
UUID userID = UUID.Zero;
try
{
userID = uasConn.GetUUID(names[0], names[1]);
}
catch (Exception e)
{
m_log.Debug("[USER MANAGEMENT MODULE]: GetUUID call failed ", e);
}
if (!userID.Equals(UUID.Zero))
{
UserData ud = new UserData();

View File

@@ -473,7 +473,16 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
// serverType, userdata.HomeURL, userID);
UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL);
userdata.ServerURLs = uConn.GetServerURLs(userID);
try
{
userdata.ServerURLs = uConn.GetServerURLs(userID);
}
catch (Exception e)
{
m_log.Debug("[USER MANAGEMENT MODULE]: GetServerURLs call failed ", e);
userdata.ServerURLs = new Dictionary<string, object>();
}
if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null)
return userdata.ServerURLs[serverType].ToString();
}

View File

@@ -126,7 +126,8 @@ namespace OpenSim.Region.CoreModules.Hypergrid
foreach (MapBlockData b in mapBlocks)
{
b.Name = string.Empty;
b.Access = 254; // means 'simulator is offline'. We need this because the viewer ignores 255's
// Set 'simulator is offline'. We need this because the viewer ignores SimAccess.Unknown (255)
b.Access = (byte)SimAccess.Down;
}
m_log.DebugFormat("[HG MAP]: Resetting {0} blocks", mapBlocks.Count);

View File

@@ -213,8 +213,8 @@ namespace OpenSim.Region.CoreModules.Scripting.EmailModules
if (part != null)
{
ObjectRegionName = s.RegionInfo.RegionName;
uint localX = (s.RegionInfo.RegionLocX * (int)Constants.RegionSize);
uint localY = (s.RegionInfo.RegionLocY * (int)Constants.RegionSize);
uint localX = s.RegionInfo.WorldLocX;
uint localY = s.RegionInfo.WorldLocY;
ObjectRegionName = ObjectRegionName + " (" + localX + ", " + localY + ")";
return part;
}

View File

@@ -488,9 +488,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
byte[] data = Util.UTF8.GetBytes(OutboundBody);
Request.ContentLength = data.Length;
Stream bstream = Request.GetRequestStream();
bstream.Write(data, 0, data.Length);
bstream.Close();
using (Stream bstream = Request.GetRequestStream())
bstream.Write(data, 0, data.Length);
}
try
@@ -584,4 +583,4 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
Request.Abort();
}
}
}
}

View File

@@ -48,6 +48,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
private static readonly ILog m_log =
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[LOCAL GRID SERVICE CONNECTOR]";
private IGridService m_GridService;
private Dictionary<UUID, RegionCache> m_LocalCache = new Dictionary<UUID, RegionCache>();
@@ -56,12 +57,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
public LocalGridServicesConnector()
{
m_log.Debug("[LOCAL GRID SERVICE CONNECTOR]: LocalGridServicesConnector no parms.");
m_log.DebugFormat("{0} LocalGridServicesConnector no parms.", LogHeader);
}
public LocalGridServicesConnector(IConfigSource source)
{
m_log.Debug("[LOCAL GRID SERVICE CONNECTOR]: LocalGridServicesConnector instantiated directly.");
m_log.DebugFormat("{0} LocalGridServicesConnector instantiated directly.", LogHeader);
InitialiseService(source);
}
@@ -192,6 +193,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return m_GridService.GetRegionByUUID(scopeID, regionID);
}
// Get a region given its base coordinates.
// NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST
// be the base coordinate of the region.
public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
{
GridRegion region = null;
@@ -206,13 +210,25 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
region = rcache.GetRegionByPosition(x, y);
if (region != null)
{
return region;
// m_log.DebugFormat("{0} GetRegionByPosition. Found region {1} in cache. Pos=<{2},{3}>",
// LogHeader, region.RegionName, x, y);
break;
}
}
}
// Then try on this sim (may be a lookup in DB if this is using MySql).
return m_GridService.GetRegionByPosition(scopeID, x, y);
if (region == null)
{
region = m_GridService.GetRegionByPosition(scopeID, x, y);
if (region == null)
m_log.DebugFormat("{0} GetRegionByPosition. Region not found by grid service. Pos=<{1},{2}>",
LogHeader, x, y);
else
m_log.DebugFormat("{0} GetRegionByPosition. Requested region {1} from grid service. Pos=<{2},{3}>",
LogHeader, region.RegionName, x, y);
}
return region;
}
public GridRegion GetRegionByName(UUID scopeID, string regionName)
@@ -268,7 +284,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key);
List<GridRegion> regions = kvp.Value.GetNeighbours();
foreach (GridRegion r in regions)
caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize);
caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, Util.WorldToRegionLoc((uint)r.RegionLocX), Util.WorldToRegionLoc((uint)r.RegionLocY));
}
}

View File

@@ -66,7 +66,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return;
m_log.DebugFormat("[REGION CACHE]: (on region {0}) Region {1} is up @ {2}-{3}",
m_scene.RegionInfo.RegionName, otherRegion.RegionName, otherRegion.RegionLocX / Constants.RegionSize, otherRegion.RegionLocY / Constants.RegionSize);
m_scene.RegionInfo.RegionName, otherRegion.RegionName, Util.WorldToRegionLoc((uint)otherRegion.RegionLocX), Util.WorldToRegionLoc((uint)otherRegion.RegionLocY));
m_neighbours[otherRegion.RegionHandle] = otherRegion;
}
@@ -82,11 +82,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return new List<GridRegion>(m_neighbours.Values);
}
// Get a region given its base coordinates (in meters).
// NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST
// be the base coordinate of the region.
// The snapping is technically unnecessary but is harmless because regions are always
// multiples of the legacy region size (256).
public GridRegion GetRegionByPosition(int x, int y)
{
uint xsnap = (uint)(x / Constants.RegionSize) * Constants.RegionSize;
uint ysnap = (uint)(y / Constants.RegionSize) * Constants.RegionSize;
ulong handle = Utils.UIntsToLong(xsnap, ysnap);
ulong handle = Util.RegionWorldLocToHandle(xsnap, ysnap);
if (m_neighbours.ContainsKey(handle))
return m_neighbours[handle];

View File

@@ -186,10 +186,14 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return rinfo;
}
// Get a region given its base world coordinates (in meters).
// NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST
// be the base coordinate of the region.
// The coordinates are world coords (meters), NOT region units.
public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
{
bool inCache = false;
GridRegion rinfo = m_RegionInfoCache.Get(scopeID, Util.UIntsToLong((uint)x, (uint)y), out inCache);
GridRegion rinfo = m_RegionInfoCache.Get(scopeID, Util.RegionWorldLocToHandle((uint)x, (uint)y), out inCache);
if (inCache)
return rinfo;

View File

@@ -34,6 +34,7 @@ using log4net.Config;
using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid;
using OpenSim.Region.Framework.Scenes;
@@ -141,7 +142,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests
Assert.IsNotNull(result, "Retrieved GetRegionByUUID is null");
Assert.That(result.RegionID, Is.EqualTo(new UUID(1)), "Retrieved region's UUID does not match");
result = m_LocalConnector.GetRegionByPosition(UUID.Zero, 1000 * (int)Constants.RegionSize, 1000 * (int)Constants.RegionSize);
result = m_LocalConnector.GetRegionByPosition(UUID.Zero, (int)Util.RegionToWorldLoc(1000), (int)Util.RegionToWorldLoc(1000));
Assert.IsNotNull(result, "Retrieved GetRegionByPosition is null");
Assert.That(result.RegionLocX, Is.EqualTo(1000 * (int)Constants.RegionSize), "Retrieved region's position does not match");

View File

@@ -57,6 +57,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[MAP IMAGE SERVICE MODULE]";
private bool m_enabled = false;
private IMapImageService m_MapService;
@@ -192,42 +193,85 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
///</summary>
private void UploadMapTile(IScene scene)
{
m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: upload maptile for {0}", scene.RegionInfo.RegionName);
m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName);
string regionName = scene.RegionInfo.RegionName;
// Create a JPG map tile and upload it to the AddMapTile API
byte[] jpgData = Utils.EmptyBytes;
IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
if (tileGenerator == null)
{
m_log.Warn("[MAP IMAGE SERVICE MODULE]: Cannot upload PNG map tile without an ImageGenerator");
m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
return;
}
using (Image mapTile = tileGenerator.CreateMapTile())
using (Bitmap mapTile = tileGenerator.CreateMapTile())
{
// XXX: The MapImageModule will return a null if the user has chosen not to create map tiles and there
// is no static map tile.
if (mapTile == null)
return;
using (MemoryStream stream = new MemoryStream())
if (mapTile != null)
{
mapTile.Save(stream, ImageFormat.Jpeg);
jpgData = stream.ToArray();
// mapTile.Save( // DEBUG DEBUG
// String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
// ImageFormat.Jpeg);
// If the region/maptile is legacy sized, just upload the one tile like it has always been done
if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
{
ConvertAndUploadMaptile(mapTile,
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
scene.RegionInfo.RegionName);
}
else
{
// For larger regions (varregion) we must cut the region image into legacy sized
// pieces since that is how the maptile system works.
// Note the assumption that varregions are always a multiple of legacy size.
for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
{
for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
{
// Images are addressed from the upper left corner so have to do funny
// math to pick out the sub-tile since regions are numbered from
// the lower left.
Rectangle rect = new Rectangle(
(int)xx,
mapTile.Height - (int)yy - (int)Constants.RegionSize,
(int)Constants.RegionSize, (int)Constants.RegionSize);
using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
{
ConvertAndUploadMaptile(subMapTile,
scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
regionName);
}
}
}
}
}
else
{
m_log.WarnFormat("{0} Tile image generation failed", LogHeader);
}
}
}
if (jpgData == Utils.EmptyBytes)
private void ConvertAndUploadMaptile(Image tileImage, uint locX, uint locY, string regionName)
{
byte[] jpgData = Utils.EmptyBytes;
using (MemoryStream stream = new MemoryStream())
{
m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Tile image generation failed");
return;
tileImage.Save(stream, ImageFormat.Jpeg);
jpgData = stream.ToArray();
}
string reason = string.Empty;
if (!m_MapService.AddMapTile((int)scene.RegionInfo.RegionLocX, (int)scene.RegionInfo.RegionLocY, jpgData, out reason))
if (jpgData != Utils.EmptyBytes)
{
m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: Unable to upload tile image for {0} at {1}-{2}: {3}",
scene.RegionInfo.RegionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY, reason);
string reason = string.Empty;
if (!m_MapService.AddMapTile((int)locX, (int)locY, jpgData, out reason))
{
m_log.DebugFormat("{0} Unable to upload tile image for {1} at {2}-{3}: {4}", LogHeader,
regionName, locX, locY, reason);
}
}
else
{
m_log.WarnFormat("{0} Tile image generation failed for region {1}", LogHeader, regionName);
}
}
}

View File

@@ -125,14 +125,14 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Neighbour
public OpenSim.Services.Interfaces.GridRegion HelloNeighbour(ulong regionHandle, RegionInfo thisRegion)
{
uint x, y;
Utils.LongToUInts(regionHandle, out x, out y);
Util.RegionHandleToRegionLoc(regionHandle, out x, out y);
foreach (Scene s in m_Scenes)
{
if (s.RegionInfo.RegionHandle == regionHandle)
{
m_log.DebugFormat("[LOCAL NEIGHBOUR SERVICE CONNECTOR]: HelloNeighbour from region {0} to neighbour {1} at {2}-{3}",
thisRegion.RegionName, s.Name, x / Constants.RegionSize, y / Constants.RegionSize);
thisRegion.RegionName, s.Name, x, y );
//m_log.Debug("[NEIGHBOUR CONNECTOR]: Found region to SendHelloNeighbour");
return s.IncomingHelloNeighbour(thisRegion);

View File

@@ -96,14 +96,42 @@ namespace OpenSim.Region.CoreModules.World.Archiver
/// <value>
/// Should the archive being loaded be merged with what is already on the region?
/// Merging usually suppresses terrain and parcel loading
/// </value>
protected bool m_merge;
/// <value>
/// If true, force the loading of terrain from the oar file
/// </value>
protected bool m_forceTerrain;
/// <value>
/// If true, force the loading of parcels from the oar file
/// </value>
protected bool m_forceParcels;
/// <value>
/// Should we ignore any assets when reloading the archive?
/// </value>
protected bool m_skipAssets;
/// <value>
/// Displacement added to each object as it is added to the world
/// </value>
protected Vector3 m_displacement = Vector3.Zero;
/// <value>
/// Rotation (in radians) to apply to the objects as they are loaded.
/// </value>
protected float m_rotation = 0f;
/// <value>
/// Center around which to apply the rotation relative to the origional oar position
/// </value>
protected Vector3 m_rotationCenter = new Vector3(Constants.RegionSize / 2f, Constants.RegionSize / 2f, 0f);
protected bool m_noObjects = false;
/// <summary>
/// Used to cache lookups for valid uuids.
/// </summary>
@@ -132,7 +160,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
private IAssetService m_assetService = null;
public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
public ArchiveReadRequest(Scene scene, string loadPath, Guid requestId, Dictionary<string,object>options)
{
m_rootScene = scene;
@@ -150,9 +178,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
m_errorMessage = String.Empty;
m_merge = merge;
m_skipAssets = skipAssets;
m_merge = options.ContainsKey("merge");
m_forceTerrain = options.ContainsKey("force-terrain");
m_forceParcels = options.ContainsKey("force-parcels");
m_noObjects = options.ContainsKey("no-objects");
m_skipAssets = options.ContainsKey("skipAssets");
m_requestId = requestId;
m_displacement = options.ContainsKey("displacement") ? (Vector3)options["displacement"] : Vector3.Zero;
m_rotation = options.ContainsKey("rotation") ? (float)options["rotation"] : 0f;
m_rotationCenter = options.ContainsKey("rotation-center") ? (Vector3)options["rotation-center"]
: new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 0f);
// Zero can never be a valid user id
m_validUserUuids[UUID.Zero] = false;
@@ -161,13 +196,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver
m_assetService = m_rootScene.AssetService;
}
public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
public ArchiveReadRequest(Scene scene, Stream loadStream, Guid requestId, Dictionary<string, object>options)
{
m_rootScene = scene;
m_loadPath = null;
m_loadStream = loadStream;
m_merge = merge;
m_skipAssets = skipAssets;
m_skipAssets = options.ContainsKey("skipAssets");
m_merge = options.ContainsKey("merge");
m_requestId = requestId;
// Zero can never be a valid user id
@@ -229,7 +264,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Process the file
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH) && !m_noObjects)
{
sceneContext.SerialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
}
@@ -243,7 +278,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if ((successfulAssetRestores + failedAssetRestores) % 250 == 0)
m_log.Debug("[ARCHIVER]: Loaded " + successfulAssetRestores + " assets and failed to load " + failedAssetRestores + " assets...");
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH) && (!m_merge || m_forceTerrain))
{
LoadTerrain(scene, filePath, data);
}
@@ -251,7 +286,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
LoadRegionSettings(scene, filePath, data, dearchivedScenes);
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
else if (filePath.StartsWith(ArchiveConstants.LANDDATA_PATH) && (!m_merge || m_forceParcels))
{
sceneContext.SerialisedParcels.Add(Encoding.UTF8.GetString(data));
}
@@ -422,6 +457,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Reload serialized prims
m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
OpenMetaverse.Quaternion rot = OpenMetaverse.Quaternion.CreateFromAxisAngle(0, 0, 1, m_rotation);
UUID oldTelehubUUID = scene.RegionInfo.RegionSettings.TelehubObject;
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
@@ -445,6 +482,32 @@ namespace OpenSim.Region.CoreModules.World.Archiver
SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
// Happily this does not do much to the object since it hasn't been added to the scene yet
if (sceneObject.AttachmentPoint == 0)
{
if (m_displacement != Vector3.Zero || m_rotation != 0f)
{
Vector3 pos = sceneObject.AbsolutePosition;
if (m_rotation != 0f)
{
// Rotate the object
sceneObject.RootPart.RotationOffset = rot * sceneObject.GroupRotation;
// Get object position relative to rotation axis
Vector3 offset = pos - m_rotationCenter;
// Rotate the object position
offset *= rot;
// Restore the object position back to relative to the region
pos = m_rotationCenter + offset;
}
if (m_displacement != Vector3.Zero)
{
pos += m_displacement;
}
sceneObject.AbsolutePosition = pos;
}
}
bool isTelehub = (sceneObject.UUID == oldTelehubUUID) && (oldTelehubUUID != UUID.Zero);
// For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
@@ -549,6 +612,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (string serialisedParcel in serialisedParcels)
{
LandData parcel = LandDataSerializer.Deserialize(serialisedParcel);
if (m_displacement != Vector3.Zero)
{
Vector3 parcelDisp = new Vector3(m_displacement.X, m_displacement.Y, 0f);
parcel.AABBMin += parcelDisp;
parcel.AABBMax += parcelDisp;
}
// Validate User and Group UUID's
@@ -809,7 +879,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
ITerrainModule terrainModule = scene.RequestModuleInterface<ITerrainModule>();
MemoryStream ms = new MemoryStream(data);
terrainModule.LoadFromStream(terrainPath, ms);
if (m_displacement != Vector3.Zero || m_rotation != 0f)
{
Vector2 rotationCenter = new Vector2(m_rotationCenter.X, m_rotationCenter.Y);
terrainModule.LoadFromStream(terrainPath, m_displacement, m_rotation, rotationCenter, ms);
}
else
{
terrainModule.LoadFromStream(terrainPath, ms);
}
ms.Close();
m_log.DebugFormat("[ARCHIVER]: Restored terrain {0}", terrainPath);

View File

@@ -533,7 +533,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (isMegaregion)
size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID);
else
size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
size = new Vector2((float)scene.RegionInfo.RegionSizeX, (float)scene.RegionInfo.RegionSizeY);
xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));

View File

@@ -33,11 +33,14 @@ using log4net;
using NDesk.Options;
using Nini.Config;
using Mono.Addins;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
namespace OpenSim.Region.CoreModules.World.Archiver
{
/// <summary>
@@ -101,9 +104,59 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
bool mergeOar = false;
bool skipAssets = false;
bool forceTerrain = false;
bool forceParcels = false;
bool noObjects = false;
Vector3 displacement = new Vector3(0f, 0f, 0f);
float rotation = 0f;
Vector3 rotationCenter = new Vector3(Constants.RegionSize / 2f, Constants.RegionSize / 2f, 0);
OptionSet options = new OptionSet().Add("m|merge", delegate (string v) { mergeOar = v != null; });
options.Add("s|skip-assets", delegate (string v) { skipAssets = v != null; });
OptionSet options = new OptionSet();
options.Add("m|merge", delegate (string v) { mergeOar = (v != null); });
options.Add("s|skip-assets", delegate (string v) { skipAssets = (v != null); });
options.Add("force-terrain", delegate (string v) { forceTerrain = (v != null); });
options.Add("forceterrain", delegate (string v) { forceTerrain = (v != null); }); // downward compatibility
options.Add("force-parcels", delegate (string v) { forceParcels = (v != null); });
options.Add("forceparcels", delegate (string v) { forceParcels = (v != null); }); // downward compatibility
options.Add("no-objects", delegate (string v) { noObjects = (v != null); });
options.Add("displacement=", delegate (string v) {
try
{
displacement = v == null ? Vector3.Zero : Vector3.Parse(v);
}
catch
{
m_log.ErrorFormat("[ARCHIVER MODULE] failure parsing displacement");
m_log.ErrorFormat("[ARCHIVER MODULE] Must be represented as vector3: --displacement \"<128,128,0>\"");
return;
}
});
options.Add("rotation=", delegate (string v) {
try
{
rotation = v == null ? 0f : float.Parse(v);
}
catch
{
m_log.ErrorFormat("[ARCHIVER MODULE] failure parsing rotation");
m_log.ErrorFormat("[ARCHIVER MODULE] Must be an angle in degrees between -360 and +360: --rotation 45");
return;
}
// Convert to radians for internals
rotation = Util.Clamp<float>(rotation, -359f, 359f) / 180f * (float)Math.PI;
});
options.Add("rotation-center=", delegate (string v) {
try
{
rotationCenter = v == null ? Vector3.Zero : Vector3.Parse(v);
}
catch
{
m_log.ErrorFormat("[ARCHIVER MODULE] failure parsing rotation displacement");
m_log.ErrorFormat("[ARCHIVER MODULE] Must be represented as vector3: --rotation-center \"<128,128,0>\"");
return;
}
});
// Send a message to the region ready module
/* bluewall* Disable this for the time being
@@ -122,13 +175,23 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// foreach (string param in mainParams)
// m_log.DebugFormat("GOT PARAM [{0}]", param);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
if (mergeOar) archiveOptions.Add("merge", null);
if (skipAssets) archiveOptions.Add("skipAssets", null);
if (forceTerrain) archiveOptions.Add("force-terrain", null);
if (forceParcels) archiveOptions.Add("force-parcels", null);
if (noObjects) archiveOptions.Add("no-objects", null);
archiveOptions.Add("displacement", displacement);
archiveOptions.Add("rotation", rotation);
archiveOptions.Add("rotation-center", rotationCenter);
if (mainParams.Count > 2)
{
DearchiveRegion(mainParams[2], mergeOar, skipAssets, Guid.Empty);
DearchiveRegion(mainParams[2], Guid.Empty, archiveOptions);
}
else
{
DearchiveRegion(DEFAULT_OAR_BACKUP_FILENAME, mergeOar, skipAssets, Guid.Empty);
DearchiveRegion(DEFAULT_OAR_BACKUP_FILENAME, Guid.Empty, archiveOptions);
}
}
@@ -198,25 +261,27 @@ namespace OpenSim.Region.CoreModules.World.Archiver
public void DearchiveRegion(string loadPath)
{
DearchiveRegion(loadPath, false, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
DearchiveRegion(loadPath, Guid.Empty, archiveOptions);
}
public void DearchiveRegion(string loadPath, bool merge, bool skipAssets, Guid requestId)
public void DearchiveRegion(string loadPath, Guid requestId, Dictionary<string,object> options)
{
m_log.InfoFormat(
"[ARCHIVER]: Loading archive to region {0} from {1}", Scene.RegionInfo.RegionName, loadPath);
new ArchiveReadRequest(Scene, loadPath, merge, skipAssets, requestId).DearchiveRegion();
new ArchiveReadRequest(Scene, loadPath, requestId, options).DearchiveRegion();
}
public void DearchiveRegion(Stream loadStream)
{
DearchiveRegion(loadStream, false, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
DearchiveRegion(loadStream, Guid.Empty, archiveOptions);
}
public void DearchiveRegion(Stream loadStream, bool merge, bool skipAssets, Guid requestId)
public void DearchiveRegion(Stream loadStream, Guid requestId, Dictionary<string, object> options)
{
new ArchiveReadRequest(Scene, loadStream, merge, skipAssets, requestId).DearchiveRegion();
new ArchiveReadRequest(Scene, loadStream, requestId, options).DearchiveRegion();
}
}
}

View File

@@ -224,8 +224,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, Guid.Empty, archiveOptions);
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -308,8 +309,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, Guid.Empty, archiveOptions);
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -577,7 +579,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
ArchiveConstants.CONTROL_FILE_PATH,
new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
LandObject lo = new LandObject(groupID, true, null);
LandObject lo = new LandObject(groupID, true, m_scene);
lo.SetLandBitmap(lo.BasicFullRegionLandBitmap());
LandData ld = lo.LandData;
ld.GlobalID = landID;
@@ -752,7 +754,9 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
byte[] archive = archiveWriteStream.ToArray();
MemoryStream archiveReadStream = new MemoryStream(archive);
m_archiverModule.DearchiveRegion(archiveReadStream, true, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
archiveOptions.Add("merge", null);
m_archiverModule.DearchiveRegion(archiveReadStream, Guid.Empty, archiveOptions);
SceneObjectPart object1Existing = m_scene.GetSceneObjectPart(part1.Name);
Assert.That(object1Existing, Is.Not.Null, "object1 was not present after merge");
@@ -860,7 +864,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
Dictionary<string, object> archiveOptions = new Dictionary<string, object>();
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, Guid.Empty, archiveOptions);
arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);

View File

@@ -64,6 +64,7 @@ namespace OpenSim.Region.CoreModules.World.Land
public class LandManagementModule : INonSharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[LAND MANAGEMENT MODULE]";
private static readonly string remoteParcelRequestPath = "0009/";
@@ -74,15 +75,11 @@ namespace OpenSim.Region.CoreModules.World.Land
protected IPrimCountModule m_primCountModule;
protected IDialogModule m_Dialog;
// Minimum for parcels to work is 64m even if we don't actually use them.
#pragma warning disable 0429
private const int landArrayMax = ((int)((int)Constants.RegionSize / 4) >= 64) ? (int)((int)Constants.RegionSize / 4) : 64;
#pragma warning restore 0429
/// <value>
/// Local land ids at specified region co-ordinates (region size / 4)
/// </value>
private readonly int[,] m_landIDList = new int[landArrayMax, landArrayMax];
private int[,] m_landIDList;
private const int landUnit = 4;
/// <value>
/// Land objects keyed by local id
@@ -115,6 +112,8 @@ namespace OpenSim.Region.CoreModules.World.Land
public void AddRegion(Scene scene)
{
m_scene = scene;
m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
m_landIDList.Initialize();
landChannel = new LandChannel(scene, this);
@@ -297,6 +296,7 @@ namespace OpenSim.Region.CoreModules.World.Land
{
m_landList.Clear();
m_lastLandLocalID = LandChannel.START_LAND_LOCAL_ID - 1;
m_landIDList = new int[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
m_landIDList.Initialize();
}
}
@@ -311,7 +311,8 @@ namespace OpenSim.Region.CoreModules.World.Land
"[LAND MANAGEMENT MODULE]: Creating default parcel for region {0}", m_scene.RegionInfo.RegionName);
ILandObject fullSimParcel = new LandObject(UUID.Zero, false, m_scene);
fullSimParcel.SetLandBitmap(fullSimParcel.GetSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize));
fullSimParcel.SetLandBitmap(fullSimParcel.GetSquareLandBitmap(0, 0,
(int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY));
fullSimParcel.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
fullSimParcel.LandData.ClaimDate = Util.UnixTimeSinceEpoch();
@@ -438,8 +439,8 @@ namespace OpenSim.Region.CoreModules.World.Land
public void SendLandUpdate(ScenePresence avatar, bool force)
{
ILandObject over = GetLandObject((int)Math.Min(((int)Constants.RegionSize - 1), Math.Max(0, Math.Round(avatar.AbsolutePosition.X))),
(int)Math.Min(((int)Constants.RegionSize - 1), Math.Max(0, Math.Round(avatar.AbsolutePosition.Y))));
ILandObject over = GetLandObject((int)Math.Min(((int)m_scene.RegionInfo.RegionSizeX - 1), Math.Max(0, Math.Round(avatar.AbsolutePosition.X))),
(int)Math.Min(((int)m_scene.RegionInfo.RegionSizeY - 1), Math.Max(0, Math.Round(avatar.AbsolutePosition.Y))));
if (over != null)
{
@@ -605,17 +606,29 @@ namespace OpenSim.Region.CoreModules.World.Land
new_land.LandData.LocalID = newLandLocalID;
bool[,] landBitmap = new_land.GetLandBitmap();
for (int x = 0; x < landArrayMax; x++)
// m_log.DebugFormat("{0} AddLandObject. new_land.bitmapSize=({1},{2}). newLocalID={3}",
// LogHeader, landBitmap.GetLength(0), landBitmap.GetLength(1), newLandLocalID);
if (landBitmap.GetLength(0) != m_landIDList.GetLength(0) || landBitmap.GetLength(1) != m_landIDList.GetLength(1))
{
for (int y = 0; y < landArrayMax; y++)
// Going to variable sized regions can cause mismatches
m_log.ErrorFormat("{0} AddLandObject. Added land bitmap different size than region ID map. bitmapSize=({1},{2}), landIDSize=({3},{4})",
LogHeader, landBitmap.GetLength(0), landBitmap.GetLength(1), m_landIDList.GetLength(0), m_landIDList.GetLength(1) );
}
else
{
for (int x = 0; x < landBitmap.GetLength(0); x++)
{
if (landBitmap[x, y])
for (int y = 0; y < landBitmap.GetLength(1); y++)
{
// m_log.DebugFormat(
// "[LAND MANAGEMENT MODULE]: Registering parcel {0} for land co-ord ({1}, {2}) on {3}",
// new_land.LandData.Name, x, y, m_scene.RegionInfo.RegionName);
m_landIDList[x, y] = newLandLocalID;
if (landBitmap[x, y])
{
// m_log.DebugFormat(
// "[LAND MANAGEMENT MODULE]: Registering parcel {0} for land co-ord ({1}, {2}) on {3}",
// new_land.LandData.Name, x, y, m_scene.RegionInfo.RegionName);
m_landIDList[x, y] = newLandLocalID;
}
}
}
}
@@ -637,9 +650,9 @@ namespace OpenSim.Region.CoreModules.World.Land
ILandObject land;
lock (m_landList)
{
for (int x = 0; x < 64; x++)
for (int x = 0; x < m_landIDList.GetLength(0); x++)
{
for (int y = 0; y < 64; y++)
for (int y = 0; y < m_landIDList.GetLength(1); y++)
{
if (m_landIDList[x, y] == local_id)
{
@@ -691,9 +704,9 @@ namespace OpenSim.Region.CoreModules.World.Land
bool[,] landBitmapSlave = slave.GetLandBitmap();
lock (m_landList)
{
for (int x = 0; x < 64; x++)
for (int x = 0; x < landBitmapSlave.GetLength(0); x++)
{
for (int y = 0; y < 64; y++)
for (int y = 0; y < landBitmapSlave.GetLength(1); y++)
{
if (landBitmapSlave[x, y])
{
@@ -727,23 +740,28 @@ namespace OpenSim.Region.CoreModules.World.Land
/// <returns>Land object at the point supplied</returns>
public ILandObject GetLandObject(float x_float, float y_float)
{
return GetLandObject((int)x_float, (int)y_float, true /* returnNullIfLandObjectNotFound */);
/*
int x;
int y;
if (x_float >= Constants.RegionSize || x_float < 0 || y_float >= Constants.RegionSize || y_float < 0)
if (x_float >= m_scene.RegionInfo.RegionSizeX || x_float < 0 || y_float >= m_scene.RegionInfo.RegionSizeX || y_float < 0)
return null;
try
{
x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x_float) / 4.0));
y = Convert.ToInt32(Math.Floor(Convert.ToDouble(y_float) / 4.0));
x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x_float) / (float)landUnit));
y = Convert.ToInt32(Math.Floor(Convert.ToDouble(y_float) / (float)landUnit));
}
catch (OverflowException)
{
return null;
}
if (x >= 64 || y >= 64 || x < 0 || y < 0)
if (x >= (m_scene.RegionInfo.RegionSizeX / landUnit)
|| y >= (m_scene.RegionInfo.RegionSizeY / landUnit)
|| x < 0
|| y < 0)
{
return null;
}
@@ -759,38 +777,122 @@ namespace OpenSim.Region.CoreModules.World.Land
// m_log.DebugFormat(
// "[LAND MANAGEMENT MODULE]: No land object found at ({0}, {1}) on {2}",
// x, y, m_scene.RegionInfo.RegionName);
if (m_landList.ContainsKey(m_landIDList[x, y]))
return m_landList[m_landIDList[x, y]];
try
{
if (m_landList.ContainsKey(m_landIDList[x, y]))
return m_landList[m_landIDList[x, y]];
}
catch (Exception e)
{
m_log.DebugFormat("{0} GetLandObject exception. x={1}, y={2}, m_landIDList.len=({3},{4})",
LogHeader, x, y, m_landIDList.GetLength(0), m_landIDList.GetLength(1));
}
return null;
}
*/
}
// Public entry.
// Throws exception if land object is not found
public ILandObject GetLandObject(int x, int y)
{
if (x >= Convert.ToInt32(Constants.RegionSize) || y >= Convert.ToInt32(Constants.RegionSize) || x < 0 || y < 0)
return GetLandObject(x, y, false /* returnNullIfLandObjectNotFound */);
}
// Given a region position, return the parcel land object for that location
private ILandObject GetLandObject(int x, int y, bool returnNullIfLandObjectNotFound)
{
ILandObject ret = null;
if (x >= m_scene.RegionInfo.RegionSizeX || y >= m_scene.RegionInfo.RegionSizeY || x < 0 || y < 0)
{
// These exceptions here will cause a lot of complaints from the users specifically because
// they happen every time at border crossings
throw new Exception("Error: Parcel not found at point " + x + ", " + y);
if (returnNullIfLandObjectNotFound)
return null;
else
throw new Exception(
String.Format("{0} GetLandObject for non-existant position. Region={1}, pos=<{2},{3}",
LogHeader, m_scene.RegionInfo.RegionName, x, y)
);
}
lock (m_landIDList)
{
try
{
return m_landList[m_landIDList[x / 4, y / 4]];
int landID = m_landIDList[x / landUnit, y / landUnit];
if (landID == 0)
{
// Zero is the uninitialized value saying there is no parcel for this location.
// This sometimes happens when terrain is resized.
if (m_landList.Count == 1)
{
int onlyParcelID = 0;
ILandObject onlyLandObject = null;
foreach (KeyValuePair<int, ILandObject> kvp in m_landList)
{
onlyParcelID = kvp.Key;
onlyLandObject = kvp.Value;
break;
}
// There is only one parcel. Grow it to fill all the unallocated spaces.
for (int xx = 0; xx < m_landIDList.GetLength(0); xx++)
for (int yy = 0; yy < m_landIDList.GetLength(1); yy++)
if (m_landIDList[xx, yy] == 0)
m_landIDList[xx, yy] = onlyParcelID;
onlyLandObject.LandBitmap = CreateBitmapForID(onlyParcelID);
landID = onlyParcelID;
}
else
{
// There are several other parcels so we must create a new one for the unassigned space
ILandObject newLand = new LandObject(UUID.Zero, false, m_scene);
// Claim all the unclaimed "0" ids
newLand.SetLandBitmap(CreateBitmapForID(0));
newLand.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
newLand.LandData.ClaimDate = Util.UnixTimeSinceEpoch();
AddLandObject(newLand);
landID = m_lastLandLocalID;
}
}
ret = m_landList[landID];
}
catch (IndexOutOfRangeException)
{
// m_log.WarnFormat(
// "[LAND MANAGEMENT MODULE]: Tried to retrieve land object from out of bounds co-ordinate ({0},{1}) in {2}",
// x, y, m_scene.RegionInfo.RegionName);
m_log.ErrorFormat(
"{0} GetLandObject: Tried to retrieve land object from out of bounds co-ordinate ({1},{2}) in {3}. landListSize=({4},{5})",
LogHeader, x, y, m_scene.RegionInfo.RegionName, m_landIDList.GetLength(0), m_landIDList.GetLength(1));
return null;
}
catch
{
m_log.ErrorFormat(
"{0} GetLandObject: LandID not in landlist. XY=<{1},{2}> in {3}. landID[x,y]={4}",
LogHeader, x, y, m_scene.RegionInfo.RegionName, m_landIDList[x/landUnit, y/landUnit]);
return null;
}
}
return ret;
}
// Create a 'parcel is here' bitmap for the parcel identified by the passed landID
private bool[,] CreateBitmapForID(int landID)
{
bool[,] ret = new bool[m_landIDList.GetLength(0), m_landIDList.GetLength(1)];
ret.Initialize();
for (int xx = 0; xx < m_landIDList.GetLength(0); xx++)
for (int yy = 0; yy < m_landIDList.GetLength(0); yy++)
if (m_landIDList[xx, yy] == landID)
ret[xx, yy] = true;
return ret;
}
#endregion
@@ -1053,85 +1155,93 @@ namespace OpenSim.Region.CoreModules.World.Land
byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET];
int byteArrayCount = 0;
int sequenceID = 0;
int blockmeters = 4 * (int) Constants.RegionSize/(int)Constants.TerrainPatchSize;
for (int y = 0; y < blockmeters; y++)
// Layer data is in landUnit (4m) chunks
for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / landUnit); y++)
{
for (int x = 0; x < blockmeters; x++)
for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize * (Constants.TerrainPatchSize / landUnit); x++)
{
byte tempByte = 0; //This represents the byte for the current 4x4
ILandObject currentParcelBlock = GetLandObject(x * 4, y * 4);
if (currentParcelBlock != null)
byteArray[byteArrayCount] = BuildLayerByte(GetLandObject(x * landUnit, y * landUnit), x, y, remote_client);
byteArrayCount++;
if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
{
if (currentParcelBlock.LandData.OwnerID == remote_client.AgentId)
{
//Owner Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_REQUESTER);
}
else if (currentParcelBlock.LandData.SalePrice > 0 &&
(currentParcelBlock.LandData.AuthBuyerID == UUID.Zero ||
currentParcelBlock.LandData.AuthBuyerID == remote_client.AgentId))
{
//Sale Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_IS_FOR_SALE);
}
else if (currentParcelBlock.LandData.OwnerID == UUID.Zero)
{
//Public Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_PUBLIC);
}
else
{
//Other Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_OTHER);
}
//Now for border control
ILandObject westParcel = null;
ILandObject southParcel = null;
if (x > 0)
{
westParcel = GetLandObject((x - 1) * 4, y * 4);
}
if (y > 0)
{
southParcel = GetLandObject(x * 4, (y - 1) * 4);
}
if (x == 0)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST);
}
else if (westParcel != null && westParcel != currentParcelBlock)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST);
}
if (y == 0)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH);
}
else if (southParcel != null && southParcel != currentParcelBlock)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH);
}
byteArray[byteArrayCount] = tempByte;
byteArrayCount++;
if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
{
remote_client.SendLandParcelOverlay(byteArray, sequenceID);
byteArrayCount = 0;
sequenceID++;
byteArray = new byte[LAND_BLOCKS_PER_PACKET];
}
remote_client.SendLandParcelOverlay(byteArray, sequenceID);
byteArrayCount = 0;
sequenceID++;
byteArray = new byte[LAND_BLOCKS_PER_PACKET];
}
}
}
if (byteArrayCount != 0)
{
remote_client.SendLandParcelOverlay(byteArray, sequenceID);
}
}
private byte BuildLayerByte(ILandObject currentParcelBlock, int x, int y, IClientAPI remote_client)
{
byte tempByte = 0; //This represents the byte for the current 4x4
if (currentParcelBlock != null)
{
if (currentParcelBlock.LandData.OwnerID == remote_client.AgentId)
{
//Owner Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_REQUESTER);
}
else if (currentParcelBlock.LandData.SalePrice > 0 &&
(currentParcelBlock.LandData.AuthBuyerID == UUID.Zero ||
currentParcelBlock.LandData.AuthBuyerID == remote_client.AgentId))
{
//Sale Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_IS_FOR_SALE);
}
else if (currentParcelBlock.LandData.OwnerID == UUID.Zero)
{
//Public Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_PUBLIC);
}
else
{
//Other Flag
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_OTHER);
}
//Now for border control
ILandObject westParcel = null;
ILandObject southParcel = null;
if (x > 0)
{
westParcel = GetLandObject((x - 1) * landUnit, y * landUnit);
}
if (y > 0)
{
southParcel = GetLandObject(x * landUnit, (y - 1) * landUnit);
}
if (x == 0)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST);
}
else if (westParcel != null && westParcel != currentParcelBlock)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST);
}
if (y == 0)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH);
}
else if (southParcel != null && southParcel != currentParcelBlock)
{
tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH);
}
}
return tempByte;
}
public void ClientOnParcelPropertiesRequest(int start_x, int start_y, int end_x, int end_y, int sequence_id,
@@ -1679,7 +1789,7 @@ namespace OpenSim.Region.CoreModules.World.Land
{
// most likely still cached from building the extLandData entry
uint x = 0, y = 0;
Utils.LongToUInts(data.RegionHandle, out x, out y);
Util.RegionHandleToWorldLoc(data.RegionHandle, out x, out y);
info = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y);
}
// we need to transfer the fake parcelID, not the one in landData, so the viewer can match it to the landmark.
@@ -2007,4 +2117,4 @@ namespace OpenSim.Region.CoreModules.World.Land
cdl.AddToStringBuilder(report);
}
}
}
}

View File

@@ -45,10 +45,10 @@ namespace OpenSim.Region.CoreModules.World.Land
#region Member Variables
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#pragma warning disable 0429
private const int landArrayMax = ((int)((int)Constants.RegionSize / 4) >= 64) ? (int)((int)Constants.RegionSize / 4) : 64;
#pragma warning restore 0429
private bool[,] m_landBitmap = new bool[landArrayMax,landArrayMax];
private static readonly string LogHeader = "[LAND OBJECT]";
private bool[,] m_landBitmap;
private readonly int landUnit = 4;
private int m_lastSeqId = 0;
@@ -93,15 +93,17 @@ namespace OpenSim.Region.CoreModules.World.Land
{
get
{
for (int y = 0; y < landArrayMax; y++)
for (int y = 0; y < LandBitmap.GetLength(1); y++)
{
for (int x = 0; x < landArrayMax; x++)
for (int x = 0; x < LandBitmap.GetLength(0); x++)
{
if (LandBitmap[x, y])
return new Vector3(x * 4, y * 4, 0);
return new Vector3(x * landUnit, y * landUnit, 0);
}
}
m_log.ErrorFormat("{0} StartPoint. No start point found. bitmapSize=<{1},{2}>",
LogHeader, LandBitmap.GetLength(0), LandBitmap.GetLength(1));
return new Vector3(-1, -1, -1);
}
}
@@ -110,17 +112,19 @@ namespace OpenSim.Region.CoreModules.World.Land
{
get
{
for (int y = landArrayMax - 1; y >= 0; y--)
for (int y = LandBitmap.GetLength(1) - 1; y >= 0; y--)
{
for (int x = landArrayMax - 1; x >= 0; x--)
for (int x = LandBitmap.GetLength(0) - 1; x >= 0; x--)
{
if (LandBitmap[x, y])
{
return new Vector3(x * 4 + 4, y * 4 + 4, 0);
return new Vector3(x * landUnit + landUnit, y * landUnit + landUnit, 0);
}
}
}
m_log.ErrorFormat("{0} EndPoint. No end point found. bitmapSize=<{1},{2}>",
LogHeader, LandBitmap.GetLength(0), LandBitmap.GetLength(1));
return new Vector3(-1, -1, -1);
}
}
@@ -130,6 +134,11 @@ namespace OpenSim.Region.CoreModules.World.Land
public LandObject(UUID owner_id, bool is_group_owned, Scene scene)
{
m_scene = scene;
if (m_scene == null)
m_landBitmap = new bool[Constants.RegionSize / landUnit, Constants.RegionSize / landUnit];
else
m_landBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
LandData.OwnerID = owner_id;
if (is_group_owned)
LandData.GroupID = owner_id;
@@ -152,9 +161,9 @@ namespace OpenSim.Region.CoreModules.World.Land
/// <returns>Returns true if the piece of land contains the specified point</returns>
public bool ContainsPoint(int x, int y)
{
if (x >= 0 && y >= 0 && x < Constants.RegionSize && y < Constants.RegionSize)
if (x >= 0 && y >= 0 && x < m_scene.RegionInfo.RegionSizeX && y < m_scene.RegionInfo.RegionSizeY)
{
return (LandBitmap[x / 4, y / 4] == true);
return (LandBitmap[x / landUnit, y / landUnit] == true);
}
else
{
@@ -194,7 +203,7 @@ namespace OpenSim.Region.CoreModules.World.Land
else
{
// Normal Calculations
int parcelMax = (int)(((float)LandData.Area / 65536.0f)
int parcelMax = (int)(((float)LandData.Area / (m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY))
* (float)m_scene.RegionInfo.ObjectCapacity
* (float)m_scene.RegionInfo.RegionSettings.ObjectBonus);
// TODO: The calculation of ObjectBonus should be refactored. It does still not work in the same manner as SL!
@@ -211,7 +220,7 @@ namespace OpenSim.Region.CoreModules.World.Land
else
{
//Normal Calculations
int simMax = (int)(((float)LandData.SimwideArea / 65536.0f)
int simMax = (int)(((float)LandData.SimwideArea / (m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY))
* (float)m_scene.RegionInfo.ObjectCapacity);
return simMax;
}
@@ -224,7 +233,12 @@ namespace OpenSim.Region.CoreModules.World.Land
public void SendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client)
{
IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>();
uint regionFlags = 336723974 & ~((uint)(RegionFlags.AllowLandmark | RegionFlags.AllowSetHome));
// uint regionFlags = 336723974 & ~((uint)(RegionFlags.AllowLandmark | RegionFlags.AllowSetHome));
uint regionFlags = (uint)(RegionFlags.PublicAllowed
| RegionFlags.AllowDirectTeleport
| RegionFlags.AllowParcelChanges
| RegionFlags.AllowVoice );
if (estateModule != null)
regionFlags = estateModule.GetRegionFlags();
@@ -559,8 +573,8 @@ namespace OpenSim.Region.CoreModules.World.Land
try
{
over =
m_scene.LandChannel.GetLandObject(Util.Clamp<int>((int)Math.Round(avatar.AbsolutePosition.X), 0, ((int)Constants.RegionSize - 1)),
Util.Clamp<int>((int)Math.Round(avatar.AbsolutePosition.Y), 0, ((int)Constants.RegionSize - 1)));
m_scene.LandChannel.GetLandObject(Util.Clamp<int>((int)Math.Round(avatar.AbsolutePosition.X), 0, ((int)m_scene.RegionInfo.RegionSizeX - 1)),
Util.Clamp<int>((int)Math.Round(avatar.AbsolutePosition.Y), 0, ((int)m_scene.RegionInfo.RegionSizeY - 1)));
}
catch (Exception)
{
@@ -707,15 +721,15 @@ namespace OpenSim.Region.CoreModules.World.Land
/// </summary>
private void UpdateAABBAndAreaValues()
{
int min_x = 64;
int min_y = 64;
int min_x = 10000;
int min_y = 10000;
int max_x = 0;
int max_y = 0;
int tempArea = 0;
int x, y;
for (x = 0; x < 64; x++)
for (x = 0; x < LandBitmap.GetLength(0); x++)
{
for (y = 0; y < 64; y++)
for (y = 0; y < LandBitmap.GetLength(1); y++)
{
if (LandBitmap[x, y] == true)
{
@@ -723,31 +737,31 @@ namespace OpenSim.Region.CoreModules.World.Land
if (min_y > y) min_y = y;
if (max_x < x) max_x = x;
if (max_y < y) max_y = y;
tempArea += 16; //16sqm peice of land
tempArea += landUnit * landUnit; //16sqm peice of land
}
}
}
int tx = min_x * 4;
if (tx > ((int)Constants.RegionSize - 1))
tx = ((int)Constants.RegionSize - 1);
int ty = min_y * 4;
if (ty > ((int)Constants.RegionSize - 1))
ty = ((int)Constants.RegionSize - 1);
int tx = min_x * landUnit;
if (tx > ((int)m_scene.RegionInfo.RegionSizeX - 1))
tx = ((int)m_scene.RegionInfo.RegionSizeX - 1);
int ty = min_y * landUnit;
if (ty > ((int)m_scene.RegionInfo.RegionSizeY - 1))
ty = ((int)m_scene.RegionInfo.RegionSizeY - 1);
LandData.AABBMin =
new Vector3(
(float)(min_x * 4), (float)(min_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
(float)(min_x * landUnit), (float)(min_y * landUnit), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
tx = max_x * 4;
if (tx > ((int)Constants.RegionSize - 1))
tx = ((int)Constants.RegionSize - 1);
ty = max_y * 4;
if (ty > ((int)Constants.RegionSize - 1))
ty = ((int)Constants.RegionSize - 1);
tx = max_x * landUnit;
if (tx > ((int)m_scene.RegionInfo.RegionSizeX - 1))
tx = ((int)m_scene.RegionInfo.RegionSizeX - 1);
ty = max_y * landUnit;
if (ty > ((int)m_scene.RegionInfo.RegionSizeY - 1))
ty = ((int)m_scene.RegionInfo.RegionSizeY - 1);
LandData.AABBMax
= new Vector3(
(float)(max_x * 4), (float)(max_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
(float)(max_x * landUnit), (float)(max_y * landUnit), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
LandData.Area = tempArea;
}
@@ -759,20 +773,12 @@ namespace OpenSim.Region.CoreModules.World.Land
/// <summary>
/// Sets the land's bitmap manually
/// </summary>
/// <param name="bitmap">64x64 block representing where this land is on a map</param>
/// <param name="bitmap">block representing where this land is on a map mapped in a 4x4 meter grid</param>
public void SetLandBitmap(bool[,] bitmap)
{
if (bitmap.GetLength(0) != 64 || bitmap.GetLength(1) != 64 || bitmap.Rank != 2)
{
//Throw an exception - The bitmap is not 64x64
//throw new Exception("Error: Invalid Parcel Bitmap");
}
else
{
//Valid: Lets set it
LandBitmap = bitmap;
ForceUpdateLandInfo();
}
LandBitmap = bitmap;
// m_log.DebugFormat("{0} SetLandBitmap. BitmapSize=<{1},{2}>", LogHeader, LandBitmap.GetLength(0), LandBitmap.GetLength(1));
ForceUpdateLandInfo();
}
/// <summary>
@@ -786,15 +792,19 @@ namespace OpenSim.Region.CoreModules.World.Land
public bool[,] BasicFullRegionLandBitmap()
{
return GetSquareLandBitmap(0, 0, (int) Constants.RegionSize, (int) Constants.RegionSize);
return GetSquareLandBitmap(0, 0, (int)m_scene.RegionInfo.RegionSizeX, (int) m_scene.RegionInfo.RegionSizeY);
}
public bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y)
{
bool[,] tempBitmap = new bool[64,64];
// Empty bitmap for the whole region
bool[,] tempBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
tempBitmap.Initialize();
// Fill the bitmap square area specified by state and end
tempBitmap = ModifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true);
// m_log.DebugFormat("{0} GetSquareLandBitmap. tempBitmapSize=<{1},{2}>",
// LogHeader, tempBitmap.GetLength(0), tempBitmap.GetLength(1));
return tempBitmap;
}
@@ -811,24 +821,20 @@ namespace OpenSim.Region.CoreModules.World.Land
public bool[,] ModifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y,
bool set_value)
{
if (land_bitmap.GetLength(0) != 64 || land_bitmap.GetLength(1) != 64 || land_bitmap.Rank != 2)
{
//Throw an exception - The bitmap is not 64x64
//throw new Exception("Error: Invalid Parcel Bitmap in modifyLandBitmapSquare()");
}
int x, y;
for (y = 0; y < 64; y++)
for (y = 0; y < land_bitmap.GetLength(1); y++)
{
for (x = 0; x < 64; x++)
for (x = 0; x < land_bitmap.GetLength(0); x++)
{
if (x >= start_x / 4 && x < end_x / 4
&& y >= start_y / 4 && y < end_y / 4)
if (x >= start_x / landUnit && x < end_x / landUnit
&& y >= start_y / landUnit && y < end_y / landUnit)
{
land_bitmap[x, y] = set_value;
}
}
}
// m_log.DebugFormat("{0} ModifyLandBitmapSquare. startXY=<{1},{2}>, endXY=<{3},{4}>, val={5}, landBitmapSize=<{6},{7}>",
// LogHeader, start_x, start_y, end_x, end_y, set_value, land_bitmap.GetLength(0), land_bitmap.GetLength(1));
return land_bitmap;
}
@@ -840,21 +846,21 @@ namespace OpenSim.Region.CoreModules.World.Land
/// <returns></returns>
public bool[,] MergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add)
{
if (bitmap_base.GetLength(0) != 64 || bitmap_base.GetLength(1) != 64 || bitmap_base.Rank != 2)
if (bitmap_base.GetLength(0) != bitmap_add.GetLength(0)
|| bitmap_base.GetLength(1) != bitmap_add.GetLength(1)
|| bitmap_add.Rank != 2
|| bitmap_base.Rank != 2)
{
//Throw an exception - The bitmap is not 64x64
throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_base in mergeLandBitmaps");
}
if (bitmap_add.GetLength(0) != 64 || bitmap_add.GetLength(1) != 64 || bitmap_add.Rank != 2)
{
//Throw an exception - The bitmap is not 64x64
throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_add in mergeLandBitmaps");
throw new Exception(
String.Format("{0} MergeLandBitmaps. merging maps not same size. baseSizeXY=<{1},{2}>, addSizeXY=<{3},{4}>",
LogHeader, bitmap_base.GetLength(0), bitmap_base.GetLength(1), bitmap_add.GetLength(0), bitmap_add.GetLength(1))
);
}
int x, y;
for (y = 0; y < 64; y++)
for (y = 0; y < bitmap_base.GetLength(1); y++)
{
for (x = 0; x < 64; x++)
for (x = 0; x < bitmap_add.GetLength(0); x++)
{
if (bitmap_add[x, y])
{
@@ -871,13 +877,13 @@ namespace OpenSim.Region.CoreModules.World.Land
/// <returns></returns>
private byte[] ConvertLandBitmapToBytes()
{
byte[] tempConvertArr = new byte[512];
byte[] tempConvertArr = new byte[LandBitmap.GetLength(0) * LandBitmap.GetLength(1) / 8];
byte tempByte = 0;
int x, y, i, byteNum = 0;
i = 0;
for (y = 0; y < 64; y++)
int byteNum = 0;
int i = 0;
for (int y = 0; y < LandBitmap.GetLength(1); y++)
{
for (x = 0; x < 64; x++)
for (int x = 0; x < LandBitmap.GetLength(0); x++)
{
tempByte = Convert.ToByte(tempByte | Convert.ToByte(LandBitmap[x, y]) << (i++ % 8));
if (i % 8 == 0)
@@ -889,30 +895,52 @@ namespace OpenSim.Region.CoreModules.World.Land
}
}
}
// m_log.DebugFormat("{0} ConvertLandBitmapToBytes. BitmapSize=<{1},{2}>",
// LogHeader, LandBitmap.GetLength(0), LandBitmap.GetLength(1));
return tempConvertArr;
}
private bool[,] ConvertBytesToLandBitmap()
{
bool[,] tempConvertMap = new bool[landArrayMax, landArrayMax];
bool[,] tempConvertMap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
tempConvertMap.Initialize();
byte tempByte = 0;
int x = 0, y = 0, i = 0, bitNum = 0;
for (i = 0; i < 512; i++)
// Math.Min overcomes an old bug that might have made it into the database. Only use the bytes that fit into convertMap.
int bitmapLen = Math.Min(LandData.Bitmap.Length, tempConvertMap.GetLength(0) * tempConvertMap.GetLength(1) / 8);
int xLen = (int)(m_scene.RegionInfo.RegionSizeX / landUnit);
if (bitmapLen == 512)
{
// Legacy bitmap being passed in. Use the legacy region size
// and only set the lower area of the larger region.
xLen = (int)(Constants.RegionSize / landUnit);
}
// m_log.DebugFormat("{0} ConvertBytesToLandBitmap: bitmapLen={1}, xLen={2}", LogHeader, bitmapLen, xLen);
int x = 0, y = 0;
for (int i = 0; i < bitmapLen; i++)
{
tempByte = LandData.Bitmap[i];
for (bitNum = 0; bitNum < 8; bitNum++)
for (int bitNum = 0; bitNum < 8; bitNum++)
{
bool bit = Convert.ToBoolean(Convert.ToByte(tempByte >> bitNum) & (byte) 1);
tempConvertMap[x, y] = bit;
try
{
tempConvertMap[x, y] = bit;
}
catch (Exception e)
{
m_log.DebugFormat("{0} ConvertBytestoLandBitmap: i={1}, x={2}, y={3}", LogHeader, i, x, y);
}
x++;
if (x > 63)
if (x >= xLen)
{
x = 0;
y++;
}
}
}
return tempConvertMap;
}

View File

@@ -102,7 +102,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
terrainRenderer.Initialise(m_scene, m_config);
mapbmp = new Bitmap((int)Constants.RegionSize, (int)Constants.RegionSize, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
mapbmp = new Bitmap((int)m_scene.Heightmap.Width, (int)m_scene.Heightmap.Height,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
//long t = System.Environment.TickCount;
//for (int i = 0; i < 10; ++i) {
terrainRenderer.TerrainToBitmap(mapbmp);
@@ -277,7 +278,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp)
{
int tc = 0;
double[,] hm = whichScene.Heightmap.GetDoubles();
ITerrainChannel hm = whichScene.Heightmap;
tc = Environment.TickCount;
m_log.Debug("[MAPTILE]: Generating Maptile Step 2: Object Volume Profile");
EntityBase[] objs = whichScene.GetEntities();
@@ -287,8 +288,6 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
try
{
//SortedList<float, RectangleDrawStruct> z_sort = new SortedList<float, RectangleDrawStruct>();
lock (objs)
{
foreach (EntityBase obj in objs)
@@ -298,7 +297,6 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
{
SceneObjectGroup mapdot = (SceneObjectGroup)obj;
Color mapdotspot = Color.Gray; // Default color when prim color is white
// Loop over prim in group
foreach (SceneObjectPart part in mapdot.Parts)
{
@@ -363,7 +361,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
Vector3 pos = part.GetWorldPosition();
// skip prim outside of retion
if (pos.X < 0f || pos.X > 256f || pos.Y < 0f || pos.Y > 256f)
if (!m_scene.PositionIsInCurrentRegion(pos))
continue;
// skip prim in non-finite position
@@ -388,7 +386,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
Vector3 lscale = new Vector3(part.Shape.Scale.X, part.Shape.Scale.Y, part.Shape.Scale.Z);
Vector3 scale = new Vector3();
Vector3 tScale = new Vector3();
Vector3 axPos = new Vector3(pos.X,pos.Y,pos.Z);
Vector3 axPos = new Vector3(pos.X, pos.Y, pos.Z);
Quaternion llrot = part.GetWorldRotation();
Quaternion rot = new Quaternion(llrot.W, llrot.X, llrot.Y, llrot.Z);
@@ -406,12 +404,17 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
int mapdrawendY = (int)(pos.Y + scale.Y);
// If object is beyond the edge of the map, don't draw it to avoid errors
if (mapdrawstartX < 0 || mapdrawstartX > ((int)Constants.RegionSize - 1) || mapdrawendX < 0 || mapdrawendX > ((int)Constants.RegionSize - 1)
|| mapdrawstartY < 0 || mapdrawstartY > ((int)Constants.RegionSize - 1) || mapdrawendY < 0
|| mapdrawendY > ((int)Constants.RegionSize - 1))
if (mapdrawstartX < 0
|| mapdrawstartX > (hm.Width - 1)
|| mapdrawendX < 0
|| mapdrawendX > (hm.Width - 1)
|| mapdrawstartY < 0
|| mapdrawstartY > (hm.Height - 1)
|| mapdrawendY < 0
|| mapdrawendY > (hm.Height - 1))
continue;
#region obb face reconstruction part duex
#region obb face reconstruction part duex
Vector3[] vertexes = new Vector3[8];
// float[] distance = new float[6];
@@ -515,7 +518,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
FaceD[2] = vertexes[7];
FaceC[3] = vertexes[7];
FaceD[5] = vertexes[7];
#endregion
#endregion
//int wy = 0;
@@ -530,11 +533,11 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
for (int i = 0; i < FaceA.Length; i++)
{
Point[] working = new Point[5];
working[0] = project(FaceA[i], axPos);
working[1] = project(FaceB[i], axPos);
working[2] = project(FaceD[i], axPos);
working[3] = project(FaceC[i], axPos);
working[4] = project(FaceA[i], axPos);
working[0] = project(hm, FaceA[i], axPos);
working[1] = project(hm, FaceB[i], axPos);
working[2] = project(hm, FaceD[i], axPos);
working[3] = project(hm, FaceC[i], axPos);
working[4] = project(hm, FaceA[i], axPos);
face workingface = new face();
workingface.pts = working;
@@ -546,27 +549,25 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
z_localIDs.Add(part.LocalId);
z_sortheights.Add(pos.Z);
//for (int wx = mapdrawstartX; wx < mapdrawendX; wx++)
//{
//for (wy = mapdrawstartY; wy < mapdrawendY; wy++)
//{
//m_log.InfoFormat("[MAPDEBUG]: {0},{1}({2})", wx, (255 - wy),wy);
//try
//{
// Remember, flip the y!
// mapbmp.SetPixel(wx, (255 - wy), mapdotspot);
//}
//catch (ArgumentException)
//{
// breakYN = true;
//}
//if (breakYN)
// break;
//}
//if (breakYN)
// break;
// for (int wx = mapdrawstartX; wx < mapdrawendX; wx++)
// {
// for (wy = mapdrawstartY; wy < mapdrawendY; wy++)
// {
// m_log.InfoFormat("[MAPDEBUG]: {0},{1}({2})", wx, (255 - wy),wy);
// try
// {
// // Remember, flip the y!
// mapbmp.SetPixel(wx, (255 - wy), mapdotspot);
// }
// catch (ArgumentException)
// {
// breakYN = true;
// }
// }
// if (breakYN)
// break;
// }
// }
//}
} // Object is within 256m Z of terrain
} // object is at least a meter wide
@@ -609,17 +610,17 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
return mapbmp;
}
private Point project(Vector3 point3d, Vector3 originpos)
private Point project(ITerrainChannel hm, Vector3 point3d, Vector3 originpos)
{
Point returnpt = new Point();
//originpos = point3d;
//int d = (int)(256f / 1.5f);
//Vector3 topos = new Vector3(0, 0, 0);
// float z = -point3d.z - topos.z;
// float z = -point3d.z - topos.z;
returnpt.X = (int)point3d.X;//(int)((topos.x - point3d.x) / z * d);
returnpt.Y = (int)(((int)Constants.RegionSize - 1) - point3d.Y);//(int)(255 - (((topos.y - point3d.y) / z * d)));
returnpt.Y = (int)((hm.Width - 1) - point3d.Y);//(int)(255 - (((topos.y - point3d.y) / z * d)));
return returnpt;
}

View File

@@ -31,6 +31,7 @@ using System.Reflection;
using log4net;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.CoreModules.World.LegacyMap
@@ -39,8 +40,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
{
private static readonly Color WATER_COLOR = Color.FromArgb(29, 71, 95);
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[SHADED MAPTILE RENDERER]";
private Scene m_scene;
//private IConfigSource m_config; // not used currently
@@ -53,19 +54,26 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
public void TerrainToBitmap(Bitmap mapbmp)
{
m_log.DebugFormat("{0} Generating Maptile Step 1: Terrain", LogHeader);
int tc = Environment.TickCount;
m_log.Debug("[SHADED MAP TILE RENDERER]: Generating Maptile Step 1: Terrain");
double[,] hm = m_scene.Heightmap.GetDoubles();
ITerrainChannel hm = m_scene.Heightmap;
if (mapbmp.Width != hm.Width || mapbmp.Height != hm.Height)
{
m_log.ErrorFormat("{0} TerrainToBitmap. Passed bitmap wrong dimensions. passed=<{1},{2}>, size=<{3},{4}>",
LogHeader, mapbmp.Width, mapbmp.Height, hm.Width, hm.Height);
}
bool ShadowDebugContinue = true;
bool terraincorruptedwarningsaid = false;
float low = 255;
float high = 0;
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int x = 0; x < hm.Width; x++)
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
for (int y = 0; y < hm.Height; y++)
{
float hmval = (float)hm[x, y];
if (hmval < low)
@@ -77,12 +85,12 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int x = 0; x < hm.Width; x++)
{
for (int y = 0; y < (int)Constants.RegionSize; y++)
for (int y = 0; y < hm.Height; y++)
{
// Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
int yr = ((int)Constants.RegionSize - 1) - y;
int yr = ((int)hm.Height - 1) - y;
float heightvalue = (float)hm[x, y];
@@ -109,12 +117,12 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
// .
//
// Shade the terrain for shadows
if (x < ((int)Constants.RegionSize - 1) && yr < ((int)Constants.RegionSize - 1))
if (x < (hm.Width - 1) && yr < (hm.Height - 1))
{
float hfvalue = (float)hm[x, y];
float hfvaluecompare = 0f;
if ((x + 1 < (int)Constants.RegionSize) && (y + 1 < (int)Constants.RegionSize))
if ((x + 1 < hm.Width) && (y + 1 < hm.Height))
{
hfvaluecompare = (float)hm[x + 1, y + 1]; // light from north-east => look at land height there
}
@@ -179,7 +187,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
if (ShadowDebugContinue)
{
if ((x - 1 > 0) && (yr + 1 < (int)Constants.RegionSize))
if ((x - 1 > 0) && (yr + 1 < hm.Height))
{
color = mapbmp.GetPixel(x - 1, yr + 1);
int r = color.R;
@@ -233,7 +241,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
terraincorruptedwarningsaid = true;
}
Color black = Color.Black;
mapbmp.SetPixel(x, ((int)Constants.RegionSize - y) - 1, black);
mapbmp.SetPixel(x, (hm.Width - y) - 1, black);
}
}
}
@@ -242,4 +250,4 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
m_log.Debug("[SHADED MAP TILE RENDERER]: Generating Maptile Step 1: Done in " + (Environment.TickCount - tc) + " ms");
}
}
}
}

View File

@@ -34,6 +34,8 @@ using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.CoreModules.World.LegacyMap
@@ -122,8 +124,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
{
#region Constants
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[TEXTURED MAPTILE RENDERER]";
// some hardcoded terrain UUIDs that work with SL 1.20 (the four default textures and "Blank").
// The color-values were choosen because they "look right" (at least to me) ;-)
@@ -173,7 +175,7 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
private Bitmap fetchTexture(UUID id)
{
AssetBase asset = m_scene.AssetService.Get(id.ToString());
m_log.DebugFormat("[TEXTURED MAP TILE RENDERER]: Fetched texture {0}, found: {1}", id, asset != null);
m_log.DebugFormat("{0} Fetched texture {1}, found: {2}", LogHeader, id, asset != null);
if (asset == null) return null;
ManagedImage managedImage;
@@ -188,18 +190,15 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
}
catch (DllNotFoundException)
{
m_log.ErrorFormat("[TEXTURED MAP TILE RENDERER]: OpenJpeg is not installed correctly on this system. Asset Data is empty for {0}", id);
m_log.ErrorFormat("{0} OpenJpeg is not installed correctly on this system. Asset Data is empty for {1}", LogHeader, id);
}
catch (IndexOutOfRangeException)
{
m_log.ErrorFormat("[TEXTURED MAP TILE RENDERER]: OpenJpeg was unable to encode this. Asset Data is empty for {0}", id);
m_log.ErrorFormat("{0} OpenJpeg was unable to encode this. Asset Data is empty for {1}", LogHeader, id);
}
catch (Exception)
{
m_log.ErrorFormat("[TEXTURED MAP TILE RENDERER]: OpenJpeg was unable to encode this. Asset Data is empty for {0}", id);
m_log.ErrorFormat("{0} OpenJpeg was unable to encode this. Asset Data is empty for {1}", LogHeader, id);
}
return null;
@@ -271,8 +270,8 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
// the heigthfield might have some jumps in values. Rendered land is smooth, though,
// as a slope is rendered at that place. So average 4 neighbour values to emulate that.
private float getHeight(double[,] hm, int x, int y) {
if (x < ((int)Constants.RegionSize - 1) && y < ((int)Constants.RegionSize - 1))
private float getHeight(ITerrainChannel hm, int x, int y) {
if (x < (hm.Width - 1) && y < (hm.Height - 1))
return (float)(hm[x, y] * .444 + (hm[x + 1, y] + hm[x, y + 1]) * .222 + hm[x + 1, y +1] * .112);
else
return (float)hm[x, y];
@@ -282,7 +281,15 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
public void TerrainToBitmap(Bitmap mapbmp)
{
int tc = Environment.TickCount;
m_log.Debug("[TEXTURED MAP TILE RENDERER]: Generating Maptile Step 1: Terrain");
m_log.DebugFormat("{0} Generating Maptile Step 1: Terrain", LogHeader);
ITerrainChannel hm = m_scene.Heightmap;
if (mapbmp.Width != hm.Width || mapbmp.Height != hm.Height)
{
m_log.ErrorFormat("{0} TerrainToBitmap. Passed bitmap wrong dimensions. passed=<{1},{2}>, size=<{3},{4}>",
LogHeader, mapbmp.Width, mapbmp.Height, hm.Width, hm.Height);
}
// These textures should be in the AssetCache anyway, as every client conneting to this
// region needs them. Except on start, when the map is recreated (before anyone connected),
@@ -310,19 +317,17 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
float waterHeight = (float)settings.WaterHeight;
double[,] hm = m_scene.Heightmap.GetDoubles();
for (int x = 0; x < (int)Constants.RegionSize; x++)
for (int x = 0; x < hm.Width; x++)
{
float columnRatio = x / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation
for (int y = 0; y < (int)Constants.RegionSize; y++)
float columnRatio = x / (hm.Width - 1); // 0 - 1, for interpolation
for (int y = 0; y < hm.Height; y++)
{
float rowRatio = y / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation
float rowRatio = y / (hm.Height - 1); // 0 - 1, for interpolation
// Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
int yr = ((int)Constants.RegionSize - 1) - y;
int yr = (hm.Height - 1) - y;
float heightvalue = getHeight(hm, x, y);
float heightvalue = getHeight(m_scene.Heightmap, x, y);
if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
heightvalue = 0;
@@ -372,9 +377,9 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
}
// Shade the terrain for shadows
if (x < ((int)Constants.RegionSize - 1) && y < ((int)Constants.RegionSize - 1))
if (x < (hm.Width - 1) && y < (hm.Height - 1))
{
float hfvaluecompare = getHeight(hm, x + 1, y + 1); // light from north-east => look at land height there
float hfvaluecompare = getHeight(m_scene.Heightmap, x + 1, y + 1); // light from north-east => look at land height there
if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
hfvaluecompare = 0f;
@@ -420,4 +425,4 @@ namespace OpenSim.Region.CoreModules.World.LegacyMap
m_log.Debug("[TEXTURED MAP TILE RENDERER]: Generating Maptile Step 1: Done in " + (Environment.TickCount - tc) + " ms");
}
}
}
}

View File

@@ -151,14 +151,9 @@ namespace OpenSim.Region.CoreModules.World.Objects.BuySell
break;
case 2: // Sell a copy
Vector3 inventoryStoredPosition = new Vector3
(((group.AbsolutePosition.X > (int)Constants.RegionSize)
? 250
: group.AbsolutePosition.X)
,
(group.AbsolutePosition.X > (int)Constants.RegionSize)
? 250
: group.AbsolutePosition.X,
Vector3 inventoryStoredPosition = new Vector3(
Math.Min(group.AbsolutePosition.X, m_scene.RegionInfo.RegionSizeX - 6),
Math.Min(group.AbsolutePosition.Y, m_scene.RegionInfo.RegionSizeY - 6),
group.AbsolutePosition.Z);
Vector3 originalPosition = group.AbsolutePosition;

View File

@@ -1571,10 +1571,10 @@ namespace OpenSim.Region.CoreModules.World.Permissions
float X = position.X;
float Y = position.Y;
if (X > ((int)Constants.RegionSize - 1))
X = ((int)Constants.RegionSize - 1);
if (Y > ((int)Constants.RegionSize - 1))
Y = ((int)Constants.RegionSize - 1);
if (X > ((int)m_scene.RegionInfo.RegionSizeX - 1))
X = ((int)m_scene.RegionInfo.RegionSizeX - 1);
if (Y > ((int)m_scene.RegionInfo.RegionSizeY - 1))
Y = ((int)m_scene.RegionInfo.RegionSizeY - 1);
if (X < 0)
X = 0;
if (Y < 0)

View File

@@ -68,9 +68,6 @@ namespace OpenSim.Region.CoreModules
// updating those region settings in GenSunPos()
private bool receivedEstateToolsSunUpdate = false;
// Configurable values
private string m_RegionMode = "SL";
// Sun's position information is updated and sent to clients every m_UpdateInterval frames
private int m_UpdateInterval = 0;
@@ -90,7 +87,6 @@ namespace OpenSim.Region.CoreModules
// private double m_longitude = 0;
// private double m_latitude = 0;
// Configurable defaults Defaults close to SL
private string d_mode = "SL";
private int d_frame_mod = 100; // Every 10 seconds (actually less)
private double d_day_length = 4; // A VW day is 4 RW hours long
private int d_year_length = 60; // There are 60 VW days in a VW year
@@ -134,12 +130,15 @@ namespace OpenSim.Region.CoreModules
private const int TICKS_PER_SECOND = 10000000;
private ulong m_CurrentTimeOffset = 0;
// Current time in elapsed seconds since Jan 1st 1970
private ulong CurrentTime
{
get
{
return (ulong)(((DateTime.Now.Ticks) - TicksToEpoch + TicksUTCOffset) / TICKS_PER_SECOND);
ulong ctime = (ulong)(((DateTime.Now.Ticks) - TicksToEpoch + TicksUTCOffset) / TICKS_PER_SECOND);
return ctime + m_CurrentTimeOffset;
}
}
@@ -262,10 +261,8 @@ namespace OpenSim.Region.CoreModules
private float GetCurrentTimeAsLindenSunHour()
{
if (m_SunFixed)
return m_SunFixedHour + 6;
return GetCurrentSunHour() + 6.0f;
float curtime = m_SunFixed ? m_SunFixedHour : GetCurrentSunHour();
return (curtime + 6.0f) % 24.0f;
}
#region INonSharedRegion Methods
@@ -290,8 +287,6 @@ namespace OpenSim.Region.CoreModules
// Just in case they don't have the stanzas
try
{
// Mode: determines how the sun is handled
m_RegionMode = config.Configs["Sun"].GetString("mode", d_mode);
// Mode: determines how the sun is handled
// m_latitude = config.Configs["Sun"].GetDouble("latitude", d_latitude);
// Mode: determines how the sun is handled
@@ -314,7 +309,6 @@ namespace OpenSim.Region.CoreModules
catch (Exception e)
{
m_log.Debug("[SUN]: Configuration access failed, using defaults. Reason: " + e.Message);
m_RegionMode = d_mode;
m_YearLengthDays = d_year_length;
m_DayLengthHours = d_day_length;
m_HorizonShift = d_day_night;
@@ -325,40 +319,28 @@ namespace OpenSim.Region.CoreModules
// m_longitude = d_longitude;
}
switch (m_RegionMode)
{
case "T1":
default:
case "SL":
// Time taken to complete a cycle (day and season)
SecondsPerSunCycle = (uint) (m_DayLengthHours * 60 * 60);
SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
SecondsPerSunCycle = (uint) (m_DayLengthHours * 60 * 60);
SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
// Ration of real-to-virtual time
// Ration of real-to-virtual time
// VWTimeRatio = 24/m_day_length;
// VWTimeRatio = 24/m_day_length;
// Speed of rotation needed to complete a cycle in the
// designated period (day and season)
// Speed of rotation needed to complete a cycle in the
// designated period (day and season)
SunSpeed = m_SunCycle/SecondsPerSunCycle;
SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
SunSpeed = m_SunCycle/SecondsPerSunCycle;
SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
// Horizon translation
// Horizon translation
HorizonShift = m_HorizonShift; // Z axis translation
// HoursToRadians = (SunCycle/24)*VWTimeRatio;
m_log.Debug("[SUN]: Mode is " + m_RegionMode);
m_log.Debug("[SUN]: Initialization completed. Day is " + SecondsPerSunCycle + " seconds, and year is " + m_YearLengthDays + " days");
m_log.Debug("[SUN]: Axis offset is " + m_HorizonShift);
m_log.Debug("[SUN]: Percentage of time for daylight " + m_DayTimeSunHourScale);
m_log.Debug("[SUN]: Positional data updated every " + m_UpdateInterval + " frames");
break;
}
HorizonShift = m_HorizonShift; // Z axis translation
// HoursToRadians = (SunCycle/24)*VWTimeRatio;
m_log.Debug("[SUN]: Initialization completed. Day is " + SecondsPerSunCycle + " seconds, and year is " + m_YearLengthDays + " days");
m_log.Debug("[SUN]: Axis offset is " + m_HorizonShift);
m_log.Debug("[SUN]: Percentage of time for daylight " + m_DayTimeSunHourScale);
m_log.Debug("[SUN]: Positional data updated every " + m_UpdateInterval + " frames");
}
public Type ReplaceableInterface
@@ -385,7 +367,8 @@ namespace OpenSim.Region.CoreModules
string sunCommand = string.Format("sun {0}", kvp.Key);
m_scene.AddCommand("Regions", this, sunCommand, string.Format("{0} [<value>]", sunCommand), kvp.Value, "", HandleSunConsoleCommand);
}
m_scene.AddCommand("Regions", this, "sun help", "sun help", "list parameters that can be changed", "", HandleSunConsoleCommand);
m_scene.AddCommand("Regions", this, "sun list", "sun list", "list parameters that can be changed", "", HandleSunConsoleCommand);
ready = true;
}
@@ -419,23 +402,22 @@ namespace OpenSim.Region.CoreModules
public void SunToClient(IClientAPI client)
{
if (m_RegionMode != "T1")
if (ready)
{
if (ready)
if (m_SunFixed)
{
if (m_SunFixed)
{
// m_log.DebugFormat("[SUN]: SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ", m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
client.SendSunPos(Position, Velocity, PosTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
}
else
{
// m_log.DebugFormat("[SUN]: SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ", m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
client.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
}
// m_log.DebugFormat("[SUN]: Fixed SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ",
// m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
client.SendSunPos(Position, Velocity, PosTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
}
else
{
// m_log.DebugFormat("[SUN]: SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ",
// m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
client.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
}
}
}
}
public void SunUpdate()
{
@@ -532,6 +514,9 @@ namespace OpenSim.Region.CoreModules
case "update_interval":
return m_UpdateInterval;
case "current_time":
return GetCurrentTimeAsLindenSunHour();
default:
throw new Exception("Unknown sun parameter.");
}
@@ -539,7 +524,51 @@ namespace OpenSim.Region.CoreModules
public void SetSunParameter(string param, double value)
{
HandleSunConsoleCommand("sun", new string[] {param, value.ToString() });
switch (param)
{
case "year_length":
m_YearLengthDays = (int)value;
SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
break;
case "day_length":
m_DayLengthHours = value;
SecondsPerSunCycle = (uint) (m_DayLengthHours * 60 * 60);
SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
SunSpeed = m_SunCycle/SecondsPerSunCycle;
SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
break;
case "day_night_offset":
m_HorizonShift = value;
HorizonShift = m_HorizonShift;
break;
case "day_time_sun_hour_scale":
m_DayTimeSunHourScale = value;
break;
case "update_interval":
m_UpdateInterval = (int)value;
break;
case "current_time":
value = (value + 18.0) % 24.0;
// set the current offset so that the effective sun time is the parameter
m_CurrentTimeOffset = 0; // clear this first so we use raw time
m_CurrentTimeOffset = (ulong)(SecondsPerSunCycle * value/ 24.0) - (CurrentTime % SecondsPerSunCycle);
break;
default:
throw new Exception("Unknown sun parameter.");
// Generate shared values
GenSunPos();
// When sun settings are updated, we should update all clients with new settings.
SunUpdateToAllClients();
}
}
public float GetCurrentSunHour()
@@ -572,7 +601,7 @@ namespace OpenSim.Region.CoreModules
foreach (string output in ParseCmdParams(cmdparams))
{
m_log.Info("[SUN] " + output);
MainConsole.Instance.Output(output);
}
}
@@ -581,10 +610,11 @@ namespace OpenSim.Region.CoreModules
Dictionary<string, string> Params = new Dictionary<string, string>();
Params.Add("year_length", "number of days to a year");
Params.Add("day_length", "number of seconds to a day");
Params.Add("day_length", "number of hours to a day");
Params.Add("day_night_offset", "induces a horizon shift");
Params.Add("update_interval", "how often to update the sun's position in frames");
Params.Add("day_time_sun_hour_scale", "scales day light vs nite hours to change day/night ratio");
Params.Add("current_time", "time in seconds of the simulator");
return Params;
}
@@ -618,46 +648,15 @@ namespace OpenSim.Region.CoreModules
}
else if (args.Length == 3)
{
float value = 0.0f;
if (!float.TryParse(args[2], out value))
double value = 0.0;
if (! double.TryParse(args[2], out value))
{
Output.Add(String.Format("The parameter value {0} is not a valid number.", args[2]));
return Output;
}
switch (args[1].ToLower())
{
case "year_length":
m_YearLengthDays = (int)value;
break;
case "day_length":
m_DayLengthHours = value;
break;
case "day_night_offset":
m_HorizonShift = value;
break;
case "day_time_sun_hour_scale":
m_DayTimeSunHourScale = value;
break;
case "update_interval":
m_UpdateInterval = (int)value;
break;
default:
Output.Add(String.Format("Unknown parameter {0}.", args[1]));
return Output;
}
SetSunParameter(args[1].ToLower(), value);
Output.Add(String.Format("Parameter {0} set to {1}.", args[1], value.ToString()));
// Generate shared values
GenSunPos();
// When sun settings are updated, we should update all clients with new settings.
SunUpdateToAllClients();
}
return Output;

View File

@@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Effects
for (y = 0; y < map.Height; y++)
{
map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10;
double spherFac = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2, Constants.RegionSize / 2, 50) * 0.01;
double spherFac = TerrainUtil.SphericalFactor(x, y, map.Width / 2, map.Height / 2, 50) * 0.01;
if (map[x, y] < spherFac)
{
map[x, y] = spherFac;

View File

@@ -67,7 +67,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
using (Bitmap bitmap = new Bitmap(filename))
{
ITerrainChannel retval = new TerrainChannel(true);
ITerrainChannel retval = new TerrainChannel(w, h);
for (int x = 0; x < retval.Width; x++)
{

View File

@@ -25,7 +25,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
@@ -116,7 +119,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
public ITerrainChannel LoadStream(Stream s)
{
TerrainChannel retval = new TerrainChannel();
// The raw format doesn't contain any dimension information.
// Guess the square dimensions by using the length of the raw file.
double dimension = Math.Sqrt((double)(s.Length / 4));
// Regions are always multiples of 256.
int trimmedDimension = (int)dimension - ((int)dimension % (int)Constants.RegionSize);
if (trimmedDimension < Constants.RegionSize)
trimmedDimension = (int)Constants.RegionSize;
TerrainChannel retval = new TerrainChannel(trimmedDimension, trimmedDimension);
BinaryReader bs = new BinaryReader(s);
int y;

View File

@@ -45,7 +45,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes
{
if (fillArea[x, y])
{
double noise = TerrainUtil.PerlinNoise2D((double) x / Constants.RegionSize, (double) y / Constants.RegionSize, 8, 1.0);
double noise = TerrainUtil.PerlinNoise2D((double) x / map.Width, (double) y / map.Height, 8, 1.0);
map[x, y] += noise * strength;
}

View File

@@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes
z *= z;
z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry));
double noise = TerrainUtil.PerlinNoise2D(x / (double) Constants.RegionSize, y / (double) Constants.RegionSize, 8, 1.0);
double noise = TerrainUtil.PerlinNoise2D(x / (double) map.Width, y / (double) map.Height, 8, 1.0);
if (z > 0.0)
map[x, y] += noise * z * duration;

View File

@@ -30,10 +30,14 @@ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Net;
using log4net;
using Nini.Config;
using OpenMetaverse;
using Mono.Addins;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
using OpenSim.Region.CoreModules.World.Terrain.FileLoaders;
@@ -70,6 +74,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
#endregion
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[TERRAIN MODULE]";
private readonly Commander m_commander = new Commander("terrain");
@@ -130,15 +135,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (m_scene.Heightmap == null)
{
m_channel = new TerrainChannel(m_InitialTerrain);
m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX,
(int)m_scene.RegionInfo.RegionSizeY,
(int)m_scene.RegionInfo.RegionSizeZ);
m_scene.Heightmap = m_channel;
m_revert = new TerrainChannel();
UpdateRevertMap();
}
else
{
m_channel = m_scene.Heightmap;
m_revert = new TerrainChannel();
UpdateRevertMap();
}
@@ -230,11 +235,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
try
{
ITerrainChannel channel = loader.Value.LoadFile(filename);
if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize)
if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY)
{
// TerrainChannel expects a RegionSize x RegionSize map, currently
throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}",
Constants.RegionSize, Constants.RegionSize));
m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY));
}
m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
m_scene.Heightmap = channel;
@@ -309,12 +314,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
LoadFromStream(filename, URIFetch(pathToTerrainHeightmap));
}
public void LoadFromStream(string filename, Stream stream)
{
LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream);
}
/// <summary>
/// Loads a terrain file from a stream and installs it in the scene.
/// </summary>
/// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
/// <param name="stream"></param>
public void LoadFromStream(string filename, Stream stream)
public void LoadFromStream(string filename, Vector3 displacement,
float radianRotation, Vector2 rotationDisplacement, Stream stream)
{
foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
{
@@ -325,8 +336,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
try
{
ITerrainChannel channel = loader.Value.LoadStream(stream);
m_scene.Heightmap = channel;
m_channel = channel;
m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement);
UpdateRevertMap();
}
catch (NotImplementedException)
@@ -532,6 +542,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// </summary>
public void UpdateRevertMap()
{
/*
int x;
for (x = 0; x < m_channel.Width; x++)
{
@@ -541,6 +552,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
m_revert[x, y] = m_channel[x, y];
}
}
*/
m_revert = m_channel.MakeCopy();
}
/// <summary>
@@ -567,8 +580,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY,
fileWidth, fileHeight,
(int) Constants.RegionSize,
(int) Constants.RegionSize);
(int) m_scene.RegionInfo.RegionSizeX,
(int) m_scene.RegionInfo.RegionSizeY);
m_scene.Heightmap = channel;
m_channel = channel;
UpdateRevertMap();
@@ -615,8 +628,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
loader.Value.SaveFile(m_channel, filename, offsetX, offsetY,
fileWidth, fileHeight,
(int)Constants.RegionSize,
(int)Constants.RegionSize);
(int)m_scene.RegionInfo.RegionSizeX,
(int)m_scene.RegionInfo.RegionSizeY);
MainConsole.Instance.OutputFormat(
"Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}",
@@ -705,7 +718,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void CheckForTerrainUpdates(bool respectEstateSettings)
{
bool shouldTaint = false;
float[] serialised = m_channel.GetFloatsSerialised();
float[] terrHeights = m_channel.GetFloatsSerialised();
int x;
for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize)
{
@@ -714,16 +727,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (m_channel.Tainted(x, y))
{
// if we should respect the estate settings then
// fixup and height deltas that don't respect them
// If we should respect the estate settings then
// fixup and height deltas that don't respect them.
// Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values.
if (respectEstateSettings && LimitChannelChanges(x, y))
{
// this has been vetoed, so update
// what we are going to send to the client
serialised = m_channel.GetFloatsSerialised();
// Terrain heights were modified. Refetch the terrain info.
terrHeights = m_channel.GetFloatsSerialised();
}
SendToClients(serialised, x, y);
// m_log.DebugFormat("{0} Patch modified. Sending (x,y) = ({1},{2})", LogHeader, x, y);
SendToClients(terrHeights, x, y);
shouldTaint = true;
}
}
@@ -792,13 +806,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
/// <param name="x">The patch corner to send</param>
/// <param name="y">The patch corner to send</param>
private void SendToClients(float[] serialised, int x, int y)
private void SendToClients(float[] heightMap, int x, int y)
{
m_scene.ForEachClient(
delegate(IClientAPI controller)
{ controller.SendLayerData(
x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised);
}
{ controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); }
);
}
@@ -984,28 +996,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain
if (direction.ToLower().StartsWith("y"))
{
for (int x = 0; x < Constants.RegionSize; x++)
for (int x = 0; x < m_channel.Width; x++)
{
for (int y = 0; y < Constants.RegionSize / 2; y++)
for (int y = 0; y < m_channel.Height / 2; y++)
{
double height = m_channel[x, y];
double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y];
double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y];
m_channel[x, y] = flippedHeight;
m_channel[x, (int)Constants.RegionSize - 1 - y] = height;
m_channel[x, (int)m_channel.Height - 1 - y] = height;
}
}
}
else if (direction.ToLower().StartsWith("x"))
{
for (int y = 0; y < Constants.RegionSize; y++)
for (int y = 0; y < m_channel.Height; y++)
{
for (int x = 0; x < Constants.RegionSize / 2; x++)
for (int x = 0; x < m_channel.Width / 2; x++)
{
double height = m_channel[x, y];
double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y];
double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y];
m_channel[x, y] = flippedHeight;
m_channel[(int)Constants.RegionSize - 1 - x, y] = height;
m_channel[(int)m_channel.Width - 1 - x, y] = height;
}
}

View File

@@ -40,10 +40,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests
[Test]
public void BrushTest()
{
int midRegion = (int)Constants.RegionSize / 2;
// Create a mask that covers only the left half of the region
bool[,] allowMask = new bool[(int)Constants.RegionSize, 256];
int x;
int y;
for (x = 0; x < (int)((int)Constants.RegionSize * 0.5f); x++)
for (x = 0; x < midRegion; x++)
{
for (y = 0; y < (int)Constants.RegionSize; y++)
{
@@ -57,13 +60,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests
TerrainChannel map = new TerrainChannel((int)Constants.RegionSize, (int)Constants.RegionSize);
ITerrainPaintableEffect effect = new RaiseSphere();
effect.PaintEffect(map, allowMask, (int)Constants.RegionSize * 0.5f, (int)Constants.RegionSize * 0.5f, -1.0, 2, 0.1);
Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (127,128).");
Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (124,128).");
Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (123,128).");
Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (128,128).");
Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (0,128).");
effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0);
Assert.That(map[127, midRegion] > 0.0, "Raise brush should raising value at this point (127,128).");
Assert.That(map[125, midRegion] > 0.0, "Raise brush should raising value at this point (124,128).");
Assert.That(map[120, midRegion] == 0.0, "Raise brush should not change value at this point (120,128).");
Assert.That(map[128, midRegion] == 0.0, "Raise brush should not change value at this point (128,128).");
Assert.That(map[0, midRegion] == 0.0, "Raise brush should not change value at this point (0,128).");
//
// Test LowerSphere
//
@@ -77,13 +79,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests
}
effect = new LowerSphere();
effect.PaintEffect(map, allowMask, ((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), -1.0, 2, 6.0);
Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128).");
Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128).");
Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] < 1.0, "Lower brush should lowering value at this point (124,128).");
Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (123,128).");
Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (128,128).");
Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (0,128).");
effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0);
Assert.That(map[127, midRegion] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128).");
Assert.That(map[127, midRegion] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128).");
Assert.That(map[125, midRegion] < 1.0, "Lower brush should lowering value at this point (124,128).");
Assert.That(map[120, midRegion] == 1.0, "Lower brush should not change value at this point (120,128).");
Assert.That(map[128, midRegion] == 1.0, "Lower brush should not change value at this point (128,128).");
Assert.That(map[0, midRegion] == 1.0, "Lower brush should not change value at this point (0,128).");
}
[Test]
@@ -100,10 +102,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests
x[0, 0] -= 1.0;
Assert.That(x[0, 0] == 4.0, "Terrain addition/subtraction error.");
x[0, 0] = Math.PI;
double[,] doublesExport = x.GetDoubles();
Assert.That(doublesExport[0, 0] == Math.PI, "Export to double[,] array not working correctly.");
x[0, 0] = 1.0;
float[] floatsExport = x.GetFloatsSerialised();
Assert.That(floatsExport[0] == 1.0f, "Export to float[] not working correctly.");

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
@@ -32,6 +32,7 @@ using System.Drawing.Imaging;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
namespace OpenSim.Region.CoreModules.World.Warp3DMap
@@ -66,261 +67,271 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
#endregion Constants
private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static string LogHeader = "[WARP3D TERRAIN SPLAT]";
/// <summary>
/// Builds a composited terrain texture given the region texture
/// and heightmap settings
/// </summary>
/// <param name="heightmap">Terrain heightmap</param>
/// <param name="terrain">Terrain heightmap</param>
/// <param name="regionInfo">Region information including terrain texture parameters</param>
/// <returns>A composited 256x256 RGB texture ready for rendering</returns>
/// <returns>A 256x256 square RGB texture ready for rendering</returns>
/// <remarks>Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting
/// Note we create a 256x256 dimension texture even if the actual terrain is larger.
/// </remarks>
public static Bitmap Splat(float[] heightmap, UUID[] textureIDs, float[] startHeights, float[] heightRanges, Vector3d regionPosition, IAssetService assetService, bool textureTerrain)
public static Bitmap Splat(ITerrainChannel terrain,
UUID[] textureIDs, float[] startHeights, float[] heightRanges,
Vector3d regionPosition, IAssetService assetService, bool textureTerrain)
{
Debug.Assert(heightmap.Length == 256 * 256);
Debug.Assert(textureIDs.Length == 4);
Debug.Assert(startHeights.Length == 4);
Debug.Assert(heightRanges.Length == 4);
Bitmap[] detailTexture = new Bitmap[4];
Bitmap output = null;
BitmapData outputData = null;
try
if (textureTerrain)
{
if (textureTerrain)
// Swap empty terrain textureIDs with default IDs
for (int i = 0; i < textureIDs.Length; i++)
{
// Swap empty terrain textureIDs with default IDs
for (int i = 0; i < textureIDs.Length; i++)
if (textureIDs[i] == UUID.Zero)
textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
}
#region Texture Fetching
if (assetService != null)
{
for (int i = 0; i < 4; i++)
{
if (textureIDs[i] == UUID.Zero)
textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
}
#region Texture Fetching
if (assetService != null)
{
for (int i = 0; i < 4; i++)
AssetBase asset;
UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
// Try to fetch a cached copy of the decoded/resized version of this texture
asset = assetService.GetCached(cacheID.ToString());
if (asset != null)
{
AssetBase asset;
UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
// Try to fetch a cached copy of the decoded/resized version of this texture
asset = assetService.GetCached(cacheID.ToString());
try
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
detailTexture[i] = (Bitmap)Image.FromStream(stream);
}
catch (Exception ex)
{
m_log.Warn("Failed to decode cached terrain texture " + cacheID +
" (textureID: " + textureIDs[i] + "): " + ex.Message);
}
}
if (detailTexture[i] == null)
{
// Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
asset = assetService.Get(textureIDs[i].ToString());
if (asset != null)
{
// m_log.DebugFormat(
// "[TERRAIN SPLAT]: Got asset service cached terrain texture {0} {1}", i, asset.ID);
try
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
detailTexture[i] = (Bitmap)Image.FromStream(stream);
}
catch (Exception ex)
{
m_log.Warn("Failed to decode cached terrain texture " + cacheID +
" (textureID: " + textureIDs[i] + "): " + ex.Message);
}
}
if (detailTexture[i] == null)
{
// Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
asset = assetService.Get(textureIDs[i].ToString());
if (asset != null)
{
// m_log.DebugFormat(
// "[TERRAIN SPLAT]: Got cached original JPEG2000 terrain texture {0} {1}", i, asset.ID);
try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
catch (Exception ex)
try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
catch (Exception ex)
{
m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
}
}
if (detailTexture[i] != null)
{
// Make sure this texture is the correct size, otherwise resize
if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
{
using (Bitmap origBitmap = detailTexture[i])
{
m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
detailTexture[i] = ImageUtils.ResizeImage(origBitmap, 256, 256);
}
}
if (detailTexture[i] != null)
{
// Make sure this texture is the correct size, otherwise resize
if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
{
using (Bitmap origBitmap = detailTexture[i])
{
detailTexture[i] = ImageUtils.ResizeImage(origBitmap, 256, 256);
}
}
// Save the decoded and resized texture to the cache
byte[] data;
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
detailTexture[i].Save(stream, ImageFormat.Png);
data = stream.ToArray();
}
// Cache a PNG copy of this terrain texture
AssetBase newAsset = new AssetBase
{
Data = data,
Description = "PNG",
Flags = AssetFlags.Collectable,
FullID = cacheID,
ID = cacheID.ToString(),
Local = true,
Name = String.Empty,
Temporary = true,
Type = (sbyte)AssetType.Unknown
};
newAsset.Metadata.ContentType = "image/png";
assetService.Store(newAsset);
// Save the decoded and resized texture to the cache
byte[] data;
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
detailTexture[i].Save(stream, ImageFormat.Png);
data = stream.ToArray();
}
// Cache a PNG copy of this terrain texture
AssetBase newAsset = new AssetBase
{
Data = data,
Description = "PNG",
Flags = AssetFlags.Collectable,
FullID = cacheID,
ID = cacheID.ToString(),
Local = true,
Name = String.Empty,
Temporary = true,
Type = (sbyte)AssetType.Unknown
};
newAsset.Metadata.ContentType = "image/png";
assetService.Store(newAsset);
}
}
}
#endregion Texture Fetching
}
// Fill in any missing textures with a solid color
for (int i = 0; i < 4; i++)
{
if (detailTexture[i] == null)
{
// m_log.DebugFormat(
// "[TERRAIN SPLAT]: Generating solid colour for missing texture {0}", i);
// Create a solid color texture for this layer
detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
{
using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
gfx.FillRectangle(brush, 0, 0, 256, 256);
}
#endregion Texture Fetching
}
// Fill in any missing textures with a solid color
for (int i = 0; i < 4; i++)
{
if (detailTexture[i] == null)
{
m_log.DebugFormat("{0} Missing terrain texture for layer {1}. Filling with solid default color",
LogHeader, i);
// Create a solid color texture for this layer
detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
{
using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
gfx.FillRectangle(brush, 0, 0, 256, 256);
}
}
#region Layer Map
float[] layermap = new float[256 * 256];
else
{
if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
{
detailTexture[i] = ResizeBitmap(detailTexture[i], 256, 256);
}
}
}
#region Layer Map
float[,] layermap = new float[256, 256];
// Scale difference between actual region size and the 256 texture being created
int xFactor = terrain.Width / 256;
int yFactor = terrain.Height / 256;
// Create 'layermap' where each value is the fractional layer number to place
// at that point. For instance, a value of 1.345 gives the blending of
// layer 1 and layer 2 for that point.
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float height = (float)terrain[x * xFactor, y * yFactor];
float pctX = (float)x / 255f;
float pctY = (float)y / 255f;
// Use bilinear interpolation between the four corners of start height and
// height range to select the current values at this position
float startHeight = ImageUtils.Bilinear(
startHeights[0],
startHeights[2],
startHeights[1],
startHeights[3],
pctX, pctY);
startHeight = Utils.Clamp(startHeight, 0f, 255f);
float heightRange = ImageUtils.Bilinear(
heightRanges[0],
heightRanges[2],
heightRanges[1],
heightRanges[3],
pctX, pctY);
heightRange = Utils.Clamp(heightRange, 0f, 255f);
// Generate two frequencies of perlin noise based on our global position
// The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
Vector3 vec = new Vector3
(
((float)regionPosition.X + (x * xFactor)) * 0.20319f,
((float)regionPosition.Y + (y * yFactor)) * 0.20319f,
height * 0.25f
);
float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
float noise = (lowFreq + highFreq) * 2f;
// Combine the current height, generated noise, start height, and height range parameters, then scale all of it
float layer = ((height + noise - startHeight) / heightRange) * 4f;
if (Single.IsNaN(layer))
layer = 0f;
layermap[x, y] = Utils.Clamp(layer, 0f, 3f);
}
}
#endregion Layer Map
#region Texture Compositing
Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
// Unsafe work as we lock down the source textures for quicker access and access the
// pixel data directly
unsafe
{
// Get handles to all of the texture data arrays
BitmapData[] datas = new BitmapData[]
{
detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
};
// Compute size of each pixel data (used to address into the pixel data array)
int[] comps = new int[]
{
(datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
};
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float height = heightmap[y * 256 + x];
float pctX = (float)x / 255f;
float pctY = (float)y / 255f;
// Use bilinear interpolation between the four corners of start height and
// height range to select the current values at this position
float startHeight = ImageUtils.Bilinear(
startHeights[0],
startHeights[2],
startHeights[1],
startHeights[3],
pctX, pctY);
startHeight = Utils.Clamp(startHeight, 0f, 255f);
float heightRange = ImageUtils.Bilinear(
heightRanges[0],
heightRanges[2],
heightRanges[1],
heightRanges[3],
pctX, pctY);
heightRange = Utils.Clamp(heightRange, 0f, 255f);
// Generate two frequencies of perlin noise based on our global position
// The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
Vector3 vec = new Vector3
(
((float)regionPosition.X + x) * 0.20319f,
((float)regionPosition.Y + y) * 0.20319f,
height * 0.25f
);
float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
float noise = (lowFreq + highFreq) * 2f;
// Combine the current height, generated noise, start height, and height range parameters, then scale all of it
float layer = ((height + noise - startHeight) / heightRange) * 4f;
if (Single.IsNaN(layer)) layer = 0f;
layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
float layer = layermap[x, y];
// Select two textures
int l0 = (int)Math.Floor(layer);
int l1 = Math.Min(l0 + 1, 3);
byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
float aB = *(ptrA + 0);
float aG = *(ptrA + 1);
float aR = *(ptrA + 2);
float bB = *(ptrB + 0);
float bG = *(ptrB + 1);
float bR = *(ptrB + 2);
float layerDiff = layer - l0;
// Interpolate between the two selected textures
*(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
*(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
*(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
}
}
#endregion Layer Map
#region Texture Compositing
output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
// Get handles to all of the texture data arrays
BitmapData[] datas = new BitmapData[]
{
detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
};
int[] comps = new int[]
{
(datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
};
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float layer = layermap[y * 256 + x];
// Select two textures
int l0 = (int)Math.Floor(layer);
int l1 = Math.Min(l0 + 1, 3);
byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
float aB = *(ptrA + 0);
float aG = *(ptrA + 1);
float aR = *(ptrA + 2);
float bB = *(ptrB + 0);
float bG = *(ptrB + 1);
float bR = *(ptrB + 2);
float layerDiff = layer - l0;
// Interpolate between the two selected textures
*(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
*(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
*(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
}
}
for (int i = 0; i < 4; i++)
detailTexture[i].UnlockBits(datas[i]);
}
}
finally
{
for (int i = 0; i < 4; i++)
if (detailTexture[i] != null)
detailTexture[i].Dispose();
for (int i = 0; i < detailTexture.Length; i++)
detailTexture[i].UnlockBits(datas[i]);
}
for (int i = 0; i < detailTexture.Length; i++)
if (detailTexture[i] != null)
detailTexture[i].Dispose();
output.UnlockBits(outputData);
// We generated the texture upside down, so flip it
@@ -331,6 +342,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
return output;
}
public static Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
m_log.DebugFormat("{0} ResizeBitmap. From <{1},{2}> to <{3},{4}>",
LogHeader, b.Width, b.Height, nWidth, nHeight);
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage(result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
b.Dispose();
return result;
}
public static Bitmap SplatSimple(float[] heightmap)
{
const float BASE_HSV_H = 93f / 360f;

View File

@@ -31,21 +31,25 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using CSJ2K;
using Nini.Config;
using log4net;
using Rednettle.Warp3D;
using Mono.Addins;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Rendering;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Physics.Manager;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
using OpenMetaverse.Assets;
using OpenMetaverse.Imaging;
using OpenMetaverse.Rendering;
using OpenMetaverse.StructuredData;
using WarpRenderer = global::Warp3D.Warp3D;
namespace OpenSim.Region.CoreModules.World.Warp3DMap
@@ -57,12 +61,20 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[WARP 3D IMAGE MODULE]";
private Scene m_scene;
private IRendering m_primMesher;
private IConfigSource m_config;
private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>();
private bool m_useAntiAliasing = false; // TODO: Make this a config option
private IConfigSource m_config;
private bool m_drawPrimVolume = true; // true if should render the prims on the tile
private bool m_textureTerrain = true; // true if to create terrain splatting texture
private bool m_texturePrims = true; // true if should texture the rendered prims
private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
private bool m_useAntiAliasing = false; // true if to anti-alias the rendered image
private bool m_Enabled = false;
#region Region Module interface
@@ -71,11 +83,27 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
{
m_config = source;
string[] configSections = new string[] { "Map", "Startup" };
if (Util.GetConfigVarFromSections<string>(
m_config, "MapImageModule", new string[] { "Startup", "Map" }, "MapImageModule") != "Warp3DImageModule")
m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DImageModule")
return;
m_Enabled = true;
m_drawPrimVolume
= Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
m_textureTerrain
= Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
m_texturePrims
= Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
m_texturePrimSize
= Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
m_renderMeshes
= Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
m_useAntiAliasing
= Util.GetConfigVarFromSections<bool>(m_config, "UseAntiAliasing", configSections, m_useAntiAliasing);
}
public void AddRegion(Scene scene)
@@ -127,29 +155,28 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
public Bitmap CreateMapTile()
{
Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f);
Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, (int)Constants.RegionSize, (int)Constants.RegionSize, (float)Constants.RegionSize, (float)Constants.RegionSize);
// Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f);
// Camera above the middle of the region
Vector3 camPos = new Vector3(
m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
m_scene.RegionInfo.RegionSizeY/2 - 0.5f,
221.7025033688163f);
// Viewport viewing down onto the region
Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f,
(int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY,
(float)m_scene.RegionInfo.RegionSizeX, (float)m_scene.RegionInfo.RegionSizeY );
// Fill the viewport and return the image
return CreateMapTile(viewport, false);
}
public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures)
{
Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height);
Viewport viewport = new Viewport(camPos, camDir, fov, Constants.RegionSize, 0.1f, width, height);
return CreateMapTile(viewport, useTextures);
}
public Bitmap CreateMapTile(Viewport viewport, bool useTextures)
{
bool drawPrimVolume = true;
bool textureTerrain = true;
string[] configSections = new string[] { "Map", "Startup" };
drawPrimVolume
= Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, drawPrimVolume);
textureTerrain
= Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, textureTerrain);
m_colors.Clear();
int width = viewport.Width;
@@ -193,8 +220,8 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40));
CreateWater(renderer);
CreateTerrain(renderer, textureTerrain);
if (drawPrimVolume)
CreateTerrain(renderer, m_textureTerrain);
if (m_drawPrimVolume)
CreateAllPrims(renderer, useTextures);
renderer.Render();
@@ -210,6 +237,16 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
// afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
// then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
// this map tile simply takes a lot of memory.
foreach (var o in renderer.Scene.objectData.Values)
{
warp_Object obj = (warp_Object)o;
obj.vertexData = null;
obj.triangleData = null;
}
renderer.Scene.removeAllObjects();
renderer = null;
viewport = null;
m_colors.Clear();
GC.Collect();
m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");
@@ -236,61 +273,74 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
#region Rendering Methods
// Add a water plane to the renderer.
private void CreateWater(WarpRenderer renderer)
{
float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
renderer.AddPlane("Water", 256f * 0.5f);
renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f);
renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
waterHeight,
m_scene.RegionInfo.RegionSizeY/2 - 0.5f );
renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR));
renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif
renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f));
warp_Material waterColorMaterial = new warp_Material(ConvertColor(WATER_COLOR));
waterColorMaterial.setReflectivity(0); // match water color with standard map module thanks lkalif
waterColorMaterial.setTransparency((byte)((1f - WATER_COLOR.A) * 255f));
renderer.Scene.addMaterial("WaterColor", waterColorMaterial);
renderer.SetObjectMaterial("Water", "WaterColor");
}
// Add a terrain to the renderer.
// Note that we create a 'low resolution' 256x256 vertex terrain rather than trying for
// full resolution. This saves a lot of memory especially for very large regions.
private void CreateTerrain(WarpRenderer renderer, bool textureTerrain)
{
ITerrainChannel terrain = m_scene.Heightmap;
float[] heightmap = terrain.GetFloatsSerialised();
// 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
float diff = (float)m_scene.RegionInfo.RegionSizeX / 256f;
warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2);
for (int y = 0; y < 256; y++)
// Create all the vertices for the terrain
for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
{
for (int x = 0; x < 256; x++)
for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
{
int v = y * 256 + x;
float height = heightmap[v];
warp_Vector pos = ConvertVector(new Vector3(x, y, height));
obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f));
warp_Vector pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
obj.addVertex(new warp_Vertex(pos,
x / (float)m_scene.RegionInfo.RegionSizeX,
(((float)m_scene.RegionInfo.RegionSizeY) - y) / m_scene.RegionInfo.RegionSizeY) );
}
}
for (int y = 0; y < 256; y++)
// Now that we have all the vertices, make another pass and create
// the normals for each of the surface triangles and
// create the list of triangle indices.
for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
{
for (int x = 0; x < 256; x++)
for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
{
if (x < 255 && y < 255)
float newX = x / diff;
float newY = y / diff;
if (newX < 255 && newY < 255)
{
int v = y * 256 + x;
int v = (int)newY * 256 + (int)newX;
// Normal
Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]);
Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]);
Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]);
// Normal for a triangle made up of three adjacent vertices
Vector3 v1 = new Vector3(newX, newY, (float)terrain[(int)x, (int)y]);
Vector3 v2 = new Vector3(newX + 1, newY, (float)terrain[(int)(x + 1), (int)y]);
Vector3 v3 = new Vector3(newX, newY + 1, (float)terrain[(int)x, ((int)(y + 1))]);
warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3));
norm = norm.reverse();
obj.vertex(v).n = norm;
// Triangle 1
// Make two triangles for each of the squares in the grid of vertices
obj.addTriangle(
v,
v + 1,
v + 256);
// Triangle 2
obj.addTriangle(
v + 256 + 1,
v + 256,
@@ -305,7 +355,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
float[] startHeights = new float[4];
float[] heightRanges = new float[4];
RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
textureIDs[0] = regionInfo.TerrainTexture1;
textureIDs[1] = regionInfo.TerrainTexture2;
@@ -323,14 +373,12 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
heightRanges[3] = (float)regionInfo.Elevation2NE;
uint globalX, globalY;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
warp_Texture texture;
using (
Bitmap image
= TerrainSplat.Splat(
heightmap, textureIDs, startHeights, heightRanges,
= TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
{
texture = new warp_Texture(image);
@@ -368,8 +416,48 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE)
return;
FacetedMesh renderMesh = null;
Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium);
if (m_renderMeshes)
{
if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
{
// Try fetchinng the asset
byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
if (sculptAsset != null)
{
// Is it a mesh?
if (omvPrim.Sculpt.Type == SculptType.Mesh)
{
AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out renderMesh);
meshAsset = null;
}
else // It's sculptie
{
IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
if (imgDecoder != null)
{
Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
if (sculpt != null)
{
renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt,
DetailLevel.Medium);
sculpt.Dispose();
}
}
}
}
}
}
// If not a mesh or sculptie, try the regular mesher
if (renderMesh == null)
{
renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium);
}
if (renderMesh == null)
return;
@@ -428,7 +516,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
Color4 faceColor = GetFaceColor(teFace);
string materialName = GetOrCreateMaterial(renderer, faceColor);
string materialName = String.Empty;
if (m_texturePrims && prim.Scale.LengthSquared() > m_texturePrimSize*m_texturePrimSize)
materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID);
else
materialName = GetOrCreateMaterial(renderer, faceColor);
faceObj.transform(m);
faceObj.setPos(primPos);
@@ -517,10 +609,51 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
return name;
}
public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID)
{
string materialName = "Color-" + faceColor.ToString() + "-Texture-" + textureID.ToString();
if (renderer.Scene.material(materialName) == null)
{
renderer.AddMaterial(materialName, ConvertColor(faceColor));
if (faceColor.A < 1f)
{
renderer.Scene.material(materialName).setTransparency((byte) ((1f - faceColor.A)*255f));
}
warp_Texture texture = GetTexture(textureID);
if (texture != null)
renderer.Scene.material(materialName).setTexture(texture);
}
return materialName;
}
private warp_Texture GetTexture(UUID id)
{
warp_Texture ret = null;
byte[] asset = m_scene.AssetService.GetData(id.ToString());
if (asset != null)
{
IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
Bitmap img = (Bitmap) imgDecoder.DecodeToImage(asset);
if (img != null)
{
return new warp_Texture(img);
}
}
return ret;
}
#endregion Rendering Methods
#region Static Helpers
// Note: axis change.
private static warp_Vector ConvertVector(float x, float y, float z)
{
return new warp_Vector(x, z, y);
}
private static warp_Vector ConvertVector(Vector3 vector)
{
return new warp_Vector(vector.X, vector.Z, vector.Y);

View File

@@ -184,8 +184,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
data.Name = info.RegionName;
data.RegionFlags = 0; // TODO not used?
data.WaterHeight = 0; // not used
data.X = (ushort)(info.RegionLocX / Constants.RegionSize);
data.Y = (ushort)(info.RegionLocY / Constants.RegionSize);
data.X = (ushort)Util.WorldToRegionLoc((uint)info.RegionLocX);
data.Y = (ushort)Util.WorldToRegionLoc((uint)info.RegionLocY);
blocks.Add(data);
}
}
@@ -214,7 +214,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
// final block, closing the search result
MapBlockData data = new MapBlockData();
data.Agents = 0;
data.Access = 255;
data.Access = (byte)SimAccess.NonExistent;
data.MapImageId = UUID.Zero;
data.Name = "";
data.RegionFlags = 0;

View File

@@ -59,8 +59,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WorldMapModule")]
public class WorldMapModule : INonSharedRegionModule, IWorldMapModule
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[WORLD MAP]";
private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg";
private static readonly UUID STOP_UUID = UUID.Random();
@@ -282,15 +282,15 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
{
List<MapBlockData> mapBlocks = new List<MapBlockData>(); ;
// Get regions that are within 8 regions of here
List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
(int)(m_scene.RegionInfo.RegionLocX - 8) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocX + 8) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocY - 8) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocY + 8) * (int)Constants.RegionSize);
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 8),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 8),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 8),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 8) );
foreach (GridRegion r in regions)
{
MapBlockData block = new MapBlockData();
MapBlockFromGridRegion(block, r, 0);
MapBlockData block = MapBlockFromGridRegion(r, 0);
mapBlocks.Add(block);
}
avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
@@ -410,24 +410,23 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
}
uint xstart = 0;
uint ystart = 0;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
if (itemtype == 6) // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots)
Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
if (itemtype == (int)GridItemType.AgentLocations)
{
if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
{
// Local Map Item Request
// Just requesting map info about the current, local region
int tc = Environment.TickCount;
List<mapItemReply> mapitems = new List<mapItemReply>();
mapItemReply mapitem = new mapItemReply();
if (m_scene.GetRootAgentCount() <= 1)
{
mapitem = new mapItemReply();
mapitem.x = (uint)(xstart + 1);
mapitem.y = (uint)(ystart + 1);
mapitem.id = UUID.Zero;
mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString());
mapitem.Extra = 0;
mapitem.Extra2 = 0;
mapitem = new mapItemReply(
xstart + 1,
ystart + 1,
UUID.Zero,
Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
0, 0);
mapitems.Add(mapitem);
}
else
@@ -437,13 +436,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
// Don't send a green dot for yourself
if (sp.UUID != remoteClient.AgentId)
{
mapitem = new mapItemReply();
mapitem.x = (uint)(xstart + sp.AbsolutePosition.X);
mapitem.y = (uint)(ystart + sp.AbsolutePosition.Y);
mapitem.id = UUID.Zero;
mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString());
mapitem.Extra = 1;
mapitem.Extra2 = 0;
mapitem = new mapItemReply(
xstart + (uint)sp.AbsolutePosition.X,
ystart + (uint)sp.AbsolutePosition.Y,
UUID.Zero,
Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
1, 0);
mapitems.Add(mapitem);
}
});
@@ -458,7 +456,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
}
}
else if (itemtype == 7) // Service 7 (MAP_ITEM_LAND_FOR_SALE)
else if (itemtype == (int)GridItemType.LandForSale) // Service 7 (MAP_ITEM_LAND_FOR_SALE)
{
if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
{
@@ -488,14 +486,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
float x = (min.X+max.X)/2;
float y = (min.Y+max.Y)/2;
mapitem = new mapItemReply();
mapitem.x = (uint)(xstart + x);
mapitem.y = (uint)(ystart + y);
// mapitem.z = (uint)m_scene.GetGroundHeight(x,y);
mapitem.id = parcel.GlobalID;
mapitem.name = parcel.Name;
mapitem.Extra = parcel.Area;
mapitem.Extra2 = parcel.SalePrice;
mapitem = new mapItemReply(
xstart + (uint)x,
ystart + (uint)y,
parcel.GlobalID,
parcel.Name,
parcel.Area,
parcel.SalePrice
);
mapitems.Add(mapitem);
}
}
@@ -510,7 +508,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
}
}
else if (itemtype == 1) // Service 1 (MAP_ITEM_TELEHUB)
else if (itemtype == (int)GridItemType.Telehub) // Service 1 (MAP_ITEM_TELEHUB)
{
if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
{
@@ -520,13 +518,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
if (sog != null)
{
mapitem = new mapItemReply();
mapitem.x = (uint)(xstart + sog.AbsolutePosition.X);
mapitem.y = (uint)(ystart + sog.AbsolutePosition.Y);
mapitem.id = UUID.Zero;
mapitem.name = sog.Name;
mapitem.Extra = 0; // color (not used)
mapitem.Extra2 = 0; // 0 = telehub / 1 = infohub
mapitem = new mapItemReply(
xstart + (uint)sog.AbsolutePosition.X,
ystart + (uint)sog.AbsolutePosition.Y,
UUID.Zero,
sog.Name,
0, // color (not used)
0 // 0 = telehub / 1 = infohub
);
mapitems.Add(mapitem);
remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
@@ -676,19 +675,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
{
OSDMap mapitem = (OSDMap)itemarray[i];
mapItemReply mi = new mapItemReply();
mi.x = (uint)mapitem["X"].AsInteger();
mi.y = (uint)mapitem["Y"].AsInteger();
mi.id = mapitem["ID"].AsUUID();
mi.Extra = mapitem["Extra"].AsInteger();
mi.Extra2 = mapitem["Extra2"].AsInteger();
mi.name = mapitem["Name"].AsString();
mi.FromOSD(mapitem);
returnitems.Add(mi);
}
av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags);
}
// Service 7 (MAP_ITEM_LAND_FOR_SALE)
uint itemtype = 7;
uint itemtype = (uint)GridItemType.LandForSale;
if (response.ContainsKey(itemtype.ToString()))
{
@@ -698,19 +692,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
{
OSDMap mapitem = (OSDMap)itemarray[i];
mapItemReply mi = new mapItemReply();
mi.x = (uint)mapitem["X"].AsInteger();
mi.y = (uint)mapitem["Y"].AsInteger();
mi.id = mapitem["ID"].AsUUID();
mi.Extra = mapitem["Extra"].AsInteger();
mi.Extra2 = mapitem["Extra2"].AsInteger();
mi.name = mapitem["Name"].AsString();
mi.FromOSD(mapitem);
returnitems.Add(mi);
}
av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags);
}
// Service 1 (MAP_ITEM_TELEHUB)
itemtype = 1;
itemtype = (uint)GridItemType.Telehub;
if (response.ContainsKey(itemtype.ToString()))
{
@@ -720,12 +709,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
{
OSDMap mapitem = (OSDMap)itemarray[i];
mapItemReply mi = new mapItemReply();
mi.x = (uint)mapitem["X"].AsInteger();
mi.y = (uint)mapitem["Y"].AsInteger();
mi.id = mapitem["ID"].AsUUID();
mi.Extra = mapitem["Extra"].AsInteger();
mi.Extra2 = mapitem["Extra2"].AsInteger();
mi.name = mapitem["Name"].AsString();
mi.FromOSD(mapitem);
returnitems.Add(mi);
}
av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags);
@@ -808,7 +792,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (httpserver.Length == 0)
{
uint x = 0, y = 0;
Utils.LongToUInts(regionhandle, out x, out y);
Util.RegionHandleToWorldLoc(regionhandle, out x, out y);
GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y);
if (mreg != null)
@@ -915,7 +899,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
finally
{
if (os != null)
os.Close();
os.Dispose();
}
string response_mapItems_reply = null;
@@ -1007,7 +991,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
/// <param name="maxY"></param>
public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
{
//m_log.ErrorFormat("[YYY] RequestMapBlocks {0}={1}={2}={3} {4}", minX, minY, maxX, maxY, flag);
if ((flag & 0x10000) != 0) // user clicked on qthe map a tile that isn't visible
{
List<MapBlockData> response = new List<MapBlockData>();
@@ -1016,22 +999,24 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
// on an unloaded square.
// But make sure: Look whether the one we requested is in there
List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
minX * (int)Constants.RegionSize,
maxX * (int)Constants.RegionSize,
minY * (int)Constants.RegionSize,
maxY * (int)Constants.RegionSize);
(int)Util.RegionToWorldLoc((uint)minX), (int)Util.RegionToWorldLoc((uint)maxX),
(int)Util.RegionToWorldLoc((uint)minY), (int)Util.RegionToWorldLoc((uint)maxY) );
m_log.DebugFormat("[WORLD MAP MODULE] RequestMapBlocks min=<{0},{1}>, max=<{2},{3}>, flag={4}, cntFound={5}",
minX, minY, maxX, maxY, flag.ToString("X"), regions.Count);
if (regions != null)
{
foreach (GridRegion r in regions)
{
if ((r.RegionLocX == minX * (int)Constants.RegionSize) &&
(r.RegionLocY == minY * (int)Constants.RegionSize))
if (r.RegionLocX == Util.RegionToWorldLoc((uint)minX)
&& r.RegionLocY == Util.RegionToWorldLoc((uint)minY) )
{
// found it => add it to response
MapBlockData block = new MapBlockData();
MapBlockFromGridRegion(block, r, flag);
response.Add(block);
// Version 2 viewers can handle the larger regions
if ((flag & 2) == 2)
response.AddRange(Map2BlockFromGridRegion(r, flag));
else
response.Add(MapBlockFromGridRegion(r, flag));
break;
}
}
@@ -1043,7 +1028,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
MapBlockData block = new MapBlockData();
block.X = (ushort)minX;
block.Y = (ushort)minY;
block.Access = 254; // means 'simulator is offline'
block.Access = (byte)SimAccess.Down; // means 'simulator is offline'
// block.Access = (byte)SimAccess.NonExistant;
response.Add(block);
}
// The lower 16 bits are an unsigned int16
@@ -1060,41 +1046,112 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
{
List<MapBlockData> mapBlocks = new List<MapBlockData>();
List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
(minX - 4) * (int)Constants.RegionSize,
(maxX + 4) * (int)Constants.RegionSize,
(minY - 4) * (int)Constants.RegionSize,
(maxY + 4) * (int)Constants.RegionSize);
(int)Util.RegionToWorldLoc((uint)(minX - 4)), (int)Util.RegionToWorldLoc((uint)(maxX + 4)),
(int)Util.RegionToWorldLoc((uint)(minY - 4)), (int)Util.RegionToWorldLoc((uint)(maxY + 4)) );
m_log.DebugFormat("{0} GetAndSendBlocks. min=<{1},{2}>, max=<{3},{4}>, cntFound={5}",
LogHeader, minX, minY, maxX, maxY, regions.Count);
foreach (GridRegion r in regions)
{
MapBlockData block = new MapBlockData();
MapBlockFromGridRegion(block, r, flag);
mapBlocks.Add(block);
// Version 2 viewers can handle the larger regions
if ((flag & 2) == 2)
mapBlocks.AddRange(Map2BlockFromGridRegion(r, flag));
else
mapBlocks.Add(MapBlockFromGridRegion(r, flag));
}
remoteClient.SendMapBlock(mapBlocks, flag & 0xffff);
return mapBlocks;
}
protected void MapBlockFromGridRegion(MapBlockData block, GridRegion r, uint flag)
// Fill a passed MapBlockData from a GridRegion
protected MapBlockData MapBlockFromGridRegion(GridRegion r, uint flag)
{
MapBlockData block = new MapBlockData();
block.Access = r.Access;
switch (flag & 0xffff)
{
case 0:
block.MapImageId = r.TerrainImage;
break;
case 2:
block.MapImageId = r.ParcelImage;
break;
default:
block.MapImageId = UUID.Zero;
break;
case 0:
block.MapImageId = r.TerrainImage;
break;
case 2:
block.MapImageId = r.ParcelImage;
break;
default:
block.MapImageId = UUID.Zero;
break;
}
block.Name = r.RegionName;
block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
block.X = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocX);
block.Y = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocY);
block.SizeX = (ushort) r.RegionSizeX;
block.SizeY = (ushort) r.RegionSizeY;
return block;
}
protected List<MapBlockData> Map2BlockFromGridRegion(GridRegion r, uint flag)
{
List<MapBlockData> blocks = new List<MapBlockData>();
MapBlockData block = new MapBlockData();
if (r == null)
{
block.Access = (byte)SimAccess.Down;
block.MapImageId = UUID.Zero;
blocks.Add(block);
}
else
{
block.Access = r.Access;
switch (flag & 0xffff)
{
case 0:
block.MapImageId = r.TerrainImage;
break;
case 2:
block.MapImageId = r.ParcelImage;
break;
default:
block.MapImageId = UUID.Zero;
break;
}
block.Name = r.RegionName;
block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
block.SizeX = (ushort)r.RegionSizeX;
block.SizeY = (ushort)r.RegionSizeY;
blocks.Add(block);
// If these are larger than legacy regions, create fake map entries for the covered
// regions. The map system only does legacy sized regions so we have to fake map
// entries for all the covered regions.
if (r.RegionSizeX > Constants.RegionSize || r.RegionSizeY > Constants.RegionSize)
{
for (int x = 0; x < r.RegionSizeX / Constants.RegionSize; x++)
{
for (int y = 0; y < r.RegionSizeY / Constants.RegionSize; y++)
{
if (x == 0 && y == 0)
continue;
block = new MapBlockData
{
Access = r.Access,
MapImageId = r.TerrainImage,
Name = r.RegionName,
X = (ushort)((r.RegionLocX / Constants.RegionSize) + x),
Y = (ushort)((r.RegionLocY / Constants.RegionSize) + y),
SizeX = (ushort)r.RegionSizeX,
SizeY = (ushort)r.RegionSizeY
};
//Child piece, so ignore it
blocks.Add(block);
}
}
}
}
return blocks;
}
public Hashtable OnHTTPThrottled(Hashtable keysvals)
{
Hashtable reply = new Hashtable();
@@ -1223,17 +1280,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
List<MapBlockData> mapBlocks = new List<MapBlockData>();
List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
(int)(m_scene.RegionInfo.RegionLocX - 9) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocX + 9) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocY - 9) * (int)Constants.RegionSize,
(int)(m_scene.RegionInfo.RegionLocY + 9) * (int)Constants.RegionSize);
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 9),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 9),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 9),
(int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 9));
List<AssetBase> textures = new List<AssetBase>();
List<Image> bitImages = new List<Image>();
foreach (GridRegion r in regions)
{
MapBlockData mapBlock = new MapBlockData();
MapBlockFromGridRegion(mapBlock, r, 0);
MapBlockData mapBlock = MapBlockFromGridRegion(r, 0);
AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
if (texAsset != null)
@@ -1294,7 +1350,9 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
uint xstart = 0;
uint ystart = 0;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle,out xstart,out ystart);
Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle,out xstart,out ystart);
// m_log.DebugFormat("{0} HandleRemoteMapItemRequest. loc=<{1},{2}>",
// LogHeader, Util.WorldToRegionLoc(xstart), Util.WorldToRegionLoc(ystart));
// Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots)

View File

@@ -92,12 +92,12 @@ namespace OpenSim.Region.Framework.Interfaces
void EnableChildAgent(ScenePresence agent, GridRegion region);
GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, out uint xDest, out uint yDest, out string version, out Vector3 newpos);
GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, out string version,
out Vector3 newpos, out string reason);
void Cross(SceneObjectGroup sog, Vector3 position, bool silent);
ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, string version);
}
public interface IUserAgentVerificationModule

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
@@ -25,6 +25,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Drawing;
using OpenMetaverse;
using OpenMetaverse.Imaging;
@@ -53,5 +54,12 @@ namespace OpenSim.Region.Framework.Interfaces
/// <param name="components">number of components</param>
/// <returns>true if decode was successful. false otherwise.</returns>
bool Decode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out int components);
/// <summary>
/// Provides a synchronous decode direct to an image object
/// </summary>
/// <param name="j2kData"></param>
/// <returns>decoded image or 'null' of unsuccessful</returns>
Image DecodeToImage(byte[] j2kData);
}
}

View File

@@ -71,6 +71,32 @@ namespace OpenSim.Region.Framework.Interfaces
UUID owner, bool senseAsAgent, Scene scene,
AvatarAppearance appearance);
/// <summary>
/// Create an NPC with a user-supplied agentID
/// </summary>
/// <param name="firstname"></param>
/// <param name="lastname"></param>
/// <param name="position"></param>
/// <param name="agentID"></param>
/// The desired agent ID
/// <param name="owner"></param>
/// <param name="senseAsAgent">
/// Make the NPC show up as an agent on LSL sensors. The default is
/// that they show up as the NPC type instead, but this is currently
/// an OpenSim-only extension.
/// </param>
/// <param name="scene"></param>
/// <param name="appearance">
/// The avatar appearance to use for the new NPC.
/// </param>
/// <returns>
/// The UUID of the ScenePresence created. UUID.Zero if there was a
/// failure.
/// </returns>
UUID CreateNPC(string firstname, string lastname,
Vector3 position, UUID agentID, UUID owner, bool senseAsAgent, Scene scene,
AvatarAppearance appearance);
/// <summary>
/// Check if the agent is an NPC.
/// </summary>

View File

@@ -29,6 +29,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using OpenMetaverse;
namespace OpenSim.Region.Framework.Interfaces
{
/// <summary>
@@ -100,16 +102,11 @@ namespace OpenSim.Region.Framework.Interfaces
/// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event.
///
/// <param name="loadPath"></param>
/// <param name="merge">
/// If true, the loaded region merges with the existing one rather than replacing it. Any terrain or region
/// settings in the archive will be ignored.
/// </param>
/// <param name="skipAssets">
/// If true, the archive is loaded without loading any assets contained within it. This is useful if the
/// assets are already known to be present in the grid's asset service.
/// </param>
/// <param name="requestId">If supplied, this request Id is later returned in the saved event</param>
void DearchiveRegion(string loadPath, bool merge, bool skipAssets, Guid requestId);
/// <param name="options">
/// Dictionary of options.
/// </param>
void DearchiveRegion(string loadPath, Guid requestId, Dictionary<string,object> options);
/// <summary>
/// Dearchive a region from a stream. This replaces the existing scene.
@@ -127,15 +124,10 @@ namespace OpenSim.Region.Framework.Interfaces
/// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event.
///
/// <param name="loadStream"></param>
/// <param name="merge">
/// If true, the loaded region merges with the existing one rather than replacing it. Any terrain or region
/// settings in the archive will be ignored.
/// </param>
/// <param name="skipAssets">
/// If true, the archive is loaded without loading any assets contained within it. This is useful if the
/// assets are already known to be present in the grid's asset service.
/// </param
/// <param name="requestId">If supplied, this request Id is later returned in the saved event</param>
void DearchiveRegion(Stream loadStream, bool merge, bool skipAssets, Guid requestId);
/// <param name="options">
/// Dictionary of options.
/// </param>
void DearchiveRegion(Stream loadStream, Guid requestId, Dictionary<string,object> options);
}
}

View File

@@ -55,5 +55,10 @@ namespace OpenSim.Region.Framework.Interfaces
/// Currently, will throw an exception if this does not match a root region.
/// </param>
Vector2 GetSizeOfMegaregion(UUID regionId);
/// <summary>
/// Tests to see of position (relative to the region) is within the megaregion
/// </summary>
bool PositionIsInMegaregion(UUID currentRegion, int xx, int yy);
}
}

View File

@@ -68,13 +68,22 @@ namespace OpenSim.Region.Framework.Interfaces
/// </summary>
/// <param name="ter">HeightField data</param>
/// <param name="regionID">region UUID</param>
void StoreTerrain(TerrainData terrain, UUID regionID);
// Legacy version kept for downward compabibility
void StoreTerrain(double[,] terrain, UUID regionID);
/// <summary>
/// Load the latest terrain revision from region storage
/// </summary>
/// <param name="regionID">the region UUID</param>
/// <param name="sizeX">the X dimension of the region being filled</param>
/// <param name="sizeY">the Y dimension of the region being filled</param>
/// <param name="sizeZ">the Z dimension of the region being filled</param>
/// <returns>Heightfield data</returns>
TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ);
// Legacy version kept for downward compabibility
double[,] LoadTerrain(UUID regionID);
void StoreLandObject(ILandObject Parcel);

View File

@@ -79,13 +79,22 @@ namespace OpenSim.Region.Framework.Interfaces
/// </summary>
/// <param name="ter">HeightField data</param>
/// <param name="regionID">region UUID</param>
void StoreTerrain(TerrainData terrain, UUID regionID);
// Legacy version kept for downward compabibility
void StoreTerrain(double[,] terrain, UUID regionID);
/// <summary>
/// Load the latest terrain revision from region storage
/// </summary>
/// <param name="regionID">the region UUID</param>
/// <param name="pSizeX">the X dimension of the terrain being filled</param>
/// <param name="pSizeY">the Y dimension of the terrain being filled</param>
/// <param name="pSizeZ">the Z dimension of the terrain being filled</param>
/// <returns>Heightfield data</returns>
TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ);
// Legacy version kept for downward compabibility
double[,] LoadTerrain(UUID regionID);
void StoreLandObject(ILandObject Parcel);
@@ -135,4 +144,5 @@ namespace OpenSim.Region.Framework.Interfaces
void Shutdown();
}
}

View File

@@ -25,13 +25,23 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using OpenSim.Framework;
using OpenMetaverse;
namespace OpenSim.Region.Framework.Interfaces
{
public interface ITerrainChannel
{
int Height { get; }
int Width { get;} // X dimension
int Height { get;} // Y dimension
int Altitude { get;} // Z dimension
double this[int x, int y] { get; set; }
int Width { get; }
float GetHeightAtXYZ(float x, float y, float z);
// Return the packaged terrain data for passing into lower levels of communication
TerrainData GetTerrainData();
/// <summary>
/// Squash the entire heightmap into a single dimensioned array
@@ -40,9 +50,14 @@ namespace OpenSim.Region.Framework.Interfaces
float[] GetFloatsSerialised();
double[,] GetDoubles();
// Check if a location has been updated. Clears the taint flag as a side effect.
bool Tainted(int x, int y);
ITerrainChannel MakeCopy();
string SaveToXmlString();
void LoadFromXmlString(string data);
// Merge some terrain into this channel
void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement);
}
}

View File

@@ -51,6 +51,7 @@ namespace OpenSim.Region.Framework.Interfaces
/// </param>
/// <param name="stream"></param>
void LoadFromStream(string filename, Stream stream);
void LoadFromStream(string filename, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement, Stream stream);
void LoadFromStream(string filename, System.Uri pathToTerrainHeightmap);
/// <summary>
/// Save a terrain to a stream.

View File

@@ -46,8 +46,8 @@ namespace OpenSim.Region.Framework.Scenes
{
public partial class Scene
{
private static readonly ILog m_log
= LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[SCENE INVENTORY]";
/// <summary>
/// Allows asynchronous derezzing of objects from the scene into a client's inventory.

View File

@@ -160,11 +160,6 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public SimStatsReporter StatsReporter { get; private set; }
public List<Border> NorthBorders = new List<Border>();
public List<Border> EastBorders = new List<Border>();
public List<Border> SouthBorders = new List<Border>();
public List<Border> WestBorders = new List<Border>();
/// <summary>
/// Controls whether physics can be applied to prims. Even if false, prims still have entries in a
/// PhysicsScene in order to perform collision detection
@@ -349,7 +344,6 @@ namespace OpenSim.Region.Framework.Scenes
// TODO: Possibly stop other classes being able to manipulate this directly.
private SceneGraph m_sceneGraph;
private volatile int m_bordersLocked;
private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing
private volatile bool m_backingup;
private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>();
@@ -426,18 +420,6 @@ namespace OpenSim.Region.Framework.Scenes
set { m_splitRegionID = value; }
}
public bool BordersLocked
{
get { return m_bordersLocked == 1; }
set
{
if (value == true)
m_bordersLocked = 1;
else
m_bordersLocked = 0;
}
}
public new float TimeDilation
{
get { return m_sceneGraph.PhysicsScene.TimeDilation; }
@@ -1031,28 +1013,6 @@ namespace OpenSim.Region.Framework.Scenes
PeriodicBackup = true;
UseBackup = true;
BordersLocked = true;
Border northBorder = new Border();
northBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, (int)Constants.RegionSize); //<---
northBorder.CrossDirection = Cardinals.N;
NorthBorders.Add(northBorder);
Border southBorder = new Border();
southBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //--->
southBorder.CrossDirection = Cardinals.S;
SouthBorders.Add(southBorder);
Border eastBorder = new Border();
eastBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue, (int)Constants.RegionSize); //<---
eastBorder.CrossDirection = Cardinals.E;
EastBorders.Add(eastBorder);
Border westBorder = new Border();
westBorder.BorderLine = new Vector3(float.MinValue, float.MaxValue,0); //--->
westBorder.CrossDirection = Cardinals.W;
WestBorders.Add(westBorder);
BordersLocked = false;
m_eventManager = new EventManager();
m_permissions = new ScenePermissions(this);
@@ -1099,8 +1059,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns>True after all operations complete, throws exceptions otherwise.</returns>
public override void OtherRegionUp(GridRegion otherRegion)
{
uint xcell = (uint)((int)otherRegion.RegionLocX / (int)Constants.RegionSize);
uint ycell = (uint)((int)otherRegion.RegionLocY / (int)Constants.RegionSize);
uint xcell = Util.WorldToRegionLoc((uint)otherRegion.RegionLocX);
uint ycell = Util.WorldToRegionLoc((uint)otherRegion.RegionLocY);
//m_log.InfoFormat("[SCENE]: (on region {0}): Region {1} up in coords {2}-{3}",
// RegionInfo.RegionName, otherRegion.RegionName, xcell, ycell);
@@ -1172,46 +1133,6 @@ namespace OpenSim.Region.Framework.Scenes
return found;
}
/// <summary>
/// Checks whether this region has a neighbour in the given direction.
/// </summary>
/// <param name="car"></param>
/// <param name="fix"></param>
/// <returns>
/// An integer which represents a compass point. N == 1, going clockwise until we reach NW == 8.
/// Returns a positive integer if there is a region in that direction, a negative integer if not.
/// </returns>
public int HaveNeighbor(Cardinals car, ref int[] fix)
{
uint neighbourx = RegionInfo.RegionLocX;
uint neighboury = RegionInfo.RegionLocY;
int dir = (int)car;
if (dir > 1 && dir < 5) //Heading East
neighbourx++;
else if (dir > 5) // Heading West
neighbourx--;
if (dir < 3 || dir == 8) // Heading North
neighboury++;
else if (dir > 3 && dir < 7) // Heading Sout
neighboury--;
int x = (int)(neighbourx * Constants.RegionSize);
int y = (int)(neighboury * Constants.RegionSize);
GridRegion neighbourRegion = GridService.GetRegionByPosition(RegionInfo.ScopeID, x, y);
if (neighbourRegion == null)
{
fix[0] = (int)(RegionInfo.RegionLocX - neighbourx);
fix[1] = (int)(RegionInfo.RegionLocY - neighboury);
return dir * (-1);
}
else
return dir;
}
// Alias IncomingHelloNeighbour OtherRegionUp, for now
public GridRegion IncomingHelloNeighbour(RegionInfo neighbour)
{
@@ -1894,7 +1815,7 @@ namespace OpenSim.Region.Framework.Scenes
{
try
{
double[,] map = SimulationDataService.LoadTerrain(RegionInfo.RegionID);
TerrainData map = SimulationDataService.LoadTerrain(RegionInfo.RegionID, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
if (map == null)
{
// This should be in the Terrain module, but it isn't because
@@ -1905,7 +1826,7 @@ namespace OpenSim.Region.Framework.Scenes
m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain);
Heightmap = new TerrainChannel(m_InitialTerrain);
Heightmap = new TerrainChannel(m_InitialTerrain, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID);
}
@@ -2483,185 +2404,35 @@ namespace OpenSim.Region.Framework.Scenes
EntityTransferModule.Cross(grp, attemptedPosition, silent);
}
public Border GetCrossedBorder(Vector3 position, Cardinals gridline)
// Simple test to see if a position is in the current region.
// This test is mostly used to see if a region crossing is necessary.
// Assuming the position is relative to the region so anything outside its bounds.
// Return 'true' if position inside region.
public bool PositionIsInCurrentRegion(Vector3 pos)
{
if (BordersLocked)
bool ret = false;
int xx = (int)Math.Floor(pos.X);
int yy = (int)Math.Floor(pos.Y);
if (xx < 0 || yy < 0)
return false;
IRegionCombinerModule regionCombinerModule = RequestModuleInterface<IRegionCombinerModule>();
if (regionCombinerModule == null)
{
switch (gridline)
{
case Cardinals.N:
lock (NorthBorders)
{
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.S:
lock (SouthBorders)
{
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.E:
lock (EastBorders)
{
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
case Cardinals.W:
lock (WestBorders)
{
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return b;
}
}
break;
}
// Regular region. Just check for region size
if (xx < RegionInfo.RegionSizeX || yy < RegionInfo.RegionSizeY )
ret = true;
}
else
{
switch (gridline)
{
case Cardinals.N:
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.S:
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.E:
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return b;
}
break;
case Cardinals.W:
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return b;
}
break;
}
// We're in a mega-region so see if we are still in that larger region
ret = regionCombinerModule.PositionIsInMegaregion(this.RegionInfo.RegionID, xx, yy);
}
return null;
return ret;
}
public bool TestBorderCross(Vector3 position, Cardinals border)
{
if (BordersLocked)
{
switch (border)
{
case Cardinals.N:
lock (NorthBorders)
{
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.E:
lock (EastBorders)
{
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.S:
lock (SouthBorders)
{
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
case Cardinals.W:
lock (WestBorders)
{
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return true;
}
}
break;
}
}
else
{
switch (border)
{
case Cardinals.N:
foreach (Border b in NorthBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.E:
foreach (Border b in EastBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.S:
foreach (Border b in SouthBorders)
{
if (b.TestCross(position))
return true;
}
break;
case Cardinals.W:
foreach (Border b in WestBorders)
{
if (b.TestCross(position))
return true;
}
break;
}
}
return false;
}
/// <summary>
/// Called when objects or attachments cross the border, or teleport, between regions.
/// </summary>
@@ -3896,60 +3667,11 @@ namespace OpenSim.Region.Framework.Scenes
{
// CleanDroppedAttachments();
if (TestBorderCross(acd.startpos, Cardinals.E))
{
Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.E);
acd.startpos.X = crossedBorder.BorderLine.Z - 1;
}
if (TestBorderCross(acd.startpos, Cardinals.N))
{
Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.N);
acd.startpos.Y = crossedBorder.BorderLine.Z - 1;
}
//Mitigate http://opensimulator.org/mantis/view.php?id=3522
// Check if start position is outside of region
// If it is, check the Z start position also.. if not, leave it alone.
if (BordersLocked)
{
lock (EastBorders)
{
if (acd.startpos.X > EastBorders[0].BorderLine.Z)
{
m_log.Warn("FIX AGENT POSITION");
acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
lock (NorthBorders)
{
if (acd.startpos.Y > NorthBorders[0].BorderLine.Z)
{
m_log.Warn("FIX Agent POSITION");
acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
} else
{
if (acd.startpos.X > EastBorders[0].BorderLine.Z)
{
m_log.Warn("FIX AGENT POSITION");
acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
if (acd.startpos.Y > NorthBorders[0].BorderLine.Z)
{
m_log.Warn("FIX Agent POSITION");
acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f;
if (acd.startpos.Z > 720)
acd.startpos.Z = 720;
}
}
// Make sure avatar position is in the region (why it wouldn't be is a mystery but do sanity checking)
if (acd.startpos.X < 0) acd.startpos.X = 1f;
if (acd.startpos.X >= RegionInfo.RegionSizeX) acd.startpos.X = RegionInfo.RegionSizeX - 1f;
if (acd.startpos.Y < 0) acd.startpos.Y = 1f;
if (acd.startpos.Y >= RegionInfo.RegionSizeY) acd.startpos.Y = RegionInfo.RegionSizeY - 1f;
// m_log.DebugFormat(
// "[SCENE]: Found telehub object {0} for new user connection {1} to {2}",
@@ -4019,12 +3741,12 @@ namespace OpenSim.Region.Framework.Scenes
{
if (posX < 0)
posX = 0;
else if (posX >= 256)
posX = 255.999f;
else if (posX >= (float)RegionInfo.RegionSizeX)
posX = (float)RegionInfo.RegionSizeX - 0.001f;
if (posY < 0)
posY = 0;
else if (posY >= 256)
posY = 255.999f;
else if (posY >= (float)RegionInfo.RegionSizeY)
posY = (float)RegionInfo.RegionSizeY - 0.001f;
reason = String.Empty;
if (Permissions.IsGod(agentID))
@@ -4318,7 +4040,7 @@ namespace OpenSim.Region.Framework.Scenes
"[SCENE]: Incoming child agent update for {0} in {1}", cAgentData.AgentID, RegionInfo.RegionName);
// TODO: This check should probably be in QueryAccess().
ILandObject nearestParcel = GetNearestAllowedParcel(cAgentData.AgentID, Constants.RegionSize / 2, Constants.RegionSize / 2);
ILandObject nearestParcel = GetNearestAllowedParcel(cAgentData.AgentID, RegionInfo.RegionSizeX / 2, RegionInfo.RegionSizeY / 2);
if (nearestParcel == null)
{
m_log.InfoFormat(
@@ -4613,44 +4335,6 @@ namespace OpenSim.Region.Framework.Scenes
ScenePresence sp = GetScenePresence(remoteClient.AgentId);
if (sp != null)
{
uint regionX = RegionInfo.RegionLocX;
uint regionY = RegionInfo.RegionLocY;
Utils.LongToUInts(regionHandle, out regionX, out regionY);
int shiftx = (int) regionX - (int) RegionInfo.RegionLocX * (int)Constants.RegionSize;
int shifty = (int) regionY - (int) RegionInfo.RegionLocY * (int)Constants.RegionSize;
position.X += shiftx;
position.Y += shifty;
bool result = false;
if (TestBorderCross(position,Cardinals.N))
result = true;
if (TestBorderCross(position, Cardinals.S))
result = true;
if (TestBorderCross(position, Cardinals.E))
result = true;
if (TestBorderCross(position, Cardinals.W))
result = true;
// bordercross if position is outside of region
if (!result)
{
regionHandle = RegionInfo.RegionHandle;
}
else
{
// not in this region, undo the shift!
position.X -= shiftx;
position.Y -= shifty;
}
if (EntityTransferModule != null)
{
EntityTransferModule.Teleport(sp, regionHandle, position, lookAt, teleportFlags);
@@ -4830,7 +4514,7 @@ namespace OpenSim.Region.Framework.Scenes
else
{
if (pos.X > 0f && pos.X < Constants.RegionSize && pos.Y > 0f && pos.Y < Constants.RegionSize)
if (pos.X > 0f && pos.X < RegionInfo.RegionSizeX && pos.Y > 0f && pos.Y < RegionInfo.RegionSizeY)
{
// The only time parcel != null when an object is inside a region is when
// there is nothing behind the landchannel. IE, no land plugin loaded.
@@ -5491,7 +5175,7 @@ namespace OpenSim.Region.Framework.Scenes
{
Vector3 unitDirection = Vector3.Normalize(direction);
//Making distance to search go through some sane limit of distance
for (float distance = 0; distance < Constants.RegionSize * 2; distance += .5f)
for (float distance = 0; distance < Math.Max(RegionInfo.RegionSizeX, RegionInfo.RegionSizeY) * 2; distance += .5f)
{
Vector3 testPos = Vector3.Add(pos, Vector3.Multiply(unitDirection, distance));
if (parcel.ContainsPoint((int)testPos.X, (int)testPos.Y))
@@ -5545,9 +5229,9 @@ namespace OpenSim.Region.Framework.Scenes
int count = 0;
int avgx = 0;
int avgy = 0;
for (int x = 0; x < Constants.RegionSize; x++)
for (int x = 0; x < RegionInfo.RegionSizeX; x++)
{
for (int y = 0; y < Constants.RegionSize; y++)
for (int y = 0; y < RegionInfo.RegionSizeY; y++)
{
//Just keep a running average as we check if all the points are inside or not
if (parcel.ContainsPoint(x, y))
@@ -5571,31 +5255,33 @@ namespace OpenSim.Region.Framework.Scenes
private Vector3 GetNearestRegionEdgePosition(ScenePresence avatar)
{
float xdistance = avatar.AbsolutePosition.X < Constants.RegionSize / 2 ? avatar.AbsolutePosition.X : Constants.RegionSize - avatar.AbsolutePosition.X;
float ydistance = avatar.AbsolutePosition.Y < Constants.RegionSize / 2 ? avatar.AbsolutePosition.Y : Constants.RegionSize - avatar.AbsolutePosition.Y;
float xdistance = avatar.AbsolutePosition.X < RegionInfo.RegionSizeX / 2
? avatar.AbsolutePosition.X : RegionInfo.RegionSizeX - avatar.AbsolutePosition.X;
float ydistance = avatar.AbsolutePosition.Y < RegionInfo.RegionSizeY / 2
? avatar.AbsolutePosition.Y : RegionInfo.RegionSizeY - avatar.AbsolutePosition.Y;
//find out what vertical edge to go to
if (xdistance < ydistance)
{
if (avatar.AbsolutePosition.X < Constants.RegionSize / 2)
if (avatar.AbsolutePosition.X < RegionInfo.RegionSizeX / 2)
{
return GetPositionAtAvatarHeightOrGroundHeight(avatar, 0.0f, avatar.AbsolutePosition.Y);
}
else
{
return GetPositionAtAvatarHeightOrGroundHeight(avatar, Constants.RegionSize, avatar.AbsolutePosition.Y);
return GetPositionAtAvatarHeightOrGroundHeight(avatar, RegionInfo.RegionSizeY, avatar.AbsolutePosition.Y);
}
}
//find out what horizontal edge to go to
else
{
if (avatar.AbsolutePosition.Y < Constants.RegionSize / 2)
if (avatar.AbsolutePosition.Y < RegionInfo.RegionSizeY / 2)
{
return GetPositionAtAvatarHeightOrGroundHeight(avatar, avatar.AbsolutePosition.X, 0.0f);
}
else
{
return GetPositionAtAvatarHeightOrGroundHeight(avatar, avatar.AbsolutePosition.X, Constants.RegionSize);
return GetPositionAtAvatarHeightOrGroundHeight(avatar, avatar.AbsolutePosition.X, RegionInfo.RegionSizeY);
}
}
}

View File

@@ -42,8 +42,8 @@ namespace OpenSim.Region.Framework.Scenes
{
public abstract class SceneBase : IScene
{
protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected static readonly string LogHeader = "[SCENE]";
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[SCENE]";
#region Events

View File

@@ -92,7 +92,7 @@ namespace OpenSim.Region.Framework.Scenes
{
m_log.DebugFormat(
"[SCENE COMMUNICATION SERVICE]: Region {0} successfully informed neighbour {1} at {2}-{3} that it is up",
m_scene.Name, neighbour.RegionName, x / Constants.RegionSize, y / Constants.RegionSize);
m_scene.Name, neighbour.RegionName, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y));
m_scene.EventManager.TriggerOnRegionUp(neighbour);
}
@@ -100,7 +100,7 @@ namespace OpenSim.Region.Framework.Scenes
{
m_log.WarnFormat(
"[SCENE COMMUNICATION SERVICE]: Region {0} failed to inform neighbour at {1}-{2} that it is up.",
m_scene.Name, x / Constants.RegionSize, y / Constants.RegionSize);
m_scene.Name, Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y));
}
}
@@ -166,7 +166,7 @@ namespace OpenSim.Region.Framework.Scenes
// we only want to send one update to each simulator; the simulator will
// hand it off to the regions where a child agent exists, this does assume
// that the region position is cached or performance will degrade
Utils.LongToUInts(regionHandle, out x, out y);
Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
GridRegion dest = m_scene.GridService.GetRegionByPosition(UUID.Zero, (int)x, (int)y);
if (dest == null)
continue;
@@ -203,7 +203,7 @@ namespace OpenSim.Region.Framework.Scenes
//m_commsProvider.InterRegion.TellRegionToCloseChildConnection(regionHandle, agentID);
uint x = 0, y = 0;
Utils.LongToUInts(regionHandle, out x, out y);
Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
GridRegion destination = m_scene.GridService.GetRegionByPosition(m_regionInfo.ScopeID, (int)x, (int)y);

View File

@@ -334,7 +334,7 @@ namespace OpenSim.Region.Framework.Scenes
{
get
{
Vector3 minScale = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
Vector3 minScale = new Vector3(Constants.MaximumRegionSize, Constants.MaximumRegionSize, Constants.MaximumRegionSize);
Vector3 maxScale = Vector3.Zero;
Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
@@ -429,7 +429,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns>
public bool IsAttachmentCheckFull()
{
return (IsAttachment || (m_rootPart.Shape.PCode == 9 && m_rootPart.Shape.State != 0));
return (IsAttachment || (m_rootPart.Shape.PCode == (byte)PCodeEnum.Primitive && m_rootPart.Shape.State != 0));
}
private struct avtocrossInfo
@@ -451,25 +451,15 @@ namespace OpenSim.Region.Framework.Scenes
if (Scene != null)
{
if (
// (Scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E)
// || Scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W)
// || Scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N)
// || Scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S))
// Experimental change for better border crossings.
// The commented out original lines above would, it seems, trigger
// a border crossing a little early or late depending on which
// direction the object was moving.
(Scene.TestBorderCross(val, Cardinals.E)
|| Scene.TestBorderCross(val, Cardinals.W)
|| Scene.TestBorderCross(val, Cardinals.N)
|| Scene.TestBorderCross(val, Cardinals.S))
&& !IsAttachmentCheckFull() && (!Scene.LoadingPrims))
!Scene.PositionIsInCurrentRegion(val)
&& !IsAttachmentCheckFull()
&& (!Scene.LoadingPrims)
)
{
IEntityTransferModule entityTransfer = m_scene.RequestModuleInterface<IEntityTransferModule>();
uint x = 0;
uint y = 0;
string version = String.Empty;
Vector3 newpos = Vector3.Zero;
string failureReason = String.Empty;
OpenSim.Services.Interfaces.GridRegion destination = null;
if (m_rootPart.KeyframeMotion != null)
@@ -487,7 +477,7 @@ namespace OpenSim.Region.Framework.Scenes
// We set the avatar position as being the object
// position to get the region to send to
if ((destination = entityTransfer.GetDestination(m_scene, av.UUID, val, out x, out y, out version, out newpos)) == null)
if ((destination = entityTransfer.GetDestination(m_scene, av.UUID, val, out version, out newpos, out failureReason)) == null)
{
canCross = false;
break;
@@ -522,14 +512,14 @@ namespace OpenSim.Region.Framework.Scenes
m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
// Normalize
if (val.X >= Constants.RegionSize)
val.X -= Constants.RegionSize;
if (val.Y >= Constants.RegionSize)
val.Y -= Constants.RegionSize;
if (val.X >= m_scene.RegionInfo.RegionSizeX)
val.X -= m_scene.RegionInfo.RegionSizeX;
if (val.Y >= m_scene.RegionInfo.RegionSizeY)
val.Y -= m_scene.RegionInfo.RegionSizeY;
if (val.X < 0)
val.X += Constants.RegionSize;
val.X += m_scene.RegionInfo.RegionSizeX;
if (val.Y < 0)
val.Y += Constants.RegionSize;
val.Y += m_scene.RegionInfo.RegionSizeY;
// If it's deleted, crossing was successful
if (IsDeleted)
@@ -577,9 +567,9 @@ namespace OpenSim.Region.Framework.Scenes
}
}
Vector3 oldp = AbsolutePosition;
val.X = Util.Clamp<float>(oldp.X, 0.5f, (float)Constants.RegionSize - 0.5f);
val.Y = Util.Clamp<float>(oldp.Y, 0.5f, (float)Constants.RegionSize - 0.5f);
val.Z = Util.Clamp<float>(oldp.Z, 0.5f, 4096.0f);
val.X = Util.Clamp<float>(oldp.X, 0.5f, (float)m_scene.RegionInfo.RegionSizeX - 0.5f);
val.Y = Util.Clamp<float>(oldp.Y, 0.5f, (float)m_scene.RegionInfo.RegionSizeY - 0.5f);
val.Z = Util.Clamp<float>(oldp.Z, 0.5f, Constants.RegionHeight);
}
}
@@ -996,9 +986,9 @@ namespace OpenSim.Region.Framework.Scenes
maxX = -256f;
maxY = -256f;
maxZ = -256f;
minX = 256f;
minY = 256f;
minZ = 8192f;
minX = 10000f;
minY = 10000f;
minZ = 10000f;
SceneObjectPart[] parts = m_parts.GetArray();
for (int i = 0; i < parts.Length; i++)

View File

@@ -2479,13 +2479,10 @@ namespace OpenSim.Region.Framework.Scenes
if (pa != null)
{
Vector3 newpos = new Vector3(pa.Position.GetBytes(), 0);
if (ParentGroup.Scene.TestBorderCross(newpos, Cardinals.N)
| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.S)
| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.E)
| ParentGroup.Scene.TestBorderCross(newpos, Cardinals.W))
Vector3 newpos = pa.Position;
if (!ParentGroup.Scene.PositionIsInCurrentRegion(newpos))
{
// Setting position outside current region will start region crossing
ParentGroup.AbsolutePosition = newpos;
return;
}

View File

@@ -76,6 +76,7 @@ namespace OpenSim.Region.Framework.Scenes
public class ScenePresence : EntityBase, IScenePresence
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly String LogHeader = "[SCENE PRESENCE]";
// ~ScenePresence()
// {
@@ -759,9 +760,8 @@ namespace OpenSim.Region.Framework.Scenes
foreach (ulong handle in seeds.Keys)
{
uint x, y;
Utils.LongToUInts(handle, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(handle, out x, out y);
if (Util.IsOutsideView(DrawDistance, x, Scene.RegionInfo.RegionLocX, y, Scene.RegionInfo.RegionLocY))
{
old.Add(handle);
@@ -783,9 +783,7 @@ namespace OpenSim.Region.Framework.Scenes
foreach (KeyValuePair<ulong, string> kvp in KnownRegions)
{
uint x, y;
Utils.LongToUInts(kvp.Key, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(kvp.Key, out x, out y);
m_log.Info(" >> "+x+", "+y+": "+kvp.Value);
}
}
@@ -1059,30 +1057,32 @@ namespace OpenSim.Region.Framework.Scenes
m_scene.EventManager.TriggerSetRootAgentScene(m_uuid, m_scene);
UUID groupUUID = UUID.Zero;
string GroupName = string.Empty;
UUID groupUUID = ControllingClient.ActiveGroupId;
string groupName = string.Empty;
ulong groupPowers = 0;
// ----------------------------------
// Previous Agent Difference - AGNI sends an unsolicited AgentDataUpdate upon root agent status
try
{
if (gm != null)
if (groupUUID != UUID.Zero && gm != null)
{
groupUUID = ControllingClient.ActiveGroupId;
GroupRecord record = gm.GetGroupRecord(groupUUID);
if (record != null)
GroupName = record.GroupName;
groupName = record.GroupName;
GroupMembershipData groupMembershipData = gm.GetMembershipData(groupUUID, m_uuid);
if (groupMembershipData != null)
groupPowers = groupMembershipData.GroupPowers;
}
ControllingClient.SendAgentDataUpdate(m_uuid, groupUUID, Firstname, Lastname, groupPowers, GroupName,
Grouptitle);
ControllingClient.SendAgentDataUpdate(
m_uuid, groupUUID, Firstname, Lastname, groupPowers, groupName, Grouptitle);
}
catch (Exception e)
{
m_log.Debug("[AGENTUPDATE]: " + e.ToString());
m_log.Error("[AGENTUPDATE]: Error ", e);
}
// ------------------------------------
@@ -1092,6 +1092,10 @@ namespace OpenSim.Region.Framework.Scenes
// before the inventory is processed in MakeRootAgent. This fixes a race condition
// related to the handling of attachments
//m_scene.GetAvatarAppearance(ControllingClient, out Appearance);
/* RA 20140111: Commented out these TestBorderCross's.
* Not sure why this code is here. It is not checking all the borders
* and 'in region' sanity checking is done in CheckAndAdjustLandingPoint and below.
if (m_scene.TestBorderCross(pos, Cardinals.E))
{
Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.E);
@@ -1103,6 +1107,7 @@ namespace OpenSim.Region.Framework.Scenes
Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.N);
pos.Y = crossedBorder.BorderLine.Z - 1;
}
*/
CheckAndAdjustLandingPoint(ref pos);
@@ -1123,7 +1128,7 @@ namespace OpenSim.Region.Framework.Scenes
float posZLimit = 0;
if (pos.X < Constants.RegionSize && pos.Y < Constants.RegionSize)
if (pos.X < m_scene.RegionInfo.RegionSizeX && pos.Y < m_scene.RegionInfo.RegionSizeY)
posZLimit = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y];
float newPosZ = posZLimit + localAVHeight / 2;
@@ -2400,7 +2405,7 @@ namespace OpenSim.Region.Framework.Scenes
if (regionCombinerModule != null)
regionSize = regionCombinerModule.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
else
regionSize = new Vector2(Constants.RegionSize);
regionSize = new Vector2(m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
if (pos.X < 0 || pos.X >= regionSize.X
|| pos.Y < 0 || pos.Y >= regionSize.Y
@@ -2418,8 +2423,8 @@ namespace OpenSim.Region.Framework.Scenes
// }
// Get terrain height for sub-region in a megaregion if necessary
int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X);
int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y);
int X = (int)((m_scene.RegionInfo.WorldLocX) + pos.X);
int Y = (int)((m_scene.RegionInfo.WorldLocY) + pos.Y);
GridRegion target_region = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y);
// If X and Y is NaN, target_region will be null
if (target_region == null)
@@ -2430,7 +2435,7 @@ namespace OpenSim.Region.Framework.Scenes
if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene))
targetScene = m_scene;
float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % Constants.RegionSize), (int)(pos.Y % Constants.RegionSize)];
float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % regionSize.X), (int)(pos.Y % regionSize.Y)];
pos.Z = Math.Max(terrainHeight, pos.Z);
// Fudge factor. It appears that if one clicks "go here" on a piece of ground, the go here request is
@@ -3466,10 +3471,12 @@ namespace OpenSim.Region.Framework.Scenes
if (!IsInTransit)
{
Vector3 pos2 = AbsolutePosition;
Vector3 origPosition = pos2;
Vector3 vel = Velocity;
int neighbor = 0;
int[] fix = new int[2];
// Compute the avatar position in the next physics tick.
// If the avatar will be crossing, we force the crossing to happen now
// in the hope that this will make the avatar movement smoother when crossing.
float timeStep = 0.1f;
pos2.X = pos2.X + (vel.X * timeStep);
pos2.Y = pos2.Y + (vel.Y * timeStep);
@@ -3477,107 +3484,30 @@ namespace OpenSim.Region.Framework.Scenes
if (!IsInTransit)
{
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Testing border check for projected position {0} of {1} in {2}",
// pos2, Name, Scene.Name);
// Checks if where it's headed exists a region
bool needsTransit = false;
if (m_scene.TestBorderCross(pos2, Cardinals.W))
if (!m_scene.PositionIsInCurrentRegion(pos2))
{
if (m_scene.TestBorderCross(pos2, Cardinals.S))
m_log.DebugFormat("{0} CheckForBorderCrossing: position outside region. {1} in {2} at pos {3}",
LogHeader, Name, Scene.Name, pos2);
// Disconnect from the current region
bool isFlying = Flying;
RemoveFromPhysicalScene();
// pos2 is the forcasted position so make that the 'current' position so the crossing
// code will move us into the newly addressed region.
m_pos = pos2;
if (CrossToNewRegion())
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.SW, ref fix);
}
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.NW, ref fix);
AddToPhysicalScene(isFlying);
}
else
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.W, ref fix);
}
}
else if (m_scene.TestBorderCross(pos2, Cardinals.E))
{
if (m_scene.TestBorderCross(pos2, Cardinals.S))
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.SE, ref fix);
}
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.NE, ref fix);
}
else
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.E, ref fix);
}
}
else if (m_scene.TestBorderCross(pos2, Cardinals.S))
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.S, ref fix);
}
else if (m_scene.TestBorderCross(pos2, Cardinals.N))
{
needsTransit = true;
neighbor = m_scene.HaveNeighbor(Cardinals.N, ref fix);
}
// Makes sure avatar does not end up outside region
if (neighbor <= 0)
{
if (needsTransit)
{
// Tried to make crossing happen but it failed.
if (m_requestedSitTargetUUID == UUID.Zero)
{
bool isFlying = Flying;
RemoveFromPhysicalScene();
m_log.DebugFormat("{0} CheckForBorderCrossing: Crossing failed. Restoring old position.", LogHeader);
Vector3 pos = AbsolutePosition;
if (AbsolutePosition.X < 0)
pos.X += Velocity.X * 2;
else if (AbsolutePosition.X > Constants.RegionSize)
pos.X -= Velocity.X * 2;
if (AbsolutePosition.Y < 0)
pos.Y += Velocity.Y * 2;
else if (AbsolutePosition.Y > Constants.RegionSize)
pos.Y -= Velocity.Y * 2;
Velocity = Vector3.Zero;
AbsolutePosition = pos;
// m_log.DebugFormat("[SCENE PRESENCE]: Prevented flyoff for {0} at {1}", Name, AbsolutePosition);
AddToPhysicalScene(isFlying);
}
}
}
else if (neighbor > 0)
{
if (!CrossToNewRegion())
{
if (m_requestedSitTargetUUID == UUID.Zero)
{
bool isFlying = Flying;
RemoveFromPhysicalScene();
Vector3 pos = AbsolutePosition;
if (AbsolutePosition.X < 0)
pos.X += Velocity.X * 2;
else if (AbsolutePosition.X > Constants.RegionSize)
pos.X -= Velocity.X * 2;
if (AbsolutePosition.Y < 0)
pos.Y += Velocity.Y * 2;
else if (AbsolutePosition.Y > Constants.RegionSize)
pos.Y -= Velocity.Y * 2;
Velocity = Vector3.Zero;
AbsolutePosition = pos;
AbsolutePosition = EnforceSanityOnPosition(origPosition);
AddToPhysicalScene(isFlying);
}
@@ -3599,6 +3529,36 @@ namespace OpenSim.Region.Framework.Scenes
}
}
// Given a position, make sure it is within the current region.
// If just outside some border, the returned position will be just inside the border on that side.
private Vector3 EnforceSanityOnPosition(Vector3 origPosition)
{
const float borderFudge = 0.1f;
Vector3 ret = origPosition;
// Sanity checking on the position to make sure it is in the region we couldn't cross from
float extentX = (float)m_scene.RegionInfo.RegionSizeX;
float extentY = (float)m_scene.RegionInfo.RegionSizeY;
IRegionCombinerModule combiner = m_scene.RequestModuleInterface<IRegionCombinerModule>();
if (combiner != null)
{
// If a mega-region, the size could be much bigger
Vector2 megaExtent = combiner.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
extentX = megaExtent.X;
extentY = megaExtent.Y;
}
if (ret.X < 0)
ret.X = borderFudge;
else if (ret.X >= extentX)
ret.X = extentX - borderFudge;
if (ret.Y < 0)
ret.Y = borderFudge;
else if (ret.Y >= extentY)
ret.Y = extentY - borderFudge;
return ret;
}
/// <summary>
/// Moves the agent outside the region bounds
/// Tells neighbor region that we're crossing to it
@@ -3623,7 +3583,7 @@ namespace OpenSim.Region.Framework.Scenes
// Put the child agent back at the center
AbsolutePosition
= new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70);
= new Vector3(((float)m_scene.RegionInfo.RegionSizeX * 0.5f), ((float)m_scene.RegionInfo.RegionSizeY * 0.5f), 70);
Animator.ResetAnimations();
}
@@ -3650,9 +3610,7 @@ namespace OpenSim.Region.Framework.Scenes
if (handle != Scene.RegionInfo.RegionHandle)
{
uint x, y;
Utils.LongToUInts(handle, out x, out y);
x = x / Constants.RegionSize;
y = y / Constants.RegionSize;
Util.RegionHandleToRegionLoc(handle, out x, out y);
// m_log.Debug("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX)));
// m_log.Debug("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY)));
@@ -3733,8 +3691,9 @@ namespace OpenSim.Region.Framework.Scenes
return;
//m_log.Debug(" >>> ChildAgentPositionUpdate <<< " + rRegionX + "-" + rRegionY);
int shiftx = ((int)rRegionX - (int)tRegionX) * (int)Constants.RegionSize;
int shifty = ((int)rRegionY - (int)tRegionY) * (int)Constants.RegionSize;
// Find the distance (in meters) between the two regions
uint shiftx = Util.RegionToWorldLoc(rRegionX - tRegionX);
uint shifty = Util.RegionToWorldLoc(rRegionY - tRegionY);
Vector3 offset = new Vector3(shiftx, shifty, 0f);
@@ -4770,6 +4729,7 @@ namespace OpenSim.Region.Framework.Scenes
}
}
// Modify landing point based on possible banning, telehubs or parcel restrictions.
private void CheckAndAdjustLandingPoint(ref Vector3 pos)
{
string reason;

View File

@@ -25,13 +25,20 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Text;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using System;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using OpenMetaverse;
using log4net;
namespace OpenSim.Region.Framework.Scenes
{
@@ -40,132 +47,146 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public class TerrainChannel : ITerrainChannel
{
private readonly bool[,] taint;
private double[,] map;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[TERRAIN CHANNEL]";
protected TerrainData m_terrainData;
public int Width { get { return m_terrainData.SizeX; } } // X dimension
// Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y
public int Height { get { return m_terrainData.SizeY; } } // Y dimension
public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension
// Default, not-often-used builder
public TerrainChannel()
{
map = new double[Constants.RegionSize, Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
PinHeadIsland();
m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
FlatLand();
// PinHeadIsland();
}
public TerrainChannel(String type)
// Create terrain of given size
public TerrainChannel(int pX, int pY)
{
map = new double[Constants.RegionSize, Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight);
}
// Create terrain of specified size and initialize with specified terrain.
// TODO: join this with the terrain initializers.
public TerrainChannel(String type, int pX, int pY, int pZ)
{
m_terrainData = new HeightmapTerrainData(pX, pY, pZ);
if (type.Equals("flat"))
FlatLand();
else
PinHeadIsland();
}
public TerrainChannel(double[,] import)
// Create channel passed a heightmap and expected dimensions of the region.
// The heightmap might not fit the passed size so accomodations must be made.
public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude)
{
map = import;
taint = new bool[import.GetLength(0),import.GetLength(1)];
int hmSizeX = pM.GetLength(0);
int hmSizeY = pM.GetLength(1);
m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude);
for (int xx = 0; xx < pSizeX; xx++)
for (int yy = 0; yy < pSizeY; yy++)
if (xx > hmSizeX || yy > hmSizeY)
m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight;
else
m_terrainData[xx, yy] = (float)pM[xx, yy];
}
public TerrainChannel(bool createMap)
public TerrainChannel(TerrainData pTerrData)
{
if (createMap)
{
map = new double[Constants.RegionSize,Constants.RegionSize];
taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16];
}
}
public TerrainChannel(int w, int h)
{
map = new double[w,h];
taint = new bool[w / 16,h / 16];
m_terrainData = pTerrData;
}
#region ITerrainChannel Members
public int Width
{
get { return map.GetLength(0); }
}
public int Height
{
get { return map.GetLength(1); }
}
// ITerrainChannel.MakeCopy()
public ITerrainChannel MakeCopy()
{
TerrainChannel copy = new TerrainChannel(false);
copy.map = (double[,]) map.Clone();
return copy;
return this.Copy();
}
// ITerrainChannel.GetTerrainData()
public TerrainData GetTerrainData()
{
return m_terrainData;
}
// ITerrainChannel.GetFloatsSerialized()
// This one dimensional version is ordered so height = map[y*sizeX+x];
// DEPRECATED: don't use this function as it does not retain the dimensions of the terrain
// and the caller will probably do the wrong thing if the terrain is not the legacy 256x256.
public float[] GetFloatsSerialised()
{
// Move the member variables into local variables, calling
// member variables 256*256 times gets expensive
int w = Width;
int h = Height;
float[] heights = new float[w * h];
int points = Width * Height;
float[] heights = new float[points];
int i, j; // map coordinates
int idx = 0; // index into serialized array
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
int idx = 0;
for (int jj = 0; jj < Height; jj++)
for (int ii = 0; ii < Width; ii++)
{
heights[idx++] = (float)map[j, i];
heights[idx++] = m_terrainData[ii, jj];
}
return heights;
}
// ITerrainChannel.GetDoubles()
public double[,] GetDoubles()
{
double[,] heights = new double[Width, Height];
int idx = 0; // index into serialized array
for (int ii = 0; ii < Width; ii++)
{
for (int jj = 0; jj < Height; jj++)
{
heights[ii, jj] = (double)m_terrainData[ii, jj];
idx++;
}
}
return heights;
}
public double[,] GetDoubles()
{
return map;
}
// ITerrainChannel.this[x,y]
public double this[int x, int y]
{
get { return map[x, y]; }
get {
if (x < 0 || x >= Width || y < 0 || y >= Height)
return 0;
return (double)m_terrainData[x, y];
}
set
{
// Will "fix" terrain hole problems. Although not fantastically.
if (Double.IsNaN(value) || Double.IsInfinity(value))
return;
if (map[x, y] != value)
{
taint[x / 16, y / 16] = true;
map[x, y] = value;
}
m_terrainData[x, y] = (float)value;
}
}
// ITerrainChannel.GetHieghtAtXYZ(x, y, z)
public float GetHeightAtXYZ(float x, float y, float z)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return 0;
return m_terrainData[(int)x, (int)y];
}
// ITerrainChannel.Tainted()
public bool Tainted(int x, int y)
{
if (taint[x / 16, y / 16])
{
taint[x / 16, y / 16] = false;
return true;
}
return false;
}
#endregion
public TerrainChannel Copy()
{
TerrainChannel copy = new TerrainChannel(false);
copy.map = (double[,]) map.Clone();
return copy;
return m_terrainData.IsTaintedAt(x, y);
}
// ITerrainChannel.SaveToXmlString()
public string SaveToXmlString()
{
XmlWriterSettings settings = new XmlWriterSettings();
@@ -181,13 +202,7 @@ namespace OpenSim.Region.Framework.Scenes
}
}
private void WriteXml(XmlWriter writer)
{
writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
ToXml(writer);
writer.WriteEndElement();
}
// ITerrainChannel.LoadFromXmlString()
public void LoadFromXmlString(string data)
{
StringReader sr = new StringReader(data);
@@ -199,12 +214,124 @@ namespace OpenSim.Region.Framework.Scenes
sr.Close();
}
private void ReadXml(XmlReader reader)
// ITerrainChannel.Merge
public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement)
{
reader.ReadStartElement("TerrainMap");
FromXml(reader);
m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
newTerrain.Width, newTerrain.Height,
displacement, radianRotation, rotationDisplacement,
m_terrainData.SizeX, m_terrainData.SizeY);
for (int xx = 0; xx < newTerrain.Width; xx++)
{
for (int yy = 0; yy < newTerrain.Height; yy++)
{
int dispX = (int)displacement.X;
int dispY = (int)displacement.Y;
float newHeight = (float)newTerrain[xx, yy] + displacement.Z;
if (radianRotation == 0)
{
// If no rotation, place the new height in the specified location
dispX += xx;
dispY += yy;
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
m_terrainData[dispX, dispY] = newHeight;
}
}
else
{
// If rotating, we have to smooth the result because the conversion
// to ints will mean heightmap entries will not get changed
// First compute the rotation location for the new height.
dispX += (int)(rotationDisplacement.X
+ ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation)
- ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) );
dispY += (int)(rotationDisplacement.Y
+ ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation)
+ ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) );
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
float oldHeight = m_terrainData[dispX, dispY];
// Smooth the heights around this location if the old height is far from this one
for (int sxx = dispX - 2; sxx < dispX + 2; sxx++)
{
for (int syy = dispY - 2; syy < dispY + 2; syy++)
{
if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY)
{
if (sxx == dispX && syy == dispY)
{
// Set height for the exact rotated point
m_terrainData[dispX, dispY] = newHeight;
}
else
{
if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f)
{
// If the adjacent height is far off, force it to this height
m_terrainData[sxx, syy] = newHeight;
}
}
}
}
}
}
if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
{
m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy];
}
}
}
}
}
#endregion
public TerrainChannel Copy()
{
TerrainChannel copy = new TerrainChannel();
copy.m_terrainData = m_terrainData.Clone();
return copy;
}
private void WriteXml(XmlWriter writer)
{
if (Width == Constants.RegionSize && Height == Constants.RegionSize)
{
// Downward compatibility for legacy region terrain maps.
// If region is exactly legacy size, return the old format XML.
writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
ToXml(writer);
writer.WriteEndElement();
}
else
{
// New format XML that includes width and length.
writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty);
ToXml2(writer);
writer.WriteEndElement();
}
}
private void ReadXml(XmlReader reader)
{
// Check the first element. If legacy element, use the legacy reader.
if (reader.IsStartElement("TerrainMap"))
{
reader.ReadStartElement("TerrainMap");
FromXml(reader);
}
else
{
reader.ReadStartElement("TerrainMap2");
FromXml2(reader);
}
}
// Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void ToXml(XmlWriter xmlWriter)
{
float[] mapData = GetFloatsSerialised();
@@ -218,12 +345,15 @@ namespace OpenSim.Region.Framework.Scenes
serializer.Serialize(xmlWriter, buffer);
}
// Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void FromXml(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
int index = 0;
m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
@@ -236,35 +366,63 @@ namespace OpenSim.Region.Framework.Scenes
}
}
private class TerrainChannelXMLPackage
{
public int Version;
public int SizeX;
public int SizeY;
public int SizeZ;
public float CompressionFactor;
public short[] Map;
public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, short[] pMap)
{
Version = 1;
SizeX = pX;
SizeY = pY;
SizeZ = pZ;
CompressionFactor = pCompressionFactor;
Map = pMap;
}
}
// New terrain serialization format that includes the width and length.
private void ToXml2(XmlWriter xmlWriter)
{
TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor,
m_terrainData.GetCompressedMap());
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
serializer.Serialize(xmlWriter, package);
}
// New terrain serialization format that includes the width and length.
private void FromXml2(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ);
}
// Fill the heightmap with the center bump terrain
private void PinHeadIsland()
{
int x;
for (x = 0; x < Constants.RegionSize; x++)
for (int x = 0; x < Width; x++)
{
int y;
for (y = 0; y < Constants.RegionSize; y++)
for (int y = 0; y < Height; y++)
{
map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01;
double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001;
if (map[x, y] < spherFacA)
map[x, y] = spherFacA;
if (map[x, y] < spherFacB)
map[x, y] = spherFacB;
m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d);
float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d);
if (m_terrainData[x, y]< spherFacA)
m_terrainData[x, y]= spherFacA;
if (m_terrainData[x, y]< spherFacB)
m_terrainData[x, y] = spherFacB;
}
}
}
private void FlatLand()
{
int x;
for (x = 0; x < Constants.RegionSize; x++)
{
int y;
for (y = 0; y < Constants.RegionSize; y++)
map[x, y] = 21;
}
m_terrainData.ClearLand();
}
}
}

View File

@@ -0,0 +1,942 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Freely adapted from the Aurora version of the terrain compressor.
* Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/
*/
using System;
using System.Reflection;
using log4net;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public static class OpenSimTerrainCompressor
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[TERRAIN COMPRESSOR]";
public const int END_OF_PATCHES = 97;
private const float OO_SQRT2 = 0.7071067811865475244008443621049f;
private const int STRIDE = 264;
private const int ZERO_CODE = 0x0;
private const int ZERO_EOB = 0x2;
private const int POSITIVE_VALUE = 0x6;
private const int NEGATIVE_VALUE = 0x7;
private static readonly float[] DequantizeTable16 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] DequantizeTable32 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] CosineTable16 = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
//private static readonly float[] CosineTable32 = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize];
private static readonly int[] CopyMatrix16 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly int[] CopyMatrix32 = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
private static readonly float[] QuantizeTable16 =
new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
static OpenSimTerrainCompressor()
{
// Initialize the decompression tables
BuildDequantizeTable16();
SetupCosines16();
BuildCopyMatrix16();
BuildQuantizeTable16();
}
// Unused: left for historical reference.
public static LayerDataPacket CreateLayerDataPacket(TerrainPatch[] patches, byte type, int pRegionSizeX,
int pRegionSizeY)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
// Should be enough to fit even the most poorly packed data
byte[] data = new byte[patches.Length*Constants.TerrainPatchSize*Constants.TerrainPatchSize*2];
BitPack bitpack = new BitPack(data, 0);
bitpack.PackBits(header.Stride, 16);
bitpack.PackBits(header.PatchSize, 8);
bitpack.PackBits(type, 8);
foreach (TerrainPatch t in patches)
CreatePatch(bitpack, t.Data, t.X, t.Y, pRegionSizeX, pRegionSizeY);
bitpack.PackBits(END_OF_PATCHES, 8);
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
return layer;
}
// Create a land packet for a single patch.
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY)
{
int[] xPieces = new int[1];
int[] yPieces = new int[1];
xPieces[0] = patchX; // patch X dimension
yPieces[0] = patchY;
byte landPacketType = (byte)TerrainPatch.LayerType.Land;
if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
{
landPacketType = (byte)TerrainPatch.LayerType.LandExtended;
}
return CreateLandPacket(terrData, xPieces, yPieces, landPacketType);
}
/// <summary>
/// Creates a LayerData packet for compressed land data given a full
/// simulator heightmap and an array of indices of patches to compress
/// </summary>
/// <param name="terrData">
/// Terrain data that can result in a meter square heightmap.
/// </param>
/// <param name="x">
/// Array of indexes in the grid of patches
/// for this simulator.
/// If creating a packet for multiple patches, there will be entries in
/// both the X and Y arrays for each of the patches.
/// For example if patches 1 and 17 are to be sent,
/// x[] = {1,1} and y[] = {0,1} which specifies the patches at
/// indexes <1,0> and <1,1> (presuming the terrain size is 16x16 patches).
/// </param>
/// <param name="y">
/// Array of indexes in the grid of patches.
/// </param>
/// <param name="type"></param>
/// <param name="pRegionSizeX"></param>
/// <param name="pRegionSizeY"></param>
/// <returns></returns>
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type)
{
LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}};
TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader
{Stride = STRIDE, PatchSize = Constants.TerrainPatchSize};
byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2];
BitPack bitpack = new BitPack(data, 0);
bitpack.PackBits(header.Stride, 16);
bitpack.PackBits(header.PatchSize, 8);
bitpack.PackBits(type, 8);
for (int i = 0; i < x.Length; i++)
CreatePatchFromHeightmap(bitpack, terrData, x[i], y[i]);
bitpack.PackBits(END_OF_PATCHES, 8);
layer.LayerData.Data = new byte[bitpack.BytePos + 1];
Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1);
return layer;
}
// Unused: left for historical reference.
public static void CreatePatch(BitPack output, float[] patchData, int x, int y, int pRegionSizeX, int pRegionSizeY)
{
TerrainPatch.Header header = PrescanPatch(patchData);
header.QuantWBits = 136;
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
{
header.PatchIDs = (y & 0xFFFF);
header.PatchIDs += (x << 16);
}
else
{
header.PatchIDs = (y & 0x1F);
header.PatchIDs += (x << 5);
}
// NOTE: No idea what prequant and postquant should be or what they do
int wbits;
int[] patch = CompressPatch(patchData, header, 10, out wbits);
wbits = EncodePatchHeader(output, header, patch, (uint)pRegionSizeX, (uint)pRegionSizeY, wbits);
EncodePatch(output, patch, 0, wbits);
}
/// <summary>
/// Add a patch of terrain to a BitPacker
/// </summary>
/// <param name="output">BitPacker to write the patch to</param>
/// <param name="heightmap">
/// Heightmap of the simulator. Presumed to be an sizeX*sizeY array.
/// </param>
/// <param name="patchX">
/// X offset of the patch to create.
/// </param>
/// <param name="patchY">
/// Y offset of the patch to create.
/// </param>
/// <param name="pRegionSizeX"></param>
/// <param name="pRegionSizeY"></param>
public static void CreatePatchFromHeightmap(BitPack output, TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY);
header.QuantWBits = 136;
// If larger than legacy region size, pack patch X and Y info differently.
if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize)
{
header.PatchIDs = (patchY & 0xFFFF);
header.PatchIDs += (patchX << 16);
}
else
{
header.PatchIDs = (patchY & 0x1F);
header.PatchIDs += (patchX << 5);
}
// m_log.DebugFormat("{0} CreatePatchFromHeightmap. patchX={1}, patchY={2}, DCOffset={3}, range={4}",
// LogHeader, patchX, patchY, header.DCOffset, header.Range);
// NOTE: No idea what prequant and postquant should be or what they do
int wbits;
int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits);
wbits = EncodePatchHeader(output, header, patch, (uint)terrData.SizeX, (uint)terrData.SizeY, wbits);
EncodePatch(output, patch, 0, wbits);
}
private static TerrainPatch.Header PrescanPatch(float[] patch)
{
TerrainPatch.Header header = new TerrainPatch.Header();
float zmax = -99999999.0f;
float zmin = 99999999.0f;
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
{
float val = patch[i];
if (val > zmax) zmax = val;
if (val < zmin) zmin = val;
}
header.DCOffset = zmin;
header.Range = (int) ((zmax - zmin) + 1.0f);
return header;
}
// Scan the height info we're returning and return a patch packet header for this patch.
private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY)
{
TerrainPatch.Header header = new TerrainPatch.Header();
float zmax = -99999999.0f;
float zmin = 99999999.0f;
for (int j = patchY*Constants.TerrainPatchSize; j < (patchY + 1)*Constants.TerrainPatchSize; j++)
{
for (int i = patchX*Constants.TerrainPatchSize; i < (patchX + 1)*Constants.TerrainPatchSize; i++)
{
float val = terrData[i, j];
if (val > zmax) zmax = val;
if (val < zmin) zmin = val;
}
}
header.DCOffset = zmin;
header.Range = (int)(zmax - zmin + 1.0f);
return header;
}
public static TerrainPatch.Header DecodePatchHeader(BitPack bitpack)
{
TerrainPatch.Header header = new TerrainPatch.Header {QuantWBits = bitpack.UnpackBits(8)};
// Quantized word bits
if (header.QuantWBits == END_OF_PATCHES)
return header;
// DC offset
header.DCOffset = bitpack.UnpackFloat();
// Range
header.Range = bitpack.UnpackBits(16);
// Patch IDs (10 bits)
header.PatchIDs = bitpack.UnpackBits(10);
// Word bits
header.WordBits = (uint) ((header.QuantWBits & 0x0f) + 2);
return header;
}
private static int EncodePatchHeader(BitPack output, TerrainPatch.Header header, int[] patch, uint pRegionSizeX,
uint pRegionSizeY, int wbits)
{
/*
int temp;
int wbits = (header.QuantWBits & 0x0f) + 2;
uint maxWbits = (uint)wbits + 5;
uint minWbits = ((uint)wbits >> 1);
int wbitsMaxValue;
*/
// goal is to determ minimum number of bits to use so all data fits
/*
wbits = (int)minWbits;
wbitsMaxValue = (1 << wbits);
for (int i = 0; i < patch.Length; i++)
{
temp = patch[i];
if (temp != 0)
{
// Get the absolute value
if (temp < 0) temp *= -1;
no coments..
for (int j = (int)maxWbits; j > (int)minWbits; j--)
{
if ((temp & (1 << j)) != 0)
{
if (j > wbits) wbits = j;
break;
}
}
while (temp > wbitsMaxValue)
{
wbits++;
if (wbits == maxWbits)
goto Done;
wbitsMaxValue = 1 << wbits;
}
}
}
Done:
// wbits += 1;
*/
// better check
if (wbits > 17)
wbits = 16;
else if (wbits < 3)
wbits = 3;
header.QuantWBits &= 0xf0;
header.QuantWBits |= (wbits - 2);
output.PackBits(header.QuantWBits, 8);
output.PackFloat(header.DCOffset);
output.PackBits(header.Range, 16);
if (pRegionSizeX > Constants.RegionSize || pRegionSizeY > Constants.RegionSize)
output.PackBits(header.PatchIDs, 32);
else
output.PackBits(header.PatchIDs, 10);
return wbits;
}
private static void IDCTColumn16(float[] linein, float[] lineout, int column)
{
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
float total = OO_SQRT2*linein[column];
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
int usize = u*Constants.TerrainPatchSize;
total += linein[usize + column]*CosineTable16[usize + n];
}
lineout[Constants.TerrainPatchSize*n + column] = total;
}
}
private static void IDCTLine16(float[] linein, float[] lineout, int line)
{
const float oosob = 2.0f/Constants.TerrainPatchSize;
int lineSize = line*Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
float total = OO_SQRT2*linein[lineSize];
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
total += linein[lineSize + u]*CosineTable16[u*Constants.TerrainPatchSize + n];
}
lineout[lineSize + n] = total*oosob;
}
}
/*
private static void DCTLine16(float[] linein, float[] lineout, int line)
{
float total = 0.0f;
int lineSize = line * Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n];
}
lineout[lineSize] = OO_SQRT2 * total;
int uptr = 0;
for (int u = 1; u < Constants.TerrainPatchSize; u++)
{
total = 0.0f;
uptr += Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n] * CosineTable16[uptr + n];
}
lineout[lineSize + u] = total;
}
}
*/
private static void DCTLine16(float[] linein, float[] lineout, int line)
{
// outputs transpose data (lines exchanged with coluns )
// so to save a bit of cpu when doing coluns
float total = 0.0f;
int lineSize = line*Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[lineSize + n];
}
lineout[line] = OO_SQRT2*total;
for (int u = Constants.TerrainPatchSize;
u < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
u += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int ptrn = lineSize, ptru = u; ptrn < lineSize + Constants.TerrainPatchSize; ptrn++,ptru++)
{
total += linein[ptrn]*CosineTable16[ptru];
}
lineout[line + u] = total;
}
}
/*
private static void DCTColumn16(float[] linein, int[] lineout, int column)
{
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[Constants.TerrainPatchSize * n + column];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * QuantizeTable16[column]);
for (int uptr = Constants.TerrainPatchSize; uptr < Constants.TerrainPatchSize * Constants.TerrainPatchSize; uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[Constants.TerrainPatchSize * n + column] * CosineTable16[uptr + n];
}
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
lineout[CopyMatrix16[uptr + column]] = (int)(total * QuantizeTable16[uptr + column]);
}
}
*/
private static void DCTColumn16(float[] linein, int[] lineout, int column)
{
// input columns are in fact stored in lines now
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
int inlinesptr = Constants.TerrainPatchSize*column;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[inlinesptr + n];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = (int) (OO_SQRT2*total*QuantizeTable16[column]);
for (int uptr = Constants.TerrainPatchSize;
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
{
total += linein[n]*CosineTable16[ptru];
}
// lineout[CopyMatrix16[Constants.TerrainPatchSize * u + column]] = (int)(total * oosob * QuantizeTable16[Constants.TerrainPatchSize * u + column]);
lineout[CopyMatrix16[uptr + column]] = (int) (total*QuantizeTable16[uptr + column]);
}
}
private static int DCTColumn16Wbits(float[] linein, int[] lineout, int column, int wbits, int maxwbits)
{
// input columns are in fact stored in lines now
bool dowbits = wbits != maxwbits;
int wbitsMaxValue = 1 << wbits;
float total = 0.0f;
// const float oosob = 2.0f / Constants.TerrainPatchSize;
int inlinesptr = Constants.TerrainPatchSize*column;
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
total += linein[inlinesptr + n];
}
// lineout[CopyMatrix16[column]] = (int)(OO_SQRT2 * total * oosob * QuantizeTable16[column]);
int tmp = (int) (OO_SQRT2*total*QuantizeTable16[column]);
lineout[CopyMatrix16[column]] = tmp;
if (dowbits)
{
if (tmp < 0) tmp *= -1;
while (tmp > wbitsMaxValue)
{
wbits++;
wbitsMaxValue = 1 << wbits;
if (wbits == maxwbits)
{
dowbits = false;
break;
}
}
}
for (int uptr = Constants.TerrainPatchSize;
uptr < Constants.TerrainPatchSize*Constants.TerrainPatchSize;
uptr += Constants.TerrainPatchSize)
{
total = 0.0f;
for (int n = inlinesptr, ptru = uptr; n < inlinesptr + Constants.TerrainPatchSize; n++, ptru++)
{
total += linein[n]*CosineTable16[ptru];
}
tmp = (int) (total*QuantizeTable16[uptr + column]);
lineout[CopyMatrix16[uptr + column]] = tmp;
if (dowbits)
{
if (tmp < 0) tmp *= -1;
while (tmp > wbitsMaxValue)
{
wbits++;
wbitsMaxValue = 1 << wbits;
if (wbits == maxwbits)
{
dowbits = false;
break;
}
}
}
}
return wbits;
}
public static void DecodePatch(int[] patches, BitPack bitpack, TerrainPatch.Header header, int size)
{
for (int n = 0; n < size*size; n++)
{
// ?
int temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Value or EOB
temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Value
temp = bitpack.UnpackBits(1);
if (temp != 0)
{
// Negative
temp = bitpack.UnpackBits((int) header.WordBits);
patches[n] = temp*-1;
}
else
{
// Positive
temp = bitpack.UnpackBits((int) header.WordBits);
patches[n] = temp;
}
}
else
{
// Set the rest to zero
// TODO: This might not be necessary
for (int o = n; o < size*size; o++)
{
patches[o] = 0;
}
break;
}
}
else
{
patches[n] = 0;
}
}
}
private static void EncodePatch(BitPack output, int[] patch, int postquant, int wbits)
{
int maxwbitssize = (1 << wbits) - 1;
if (postquant > Constants.TerrainPatchSize*Constants.TerrainPatchSize || postquant < 0)
{
Logger.Log("Postquant is outside the range of allowed values in EncodePatch()", Helpers.LogLevel.Error);
return;
}
if (postquant != 0) patch[Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant] = 0;
for (int i = 0; i < Constants.TerrainPatchSize*Constants.TerrainPatchSize; i++)
{
int temp = patch[i];
if (temp == 0)
{
bool eob = true;
for (int j = i; j < Constants.TerrainPatchSize*Constants.TerrainPatchSize - postquant; j++)
{
if (patch[j] != 0)
{
eob = false;
break;
}
}
if (eob)
{
output.PackBits(ZERO_EOB, 2);
return;
}
output.PackBits(ZERO_CODE, 1);
}
else
{
if (temp < 0)
{
temp *= -1;
if (temp > maxwbitssize) temp = maxwbitssize;
output.PackBits(NEGATIVE_VALUE, 3);
output.PackBits(temp, wbits);
}
else
{
if (temp > maxwbitssize) temp = maxwbitssize;
output.PackBits(POSITIVE_VALUE, 3);
output.PackBits(temp, wbits);
}
}
}
}
public static float[] DecompressPatch(int[] patches, TerrainPatch.Header header, TerrainPatch.GroupHeader group)
{
float[] block = new float[group.PatchSize*group.PatchSize];
float[] output = new float[group.PatchSize*group.PatchSize];
int prequant = (header.QuantWBits >> 4) + 2;
int quantize = 1 << prequant;
float ooq = 1.0f/quantize;
float mult = ooq*header.Range;
float addval = mult*(1 << (prequant - 1)) + header.DCOffset;
if (group.PatchSize == Constants.TerrainPatchSize)
{
for (int n = 0; n < Constants.TerrainPatchSize*Constants.TerrainPatchSize; n++)
{
block[n] = patches[CopyMatrix16[n]]*DequantizeTable16[n];
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
for (int o = 0; o < Constants.TerrainPatchSize; o++)
IDCTColumn16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
IDCTLine16(ftemp, block, o);
}
else
{
for (int n = 0; n < Constants.TerrainPatchSize*2*Constants.TerrainPatchSize*2; n++)
{
block[n] = patches[CopyMatrix32[n]]*DequantizeTable32[n];
}
Logger.Log("Implement IDCTPatchLarge", Helpers.LogLevel.Error);
}
for (int j = 0; j < block.Length; j++)
{
output[j] = block[j]*mult + addval;
}
return output;
}
private static int[] CompressPatch(float[] patchData, TerrainPatch.Header header, int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int wordsize = (prequant - 2) & 0x0f;
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
header.QuantWBits = wordsize;
header.QuantWBits |= wordsize << 4;
int k = 0;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
block[k++] = patchData[j*Constants.TerrainPatchSize + i]*premult - sub;
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
private static int[] CompressPatch(float[,] patchData, TerrainPatch.Header header, int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
int wordsize = (prequant - 2) & 0x0f;
header.QuantWBits = wordsize;
header.QuantWBits |= wordsize << 4;
int k = 0;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
block[k++] = patchData[j, i]*premult - sub;
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header,
int prequant, out int wbits)
{
float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int wordsize = prequant;
float oozrange = 1.0f/header.Range;
float range = (1 << prequant);
float premult = oozrange*range;
float sub = (1 << (prequant - 1)) + header.DCOffset*premult;
header.QuantWBits = wordsize - 2;
header.QuantWBits |= (prequant - 2) << 4;
int k = 0;
int yPatchLimit = patchY >= (terrData.SizeY / Constants.TerrainPatchSize) ?
(terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchY;
yPatchLimit = (yPatchLimit + 1) * Constants.TerrainPatchSize;
int xPatchLimit = patchX >= (terrData.SizeX / Constants.TerrainPatchSize) ?
(terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchX;
xPatchLimit = (xPatchLimit + 1) * Constants.TerrainPatchSize;
for (int yy = patchY * Constants.TerrainPatchSize; yy < yPatchLimit; yy++)
{
for (int xx = patchX * Constants.TerrainPatchSize; xx < xPatchLimit; xx++)
{
block[k++] = terrData[xx, yy] * premult - sub;
}
}
float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize];
int maxWbits = prequant + 5;
wbits = (prequant >> 1);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
DCTLine16(block, ftemp, o);
for (int o = 0; o < Constants.TerrainPatchSize; o++)
wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits);
return itemp;
}
#region Initialization
private static void BuildDequantizeTable16()
{
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
{
DequantizeTable16[j*Constants.TerrainPatchSize + i] = 1.0f + 2.0f*(i + j);
}
}
}
private static void BuildQuantizeTable16()
{
const float oosob = 2.0f/Constants.TerrainPatchSize;
for (int j = 0; j < Constants.TerrainPatchSize; j++)
{
for (int i = 0; i < Constants.TerrainPatchSize; i++)
{
// QuantizeTable16[j * Constants.TerrainPatchSize + i] = 1.0f / (1.0f + 2.0f * ((float)i + (float)j));
QuantizeTable16[j*Constants.TerrainPatchSize + i] = oosob/(1.0f + 2.0f*(i + (float) j));
}
}
}
private static void SetupCosines16()
{
const float hposz = (float) Math.PI*0.5f/Constants.TerrainPatchSize;
for (int u = 0; u < Constants.TerrainPatchSize; u++)
{
for (int n = 0; n < Constants.TerrainPatchSize; n++)
{
CosineTable16[u*Constants.TerrainPatchSize + n] = (float) Math.Cos((2.0f*n + 1.0f)*u*hposz);
}
}
}
private static void BuildCopyMatrix16()
{
bool diag = false;
bool right = true;
int i = 0;
int j = 0;
int count = 0;
while (i < Constants.TerrainPatchSize && j < Constants.TerrainPatchSize)
{
CopyMatrix16[j*Constants.TerrainPatchSize + i] = count++;
if (!diag)
{
if (right)
{
if (i < Constants.TerrainPatchSize - 1) i++;
else j++;
right = false;
diag = true;
}
else
{
if (j < Constants.TerrainPatchSize - 1) j++;
else i++;
right = true;
diag = true;
}
}
else
{
if (right)
{
i++;
j--;
if (i == Constants.TerrainPatchSize - 1 || j == 0) diag = false;
}
else
{
i--;
j++;
if (j == Constants.TerrainPatchSize - 1 || i == 0) diag = false;
}
}
}
}
#endregion Initialization
}
}

View File

@@ -161,7 +161,7 @@ namespace OpenSim.Region.Framework.Scenes
{
// Get the prim's default texture. This will be used for faces which don't have their own texture
if (textureEntry.DefaultTexture != null)
assetUuids[textureEntry.DefaultTexture.TextureID] = (sbyte)AssetType.Texture;
GatherTextureEntryAssets(textureEntry.DefaultTexture, assetUuids);
if (textureEntry.FaceTextures != null)
{
@@ -169,7 +169,7 @@ namespace OpenSim.Region.Framework.Scenes
foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures)
{
if (texture != null)
assetUuids[texture.TextureID] = (sbyte)AssetType.Texture;
GatherTextureEntryAssets(texture, assetUuids);
}
}
}
@@ -233,6 +233,19 @@ namespace OpenSim.Region.Framework.Scenes
}
}
/// <summary>
/// Gather all the asset uuids found in one face of a Texture Entry.
/// </summary>
private void GatherTextureEntryAssets(Primitive.TextureEntryFace texture, IDictionary<UUID, sbyte> assetUuids)
{
assetUuids[texture.TextureID] = (sbyte)AssetType.Texture;
if (texture.MaterialID != UUID.Zero)
{
GatherAssetUuids(texture.MaterialID, (sbyte)OpenSimAssetType.Material, assetUuids);
}
}
// /// <summary>
// /// The callback made when we request the asset for an object from the asset service.
// /// </summary>

View File

@@ -516,7 +516,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
public Vector3 StartPos
{
get { return new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 50); }
get { return new Vector3(m_scene.RegionInfo.RegionSizeX * 0.5f, m_scene.RegionInfo.RegionSizeY * 0.5f, 50f); }
set { }
}

View File

@@ -52,6 +52,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
// Local constants
// This computation is not the real region center if the region is larger than 256.
// This computation isn't fixed because there is not a handle back to the region.
private static readonly Vector3 CenterOfRegion = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
private static readonly char[] CS_SPACE = { ' ' };

View File

@@ -44,6 +44,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// This computation is not the real region center if the region is larger than 256.
// This computation isn't fixed because there is not a handle back to the region.
private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
private const int DEBUG_CHANNEL = 2147483647;

View File

@@ -1012,7 +1012,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
Hashtable respData = (Hashtable)resp.Value;
if (respData.Contains("error") && !respData.Contains("succeed"))
{
LogRespDataToConsoleError(respData);
LogRespDataToConsoleError(requestingAgentID, function, param, respData);
}
return respData;
@@ -1040,20 +1040,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return error;
}
private void LogRespDataToConsoleError(Hashtable respData)
private void LogRespDataToConsoleError(UUID requestingAgentID, string function, Hashtable param, Hashtable respData)
{
m_log.Error("[XMLRPC-GROUPS-CONNECTOR]: Error:");
foreach (string key in respData.Keys)
{
m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Key: {0}", key);
string[] lines = respData[key].ToString().Split(new char[] { '\n' });
foreach (string line in lines)
{
m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}", line);
}
}
m_log.ErrorFormat(
"[XMLRPC-GROUPS-CONNECTOR]: Error when calling {0} for {1} with params {2}. Response params are {3}",
function, requestingAgentID, Util.PrettyFormatToSingleLine(param), Util.PrettyFormatToSingleLine(respData));
}
/// <summary>

View File

@@ -314,6 +314,19 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
return m_store.TestStore(storeID) ? 1 : 0;
}
// -----------------------------------------------------------------
/// <summary>
///
/// </summary>
// -----------------------------------------------------------------
[ScriptInvocation]
public UUID JsonRezAtRoot(UUID hostID, UUID scriptID, string item, Vector3 pos, Vector3 vel, Quaternion rot, string param)
{
UUID reqID = UUID.Random();
Util.FireAndForget(o => DoJsonRezObject(hostID, scriptID, reqID, item, pos, vel, rot, param));
return reqID;
}
// -----------------------------------------------------------------
/// <summary>
///
@@ -682,5 +695,103 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
return path;
}
// -----------------------------------------------------------------
/// <summary>
///
/// </summary>
// -----------------------------------------------------------------
private void DoJsonRezObject(UUID hostID, UUID scriptID, UUID reqID, string name, Vector3 pos, Vector3 vel, Quaternion rot, string param)
{
if (Double.IsNaN(rot.X) || Double.IsNaN(rot.Y) || Double.IsNaN(rot.Z) || Double.IsNaN(rot.W))
{
GenerateRuntimeError("Invalid rez rotation");
return;
}
SceneObjectGroup host = m_scene.GetSceneObjectGroup(hostID);
if (host == null)
{
GenerateRuntimeError(String.Format("Unable to find rezzing host '{0}'",hostID));
return;
}
// hpos = host.RootPart.GetWorldPosition()
// float dist = (float)llVecDist(hpos, pos);
// if (dist > m_ScriptDistanceFactor * 10.0f)
// return;
TaskInventoryItem item = host.RootPart.Inventory.GetInventoryItem(name);
if (item == null)
{
GenerateRuntimeError(String.Format("Unable to find object to rez '{0}'",name));
return;
}
if (item.InvType != (int)InventoryType.Object)
{
GenerateRuntimeError("Can't create requested object; object is missing from database");
return;
}
List<SceneObjectGroup> objlist;
List<Vector3> veclist;
bool success = host.RootPart.Inventory.GetRezReadySceneObjects(item, out objlist, out veclist);
if (! success)
{
GenerateRuntimeError("Failed to create object");
return;
}
int totalPrims = 0;
foreach (SceneObjectGroup group in objlist)
totalPrims += group.PrimCount;
if (! m_scene.Permissions.CanRezObject(totalPrims, item.OwnerID, pos))
{
GenerateRuntimeError("Not allowed to create the object");
return;
}
if (! m_scene.Permissions.BypassPermissions())
{
if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0)
host.RootPart.Inventory.RemoveInventoryItem(item.ItemID);
}
for (int i = 0; i < objlist.Count; i++)
{
SceneObjectGroup group = objlist[i];
Vector3 curpos = pos + veclist[i];
if (group.IsAttachment == false && group.RootPart.Shape.State != 0)
{
group.RootPart.AttachedPos = group.AbsolutePosition;
group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint;
}
group.FromPartID = host.RootPart.UUID;
m_scene.AddNewSceneObject(group, true, curpos, rot, vel);
UUID storeID = group.UUID;
if (! m_store.CreateStore(param, ref storeID))
{
GenerateRuntimeError("Unable to create jsonstore for new object");
continue;
}
// We can only call this after adding the scene object, since the scene object references the scene
// to find out if scripts should be activated at all.
group.RootPart.SetDieAtEdge(true);
group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 3);
group.ResumeScripts();
group.ScheduleGroupForFullUpdate();
// send the reply back to the host object, use the integer param to indicate the number
// of remaining objects
m_comms.DispatchReply(scriptID, objlist.Count-i-1, group.RootPart.UUID.ToString(), reqID.ToString());
}
}
}
}

View File

@@ -170,7 +170,10 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
c.Channel = m_channelNotify;
c.Message += numScriptsFailed.ToString() + "," + message;
c.Type = ChatTypeEnum.Region;
c.Position = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30);
if (m_scene != null)
c.Position = new Vector3((m_scene.RegionInfo.RegionSizeX * 0.5f), (m_scene.RegionInfo.RegionSizeY * 0.5f), 30);
else
c.Position = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30);
c.Sender = null;
c.SenderUUID = UUID.Zero;
c.Scene = m_scene;
@@ -301,7 +304,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
finally
{
if (os != null)
os.Close();
os.Dispose();
}
}
}

View File

@@ -61,7 +61,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
private readonly string m_firstname;
private readonly string m_lastname;
private readonly Vector3 m_startPos;
private readonly UUID m_uuid = UUID.Random();
private readonly UUID m_uuid;
private readonly Scene m_scene;
private readonly UUID m_ownerID;
@@ -71,6 +71,19 @@ namespace OpenSim.Region.OptionalModules.World.NPC
m_firstname = firstname;
m_lastname = lastname;
m_startPos = position;
m_uuid = UUID.Random();
m_scene = scene;
m_ownerID = ownerID;
SenseAsAgent = senseAsAgent;
}
public NPCAvatar(
string firstname, string lastname, UUID agentID, Vector3 position, UUID ownerID, bool senseAsAgent, Scene scene)
{
m_firstname = firstname;
m_lastname = lastname;
m_startPos = position;
m_uuid = agentID;
m_scene = scene;
m_ownerID = ownerID;
SenseAsAgent = senseAsAgent;

View File

@@ -140,8 +140,30 @@ namespace OpenSim.Region.OptionalModules.World.NPC
Vector3 position, UUID owner, bool senseAsAgent, Scene scene,
AvatarAppearance appearance)
{
NPCAvatar npcAvatar = new NPCAvatar(firstname, lastname, position,
owner, senseAsAgent, scene);
return CreateNPC(firstname, lastname, position, UUID.Zero, owner, senseAsAgent, scene, appearance);
}
public UUID CreateNPC(string firstname, string lastname,
Vector3 position, UUID agentID, UUID owner, bool senseAsAgent, Scene scene,
AvatarAppearance appearance)
{
NPCAvatar npcAvatar = null;
try
{
if (agentID == UUID.Zero)
npcAvatar = new NPCAvatar(firstname, lastname, position,
owner, senseAsAgent, scene);
else
npcAvatar = new NPCAvatar(firstname, lastname, agentID, position,
owner, senseAsAgent, scene);
}
catch (Exception e)
{
m_log.Info("[NPC MODULE]: exception creating NPC avatar: " + e.ToString());
return UUID.Zero;
}
npcAvatar.CircuitCode = (uint)Util.RandomClass.Next(0,
int.MaxValue);

View File

@@ -116,37 +116,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
+ "If teleport is true then some extra teleport debug information is logged.\n"
+ "If updates is true then any frame which exceeds double the maximum desired frame time is logged.",
HandleDebugSceneSetCommand);
scene.AddCommand(
"Regions",
this, "show borders", "show borders", "Show border information for regions", HandleShowBordersCommand);
}
private void HandleShowBordersCommand(string module, string[] args)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Borders for {0}:\n", m_scene.Name);
ConsoleDisplayTable cdt = new ConsoleDisplayTable();
cdt.AddColumn("Cross Direction", 15);
cdt.AddColumn("Line", 34);
cdt.AddColumn("Trigger Region", 14);
foreach (Border b in m_scene.NorthBorders)
cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY));
foreach (Border b in m_scene.EastBorders)
cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY));
foreach (Border b in m_scene.SouthBorders)
cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY));
foreach (Border b in m_scene.WestBorders)
cdt.AddRow(b.CrossDirection, b.BorderLine, string.Format("{0}, {1}", b.TriggerRegionX, b.TriggerRegionY));
cdt.AddToStringBuilder(sb);
MainConsole.Instance.Output(sb.ToString());
}
private void HandleDebugSceneGetCommand(string module, string[] args)

View File

@@ -748,8 +748,8 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
position.X = s_tree.AbsolutePosition.X + (float)randX;
position.Y = s_tree.AbsolutePosition.Y + (float)randY;
if (position.X <= ((int)Constants.RegionSize - 1) && position.X >= 0 &&
position.Y <= ((int)Constants.RegionSize - 1) && position.Y >= 0 &&
if (position.X <= (m_scene.RegionInfo.RegionSizeX - 1) && position.X >= 0 &&
position.Y <= (m_scene.RegionInfo.RegionSizeY - 1) && position.Y >= 0 &&
Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range)
{
UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;

View File

@@ -1311,7 +1311,7 @@ private sealed class BulletConstraintXNA : BulletConstraint
/* TODO */
ConfigurationParameters[] configparms = new ConfigurationParameters[1];
configparms[0] = parms;
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
Vector3 worldExtent = maxPosition;
m_maxCollisions = maxCollisions;
m_maxUpdatesPerFrame = maxUpdates;
specialCollisionObjects = new Dictionary<uint, GhostObject>();

View File

@@ -1011,8 +1011,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}",
ControllingPrim.LocalID, origVelW, newVelocityLengthSq, BSParam.VehicleMaxLinearVelocitySquared, VehicleVelocity);
}
else if (newVelocityLengthSq < 0.001f)
else if (newVelocityLengthSq < BSParam.VehicleMinLinearVelocitySquared)
{
Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG
VDetailLog("{0}, MoveLinear,clampMin,origVelW={1},lenSq={2}",
ControllingPrim.LocalID, origVelW, newVelocityLengthSq);
VehicleVelocity = Vector3.Zero;
}
VDetailLog("{0}, MoveLinear,done,isColl={1},newVel={2}", ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, VehicleVelocity );

View File

@@ -147,6 +147,8 @@ public static class BSParam
// Vehicle parameters
public static float VehicleMaxLinearVelocity { get; private set; }
public static float VehicleMaxLinearVelocitySquared { get; private set; }
public static float VehicleMinLinearVelocity { get; private set; }
public static float VehicleMinLinearVelocitySquared { get; private set; }
public static float VehicleMaxAngularVelocity { get; private set; }
public static float VehicleMaxAngularVelocitySq { get; private set; }
public static float VehicleAngularDamping { get; private set; }
@@ -538,7 +540,7 @@ public static class BSParam
(s,o) => { s.PE.SetContactProcessingThreshold(o.PhysBody, ContactProcessingThreshold); } ),
new ParameterDefn<float>("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
(float)BSTerrainPhys.TerrainImplementation.Mesh ),
(float)BSTerrainPhys.TerrainImplementation.Heightmap ),
new ParameterDefn<int>("TerrainMeshMagnification", "Number of times the 256x256 heightmap is multiplied to create the terrain mesh" ,
2 ),
new ParameterDefn<float>("TerrainFriction", "Factor to reduce movement against terrain surface" ,
@@ -598,6 +600,10 @@ public static class BSParam
1000.0f,
(s) => { return (float)VehicleMaxLinearVelocity; },
(s,v) => { VehicleMaxLinearVelocity = v; VehicleMaxLinearVelocitySquared = v * v; } ),
new ParameterDefn<float>("VehicleMinLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle",
0.001f,
(s) => { return (float)VehicleMinLinearVelocity; },
(s,v) => { VehicleMinLinearVelocity = v; VehicleMinLinearVelocitySquared = v * v; } ),
new ParameterDefn<float>("VehicleMaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to a vehicle",
12.0f,
(s) => { return (float)VehicleMaxAngularVelocity; },

View File

@@ -376,18 +376,19 @@ public class BSPrim : BSPhysObject
{
bool ret = false;
uint wayOutThere = Constants.RegionSize * Constants.RegionSize;
int wayOverThere = -1000;
int wayOutThere = 10000;
// There have been instances of objects getting thrown way out of bounds and crashing
// the border crossing code.
if ( RawPosition.X < -Constants.RegionSize || RawPosition.X > wayOutThere
|| RawPosition.Y < -Constants.RegionSize || RawPosition.Y > wayOutThere
|| RawPosition.Z < -Constants.RegionSize || RawPosition.Z > wayOutThere)
if ( RawPosition.X < wayOverThere || RawPosition.X > wayOutThere
|| RawPosition.Y < wayOverThere || RawPosition.X > wayOutThere
|| RawPosition.Z < wayOverThere || RawPosition.X > wayOutThere)
{
RawPosition = new OMV.Vector3(10, 10, 50);
ZeroMotion(inTaintTime);
ret = true;
}
if (RawVelocity.LengthSquared() > BSParam.MaxLinearVelocity)
if (RawVelocity.LengthSquared() > BSParam.MaxLinearVelocitySquared)
{
RawVelocity = Util.ClampV(RawVelocity, BSParam.MaxLinearVelocity);
ret = true;

View File

@@ -208,7 +208,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
Name = EngineType + "/" + RegionName;
}
// Old version of initialization that assumes legacy sized regions (256x256)
public override void Initialise(IMesher meshmerizer, IConfigSource config)
{
m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up.");
Vector3 regionExtent = new Vector3( Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
Initialise(meshmerizer, config, regionExtent);
}
public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent)
{
mesher = meshmerizer;
_taintOperations = new List<TaintCallbackEntry>();
@@ -226,6 +235,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// Set default values for physics parameters plus any overrides from the ini file
GetInitialParameterValues(config);
// Force some parameters to values depending on other configurations
// Only use heightmap terrain implementation if terrain larger than legacy size
if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
{
m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
}
// Get the connection to the physics engine (could be native or one of many DLLs)
PE = SelectUnderlyingBulletEngine(BulletEngineName);
@@ -250,13 +267,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// a child in a mega-region.
// Bullet actually doesn't care about the extents of the simulated
// area. It tracks active objects no matter where they are.
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
Vector3 worldExtent = regionExtent;
World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);
Constraints = new BSConstraintCollection(World);
TerrainManager = new BSTerrainManager(this);
TerrainManager = new BSTerrainManager(this, worldExtent);
TerrainManager.CreateInitialGroundPlaneAndTerrain();
// Put some informational messages into the log file.

View File

@@ -58,7 +58,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
{
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
}
m_mapInfo = new BulletHMapInfo(id, initialMap);
m_mapInfo = new BulletHMapInfo(id, initialMap, regionSize.X, regionSize.Y);
m_mapInfo.minCoords = minTerrainCoords;
m_mapInfo.maxCoords = maxTerrainCoords;
m_mapInfo.terrainRegionBase = TerrainBase;
@@ -72,7 +72,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id)
{
m_mapInfo = new BulletHMapInfo(id, initialMap);
m_mapInfo = new BulletHMapInfo(id, initialMap, maxCoords.X - minCoords.X, maxCoords.Y - minCoords.Y);
m_mapInfo.minCoords = minCoords;
m_mapInfo.maxCoords = maxCoords;
m_mapInfo.minZ = minCoords.Z;

Some files were not shown because too many files have changed in this diff Show More