Compare commits

...

106 Commits

Author SHA1 Message Date
Justin Clark-Casey (justincc) 28e68329b6 Change 0.7.5-post-fixes branch back to post-fixes flavour 2014-11-11 20:43:32 +00:00
Justin Clark-Casey (justincc) 0d88c3d4dc Change version to 0.7.5.2 and flavour to release 2014-11-11 17:39:55 +00:00
Justin Clark-Casey (justincc) 0387725fe2 Fix issue where llRemoteLoadScriptPin() would treat 0 (the default) as a valid set pin in a destination prim rather than the unset no pin state
Adds regression test for this case.
2014-11-11 17:19:16 +00:00
Justin Clark-Casey (justincc) c97f16a89b Change 0.7.5-post-fixes branch release flavour back to Post_Fixes 2014-02-01 00:16:32 +00:00
Justin Clark-Casey (justincc) a29c2fe8fe Change flavour of 0.7.5.1 to Release 2014-01-31 23:20:14 +00:00
Justin Clark-Casey (justincc) 9760cd47f1 Change release verson to 0.7.5.1 and flavour to rc1 2014-01-24 23:00:10 +00:00
Justin Clark-Casey (justincc) 40cee9b836 Fix false positive test failure in TestRepeatSameDrawContainingImageReusingTexture() and TestRepeatSameDrawContainingImage() if localhost has a webserver set up.
Use 0.0.0.0 instead of localhost
2014-01-24 22:57:13 +00:00
Melanie 12c2b3e983 Dynamically adjust to the number of visual params sent. 2014-01-24 18:27:37 +00:00
Kevin Cozens bfaa24f02d Added support for attachments to group notices when using Flotsam groups. 2013-10-15 23:10:52 +01:00
Diva Canto 98b65bd5f4 Merge branch '0.7.5-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.5-post-fixes 2013-07-26 16:00:16 -07:00
Diva Canto 0e772c6516 Don't touch the Current Outfit folder also on coming back home 2013-07-26 15:54:54 -07:00
Diva Canto 5be83d47be In renaming the folders for hypergriding, don't rename the Current Outfit folder. The viewer doesn't like that. 2013-07-26 15:54:36 -07:00
Diva Canto 028947f7b8 Better version of previous commit 2013-07-26 15:53:34 -07:00
Diva Canto 5e094bcd46 Add the Current Outfit folder as an available folder in the SuitcaseInventory. 2013-07-26 15:53:21 -07:00
Justin Clark-Casey (justincc) 2c54659ca8 Revert "Committing the same fix here for the infinity problem on TPs."
This reverts commit f5343e4b07.

This is because a returning viewer by teleport before 15 seconds are up will be disrupted by the close and so is a significant behaviour change
Development along the longer delay lines will continue in dev code
2013-07-26 01:44:48 +01:00
Diva Canto f5343e4b07 Committing the same fix here for the infinity problem on TPs. 2013-07-24 16:44:12 -07:00
Robert Adams 764f6dd5e9 BulletSim: remove unused reference to BulletXNA in prebuild.xml. 2013-07-08 12:39:02 -07:00
Robert Adams 5444b3b1a5 BulletSim: fix problem with walking up stairs that are oriented
in certain directions. The problem was really that the avatar capsule
orientation was being set incorrectly.
2013-05-22 16:14:05 -07:00
Robert Adams cfbb4f52e4 Eliminate race condition where SimStatsReporter starts reporting
stats before the region is completely initialized.
2013-05-14 18:45:27 -07:00
Vegaslon 00d125dada BulletSim: Fix for mantis 6487, also minor adjustment to fix flying while you are running.
Signed-off-by: Robert Adams <Robert.Adams@intel.com>
2013-05-14 18:45:17 -07:00
Robert Adams 5b3e7156a3 BulletSim: add a lock to try and catch a native shape creation/destruction
race condition.
2013-05-14 18:45:06 -07:00
Robert Adams 783e8e4bab BulletSim: add adjustment for avatar capsule height scaling. Makes
avatar standing on ground view better and enables tuning.
2013-05-14 18:44:53 -07:00
Robert Adams 921f911b21 vh: remove unused and error generating BulletXNA DLLs 2013-05-12 20:06:30 -07:00
Robert Adams bd9b5927d8 BulletSim: fix CPU loop that occurs when any 'degenerate' sculptie
is in a region. This fixes the high CPU usage for regions with nothing
else going on.
2013-05-11 16:13:54 -07:00
Robert Adams d48c9b433a vh: Update physics parameter get/set to string based from old float
value based.
2013-05-09 22:16:32 -07:00
Robert Adams 43f3459c3a vh: add material physical property definitions to PhysicsActor 2013-05-09 22:16:26 -07:00
Robert Adams e8c01eff40 vh: make a vertex fetching method in Mesher public for BulletSim reference. 2013-05-09 22:14:32 -07:00
Robert Adams 10a1a6ad3c vh: Remove BulletXNA from sources. 2013-05-09 22:13:39 -07:00
Robert Adams 90bdb36e89 vh: add BulletSPlugin/Tests/* 2013-05-08 06:05:28 -07:00
Robert Adams eb0687f5af vh: update BulletSim (OpenSim/Region/Physics/BulletSPlugin
and DLL/SO) to ac6dcd35fb
(Mon May 6 21:10:02 2013 -0400) on top of 0.7.5-postfixes.
2013-05-08 06:02:12 -07:00
Justin Clark-Casey (justincc) aeb5ccaa0a Add regression test for inventory item give, reject and subsequent trash folder purge by receiver.
This commit also actually adds the InventoryTransferModuleTests file which I previously forgot
2013-05-03 22:47:52 +01:00
Justin Clark-Casey (justincc) 6b06157126 Add regression test for offer, accept and subsequent receiver delete of an item offered via instant message. 2013-05-03 22:41:14 +01:00
Justin Clark-Casey (justincc) 326522dc3d Fix bug where an agent that declined an inventory offer and subsequently emptied their trash would make the item invalid in the giver's inventory
This was because the original item/folder ID was sent in the session slot of the offer IM rather than the copy.
2013-05-03 22:41:09 +01:00
Justin Clark-Casey (justincc) 40ed5433bd Fix issue in the mesh upload flag module where the ID of the last agent to request the capability was always used instead of the original requesting agent for each cap.
Should address http://opensimulator.org/mantis/view.php?id=6556
2013-04-23 22:31:53 +01:00
Justin Clark-Casey (justincc) a6722abbed Merge branch '0.7.5-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.5-post-fixes 2013-03-29 23:39:51 +00:00
Kevin Cozens 8b1ca7b35e Added missing functionality (mainly custom headers) to llHTTPRequest. 2013-03-29 23:35:12 +00:00
Diva Canto 00904a0b56 Bug fix in DataSnapshot, where a var was being used before being initialized. 2013-02-28 14:29:06 -08:00
Diva Canto a830e09bad Replaced Ionic.Zip.dll with a new one that fixes a bug in it. DotNetZip (from which Ionic.Zip.dll is derived) is now a fork in opensim-libs, forked from 1.9.1.8 and added that simple bug fix. 2013-02-12 14:48:16 -08:00
Justin Clark-Casey (justincc) 685a3c3c73 Change 0.7.5 flavour to post-fixes 2013-02-09 00:29:45 +00:00
Justin Clark-Casey (justincc) a2d327b70b Merge branch '0.7.5-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.5-post-fixes 2013-02-08 21:43:42 +00:00
Justin Clark-Casey (justincc) 30638317ab Bump 0.7.5 flavour to Release 2013-02-08 21:37:14 +00:00
Justin Clark-Casey (justincc) 689da0f14d If a component of a coalesced object fails to deserialization, do not add a null where the object should be.
This prevents a later load IAR failure.
This code is currently only used by IAR loading.
2013-02-08 21:33:08 +00:00
Justin Clark-Casey (justincc) 6d3b2d2cf7 On IAR loading, if loading of a coaleseced item entirely fails, then continue with the IAR load rather than failing completely. 2013-02-08 21:32:59 +00:00
teravus 78debc981f * the root prim was being given an OffsetPosition in addition to setting the position when creating the root prim. The offset position caused the positioning code to re-move the root prim when you selected it and released it. 2013-02-08 21:31:51 +00:00
Melanie e3f01f23af Try to fix uploaded mesh rotations - code from Avination code base. 2013-02-08 21:31:38 +00:00
Dan Lake f28a592310 Added option for UUID as command parameters. This lets the command handle the UUID parsing and type checking before the command is executed. 2013-02-08 21:30:41 +00:00
Justin Clark-Casey (justincc) 4c4cc917b4 minor: Tidy up disabled logging on AssetTransactionModule for future use. Make it clear that transaction parameter to HandleUDPUploadRequest is an ID. 2013-02-08 21:29:56 +00:00
Justin Clark-Casey (justincc) 430000ad24 minor: remove some mono compile warnings in XEngine.cs 2013-02-08 21:29:43 +00:00
Melanie eab57cdd13 Prevent items being destroyed by rename operations. Renaming of a wearable also
sends an asset transaciton but it is empty. So we can't ignore name data
when a transaction is present and can't treat every transaction as valid.

Conflicts:

	OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
2013-02-08 21:29:15 +00:00
Justin Clark-Casey (justincc) 5b60f632c9 Fix issue where the "set terrain texture" console command did not tell the viewers that textures had updated (hence they did not display the changes).
Addresses http://opensimulator.org/mantis/view.php?id=6513
2013-02-08 21:29:01 +00:00
Talun d1320993a4 Mantis 6343: Turn a prim to flexy to OFF don't work llSetPrimParams
Correction so that scripts can turn Flexi off as well as on.
2013-02-08 21:28:48 +00:00
Oren Hurvitz 319069d193 Assign the SmartThreadPool name in the constructor
This is required because some threads are created in the constructor, so assigning the name afterwards would be too late.
2013-02-08 21:26:05 +00:00
Justin Clark-Casey (justincc) 82268d715e Explicitly stop PollServiceRequestManager() rather than relying on its destructor.
Hopes to address occasional shutdown failures from http://opensimulator.org/mantis/view.php?id=6503
2013-02-08 21:25:57 +00:00
Talun efcbae372b Mantis 6507 keys returned by llGetAgentList incorrect for llList2Key
The type of the keys returned by llGetAgentList corrected to LSL_Key
2013-02-08 21:25:47 +00:00
Diva Canto 6e5d352bf6 Changed protection of CreateDefaultAppearanceEntries to protected, so extensions of the UserAccountService can reuse this. 2013-02-05 16:29:25 -08:00
Justin Clark-Casey (justincc) 8f32e16497 minor: Fix full scene part console report to show proper Light* names rather than all wrongly FlexiDrag 2013-01-26 00:33:28 +00:00
Justin Clark-Casey (justincc) c42e2c6906 minor: Call down to base OpenSimTestCase.SetUp() in NPCModuleTests to disable any enabled logging from previous tests 2013-01-26 00:33:19 +00:00
Justin Clark-Casey (justincc) 6bd57f2455 Bump version up to 0.7.5-rc2 2013-01-26 00:09:43 +00:00
Justin Clark-Casey (justincc) af5a3f2d73 Restore previous client AO behaviour by not allowing them to remove the default animation but continue to allow scripts to do so.
This keeps the fix from http://opensimulator.org/mantis/view.php?id=6327
and fixes the behaviour regression in http://opensimulator.org/mantis/view.php?id=6483
Animations may still exhibit different behaviour if both scripts and clients are adjusting animations.
A change in the behaviour of client AO to not remove all animations may be a better long term approach.
2013-01-25 23:54:53 +00:00
Justin Clark-Casey (justincc) 0ba01ce699 Check the existing ScenePresence.ParentPart to make sure we're not trying to sit on a prim we're already sat upon, rather than looking up the part from scratch.
An adaptation of commit 055b8a2
Having both ParentID and ParentPart references now is redundant.  ParentID should probably be eliminated.
2013-01-25 23:53:16 +00:00
Talun 70e1dd54fa New constants for llGetObjectDetails
New constants for llGetObjectDetails OBJECT_CHARACTER_TIME,
OBJECT_ROOT, OBJECT_ATTACHED_POINT, OBJECT_PATHFINDING_TYPE,
OBJECT_PHYSICS, OBJECT_PHANTOM and OBJECT_TEMP_ON_REZ
also Pathfining constants, 3 of which are used by llGetObjectDetails
2013-01-25 23:52:55 +00:00
Justin Clark-Casey (justincc) c42bff5388 Fix use of scene debug commands when region is set to root or a specific region where there is more than one region on the simulator. 2013-01-25 23:52:36 +00:00
Justin Clark-Casey (justincc) 10b3c6b5aa Add "debug set set animations true|false" region console command.
Setting this logs extra information about animation add/remove, such as uuid and animation name
Unfortunately cannot be done per client yet
2013-01-25 23:52:25 +00:00
Robert Adams 8ecf6ed08e Add utility function to clamp a vector to a maximum magnitude. 2013-01-25 23:51:50 +00:00
Diva Canto 09450448b0 Changed a couple of debug messages at the request of osgrid. 2013-01-25 23:51:36 +00:00
dahlia e2823bfefe move resit fix to ScenePresence.cs and allow for requesting sit on objects other than the object currently sat on 2013-01-25 23:51:17 +00:00
Justin Clark-Casey (justincc) 795009cae2 Print full stacktrace from plugin loading failure to help determine what went wrong, rather than a possibly unhelpful simple exception message. 2013-01-25 23:51:09 +00:00
Robert Adams cc8fbf76e6 Fix exception reporting in SceneObjectPart so it logs what the exception is rather than just saying it happened. 2013-01-25 23:50:57 +00:00
dahlia af27180131 add some sanity checking to HandleAgentRequestSit handler 2013-01-25 23:50:48 +00:00
Justin Clark-Casey (justincc) c182157d4d Fix a regression in the last few scene commands changes where setting these via the viewer estate dialog stopped working.
Forgot to register the new interface.
Also removes some code which got included by adpating an existing module.
2013-01-25 23:50:29 +00:00
Justin Clark-Casey (justincc) 1cda887d9b Add "debug scene get" console command to list current scene options 2013-01-25 23:50:22 +00:00
Justin Clark-Casey (justincc) 0ba0ac4669 Move scene debug commands into separate module. Command changes from "debug scene <key> <value>" to "debug scene set <key> <value>" to accomodate future settings 2013-01-25 23:50:15 +00:00
Justin Clark-Casey (justincc) ce8b30d868 minor: Capitalize GroupsModule command category 2013-01-25 23:49:47 +00:00
Justin Clark-Casey (justincc) d4c4cb3e67 minor: add missing newline to "debug scene" console command 2013-01-25 23:49:40 +00:00
Justin Clark-Casey (justincc) ac4300664e Remove unimplemented "debug teleport" console command 2013-01-25 23:49:34 +00:00
Justin Clark-Casey (justincc) 069e2d6443 Add "debug scene pbackup true|false" console command. This enables or disable periodic scene backup. For debug purposes.
If false, scene is still saved on shutdown.
2013-01-25 23:49:26 +00:00
Justin Clark-Casey (justincc) 30eded2bda revert accidental change to MemoryWatchdog stat calculation in previous b1b4687 2013-01-25 23:49:14 +00:00
Justin Clark-Casey (justincc) 2563553f80 Add "show script timers" command to show script timers. For debug purposes.
Also, "show sensors" changes to "show script sensors".
2013-01-25 23:49:06 +00:00
Justin Clark-Casey (justincc) 9ddddde42e Add "show sensors" command to show script sensor information for debug purposes. 2013-01-25 23:48:59 +00:00
Justin Clark-Casey (justincc) 99a5ea9f90 minor: Remove unnecessary commented out code from last commit c28a2f05 and fix up code comment 2013-01-25 23:48:52 +00:00
Justin Clark-Casey (justincc) 880b2361f5 minor: make spacing consistent in console help output 2013-01-25 23:48:42 +00:00
Justin Clark-Casey (justincc) f47f3dd8b9 minor: Fix command match of "debug script" command to "debug scripts" to match other scripts commands (and it's own short help text) 2013-01-25 23:48:31 +00:00
Justin Clark-Casey (justincc) 8ee078c86b minor: Allow "script *" console commands to take multiple script item ids 2013-01-25 23:48:20 +00:00
Melanie 17d54a7c37 Add the new UpdateAgentInformation cap to make maturity on more recent viewers
work.
2013-01-25 23:47:58 +00:00
Justin Clark-Casey (justincc) 6f928f52dd Flip version to 0.7.5.RC1 2013-01-04 21:59:49 +00:00
Justin Clark-Casey (justincc) 7199ac09ee Set default particle burst count to 1 instead of 0 in any set particle system script call that does not have an empty list.
As per http://opensimulator.org/mantis/view.php?id=6353
2013-01-04 21:50:38 +00:00
Justin Clark-Casey (justincc) 59263fe78e Fix build break caused by missing ) from dce2809.
Was hand-typing in a line of code I had tested before but not retested this time
2013-01-04 21:50:28 +00:00
Justin Clark-Casey (justincc) 3c631fea9e Automatically grant sit-related llRequestPermissions() for subsequent avatars sitting on the same scene obejct, instead of wrongly popping up request permissions dialog.
Resolves http://opensimulator.org/mantis/view.php?id=6478
2013-01-04 21:50:22 +00:00
Justin Clark-Casey (justincc) ebfcb9d4e4 refactor: simplify llGetNumberOfPrims() to return prim count + sitting avatar count rather than independently inspecting every scene presence 2013-01-04 21:50:14 +00:00
Justin Clark-Casey (justincc) 03142980ee Fix llGetLinkName() to return the name of the last avatar sat as the last link number.
As per http://wiki.secondlife.com/wiki/LlGetLinkName
2013-01-04 21:50:09 +00:00
Justin Clark-Casey (justincc) ae355720ab Fix llGetLinkKey() to return the last sat avatar as the last link number.
As per http://wiki.secondlife.com/wiki/LlGetLinkKey
This is done by keeping a scene-object wide list of sitters.
This also fixes bugs in this function where linknums 0 and 1 weren't treated properly if there were sitting avatars on a single prim.
This also fixes a minor race condition for multiple concurrent sitters on a prim with no current sitters by locking on the object-wide list rather than individual sop lists
Addresses http://opensimulator.org/mantis/view.php?id=6477
2013-01-04 21:50:03 +00:00
Justin Clark-Casey (justincc) d87d9af1a5 minor: Add some doc to the extremely unhelpful 'fudge....' comment as to why we're deselecting the prim in code before scheduling an update on attachment 2013-01-04 21:49:55 +00:00
Justin Clark-Casey (justincc) b00935015a Fix problem where object attached from ground often does not get attached properly.
It seems this is happening because we send a kill for objects that are selected when attached.
A code comment says that this is to get the client to deselect it, but v3 and v1 clients do this just fine without the kill.
Aims to address http://opensimulator.org/mantis/view.php?id=6456
2013-01-04 21:49:48 +00:00
SignpostMarv 0022f48d42 Improving documentation of AttachToAvatar and GetLine methods in LSL_Api.cs based on doxygen error output 2013-01-04 21:49:30 +00:00
SignpostMarv ce448adf91 updating documentation in SampleMoneyModule based on doxygen error log output; changing an xml-style hint to a uri-style hint in the class summary, improving documentation of Initialise method and removing a superfluous parameter, improving documentating of ClientClosed method and documenting an omitted parameter 2013-01-04 21:48:55 +00:00
SignpostMarv c4fd1c6297 updating config properties added during upgrade process, adding error log file to doxygen config, adding doxygen output directory & error log to .gitignore 2013-01-04 21:48:33 +00:00
SignpostMarv bbe5018b95 ran doxygen -s -u to upgrade the doxygen config file 2013-01-04 21:48:19 +00:00
Oren Hurvitz eeeb787f36 Fixed: the AvatarEnteringNewParcel event wasn't triggered in some cases
If an avatar moved between regions: A -> B -> A, then when returning to region A the AvatarEnteringNewParcel wasn't triggered. This happened because the ScenePresence in region A still remembered its previous 'currentParcelUUID', so it appeared as if the avatar didn't change parcels. Now, however, when a ScenePresence becomes a child presence we clear its 'currentParcelUUID'.
2013-01-04 21:47:32 +00:00
Justin Clark-Casey (justincc) 70e46ba815 Change nant distbin target to also remove ThirdParty/ source code when making binary distribution 2013-01-04 00:47:54 +00:00
Justin Clark-Casey (justincc) f69731b955 minor: Change channel digger replacement message in TerrainModule to Info from Warn.
This is to stop this unnecessarily triggering log analysis code which reports warn and error level statements.
2013-01-04 00:47:43 +00:00
Oren Hurvitz e5e6fe8c41 Added locking in NullRegionData.
This prevents errors when one thread iterates over the regions (e.g., from RegenerateMaptileAndReregister()) while another thread is adding a region.
2013-01-04 00:47:26 +00:00
Justin Clark-Casey (justincc) 226d655f23 Fix indenting on ConsoleDisplayTable, align indenting on "show animations" console command 2013-01-04 00:47:20 +00:00
Justin Clark-Casey (justincc) afcf5a7591 minor: Allow objects to be added directly to a row on a ConsoleDisplayTable rather than having to ToString() them first 2013-01-04 00:46:58 +00:00
Oren Hurvitz 39d2cafb5c Implemented Return Objects when it's invoked from the Top Colliders or Top Scripts dialogs 2013-01-04 00:46:52 +00:00
Justin Clark-Casey (justincc) 2b6f12a1d3 Add "show animations" console command for debug purposes.
This shows the current animation sequence and default anims for avatars.
2013-01-04 00:46:44 +00:00
Justin Clark-Casey (justincc) acd9d62af2 If an NPC is unowned, then always auto-grant permissions requested via llRequestPermissions()
This is consistent with all other OSSL NPC functions that allow unowned avatars to be manipulated.
Aims to address http://opensimulator.org/mantis/view.php?id=6483
2013-01-04 00:46:37 +00:00
116 changed files with 8909 additions and 6566 deletions

2
.gitignore vendored
View File

@ -95,3 +95,5 @@ OpenSim/Region/ScriptEngine/test-results/
OpenSim/Tests/Common/test-results/ OpenSim/Tests/Common/test-results/
OpenSim/Tests/test-results/ OpenSim/Tests/test-results/
test-results/ test-results/
doc/html
doc/doxygen.error.log

View File

@ -43,6 +43,7 @@
<delete dir="${distbindir}/Prebuild"/> <delete dir="${distbindir}/Prebuild"/>
<delete dir="${distbindir}/%temp%"/> <delete dir="${distbindir}/%temp%"/>
<delete dir="${distbindir}/.nant"/> <delete dir="${distbindir}/.nant"/>
<delete dir="${distbindir}/ThirdParty"/>
<delete> <delete>
<fileset basedir="${distbindir}"> <fileset basedir="${distbindir}">
<include name="compile.bat"/> <include name="compile.bat"/>

View File

@ -113,11 +113,14 @@ namespace OpenSim.Data.Null
// Find region data // Find region data
List<RegionData> ret = new List<RegionData>(); List<RegionData> ret = new List<RegionData>();
foreach (RegionData r in m_regionData.Values) lock (m_regionData)
{ {
// m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanName, r.RegionName.ToLower()); foreach (RegionData r in m_regionData.Values)
{
// m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanName, r.RegionName.ToLower());
if (queryMatch(r.RegionName.ToLower())) if (queryMatch(r.RegionName.ToLower()))
ret.Add(r); ret.Add(r);
}
} }
if (ret.Count > 0) if (ret.Count > 0)
@ -133,10 +136,13 @@ namespace OpenSim.Data.Null
List<RegionData> ret = new List<RegionData>(); List<RegionData> ret = new List<RegionData>();
foreach (RegionData r in m_regionData.Values) lock (m_regionData)
{ {
if (r.posX == posX && r.posY == posY) foreach (RegionData r in m_regionData.Values)
ret.Add(r); {
if (r.posX == posX && r.posY == posY)
ret.Add(r);
}
} }
if (ret.Count > 0) if (ret.Count > 0)
@ -150,8 +156,11 @@ namespace OpenSim.Data.Null
if (m_useStaticInstance && Instance != this) if (m_useStaticInstance && Instance != this)
return Instance.Get(regionID, scopeID); return Instance.Get(regionID, scopeID);
if (m_regionData.ContainsKey(regionID)) lock (m_regionData)
return m_regionData[regionID]; {
if (m_regionData.ContainsKey(regionID))
return m_regionData[regionID];
}
return null; return null;
} }
@ -163,10 +172,13 @@ namespace OpenSim.Data.Null
List<RegionData> ret = new List<RegionData>(); List<RegionData> ret = new List<RegionData>();
foreach (RegionData r in m_regionData.Values) lock (m_regionData)
{ {
if (r.posX >= startX && r.posX <= endX && r.posY >= startY && r.posY <= endY) foreach (RegionData r in m_regionData.Values)
ret.Add(r); {
if (r.posX >= startX && r.posX <= endX && r.posY >= startY && r.posY <= endY)
ret.Add(r);
}
} }
return ret; return ret;
@ -180,7 +192,10 @@ namespace OpenSim.Data.Null
// m_log.DebugFormat( // m_log.DebugFormat(
// "[NULL REGION DATA]: Storing region {0} {1}, scope {2}", data.RegionName, data.RegionID, data.ScopeID); // "[NULL REGION DATA]: Storing region {0} {1}, scope {2}", data.RegionName, data.RegionID, data.ScopeID);
m_regionData[data.RegionID] = data; lock (m_regionData)
{
m_regionData[data.RegionID] = data;
}
return true; return true;
} }
@ -190,10 +205,13 @@ namespace OpenSim.Data.Null
if (m_useStaticInstance && Instance != this) if (m_useStaticInstance && Instance != this)
return Instance.SetDataItem(regionID, item, value); return Instance.SetDataItem(regionID, item, value);
if (!m_regionData.ContainsKey(regionID)) lock (m_regionData)
return false; {
if (!m_regionData.ContainsKey(regionID))
return false;
m_regionData[regionID].Data[item] = value; m_regionData[regionID].Data[item] = value;
}
return true; return true;
} }
@ -205,10 +223,13 @@ namespace OpenSim.Data.Null
// m_log.DebugFormat("[NULL REGION DATA]: Deleting region {0}", regionID); // m_log.DebugFormat("[NULL REGION DATA]: Deleting region {0}", regionID);
if (!m_regionData.ContainsKey(regionID)) lock (m_regionData)
return false; {
if (!m_regionData.ContainsKey(regionID))
return false;
m_regionData.Remove(regionID); m_regionData.Remove(regionID);
}
return true; return true;
} }
@ -238,10 +259,13 @@ namespace OpenSim.Data.Null
List<RegionData> ret = new List<RegionData>(); List<RegionData> ret = new List<RegionData>();
foreach (RegionData r in m_regionData.Values) lock (m_regionData)
{ {
if ((Convert.ToInt32(r.Data["flags"]) & regionFlags) != 0) foreach (RegionData r in m_regionData.Values)
ret.Add(r); {
if ((Convert.ToInt32(r.Data["flags"]) & regionFlags) != 0)
ret.Add(r);
}
} }
return ret; return ret;

View File

@ -110,10 +110,11 @@ namespace OpenSim.Framework.Console
// Remove initial help keyword // Remove initial help keyword
helpParts.RemoveAt(0); helpParts.RemoveAt(0);
help.Add(""); // Will become a newline.
// General help // General help
if (helpParts.Count == 0) if (helpParts.Count == 0)
{ {
help.Add(""); // Will become a newline.
help.Add(GeneralHelpText); help.Add(GeneralHelpText);
help.Add(ItemHelpText); help.Add(ItemHelpText);
help.AddRange(CollectModulesHelp(tree)); help.AddRange(CollectModulesHelp(tree));
@ -127,6 +128,8 @@ namespace OpenSim.Framework.Console
help.AddRange(CollectHelp(helpParts)); help.AddRange(CollectHelp(helpParts));
} }
help.Add(""); // Will become a newline.
return help; return help;
} }
@ -194,14 +197,11 @@ namespace OpenSim.Framework.Console
string descriptiveHelp = commandInfo.descriptive_help; string descriptiveHelp = commandInfo.descriptive_help;
// If we do have some descriptive help then insert a spacing line before and after for readability. // If we do have some descriptive help then insert a spacing line before for readability.
if (descriptiveHelp != string.Empty) if (descriptiveHelp != string.Empty)
help.Add(string.Empty); help.Add(string.Empty);
help.Add(commandInfo.descriptive_help); help.Add(commandInfo.descriptive_help);
if (descriptiveHelp != string.Empty)
help.Add(string.Empty);
} }
else else
{ {

View File

@ -56,7 +56,7 @@ namespace OpenSim.Framework.Console
public List<ConsoleDisplayTableRow> Rows { get; private set; } public List<ConsoleDisplayTableRow> Rows { get; private set; }
/// <summary> /// <summary>
/// Number of spaces to indent the table. /// Number of spaces to indent the whole table.
/// </summary> /// </summary>
public int Indent { get; set; } public int Indent { get; set; }
@ -84,7 +84,7 @@ namespace OpenSim.Framework.Console
Columns.Add(new ConsoleDisplayTableColumn(name, width)); Columns.Add(new ConsoleDisplayTableColumn(name, width));
} }
public void AddRow(params string[] cells) public void AddRow(params object[] cells)
{ {
Rows.Add(new ConsoleDisplayTableRow(cells)); Rows.Add(new ConsoleDisplayTableRow(cells));
} }
@ -113,7 +113,8 @@ namespace OpenSim.Framework.Console
for (int i = 0; i < Columns.Count; i++) for (int i = 0; i < Columns.Count; i++)
{ {
formatSb.Append(' ', TableSpacing); if (i != 0)
formatSb.Append(' ', TableSpacing);
// Can only do left formatting for now // Can only do left formatting for now
formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width); formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width);
@ -139,16 +140,16 @@ namespace OpenSim.Framework.Console
public struct ConsoleDisplayTableRow public struct ConsoleDisplayTableRow
{ {
public List<string> Cells { get; private set; } public List<object> Cells { get; private set; }
public ConsoleDisplayTableRow(List<string> cells) : this() public ConsoleDisplayTableRow(List<object> cells) : this()
{ {
Cells = cells; Cells = cells;
} }
public ConsoleDisplayTableRow(params string[] cells) : this() public ConsoleDisplayTableRow(params object[] cells) : this()
{ {
Cells = new List<string>(cells); Cells = new List<object>(cells);
} }
} }
} }

View File

@ -1623,6 +1623,7 @@ namespace OpenSim.Framework.Servers.HttpServer
// Long Poll Service Manager with 3 worker threads a 25 second timeout for no events // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000); m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000);
m_PollServiceManager.Start();
HTTPDRunning = true; HTTPDRunning = true;
//HttpListenerContext context; //HttpListenerContext context;
@ -1673,6 +1674,8 @@ namespace OpenSim.Framework.Servers.HttpServer
HTTPDRunning = false; HTTPDRunning = false;
try try
{ {
m_PollServiceManager.Stop();
m_httpListener2.ExceptionThrown -= httpServerException; m_httpListener2.ExceptionThrown -= httpServerException;
//m_httpListener2.DisconnectHandler = null; //m_httpListener2.DisconnectHandler = null;

View File

@ -45,19 +45,26 @@ namespace OpenSim.Framework.Servers.HttpServer
private uint m_WorkerThreadCount = 0; private uint m_WorkerThreadCount = 0;
private Thread[] m_workerThreads; private Thread[] m_workerThreads;
private PollServiceWorkerThread[] m_PollServiceWorkerThreads; private PollServiceWorkerThread[] m_PollServiceWorkerThreads;
private bool m_running = true; private volatile bool m_running = true;
private int m_pollTimeout;
public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout)
{ {
m_server = pSrv; m_server = pSrv;
m_WorkerThreadCount = pWorkerThreadCount; m_WorkerThreadCount = pWorkerThreadCount;
m_pollTimeout = pTimeout;
}
public void Start()
{
m_running = true;
m_workerThreads = new Thread[m_WorkerThreadCount]; m_workerThreads = new Thread[m_WorkerThreadCount];
m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount]; m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount];
//startup worker threads //startup worker threads
for (uint i = 0; i < m_WorkerThreadCount; i++) for (uint i = 0; i < m_WorkerThreadCount; i++)
{ {
m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, pTimeout); m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout);
m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent; m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent;
m_workerThreads[i] m_workerThreads[i]
@ -136,8 +143,10 @@ namespace OpenSim.Framework.Servers.HttpServer
} }
~PollServiceRequestManager() public void Stop()
{ {
m_running = false;
foreach (object o in m_requests) foreach (object o in m_requests)
{ {
PollServiceHttpRequest req = (PollServiceHttpRequest) o; PollServiceHttpRequest req = (PollServiceHttpRequest) o;
@ -151,8 +160,6 @@ namespace OpenSim.Framework.Servers.HttpServer
{ {
t.Abort(); t.Abort();
} }
m_running = false;
} }
} }
} }

View File

@ -29,8 +29,8 @@ namespace OpenSim
{ {
public class VersionInfo public class VersionInfo
{ {
private const string VERSION_NUMBER = "0.7.5"; private const string VERSION_NUMBER = "0.7.5.2";
private const Flavour VERSION_FLAVOUR = Flavour.Dev; private const Flavour VERSION_FLAVOUR = Flavour.Post_Fixes;
public enum Flavour public enum Flavour
{ {

View File

@ -299,6 +299,18 @@ namespace OpenSim.Framework
x; x;
} }
// Clamp the maximum magnitude of a vector
public static Vector3 ClampV(Vector3 x, float max)
{
Vector3 ret = x;
float lenSq = x.LengthSquared();
if (lenSq > (max * max))
{
x = x / x.Length() * max;
}
return x;
}
// Inclusive, within range test (true if equal to the endpoints) // Inclusive, within range test (true if equal to the endpoints)
public static bool InRange<T>(T x, T min, T max) public static bool InRange<T>(T x, T min, T max)
where T : IComparable<T> where T : IComparable<T>
@ -1646,8 +1658,13 @@ namespace OpenSim.Framework
if (m_ThreadPool != null) if (m_ThreadPool != null)
throw new InvalidOperationException("SmartThreadPool is already initialized"); throw new InvalidOperationException("SmartThreadPool is already initialized");
m_ThreadPool = new SmartThreadPool(2000, maxThreads, 2); STPStartInfo startInfo = new STPStartInfo();
m_ThreadPool.Name = "Util"; startInfo.ThreadPoolName = "Util";
startInfo.IdleTimeout = 2000;
startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = 2;
m_ThreadPool = new SmartThreadPool(startInfo);
} }
public static int FireAndForgetCount() public static int FireAndForgetCount()

View File

@ -236,18 +236,6 @@ namespace OpenSim
+ "If an avatar name is given then only packets from that avatar are logged", + "If an avatar name is given then only packets from that avatar are logged",
Debug); Debug);
m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug);
m_console.Commands.AddCommand("Debug", false, "debug scene",
"debug scene active|collisions|physics|scripting|teleport true|false",
"Turn on scene debugging.",
"If active is false then main scene update and maintenance loops are suspended.\n"
+ "If collisions is false then collisions with other objects are turned off.\n"
+ "If physics is false then all physics objects are non-physical.\n"
+ "If scripting is false then no scripting operations happen.\n"
+ "If teleport is true then some extra teleport debug information is logged.",
Debug);
m_console.Commands.AddCommand("General", false, "change region", m_console.Commands.AddCommand("General", false, "change region",
"change region <region name>", "change region <region name>",
"Change current console region", ChangeSelectedRegion); "Change current console region", ChangeSelectedRegion);
@ -744,31 +732,6 @@ namespace OpenSim
break; break;
case "scene":
if (args.Length == 4)
{
if (SceneManager.CurrentScene == null)
{
MainConsole.Instance.Output("Please use 'change region <regioname>' first");
}
else
{
string key = args[2];
string value = args[3];
SceneManager.CurrentScene.SetSceneCoreDebug(
new Dictionary<string, string>() { { key, value } });
MainConsole.Instance.OutputFormat("Set debug scene {0} = {1}", key, value);
}
}
else
{
MainConsole.Instance.Output(
"Usage: debug scene active|scripting|collisions|physics|teleport true|false");
}
break;
default: default:
MainConsole.Instance.Output("Unknown debug command"); MainConsole.Instance.Output("Unknown debug command");
break; break;

View File

@ -96,8 +96,8 @@ namespace OpenSim.Region.ClientStack.Linden
// private static readonly string m_fetchInventoryPath = "0006/"; // private static readonly string m_fetchInventoryPath = "0006/";
private static readonly string m_copyFromNotecardPath = "0007/"; private static readonly string m_copyFromNotecardPath = "0007/";
// private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule.
private static readonly string m_UpdateAgentInformationPath = "0500/";
// These are callbacks which will be setup by the scene so that we can update scene data when we // These are callbacks which will be setup by the scene so that we can update scene data when we
// receive capability calls // receive capability calls
public NewInventoryItem AddNewInventoryItem = null; public NewInventoryItem AddNewInventoryItem = null;
@ -204,6 +204,8 @@ namespace OpenSim.Region.ClientStack.Linden
m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req);
m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req);
m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req);
IRequestHandler UpdateAgentInformationHandler = new RestStreamHandler("POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation);
m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler);
m_HostCapsObj.RegisterHandler( m_HostCapsObj.RegisterHandler(
"CopyInventoryFromNotecard", "CopyInventoryFromNotecard",
@ -615,7 +617,7 @@ namespace OpenSim.Region.ClientStack.Linden
= new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero); = new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero);
prim.Scale = scale; prim.Scale = scale;
prim.OffsetPosition = position; //prim.OffsetPosition = position;
rotations.Add(rotation); rotations.Add(rotation);
positions.Add(position); positions.Add(position);
prim.UUID = UUID.Random(); prim.UUID = UUID.Random();
@ -639,25 +641,40 @@ namespace OpenSim.Region.ClientStack.Linden
grp.AddPart(prim); grp.AddPart(prim);
} }
// Fix first link number Vector3 rootPos = positions[0];
if (grp.Parts.Length > 1) if (grp.Parts.Length > 1)
{
// Fix first link number
grp.RootPart.LinkNum++; grp.RootPart.LinkNum++;
Vector3 rootPos = positions[0]; Quaternion rootRotConj = Quaternion.Conjugate(rotations[0]);
grp.AbsolutePosition = rootPos; Quaternion tmprot;
for (int i = 0; i < positions.Count; i++) Vector3 offset;
// fix children rotations and positions
for (int i = 1; i < rotations.Count; i++)
{
tmprot = rotations[i];
tmprot = rootRotConj * tmprot;
grp.Parts[i].RotationOffset = tmprot;
offset = positions[i] - rootPos;
offset *= rootRotConj;
grp.Parts[i].OffsetPosition = offset;
}
grp.AbsolutePosition = rootPos;
grp.UpdateGroupRotationR(rotations[0]);
}
else
{ {
Vector3 offset = positions[i] - rootPos; grp.AbsolutePosition = rootPos;
grp.Parts[i].OffsetPosition = offset; grp.UpdateGroupRotationR(rotations[0]);
} }
for (int i = 0; i < rotations.Count; i++)
{
if (i != 0)
grp.Parts[i].RotationOffset = rotations[i];
}
grp.UpdateGroupRotationR(rotations[0]);
data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp)); data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp));
} }
@ -855,6 +872,22 @@ namespace OpenSim.Region.ClientStack.Linden
response["int_response_code"] = 200; response["int_response_code"] = 200;
return LLSDHelpers.SerialiseLLSDReply(response); return LLSDHelpers.SerialiseLLSDReply(response);
} }
public string UpdateAgentInformation(string request, string path,
string param, IOSHttpRequest httpRequest,
IOSHttpResponse httpResponse)
{
OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
OSDMap resp = new OSDMap();
OSDMap accessPrefs = new OSDMap();
accessPrefs["max"] = "A";
resp["access_prefs"] = accessPrefs;
string response = OSDParser.SerializeLLSDXmlString(resp);
return response;
}
} }
public class AssetUploader public class AssetUploader

View File

@ -57,7 +57,6 @@ namespace OpenSim.Region.ClientStack.Linden
public bool Enabled { get; private set; } public bool Enabled { get; private set; }
private Scene m_scene; private Scene m_scene;
private UUID m_agentID;
#region ISharedRegionModule Members #region ISharedRegionModule Members
@ -118,25 +117,26 @@ namespace OpenSim.Region.ClientStack.Linden
public void RegisterCaps(UUID agentID, Caps caps) public void RegisterCaps(UUID agentID, Caps caps)
{ {
IRequestHandler reqHandler IRequestHandler reqHandler
= new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), MeshUploadFlag, "MeshUploadFlag", agentID.ToString()); = new RestHTTPHandler(
"GET", "/CAPS/" + UUID.Random(), ht => MeshUploadFlag(ht, agentID), "MeshUploadFlag", agentID.ToString());
caps.RegisterHandler("MeshUploadFlag", reqHandler); caps.RegisterHandler("MeshUploadFlag", reqHandler);
m_agentID = agentID;
} }
private Hashtable MeshUploadFlag(Hashtable mDhttpMethod) private Hashtable MeshUploadFlag(Hashtable mDhttpMethod, UUID agentID)
{ {
// m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request"); // m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request");
OSDMap data = new OSDMap(); OSDMap data = new OSDMap();
ScenePresence sp = m_scene.GetScenePresence(m_agentID); ScenePresence sp = m_scene.GetScenePresence(agentID);
data["username"] = sp.Firstname + "." + sp.Lastname; data["username"] = sp.Firstname + "." + sp.Lastname;
data["display_name_next_update"] = new OSDDate(DateTime.Now); data["display_name_next_update"] = new OSDDate(DateTime.Now);
data["legacy_first_name"] = sp.Firstname; data["legacy_first_name"] = sp.Firstname;
data["mesh_upload_status"] = "valid"; data["mesh_upload_status"] = "valid";
data["display_name"] = sp.Firstname + " " + sp.Lastname; data["display_name"] = sp.Firstname + " " + sp.Lastname;
data["legacy_last_name"] = sp.Lastname; data["legacy_last_name"] = sp.Lastname;
data["id"] = m_agentID; data["id"] = agentID;
data["is_display_name_default"] = true; data["is_display_name_default"] = true;
//Send back data //Send back data

View File

@ -3530,7 +3530,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance); AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance);
// TODO: don't create new blocks if recycling an old packet // TODO: don't create new blocks if recycling an old packet
avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218]; avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[visualParams.Length];
avp.ObjectData.TextureEntry = textureEntry; avp.ObjectData.TextureEntry = textureEntry;
AvatarAppearancePacket.VisualParamBlock avblock = null; AvatarAppearancePacket.VisualParamBlock avblock = null;
@ -6427,6 +6427,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion #endregion
AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit;
if (handlerAgentRequestSit != null) if (handlerAgentRequestSit != null)
handlerAgentRequestSit(this, agentRequestSit.AgentData.AgentID, handlerAgentRequestSit(this, agentRequestSit.AgentData.AgentID,
agentRequestSit.TargetObject.TargetID, agentRequestSit.TargetObject.Offset); agentRequestSit.TargetObject.TargetID, agentRequestSit.TargetObject.Offset);

View File

@ -214,9 +214,9 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
public void HandleTaskItemUpdateFromTransaction( public void HandleTaskItemUpdateFromTransaction(
IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item) IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
{ {
m_log.DebugFormat( // m_log.DebugFormat(
"[ASSET TRANSACTION MODULE] Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}", // "[ASSET TRANSACTION MODULE]: Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}",
item.Name, part.Name, remoteClient.Name, m_Scene.RegionInfo.RegionName); // item.Name, part.Name, remoteClient.Name, m_Scene.RegionInfo.RegionName);
AgentAssetTransactions transactions = AgentAssetTransactions transactions =
GetUserTransactions(remoteClient.AgentId); GetUserTransactions(remoteClient.AgentId);
@ -230,15 +230,17 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
/// </summary> /// </summary>
/// <param name="remoteClient"></param> /// <param name="remoteClient"></param>
/// <param name="assetID"></param> /// <param name="assetID"></param>
/// <param name="transaction"></param> /// <param name="transactionID"></param>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="data"></param></param> /// <param name="data"></param></param>
/// <param name="tempFile"></param> /// <param name="tempFile"></param>
public void HandleUDPUploadRequest(IClientAPI remoteClient, public void HandleUDPUploadRequest(IClientAPI remoteClient,
UUID assetID, UUID transaction, sbyte type, byte[] data, UUID assetID, UUID transactionID, sbyte type, byte[] data,
bool storeLocal, bool tempFile) bool storeLocal, bool tempFile)
{ {
// m_log.Debug("HandleUDPUploadRequest - assetID: " + assetID.ToString() + " transaction: " + transaction.ToString() + " type: " + type.ToString() + " storelocal: " + storeLocal + " tempFile: " + tempFile); // m_log.DebugFormat(
// "[ASSET TRANSACTION MODULE]: HandleUDPUploadRequest - assetID: {0}, transaction {1}, type {2}, storeLocal {3}, tempFile {4}, data.Length {5}",
// assetID, transactionID, type, storeLocal, tempFile, data.Length);
if (((AssetType)type == AssetType.Texture || if (((AssetType)type == AssetType.Texture ||
(AssetType)type == AssetType.Sound || (AssetType)type == AssetType.Sound ||
@ -274,8 +276,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
} }
AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId); AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
AssetXferUploader uploader = transactions.RequestXferUploader(transaction); AssetXferUploader uploader = transactions.RequestXferUploader(transactionID);
uploader.StartUpload(remoteClient, assetID, transaction, type, data, storeLocal, tempFile); uploader.StartUpload(remoteClient, assetID, transactionID, type, data, storeLocal, tempFile);
} }
/// <summary> /// <summary>

View File

@ -321,7 +321,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
// to avoid a race condition when the appearance module retrieves the item to set the asset id in // to avoid a race condition when the appearance module retrieves the item to set the asset id in
// the AvatarAppearance structure. // the AvatarAppearance structure.
item.AssetID = m_asset.FullID; item.AssetID = m_asset.FullID;
m_Scene.InventoryService.UpdateItem(item); if (item.AssetID != UUID.Zero)
m_Scene.InventoryService.UpdateItem(item);
if (m_uploadState == UploadState.Complete) if (m_uploadState == UploadState.Complete)
{ {

View File

@ -654,15 +654,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
if (!silent) if (!silent)
{ {
// Killing it here will cause the client to deselect it if (so.HasPrivateAttachmentPoint)
// It then reappears on the avatar, deselected
// through the full update below
//
if (so.IsSelected)
{
m_scene.SendKillObject(new List<uint> { so.RootPart.LocalId });
}
else if (so.HasPrivateAttachmentPoint)
{ {
// m_log.DebugFormat( // m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}", // "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}",
@ -677,7 +669,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
}); });
} }
so.IsSelected = false; // fudge.... // Fudge below is an extremely unhelpful comment. It's probably here so that the scheduled full update
// will succeed, as that will not update if an attachment is selected.
so.IsSelected = false; // fudge....
so.ScheduleGroupForFullUpdate(); so.ScheduleGroupForFullUpdate();
} }

View File

@ -487,6 +487,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
{ {
// m_log.DebugFormat( // m_log.DebugFormat(
// "[INVENTORY ARCHIVER]: Loaded coalescence {0} has {1} objects", assetId, coa.Count); // "[INVENTORY ARCHIVER]: Loaded coalescence {0} has {1} objects", assetId, coa.Count);
if (coa.Objects.Count == 0)
{
m_log.WarnFormat(
"[INVENTORY ARCHIVE READ REQUEST]: Aborting load of coalesced object from asset {0} as it has zero loaded components",
assetId);
return false;
}
sceneObjects.AddRange(coa.Objects); sceneObjects.AddRange(coa.Objects);
} }
@ -495,7 +503,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
SceneObjectGroup deserializedObject = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); SceneObjectGroup deserializedObject = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
if (deserializedObject != null) if (deserializedObject != null)
{
sceneObjects.Add(deserializedObject); sceneObjects.Add(deserializedObject);
}
else
{
m_log.WarnFormat(
"[INVENTORY ARCHIVE READ REQUEST]: Aborting load of object from asset {0} as deserialization failed",
assetId);
return false;
}
} }
foreach (SceneObjectGroup sog in sceneObjects) foreach (SceneObjectGroup sog in sceneObjects)

View File

@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
/// <summary> /// <summary>
private List<Scene> m_Scenelist = new List<Scene>(); private List<Scene> m_Scenelist = new List<Scene>();
// private Dictionary<UUID, Scene> m_AgentRegions =
// new Dictionary<UUID, Scene>();
private IMessageTransferModule m_TransferModule = null; private IMessageTransferModule m_TransferModule;
private bool m_Enabled = true; private bool m_Enabled = true;
#region Region Module interface #region Region Module interface
@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
// scene.RegisterModuleInterface<IInventoryTransferModule>(this); // scene.RegisterModuleInterface<IInventoryTransferModule>(this);
scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnNewClient += OnNewClient;
// scene.EventManager.OnClientClosed += ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
} }
public void RegionLoaded(Scene scene) public void RegionLoaded(Scene scene)
@ -96,11 +92,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only"); m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only");
m_Enabled = false; m_Enabled = false;
m_Scenelist.Clear(); // m_Scenelist.Clear();
scene.EventManager.OnNewClient -= OnNewClient; // scene.EventManager.OnNewClient -= OnNewClient;
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
} }
} }
} }
@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)
{ {
scene.EventManager.OnNewClient -= OnNewClient; scene.EventManager.OnNewClient -= OnNewClient;
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
m_Scenelist.Remove(scene); m_Scenelist.Remove(scene);
} }
@ -139,11 +131,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
// Inventory giving is conducted via instant message // Inventory giving is conducted via instant message
client.OnInstantMessage += OnInstantMessage; client.OnInstantMessage += OnInstantMessage;
} }
// protected void OnSetRootAgentScene(UUID id, Scene scene)
// {
// m_AgentRegions[id] = scene;
// }
private Scene FindClientScene(UUID agentId) private Scene FindClientScene(UUID agentId)
{ {
@ -213,7 +200,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
user.ControllingClient.SendBulkUpdateInventory(folderCopy); user.ControllingClient.SendBulkUpdateInventory(folderCopy);
// HACK!! // HACK!!
im.imSessionID = folderID.Guid; // Insert the ID of the copied item into the IM so that we know which item to move to trash if it
// is rejected.
// XXX: This is probably a misuse of the session ID slot.
im.imSessionID = copyID.Guid;
} }
else else
{ {
@ -243,7 +233,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
user.ControllingClient.SendBulkUpdateInventory(itemCopy); user.ControllingClient.SendBulkUpdateInventory(itemCopy);
// HACK!! // HACK!!
im.imSessionID = itemID.Guid; // Insert the ID of the copied item into the IM so that we know which item to move to trash if it
// is rejected.
// XXX: This is probably a misuse of the session ID slot.
im.imSessionID = copyID.Guid;
} }
// Send the IM to the recipient. The item is already // Send the IM to the recipient. The item is already
@ -454,70 +447,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
} }
} }
// public bool NeedSceneCacheClear(UUID agentID, Scene scene)
// {
// if (!m_AgentRegions.ContainsKey(agentID))
// {
// // Since we can get here two ways, we need to scan
// // the scenes here. This is somewhat more expensive
// // but helps avoid a nasty bug
// //
//
// foreach (Scene s in m_Scenelist)
// {
// ScenePresence presence;
//
// if (s.TryGetScenePresence(agentID, out presence))
// {
// // If the agent is in this scene, then we
// // are being called twice in a single
// // teleport. This is wasteful of cycles
// // but harmless due to this 2nd level check
// //
// // If the agent is found in another scene
// // then the list wasn't current
// //
// // If the agent is totally unknown, then what
// // are we even doing here??
// //
// if (s == scene)
// {
// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName);
// return true;
// }
// else
// {
// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName);
// return false;
// }
// }
// }
// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName);
// return true;
// }
//
// // The agent is left in current Scene, so we must be
// // going to another instance
// //
// if (m_AgentRegions[agentID] == scene)
// {
// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName);
// m_AgentRegions.Remove(agentID);
// return true;
// }
//
// // Another region has claimed the agent
// //
// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName);
// return false;
// }
//
// public void ClientLoggedOut(UUID agentID, Scene scene)
// {
// if (m_AgentRegions.ContainsKey(agentID))
// m_AgentRegions.Remove(agentID);
// }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

View File

@ -0,0 +1,256 @@
/*
* 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.Reflection;
using log4net.Config;
using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenMetaverse.Assets;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Avatar.Inventory.Transfer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
{
[TestFixture]
public class InventoryTransferModuleTests : OpenSimTestCase
{
protected TestScene m_scene;
[SetUp]
public override void SetUp()
{
base.SetUp();
IConfigSource config = new IniConfigSource();
config.AddConfig("Messaging");
config.Configs["Messaging"].Set("InventoryTransferModule", "InventoryTransferModule");
m_scene = new SceneHelpers().SetupScene();
SceneHelpers.SetupSceneModules(m_scene, config, new InventoryTransferModule());
}
[Test]
public void TestAcceptGivenItem()
{
// TestHelpers.EnableLogging();
UUID initialSessionId = TestHelpers.ParseTail(0x10);
UUID itemId = TestHelpers.ParseTail(0x100);
UUID assetId = TestHelpers.ParseTail(0x200);
UserAccount ua1
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
UserAccount ua2
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
TestClient giverClient = (TestClient)giverSp.ControllingClient;
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
// Create the object to test give
InventoryItemBase originalItem
= UserInventoryHelpers.CreateInventoryItem(
m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object);
byte[] giveImBinaryBucket = new byte[17];
byte[] itemIdBytes = itemId.GetBytes();
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
GridInstantMessage giveIm
= new GridInstantMessage(
m_scene,
giverSp.UUID,
giverSp.Name,
receiverSp.UUID,
(byte)InstantMessageDialog.InventoryOffered,
false,
"inventory offered msg",
initialSessionId,
false,
Vector3.Zero,
giveImBinaryBucket,
true);
giverClient.HandleImprovedInstantMessage(giveIm);
// These details might not all be correct.
GridInstantMessage acceptIm
= new GridInstantMessage(
m_scene,
receiverSp.UUID,
receiverSp.Name,
giverSp.UUID,
(byte)InstantMessageDialog.InventoryAccepted,
false,
"inventory accepted msg",
initialSessionId,
false,
Vector3.Zero,
null,
true);
receiverClient.HandleImprovedInstantMessage(acceptIm);
// Test for item remaining in the giver's inventory (here we assume a copy item)
// TODO: Test no-copy items.
InventoryItemBase originalItemAfterGive
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
Assert.That(originalItemAfterGive, Is.Not.Null);
Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID));
// Test for item successfully making it into the receiver's inventory
InventoryItemBase receivedItem
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Objects/givenObj");
Assert.That(receivedItem, Is.Not.Null);
Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID));
// Test that on a delete, item still exists and is accessible for the giver.
m_scene.InventoryService.DeleteItems(receiverSp.UUID, new List<UUID>() { receivedItem.ID });
InventoryItemBase originalItemAfterDelete
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
Assert.That(originalItemAfterDelete, Is.Not.Null);
// TODO: Test scenario where giver deletes their item first.
}
/// <summary>
/// Test user rejection of a given item.
/// </summary>
/// <remarks>
/// A rejected item still ends up in the user's trash folder.
/// </remarks>
[Test]
public void TestRejectGivenItem()
{
// TestHelpers.EnableLogging();
UUID initialSessionId = TestHelpers.ParseTail(0x10);
UUID itemId = TestHelpers.ParseTail(0x100);
UUID assetId = TestHelpers.ParseTail(0x200);
UserAccount ua1
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
UserAccount ua2
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
TestClient giverClient = (TestClient)giverSp.ControllingClient;
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
// Create the object to test give
InventoryItemBase originalItem
= UserInventoryHelpers.CreateInventoryItem(
m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object);
GridInstantMessage receivedIm = null;
receiverClient.OnReceivedInstantMessage += im => receivedIm = im;
byte[] giveImBinaryBucket = new byte[17];
byte[] itemIdBytes = itemId.GetBytes();
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
GridInstantMessage giveIm
= new GridInstantMessage(
m_scene,
giverSp.UUID,
giverSp.Name,
receiverSp.UUID,
(byte)InstantMessageDialog.InventoryOffered,
false,
"inventory offered msg",
initialSessionId,
false,
Vector3.Zero,
giveImBinaryBucket,
true);
giverClient.HandleImprovedInstantMessage(giveIm);
// These details might not all be correct.
// Session ID is now the created item ID (!)
GridInstantMessage rejectIm
= new GridInstantMessage(
m_scene,
receiverSp.UUID,
receiverSp.Name,
giverSp.UUID,
(byte)InstantMessageDialog.InventoryDeclined,
false,
"inventory declined msg",
new UUID(receivedIm.imSessionID),
false,
Vector3.Zero,
null,
true);
receiverClient.HandleImprovedInstantMessage(rejectIm);
// Test for item remaining in the giver's inventory (here we assume a copy item)
// TODO: Test no-copy items.
InventoryItemBase originalItemAfterGive
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
Assert.That(originalItemAfterGive, Is.Not.Null);
Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID));
// Test for item successfully making it into the receiver's inventory
InventoryItemBase receivedItem
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Trash/givenObj");
InventoryFolderBase trashFolder
= m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder);
Assert.That(receivedItem, Is.Not.Null);
Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID));
Assert.That(receivedItem.Folder, Is.EqualTo(trashFolder.ID));
// Test that on a delete, item still exists and is accessible for the giver.
m_scene.InventoryService.PurgeFolder(trashFolder);
InventoryItemBase originalItemAfterDelete
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
Assert.That(originalItemAfterDelete, Is.Not.Null);
}
}
}

View File

@ -212,11 +212,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
protected override GridRegion GetFinalDestination(GridRegion region) protected override GridRegion GetFinalDestination(GridRegion region)
{ {
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags); m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags);
if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
{ {
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region {0} is hyperlink", region.RegionID); m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink");
GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID);
if (real_destination != null) if (real_destination != null)
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI);

View File

@ -28,6 +28,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenMetaverse;
namespace OpenSim.Region.CoreModules.Framework.InterfaceCommander namespace OpenSim.Region.CoreModules.Framework.InterfaceCommander
{ {
@ -152,6 +153,9 @@ namespace OpenSim.Region.CoreModules.Framework.InterfaceCommander
case "Boolean": case "Boolean":
m_args[i].ArgumentValue = Boolean.Parse(arg.ToString()); m_args[i].ArgumentValue = Boolean.Parse(arg.ToString());
break; break;
case "UUID":
m_args[i].ArgumentValue = UUID.Parse(arg.ToString());
break;
default: default:
Console.WriteLine("ERROR: Unknown desired type for argument " + m_args[i].Name + " on command " + m_name); Console.WriteLine("ERROR: Unknown desired type for argument " + m_args[i].Name + " on command " + m_name);
break; break;

View File

@ -346,7 +346,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId); InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId);
InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID); InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID);
inv.SendBulkUpdateInventory(content.Folders.ToArray(), content.Items.ToArray()); List<InventoryFolderBase> keep = new List<InventoryFolderBase>();
foreach (InventoryFolderBase f in content.Folders)
{
if (f.Name != "My Suitcase" && f.Name != "Current Outfit")
keep.Add(f);
}
inv.SendBulkUpdateInventory(keep.ToArray(), content.Items.ToArray());
} }
} }
} }
@ -379,7 +387,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
foreach (InventoryFolderBase f in content.Folders) foreach (InventoryFolderBase f in content.Folders)
{ {
if (f.Name != "My Suitcase") if (f.Name != "My Suitcase" && f.Name != "Current Outfit")
{ {
f.Name = f.Name + " (Unavailable)"; f.Name = f.Name + " (Unavailable)";
keep.Add(f); keep.Add(f);

View File

@ -187,6 +187,45 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
case (int)HttpRequestConstants.HTTP_VERIFY_CERT: case (int)HttpRequestConstants.HTTP_VERIFY_CERT:
htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0); htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0);
break; break;
case (int)HttpRequestConstants.HTTP_VERBOSE_THROTTLE:
// TODO implement me
break;
case (int)HttpRequestConstants.HTTP_CUSTOM_HEADER:
//Parameters are in pairs and custom header takes
//arguments in pairs so adjust for header marker.
++i;
//Maximum of 8 headers are allowed based on the
//Second Life documentation for llHTTPRequest.
for (int count = 1; count <= 8; ++count)
{
//Not enough parameters remaining for a header?
if (parms.Length - i < 2)
break;
//Have we reached the end of the list of headers?
//End is marked by a string with a single digit.
//We already know we have at least one parameter
//so it is safe to do this check at top of loop.
if (Char.IsDigit(parms[i][0]))
break;
if (htc.HttpCustomHeaders == null)
htc.HttpCustomHeaders = new List<string>();
htc.HttpCustomHeaders.Add(parms[i]);
htc.HttpCustomHeaders.Add(parms[i+1]);
i += 2;
}
break;
case (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE:
htc.HttpPragmaNoCache = (int.Parse(parms[i + 1]) != 0);
break;
} }
} }
} }
@ -328,9 +367,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
// public const int HTTP_METHOD = 0; // public const int HTTP_METHOD = 0;
// public const int HTTP_MIMETYPE = 1; // public const int HTTP_MIMETYPE = 1;
// public const int HTTP_VERIFY_CERT = 3; // public const int HTTP_VERIFY_CERT = 3;
// public const int HTTP_VERBOSE_THROTTLE = 4;
// public const int HTTP_CUSTOM_HEADER = 5;
// public const int HTTP_PRAGMA_NO_CACHE = 6;
private bool _finished; private bool _finished;
public bool Finished public bool Finished
{ {
get { return _finished; } get { return _finished; }
} }
// public int HttpBodyMaxLen = 2048; // not implemented // public int HttpBodyMaxLen = 2048; // not implemented
@ -340,11 +382,14 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
public string HttpMIMEType = "text/plain;charset=utf-8"; public string HttpMIMEType = "text/plain;charset=utf-8";
public int HttpTimeout; public int HttpTimeout;
public bool HttpVerifyCert = true; public bool HttpVerifyCert = true;
//public bool HttpVerboseThrottle = true; // not implemented
public List<string> HttpCustomHeaders = null;
public bool HttpPragmaNoCache = true;
private Thread httpThread; private Thread httpThread;
// Request info // Request info
private UUID _itemID; private UUID _itemID;
public UUID ItemID public UUID ItemID
{ {
get { return _itemID; } get { return _itemID; }
set { _itemID = value; } set { _itemID = value; }
@ -360,7 +405,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
public string proxyexcepts; public string proxyexcepts;
public string OutboundBody; public string OutboundBody;
private UUID _reqID; private UUID _reqID;
public UUID ReqID public UUID ReqID
{ {
get { return _reqID; } get { return _reqID; }
set { _reqID = value; } set { _reqID = value; }
@ -401,7 +446,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
Request.Method = HttpMethod; Request.Method = HttpMethod;
Request.ContentType = HttpMIMEType; Request.ContentType = HttpMIMEType;
if(!HttpVerifyCert) if (!HttpVerifyCert)
{ {
// We could hijack Connection Group Name to identify // We could hijack Connection Group Name to identify
// a desired security exception. But at the moment we'll use a dummy header instead. // a desired security exception. But at the moment we'll use a dummy header instead.
@ -412,14 +457,24 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
// { // {
// Request.ConnectionGroupName="Verify"; // Request.ConnectionGroupName="Verify";
// } // }
if (proxyurl != null && proxyurl.Length > 0) if (!HttpPragmaNoCache)
{ {
if (proxyexcepts != null && proxyexcepts.Length > 0) Request.Headers.Add("Pragma", "no-cache");
}
if (HttpCustomHeaders != null)
{
for (int i = 0; i < HttpCustomHeaders.Count; i += 2)
Request.Headers.Add(HttpCustomHeaders[i],
HttpCustomHeaders[i+1]);
}
if (proxyurl != null && proxyurl.Length > 0)
{
if (proxyexcepts != null && proxyexcepts.Length > 0)
{ {
string[] elist = proxyexcepts.Split(';'); string[] elist = proxyexcepts.Split(';');
Request.Proxy = new WebProxy(proxyurl, true, elist); Request.Proxy = new WebProxy(proxyurl, true, elist);
} }
else else
{ {
Request.Proxy = new WebProxy(proxyurl, true); Request.Proxy = new WebProxy(proxyurl, true);
} }
@ -432,7 +487,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
Request.Headers[entry.Key] = entry.Value; Request.Headers[entry.Key] = entry.Value;
// Encode outbound data // Encode outbound data
if (OutboundBody.Length > 0) if (OutboundBody.Length > 0)
{ {
byte[] data = Util.UTF8.GetBytes(OutboundBody); byte[] data = Util.UTF8.GetBytes(OutboundBody);

View File

@ -152,7 +152,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
TestHelpers.InMethod(); TestHelpers.InMethod();
string dtText string dtText
= "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png"; = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://0.0.0.0/shouldnotexist.png";
SetupScene(false); SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene); SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
@ -307,7 +307,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
TestHelpers.InMethod(); TestHelpers.InMethod();
string dtText string dtText
= "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png"; = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://0.0.0.0/shouldnotexist.png";
SetupScene(true); SetupScene(true);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene); SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);

View File

@ -117,7 +117,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
m_module.Scene.RegionInfo.RegionSettings.Save(); m_module.Scene.RegionInfo.RegionSettings.Save();
m_module.TriggerRegionInfoChange(); m_module.TriggerRegionInfoChange();
m_module.sendRegionInfoPacketToAll(); m_module.sendRegionHandshakeToAll();
} }
} }
} }

View File

@ -753,13 +753,18 @@ namespace OpenSim.Region.CoreModules.World.Estate
Scene.RegionInfo.RegionSettings.Save(); Scene.RegionInfo.RegionSettings.Save();
TriggerRegionInfoChange(); TriggerRegionInfoChange();
Scene.SetSceneCoreDebug( ISceneCommandsModule scm = Scene.RequestModuleInterface<ISceneCommandsModule>();
new Dictionary<string, string>() {
{ "scripting", (!disableScripts).ToString() }, if (scm != null)
{ "collisions", (!disableCollisions).ToString() }, {
{ "physics", (!disablePhysics).ToString() } scm.SetSceneDebugOptions(
} new Dictionary<string, string>() {
); { "scripting", (!disableScripts).ToString() },
{ "collisions", (!disableCollisions).ToString() },
{ "physics", (!disablePhysics).ToString() }
}
);
}
} }
private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey) private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey)

View File

@ -133,6 +133,7 @@ namespace OpenSim.Region.CoreModules.World.Land
m_scene.EventManager.OnValidateLandBuy += EventManagerOnValidateLandBuy; m_scene.EventManager.OnValidateLandBuy += EventManagerOnValidateLandBuy;
m_scene.EventManager.OnLandBuy += EventManagerOnLandBuy; m_scene.EventManager.OnLandBuy += EventManagerOnLandBuy;
m_scene.EventManager.OnNewClient += EventManagerOnNewClient; m_scene.EventManager.OnNewClient += EventManagerOnNewClient;
m_scene.EventManager.OnMakeChildAgent += EventMakeChildAgent;
m_scene.EventManager.OnSignificantClientMovement += EventManagerOnSignificantClientMovement; m_scene.EventManager.OnSignificantClientMovement += EventManagerOnSignificantClientMovement;
m_scene.EventManager.OnNoticeNoLandDataFromStorage += EventManagerOnNoLandDataFromStorage; m_scene.EventManager.OnNoticeNoLandDataFromStorage += EventManagerOnNoLandDataFromStorage;
m_scene.EventManager.OnIncomingLandDataFromStorage += EventManagerOnIncomingLandDataFromStorage; m_scene.EventManager.OnIncomingLandDataFromStorage += EventManagerOnIncomingLandDataFromStorage;
@ -218,6 +219,11 @@ namespace OpenSim.Region.CoreModules.World.Land
} }
} }
public void EventMakeChildAgent(ScenePresence avatar)
{
avatar.currentParcelUUID = UUID.Zero;
}
void ClientOnPreAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) void ClientOnPreAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData)
{ {
//If we are forcing a position for them to go //If we are forcing a position for them to go
@ -1395,15 +1401,65 @@ namespace OpenSim.Region.CoreModules.World.Land
public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient)
{ {
ILandObject selectedParcel = null; if (localID != -1)
lock (m_landList)
{ {
m_landList.TryGetValue(localID, out selectedParcel); ILandObject selectedParcel = null;
lock (m_landList)
{
m_landList.TryGetValue(localID, out selectedParcel);
}
if (selectedParcel == null) return;
selectedParcel.ReturnLandObjects(returnType, agentIDs, taskIDs, remoteClient);
} }
else
{
if (returnType != 1)
{
m_log.WarnFormat("[LAND MANAGEMENT MODULE] ReturnObjectsInParcel: unknown return type {0}", returnType);
return;
}
if (selectedParcel == null) return; // We get here when the user returns objects from the list of Top Colliders or Top Scripts.
// In that case we receive specific object UUID's, but no parcel ID.
selectedParcel.ReturnLandObjects(returnType, agentIDs, taskIDs, remoteClient); Dictionary<UUID, HashSet<SceneObjectGroup>> returns = new Dictionary<UUID, HashSet<SceneObjectGroup>>();
foreach (UUID groupID in taskIDs)
{
SceneObjectGroup obj = m_scene.GetSceneObjectGroup(groupID);
if (obj != null)
{
if (!returns.ContainsKey(obj.OwnerID))
returns[obj.OwnerID] = new HashSet<SceneObjectGroup>();
returns[obj.OwnerID].Add(obj);
}
else
{
m_log.WarnFormat("[LAND MANAGEMENT MODULE] ReturnObjectsInParcel: unknown object {0}", groupID);
}
}
int num = 0;
foreach (HashSet<SceneObjectGroup> objs in returns.Values)
num += objs.Count;
m_log.DebugFormat("[LAND MANAGEMENT MODULE] Returning {0} specific object(s)", num);
foreach (HashSet<SceneObjectGroup> objs in returns.Values)
{
List<SceneObjectGroup> objs2 = new List<SceneObjectGroup>(objs);
if (m_scene.Permissions.CanReturnObjects(null, remoteClient.AgentId, objs2))
{
m_scene.returnObjects(objs2.ToArray(), remoteClient.AgentId);
}
else
{
m_log.WarnFormat("[LAND MANAGEMENT MODULE] ReturnObjectsInParcel: not permitted to return {0} object(s) belonging to user {1}",
objs2.Count, objs2[0].OwnerID);
}
}
}
} }
public void EventManagerOnNoLandDataFromStorage() public void EventManagerOnNoLandDataFromStorage()

View File

@ -592,11 +592,11 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
cdl.AddRow( cdl.AddRow(
"LightColor", "LightColor",
string.Format("<{0},{1},{2},{3}>", s.LightColorR, s.LightColorB, s.LightColorG, s.LightColorA)); string.Format("<{0},{1},{2},{3}>", s.LightColorR, s.LightColorB, s.LightColorG, s.LightColorA));
cdl.AddRow("FlexiDrag", s.LightCutoff); cdl.AddRow("LightCutoff", s.LightCutoff);
cdl.AddRow("FlexiDrag", s.LightEntry); cdl.AddRow("LightEntry", s.LightEntry);
cdl.AddRow("FlexiDrag", s.LightFalloff); cdl.AddRow("LightFalloff", s.LightFalloff);
cdl.AddRow("FlexiDrag", s.LightIntensity); cdl.AddRow("LightIntensity", s.LightIntensity);
cdl.AddRow("FlexiDrag", s.LightRadius); cdl.AddRow("LightRadius", s.LightRadius);
cdl.AddRow("Media", string.Format("{0} entries", s.Media != null ? s.Media.Count.ToString() : "n/a")); cdl.AddRow("Media", string.Format("{0} entries", s.Media != null ? s.Media.Count.ToString() : "n/a"));
cdl.AddRow("PathBegin", s.PathBegin); cdl.AddRow("PathBegin", s.PathBegin);
cdl.AddRow("PathEnd", s.PathEnd); cdl.AddRow("PathEnd", s.PathEnd);

View File

@ -480,7 +480,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
else else
{ {
m_plugineffects[pluginName] = effect; m_plugineffects[pluginName] = effect;
m_log.Warn("E ... " + pluginName + " (Replaced)"); m_log.Info("E ... " + pluginName + " (Replaced)");
} }
} }
} }

View File

@ -140,8 +140,6 @@ namespace OpenSim.Region.DataSnapshot
return; return;
} }
if (m_enabled)
m_snapStore = new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname);
} }
} }
@ -155,8 +153,22 @@ namespace OpenSim.Region.DataSnapshot
m_log.DebugFormat("[DATASNAPSHOT]: Module added to Scene {0}.", scene.RegionInfo.RegionName); m_log.DebugFormat("[DATASNAPSHOT]: Module added to Scene {0}.", scene.RegionInfo.RegionName);
m_snapStore.AddScene(scene); if (!m_servicesNotified)
{
m_hostname = scene.RegionInfo.ExternalHostName;
m_snapStore = new SnapshotStore(m_snapsDir, m_gridinfo, m_listener_port, m_hostname);
//Hand it the first scene, assuming that all scenes have the same BaseHTTPServer
new DataRequestHandler(scene, this);
if (m_dataServices != "" && m_dataServices != "noservices")
NotifyDataServices(m_dataServices, "online");
m_servicesNotified = true;
}
m_scenes.Add(scene); m_scenes.Add(scene);
m_snapStore.AddScene(scene);
Assembly currentasm = Assembly.GetExecutingAssembly(); Assembly currentasm = Assembly.GetExecutingAssembly();
@ -181,22 +193,6 @@ namespace OpenSim.Region.DataSnapshot
} }
} }
// Must be done here because on shared modules, PostInitialise() will run
// BEFORE any scenes are registered. There is no "all scenes have been loaded"
// kind of callback because scenes may be created dynamically, so we cannot
// have that info, ever.
if (!m_servicesNotified)
{
//Hand it the first scene, assuming that all scenes have the same BaseHTTPServer
new DataRequestHandler(m_scenes[0], this);
m_hostname = m_scenes[0].RegionInfo.ExternalHostName;
if (m_dataServices != "" && m_dataServices != "noservices")
NotifyDataServices(m_dataServices, "online");
m_servicesNotified = true;
}
} }
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)

View File

@ -36,6 +36,9 @@ namespace OpenSim.Region.Framework.Interfaces
HTTP_MIMETYPE = 1, HTTP_MIMETYPE = 1,
HTTP_BODY_MAXLENGTH = 2, HTTP_BODY_MAXLENGTH = 2,
HTTP_VERIFY_CERT = 3, HTTP_VERIFY_CERT = 3,
HTTP_VERBOSE_THROTTLE = 4,
HTTP_CUSTOM_HEADER = 5,
HTTP_PRAGMA_NO_CACHE = 6
} }
public interface IHttpRequestModule public interface IHttpRequestModule

View File

@ -0,0 +1,43 @@
/*
* 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 OpenSim 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 OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.Framework.Interfaces
{
public interface ISceneCommandsModule
{
/// <summary>
/// Sets the scene debug options.
/// </summary>
void SetSceneDebugOptions(Dictionary<string, string> options);
}
}

View File

@ -87,13 +87,24 @@ namespace OpenSim.Region.Framework.Scenes.Animation
return false; return false;
} }
public bool Remove(UUID animID) /// <summary>
/// Remove the specified animation
/// </summary>
/// <param name='animID'></param>
/// <param name='allowNoDefault'>
/// If true, then the default animation can be entirely removed.
/// If false, then removing the default animation will reset it to the simulator default (currently STAND).
/// </param>
public bool Remove(UUID animID, bool allowNoDefault)
{ {
lock (m_animations) lock (m_animations)
{ {
if (m_defaultAnimation.AnimID == animID) if (m_defaultAnimation.AnimID == animID)
{ {
m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero); if (allowNoDefault)
m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero);
else
ResetDefaultAnimation();
} }
else if (HasAnimation(animID)) else if (HasAnimation(animID))
{ {

View File

@ -86,7 +86,10 @@ namespace OpenSim.Region.Framework.Scenes.Animation
if (m_scenePresence.IsChildAgent) if (m_scenePresence.IsChildAgent)
return; return;
// m_log.DebugFormat("[SCENE PRESENCE ANIMATOR]: Adding animation {0} for {1}", animID, m_scenePresence.Name); if (m_scenePresence.Scene.DebugAnimations)
m_log.DebugFormat(
"[SCENE PRESENCE ANIMATOR]: Adding animation {0} {1} for {2}",
GetAnimName(animID), animID, m_scenePresence.Name);
if (m_animations.Add(animID, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, objectID)) if (m_animations.Add(animID, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, objectID))
SendAnimPack(); SendAnimPack();
@ -109,14 +112,25 @@ namespace OpenSim.Region.Framework.Scenes.Animation
AddAnimation(animID, objectID); AddAnimation(animID, objectID);
} }
public void RemoveAnimation(UUID animID) /// <summary>
/// Remove the specified animation
/// </summary>
/// <param name='animID'></param>
/// <param name='allowNoDefault'>
/// If true, then the default animation can be entirely removed.
/// If false, then removing the default animation will reset it to the simulator default (currently STAND).
/// </param>
public void RemoveAnimation(UUID animID, bool allowNoDefault)
{ {
if (m_scenePresence.IsChildAgent) if (m_scenePresence.IsChildAgent)
return; return;
// m_log.DebugFormat("[SCENE PRESENCE ANIMATOR]: Removing animation {0} for {1}", animID, m_scenePresence.Name); if (m_scenePresence.Scene.DebugAnimations)
m_log.DebugFormat(
"[SCENE PRESENCE ANIMATOR]: Removing animation {0} {1} for {2}",
GetAnimName(animID), animID, m_scenePresence.Name);
if (m_animations.Remove(animID)) if (m_animations.Remove(animID, allowNoDefault))
SendAnimPack(); SendAnimPack();
} }
@ -132,14 +146,15 @@ namespace OpenSim.Region.Framework.Scenes.Animation
if (animID == UUID.Zero) if (animID == UUID.Zero)
return; return;
RemoveAnimation(animID); RemoveAnimation(animID, true);
} }
public void ResetAnimations() public void ResetAnimations()
{ {
// m_log.DebugFormat( if (m_scenePresence.Scene.DebugAnimations)
// "[SCENE PRESENCE ANIMATOR]: Resetting animations for {0} in {1}", m_log.DebugFormat(
// m_scenePresence.Name, m_scenePresence.Scene.RegionInfo.RegionName); "[SCENE PRESENCE ANIMATOR]: Resetting animations for {0} in {1}",
m_scenePresence.Name, m_scenePresence.Scene.RegionInfo.RegionName);
m_animations.Clear(); m_animations.Clear();
} }
@ -550,5 +565,21 @@ namespace OpenSim.Region.Framework.Scenes.Animation
SendAnimPack(animIDs, sequenceNums, objectIDs); SendAnimPack(animIDs, sequenceNums, objectIDs);
} }
public string GetAnimName(UUID animId)
{
string animName;
if (!DefaultAvatarAnimations.AnimsNames.TryGetValue(animId, out animName))
{
AssetMetadata amd = m_scenePresence.Scene.AssetService.GetMetadata(animId.ToString());
if (amd != null)
animName = amd.Name;
else
animName = "Unknown";
}
return animName;
}
} }
} }

View File

@ -407,16 +407,16 @@ namespace OpenSim.Region.Framework.Scenes
if (item.Owner != remoteClient.AgentId) if (item.Owner != remoteClient.AgentId)
return; return;
if (UUID.Zero == transactionID) item.Name = itemUpd.Name;
{ item.Description = itemUpd.Description;
item.Name = itemUpd.Name;
item.Description = itemUpd.Description;
// m_log.DebugFormat( // m_log.DebugFormat(
// "[USER INVENTORY]: itemUpd {0} {1} {2} {3}, item {4} {5} {6} {7}", // "[USER INVENTORY]: itemUpd {0} {1} {2} {3}, item {4} {5} {6} {7}",
// itemUpd.NextPermissions, itemUpd.GroupPermissions, itemUpd.EveryOnePermissions, item.Flags, // itemUpd.NextPermissions, itemUpd.GroupPermissions, itemUpd.EveryOnePermissions, item.Flags,
// item.NextPermissions, item.GroupPermissions, item.EveryOnePermissions, item.CurrentPermissions); // item.NextPermissions, item.GroupPermissions, item.EveryOnePermissions, item.CurrentPermissions);
if (itemUpd.NextPermissions != 0) // Use this to determine validity. Can never be 0 if valid
{
if (item.NextPermissions != (itemUpd.NextPermissions & item.BasePermissions)) if (item.NextPermissions != (itemUpd.NextPermissions & item.BasePermissions))
item.Flags |= (uint)InventoryItemFlags.ObjectOverwriteNextOwner; item.Flags |= (uint)InventoryItemFlags.ObjectOverwriteNextOwner;
item.NextPermissions = itemUpd.NextPermissions & item.BasePermissions; item.NextPermissions = itemUpd.NextPermissions & item.BasePermissions;
@ -451,7 +451,8 @@ namespace OpenSim.Region.Framework.Scenes
InventoryService.UpdateItem(item); InventoryService.UpdateItem(item);
} }
else
if (UUID.Zero != transactionID)
{ {
if (AgentTransactionsModule != null) if (AgentTransactionsModule != null)
{ {
@ -1800,8 +1801,11 @@ namespace OpenSim.Region.Framework.Scenes
/// Rez a script into a prim's inventory from another prim /// Rez a script into a prim's inventory from another prim
/// </summary> /// </summary>
/// <param name="remoteClient"></param> /// <param name="remoteClient"></param>
/// <param name="itemID"> </param> /// <param name="srcPart"> </param>
/// <param name="localID"></param> /// <param name="destId"> </param>
/// <param name="pin"></param>
/// <param name="running"></param>
/// <param name="start_param"></param>
public void RezScriptFromPrim(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param) public void RezScriptFromPrim(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param)
{ {
TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId); TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId);
@ -1821,12 +1825,11 @@ namespace OpenSim.Region.Framework.Scenes
if (destPart == null) if (destPart == null)
{ {
m_log.ErrorFormat( m_log.ErrorFormat(
"[PRIM INVENTORY]: " + "[PRIM INVENTORY]: Could not find part {0} to insert script item {1} from {2} {3} in {4}",
"Could not find script for ID {0}", destId, srcId, srcPart.Name, srcPart.UUID, Name);
destId);
return; return;
} }
// Must own the object, and have modify rights // Must own the object, and have modify rights
if (srcPart.OwnerID != destPart.OwnerID) if (srcPart.OwnerID != destPart.OwnerID)
{ {
@ -1834,12 +1837,14 @@ namespace OpenSim.Region.Framework.Scenes
if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) || if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) ||
((destPart.GroupMask & (uint)PermissionMask.Modify) == 0)) ((destPart.GroupMask & (uint)PermissionMask.Modify) == 0))
return; return;
} else { }
else
{
if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0) if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0)
return; return;
} }
if (destPart.ScriptAccessPin != pin) if (destPart.ScriptAccessPin == 0 || destPart.ScriptAccessPin != pin)
{ {
m_log.WarnFormat( m_log.WarnFormat(
"[PRIM INVENTORY]: " + "[PRIM INVENTORY]: " +

View File

@ -67,15 +67,85 @@ namespace OpenSim.Region.Framework.Scenes
public bool EmergencyMonitoring = false; public bool EmergencyMonitoring = false;
/// <summary>
/// Show debug information about animations.
/// </summary>
public bool DebugAnimations { get; set; }
/// <summary> /// <summary>
/// Show debug information about teleports. /// Show debug information about teleports.
/// </summary> /// </summary>
public bool DebugTeleporting { get; private set; } public bool DebugTeleporting { get; set; }
/// <summary> /// <summary>
/// Show debug information about the scene loop. /// Show debug information about the scene loop.
/// </summary> /// </summary>
public bool DebugUpdates { get; private set; } public bool DebugUpdates { get; set; }
/// <summary>
/// If true then the scene is saved to persistent storage periodically, every m_update_backup frames and
/// if objects meet required conditions (m_dontPersistBefore and m_dontPersistAfter).
/// </summary>
/// <remarks>
/// Even if false, the scene will still be saved on clean shutdown.
/// FIXME: Currently, setting this to false will mean that objects are not periodically returned from parcels.
/// This needs to be fixed.
/// </remarks>
public bool PeriodicBackup { get; set; }
/// <summary>
/// If false then the scene is never saved to persistence storage even if PeriodicBackup == true and even
/// if the scene is being shut down for the final time.
/// </summary>
public bool UseBackup { get; set; }
/// <summary>
/// If false then physical objects are disabled, though collisions will continue as normal.
/// </summary>
public bool PhysicsEnabled { get; set; }
/// <summary>
/// If false then scripts are not enabled on the smiulator
/// </summary>
public bool ScriptsEnabled
{
get { return m_scripts_enabled; }
set
{
if (m_scripts_enabled != value)
{
if (!value)
{
m_log.Info("Stopping all Scripts in Scene");
EntityBase[] entities = Entities.GetEntities();
foreach (EntityBase ent in entities)
{
if (ent is SceneObjectGroup)
((SceneObjectGroup)ent).RemoveScriptInstances(false);
}
}
else
{
m_log.Info("Starting all Scripts in Scene");
EntityBase[] entities = Entities.GetEntities();
foreach (EntityBase ent in entities)
{
if (ent is SceneObjectGroup)
{
SceneObjectGroup sog = (SceneObjectGroup)ent;
sog.CreateScriptInstances(0, false, DefaultScriptEngine, 0);
sog.ResumeScripts();
}
}
}
m_scripts_enabled = value;
}
}
}
private bool m_scripts_enabled;
public SynchronizeSceneHandler SynchronizeScene; public SynchronizeSceneHandler SynchronizeScene;
@ -282,8 +352,6 @@ namespace OpenSim.Region.Framework.Scenes
private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>(); private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>();
private Dictionary<UUID, SceneObjectGroup> m_groupsWithTargets = new Dictionary<UUID, SceneObjectGroup>(); private Dictionary<UUID, SceneObjectGroup> m_groupsWithTargets = new Dictionary<UUID, SceneObjectGroup>();
private bool m_physics_enabled = true;
private bool m_scripts_enabled = true;
private string m_defaultScriptEngine; private string m_defaultScriptEngine;
/// <summary> /// <summary>
@ -341,7 +409,6 @@ namespace OpenSim.Region.Framework.Scenes
private Timer m_mapGenerationTimer = new Timer(); private Timer m_mapGenerationTimer = new Timer();
private bool m_generateMaptiles; private bool m_generateMaptiles;
private bool m_useBackup = true;
#endregion Fields #endregion Fields
@ -594,11 +661,6 @@ namespace OpenSim.Region.Framework.Scenes
get { return m_authenticateHandler; } get { return m_authenticateHandler; }
} }
public bool UseBackup
{
get { return m_useBackup; }
}
// an instance to the physics plugin's Scene object. // an instance to the physics plugin's Scene object.
public PhysicsScene PhysicsScene public PhysicsScene PhysicsScene
{ {
@ -751,9 +813,11 @@ namespace OpenSim.Region.Framework.Scenes
DumpAssetsToFile = dumpAssetsToFile; DumpAssetsToFile = dumpAssetsToFile;
// XXX: Don't set the public property since we don't want to activate here. This needs to be handled
// better in the future.
m_scripts_enabled = !RegionInfo.RegionSettings.DisableScripts; m_scripts_enabled = !RegionInfo.RegionSettings.DisableScripts;
m_physics_enabled = !RegionInfo.RegionSettings.DisablePhysics; PhysicsEnabled = !RegionInfo.RegionSettings.DisablePhysics;
m_simulatorVersion = simulatorVersion + " (" + Util.GetRuntimeInformation() + ")"; m_simulatorVersion = simulatorVersion + " (" + Util.GetRuntimeInformation() + ")";
@ -768,8 +832,8 @@ namespace OpenSim.Region.Framework.Scenes
StartDisabled = startupConfig.GetBoolean("StartDisabled", false); StartDisabled = startupConfig.GetBoolean("StartDisabled", false);
m_defaultDrawDistance = startupConfig.GetFloat("DefaultDrawDistance", m_defaultDrawDistance); m_defaultDrawDistance = startupConfig.GetFloat("DefaultDrawDistance", m_defaultDrawDistance);
m_useBackup = startupConfig.GetBoolean("UseSceneBackup", m_useBackup); UseBackup = startupConfig.GetBoolean("UseSceneBackup", UseBackup);
if (!m_useBackup) if (!UseBackup)
m_log.InfoFormat("[SCENE]: Backup has been disabled for {0}", RegionInfo.RegionName); m_log.InfoFormat("[SCENE]: Backup has been disabled for {0}", RegionInfo.RegionName);
//Animation states //Animation states
@ -937,6 +1001,10 @@ namespace OpenSim.Region.Framework.Scenes
{ {
PhysicalPrims = true; PhysicalPrims = true;
CollidablePrims = true; CollidablePrims = true;
PhysicsEnabled = true;
PeriodicBackup = true;
UseBackup = true;
BordersLocked = true; BordersLocked = true;
Border northBorder = new Border(); Border northBorder = new Border();
@ -1179,83 +1247,6 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
public void SetSceneCoreDebug(Dictionary<string, string> options)
{
if (options.ContainsKey("active"))
{
bool active;
if (bool.TryParse(options["active"], out active))
Active = active;
}
if (options.ContainsKey("scripting"))
{
bool enableScripts = true;
if (bool.TryParse(options["scripting"], out enableScripts) && m_scripts_enabled != enableScripts)
{
if (!enableScripts)
{
m_log.Info("Stopping all Scripts in Scene");
EntityBase[] entities = Entities.GetEntities();
foreach (EntityBase ent in entities)
{
if (ent is SceneObjectGroup)
((SceneObjectGroup)ent).RemoveScriptInstances(false);
}
}
else
{
m_log.Info("Starting all Scripts in Scene");
EntityBase[] entities = Entities.GetEntities();
foreach (EntityBase ent in entities)
{
if (ent is SceneObjectGroup)
{
SceneObjectGroup sog = (SceneObjectGroup)ent;
sog.CreateScriptInstances(0, false, DefaultScriptEngine, 0);
sog.ResumeScripts();
}
}
}
m_scripts_enabled = enableScripts;
}
}
if (options.ContainsKey("physics"))
{
bool enablePhysics;
if (bool.TryParse(options["physics"], out enablePhysics))
m_physics_enabled = enablePhysics;
}
// if (options.ContainsKey("collisions"))
// {
// // TODO: Implement. If false, should stop objects colliding, though possibly should still allow
// // the avatar themselves to collide with the ground.
// }
if (options.ContainsKey("teleport"))
{
bool enableTeleportDebugging;
if (bool.TryParse(options["teleport"], out enableTeleportDebugging))
DebugTeleporting = enableTeleportDebugging;
}
if (options.ContainsKey("updates"))
{
bool enableUpdateDebugging;
if (bool.TryParse(options["updates"], out enableUpdateDebugging))
{
DebugUpdates = enableUpdateDebugging;
GcNotify.Enabled = DebugUpdates;
}
}
}
public int GetInaccurateNeighborCount() public int GetInaccurateNeighborCount()
{ {
return m_neighbours.Count; return m_neighbours.Count;
@ -1514,7 +1505,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
tmpMS = Util.EnvironmentTickCount(); tmpMS = Util.EnvironmentTickCount();
if ((Frame % m_update_physics == 0) && m_physics_enabled) if (PhysicsEnabled && Frame % m_update_physics == 0)
m_sceneGraph.UpdatePreparePhysics(); m_sceneGraph.UpdatePreparePhysics();
physicsMS2 = Util.EnvironmentTickCountSubtract(tmpMS); physicsMS2 = Util.EnvironmentTickCountSubtract(tmpMS);
@ -1529,7 +1520,7 @@ namespace OpenSim.Region.Framework.Scenes
tmpMS = Util.EnvironmentTickCount(); tmpMS = Util.EnvironmentTickCount();
if (Frame % m_update_physics == 0) if (Frame % m_update_physics == 0)
{ {
if (m_physics_enabled) if (PhysicsEnabled)
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime); physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime);
if (SynchronizeScene != null) if (SynchronizeScene != null)
@ -1570,7 +1561,7 @@ namespace OpenSim.Region.Framework.Scenes
eventMS = Util.EnvironmentTickCountSubtract(tmpMS); eventMS = Util.EnvironmentTickCountSubtract(tmpMS);
} }
if (Frame % m_update_backup == 0) if (PeriodicBackup && Frame % m_update_backup == 0)
{ {
tmpMS = Util.EnvironmentTickCount(); tmpMS = Util.EnvironmentTickCount();
UpdateStorageBackup(); UpdateStorageBackup();

View File

@ -647,6 +647,18 @@ namespace OpenSim.Region.Framework.Scenes
/// </remarks> /// </remarks>
public UUID FromFolderID { get; set; } public UUID FromFolderID { get; set; }
/// <summary>
/// IDs of all avatars sat on this scene object.
/// </summary>
/// <remarks>
/// We need this so that we can maintain a linkset wide ordering of avatars sat on different parts.
/// This must be locked before it is read or written.
/// SceneObjectPart sitting avatar add/remove code also locks on this object to avoid race conditions.
/// No avatar should appear more than once in this list.
/// Do not manipulate this list directly - use the Add/Remove sitting avatar methods on SceneObjectPart.
/// </remarks>
protected internal List<UUID> m_sittingAvatars = new List<UUID>();
#endregion #endregion
// ~SceneObjectGroup() // ~SceneObjectGroup()
@ -3563,6 +3575,20 @@ namespace OpenSim.Region.Framework.Scenes
return count; return count;
} }
/// <summary>
/// Get a copy of the list of sitting avatars on all prims of this object.
/// </summary>
/// <remarks>
/// This is sorted by the order in which avatars sat down. If an avatar stands up then all avatars that sat
/// down after it move one place down the list.
/// </remarks>
/// <returns>A list of the sitting avatars. Returns an empty list if there are no sitting avatars.</returns>
public List<UUID> GetSittingAvatars()
{
lock (m_sittingAvatars)
return new List<UUID>(m_sittingAvatars);
}
/// <summary> /// <summary>
/// Gets the number of sitting avatars. /// Gets the number of sitting avatars.
/// </summary> /// </summary>
@ -3570,11 +3596,8 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns> /// <returns></returns>
public int GetSittingAvatarsCount() public int GetSittingAvatarsCount()
{ {
int count = 0; lock (m_sittingAvatars)
return m_sittingAvatars.Count;
Array.ForEach<SceneObjectPart>(m_parts.GetArray(), p => count += p.GetSittingAvatarsCount());
return count;
} }
public override string ToString() public override string ToString()
@ -3583,7 +3606,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
#region ISceneObject #region ISceneObject
public virtual ISceneObject CloneForNewScene() public virtual ISceneObject CloneForNewScene()
{ {
SceneObjectGroup sog = Copy(false); SceneObjectGroup sog = Copy(false);

View File

@ -1256,7 +1256,7 @@ namespace OpenSim.Region.Framework.Scenes
public UUID SitTargetAvatar { get; set; } public UUID SitTargetAvatar { get; set; }
/// <summary> /// <summary>
/// IDs of all avatars start on this object part. /// IDs of all avatars sat on this part.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// We need to track this so that we can stop sat upon prims from being attached. /// We need to track this so that we can stop sat upon prims from being attached.
@ -4050,9 +4050,9 @@ namespace OpenSim.Region.Framework.Scenes
rigidBody, rigidBody,
m_localId); m_localId);
} }
catch catch (Exception e)
{ {
m_log.ErrorFormat("[SCENE]: caught exception meshing object {0}. Object set to phantom.", m_uuid); m_log.ErrorFormat("[SCENE]: caught exception meshing object {0}. Object set to phantom. e={1}", m_uuid, e);
pa = null; pa = null;
} }
@ -4504,18 +4504,22 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name='avatarId'></param> /// <param name='avatarId'></param>
protected internal bool AddSittingAvatar(UUID avatarId) protected internal bool AddSittingAvatar(UUID avatarId)
{ {
if (IsSitTargetSet && SitTargetAvatar == UUID.Zero) lock (ParentGroup.m_sittingAvatars)
SitTargetAvatar = avatarId;
HashSet<UUID> sittingAvatars = m_sittingAvatars;
if (sittingAvatars == null)
sittingAvatars = new HashSet<UUID>();
lock (sittingAvatars)
{ {
m_sittingAvatars = sittingAvatars; if (IsSitTargetSet && SitTargetAvatar == UUID.Zero)
return m_sittingAvatars.Add(avatarId); SitTargetAvatar = avatarId;
if (m_sittingAvatars == null)
m_sittingAvatars = new HashSet<UUID>();
if (m_sittingAvatars.Add(avatarId))
{
ParentGroup.m_sittingAvatars.Add(avatarId);
return true;
}
return false;
} }
} }
@ -4529,27 +4533,26 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name='avatarId'></param> /// <param name='avatarId'></param>
protected internal bool RemoveSittingAvatar(UUID avatarId) protected internal bool RemoveSittingAvatar(UUID avatarId)
{ {
if (SitTargetAvatar == avatarId) lock (ParentGroup.m_sittingAvatars)
SitTargetAvatar = UUID.Zero;
HashSet<UUID> sittingAvatars = m_sittingAvatars;
// This can occur under a race condition where another thread
if (sittingAvatars == null)
return false;
lock (sittingAvatars)
{ {
if (sittingAvatars.Remove(avatarId)) if (SitTargetAvatar == avatarId)
SitTargetAvatar = UUID.Zero;
if (m_sittingAvatars == null)
return false;
if (m_sittingAvatars.Remove(avatarId))
{ {
if (sittingAvatars.Count == 0) if (m_sittingAvatars.Count == 0)
m_sittingAvatars = null; m_sittingAvatars = null;
ParentGroup.m_sittingAvatars.Remove(avatarId);
return true; return true;
} }
}
return false; return false;
}
} }
/// <summary> /// <summary>
@ -4559,16 +4562,12 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns>A hashset of the sitting avatars. Returns null if there are no sitting avatars.</returns> /// <returns>A hashset of the sitting avatars. Returns null if there are no sitting avatars.</returns>
public HashSet<UUID> GetSittingAvatars() public HashSet<UUID> GetSittingAvatars()
{ {
HashSet<UUID> sittingAvatars = m_sittingAvatars; lock (ParentGroup.m_sittingAvatars)
if (sittingAvatars == null)
{ {
return null; if (m_sittingAvatars == null)
} return null;
else else
{ return new HashSet<UUID>(m_sittingAvatars);
lock (sittingAvatars)
return new HashSet<UUID>(sittingAvatars);
} }
} }
@ -4579,13 +4578,13 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns> /// <returns></returns>
public int GetSittingAvatarsCount() public int GetSittingAvatarsCount()
{ {
HashSet<UUID> sittingAvatars = m_sittingAvatars; lock (ParentGroup.m_sittingAvatars)
{
if (sittingAvatars == null) if (m_sittingAvatars == null)
return 0; return 0;
else
lock (sittingAvatars) return m_sittingAvatars.Count;
return sittingAvatars.Count; }
} }
} }
} }

View File

@ -1954,6 +1954,9 @@ namespace OpenSim.Region.Framework.Scenes
{ {
if (ParentID != 0) if (ParentID != 0)
{ {
if (ParentPart.UUID == targetID)
return; // already sitting here, ignore
StandUp(); StandUp();
} }
@ -2256,7 +2259,7 @@ namespace OpenSim.Region.Framework.Scenes
public void HandleStopAnim(IClientAPI remoteClient, UUID animID) public void HandleStopAnim(IClientAPI remoteClient, UUID animID)
{ {
Animator.RemoveAnimation(animID); Animator.RemoveAnimation(animID, false);
} }
/// <summary> /// <summary>

View File

@ -42,9 +42,6 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
/// <summary> /// <summary>
/// Serialize and deserialize coalesced scene objects. /// Serialize and deserialize coalesced scene objects.
/// </summary> /// </summary>
/// <remarks>
/// Deserialization not yet here.
/// </remarks>
public class CoalescedSceneObjectsSerializer public class CoalescedSceneObjectsSerializer
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -128,6 +125,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
// m_log.DebugFormat("[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() deserializing {0}", xml); // m_log.DebugFormat("[COALESCED SCENE OBJECTS SERIALIZER]: TryFromXml() deserializing {0}", xml);
coa = null; coa = null;
int i = 0;
using (StringReader sr = new StringReader(xml)) using (StringReader sr = new StringReader(xml))
{ {
@ -153,7 +151,23 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
if (reader.Name == "SceneObjectGroup") if (reader.Name == "SceneObjectGroup")
{ {
string soXml = reader.ReadOuterXml(); string soXml = reader.ReadOuterXml();
coa.Add(SceneObjectSerializer.FromOriginalXmlFormat(soXml));
SceneObjectGroup so = SceneObjectSerializer.FromOriginalXmlFormat(soXml);
if (so != null)
{
coa.Add(so);
}
else
{
// XXX: Possibly we should fail outright here rather than continuing if a particular component of the
// coalesced object fails to load.
m_log.WarnFormat(
"[COALESCED SCENE OBJECTS SERIALIZER]: Deserialization of xml for component {0} failed. Continuing.",
i);
}
i++;
} }
} }

View File

@ -289,6 +289,9 @@ namespace OpenSim.Region.Framework.Scenes
private void statsHeartBeat(object sender, EventArgs e) private void statsHeartBeat(object sender, EventArgs e)
{ {
if (!m_scene.Active)
return;
SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[22]; SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[22];
SimStatsPacket.RegionBlock rb = new SimStatsPacket.RegionBlock(); SimStatsPacket.RegionBlock rb = new SimStatsPacket.RegionBlock();

View File

@ -0,0 +1,200 @@
/*
* 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.Linq;
using System.Reflection;
using System.Text;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.ClientStack.LindenUDP;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Animation;
using OpenSim.Services.Interfaces;
namespace OpenSim.Region.OptionalModules.Avatar.Animations
{
/// <summary>
/// A module that just holds commands for inspecting avatar animations.
/// </summary>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AnimationsCommandModule")]
public class AnimationsCommandModule : ISharedRegionModule
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Scene> m_scenes = new List<Scene>();
public string Name { get { return "Animations Command Module"; } }
public Type ReplaceableInterface { get { return null; } }
public void Initialise(IConfigSource source)
{
// m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: INITIALIZED MODULE");
}
public void PostInitialise()
{
// m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: POST INITIALIZED MODULE");
}
public void Close()
{
// m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: CLOSED MODULE");
}
public void AddRegion(Scene scene)
{
// m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName);
}
public void RemoveRegion(Scene scene)
{
// m_log.DebugFormat("[ATTACHMENTS COMMAND MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
lock (m_scenes)
m_scenes.Remove(scene);
}
public void RegionLoaded(Scene scene)
{
// m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
lock (m_scenes)
m_scenes.Add(scene);
scene.AddCommand(
"Users", this, "show animations",
"show animations [<first-name> <last-name>]",
"Show animation information for avatars in this simulator.",
"If no name is supplied then information for all avatars is shown.\n"
+ "Please note that for inventory animations, the animation name is the name under which the animation was originally uploaded\n"
+ ", which is not necessarily the current inventory name.",
HandleShowAnimationsCommand);
}
protected void HandleShowAnimationsCommand(string module, string[] cmd)
{
if (cmd.Length != 2 && cmd.Length < 4)
{
MainConsole.Instance.OutputFormat("Usage: show animations [<first-name> <last-name>]");
return;
}
bool targetNameSupplied = false;
string optionalTargetFirstName = null;
string optionalTargetLastName = null;
if (cmd.Length >= 4)
{
targetNameSupplied = true;
optionalTargetFirstName = cmd[2];
optionalTargetLastName = cmd[3];
}
StringBuilder sb = new StringBuilder();
lock (m_scenes)
{
foreach (Scene scene in m_scenes)
{
if (targetNameSupplied)
{
ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName);
if (sp != null && !sp.IsChildAgent)
GetAttachmentsReport(sp, sb);
}
else
{
scene.ForEachRootScenePresence(sp => GetAttachmentsReport(sp, sb));
}
}
}
MainConsole.Instance.Output(sb.ToString());
}
private void GetAttachmentsReport(ScenePresence sp, StringBuilder sb)
{
sb.AppendFormat("Animations for {0}\n", sp.Name);
ConsoleDisplayList cdl = new ConsoleDisplayList() { Indent = 2 };
ScenePresenceAnimator spa = sp.Animator;
AnimationSet anims = sp.Animator.Animations;
string cma = spa.CurrentMovementAnimation;
cdl.AddRow(
"Current movement anim",
string.Format("{0}, {1}", DefaultAvatarAnimations.GetDefaultAnimation(cma), cma));
UUID defaultAnimId = anims.DefaultAnimation.AnimID;
cdl.AddRow(
"Default anim",
string.Format("{0}, {1}", defaultAnimId, sp.Animator.GetAnimName(defaultAnimId)));
UUID implicitDefaultAnimId = anims.ImplicitDefaultAnimation.AnimID;
cdl.AddRow(
"Implicit default anim",
string.Format("{0}, {1}",
implicitDefaultAnimId, sp.Animator.GetAnimName(implicitDefaultAnimId)));
cdl.AddToStringBuilder(sb);
ConsoleDisplayTable cdt = new ConsoleDisplayTable() { Indent = 2 };
cdt.AddColumn("Animation ID", 36);
cdt.AddColumn("Name", 20);
cdt.AddColumn("Seq", 3);
cdt.AddColumn("Object ID", 36);
UUID[] animIds;
int[] sequenceNumbers;
UUID[] objectIds;
sp.Animator.Animations.GetArrays(out animIds, out sequenceNumbers, out objectIds);
for (int i = 0; i < animIds.Length; i++)
{
UUID animId = animIds[i];
string animName = sp.Animator.GetAnimName(animId);
int seq = sequenceNumbers[i];
UUID objectId = objectIds[i];
cdt.AddRow(animId, animName, seq, objectId);
}
cdt.AddToStringBuilder(sb);
sb.Append("\n");
}
}
}

View File

@ -97,6 +97,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
"Users", this, "attachments show", "Users", this, "attachments show",
"attachments show [<first-name> <last-name>]", "attachments show [<first-name> <last-name>]",
"Show attachment information for avatars in this simulator.", "Show attachment information for avatars in this simulator.",
"If no name is supplied then information for all avatars is shown.",
HandleShowAttachmentsCommand); HandleShowAttachmentsCommand);
} }

View File

@ -39,6 +39,7 @@ using OpenSim.Framework.Communications;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using System.Text;
using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
@ -50,19 +51,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
/// ; To use this module, you must specify the following in your OpenSim.ini /// ; To use this module, you must specify the following in your OpenSim.ini
/// [GROUPS] /// [GROUPS]
/// Enabled = true /// Enabled = true
/// ///
/// Module = GroupsModule /// Module = GroupsModule
/// NoticesEnabled = true /// NoticesEnabled = true
/// DebugEnabled = true /// DebugEnabled = true
/// ///
/// GroupsServicesConnectorModule = XmlRpcGroupsServicesConnector /// GroupsServicesConnectorModule = XmlRpcGroupsServicesConnector
/// XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php /// XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php
/// XmlRpcServiceReadKey = 1234 /// XmlRpcServiceReadKey = 1234
/// XmlRpcServiceWriteKey = 1234 /// XmlRpcServiceWriteKey = 1234
/// ///
/// MessagingModule = GroupsMessagingModule /// MessagingModule = GroupsMessagingModule
/// MessagingEnabled = true /// MessagingEnabled = true
/// ///
/// ; Disables HTTP Keep-Alive for Groups Module HTTP Requests, work around for /// ; Disables HTTP Keep-Alive for Groups Module HTTP Requests, work around for
/// ; a problem discovered on some Windows based region servers. Only disable /// ; a problem discovered on some Windows based region servers. Only disable
/// ; if you see a large number (dozens) of the following Exceptions: /// ; if you see a large number (dozens) of the following Exceptions:
@ -77,7 +78,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private List<Scene> m_sceneList = new List<Scene>(); private List<Scene> m_sceneList = new List<Scene>();
private IMessageTransferModule m_msgTransferModule = null; private IMessageTransferModule m_msgTransferModule = null;
private IGroupsServicesConnector m_groupData = null; private IGroupsServicesConnector m_groupData = null;
// Configuration settings // Configuration settings
@ -126,7 +127,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
scene.RegisterModuleInterface<IGroupsModule>(this); scene.RegisterModuleInterface<IGroupsModule>(this);
scene.AddCommand( scene.AddCommand(
"debug", "Debug",
this, this,
"debug groups verbose", "debug groups verbose",
"debug groups verbose <true|false>", "debug groups verbose <true|false>",
@ -195,10 +196,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnNewClient += OnNewClient;
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
// The InstantMessageModule itself doesn't do this, // The InstantMessageModule itself doesn't do this,
// so lets see if things explode if we don't do it // so lets see if things explode if we don't do it
// scene.EventManager.OnClientClosed += OnClientClosed; // scene.EventManager.OnClientClosed += OnClientClosed;
} }
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)
@ -222,7 +222,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module."); if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module.");
} }
public Type ReplaceableInterface public Type ReplaceableInterface
{ {
get { return null; } get { return null; }
} }
@ -274,7 +274,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
* reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections. * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
* The OnClientClosed event does not provide anything to indicate which one of those should be closed * The OnClientClosed event does not provide anything to indicate which one of those should be closed
* nor does it provide what scene it was from so that the specific reference can be looked up. * nor does it provide what scene it was from so that the specific reference can be looked up.
* The InstantMessageModule.cs does not currently worry about unregistering the handles, * The InstantMessageModule.cs does not currently worry about unregistering the handles,
* and it should be an issue, since it's the client that references us not the other way around * and it should be an issue, since it's the client that references us not the other way around
* , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
private void OnClientClosed(UUID AgentId) private void OnClientClosed(UUID AgentId)
@ -297,8 +297,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here."); if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here.");
} }
} }
} }
*/ */
@ -307,15 +305,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups)
{ {
if (m_debugEnabled) if (m_debugEnabled)
m_log.DebugFormat( m_log.DebugFormat(
"[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", "[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})",
System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
// TODO: This currently ignores pretty much all the query flags including Mature and sort order // TODO: This currently ignores pretty much all the query flags including Mature and sort order
remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentID(remoteClient), queryText).ToArray()); remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentID(remoteClient), queryText).ToArray());
} }
} }
private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
@ -345,7 +343,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
string GroupName; string GroupName;
GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null); GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null);
if (group != null) if (group != null)
{ {
@ -407,7 +405,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
UpdateAllClientsWithGroupInfo(inviteInfo.AgentID); UpdateAllClientsWithGroupInfo(inviteInfo.AgentID);
// TODO: If the inviter is still online, they need an agent dataupdate // TODO: If the inviter is still online, they need an agent dataupdate
// and maybe group membership updates for the invitee // and maybe group membership updates for the invitee
m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID); m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
@ -437,44 +435,75 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
string Subject = im.message.Substring(0, im.message.IndexOf('|')); string Subject = im.message.Substring(0, im.message.IndexOf('|'));
string Message = im.message.Substring(Subject.Length + 1); string Message = im.message.Substring(Subject.Length + 1);
InventoryItemBase item = null;
bool hasAttachment = false;
UUID itemID = UUID.Zero; //Assignment to quiet compiler
UUID ownerID = UUID.Zero; //Assignment to quiet compiler
byte[] bucket; byte[] bucket;
if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0)) if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
{
bucket = new byte[19];
bucket[0] = 0; //dunno
bucket[1] = 0; //dunno
GroupID.ToBytes(bucket, 2);
bucket[18] = 0; //dunno
}
else
{ {
string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
binBucket = binBucket.Remove(0, 14).Trim(); binBucket = binBucket.Remove(0, 14).Trim();
if (m_debugEnabled)
{
m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket);
OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
if (binBucketOSD is OSD)
foreach (string key in binBucketOSD.Keys) {
OSDMap binBucketMap = (OSDMap)binBucketOSD;
itemID = binBucketMap["item_id"].AsUUID();
ownerID = binBucketMap["owner_id"].AsUUID();
//Attempt to get the details of the attached item.
//If sender doesn't own the attachment, the item
//variable will be set to null and attachment will
//not be included with the group notice.
Scene scene = (Scene)remoteClient.Scene;
item = new InventoryItemBase(itemID, ownerID);
item = scene.InventoryService.GetItem(item);
if (item != null)
{ {
if (binBucketOSD.ContainsKey(key)) //Got item details so include the attachment.
{ hasAttachment = true;
m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString());
}
} }
} }
else
// treat as if no attachment {
bucket = new byte[19]; m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
bucket[0] = 0; //dunno }
bucket[1] = 0; //dunno }
GroupID.ToBytes(bucket, 2);
bucket[18] = 0; //dunno if (hasAttachment)
{
//Bucket contains information about attachment.
//
//Byte offset and description of bucket data:
//0: 1 byte indicating if attachment is present
//1: 1 byte indicating the type of attachment
//2: 16 bytes - Group UUID
//18: 16 bytes - UUID of the attachment owner
//34: 16 bytes - UUID of the attachment
//50: variable - Name of the attachment
//??: NUL byte to terminate the attachment name
byte[] name = Encoding.UTF8.GetBytes(item.Name);
bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name
bucket[0] = 1; //Has attachment flag
bucket[1] = (byte)item.InvType; //Type of Attachment
GroupID.ToBytes(bucket, 2);
ownerID.ToBytes(bucket, 18);
itemID.ToBytes(bucket, 34);
name.CopyTo(bucket, 50);
}
else
{
bucket = new byte[19];
bucket[0] = 0; //Has attachment flag
bucket[1] = 0; //Type of attachment
GroupID.ToBytes(bucket, 2);
bucket[18] = 0; //NUL terminate name of attachment
} }
m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
if (OnNewGroupNotice != null) if (OnNewGroupNotice != null)
{ {
@ -484,22 +513,24 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// Send notice out to everyone that wants notices // Send notice out to everyone that wants notices
foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID)) foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID))
{ {
if (m_debugEnabled) if (m_debugEnabled)
{ {
UserAccount targetUser = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, member.AgentID); UserAccount targetUser = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, member.AgentID);
if (targetUser != null) if (targetUser != null)
{ {
m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices); m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices);
} }
else else
{ {
m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", NoticeID, member.AgentID, member.AcceptNotices); m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
NoticeID, member.AgentID, member.AcceptNotices);
} }
} }
if (member.AcceptNotices) if (member.AcceptNotices)
{ {
// Build notice IIM // Build notice IM
GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
msg.toAgentID = member.AgentID.Guid; msg.toAgentID = member.AgentID.Guid;
@ -508,10 +539,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
} }
} }
} }
if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
{
//Is bucket large enough to hold UUID of the attachment?
if (im.binaryBucket.Length < 16)
return;
UUID noticeID = new UUID(im.imSessionID);
GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID);
if (notice != null)
{
UUID giver = new UUID(notice.BinaryBucket, 18);
UUID attachmentUUID = new UUID(notice.BinaryBucket, 34);
if (m_debugEnabled)
m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
giver, attachmentUUID);
if (itemCopy == null)
{
remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false);
return;
}
remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
}
}
// Interop, received special 210 code for ejecting a group member // Interop, received special 210 code for ejecting a group member
// this only works within the comms servers domain, and won't work hypergrid // this only works within the comms servers domain, and won't work hypergrid
// TODO:FIXME: Use a presense server of some kind to find out where the // TODO:FIXME: Use a presense server of some kind to find out where the
// client actually is, and try contacting that region directly to notify them, // client actually is, and try contacting that region directly to notify them,
// or provide the notification via xmlrpc update queue // or provide the notification via xmlrpc update queue
if ((im.dialog == 210)) if ((im.dialog == 210))
@ -574,7 +635,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
return m_groupData.GetGroupRecord(UUID.Zero, UUID.Zero, name); return m_groupData.GetGroupRecord(UUID.Zero, UUID.Zero, name);
} }
public void ActivateGroup(IClientAPI remoteClient, UUID groupID) public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
{ {
if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
@ -582,7 +643,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
m_groupData.SetAgentActiveGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID); m_groupData.SetAgentActiveGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
// Changing active group changes title, active powers, all kinds of things // Changing active group changes title, active powers, all kinds of things
// anyone who is in any region that can see this client, should probably be // anyone who is in any region that can see this client, should probably be
// updated with new group info. At a minimum, they should get ScenePresence // updated with new group info. At a minimum, they should get ScenePresence
// updated with new title. // updated with new title.
UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
@ -618,10 +679,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID) public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
{ {
if (m_debugEnabled) if (m_debugEnabled)
m_log.DebugFormat( m_log.DebugFormat(
"[GROUPS]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name); "[GROUPS]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name);
List<GroupMembersData> data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); List<GroupMembersData> data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID);
if (m_debugEnabled) if (m_debugEnabled)
@ -706,7 +767,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
{ {
if (m_debugEnabled) if (m_debugEnabled)
m_log.DebugFormat( m_log.DebugFormat(
"[GROUPS]: {0} called with groupID={1}, agentID={2}", "[GROUPS]: {0} called with groupID={1}, agentID={2}",
System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID); System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID);
@ -796,7 +857,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (membership != null) if (membership != null)
{ {
return membership.GroupTitle; return membership.GroupTitle;
} }
return string.Empty; return string.Empty;
} }
@ -812,7 +873,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// TODO: Not sure what all is needed here, but if the active group role change is for the group // TODO: Not sure what all is needed here, but if the active group role change is for the group
// the client currently has set active, then we need to do a scene presence update too // the client currently has set active, then we need to do a scene presence update too
// if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID) // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID)
UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
} }
@ -870,7 +931,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
case 1: case 1:
// Remove // Remove
m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID); m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID);
break; break;
default: default:
m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
@ -889,26 +950,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (data != null) if (data != null)
{ {
GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
GridInstantMessage msg = new GridInstantMessage();
msg.imSessionID = UUID.Zero.Guid;
msg.fromAgentID = data.GroupID.Guid;
msg.toAgentID = GetRequestingAgentID(remoteClient).Guid;
msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName;
msg.message = data.noticeData.Subject + "|" + data.Message;
msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested;
msg.fromGroup = true;
msg.offline = (byte)0;
msg.ParentEstateID = 0;
msg.Position = Vector3.Zero;
msg.RegionID = UUID.Zero.Guid;
msg.binaryBucket = data.BinaryBucket;
OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
} }
} }
public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
@ -916,10 +961,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
GridInstantMessage msg = new GridInstantMessage(); GridInstantMessage msg = new GridInstantMessage();
msg.imSessionID = UUID.Zero.Guid; byte[] bucket;
msg.imSessionID = groupNoticeID.Guid;
msg.toAgentID = agentID.Guid; msg.toAgentID = agentID.Guid;
msg.dialog = dialog; msg.dialog = dialog;
// msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice;
msg.fromGroup = true; msg.fromGroup = true;
msg.offline = (byte)0; msg.offline = (byte)0;
msg.ParentEstateID = 0; msg.ParentEstateID = 0;
@ -933,13 +979,38 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
msg.timestamp = info.noticeData.Timestamp; msg.timestamp = info.noticeData.Timestamp;
msg.fromAgentName = info.noticeData.FromName; msg.fromAgentName = info.noticeData.FromName;
msg.message = info.noticeData.Subject + "|" + info.Message; msg.message = info.noticeData.Subject + "|" + info.Message;
msg.binaryBucket = info.BinaryBucket;
if (info.BinaryBucket[0] > 0)
{
//32 is due to not needing space for two of the UUIDs.
//(Don't need UUID of attachment or its owner in IM)
//50 offset gets us to start of attachment name.
//We are skipping the attachment flag, type, and
//the three UUID fields at the start of the bucket.
bucket = new byte[info.BinaryBucket.Length-32];
bucket[0] = 1; //Has attachment
bucket[1] = info.BinaryBucket[1];
Array.Copy(info.BinaryBucket, 50,
bucket, 18, info.BinaryBucket.Length-50);
}
else
{
bucket = new byte[19];
bucket[0] = 0; //No attachment
bucket[1] = 0; //Attachment type
bucket[18] = 0; //NUL terminate name
}
info.GroupID.ToBytes(bucket, 2);
msg.binaryBucket = bucket;
} }
else else
{ {
if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); if (m_debugEnabled)
m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID);
msg.fromAgentID = UUID.Zero.Guid; msg.fromAgentID = UUID.Zero.Guid;
msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
msg.fromAgentName = string.Empty; msg.fromAgentName = string.Empty;
msg.message = string.Empty; msg.message = string.Empty;
msg.binaryBucket = new byte[0]; msg.binaryBucket = new byte[0];
@ -1042,7 +1113,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// Send Message to Ejectee // Send Message to Ejectee
GridInstantMessage msg = new GridInstantMessage(); GridInstantMessage msg = new GridInstantMessage();
msg.imSessionID = UUID.Zero.Guid; msg.imSessionID = UUID.Zero.Guid;
msg.fromAgentID = agentID.Guid; msg.fromAgentID = agentID.Guid;
// msg.fromAgentID = info.GroupID; // msg.fromAgentID = info.GroupID;
@ -1063,7 +1134,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
// Message to ejector // Message to ejector
// Interop, received special 210 code for ejecting a group member // Interop, received special 210 code for ejecting a group member
// this only works within the comms servers domain, and won't work hypergrid // this only works within the comms servers domain, and won't work hypergrid
// TODO:FIXME: Use a presense server of some kind to find out where the // TODO:FIXME: Use a presense server of some kind to find out where the
// client actually is, and try contacting that region directly to notify them, // client actually is, and try contacting that region directly to notify them,
// or provide the notification via xmlrpc update queue // or provide the notification via xmlrpc update queue
@ -1232,7 +1303,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
if (!membership.ListInProfile) if (!membership.ListInProfile)
{ {
// If we're sending group info to remoteclient about another agent, // If we're sending group info to remoteclient about another agent,
// filter out groups the other agent doesn't want to share. // filter out groups the other agent doesn't want to share.
continue; continue;
} }
@ -1269,7 +1340,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient)); queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient));
} }
} }
private void SendScenePresenceUpdate(UUID AgentID, string Title) private void SendScenePresenceUpdate(UUID AgentID, string Title)
@ -1373,7 +1443,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
membershipArray = membershipData.ToArray(); membershipArray = membershipData.ToArray();
} }
} }
if (m_debugEnabled) if (m_debugEnabled)
{ {
m_log.InfoFormat("[GROUPS]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId); m_log.InfoFormat("[GROUPS]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId);
@ -1386,7 +1456,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return membershipArray; return membershipArray;
} }
private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle) private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
{ {
if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);

View File

@ -146,7 +146,7 @@ namespace OpenSim.Region.OptionalModules.PhysicsParameters
{ {
foreach (PhysParameterEntry ppe in physScene.GetParameterList()) foreach (PhysParameterEntry ppe in physScene.GetParameterList())
{ {
float val = 0.0f; string val = string.Empty;
if (physScene.GetPhysicsParameter(ppe.name, out val)) if (physScene.GetPhysicsParameter(ppe.name, out val))
{ {
WriteOut(" {0}/{1} = {2}", scene.RegionInfo.RegionName, ppe.name, val); WriteOut(" {0}/{1} = {2}", scene.RegionInfo.RegionName, ppe.name, val);
@ -159,7 +159,7 @@ namespace OpenSim.Region.OptionalModules.PhysicsParameters
} }
else else
{ {
float val = 0.0f; string val = string.Empty;
if (physScene.GetPhysicsParameter(parm, out val)) if (physScene.GetPhysicsParameter(parm, out val))
{ {
WriteOut(" {0}/{1} = {2}", scene.RegionInfo.RegionName, parm, val); WriteOut(" {0}/{1} = {2}", scene.RegionInfo.RegionName, parm, val);
@ -185,21 +185,12 @@ namespace OpenSim.Region.OptionalModules.PhysicsParameters
return; return;
} }
string parm = "xxx"; string parm = "xxx";
float val = 0f; string valparm = String.Empty;
uint localID = (uint)PhysParameterEntry.APPLY_TO_NONE; // set default value uint localID = (uint)PhysParameterEntry.APPLY_TO_NONE; // set default value
try try
{ {
parm = cmdparms[2]; parm = cmdparms[2];
string valparm = cmdparms[3].ToLower(); valparm = cmdparms[3].ToLower();
if (valparm == "true")
val = PhysParameterEntry.NUMERIC_TRUE;
else
{
if (valparm == "false")
val = PhysParameterEntry.NUMERIC_FALSE;
else
val = float.Parse(valparm, Culture.NumberFormatInfo);
}
if (cmdparms.Length > 4) if (cmdparms.Length > 4)
{ {
if (cmdparms[4].ToLower() == "all") if (cmdparms[4].ToLower() == "all")
@ -224,7 +215,7 @@ namespace OpenSim.Region.OptionalModules.PhysicsParameters
IPhysicsParameters physScene = scene.PhysicsScene as IPhysicsParameters; IPhysicsParameters physScene = scene.PhysicsScene as IPhysicsParameters;
if (physScene != null) if (physScene != null)
{ {
if (!physScene.SetPhysicsParameter(parm, val, localID)) if (!physScene.SetPhysicsParameter(parm, valparm, localID))
{ {
WriteError("Failed set of parameter '{0}' for region '{1}'", parm, scene.RegionInfo.RegionName); WriteError("Failed set of parameter '{0}' for region '{1}'", parm, scene.RegionInfo.RegionName);
} }

View File

@ -49,7 +49,7 @@ namespace OpenSim.Region.OptionalModules.World.MoneyModule
/// (such as land transfers). There is no money code here! Use FORGE as an example for money code. /// (such as land transfers). There is no money code here! Use FORGE as an example for money code.
/// Demo Economy/Money Module. This is a purposely crippled module! /// Demo Economy/Money Module. This is a purposely crippled module!
/// // To land transfer you need to add: /// // To land transfer you need to add:
/// -helperuri <ADDRESS TO THIS SERVER> /// -helperuri http://serveraddress:port/
/// to the command line parameters you use to start up your client /// to the command line parameters you use to start up your client
/// This commonly looks like -helperuri http://127.0.0.1:9000/ /// This commonly looks like -helperuri http://127.0.0.1:9000/
/// ///
@ -116,10 +116,9 @@ namespace OpenSim.Region.OptionalModules.World.MoneyModule
} }
/// <summary> /// <summary>
/// Startup /// Called on startup so the module can be configured.
/// </summary> /// </summary>
/// <param name="scene"></param> /// <param name="config">Configuration source.</param>
/// <param name="config"></param>
public void Initialise(IConfigSource config) public void Initialise(IConfigSource config)
{ {
m_gConfig = config; m_gConfig = config;
@ -674,9 +673,12 @@ namespace OpenSim.Region.OptionalModules.World.MoneyModule
} }
/// <summary> /// <summary>
/// When the client closes the connection we remove their accounting info from memory to free up resources. /// When the client closes the connection we remove their accounting
/// info from memory to free up resources.
/// </summary> /// </summary>
/// <param name="AgentID"></param> /// <param name="AgentID">UUID of agent</param>
/// <param name="scene">Scene the agent was connected to.</param>
/// <see cref="OpenSim.Region.Framework.Scenes.EventManager.ClientClosed"/>
public void ClientClosed(UUID AgentID, Scene scene) public void ClientClosed(UUID AgentID, Scene scene)
{ {

View File

@ -74,6 +74,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
[SetUp] [SetUp]
public void Init() public void Init()
{ {
base.SetUp();
IConfigSource config = new IniConfigSource(); IConfigSource config = new IniConfigSource();
config.AddConfig("NPC"); config.AddConfig("NPC");
config.Configs["NPC"].Set("Enabled", "true"); config.Configs["NPC"].Set("Enabled", "true");

View File

@ -0,0 +1,235 @@
/*
* 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.Linq;
using System.Reflection;
using System.Text;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.OptionalModules.Avatar.Attachments
{
/// <summary>
/// A module that just holds commands for inspecting avatar appearance.
/// </summary>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SceneCommandsModule")]
public class SceneCommandsModule : ISceneCommandsModule, INonSharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
public string Name { get { return "Scene Commands Module"; } }
public Type ReplaceableInterface { get { return null; } }
public void Initialise(IConfigSource source)
{
// m_log.DebugFormat("[SCENE COMMANDS MODULE]: INITIALIZED MODULE");
}
public void PostInitialise()
{
// m_log.DebugFormat("[SCENE COMMANDS MODULE]: POST INITIALIZED MODULE");
}
public void Close()
{
// m_log.DebugFormat("[SCENE COMMANDS MODULE]: CLOSED MODULE");
}
public void AddRegion(Scene scene)
{
// m_log.DebugFormat("[SCENE COMMANDS MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName);
m_scene = scene;
m_scene.RegisterModuleInterface<ISceneCommandsModule>(this);
}
public void RemoveRegion(Scene scene)
{
// m_log.DebugFormat("[SCENE COMMANDS MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName);
}
public void RegionLoaded(Scene scene)
{
// m_log.DebugFormat("[ATTACHMENTS COMMAND MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
scene.AddCommand(
"Debug", this, "debug scene get",
"debug scene get",
"List current scene options.",
"If active is false then main scene update and maintenance loops are suspended.\n"
+ "If animations is true then extra animations debug information is logged.\n"
+ "If collisions is false then collisions with other objects are turned off.\n"
+ "If pbackup is false then periodic scene backup is turned off.\n"
+ "If physics is false then all physics objects are non-physical.\n"
+ "If scripting is false then no scripting operations happen.\n"
+ "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.",
HandleDebugSceneGetCommand);
scene.AddCommand(
"Debug", this, "debug scene set",
"debug scene set active|collisions|pbackup|physics|scripting|teleport|updates true|false",
"Turn on scene debugging options.",
"If active is false then main scene update and maintenance loops are suspended.\n"
+ "If animations is true then extra animations debug information is logged.\n"
+ "If collisions is false then collisions with other objects are turned off.\n"
+ "If pbackup is false then periodic scene backup is turned off.\n"
+ "If physics is false then all physics objects are non-physical.\n"
+ "If scripting is false then no scripting operations happen.\n"
+ "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);
}
private void HandleDebugSceneGetCommand(string module, string[] args)
{
if (args.Length == 3)
{
if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null)
return;
OutputSceneDebugOptions();
}
else
{
MainConsole.Instance.Output("Usage: debug scene get");
}
}
private void OutputSceneDebugOptions()
{
ConsoleDisplayList cdl = new ConsoleDisplayList();
cdl.AddRow("active", m_scene.Active);
cdl.AddRow("animations", m_scene.DebugAnimations);
cdl.AddRow("pbackup", m_scene.PeriodicBackup);
cdl.AddRow("physics", m_scene.PhysicsEnabled);
cdl.AddRow("scripting", m_scene.ScriptsEnabled);
cdl.AddRow("teleport", m_scene.DebugTeleporting);
cdl.AddRow("updates", m_scene.DebugUpdates);
MainConsole.Instance.OutputFormat("Scene {0} options:", m_scene.Name);
MainConsole.Instance.Output(cdl.ToString());
}
private void HandleDebugSceneSetCommand(string module, string[] args)
{
if (args.Length == 5)
{
if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null)
return;
string key = args[3];
string value = args[4];
SetSceneDebugOptions(new Dictionary<string, string>() { { key, value } });
MainConsole.Instance.OutputFormat("Set {0} debug scene {1} = {2}", m_scene.Name, key, value);
}
else
{
MainConsole.Instance.Output(
"Usage: debug scene set active|collisions|pbackup|physics|scripting|teleport|updates true|false");
}
}
public void SetSceneDebugOptions(Dictionary<string, string> options)
{
if (options.ContainsKey("active"))
{
bool active;
if (bool.TryParse(options["active"], out active))
m_scene.Active = active;
}
if (options.ContainsKey("animations"))
{
bool active;
if (bool.TryParse(options["animations"], out active))
m_scene.DebugAnimations = active;
}
if (options.ContainsKey("pbackup"))
{
bool active;
if (bool.TryParse(options["pbackup"], out active))
m_scene.PeriodicBackup = active;
}
if (options.ContainsKey("scripting"))
{
bool enableScripts = true;
if (bool.TryParse(options["scripting"], out enableScripts))
m_scene.ScriptsEnabled = enableScripts;
}
if (options.ContainsKey("physics"))
{
bool enablePhysics;
if (bool.TryParse(options["physics"], out enablePhysics))
m_scene.PhysicsEnabled = enablePhysics;
}
// if (options.ContainsKey("collisions"))
// {
// // TODO: Implement. If false, should stop objects colliding, though possibly should still allow
// // the avatar themselves to collide with the ground.
// }
if (options.ContainsKey("teleport"))
{
bool enableTeleportDebugging;
if (bool.TryParse(options["teleport"], out enableTeleportDebugging))
m_scene.DebugTeleporting = enableTeleportDebugging;
}
if (options.ContainsKey("updates"))
{
bool enableUpdateDebugging;
if (bool.TryParse(options["updates"], out enableUpdateDebugging))
{
m_scene.DebugUpdates = enableUpdateDebugging;
GcNotify.Enabled = enableUpdateDebugging;
}
}
}
}
}

View File

@ -75,11 +75,11 @@ private sealed class BulletBodyUnman : BulletBody
private sealed class BulletShapeUnman : BulletShape private sealed class BulletShapeUnman : BulletShape
{ {
public IntPtr ptr; public IntPtr ptr;
public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ)
: base() : base()
{ {
ptr = xx; ptr = xx;
type = typ; shapeType = typ;
} }
public override bool HasPhysicalShape public override bool HasPhysicalShape
{ {
@ -91,7 +91,7 @@ private sealed class BulletShapeUnman : BulletShape
} }
public override BulletShape Clone() public override BulletShape Clone()
{ {
return new BulletShapeUnman(ptr, type); return new BulletShapeUnman(ptr, shapeType);
} }
public override bool ReferenceSame(BulletShape other) public override bool ReferenceSame(BulletShape other)
{ {
@ -166,7 +166,7 @@ public override BulletWorld Initialize(Vector3 maxPosition, ConfigurationParamet
// If Debug logging level, enable logging from the unmanaged code // If Debug logging level, enable logging from the unmanaged code
m_DebugLogCallbackHandle = null; m_DebugLogCallbackHandle = null;
if (BSScene.m_log.IsDebugEnabled || PhysicsScene.PhysicsLogging.Enabled) if (BSScene.m_log.IsDebugEnabled && PhysicsScene.PhysicsLogging.Enabled)
{ {
BSScene.m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", BSScene.LogHeader); BSScene.m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", BSScene.LogHeader);
if (PhysicsScene.PhysicsLogging.Enabled) if (PhysicsScene.PhysicsLogging.Enabled)
@ -202,7 +202,7 @@ private void BulletLoggerPhysLog(string msg)
} }
public override int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep, public override int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount, out int collidersCount) out int updatedEntityCount, out int collidersCount)
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
return BSAPICPP.PhysicsStep2(worldu.ptr, timeStep, maxSubSteps, fixedTimeStep, out updatedEntityCount, out collidersCount); return BSAPICPP.PhysicsStep2(worldu.ptr, timeStep, maxSubSteps, fixedTimeStep, out updatedEntityCount, out collidersCount);
@ -212,6 +212,19 @@ public override void Shutdown(BulletWorld world)
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.Shutdown2(worldu.ptr); BSAPICPP.Shutdown2(worldu.ptr);
if (m_paramsHandle.IsAllocated)
{
m_paramsHandle.Free();
}
if (m_collisionArrayPinnedHandle.IsAllocated)
{
m_collisionArrayPinnedHandle.Free();
}
if (m_updateArrayPinnedHandle.IsAllocated)
{
m_updateArrayPinnedHandle.Free();
}
} }
public override bool PushUpdate(BulletBody obj) public override bool PushUpdate(BulletBody obj)
@ -242,19 +255,38 @@ public override BulletShape CreateHullShape(BulletWorld world, int hullCount, fl
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
return new BulletShapeUnman( return new BulletShapeUnman(
BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls),
BSPhysicsShapeType.SHAPE_HULL); BSPhysicsShapeType.SHAPE_HULL);
} }
public override BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape) public override BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape, HACDParams parms)
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BulletShapeUnman shapeu = meshShape as BulletShapeUnman; BulletShapeUnman shapeu = meshShape as BulletShapeUnman;
return new BulletShapeUnman( return new BulletShapeUnman(
BSAPICPP.BuildHullShapeFromMesh2(worldu.ptr, shapeu.ptr), BSAPICPP.BuildHullShapeFromMesh2(worldu.ptr, shapeu.ptr, parms),
BSPhysicsShapeType.SHAPE_HULL); BSPhysicsShapeType.SHAPE_HULL);
} }
public override BulletShape BuildConvexHullShapeFromMesh(BulletWorld world, BulletShape meshShape)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletShapeUnman shapeu = meshShape as BulletShapeUnman;
return new BulletShapeUnman(
BSAPICPP.BuildConvexHullShapeFromMesh2(worldu.ptr, shapeu.ptr),
BSPhysicsShapeType.SHAPE_CONVEXHULL);
}
public override BulletShape CreateConvexHullShape(BulletWorld world,
int indicesCount, int[] indices,
int verticesCount, float[] vertices)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
return new BulletShapeUnman(
BSAPICPP.CreateConvexHullShape2(worldu.ptr, indicesCount, indices, verticesCount, vertices),
BSPhysicsShapeType.SHAPE_CONVEXHULL);
}
public override BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData) public override BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData)
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
@ -273,7 +305,7 @@ public override void SetShapeCollisionMargin(BulletShape shape, float margin)
{ {
BulletShapeUnman shapeu = shape as BulletShapeUnman; BulletShapeUnman shapeu = shape as BulletShapeUnman;
if (shapeu != null && shapeu.HasPhysicalShape) if (shapeu != null && shapeu.HasPhysicalShape)
BSAPICPP.SetShapeCollisionMargin2(shapeu.ptr, margin); BSAPICPP.SetShapeCollisionMargin(shapeu.ptr, margin);
} }
public override BulletShape BuildCapsuleShape(BulletWorld world, float radius, float height, Vector3 scale) public override BulletShape BuildCapsuleShape(BulletWorld world, float radius, float height, Vector3 scale)
@ -327,6 +359,12 @@ public override void RemoveChildShapeFromCompoundShape(BulletShape shape, Bullet
BSAPICPP.RemoveChildShapeFromCompoundShape2(shapeu.ptr, removeShapeu.ptr); BSAPICPP.RemoveChildShapeFromCompoundShape2(shapeu.ptr, removeShapeu.ptr);
} }
public override void UpdateChildTransform(BulletShape pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb)
{
BulletShapeUnman shapeu = pShape as BulletShapeUnman;
BSAPICPP.UpdateChildTransform2(shapeu.ptr, childIndex, pos, rot, shouldRecalculateLocalAabb);
}
public override void RecalculateCompoundShapeLocalAabb(BulletShape shape) public override void RecalculateCompoundShapeLocalAabb(BulletShape shape)
{ {
BulletShapeUnman shapeu = shape as BulletShapeUnman; BulletShapeUnman shapeu = shape as BulletShapeUnman;
@ -337,7 +375,7 @@ public override BulletShape DuplicateCollisionShape(BulletWorld world, BulletSha
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman; BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman;
return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.type); return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.shapeType);
} }
public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape) public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape)
@ -419,6 +457,28 @@ public override BulletConstraint Create6DofConstraintToPoint(BulletWorld world,
joinPoint, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); joinPoint, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
} }
public override BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1,
Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.Create6DofConstraintFixed2(worldu.ptr, bodyu1.ptr,
frameInBloc, frameInBrot, useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies));
}
public override BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.Create6DofSpringConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot,
frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
}
public override BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, public override BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 pivotinA, Vector3 pivotinB, Vector3 pivotinA, Vector3 pivotinB,
Vector3 axisInA, Vector3 axisInB, Vector3 axisInA, Vector3 axisInB,
@ -431,6 +491,52 @@ public override BulletConstraint CreateHingeConstraint(BulletWorld world, Bullet
pivotinA, pivotinB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); pivotinA, pivotinB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
} }
public override BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.CreateSliderConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot,
frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
}
public override BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot,
bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.CreateConeTwistConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot,
frame2loc, frame2rot, disableCollisionsBetweenLinkedBodies));
}
public override BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 axisInA, Vector3 axisInB,
float ratio, bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.CreateGearConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, axisInA, axisInB,
ratio, disableCollisionsBetweenLinkedBodies));
}
public override BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB,
bool disableCollisionsBetweenLinkedBodies)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman;
BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman;
return new BulletConstraintUnman(BSAPICPP.CreatePoint2PointConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, pivotInA, pivotInB,
disableCollisionsBetweenLinkedBodies));
}
public override void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse) public override void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse)
{ {
BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; BulletConstraintUnman constrainu = constrain as BulletConstraintUnman;
@ -530,12 +636,12 @@ public override void SetForceUpdateAllAabbs(BulletWorld world, bool force)
// btDynamicsWorld entries // btDynamicsWorld entries
public override bool AddObjectToWorld(BulletWorld world, BulletBody obj) public override bool AddObjectToWorld(BulletWorld world, BulletBody obj)
{ {
// Bullet resets several variables when an object is added to the world.
// Gravity is reset to world default depending on the static/dynamic
// type. Of course, the collision flags in the broadphase proxy are initialized to default.
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
// Bullet resets several variables when an object is added to the world.
// Gravity is reset to world default depending on the static/dynamic
// type. Of course, the collision flags in the broadphase proxy are initialized to default.
Vector3 origGrav = BSAPICPP.GetGravity2(bodyu.ptr); Vector3 origGrav = BSAPICPP.GetGravity2(bodyu.ptr);
bool ret = BSAPICPP.AddObjectToWorld2(worldu.ptr, bodyu.ptr); bool ret = BSAPICPP.AddObjectToWorld2(worldu.ptr, bodyu.ptr);
@ -921,6 +1027,7 @@ public override void SetCenterOfMassByPosRot(BulletBody obj, Vector3 pos, Quater
} }
// Add a force to the object as if its mass is one. // Add a force to the object as if its mass is one.
// Deep down in Bullet: m_totalForce += force*m_linearFactor;
public override void ApplyCentralForce(BulletBody obj, Vector3 force) public override void ApplyCentralForce(BulletBody obj, Vector3 force)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -964,6 +1071,7 @@ public override void SetSleepingThresholds(BulletBody obj, float lin_threshold,
BSAPICPP.SetSleepingThresholds2(bodyu.ptr, lin_threshold, ang_threshold); BSAPICPP.SetSleepingThresholds2(bodyu.ptr, lin_threshold, ang_threshold);
} }
// Deep down in Bullet: m_totalTorque += torque*m_angularFactor;
public override void ApplyTorque(BulletBody obj, Vector3 torque) public override void ApplyTorque(BulletBody obj, Vector3 torque)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -971,6 +1079,8 @@ public override void ApplyTorque(BulletBody obj, Vector3 torque)
} }
// Apply force at the given point. Will add torque to the object. // Apply force at the given point. Will add torque to the object.
// Deep down in Bullet: applyCentralForce(force);
// applyTorque(rel_pos.cross(force*m_linearFactor));
public override void ApplyForce(BulletBody obj, Vector3 force, Vector3 pos) public override void ApplyForce(BulletBody obj, Vector3 force, Vector3 pos)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -978,6 +1088,7 @@ public override void ApplyForce(BulletBody obj, Vector3 force, Vector3 pos)
} }
// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass. // Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass.
// Deep down in Bullet: m_linearVelocity += impulse *m_linearFactor * m_inverseMass;
public override void ApplyCentralImpulse(BulletBody obj, Vector3 imp) public override void ApplyCentralImpulse(BulletBody obj, Vector3 imp)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -985,6 +1096,7 @@ public override void ApplyCentralImpulse(BulletBody obj, Vector3 imp)
} }
// Apply impulse to the object's torque. Force is scaled by object's mass. // Apply impulse to the object's torque. Force is scaled by object's mass.
// Deep down in Bullet: m_angularVelocity += m_invInertiaTensorWorld * torque * m_angularFactor;
public override void ApplyTorqueImpulse(BulletBody obj, Vector3 imp) public override void ApplyTorqueImpulse(BulletBody obj, Vector3 imp)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -992,6 +1104,8 @@ public override void ApplyTorqueImpulse(BulletBody obj, Vector3 imp)
} }
// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces. // Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces.
// Deep down in Bullet: applyCentralImpulse(impulse);
// applyTorqueImpulse(rel_pos.cross(impulse*m_linearFactor));
public override void ApplyImpulse(BulletBody obj, Vector3 imp, Vector3 pos) public override void ApplyImpulse(BulletBody obj, Vector3 imp, Vector3 pos)
{ {
BulletBodyUnman bodyu = obj as BulletBodyUnman; BulletBodyUnman bodyu = obj as BulletBodyUnman;
@ -1259,6 +1373,16 @@ public override void DumpPhysicsStatistics(BulletWorld world)
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.DumpPhysicsStatistics2(worldu.ptr); BSAPICPP.DumpPhysicsStatistics2(worldu.ptr);
} }
public override void ResetBroadphasePool(BulletWorld world)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.ResetBroadphasePool(worldu.ptr);
}
public override void ResetConstraintSolver(BulletWorld world)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.ResetConstraintSolver(worldu.ptr);
}
// ===================================================================================== // =====================================================================================
// ===================================================================================== // =====================================================================================
@ -1306,7 +1430,15 @@ public static extern IntPtr CreateHullShape2(IntPtr world,
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls); int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildHullShapeFromMesh2(IntPtr world, IntPtr meshShape); public static extern IntPtr BuildHullShapeFromMesh2(IntPtr world, IntPtr meshShape, HACDParams parms);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildConvexHullShapeFromMesh2(IntPtr world, IntPtr meshShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateConvexHullShape2(IntPtr world,
int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices );
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData); public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData);
@ -1315,7 +1447,7 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
public static extern bool IsNativeShape2(IntPtr shape); public static extern bool IsNativeShape2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetShapeCollisionMargin2(IntPtr shape, float margin); public static extern void SetShapeCollisionMargin(IntPtr shape, float margin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
@ -1338,6 +1470,9 @@ public static extern IntPtr RemoveChildShapeFromCompoundShapeIndex2(IntPtr cShap
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void RemoveChildShapeFromCompoundShape2(IntPtr cShape, IntPtr removeShape); public static extern void RemoveChildShapeFromCompoundShape2(IntPtr cShape, IntPtr removeShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void UpdateChildTransform2(IntPtr pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void RecalculateCompoundShapeLocalAabb2(IntPtr cShape); public static extern void RecalculateCompoundShapeLocalAabb2(IntPtr cShape);
@ -1368,7 +1503,7 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj);
public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin); public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight,
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap,
float scaleFactor, float collisionMargin); float scaleFactor, float collisionMargin);
@ -1385,12 +1520,46 @@ public static extern IntPtr Create6DofConstraintToPoint2(IntPtr world, IntPtr ob
Vector3 joinPoint, Vector3 joinPoint,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Create6DofConstraintFixed2(IntPtr world, IntPtr obj1,
Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Create6DofSpringConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateHingeConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, public static extern IntPtr CreateHingeConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 pivotinA, Vector3 pivotinB, Vector3 pivotinA, Vector3 pivotinB,
Vector3 axisInA, Vector3 axisInB, Vector3 axisInA, Vector3 axisInB,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateSliderConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 frameInAloc, Quaternion frameInArot,
Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateConeTwistConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 frameInAloc, Quaternion frameInArot,
Vector3 frameInBloc, Quaternion frameInBrot,
bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateGearConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 axisInA, Vector3 axisInB,
float ratio, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreatePoint2PointConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 pivotInA, Vector3 pivotInB,
bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetConstraintEnable2(IntPtr constrain, float numericTrueFalse); public static extern void SetConstraintEnable2(IntPtr constrain, float numericTrueFalse);
@ -1832,6 +2001,12 @@ public static extern void DumpAllInfo2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpPhysicsStatistics2(IntPtr sim); public static extern void DumpPhysicsStatistics2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ResetBroadphasePool(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ResetConstraintSolver(IntPtr sim);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,351 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorAvatarMove : BSActor
{
BSVMotor m_velocityMotor;
// Set to true if we think we're going up stairs.
// This state is remembered because collisions will turn on and off as we go up stairs.
int m_walkingUpStairs;
float m_lastStepUp;
public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_velocityMotor = null;
m_walkingUpStairs = 0;
m_physicsScene.DetailLog("{0},BSActorAvatarMove,constructor", m_controllingPrim.LocalID);
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled && m_controllingPrim.IsPhysicallyActive; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
Enabled = false;
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorAvatarMove,refresh", m_controllingPrim.LocalID);
// If the object is physically active, add the hoverer prestep action
if (isActive)
{
ActivateAvatarMove();
}
else
{
DeactivateAvatarMove();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
// Nothing to do for the hoverer since it is all software at pre-step action time.
}
// Usually called when target velocity changes to set the current velocity and the target
// into the movement motor.
public void SetVelocityAndTarget(OMV.Vector3 vel, OMV.Vector3 targ, bool inTaintTime)
{
m_physicsScene.TaintedObject(inTaintTime, "BSActorAvatarMove.setVelocityAndTarget", delegate()
{
if (m_velocityMotor != null)
{
m_velocityMotor.Reset();
m_velocityMotor.SetTarget(targ);
m_velocityMotor.SetCurrent(vel);
m_velocityMotor.Enabled = true;
}
});
}
// If a hover motor has not been created, create one and start the hovering.
private void ActivateAvatarMove()
{
if (m_velocityMotor == null)
{
// Infinite decay and timescale values so motor only changes current to target values.
m_velocityMotor = new BSVMotor("BSCharacter.Velocity",
0.2f, // time scale
BSMotor.Infinite, // decay time scale
1f // efficiency
);
// _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */);
m_physicsScene.BeforeStep += Mover;
m_walkingUpStairs = 0;
}
}
private void DeactivateAvatarMove()
{
if (m_velocityMotor != null)
{
m_physicsScene.BeforeStep -= Mover;
m_velocityMotor = null;
}
}
// Called just before the simulation step. Update the vertical position for hoverness.
private void Mover(float timeStep)
{
// Don't do movement while the object is selected.
if (!isActive)
return;
// TODO: Decide if the step parameters should be changed depending on the avatar's
// state (flying, colliding, ...). There is code in ODE to do this.
// COMMENTARY: when the user is making the avatar walk, except for falling, the velocity
// specified for the avatar is the one that should be used. For falling, if the avatar
// is not flying and is not colliding then it is presumed to be falling and the Z
// component is not fooled with (thus allowing gravity to do its thing).
// When the avatar is standing, though, the user has specified a velocity of zero and
// the avatar should be standing. But if the avatar is pushed by something in the world
// (raising elevator platform, moving vehicle, ...) the avatar should be allowed to
// move. Thus, the velocity cannot be forced to zero. The problem is that small velocity
// errors can creap in and the avatar will slowly float off in some direction.
// So, the problem is that, when an avatar is standing, we cannot tell creaping error
// from real pushing.
// The code below uses whether the collider is static or moving to decide whether to zero motion.
m_velocityMotor.Step(timeStep);
m_controllingPrim.IsStationary = false;
// If we're not supposed to be moving, make sure things are zero.
if (m_velocityMotor.ErrorIsZero() && m_velocityMotor.TargetValue == OMV.Vector3.Zero)
{
// The avatar shouldn't be moving
m_velocityMotor.Zero();
if (m_controllingPrim.IsColliding)
{
// If we are colliding with a stationary object, presume we're standing and don't move around
if (!m_controllingPrim.ColliderIsMoving)
{
m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,collidingWithStationary,zeroingMotion", m_controllingPrim.LocalID);
m_controllingPrim.IsStationary = true;
m_controllingPrim.ZeroMotion(true /* inTaintTime */);
}
// Standing has more friction on the ground
if (m_controllingPrim.Friction != BSParam.AvatarStandingFriction)
{
m_controllingPrim.Friction = BSParam.AvatarStandingFriction;
m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction);
}
}
else
{
if (m_controllingPrim.Flying)
{
// Flying and not collising and velocity nearly zero.
m_controllingPrim.ZeroMotion(true /* inTaintTime */);
}
}
m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}",
m_controllingPrim.LocalID, m_velocityMotor.TargetValue, m_controllingPrim.IsColliding);
}
else
{
// Supposed to be moving.
OMV.Vector3 stepVelocity = m_velocityMotor.CurrentValue;
if (m_controllingPrim.Friction != BSParam.AvatarFriction)
{
// Probably starting up walking. Set friction to moving friction.
m_controllingPrim.Friction = BSParam.AvatarFriction;
m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction);
}
// If falling, we keep the world's downward vector no matter what the other axis specify.
// The check for RawVelocity.Z < 0 makes jumping work (temporary upward force).
if (!m_controllingPrim.Flying && !m_controllingPrim.IsColliding)
{
if (m_controllingPrim.RawVelocity.Z < 0)
stepVelocity.Z = m_controllingPrim.RawVelocity.Z;
// DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
}
// 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force.
OMV.Vector3 moveForce = (stepVelocity - m_controllingPrim.RawVelocity) * m_controllingPrim.Mass;
// Add special movement force to allow avatars to walk up stepped surfaces.
moveForce += WalkUpStairs();
m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}",
m_controllingPrim.LocalID, stepVelocity, m_controllingPrim.RawVelocity, m_controllingPrim.Mass, moveForce);
m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, moveForce);
}
}
// Decide if the character is colliding with a low object and compute a force to pop the
// avatar up so it can walk up and over the low objects.
private OMV.Vector3 WalkUpStairs()
{
OMV.Vector3 ret = OMV.Vector3.Zero;
m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4},avHeight={5}",
m_controllingPrim.LocalID, m_controllingPrim.IsColliding, m_controllingPrim.Flying,
m_controllingPrim.TargetVelocitySpeed, m_controllingPrim.CollisionsLastTick.Count, m_controllingPrim.Size.Z);
// This test is done if moving forward, not flying and is colliding with something.
// Check for stairs climbing if colliding, not flying and moving forward
if ( m_controllingPrim.IsColliding
&& !m_controllingPrim.Flying
&& m_controllingPrim.TargetVelocitySpeed > 0.1f )
{
// The range near the character's feet where we will consider stairs
// float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) + 0.05f;
// Note: there is a problem with the computation of the capsule height. Thus RawPosition is off
// from the height. Revisit size and this computation when height is scaled properly.
float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) - 0.05f;
float nearFeetHeightMax = nearFeetHeightMin + BSParam.AvatarStepHeight;
// Look for a collision point that is near the character's feet and is oriented the same as the charactor is.
// Find the highest 'good' collision.
OMV.Vector3 highestTouchPosition = OMV.Vector3.Zero;
foreach (KeyValuePair<uint, ContactPoint> kvp in m_controllingPrim.CollisionsLastTick.m_objCollisionList)
{
// Don't care about collisions with the terrain
if (kvp.Key > m_physicsScene.TerrainManager.HighestTerrainID)
{
OMV.Vector3 touchPosition = kvp.Value.Position;
m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,min={1},max={2},touch={3}",
m_controllingPrim.LocalID, nearFeetHeightMin, nearFeetHeightMax, touchPosition);
if (touchPosition.Z >= nearFeetHeightMin && touchPosition.Z <= nearFeetHeightMax)
{
// This contact is within the 'near the feet' range.
// The normal should be our contact point to the object so it is pointing away
// thus the difference between our facing orientation and the normal should be small.
OMV.Vector3 directionFacing = OMV.Vector3.UnitX * m_controllingPrim.RawOrientation;
OMV.Vector3 touchNormal = OMV.Vector3.Normalize(kvp.Value.SurfaceNormal);
float diff = Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal));
if (diff < BSParam.AvatarStepApproachFactor)
{
if (highestTouchPosition.Z < touchPosition.Z)
highestTouchPosition = touchPosition;
}
}
}
}
m_walkingUpStairs = 0;
// If there is a good step sensing, move the avatar over the step.
if (highestTouchPosition != OMV.Vector3.Zero)
{
// Remember that we are going up stairs. This is needed because collisions
// will stop when we move up so this smoothes out that effect.
m_walkingUpStairs = BSParam.AvatarStepSmoothingSteps;
m_lastStepUp = highestTouchPosition.Z - nearFeetHeightMin;
ret = ComputeStairCorrection(m_lastStepUp);
m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,touchPos={1},nearFeetMin={2},ret={3}",
m_controllingPrim.LocalID, highestTouchPosition, nearFeetHeightMin, ret);
}
}
else
{
// If we used to be going up stairs but are not now, smooth the case where collision goes away while
// we are bouncing up the stairs.
if (m_walkingUpStairs > 0)
{
m_walkingUpStairs--;
ret = ComputeStairCorrection(m_lastStepUp);
}
}
return ret;
}
private OMV.Vector3 ComputeStairCorrection(float stepUp)
{
OMV.Vector3 ret = OMV.Vector3.Zero;
OMV.Vector3 displacement = OMV.Vector3.Zero;
if (stepUp > 0f)
{
// Found the stairs contact point. Push up a little to raise the character.
if (BSParam.AvatarStepForceFactor > 0f)
{
float upForce = stepUp * m_controllingPrim.Mass * BSParam.AvatarStepForceFactor;
ret = new OMV.Vector3(0f, 0f, upForce);
}
// Also move the avatar up for the new height
if (BSParam.AvatarStepUpCorrectionFactor > 0f)
{
// Move the avatar up related to the height of the collision
displacement = new OMV.Vector3(0f, 0f, stepUp * BSParam.AvatarStepUpCorrectionFactor);
m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement;
}
else
{
if (BSParam.AvatarStepUpCorrectionFactor < 0f)
{
// Move the avatar up about the specified step height
displacement = new OMV.Vector3(0f, 0f, BSParam.AvatarStepHeight);
m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement;
}
}
m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs.ComputeStairCorrection,disp={1},force={2}",
m_controllingPrim.LocalID, displacement, ret);
}
return ret;
}
}
}

View File

@ -0,0 +1,173 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorHover : BSActor
{
private BSFMotor m_hoverMotor;
public BSActorHover(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_hoverMotor = null;
m_physicsScene.DetailLog("{0},BSActorHover,constructor", m_controllingPrim.LocalID);
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
Enabled = false;
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorHover,refresh", m_controllingPrim.LocalID);
// If not active any more, turn me off
if (!m_controllingPrim.HoverActive)
{
SetEnabled(false);
}
// If the object is physically active, add the hoverer prestep action
if (isActive)
{
ActivateHover();
}
else
{
DeactivateHover();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
// Nothing to do for the hoverer since it is all software at pre-step action time.
}
// If a hover motor has not been created, create one and start the hovering.
private void ActivateHover()
{
if (m_hoverMotor == null)
{
// Turning the target on
m_hoverMotor = new BSFMotor("BSActorHover",
m_controllingPrim.HoverTau, // timeScale
BSMotor.Infinite, // decay time scale
1f // efficiency
);
m_hoverMotor.SetTarget(ComputeCurrentHoverHeight());
m_hoverMotor.SetCurrent(m_controllingPrim.RawPosition.Z);
m_hoverMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG so motor will output detail log messages.
m_physicsScene.BeforeStep += Hoverer;
}
}
private void DeactivateHover()
{
if (m_hoverMotor != null)
{
m_physicsScene.BeforeStep -= Hoverer;
m_hoverMotor = null;
}
}
// Called just before the simulation step. Update the vertical position for hoverness.
private void Hoverer(float timeStep)
{
// Don't do hovering while the object is selected.
if (!isActive)
return;
m_hoverMotor.SetCurrent(m_controllingPrim.RawPosition.Z);
m_hoverMotor.SetTarget(ComputeCurrentHoverHeight());
float targetHeight = m_hoverMotor.Step(timeStep);
// 'targetHeight' is where we'd like the Z of the prim to be at this moment.
// Compute the amount of force to push us there.
float moveForce = (targetHeight - m_controllingPrim.RawPosition.Z) * m_controllingPrim.RawMass;
// Undo anything the object thinks it's doing at the moment
moveForce = -m_controllingPrim.RawVelocity.Z * m_controllingPrim.Mass;
m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, new OMV.Vector3(0f, 0f, moveForce));
m_physicsScene.DetailLog("{0},BSPrim.Hover,move,targHt={1},moveForce={2},mass={3}",
m_controllingPrim.LocalID, targetHeight, moveForce, m_controllingPrim.RawMass);
}
// Based on current position, determine what we should be hovering at now.
// Must recompute often. What if we walked offa cliff>
private float ComputeCurrentHoverHeight()
{
float ret = m_controllingPrim.HoverHeight;
float groundHeight = m_physicsScene.TerrainManager.GetTerrainHeightAtXYZ(m_controllingPrim.RawPosition);
switch (m_controllingPrim.HoverType)
{
case PIDHoverType.Ground:
ret = groundHeight + m_controllingPrim.HoverHeight;
break;
case PIDHoverType.GroundAndWater:
float waterHeight = m_physicsScene.TerrainManager.GetWaterLevelAtXYZ(m_controllingPrim.RawPosition);
if (groundHeight > waterHeight)
{
ret = groundHeight + m_controllingPrim.HoverHeight;
}
else
{
ret = waterHeight + m_controllingPrim.HoverHeight;
}
break;
}
return ret;
}
}
}

View File

@ -0,0 +1,187 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorLockAxis : BSActor
{
BSConstraint LockAxisConstraint = null;
public BSActorLockAxis(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_physicsScene.DetailLog("{0},BSActorLockAxis,constructor", m_controllingPrim.LocalID);
LockAxisConstraint = null;
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled && m_controllingPrim.IsPhysicallyActive; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
RemoveAxisLockConstraint();
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorLockAxis,refresh,lockedAxis={1},enabled={2},pActive={3}",
m_controllingPrim.LocalID, m_controllingPrim.LockedAngularAxis, Enabled, m_controllingPrim.IsPhysicallyActive);
// If all the axis are free, we don't need to exist
if (m_controllingPrim.LockedAngularAxis == m_controllingPrim.LockedAxisFree)
{
Enabled = false;
}
// If the object is physically active, add the axis locking constraint
if (isActive)
{
AddAxisLockConstraint();
}
else
{
RemoveAxisLockConstraint();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
if (LockAxisConstraint != null)
{
// If a constraint is set up, remove it from the physical scene
RemoveAxisLockConstraint();
// Schedule a call before the next simulation step to restore the constraint.
m_physicsScene.PostTaintObject("BSActorLockAxis:" + ActorName, m_controllingPrim.LocalID, delegate()
{
Refresh();
});
}
}
private void AddAxisLockConstraint()
{
if (LockAxisConstraint == null)
{
// Lock that axis by creating a 6DOF constraint that has one end in the world and
// the other in the object.
// http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=20817
// http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=26380
// Remove any existing axis constraint (just to be sure)
RemoveAxisLockConstraint();
BSConstraint6Dof axisConstrainer = new BSConstraint6Dof(m_physicsScene.World, m_controllingPrim.PhysBody,
OMV.Vector3.Zero, OMV.Quaternion.Identity,
false /* useLinearReferenceFrameB */, true /* disableCollisionsBetweenLinkedBodies */);
LockAxisConstraint = axisConstrainer;
m_physicsScene.Constraints.AddConstraint(LockAxisConstraint);
// The constraint is tied to the world and oriented to the prim.
// Free to move linearly in the region
OMV.Vector3 linearLow = OMV.Vector3.Zero;
OMV.Vector3 linearHigh = m_physicsScene.TerrainManager.DefaultRegionSize;
if (m_controllingPrim.LockedLinearAxis.X != BSPhysObject.FreeAxis)
{
linearLow.X = m_controllingPrim.RawPosition.X;
linearHigh.X = m_controllingPrim.RawPosition.X;
}
if (m_controllingPrim.LockedLinearAxis.Y != BSPhysObject.FreeAxis)
{
linearLow.Y = m_controllingPrim.RawPosition.Y;
linearHigh.Y = m_controllingPrim.RawPosition.Y;
}
if (m_controllingPrim.LockedLinearAxis.Z != BSPhysObject.FreeAxis)
{
linearLow.Z = m_controllingPrim.RawPosition.Z;
linearHigh.Z = m_controllingPrim.RawPosition.Z;
}
axisConstrainer.SetLinearLimits(linearLow, linearHigh);
// Angular with some axis locked
float fPI = (float)Math.PI;
OMV.Vector3 angularLow = new OMV.Vector3(-fPI, -fPI, -fPI);
OMV.Vector3 angularHigh = new OMV.Vector3(fPI, fPI, fPI);
if (m_controllingPrim.LockedAngularAxis.X != BSPhysObject.FreeAxis)
{
angularLow.X = 0f;
angularHigh.X = 0f;
}
if (m_controllingPrim.LockedAngularAxis.Y != BSPhysObject.FreeAxis)
{
angularLow.Y = 0f;
angularHigh.Y = 0f;
}
if (m_controllingPrim.LockedAngularAxis.Z != BSPhysObject.FreeAxis)
{
angularLow.Z = 0f;
angularHigh.Z = 0f;
}
if (!axisConstrainer.SetAngularLimits(angularLow, angularHigh))
{
m_physicsScene.DetailLog("{0},BSActorLockAxis.AddAxisLockConstraint,failedSetAngularLimits", m_controllingPrim.LocalID);
}
m_physicsScene.DetailLog("{0},BSActorLockAxis.AddAxisLockConstraint,create,linLow={1},linHi={2},angLow={3},angHi={4}",
m_controllingPrim.LocalID, linearLow, linearHigh, angularLow, angularHigh);
// Constants from one of the posts mentioned above and used in Bullet's ConstraintDemo.
axisConstrainer.TranslationalLimitMotor(true /* enable */, 5.0f, 0.1f);
axisConstrainer.RecomputeConstraintVariables(m_controllingPrim.RawMass);
}
}
private void RemoveAxisLockConstraint()
{
if (LockAxisConstraint != null)
{
m_physicsScene.Constraints.RemoveAndDestroyConstraint(LockAxisConstraint);
LockAxisConstraint = null;
m_physicsScene.DetailLog("{0},BSActorLockAxis.RemoveAxisLockConstraint,destroyingConstraint", m_controllingPrim.LocalID);
}
}
}
}

View File

@ -0,0 +1,157 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorMoveToTarget : BSActor
{
private BSVMotor m_targetMotor;
public BSActorMoveToTarget(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_targetMotor = null;
m_physicsScene.DetailLog("{0},BSActorMoveToTarget,constructor", m_controllingPrim.LocalID);
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
Enabled = false;
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorMoveToTarget,refresh,enabled={1},active={2},target={3},tau={4}",
m_controllingPrim.LocalID, Enabled, m_controllingPrim.MoveToTargetActive,
m_controllingPrim.MoveToTargetTarget, m_controllingPrim.MoveToTargetTau );
// If not active any more...
if (!m_controllingPrim.MoveToTargetActive)
{
Enabled = false;
}
if (isActive)
{
ActivateMoveToTarget();
}
else
{
DeactivateMoveToTarget();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
// Nothing to do for the moveToTarget since it is all software at pre-step action time.
}
// If a hover motor has not been created, create one and start the hovering.
private void ActivateMoveToTarget()
{
if (m_targetMotor == null)
{
// We're taking over after this.
m_controllingPrim.ZeroMotion(true);
m_targetMotor = new BSVMotor("BSActorMoveToTargget.Activate",
m_controllingPrim.MoveToTargetTau, // timeScale
BSMotor.Infinite, // decay time scale
1f // efficiency
);
m_targetMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG so motor will output detail log messages.
m_targetMotor.SetTarget(m_controllingPrim.MoveToTargetTarget);
m_targetMotor.SetCurrent(m_controllingPrim.RawPosition);
m_physicsScene.BeforeStep += Mover;
}
}
private void DeactivateMoveToTarget()
{
if (m_targetMotor != null)
{
m_physicsScene.BeforeStep -= Mover;
m_targetMotor = null;
}
}
// Called just before the simulation step. Update the vertical position for hoverness.
private void Mover(float timeStep)
{
// Don't do hovering while the object is selected.
if (!isActive)
return;
OMV.Vector3 origPosition = m_controllingPrim.RawPosition; // DEBUG DEBUG (for printout below)
// 'movePosition' is where we'd like the prim to be at this moment.
OMV.Vector3 movePosition = m_controllingPrim.RawPosition + m_targetMotor.Step(timeStep);
// If we are very close to our target, turn off the movement motor.
if (m_targetMotor.ErrorIsZero())
{
m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover,zeroMovement,movePos={1},pos={2},mass={3}",
m_controllingPrim.LocalID, movePosition, m_controllingPrim.RawPosition, m_controllingPrim.Mass);
m_controllingPrim.ForcePosition = m_targetMotor.TargetValue;
// Setting the position does not cause the physics engine to generate a property update. Force it.
m_physicsScene.PE.PushUpdate(m_controllingPrim.PhysBody);
}
else
{
m_controllingPrim.ForcePosition = movePosition;
// Setting the position does not cause the physics engine to generate a property update. Force it.
m_physicsScene.PE.PushUpdate(m_controllingPrim.PhysBody);
}
m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover,move,fromPos={1},movePos={2}", m_controllingPrim.LocalID, origPosition, movePosition);
}
}
}

View File

@ -0,0 +1,137 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorSetForce : BSActor
{
BSFMotor m_forceMotor;
public BSActorSetForce(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_forceMotor = null;
m_physicsScene.DetailLog("{0},BSActorSetForce,constructor", m_controllingPrim.LocalID);
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled && m_controllingPrim.IsPhysicallyActive; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
Enabled = false;
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorSetForce,refresh", m_controllingPrim.LocalID);
// If not active any more, get rid of me (shouldn't ever happen, but just to be safe)
if (m_controllingPrim.RawForce == OMV.Vector3.Zero)
{
m_physicsScene.DetailLog("{0},BSActorSetForce,refresh,notSetForce,removing={1}", m_controllingPrim.LocalID, ActorName);
Enabled = false;
return;
}
// If the object is physically active, add the hoverer prestep action
if (isActive)
{
ActivateSetForce();
}
else
{
DeactivateSetForce();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
// Nothing to do for the hoverer since it is all software at pre-step action time.
}
// If a hover motor has not been created, create one and start the hovering.
private void ActivateSetForce()
{
if (m_forceMotor == null)
{
// A fake motor that might be used someday
m_forceMotor = new BSFMotor("setForce", 1f, 1f, 1f);
m_physicsScene.BeforeStep += Mover;
}
}
private void DeactivateSetForce()
{
if (m_forceMotor != null)
{
m_physicsScene.BeforeStep -= Mover;
m_forceMotor = null;
}
}
// Called just before the simulation step. Update the vertical position for hoverness.
private void Mover(float timeStep)
{
// Don't do force while the object is selected.
if (!isActive)
return;
m_physicsScene.DetailLog("{0},BSActorSetForce,preStep,force={1}", m_controllingPrim.LocalID, m_controllingPrim.RawForce);
if (m_controllingPrim.PhysBody.HasPhysicalBody)
{
m_physicsScene.PE.ApplyCentralForce(m_controllingPrim.PhysBody, m_controllingPrim.RawForce);
m_controllingPrim.ActivateIfPhysical(false);
}
// TODO:
}
}
}

View File

@ -0,0 +1,138 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorSetTorque : BSActor
{
BSFMotor m_torqueMotor;
public BSActorSetTorque(BSScene physicsScene, BSPhysObject pObj, string actorName)
: base(physicsScene, pObj, actorName)
{
m_torqueMotor = null;
m_physicsScene.DetailLog("{0},BSActorSetTorque,constructor", m_controllingPrim.LocalID);
}
// BSActor.isActive
public override bool isActive
{
get { return Enabled && m_controllingPrim.IsPhysicallyActive; }
}
// Release any connections and resources used by the actor.
// BSActor.Dispose()
public override void Dispose()
{
Enabled = false;
}
// Called when physical parameters (properties set in Bullet) need to be re-applied.
// Called at taint-time.
// BSActor.Refresh()
public override void Refresh()
{
m_physicsScene.DetailLog("{0},BSActorSetTorque,refresh,torque={1}", m_controllingPrim.LocalID, m_controllingPrim.RawTorque);
// If not active any more, get rid of me (shouldn't ever happen, but just to be safe)
if (m_controllingPrim.RawTorque == OMV.Vector3.Zero)
{
m_physicsScene.DetailLog("{0},BSActorSetTorque,refresh,notSetTorque,disabling={1}", m_controllingPrim.LocalID, ActorName);
Enabled = false;
return;
}
// If the object is physically active, add the hoverer prestep action
if (isActive)
{
ActivateSetTorque();
}
else
{
DeactivateSetTorque();
}
}
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time.
// BSActor.RemoveDependencies()
public override void RemoveDependencies()
{
// Nothing to do for the hoverer since it is all software at pre-step action time.
}
// If a hover motor has not been created, create one and start the hovering.
private void ActivateSetTorque()
{
if (m_torqueMotor == null)
{
// A fake motor that might be used someday
m_torqueMotor = new BSFMotor("setTorque", 1f, 1f, 1f);
m_physicsScene.BeforeStep += Mover;
}
}
private void DeactivateSetTorque()
{
if (m_torqueMotor != null)
{
m_physicsScene.BeforeStep -= Mover;
m_torqueMotor = null;
}
}
// Called just before the simulation step. Update the vertical position for hoverness.
private void Mover(float timeStep)
{
// Don't do force while the object is selected.
if (!isActive)
return;
m_physicsScene.DetailLog("{0},BSActorSetTorque,preStep,force={1}", m_controllingPrim.LocalID, m_controllingPrim.RawTorque);
if (m_controllingPrim.PhysBody.HasPhysicalBody)
{
m_controllingPrim.AddAngularForce(m_controllingPrim.RawTorque, false, true);
m_controllingPrim.ActivateIfPhysical(false);
}
// TODO:
}
}
}

View File

@ -0,0 +1,160 @@
/*
* 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 copyrightD
* 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.Text;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSActorCollection
{
private BSScene m_physicsScene { get; set; }
private Dictionary<string, BSActor> m_actors;
public BSActorCollection(BSScene physicsScene)
{
m_physicsScene = physicsScene;
m_actors = new Dictionary<string, BSActor>();
}
public void Add(string name, BSActor actor)
{
lock (m_actors)
{
if (!m_actors.ContainsKey(name))
{
m_actors[name] = actor;
}
}
}
public bool RemoveAndRelease(string name)
{
bool ret = false;
lock (m_actors)
{
if (m_actors.ContainsKey(name))
{
BSActor beingRemoved = m_actors[name];
m_actors.Remove(name);
beingRemoved.Dispose();
ret = true;
}
}
return ret;
}
public void Clear()
{
lock (m_actors)
{
Release();
m_actors.Clear();
}
}
public void Dispose()
{
Clear();
}
public bool HasActor(string name)
{
return m_actors.ContainsKey(name);
}
public bool TryGetActor(string actorName, out BSActor theActor)
{
return m_actors.TryGetValue(actorName, out theActor);
}
public void ForEachActor(Action<BSActor> act)
{
lock (m_actors)
{
foreach (KeyValuePair<string, BSActor> kvp in m_actors)
act(kvp.Value);
}
}
public void Enable(bool enabl)
{
ForEachActor(a => a.SetEnabled(enabl));
}
public void Release()
{
ForEachActor(a => a.Dispose());
}
public void Refresh()
{
ForEachActor(a => a.Refresh());
}
public void RemoveDependencies()
{
ForEachActor(a => a.RemoveDependencies());
}
}
// =============================================================================
/// <summary>
/// Each physical object can have 'actors' who are pushing the object around.
/// This can be used for hover, locking axis, making vehicles, etc.
/// Each physical object can have multiple actors acting on it.
///
/// An actor usually registers itself with physics scene events (pre-step action)
/// and modifies the parameters on the host physical object.
/// </summary>
public abstract class BSActor
{
protected BSScene m_physicsScene { get; private set; }
protected BSPhysObject m_controllingPrim { get; private set; }
public virtual bool Enabled { get; set; }
public string ActorName { get; private set; }
public BSActor(BSScene physicsScene, BSPhysObject pObj, string actorName)
{
m_physicsScene = physicsScene;
m_controllingPrim = pObj;
ActorName = actorName;
Enabled = true;
}
// Return 'true' if activily updating the prim
public virtual bool isActive
{
get { return Enabled; }
}
// Turn the actor on an off. Only used by ActorCollection to set all enabled/disabled.
// Anyone else should assign true/false to 'Enabled'.
public void SetEnabled(bool setEnabled)
{
Enabled = setEnabled;
}
// Release any connections and resources used by the actor.
public abstract void Dispose();
// Called when physical parameters (properties set in Bullet) need to be re-applied.
public abstract void Refresh();
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...).
// Register a prestep action to restore physical requirements before the next simulation step.
public abstract void RemoveDependencies();
}
}

View File

@ -6,7 +6,7 @@
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the * * Neither the name of the OpenSimulator Project nor the
@ -70,6 +70,7 @@ public enum BSPhysicsShapeType
SHAPE_COMPOUND = 22, SHAPE_COMPOUND = 22,
SHAPE_HEIGHTMAP = 23, SHAPE_HEIGHTMAP = 23,
SHAPE_AVATAR = 24, SHAPE_AVATAR = 24,
SHAPE_CONVEXHULL= 25,
}; };
// The native shapes have predefined shape hash keys // The native shapes have predefined shape hash keys
@ -87,7 +88,7 @@ public enum FixedShapeKey : ulong
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct ShapeData public struct ShapeData
{ {
public uint ID; public UInt32 ID;
public BSPhysicsShapeType Type; public BSPhysicsShapeType Type;
public Vector3 Position; public Vector3 Position;
public Quaternion Rotation; public Quaternion Rotation;
@ -111,7 +112,7 @@ public struct ShapeData
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SweepHit public struct SweepHit
{ {
public uint ID; public UInt32 ID;
public float Fraction; public float Fraction;
public Vector3 Normal; public Vector3 Normal;
public Vector3 Point; public Vector3 Point;
@ -119,27 +120,47 @@ public struct SweepHit
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct RaycastHit public struct RaycastHit
{ {
public uint ID; public UInt32 ID;
public float Fraction; public float Fraction;
public Vector3 Normal; public Vector3 Normal;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct CollisionDesc public struct CollisionDesc
{ {
public uint aID; public UInt32 aID;
public uint bID; public UInt32 bID;
public Vector3 point; public Vector3 point;
public Vector3 normal; public Vector3 normal;
public float penetration;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct EntityProperties public struct EntityProperties
{ {
public uint ID; public UInt32 ID;
public Vector3 Position; public Vector3 Position;
public Quaternion Rotation; public Quaternion Rotation;
public Vector3 Velocity; public Vector3 Velocity;
public Vector3 Acceleration; public Vector3 Acceleration;
public Vector3 RotationalVelocity; public Vector3 RotationalVelocity;
public override string ToString()
{
StringBuilder buff = new StringBuilder();
buff.Append("<i=");
buff.Append(ID.ToString());
buff.Append(",p=");
buff.Append(Position.ToString());
buff.Append(",r=");
buff.Append(Rotation.ToString());
buff.Append(",v=");
buff.Append(Velocity.ToString());
buff.Append(",a=");
buff.Append(Acceleration.ToString());
buff.Append(",rv=");
buff.Append(RotationalVelocity.ToString());
buff.Append(">");
return buff.ToString();
}
} }
// Format of this structure must match the definition in the C++ code // Format of this structure must match the definition in the C++ code
@ -154,32 +175,6 @@ public struct ConfigurationParameters
public float collisionMargin; public float collisionMargin;
public float gravity; public float gravity;
public float XlinearDamping;
public float XangularDamping;
public float XdeactivationTime;
public float XlinearSleepingThreshold;
public float XangularSleepingThreshold;
public float XccdMotionThreshold;
public float XccdSweptSphereRadius;
public float XcontactProcessingThreshold;
public float XterrainImplementation;
public float XterrainFriction;
public float XterrainHitFraction;
public float XterrainRestitution;
public float XterrainCollisionMargin;
public float XavatarFriction;
public float XavatarStandingFriction;
public float XavatarDensity;
public float XavatarRestitution;
public float XavatarCapsuleWidth;
public float XavatarCapsuleDepth;
public float XavatarCapsuleHeight;
public float XavatarContactProcessingThreshold;
public float XvehicleAngularDamping;
public float maxPersistantManifoldPoolSize; public float maxPersistantManifoldPoolSize;
public float maxCollisionAlgorithmPoolSize; public float maxCollisionAlgorithmPoolSize;
public float shouldDisableContactPoolDynamicAllocation; public float shouldDisableContactPoolDynamicAllocation;
@ -188,22 +183,30 @@ public struct ConfigurationParameters
public float shouldSplitSimulationIslands; public float shouldSplitSimulationIslands;
public float shouldEnableFrictionCaching; public float shouldEnableFrictionCaching;
public float numberOfSolverIterations; public float numberOfSolverIterations;
public float useSingleSidedMeshes;
public float globalContactBreakingThreshold;
public float XlinksetImplementation; public float physicsLoggingFrames;
public float XlinkConstraintUseFrameOffset;
public float XlinkConstraintEnableTransMotor;
public float XlinkConstraintTransMotorMaxVel;
public float XlinkConstraintTransMotorMaxForce;
public float XlinkConstraintERP;
public float XlinkConstraintCFM;
public float XlinkConstraintSolverIterations;
public float XphysicsLoggingFrames;
public const float numericTrue = 1f; public const float numericTrue = 1f;
public const float numericFalse = 0f; public const float numericFalse = 0f;
} }
// Parameters passed for the conversion of a mesh to a hull using Bullet's HACD library.
[StructLayout(LayoutKind.Sequential)]
public struct HACDParams
{
// usual default values
public float maxVerticesPerHull; // 100
public float minClusters; // 2
public float compacityWeight; // 0.1
public float volumeWeight; // 0.0
public float concavity; // 100
public float addExtraDistPoints; // false
public float addNeighboursDistPoints; // false
public float addFacesPoints; // false
public float shouldAdjustCollisionMargin; // false
}
// The states a bullet collision object can have // The states a bullet collision object can have
public enum ActivationState : uint public enum ActivationState : uint
@ -238,9 +241,10 @@ public enum CollisionFlags : uint
CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
// Following used by BulletSim to control collisions and updates // Following used by BulletSim to control collisions and updates
BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, // return collision events from unmanaged to managed
BS_FLOATS_ON_WATER = 1 << 11, BS_FLOATS_ON_WATER = 1 << 11, // the object should float at water level
BS_VEHICLE_COLLISIONS = 1 << 12, BS_VEHICLE_COLLISIONS = 1 << 12, // return collisions for vehicle ground checking
BS_RETURN_ROOT_COMPOUND_SHAPE = 1 << 13, // return the pos/rot of the root shape in a compound shape
BS_NONE = 0, BS_NONE = 0,
BS_ALL = 0xFFFFFFFF BS_ALL = 0xFFFFFFFF
}; };
@ -294,7 +298,7 @@ public abstract class BSAPITemplate
{ {
// Returns the name of the underlying Bullet engine // Returns the name of the underlying Bullet engine
public abstract string BulletEngineName { get; } public abstract string BulletEngineName { get; }
public abstract string BulletEngineVersion { get; protected set;} public abstract string BulletEngineVersion { get; protected set;}
// Initialization and simulation // Initialization and simulation
public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms,
@ -305,7 +309,7 @@ public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParamet
public abstract int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep, public abstract int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep,
out int updatedEntityCount, out int collidersCount); out int updatedEntityCount, out int collidersCount);
public abstract bool UpdateParameter(BulletWorld world, uint localID, String parm, float value); public abstract bool UpdateParameter(BulletWorld world, UInt32 localID, String parm, float value);
public abstract void Shutdown(BulletWorld sim); public abstract void Shutdown(BulletWorld sim);
@ -320,7 +324,13 @@ public abstract BulletShape CreateMeshShape(BulletWorld world,
public abstract BulletShape CreateHullShape(BulletWorld world, public abstract BulletShape CreateHullShape(BulletWorld world,
int hullCount, float[] hulls); int hullCount, float[] hulls);
public abstract BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape); public abstract BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape, HACDParams parms);
public abstract BulletShape BuildConvexHullShapeFromMesh(BulletWorld world, BulletShape meshShape);
public abstract BulletShape CreateConvexHullShape(BulletWorld world,
int indicesCount, int[] indices,
int verticesCount, float[] vertices );
public abstract BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData); public abstract BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData);
@ -342,26 +352,28 @@ public abstract BulletShape RemoveChildShapeFromCompoundShapeIndex(BulletShape c
public abstract void RemoveChildShapeFromCompoundShape(BulletShape cShape, BulletShape removeShape); public abstract void RemoveChildShapeFromCompoundShape(BulletShape cShape, BulletShape removeShape);
public abstract void UpdateChildTransform(BulletShape pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb);
public abstract void RecalculateCompoundShapeLocalAabb(BulletShape cShape); public abstract void RecalculateCompoundShapeLocalAabb(BulletShape cShape);
public abstract BulletShape DuplicateCollisionShape(BulletWorld sim, BulletShape srcShape, uint id); public abstract BulletShape DuplicateCollisionShape(BulletWorld sim, BulletShape srcShape, UInt32 id);
public abstract bool DeleteCollisionShape(BulletWorld world, BulletShape shape); public abstract bool DeleteCollisionShape(BulletWorld world, BulletShape shape);
public abstract CollisionObjectTypes GetBodyType(BulletBody obj); public abstract CollisionObjectTypes GetBodyType(BulletBody obj);
public abstract BulletBody CreateBodyFromShape(BulletWorld sim, BulletShape shape, uint id, Vector3 pos, Quaternion rot); public abstract BulletBody CreateBodyFromShape(BulletWorld sim, BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot);
public abstract BulletBody CreateBodyWithDefaultMotionState(BulletShape shape, uint id, Vector3 pos, Quaternion rot); public abstract BulletBody CreateBodyWithDefaultMotionState(BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot);
public abstract BulletBody CreateGhostFromShape(BulletWorld sim, BulletShape shape, uint id, Vector3 pos, Quaternion rot); public abstract BulletBody CreateGhostFromShape(BulletWorld sim, BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot);
public abstract void DestroyObject(BulletWorld sim, BulletBody obj); public abstract void DestroyObject(BulletWorld sim, BulletBody obj);
// ===================================================================================== // =====================================================================================
public abstract BulletShape CreateGroundPlaneShape(uint id, float height, float collisionMargin); public abstract BulletShape CreateGroundPlaneShape(UInt32 id, float height, float collisionMargin);
public abstract BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap,
float scaleFactor, float collisionMargin); float scaleFactor, float collisionMargin);
// ===================================================================================== // =====================================================================================
@ -375,11 +387,38 @@ public abstract BulletConstraint Create6DofConstraintToPoint(BulletWorld world,
Vector3 joinPoint, Vector3 joinPoint,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1,
Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, public abstract BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 pivotinA, Vector3 pivotinB, Vector3 pivotinA, Vector3 pivotinB,
Vector3 axisInA, Vector3 axisInB, Vector3 axisInA, Vector3 axisInB,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frameInAloc, Quaternion frameInArot,
Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 frameInAloc, Quaternion frameInArot,
Vector3 frameInBloc, Quaternion frameInBrot,
bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 axisInA, Vector3 axisInB,
float ratio, bool disableCollisionsBetweenLinkedBodies);
public abstract BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB,
bool disableCollisionsBetweenLinkedBodies);
public abstract void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse); public abstract void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse);
public abstract void SetConstraintNumSolverIterations(BulletConstraint constrain, float iterations); public abstract void SetConstraintNumSolverIterations(BulletConstraint constrain, float iterations);
@ -607,7 +646,7 @@ public abstract BulletConstraint GetConstraintRef(BulletBody obj, int index);
public abstract int GetNumConstraintRefs(BulletBody obj); public abstract int GetNumConstraintRefs(BulletBody obj);
public abstract bool SetCollisionGroupMask(BulletBody body, uint filter, uint mask); public abstract bool SetCollisionGroupMask(BulletBody body, UInt32 filter, UInt32 mask);
// ===================================================================================== // =====================================================================================
// btCollisionShape entries // btCollisionShape entries
@ -646,17 +685,21 @@ public abstract float GetMargin(BulletShape shape);
// ===================================================================================== // =====================================================================================
// Debugging // Debugging
public abstract void DumpRigidBody(BulletWorld sim, BulletBody collisionObject); public virtual void DumpRigidBody(BulletWorld sim, BulletBody collisionObject) { }
public abstract void DumpCollisionShape(BulletWorld sim, BulletShape collisionShape); public virtual void DumpCollisionShape(BulletWorld sim, BulletShape collisionShape) { }
public abstract void DumpConstraint(BulletWorld sim, BulletConstraint constrain); public virtual void DumpConstraint(BulletWorld sim, BulletConstraint constrain) { }
public abstract void DumpActivationInfo(BulletWorld sim); public virtual void DumpActivationInfo(BulletWorld sim) { }
public abstract void DumpAllInfo(BulletWorld sim); public virtual void DumpAllInfo(BulletWorld sim) { }
public abstract void DumpPhysicsStatistics(BulletWorld sim); public virtual void DumpPhysicsStatistics(BulletWorld sim) { }
public virtual void ResetBroadphasePool(BulletWorld sim) { }
public virtual void ResetConstraintSolver(BulletWorld sim) { }
}; };
} }

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
@ -45,11 +45,7 @@ public sealed class BSCharacter : BSPhysObject
private bool _selected; private bool _selected;
private OMV.Vector3 _position; private OMV.Vector3 _position;
private float _mass; private float _mass;
private float _avatarDensity;
private float _avatarVolume; private float _avatarVolume;
private OMV.Vector3 _force;
private OMV.Vector3 _velocity;
private OMV.Vector3 _torque;
private float _collisionScore; private float _collisionScore;
private OMV.Vector3 _acceleration; private OMV.Vector3 _acceleration;
private OMV.Quaternion _orientation; private OMV.Quaternion _orientation;
@ -58,25 +54,17 @@ public sealed class BSCharacter : BSPhysObject
private bool _flying; private bool _flying;
private bool _setAlwaysRun; private bool _setAlwaysRun;
private bool _throttleUpdates; private bool _throttleUpdates;
private bool _isColliding;
private bool _collidingObj;
private bool _floatOnWater; private bool _floatOnWater;
private OMV.Vector3 _rotationalVelocity; private OMV.Vector3 _rotationalVelocity;
private bool _kinematic; private bool _kinematic;
private float _buoyancy; private float _buoyancy;
// The friction and velocity of the avatar is modified depending on whether walking or not. private BSActorAvatarMove m_moveActor;
private float _currentFriction; // the friction currently being used (changed by setVelocity). private const string AvatarMoveActorName = "BSCharacter.AvatarMove";
private BSVMotor _velocityMotor;
private OMV.Vector3 _PIDTarget; private OMV.Vector3 _PIDTarget;
private bool _usePID; private bool _usePID;
private float _PIDTau; private float _PIDTau;
private bool _useHoverPID;
private float _PIDHoverHeight;
private PIDHoverType _PIDHoverType;
private float _PIDHoverTao;
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying) public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
: base(parent_scene, localID, avName, "BSCharacter") : base(parent_scene, localID, avName, "BSCharacter")
@ -86,10 +74,10 @@ public sealed class BSCharacter : BSPhysObject
_flying = isFlying; _flying = isFlying;
_orientation = OMV.Quaternion.Identity; _orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero; RawVelocity = OMV.Vector3.Zero;
_buoyancy = ComputeBuoyancyFromFlying(isFlying); _buoyancy = ComputeBuoyancyFromFlying(isFlying);
_currentFriction = BSParam.AvatarStandingFriction; Friction = BSParam.AvatarStandingFriction;
_avatarDensity = BSParam.AvatarDensity; Density = BSParam.AvatarDensity / BSParam.DensityScaleFactor;
// Old versions of ScenePresence passed only the height. If width and/or depth are zero, // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
// replace with the default values. // replace with the default values.
@ -103,17 +91,22 @@ public sealed class BSCharacter : BSPhysObject
// set _avatarVolume and _mass based on capsule size, _density and Scale // set _avatarVolume and _mass based on capsule size, _density and Scale
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
SetupMovementMotor(); // The avatar's movement is controlled by this motor that speeds up and slows down
// the avatar seeking to reach the motor's target speed.
// This motor runs as a prestep action for the avatar so it will keep the avatar
// standing as well as moving. Destruction of the avatar will destroy the pre-step action.
m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName);
PhysicalActors.Add(AvatarMoveActorName, m_moveActor);
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); LocalID, _size, Scale, Density, _avatarVolume, RawMass);
// do actual creation in taint time // do actual creation in taint time
PhysicsScene.TaintedObject("BSCharacter.create", delegate() PhysScene.TaintedObject("BSCharacter.create", delegate()
{ {
DetailLog("{0},BSCharacter.create,taint", LocalID); DetailLog("{0},BSCharacter.create,taint", LocalID);
// New body and shape into PhysBody and PhysShape // New body and shape into PhysBody and PhysShape
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this); PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this);
SetPhysicalProperties(); SetPhysicalProperties();
}); });
@ -126,114 +119,63 @@ public sealed class BSCharacter : BSPhysObject
base.Destroy(); base.Destroy();
DetailLog("{0},BSCharacter.Destroy", LocalID); DetailLog("{0},BSCharacter.Destroy", LocalID);
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() PhysScene.TaintedObject("BSCharacter.destroy", delegate()
{ {
PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */);
PhysBody.Clear(); PhysBody.Clear();
PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); PhysShape.Dereference(PhysScene);
PhysShape.Clear(); PhysShape = new BSShapeNull();
}); });
} }
private void SetPhysicalProperties() private void SetPhysicalProperties()
{ {
PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody);
ZeroMotion(true); ZeroMotion(true);
ForcePosition = _position; ForcePosition = _position;
// Set the velocity and compute the proper friction // Set the velocity
_velocityMotor.Reset(); if (m_moveActor != null)
_velocityMotor.SetTarget(_velocity); m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, false);
_velocityMotor.SetCurrent(_velocity);
ForceVelocity = _velocity; ForceVelocity = RawVelocity;
// This will enable or disable the flying buoyancy of the avatar. // This will enable or disable the flying buoyancy of the avatar.
// Needs to be reset especially when an avatar is recreated after crossing a region boundry. // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
Flying = _flying; Flying = _flying;
PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution);
PhysicsScene.PE.SetMargin(PhysShape, PhysicsScene.Params.collisionMargin); PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin);
PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale);
PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
if (BSParam.CcdMotionThreshold > 0f) if (BSParam.CcdMotionThreshold > 0f)
{ {
PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
} }
UpdatePhysicalMassProperties(RawMass, false); UpdatePhysicalMassProperties(RawMass, false);
// Make so capsule does not fall over // Make so capsule does not fall over
PhysicsScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero);
PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); // The avatar mover sets some parameters.
PhysicalActors.Refresh();
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT);
PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody);
// PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG);
PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION);
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody);
// Do this after the object has been added to the world // Do this after the object has been added to the world
PhysBody.collisionType = CollisionType.Avatar; PhysBody.collisionType = CollisionType.Avatar;
PhysBody.ApplyCollisionMask(PhysicsScene); PhysBody.ApplyCollisionMask(PhysScene);
} }
// The avatar's movement is controlled by this motor that speeds up and slows down
// the avatar seeking to reach the motor's target speed.
// This motor runs as a prestep action for the avatar so it will keep the avatar
// standing as well as moving. Destruction of the avatar will destroy the pre-step action.
private void SetupMovementMotor()
{
// Someday, use a PID motor for asymmetric speed up and slow down
// _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
// Infinite decay and timescale values so motor only changes current to target values.
_velocityMotor = new BSVMotor("BSCharacter.Velocity",
0.2f, // time scale
BSMotor.Infinite, // decay time scale
BSMotor.InfiniteVector, // friction timescale
1f // efficiency
);
// _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
RegisterPreStepAction("BSCharactor.Movement", LocalID, delegate(float timeStep)
{
// TODO: Decide if the step parameters should be changed depending on the avatar's
// state (flying, colliding, ...). There is code in ODE to do this.
OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep);
// If falling, we keep the world's downward vector no matter what the other axis specify.
if (!Flying && !IsColliding)
{
stepVelocity.Z = _velocity.Z;
// DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
}
// 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force.
OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass / PhysicsScene.LastTimeStep;
/*
// If moveForce is very small, zero things so we don't keep sending microscopic updates to the user
float moveForceMagnitudeSquared = moveForce.LengthSquared();
if (moveForceMagnitudeSquared < 0.0001)
{
DetailLog("{0},BSCharacter.MoveMotor,zeroMovement,stepVel={1},vel={2},mass={3},magSq={4},moveForce={5}",
LocalID, stepVelocity, _velocity, Mass, moveForceMagnitudeSquared, moveForce);
ForceVelocity = OMV.Vector3.Zero;
}
else
{
AddForce(moveForce, false, true);
}
*/
// DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
AddForce(moveForce, false, true);
});
}
public override void RequestPhysicsterseUpdate() public override void RequestPhysicsterseUpdate()
{ {
@ -259,16 +201,16 @@ public sealed class BSCharacter : BSPhysObject
Scale = ComputeAvatarScale(_size); Scale = ComputeAvatarScale(_size);
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); LocalID, _size, Scale, Density, _avatarVolume, RawMass);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() PhysScene.TaintedObject("BSCharacter.setSize", delegate()
{ {
if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape)
{ {
PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale);
UpdatePhysicalMassProperties(RawMass, true); UpdatePhysicalMassProperties(RawMass, true);
// Make sure this change appears as a property update event // Make sure this change appears as a property update event
PhysicsScene.PE.PushUpdate(PhysBody); PhysScene.PE.PushUpdate(PhysBody);
} }
}); });
@ -279,11 +221,6 @@ public sealed class BSCharacter : BSPhysObject
{ {
set { BaseShape = value; } set { BaseShape = value; }
} }
// I want the physics engine to make an avatar capsule
public override BSPhysicsShapeType PreferredPhysicalShape
{
get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
}
public override bool Grabbed { public override bool Grabbed {
set { _grabbed = value; } set { _grabbed = value; }
@ -291,6 +228,10 @@ public sealed class BSCharacter : BSPhysObject
public override bool Selected { public override bool Selected {
set { _selected = value; } set { _selected = value; }
} }
public override bool IsSelected
{
get { return _selected; }
}
public override void CrossingFailure() { return; } public override void CrossingFailure() { return; }
public override void link(PhysicsActor obj) { return; } public override void link(PhysicsActor obj) { return; }
public override void delink() { return; } public override void delink() { return; }
@ -301,29 +242,29 @@ public sealed class BSCharacter : BSPhysObject
// Called at taint time! // Called at taint time!
public override void ZeroMotion(bool inTaintTime) public override void ZeroMotion(bool inTaintTime)
{ {
_velocity = OMV.Vector3.Zero; RawVelocity = OMV.Vector3.Zero;
_acceleration = OMV.Vector3.Zero; _acceleration = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine // Zero some other properties directly into the physics engine
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.ClearAllForces(PhysBody); PhysScene.PE.ClearAllForces(PhysBody);
}); });
} }
public override void ZeroAngularMotion(bool inTaintTime) public override void ZeroAngularMotion(bool inTaintTime)
{ {
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero);
PhysicsScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero);
// The next also get rid of applied linear force but the linear velocity is untouched. // The next also get rid of applied linear force but the linear velocity is untouched.
PhysicsScene.PE.ClearForces(PhysBody); PhysScene.PE.ClearForces(PhysBody);
} }
}); });
} }
@ -344,25 +285,26 @@ public sealed class BSCharacter : BSPhysObject
} }
set { set {
_position = value; _position = value;
PositionSanityCheck();
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() PhysScene.TaintedObject("BSCharacter.setPosition", delegate()
{ {
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
if (PhysBody.HasPhysicalBody) PositionSanityCheck();
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); ForcePosition = _position;
}); });
} }
} }
public override OMV.Vector3 ForcePosition { public override OMV.Vector3 ForcePosition {
get { get {
_position = PhysicsScene.PE.GetPosition(PhysBody); _position = PhysScene.PE.GetPosition(PhysBody);
return _position; return _position;
} }
set { set {
_position = value; _position = value;
PositionSanityCheck(); if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); {
PhysScene.PE.SetTranslation(PhysBody, _position, _orientation);
}
} }
} }
@ -375,25 +317,27 @@ public sealed class BSCharacter : BSPhysObject
bool ret = false; bool ret = false;
// TODO: check for out of bounds // TODO: check for out of bounds
if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position)) if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition))
{ {
// The character is out of the known/simulated area. // The character is out of the known/simulated area.
// Upper levels of code will handle the transition to other areas so, for // Force the avatar position to be within known. ScenePresence will use the position
// the time, we just ignore the position. // plus the velocity to decide if the avatar is moving out of the region.
return ret; RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition);
DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition);
return true;
} }
// If below the ground, move the avatar up // If below the ground, move the avatar up
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
if (Position.Z < terrainHeight) if (Position.Z < terrainHeight)
{ {
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, terrainHeight);
_position.Z = terrainHeight + 2.0f; _position.Z = terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters;
ret = true; ret = true;
} }
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{ {
float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position);
if (Position.Z < waterHeight) if (Position.Z < waterHeight)
{ {
_position.Z = waterHeight; _position.Z = waterHeight;
@ -414,11 +358,10 @@ public sealed class BSCharacter : BSPhysObject
{ {
// The new position value must be pushed into the physics engine but we can't // The new position value must be pushed into the physics engine but we can't
// just assign to "Position" because of potential call loops. // just assign to "Position" because of potential call loops.
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
{ {
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
if (PhysBody.HasPhysicalBody) ForcePosition = _position;
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation);
}); });
ret = true; ret = true;
} }
@ -428,25 +371,25 @@ public sealed class BSCharacter : BSPhysObject
public override float Mass { get { return _mass; } } public override float Mass { get { return _mass; } }
// used when we only want this prim's mass and not the linkset thing // used when we only want this prim's mass and not the linkset thing
public override float RawMass { public override float RawMass {
get {return _mass; } get {return _mass; }
} }
public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
{ {
OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass);
PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia);
} }
public override OMV.Vector3 Force { public override OMV.Vector3 Force {
get { return _force; } get { return RawForce; }
set { set {
_force = value; RawForce = value;
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() PhysScene.TaintedObject("BSCharacter.SetForce", delegate()
{ {
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetObjectForce(PhysBody, _force); PhysScene.PE.SetObjectForce(PhysBody, RawForce);
}); });
} }
} }
@ -469,77 +412,49 @@ public sealed class BSCharacter : BSPhysObject
{ {
get get
{ {
return _velocityMotor.TargetValue; return base.m_targetVelocity;
} }
set set
{ {
DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
m_targetVelocity = value;
OMV.Vector3 targetVel = value; OMV.Vector3 targetVel = value;
if (_setAlwaysRun) if (_setAlwaysRun && !_flying)
targetVel *= BSParam.AvatarAlwaysRunFactor; targetVel *= new OMV.Vector3(BSParam.AvatarAlwaysRunFactor, BSParam.AvatarAlwaysRunFactor, 0f);
PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate() if (m_moveActor != null)
{ m_moveActor.SetVelocityAndTarget(RawVelocity, targetVel, false /* inTaintTime */);
_velocityMotor.Reset();
_velocityMotor.SetTarget(targetVel);
_velocityMotor.SetCurrent(_velocity);
_velocityMotor.Enabled = true;
});
} }
} }
// Directly setting velocity means this is what the user really wants now. // Directly setting velocity means this is what the user really wants now.
public override OMV.Vector3 Velocity { public override OMV.Vector3 Velocity {
get { return _velocity; } get { return RawVelocity; }
set { set {
_velocity = value; RawVelocity = value;
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, RawVelocity);
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() PhysScene.TaintedObject("BSCharacter.setVelocity", delegate()
{ {
_velocityMotor.Reset(); if (m_moveActor != null)
_velocityMotor.SetCurrent(_velocity); m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, true /* inTaintTime */);
_velocityMotor.SetTarget(_velocity);
// Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
_velocityMotor.Enabled = false;
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, RawVelocity);
ForceVelocity = _velocity; ForceVelocity = RawVelocity;
}); });
} }
} }
public override OMV.Vector3 ForceVelocity { public override OMV.Vector3 ForceVelocity {
get { return _velocity; } get { return RawVelocity; }
set { set {
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity");
_velocity = value; RawVelocity = value;
// Depending on whether the avatar is moving or not, change the friction PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity);
// to keep the avatar from slipping around PhysScene.PE.Activate(PhysBody, true);
if (_velocity.Length() == 0)
{
if (_currentFriction != BSParam.AvatarStandingFriction)
{
_currentFriction = BSParam.AvatarStandingFriction;
if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
}
}
else
{
if (_currentFriction != BSParam.AvatarFriction)
{
_currentFriction = BSParam.AvatarFriction;
if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
}
}
PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity);
PhysicsScene.PE.Activate(PhysBody, true);
} }
} }
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
get { return _torque; } get { return RawTorque; }
set { _torque = value; set { RawTorque = value;
} }
} }
public override float CollisionScore { public override float CollisionScore {
@ -564,9 +479,19 @@ public sealed class BSCharacter : BSPhysObject
if (_orientation != value) if (_orientation != value)
{ {
_orientation = value; _orientation = value;
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() PhysScene.TaintedObject("BSCharacter.setOrientation", delegate()
{ {
ForceOrientation = _orientation; // Bullet assumes we know what we are doing when forcing orientation
// so it lets us go against all the rules and just compensates for them later.
// This forces rotation to be only around the Z axis and doesn't change any of the other axis.
// This keeps us from flipping the capsule over which the veiwer does not understand.
float oRoll, oPitch, oYaw;
_orientation.GetEulerAngles(out oRoll, out oPitch, out oYaw);
OMV.Quaternion trimmedOrientation = OMV.Quaternion.CreateFromEulers(0f, 0f, oYaw);
// DetailLog("{0},BSCharacter.setOrientation,taint,val={1},valDir={2},conv={3},convDir={4}",
// LocalID, _orientation, OMV.Vector3.UnitX * _orientation,
// trimmedOrientation, OMV.Vector3.UnitX * trimmedOrientation);
ForceOrientation = trimmedOrientation;
}); });
} }
} }
@ -576,7 +501,7 @@ public sealed class BSCharacter : BSPhysObject
{ {
get get
{ {
_orientation = PhysicsScene.PE.GetOrientation(PhysBody); _orientation = PhysScene.PE.GetOrientation(PhysBody);
return _orientation; return _orientation;
} }
set set
@ -585,7 +510,7 @@ public sealed class BSCharacter : BSPhysObject
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
// _position = PhysicsScene.PE.GetPosition(BSBody); // _position = PhysicsScene.PE.GetPosition(BSBody);
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); PhysScene.PE.SetTranslation(PhysBody, _position, _orientation);
} }
} }
} }
@ -605,6 +530,9 @@ public sealed class BSCharacter : BSPhysObject
public override bool IsStatic { public override bool IsStatic {
get { return false; } get { return false; }
} }
public override bool IsPhysicallyActive {
get { return true; }
}
public override bool Flying { public override bool Flying {
get { return _flying; } get { return _flying; }
set { set {
@ -631,14 +559,14 @@ public sealed class BSCharacter : BSPhysObject
public override bool FloatOnWater { public override bool FloatOnWater {
set { set {
_floatOnWater = value; _floatOnWater = value;
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() PhysScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
if (_floatOnWater) if (_floatOnWater)
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
else else
CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
} }
}); });
} }
@ -659,7 +587,7 @@ public sealed class BSCharacter : BSPhysObject
public override float Buoyancy { public override float Buoyancy {
get { return _buoyancy; } get { return _buoyancy; }
set { _buoyancy = value; set { _buoyancy = value;
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() PhysScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
{ {
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
ForceBuoyancy = _buoyancy; ForceBuoyancy = _buoyancy;
@ -668,15 +596,16 @@ public sealed class BSCharacter : BSPhysObject
} }
public override float ForceBuoyancy { public override float ForceBuoyancy {
get { return _buoyancy; } get { return _buoyancy; }
set { set {
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
_buoyancy = value; _buoyancy = value;
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object // Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); float grav = BSParam.Gravity * (1f - _buoyancy);
Gravity = new OMV.Vector3(0f, 0f, grav);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetGravity(PhysBody, new OMV.Vector3(0f, 0f, grav)); PhysScene.PE.SetGravity(PhysBody, Gravity);
} }
} }
@ -691,53 +620,25 @@ public sealed class BSCharacter : BSPhysObject
set { _PIDTau = value; } set { _PIDTau = value; }
} }
// Used for llSetHoverHeight and maybe vehicle height
// Hover Height will override MoveTo target's Z
public override bool PIDHoverActive {
set { _useHoverPID = value; }
}
public override float PIDHoverHeight {
set { _PIDHoverHeight = value; }
}
public override PIDHoverType PIDHoverType {
set { _PIDHoverType = value; }
}
public override float PIDHoverTau {
set { _PIDHoverTao = value; }
}
// For RotLookAt
public override OMV.Quaternion APIDTarget { set { return; } }
public override bool APIDActive { set { return; } }
public override float APIDStrength { set { return; } }
public override float APIDDamping { set { return; } }
public override void AddForce(OMV.Vector3 force, bool pushforce) public override void AddForce(OMV.Vector3 force, bool pushforce)
{ {
// Since this force is being applied in only one step, make this a force per second. // Since this force is being applied in only one step, make this a force per second.
OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; OMV.Vector3 addForce = force / PhysScene.LastTimeStep;
AddForce(addForce, pushforce, false); AddForce(addForce, pushforce, false);
} }
private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
if (force.IsFinite()) if (force.IsFinite())
{ {
float magnitude = force.Length(); OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
if (magnitude > BSParam.MaxAddForceMagnitude)
{
// Force has a limit
force = force / magnitude * BSParam.MaxAddForceMagnitude;
}
OMV.Vector3 addForce = force;
// DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce);
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate()
{ {
// Bullet adds this central force to the total force for this tick // Bullet adds this central force to the total force for this tick
// DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); PhysScene.PE.ApplyCentralForce(PhysBody, addForce);
} }
}); });
} }
@ -748,7 +649,7 @@ public sealed class BSCharacter : BSPhysObject
} }
} }
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
} }
public override void SetMomentum(OMV.Vector3 momentum) { public override void SetMomentum(OMV.Vector3 momentum) {
} }
@ -756,14 +657,14 @@ public sealed class BSCharacter : BSPhysObject
private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size)
{ {
OMV.Vector3 newScale; OMV.Vector3 newScale;
// Bullet's capsule total height is the "passed height + radius * 2"; // Bullet's capsule total height is the "passed height + radius * 2";
// The base capsule is 1 diameter and 2 height (passed radius=0.5, passed height = 1) // The base capsule is 1 unit in diameter and 2 units in height (passed radius=0.5, passed height = 1)
// The number we pass in for 'scaling' is the multiplier to get that base // The number we pass in for 'scaling' is the multiplier to get that base
// shape to be the size desired. // shape to be the size desired.
// So, when creating the scale for the avatar height, we take the passed height // So, when creating the scale for the avatar height, we take the passed height
// (size.Z) and remove the caps. // (size.Z) and remove the caps.
// Another oddity of the Bullet capsule implementation is that it presumes the Y // An oddity of the Bullet capsule implementation is that it presumes the Y
// dimension is the radius of the capsule. Even though some of the code allows // dimension is the radius of the capsule. Even though some of the code allows
// for a asymmetrical capsule, other parts of the code presume it is cylindrical. // for a asymmetrical capsule, other parts of the code presume it is cylindrical.
@ -771,8 +672,27 @@ public sealed class BSCharacter : BSPhysObject
newScale.X = size.X / 2f; newScale.X = size.X / 2f;
newScale.Y = size.Y / 2f; newScale.Y = size.Y / 2f;
float heightAdjust = BSParam.AvatarHeightMidFudge;
if (BSParam.AvatarHeightLowFudge != 0f || BSParam.AvatarHeightHighFudge != 0f)
{
// An avatar is between 1.61 and 2.12 meters. Midpoint is 1.87m.
// The "times 4" relies on the fact that the difference from the midpoint to the extremes is exactly 0.25
float midHeightOffset = size.Z - 1.87f;
if (midHeightOffset < 0f)
{
// Small avatar. Add the adjustment based on the distance from midheight
heightAdjust += -1f * midHeightOffset * 4f * BSParam.AvatarHeightLowFudge;
}
else
{
// Large avatar. Add the adjustment based on the distance from midheight
heightAdjust += midHeightOffset * 4f * BSParam.AvatarHeightHighFudge;
}
}
// The total scale height is the central cylindar plus the caps on the two ends. // The total scale height is the central cylindar plus the caps on the two ends.
newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2)) / 2f; newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2) + heightAdjust) / 2f;
// m_log.DebugFormat("{0} ComputeAvatarScale: size={1},adj={2},scale={3}", LogHeader, size, heightAdjust, newScale);
// If smaller than the endcaps, just fake like we're almost that small // If smaller than the endcaps, just fake like we're almost that small
if (newScale.Z < 0) if (newScale.Z < 0)
newScale.Z = 0.1f; newScale.Z = 0.1f;
@ -794,34 +714,48 @@ public sealed class BSCharacter : BSPhysObject
* Math.Min(Size.X, Size.Y) / 2 * Math.Min(Size.X, Size.Y) / 2
* Size.Y / 2f // plus the volume of the capsule end caps * Size.Y / 2f // plus the volume of the capsule end caps
); );
_mass = _avatarDensity * _avatarVolume; _mass = Density * BSParam.DensityScaleFactor * _avatarVolume;
} }
// The physics engine says that properties have updated. Update same and inform // The physics engine says that properties have updated. Update same and inform
// the world that things have changed. // the world that things have changed.
public override void UpdateProperties(EntityProperties entprop) public override void UpdateProperties(EntityProperties entprop)
{ {
_position = entprop.Position; // Don't change position if standing on a stationary object.
if (!IsStationary)
_position = entprop.Position;
_orientation = entprop.Rotation; _orientation = entprop.Rotation;
_velocity = entprop.Velocity;
// Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar
// and will send agent updates to the clients if velocity changes by more than
// 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many
// extra updates.
if (!entprop.Velocity.ApproxEquals(RawVelocity, 0.1f))
RawVelocity = entprop.Velocity;
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity; _rotationalVelocity = entprop.RotationalVelocity;
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds. // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
PositionSanityCheck(true); if (PositionSanityCheck(true))
{
DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position);
entprop.Position = _position;
}
// remember the current and last set values // remember the current and last set values
LastEntityProperties = CurrentEntityProperties; LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop; CurrentEntityProperties = entprop;
// Tell the linkset about value changes // Tell the linkset about value changes
Linkset.UpdateProperties(this, true); // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
// base.RequestPhysicsterseUpdate(); // base.RequestPhysicsterseUpdate();
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); LocalID, _position, _orientation, RawVelocity, _acceleration, _rotationalVelocity);
} }
} }
} }

View File

@ -85,7 +85,9 @@ public abstract class BSConstraint : IDisposable
{ {
bool ret = false; bool ret = false;
if (m_enabled) if (m_enabled)
{
ret = PhysicsScene.PE.SetAngularLimits(m_constraint, low, high); ret = PhysicsScene.PE.SetAngularLimits(m_constraint, low, high);
}
return ret; return ret;
} }

View File

@ -57,6 +57,7 @@ public sealed class BSConstraint6Dof : BSConstraint
obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString);
} }
// 6 Dof constraint based on a midpoint between the two constrained bodies
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2, public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2,
Vector3 joinPoint, Vector3 joinPoint,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
@ -94,6 +95,21 @@ public sealed class BSConstraint6Dof : BSConstraint
} }
} }
// A 6 Dof constraint that is fixed in the world and constrained to a on-the-fly created static object
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, Vector3 frameInBloc, Quaternion frameInBrot,
bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies)
: base(world)
{
m_body1 = obj1;
m_body2 = obj1; // Look out for confusion down the road
m_constraint = PhysicsScene.PE.Create6DofConstraintFixed(m_world, m_body1,
frameInBloc, frameInBrot,
useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies);
m_enabled = true;
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFixed,wID={1},rID={2},rBody={3}",
BSScene.DetailLogZero, world.worldID, obj1.ID, obj1.AddrString);
}
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
{ {
bool ret = false; bool ret = false;

View File

@ -117,8 +117,7 @@ public sealed class BSConstraintCollection : IDisposable
if (this.TryGetConstraint(body1, body2, out constrain)) if (this.TryGetConstraint(body1, body2, out constrain))
{ {
// remove the constraint from our collection // remove the constraint from our collection
RemoveAndDestroyConstraint(constrain); ret = RemoveAndDestroyConstraint(constrain);
ret = true;
} }
} }
@ -126,17 +125,19 @@ public sealed class BSConstraintCollection : IDisposable
} }
// The constraint MUST exist in the collection // The constraint MUST exist in the collection
// Could be called if the constraint was previously removed.
// Return 'true' if the constraint was actually removed and disposed.
public bool RemoveAndDestroyConstraint(BSConstraint constrain) public bool RemoveAndDestroyConstraint(BSConstraint constrain)
{ {
bool removed = false;
lock (m_constraints) lock (m_constraints)
{ {
// remove the constraint from our collection // remove the constraint from our collection
m_constraints.Remove(constrain); removed = m_constraints.Remove(constrain);
} }
// tell the engine that all its structures need to be freed // Dispose() is safe to call multiple times
constrain.Dispose(); constrain.Dispose();
// we destroyed something return removed;
return true;
} }
// Remove all constraints that reference the passed body. // Remove all constraints that reference the passed body.

View File

@ -45,7 +45,7 @@ public sealed class BSConstraintHinge : BSConstraint
m_body1 = obj1; m_body1 = obj1;
m_body2 = obj2; m_body2 = obj2;
m_constraint = PhysicsScene.PE.CreateHingeConstraint(world, obj1, obj2, m_constraint = PhysicsScene.PE.CreateHingeConstraint(world, obj1, obj2,
pivotInA, pivotInB, axisInA, axisInB, pivotInA, pivotInB, axisInA, axisInB,
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies);
m_enabled = true; m_enabled = true;
} }

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,7 @@ public abstract class BSLinkset
Manual = 2 // linkset tied together manually (code moves all the pieces) Manual = 2 // linkset tied together manually (code moves all the pieces)
} }
// Create the correct type of linkset for this child // Create the correct type of linkset for this child
public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) public static BSLinkset Factory(BSScene physScene, BSPrimLinkable parent)
{ {
BSLinkset ret = null; BSLinkset ret = null;
@ -71,31 +71,28 @@ public abstract class BSLinkset
ret = new BSLinksetCompound(physScene, parent); ret = new BSLinksetCompound(physScene, parent);
break; break;
} }
if (ret == null)
{
physScene.Logger.ErrorFormat("[BULLETSIM LINKSET] Factory could not create linkset. Parent name={1}, ID={2}", parent.Name, parent.LocalID);
}
return ret; return ret;
} }
public BSPhysObject LinksetRoot { get; protected set; } public BSPrimLinkable LinksetRoot { get; protected set; }
public BSScene PhysicsScene { get; private set; } protected BSScene m_physicsScene { get; private set; }
static int m_nextLinksetID = 1; static int m_nextLinksetID = 1;
public int LinksetID { get; private set; } public int LinksetID { get; private set; }
// The children under the root in this linkset. // The children under the root in this linkset.
protected HashSet<BSPhysObject> m_children; protected HashSet<BSPrimLinkable> m_children;
// We lock the diddling of linkset classes to prevent any badness. // We lock the diddling of linkset classes to prevent any badness.
// This locks the modification of the instances of this class. Changes // This locks the modification of the instances of this class. Changes
// to the physical representation is done via the tainting mechenism. // to the physical representation is done via the tainting mechenism.
protected object m_linksetActivityLock = new Object(); protected object m_linksetActivityLock = new Object();
// Some linksets have a preferred physical shape.
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{
return BSPhysicsShapeType.SHAPE_UNKNOWN;
}
// We keep the prim's mass in the linkset structure since it could be dependent on other prims // We keep the prim's mass in the linkset structure since it could be dependent on other prims
public float LinksetMass { get; protected set; } public float LinksetMass { get; protected set; }
@ -111,25 +108,27 @@ public abstract class BSLinkset
get { return ComputeLinksetGeometricCenter(); } get { return ComputeLinksetGeometricCenter(); }
} }
protected BSLinkset(BSScene scene, BSPhysObject parent) protected BSLinkset(BSScene scene, BSPrimLinkable parent)
{ {
// A simple linkset of one (no children) // A simple linkset of one (no children)
LinksetID = m_nextLinksetID++; LinksetID = m_nextLinksetID++;
// We create LOTS of linksets. // We create LOTS of linksets.
if (m_nextLinksetID <= 0) if (m_nextLinksetID <= 0)
m_nextLinksetID = 1; m_nextLinksetID = 1;
PhysicsScene = scene; m_physicsScene = scene;
LinksetRoot = parent; LinksetRoot = parent;
m_children = new HashSet<BSPhysObject>(); m_children = new HashSet<BSPrimLinkable>();
LinksetMass = parent.RawMass; LinksetMass = parent.RawMass;
Rebuilding = false; Rebuilding = false;
parent.ClearDisplacement();
} }
// Link to a linkset where the child knows the parent. // Link to a linkset where the child knows the parent.
// Parent changing should not happen so do some sanity checking. // Parent changing should not happen so do some sanity checking.
// We return the parent's linkset so the child can track its membership. // We return the parent's linkset so the child can track its membership.
// Called at runtime. // Called at runtime.
public BSLinkset AddMeToLinkset(BSPhysObject child) public BSLinkset AddMeToLinkset(BSPrimLinkable child)
{ {
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
@ -145,7 +144,7 @@ public abstract class BSLinkset
// Returns a new linkset for the child which is a linkset of one (just the // Returns a new linkset for the child which is a linkset of one (just the
// orphened child). // orphened child).
// Called at runtime. // Called at runtime.
public BSLinkset RemoveMeFromLinkset(BSPhysObject child) public BSLinkset RemoveMeFromLinkset(BSPrimLinkable child)
{ {
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
@ -159,11 +158,11 @@ public abstract class BSLinkset
} }
// The child is down to a linkset of just itself // The child is down to a linkset of just itself
return BSLinkset.Factory(PhysicsScene, child); return BSLinkset.Factory(m_physicsScene, child);
} }
// Return 'true' if the passed object is the root object of this linkset // Return 'true' if the passed object is the root object of this linkset
public bool IsRoot(BSPhysObject requestor) public bool IsRoot(BSPrimLinkable requestor)
{ {
return (requestor.LocalID == LinksetRoot.LocalID); return (requestor.LocalID == LinksetRoot.LocalID);
} }
@ -174,14 +173,14 @@ public abstract class BSLinkset
public bool HasAnyChildren { get { return (m_children.Count > 0); } } public bool HasAnyChildren { get { return (m_children.Count > 0); } }
// Return 'true' if this child is in this linkset // Return 'true' if this child is in this linkset
public bool HasChild(BSPhysObject child) public bool HasChild(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
ret = m_children.Contains(child); ret = m_children.Contains(child);
/* Safer version but the above should work /* Safer version but the above should work
foreach (BSPhysObject bp in m_children) foreach (BSPrimLinkable bp in m_children)
{ {
if (child.LocalID == bp.LocalID) if (child.LocalID == bp.LocalID)
{ {
@ -196,14 +195,14 @@ public abstract class BSLinkset
// Perform an action on each member of the linkset including root prim. // Perform an action on each member of the linkset including root prim.
// Depends on the action on whether this should be done at taint time. // Depends on the action on whether this should be done at taint time.
public delegate bool ForEachMemberAction(BSPhysObject obj); public delegate bool ForEachMemberAction(BSPrimLinkable obj);
public virtual bool ForEachMember(ForEachMemberAction action) public virtual bool ForEachMember(ForEachMemberAction action)
{ {
bool ret = false; bool ret = false;
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
action(LinksetRoot); action(LinksetRoot);
foreach (BSPhysObject po in m_children) foreach (BSPrimLinkable po in m_children)
{ {
if (action(po)) if (action(po))
break; break;
@ -214,16 +213,16 @@ public abstract class BSLinkset
// I am the root of a linkset and a new child is being added // I am the root of a linkset and a new child is being added
// Called while LinkActivity is locked. // Called while LinkActivity is locked.
protected abstract void AddChildToLinkset(BSPhysObject child); protected abstract void AddChildToLinkset(BSPrimLinkable child);
// I am the root of a linkset and one of my children is being removed. // I am the root of a linkset and one of my children is being removed.
// Safe to call even if the child is not really in my linkset. // Safe to call even if the child is not really in my linkset.
protected abstract void RemoveChildFromLinkset(BSPhysObject child); protected abstract void RemoveChildFromLinkset(BSPrimLinkable child);
// When physical properties are changed the linkset needs to recalculate // When physical properties are changed the linkset needs to recalculate
// its internal properties. // its internal properties.
// May be called at runtime or taint-time. // May be called at runtime or taint-time.
public virtual void Refresh(BSPhysObject requestor) public virtual void Refresh(BSPrimLinkable requestor)
{ {
LinksetMass = ComputeLinksetMass(); LinksetMass = ComputeLinksetMass();
} }
@ -238,31 +237,26 @@ public abstract class BSLinkset
// has not yet been fully constructed. // has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// Called at taint-time! // Called at taint-time!
public abstract bool MakeDynamic(BSPhysObject child); public abstract bool MakeDynamic(BSPrimLinkable child);
// The object is going static (non-physical). Do any setup necessary // The object is going static (non-physical). Do any setup necessary
// for a static linkset. // for a static linkset.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// Called at taint-time! // Called at taint-time!
public abstract bool MakeStatic(BSPhysObject child); public abstract bool MakeStatic(BSPrimLinkable child);
// Called when a parameter update comes from the physics engine for any object // Called when a parameter update comes from the physics engine for any object
// of the linkset is received. // of the linkset is received.
// Passed flag is update came from physics engine (true) or the user (false). // Passed flag is update came from physics engine (true) or the user (false).
// Called at taint-time!! // Called at taint-time!!
public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate); public abstract void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable physObject);
// Routine used when rebuilding the body of the root of the linkset // Routine used when rebuilding the body of the root of the linkset
// Destroy all the constraints have have been made to root. // Destroy all the constraints have have been made to root.
// This is called when the root body is changing. // This is called when the root body is changing.
// Returns 'true' of something was actually removed and would need restoring // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public abstract bool RemoveBodyDependencies(BSPrim child); public abstract bool RemoveDependencies(BSPrimLinkable child);
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
// this routine will restore the removed constraints.
// Called at taint-time!!
public abstract void RestoreBodyDependencies(BSPrim child);
// ================================================================ // ================================================================
protected virtual float ComputeLinksetMass() protected virtual float ComputeLinksetMass()
@ -272,7 +266,7 @@ public abstract class BSLinkset
{ {
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
foreach (BSPhysObject bp in m_children) foreach (BSPrimLinkable bp in m_children)
{ {
mass += bp.RawMass; mass += bp.RawMass;
} }
@ -281,6 +275,7 @@ public abstract class BSLinkset
return mass; return mass;
} }
// Computes linkset's center of mass in world coordinates.
protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
{ {
OMV.Vector3 com; OMV.Vector3 com;
@ -289,7 +284,7 @@ public abstract class BSLinkset
com = LinksetRoot.Position * LinksetRoot.RawMass; com = LinksetRoot.Position * LinksetRoot.RawMass;
float totalMass = LinksetRoot.RawMass; float totalMass = LinksetRoot.RawMass;
foreach (BSPhysObject bp in m_children) foreach (BSPrimLinkable bp in m_children)
{ {
com += bp.Position * bp.RawMass; com += bp.Position * bp.RawMass;
totalMass += bp.RawMass; totalMass += bp.RawMass;
@ -308,9 +303,9 @@ public abstract class BSLinkset
{ {
com = LinksetRoot.Position; com = LinksetRoot.Position;
foreach (BSPhysObject bp in m_children) foreach (BSPrimLinkable bp in m_children)
{ {
com += bp.Position * bp.RawMass; com += bp.Position;
} }
com /= (m_children.Count + 1); com /= (m_children.Count + 1);
} }
@ -321,8 +316,8 @@ public abstract class BSLinkset
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {
if (PhysicsScene.PhysicsLogging.Enabled) if (m_physicsScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args); m_physicsScene.DetailLog(msg, args);
} }
} }

View File

@ -35,59 +35,74 @@ using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
/*
// When a child is linked, the relationship position of the child to the parent // When a child is linked, the relationship position of the child to the parent
// is remembered so the child's world position can be recomputed when it is // is remembered so the child's world position can be recomputed when it is
// removed from the linkset. // removed from the linkset.
sealed class BSLinksetCompoundInfo : BSLinksetInfo sealed class BSLinksetCompoundInfo : BSLinksetInfo
{ {
public OMV.Vector3 OffsetPos; public int Index;
public OMV.Vector3 OffsetFromRoot;
public OMV.Vector3 OffsetFromCenterOfMass;
public OMV.Quaternion OffsetRot; public OMV.Quaternion OffsetRot;
public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r) public BSLinksetCompoundInfo(int indx, OMV.Vector3 p, OMV.Quaternion r)
{ {
OffsetPos = p; Index = indx;
OffsetFromRoot = p;
OffsetFromCenterOfMass = p;
OffsetRot = r; OffsetRot = r;
} }
// 'centerDisplacement' is the distance from the root the the center-of-mass (Bullet 'zero' of the shape)
public BSLinksetCompoundInfo(int indx, BSPrimLinkable root, BSPrimLinkable child, OMV.Vector3 centerDisplacement)
{
// Each child position and rotation is given relative to the center-of-mass.
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(root.RawOrientation);
OMV.Vector3 displacementFromRoot = (child.RawPosition - root.RawPosition) * invRootOrientation;
OMV.Vector3 displacementFromCOM = displacementFromRoot - centerDisplacement;
OMV.Quaternion displacementRot = child.RawOrientation * invRootOrientation;
// Save relative position for recomputing child's world position after moving linkset.
Index = indx;
OffsetFromRoot = displacementFromRoot;
OffsetFromCenterOfMass = displacementFromCOM;
OffsetRot = displacementRot;
}
public override void Clear() public override void Clear()
{ {
OffsetPos = OMV.Vector3.Zero; Index = 0;
OffsetFromRoot = OMV.Vector3.Zero;
OffsetFromCenterOfMass = OMV.Vector3.Zero;
OffsetRot = OMV.Quaternion.Identity; OffsetRot = OMV.Quaternion.Identity;
} }
public override string ToString() public override string ToString()
{ {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.Append("<p="); buff.Append("<i=");
buff.Append(OffsetPos.ToString()); buff.Append(Index.ToString());
buff.Append(",p=");
buff.Append(OffsetFromRoot.ToString());
buff.Append(",m=");
buff.Append(OffsetFromCenterOfMass.ToString());
buff.Append(",r="); buff.Append(",r=");
buff.Append(OffsetRot.ToString()); buff.Append(OffsetRot.ToString());
buff.Append(">"); buff.Append(">");
return buff.ToString(); return buff.ToString();
} }
}; };
*/
public sealed class BSLinksetCompound : BSLinkset public sealed class BSLinksetCompound : BSLinkset
{ {
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent) public BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
: base(scene, parent)
{ {
} }
// For compound implimented linksets, if there are children, use compound shape for the root.
public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{
// Returning 'unknown' means we don't have a preference.
BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
if (IsRoot(requestor) && HasAnyChildren)
{
ret = BSPhysicsShapeType.SHAPE_COMPOUND;
}
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
return ret;
}
// When physical properties are changed the linkset needs to recalculate // When physical properties are changed the linkset needs to recalculate
// its internal properties. // its internal properties.
public override void Refresh(BSPhysObject requestor) public override void Refresh(BSPrimLinkable requestor)
{ {
base.Refresh(requestor); base.Refresh(requestor);
@ -96,16 +111,16 @@ public sealed class BSLinksetCompound : BSLinkset
} }
// Schedule a refresh to happen after all the other taint processing. // Schedule a refresh to happen after all the other taint processing.
private void ScheduleRebuild(BSPhysObject requestor) private void ScheduleRebuild(BSPrimLinkable requestor)
{ {
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2}", DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}",
requestor.LocalID, Rebuilding, HasAnyChildren); requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren));
// When rebuilding, it is possible to set properties that would normally require a rebuild. // When rebuilding, it is possible to set properties that would normally require a rebuild.
// If already rebuilding, don't request another rebuild. // If already rebuilding, don't request another rebuild.
// If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
if (!Rebuilding && HasAnyChildren) if (!Rebuilding && HasAnyChildren)
{ {
PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
{ {
if (HasAnyChildren) if (HasAnyChildren)
RecomputeLinksetCompound(); RecomputeLinksetCompound();
@ -118,7 +133,7 @@ public sealed class BSLinksetCompound : BSLinkset
// has not yet been fully constructed. // has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// Called at taint-time! // Called at taint-time!
public override bool MakeDynamic(BSPhysObject child) public override bool MakeDynamic(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
@ -127,138 +142,131 @@ public sealed class BSLinksetCompound : BSLinkset
// The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly.
ScheduleRebuild(LinksetRoot); ScheduleRebuild(LinksetRoot);
} }
else
{
// The origional prims are removed from the world as the shape of the root compound
// shape takes over.
PhysicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
PhysicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION);
// We don't want collisions from the old linkset children.
PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
child.PhysBody.collisionType = CollisionType.LinksetChild;
ret = true;
}
return ret; return ret;
} }
// The object is going static (non-physical). Do any setup necessary for a static linkset. // The object is going static (non-physical). We do not do anything for static linksets.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// This doesn't normally happen -- OpenSim removes the objects from the physical
// world if it is a static linkset.
// Called at taint-time! // Called at taint-time!
public override bool MakeStatic(BSPhysObject child) public override bool MakeStatic(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
if (IsRoot(child)) if (IsRoot(child))
{ {
// Schedule a rebuild to verify that the root shape is set to the real shape.
ScheduleRebuild(LinksetRoot); ScheduleRebuild(LinksetRoot);
} }
else
{
// The non-physical children can come back to life.
PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
child.PhysBody.collisionType = CollisionType.LinksetChild;
// Don't force activation so setting of DISABLE_SIMULATION can stay if used.
PhysicsScene.PE.Activate(child.PhysBody, false);
ret = true;
}
return ret; return ret;
} }
public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate) // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
// Called at taint-time.
public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated)
{ {
if (!LinksetRoot.IsPhysicallyActive)
{
// No reason to do this physical stuff for static linksets.
DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID);
return;
}
// The user moving a child around requires the rebuilding of the linkset compound shape // The user moving a child around requires the rebuilding of the linkset compound shape
// One problem is this happens when a border is crossed -- the simulator implementation // One problem is this happens when a border is crossed -- the simulator implementation
// is to store the position into the group which causes the move of the object // stores the position into the group which causes the move of the object
// but it also means all the child positions get updated. // but it also means all the child positions get updated.
// What would cause an unnecessary rebuild so we make sure the linkset is in a // What would cause an unnecessary rebuild so we make sure the linkset is in a
// region before bothering to do a rebuild. // region before bothering to do a rebuild.
if (!IsRoot(updated) if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
&& !physicalUpdate
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
{ {
updated.LinksetInfo = null; // If a child of the linkset is updating only the position or rotation, that can be done
ScheduleRebuild(updated); // without rebuilding the linkset.
// If a handle for the child can be fetch, we update the child here. If a rebuild was
// scheduled by someone else, the rebuild will just replace this setting.
bool updatedChild = false;
// Anything other than updating position or orientation usually means a physical update
// and that is caused by us updating the object.
if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
{
// Find the physical instance of the child
if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo))
{
// It is possible that the linkset is still under construction and the child is not yet
// inserted into the compound shape. A rebuild of the linkset in a pre-step action will
// build the whole thing with the new position or rotation.
// The index must be checked because Bullet references the child array but does no validity
// checking of the child index passed.
int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo);
if (updated.LinksetChildIndex < numLinksetChildren)
{
BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex);
if (linksetChildShape.HasPhysicalShape)
{
// Found the child shape within the compound shape
m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex,
updated.RawPosition - LinksetRoot.RawPosition,
updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
true /* shouldRecalculateLocalAabb */);
updatedChild = true;
DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
}
else // DEBUG DEBUG
{ // DEBUG DEBUG
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
updated.LocalID, linksetChildShape);
} // DEBUG DEBUG
}
else // DEBUG DEBUG
{ // DEBUG DEBUG
// the child is not yet in the compound shape. This is non-fatal.
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
} // DEBUG DEBUG
}
else // DEBUG DEBUG
{ // DEBUG DEBUG
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
} // DEBUG DEBUG
if (!updatedChild)
{
// If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info.
// Note: there are several ways through this code that will not update the child if
// the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since
// there will already be a rebuild scheduled.
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}",
updated.LocalID, whichUpdated);
updated.LinksetInfo = null; // setting to 'null' causes relative position to be recomputed.
ScheduleRebuild(updated);
}
}
} }
} }
// Routine called when rebuilding the body of some member of the linkset. // Routine called when rebuilding the body of some member of the linkset.
// Since we don't keep in world relationships, do nothing unless it's a child changing. // If one of the bodies is being changed, the linkset needs rebuilding.
// For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated.
// Returns 'true' of something was actually removed and would need restoring // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrim child) public override bool RemoveDependencies(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, IsRoot(child)); child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child));
if (!IsRoot(child)) ScheduleRebuild(child);
{
// Because it is a convenient time, recompute child world position and rotation based on
// its position in the linkset.
RecomputeChildWorldPosition(child, true);
}
// Cannot schedule a refresh/rebuild here because this routine is called when
// the linkset is being rebuilt.
// InternalRefresh(LinksetRoot);
return ret; return ret;
} }
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
// this routine will restore the removed constraints.
// Called at taint-time!!
public override void RestoreBodyDependencies(BSPrim child)
{
}
// When the linkset is built, the child shape is added to the compound shape relative to the
// root shape. The linkset then moves around but this does not move the actual child
// prim. The child prim's location must be recomputed based on the location of the root shape.
private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
{
BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
if (lci != null)
{
if (inTaintTime)
{
OMV.Vector3 oldPos = child.RawPosition;
child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
child.LocalID, oldPos, lci, child.RawPosition);
}
else
{
// TaintedObject is not used here so the raw position is set now and not at taint-time.
child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
}
}
else
{
// This happens when children have been added to the linkset but the linkset
// has not been constructed yet. So like, at taint time, adding children to a linkset
// and then changing properties of the children (makePhysical, for instance)
// but the post-print action of actually rebuilding the linkset has not yet happened.
// PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
// LogHeader, child.LocalID);
DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
}
}
// ================================================================ // ================================================================
// Add a new child to the linkset. // Add a new child to the linkset.
// Called while LinkActivity is locked. // Called while LinkActivity is locked.
protected override void AddChildToLinkset(BSPhysObject child) protected override void AddChildToLinkset(BSPrimLinkable child)
{ {
if (!HasChild(child)) if (!HasChild(child))
{ {
@ -274,8 +282,10 @@ public sealed class BSLinksetCompound : BSLinkset
// Remove the specified child from the linkset. // Remove the specified child from the linkset.
// Safe to call even if the child is not really in the linkset. // Safe to call even if the child is not really in the linkset.
protected override void RemoveChildFromLinkset(BSPhysObject child) protected override void RemoveChildFromLinkset(BSPrimLinkable child)
{ {
child.ClearDisplacement();
if (m_children.Remove(child)) if (m_children.Remove(child))
{ {
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
@ -284,7 +294,7 @@ public sealed class BSLinksetCompound : BSLinkset
child.LocalID, child.PhysBody.AddrString); child.LocalID, child.PhysBody.AddrString);
// Cause the child's body to be rebuilt and thus restored to normal operation // Cause the child's body to be rebuilt and thus restored to normal operation
RecomputeChildWorldPosition(child, false); child.LinksetInfo = null;
child.ForceBodyShapeRebuild(false); child.ForceBodyShapeRebuild(false);
if (!HasAnyChildren) if (!HasAnyChildren)
@ -295,7 +305,7 @@ public sealed class BSLinksetCompound : BSLinkset
else else
{ {
// Rebuild the compound shape with the child removed // Rebuild the compound shape with the child removed
ScheduleRebuild(child); ScheduleRebuild(LinksetRoot);
} }
} }
return; return;
@ -306,87 +316,113 @@ public sealed class BSLinksetCompound : BSLinkset
// Constraint linksets are rebuilt every time. // Constraint linksets are rebuilt every time.
// Note that this works for rebuilding just the root after a linkset is taken apart. // Note that this works for rebuilding just the root after a linkset is taken apart.
// Called at taint time!! // Called at taint time!!
private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape
private bool disableCOM = true; // For basic linkset debugging, turn off the center-of-mass setting
private void RecomputeLinksetCompound() private void RecomputeLinksetCompound()
{ {
try try
{ {
// Suppress rebuilding while rebuilding
Rebuilding = true; Rebuilding = true;
// Cause the root shape to be rebuilt as a compound object with just the root in it // No matter what is being done, force the root prim's PhysBody and PhysShape to get set
// to what they should be as if the root was not in a linkset.
// Not that bad since we only get into this routine if there are children in the linkset and
// something has been updated/changed.
LinksetRoot.ForceBodyShapeRebuild(true); LinksetRoot.ForceBodyShapeRebuild(true);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", // There is no reason to build all this physical stuff for a non-physical linkset.
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); if (!LinksetRoot.IsPhysicallyActive)
// Add a shape for each of the other children in the linkset
ForEachMember(delegate(BSPhysObject cPrim)
{ {
// Clean up any old linkset shape and make sure the root shape is set to the root object.
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID);
return; // Note the 'finally' clause at the botton which will get executed.
}
// Get a new compound shape to build the linkset shape in.
BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene);
// The center of mass for the linkset is the geometric center of the group.
// Compute a displacement for each component so it is relative to the center-of-mass.
// Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
OMV.Quaternion invRootOrientation = OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation));
// 'centerDisplacement' is the value to subtract from children to give physical offset position
OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
if (UseBulletSimRootOffsetHack || disableCOM)
{
centerDisplacementV = OMV.Vector3.Zero;
LinksetRoot.ClearDisplacement();
}
else
{
LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV);
}
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}",
LinksetRoot.LocalID, LinksetRoot.RawPosition, centerOfMassW, centerDisplacementV);
// Add the shapes of all the components of the linkset
int memberIndex = 1;
ForEachMember(delegate(BSPrimLinkable cPrim)
{
// Root shape is always index zero.
cPrim.LinksetChildIndex = IsRoot(cPrim) ? 0 : memberIndex;
// Get a reference to the shape of the child and add that shape to the linkset compound shape
BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim);
OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacementV;
OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation) * invRootOrientation;
m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
LinksetRoot.LocalID, memberIndex, childShape, offsetPos, offsetRot);
// Since we are borrowing the shape of the child, disable the origional child body
if (!IsRoot(cPrim)) if (!IsRoot(cPrim))
{ {
// Compute the displacement of the child from the root of the linkset. m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
// This info is saved in the child prim so the relationship does not m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
// change over time and the new child position can be computed // We don't want collisions from the old linkset children.
// when the linkset is being disassembled (the linkset may have moved). m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo; cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
if (lci == null)
{
// Each child position and rotation is given relative to the root.
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
// Save relative position for recomputing child's world position after moving linkset.
lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
cPrim.LinksetInfo = lci;
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
}
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
if (cPrim.PhysShape.isNativeShape)
{
// A native shape is turning into a hull collision shape because native
// shapes are not shared so we have to hullify it so it will be tracked
// and freed at the correct time. This also solves the scaling problem
// (native shapes scaled but hull/meshes are assumed to not be).
// TODO: decide of the native shape can just be used in the compound shape.
// Use call to CreateGeomNonSpecial().
BulletShape saveShape = cPrim.PhysShape;
cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
// PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
BulletShape newShape = cPrim.PhysShape;
cPrim.PhysShape = saveShape;
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, lci.OffsetPos, lci.OffsetRot);
}
else
{
// For the shared shapes (meshes and hulls), just use the shape in the child.
// The reference count added here will be decremented when the compound shape
// is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
{
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
}
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
}
} }
memberIndex++;
return false; // 'false' says to move onto the next child in the list return false; // 'false' says to move onto the next child in the list
}); });
// Replace the root shape with the built compound shape.
// Object removed and added to world to get collision cache rebuilt for new shape.
LinksetRoot.PhysShape.Dereference(m_physicsScene);
LinksetRoot.PhysShape = linksetShape;
m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody);
m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}",
LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape);
// With all of the linkset packed into the root prim, it has the mass of everyone. // With all of the linkset packed into the root prim, it has the mass of everyone.
LinksetMass = ComputeLinksetMass(); LinksetMass = ComputeLinksetMass();
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
if (UseBulletSimRootOffsetHack)
{
// Enable the physical position updator to return the position and rotation of the root shape.
// This enables a feature in the C++ code to return the world coordinates of the first shape in the
// compound shape. This eleviates the need to offset the returned physical position by the
// center-of-mass offset.
m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
}
} }
finally finally
{ {
Rebuilding = false; Rebuilding = false;
} }
PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); // See that the Aabb surrounds the new shape
m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
} }
} }
} }

View File

@ -36,7 +36,7 @@ public sealed class BSLinksetConstraints : BSLinkset
{ {
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent) public BSLinksetConstraints(BSScene scene, BSPrimLinkable parent) : base(scene, parent)
{ {
} }
@ -44,14 +44,14 @@ public sealed class BSLinksetConstraints : BSLinkset
// its internal properties. // its internal properties.
// This is queued in the 'post taint' queue so the // This is queued in the 'post taint' queue so the
// refresh will happen once after all the other taints are applied. // refresh will happen once after all the other taints are applied.
public override void Refresh(BSPhysObject requestor) public override void Refresh(BSPrimLinkable requestor)
{ {
base.Refresh(requestor); base.Refresh(requestor);
if (HasAnyChildren && IsRoot(requestor)) if (HasAnyChildren && IsRoot(requestor))
{ {
// Queue to happen after all the other taint processing // Queue to happen after all the other taint processing
PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
{ {
if (HasAnyChildren && IsRoot(requestor)) if (HasAnyChildren && IsRoot(requestor))
RecomputeLinksetConstraints(); RecomputeLinksetConstraints();
@ -65,7 +65,7 @@ public sealed class BSLinksetConstraints : BSLinkset
// has not yet been fully constructed. // has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// Called at taint-time! // Called at taint-time!
public override bool MakeDynamic(BSPhysObject child) public override bool MakeDynamic(BSPrimLinkable child)
{ {
// What is done for each object in BSPrim is what we want. // What is done for each object in BSPrim is what we want.
return false; return false;
@ -76,14 +76,14 @@ public sealed class BSLinksetConstraints : BSLinkset
// This doesn't normally happen -- OpenSim removes the objects from the physical // This doesn't normally happen -- OpenSim removes the objects from the physical
// world if it is a static linkset. // world if it is a static linkset.
// Called at taint-time! // Called at taint-time!
public override bool MakeStatic(BSPhysObject child) public override bool MakeStatic(BSPrimLinkable child)
{ {
// What is done for each object in BSPrim is what we want. // What is done for each object in BSPrim is what we want.
return false; return false;
} }
// Called at taint-time!! // Called at taint-time!!
public override void UpdateProperties(BSPhysObject updated, bool inTaintTime) public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable pObj)
{ {
// Nothing to do for constraints on property updates // Nothing to do for constraints on property updates
} }
@ -93,11 +93,11 @@ public sealed class BSLinksetConstraints : BSLinkset
// up to rebuild the constraints before the next simulation step. // up to rebuild the constraints before the next simulation step.
// Returns 'true' of something was actually removed and would need restoring // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrim child) public override bool RemoveDependencies(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", DetailLog("{0},BSLinksetConstraint.RemoveDependencies,removeChildrenForRoot,rID={1},rBody={2}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString);
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
@ -110,19 +110,11 @@ public sealed class BSLinksetConstraints : BSLinkset
return ret; return ret;
} }
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
// this routine will restore the removed constraints.
// Called at taint-time!!
public override void RestoreBodyDependencies(BSPrim child)
{
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
}
// ================================================================ // ================================================================
// Add a new child to the linkset. // Add a new child to the linkset.
// Called while LinkActivity is locked. // Called while LinkActivity is locked.
protected override void AddChildToLinkset(BSPhysObject child) protected override void AddChildToLinkset(BSPrimLinkable child)
{ {
if (!HasChild(child)) if (!HasChild(child))
{ {
@ -138,19 +130,19 @@ public sealed class BSLinksetConstraints : BSLinkset
// Remove the specified child from the linkset. // Remove the specified child from the linkset.
// Safe to call even if the child is not really in my linkset. // Safe to call even if the child is not really in my linkset.
protected override void RemoveChildFromLinkset(BSPhysObject child) protected override void RemoveChildFromLinkset(BSPrimLinkable child)
{ {
if (m_children.Remove(child)) if (m_children.Remove(child))
{ {
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now BSPrimLinkable rootx = LinksetRoot; // capture the root and body as of now
BSPhysObject childx = child; BSPrimLinkable childx = child;
DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
childx.LocalID, childx.LocalID,
rootx.LocalID, rootx.PhysBody.AddrString, rootx.LocalID, rootx.PhysBody.AddrString,
childx.LocalID, childx.PhysBody.AddrString); childx.LocalID, childx.PhysBody.AddrString);
PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() m_physicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
{ {
PhysicallyUnlinkAChildFromRoot(rootx, childx); PhysicallyUnlinkAChildFromRoot(rootx, childx);
}); });
@ -167,13 +159,13 @@ public sealed class BSLinksetConstraints : BSLinkset
// Create a constraint between me (root of linkset) and the passed prim (the child). // Create a constraint between me (root of linkset) and the passed prim (the child).
// Called at taint time! // Called at taint time!
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) private void PhysicallyLinkAChildToRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim)
{ {
// Don't build the constraint when asked. Put it off until just before the simulation step. // Don't build the constraint when asked. Put it off until just before the simulation step.
Refresh(rootPrim); Refresh(rootPrim);
} }
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim) private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSPrimLinkable childPrim)
{ {
// Zero motion for children so they don't interpolate // Zero motion for children so they don't interpolate
childPrim.ZeroMotion(true); childPrim.ZeroMotion(true);
@ -195,7 +187,7 @@ public sealed class BSLinksetConstraints : BSLinkset
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
BSConstraint6Dof constrain = new BSConstraint6Dof( BSConstraint6Dof constrain = new BSConstraint6Dof(
PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); m_physicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
// PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true ); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
/* NOTE: below is an attempt to build constraint with full frame computation, etc. /* NOTE: below is an attempt to build constraint with full frame computation, etc.
@ -224,15 +216,15 @@ public sealed class BSLinksetConstraints : BSLinkset
// ================================================================================== // ==================================================================================
*/ */
PhysicsScene.Constraints.AddConstraint(constrain); m_physicsScene.Constraints.AddConstraint(constrain);
// zero linear and angular limits makes the objects unable to move in relation to each other // zero linear and angular limits makes the objects unable to move in relation to each other
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
// tweek the constraint to increase stability // tweek the constraint to increase stability
constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset)); constrain.UseFrameOffset(BSParam.LinkConstraintUseFrameOffset);
constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor), constrain.TranslationalLimitMotor(BSParam.LinkConstraintEnableTransMotor,
BSParam.LinkConstraintTransMotorMaxVel, BSParam.LinkConstraintTransMotorMaxVel,
BSParam.LinkConstraintTransMotorMaxForce); BSParam.LinkConstraintTransMotorMaxForce);
constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP); constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
@ -247,7 +239,7 @@ public sealed class BSLinksetConstraints : BSLinkset
// The root and child bodies are passed in because we need to remove the constraint between // The root and child bodies are passed in because we need to remove the constraint between
// the bodies that were present at unlink time. // the bodies that were present at unlink time.
// Called at taint time! // Called at taint time!
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) private bool PhysicallyUnlinkAChildFromRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
@ -256,10 +248,10 @@ public sealed class BSLinksetConstraints : BSLinkset
childPrim.LocalID, childPrim.PhysBody.AddrString); childPrim.LocalID, childPrim.PhysBody.AddrString);
// Find the constraint for this link and get rid of it from the overall collection and from my list // Find the constraint for this link and get rid of it from the overall collection and from my list
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) if (m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
{ {
// Make the child refresh its location // Make the child refresh its location
PhysicsScene.PE.PushUpdate(childPrim.PhysBody); m_physicsScene.PE.PushUpdate(childPrim.PhysBody);
ret = true; ret = true;
} }
@ -269,11 +261,11 @@ public sealed class BSLinksetConstraints : BSLinkset
// Remove linkage between myself and any possible children I might have. // Remove linkage between myself and any possible children I might have.
// Returns 'true' of any constraints were destroyed. // Returns 'true' of any constraints were destroyed.
// Called at taint time! // Called at taint time!
private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) private bool PhysicallyUnlinkAllChildrenFromRoot(BSPrimLinkable rootPrim)
{ {
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); return m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
} }
// Call each of the constraints that make up this linkset and recompute the // Call each of the constraints that make up this linkset and recompute the
@ -289,7 +281,7 @@ public sealed class BSLinksetConstraints : BSLinkset
DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}", DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, linksetMass); LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, linksetMass);
foreach (BSPhysObject child in m_children) foreach (BSPrimLinkable child in m_children)
{ {
// A child in the linkset physically shows the mass of the whole linkset. // A child in the linkset physically shows the mass of the whole linkset.
// This allows Bullet to apply enough force on the child to move the whole linkset. // This allows Bullet to apply enough force on the child to move the whole linkset.
@ -297,7 +289,7 @@ public sealed class BSLinksetConstraints : BSLinkset
child.UpdatePhysicalMassProperties(linksetMass, true); child.UpdatePhysicalMassProperties(linksetMass, true);
BSConstraint constrain; BSConstraint constrain;
if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
{ {
// If constraint doesn't exist yet, create it. // If constraint doesn't exist yet, create it.
constrain = BuildConstraint(LinksetRoot, child); constrain = BuildConstraint(LinksetRoot, child);

View File

@ -180,11 +180,14 @@ public static class BSMaterials
// Use reflection to set the value in the attribute structure. // Use reflection to set the value in the attribute structure.
private static void SetAttributeValue(int matType, string attribName, float val) private static void SetAttributeValue(int matType, string attribName, float val)
{ {
// Get the current attribute values for this material
MaterialAttributes thisAttrib = Attributes[matType]; MaterialAttributes thisAttrib = Attributes[matType];
// Find the field for the passed attribute name (eg, find field named 'friction')
FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower()); FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
if (fieldInfo != null) if (fieldInfo != null)
{ {
fieldInfo.SetValue(thisAttrib, val); fieldInfo.SetValue(thisAttrib, val);
// Copy new attributes back to array -- since MaterialAttributes is 'struct', passed by value, not reference.
Attributes[matType] = thisAttrib; Attributes[matType] = thisAttrib;
} }
} }

View File

@ -59,22 +59,17 @@ public abstract class BSMotor
{ {
if (PhysicsScene != null) if (PhysicsScene != null)
{ {
if (PhysicsScene.VehicleLoggingEnabled) PhysicsScene.DetailLog(msg, parms);
{
PhysicsScene.DetailLog(msg, parms);
}
} }
} }
} }
// Motor which moves CurrentValue to TargetValue over TimeScale seconds. // Motor which moves CurrentValue to TargetValue over TimeScale seconds.
// The TargetValue decays in TargetValueDecayTimeScale and // The TargetValue decays in TargetValueDecayTimeScale.
// the CurrentValue will be held back by FrictionTimeScale.
// This motor will "zero itself" over time in that the targetValue will // This motor will "zero itself" over time in that the targetValue will
// decay to zero and the currentValue will follow it to that zero. // decay to zero and the currentValue will follow it to that zero.
// The overall effect is for the returned correction value to go from large // The overall effect is for the returned correction value to go from large
// values (the total difference between current and target minus friction) // values to small and eventually zero values.
// to small and eventually zero values.
// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay. // TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
// For instance, if something is moving at speed X and the desired speed is Y, // For instance, if something is moving at speed X and the desired speed is Y,
@ -91,7 +86,6 @@ public class BSVMotor : BSMotor
public virtual float TimeScale { get; set; } public virtual float TimeScale { get; set; }
public virtual float TargetValueDecayTimeScale { get; set; } public virtual float TargetValueDecayTimeScale { get; set; }
public virtual Vector3 FrictionTimescale { get; set; }
public virtual float Efficiency { get; set; } public virtual float Efficiency { get; set; }
public virtual float ErrorZeroThreshold { get; set; } public virtual float ErrorZeroThreshold { get; set; }
@ -100,10 +94,13 @@ public class BSVMotor : BSMotor
public virtual Vector3 CurrentValue { get; protected set; } public virtual Vector3 CurrentValue { get; protected set; }
public virtual Vector3 LastError { get; protected set; } public virtual Vector3 LastError { get; protected set; }
public virtual bool ErrorIsZero public virtual bool ErrorIsZero()
{ get { {
return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold); return ErrorIsZero(LastError);
} }
public virtual bool ErrorIsZero(Vector3 err)
{
return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold));
} }
public BSVMotor(string useName) public BSVMotor(string useName)
@ -111,16 +108,14 @@ public class BSVMotor : BSMotor
{ {
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite; TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
Efficiency = 1f; Efficiency = 1f;
FrictionTimescale = BSMotor.InfiniteVector;
CurrentValue = TargetValue = Vector3.Zero; CurrentValue = TargetValue = Vector3.Zero;
ErrorZeroThreshold = 0.001f; ErrorZeroThreshold = 0.001f;
} }
public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) public BSVMotor(string useName, float timeScale, float decayTimeScale, float efficiency)
: this(useName) : this(useName)
{ {
TimeScale = timeScale; TimeScale = timeScale;
TargetValueDecayTimeScale = decayTimeScale; TargetValueDecayTimeScale = decayTimeScale;
FrictionTimescale = frictionTimeScale;
Efficiency = efficiency; Efficiency = efficiency;
CurrentValue = TargetValue = Vector3.Zero; CurrentValue = TargetValue = Vector3.Zero;
} }
@ -138,7 +133,8 @@ public class BSVMotor : BSMotor
CurrentValue = TargetValue = Vector3.Zero; CurrentValue = TargetValue = Vector3.Zero;
} }
// Compute the next step and return the new current value // Compute the next step and return the new current value.
// Returns the correction needed to move 'current' to 'target'.
public virtual Vector3 Step(float timeStep) public virtual Vector3 Step(float timeStep)
{ {
if (!Enabled) return TargetValue; if (!Enabled) return TargetValue;
@ -148,9 +144,10 @@ public class BSVMotor : BSMotor
Vector3 correction = Vector3.Zero; Vector3 correction = Vector3.Zero;
Vector3 error = TargetValue - CurrentValue; Vector3 error = TargetValue - CurrentValue;
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) LastError = error;
if (!ErrorIsZero(error))
{ {
correction = Step(timeStep, error); correction = StepError(timeStep, error);
CurrentValue += correction; CurrentValue += correction;
@ -163,44 +160,40 @@ public class BSVMotor : BSMotor
TargetValue *= (1f - decayFactor); TargetValue *= (1f - decayFactor);
} }
// The amount we can correct the error is reduced by the friction
Vector3 frictionFactor = Vector3.Zero;
if (FrictionTimescale != BSMotor.InfiniteVector)
{
// frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
// Individual friction components can be 'infinite' so compute each separately.
frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
frictionFactor *= timeStep;
CurrentValue *= (Vector3.One - frictionFactor);
}
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}", MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
timeStep, error, correction); timeStep, error, correction);
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}", MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},tgt={4},curr={5}",
BSScene.DetailLogZero, UseName, BSScene.DetailLogZero, UseName, TargetValueDecayTimeScale, decayFactor, TargetValue, CurrentValue);
TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
TargetValue, CurrentValue);
} }
else else
{ {
// Difference between what we have and target is small. Motor is done. // Difference between what we have and target is small. Motor is done.
if (TargetValue.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
{
// The target can step down to nearly zero but not get there. If close to zero
// it is really zero.
TargetValue = Vector3.Zero;
}
CurrentValue = TargetValue; CurrentValue = TargetValue;
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}", MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},currTgt={4},currCurr={5}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue); BSScene.DetailLogZero, UseName, origCurrVal, origTarget, TargetValue, CurrentValue);
} }
return CurrentValue; return correction;
} }
public virtual Vector3 Step(float timeStep, Vector3 error) // version of step that sets the current value before doing the step
public virtual Vector3 Step(float timeStep, Vector3 current)
{
CurrentValue = current;
return Step(timeStep);
}
public virtual Vector3 StepError(float timeStep, Vector3 error)
{ {
if (!Enabled) return Vector3.Zero; if (!Enabled) return Vector3.Zero;
LastError = error;
Vector3 returnCorrection = Vector3.Zero; Vector3 returnCorrection = Vector3.Zero;
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) if (!ErrorIsZero(error))
{ {
// correction = error / secondsItShouldTakeToCorrect // correction = error / secondsItShouldTakeToCorrect
Vector3 correctionAmount; Vector3 correctionAmount;
@ -222,9 +215,9 @@ public class BSVMotor : BSMotor
// maximum number of outputs to generate. // maximum number of outputs to generate.
int maxOutput = 50; int maxOutput = 50;
MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName); MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}", MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},eff={4},curr={5},tgt={6}",
BSScene.DetailLogZero, UseName, BSScene.DetailLogZero, UseName,
TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, TimeScale, TargetValueDecayTimeScale, Efficiency,
CurrentValue, TargetValue); CurrentValue, TargetValue);
LastError = BSMotor.InfiniteVector; LastError = BSMotor.InfiniteVector;
@ -235,43 +228,141 @@ public class BSVMotor : BSMotor
BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep); BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
} }
MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName); MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
} }
public override string ToString() public override string ToString()
{ {
return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4},frictTS={5}>", return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4}>",
UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale, FrictionTimescale); UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale);
} }
} }
// ============================================================================
// ============================================================================
public class BSFMotor : BSMotor public class BSFMotor : BSMotor
{ {
public float TimeScale { get; set; } public virtual float TimeScale { get; set; }
public float DecayTimeScale { get; set; } public virtual float TargetValueDecayTimeScale { get; set; }
public float Friction { get; set; } public virtual float Efficiency { get; set; }
public float Efficiency { get; set; }
public float Target { get; private set; } public virtual float ErrorZeroThreshold { get; set; }
public float CurrentValue { get; private set; }
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency) public virtual float TargetValue { get; protected set; }
public virtual float CurrentValue { get; protected set; }
public virtual float LastError { get; protected set; }
public virtual bool ErrorIsZero()
{
return ErrorIsZero(LastError);
}
public virtual bool ErrorIsZero(float err)
{
return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold);
}
public BSFMotor(string useName, float timeScale, float decayTimescale, float efficiency)
: base(useName) : base(useName)
{ {
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
Efficiency = 1f;
CurrentValue = TargetValue = 0f;
ErrorZeroThreshold = 0.01f;
} }
public void SetCurrent(float target) public void SetCurrent(float current)
{ {
CurrentValue = current;
} }
public void SetTarget(float target) public void SetTarget(float target)
{ {
TargetValue = target;
} }
public override void Zero()
{
base.Zero();
CurrentValue = TargetValue = 0f;
}
public virtual float Step(float timeStep) public virtual float Step(float timeStep)
{ {
return 0f; if (!Enabled) return TargetValue;
float origTarget = TargetValue; // DEBUG
float origCurrVal = CurrentValue; // DEBUG
float correction = 0f;
float error = TargetValue - CurrentValue;
LastError = error;
if (!ErrorIsZero(error))
{
correction = StepError(timeStep, error);
CurrentValue += correction;
// The desired value reduces to zero which also reduces the difference with current.
// If the decay time is infinite, don't decay at all.
float decayFactor = 0f;
if (TargetValueDecayTimeScale != BSMotor.Infinite)
{
decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
TargetValue *= (1f - decayFactor);
}
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
timeStep, error, correction);
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},tgt={4},curr={5}",
BSScene.DetailLogZero, UseName, TargetValueDecayTimeScale, decayFactor, TargetValue, CurrentValue);
}
else
{
// Difference between what we have and target is small. Motor is done.
if (Util.InRange<float>(TargetValue, -ErrorZeroThreshold, ErrorZeroThreshold))
{
// The target can step down to nearly zero but not get there. If close to zero
// it is really zero.
TargetValue = 0f;
}
CurrentValue = TargetValue;
MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
}
return CurrentValue;
} }
public virtual float StepError(float timeStep, float error)
{
if (!Enabled) return 0f;
float returnCorrection = 0f;
if (!ErrorIsZero(error))
{
// correction = error / secondsItShouldTakeToCorrect
float correctionAmount;
if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
correctionAmount = error * timeStep;
else
correctionAmount = error / TimeScale * timeStep;
returnCorrection = correctionAmount;
MDetailLog("{0}, BSFMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
}
return returnCorrection;
}
public override string ToString()
{
return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4}>",
UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale);
}
} }
// ============================================================================
// ============================================================================
// Proportional, Integral, Derivitive Motor // Proportional, Integral, Derivitive Motor
// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors. // Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
public class BSPIDVMotor : BSVMotor public class BSPIDVMotor : BSVMotor
@ -281,6 +372,12 @@ public class BSPIDVMotor : BSVMotor
public Vector3 integralFactor { get; set; } public Vector3 integralFactor { get; set; }
public Vector3 derivFactor { get; set; } public Vector3 derivFactor { get; set; }
// The factors are vectors for the three dimensions. This is the proportional of each
// that is applied. This could be multiplied through the actual factors but it
// is sometimes easier to manipulate the factors and their mix separately.
// to
public Vector3 FactorMix;
// Arbritrary factor range. // Arbritrary factor range.
// EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct. // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
public float EfficiencyHigh = 0.4f; public float EfficiencyHigh = 0.4f;
@ -295,6 +392,7 @@ public class BSPIDVMotor : BSVMotor
proportionFactor = new Vector3(1.00f, 1.00f, 1.00f); proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
integralFactor = new Vector3(1.00f, 1.00f, 1.00f); integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
derivFactor = new Vector3(1.00f, 1.00f, 1.00f); derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
FactorMix = new Vector3(0.5f, 0.25f, 0.25f);
RunningIntegration = Vector3.Zero; RunningIntegration = Vector3.Zero;
LastError = Vector3.Zero; LastError = Vector3.Zero;
} }
@ -310,20 +408,24 @@ public class BSPIDVMotor : BSVMotor
set set
{ {
base.Efficiency = Util.Clamp(value, 0f, 1f); base.Efficiency = Util.Clamp(value, 0f, 1f);
// Compute factors based on efficiency. // Compute factors based on efficiency.
// If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot. // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
// If efficiency is low (0f), use a factor value that overcorrects. // If efficiency is low (0f), use a factor value that overcorrects.
// TODO: might want to vary contribution of different factor depending on efficiency. // TODO: might want to vary contribution of different factor depending on efficiency.
float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f; float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
// float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow; // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
proportionFactor = new Vector3(factor, factor, factor); proportionFactor = new Vector3(factor, factor, factor);
integralFactor = new Vector3(factor, factor, factor); integralFactor = new Vector3(factor, factor, factor);
derivFactor = new Vector3(factor, factor, factor); derivFactor = new Vector3(factor, factor, factor);
MDetailLog("{0},BSPIDVMotor.setEfficiency,eff={1},factor={2}", BSScene.DetailLogZero, Efficiency, factor);
} }
} }
// Ignore Current and Target Values and just advance the PID computation on this error. // Advance the PID computation on this error.
public override Vector3 Step(float timeStep, Vector3 error) public override Vector3 StepError(float timeStep, Vector3 error)
{ {
if (!Enabled) return Vector3.Zero; if (!Enabled) return Vector3.Zero;
@ -331,15 +433,17 @@ public class BSPIDVMotor : BSVMotor
RunningIntegration += error * timeStep; RunningIntegration += error * timeStep;
// A simple derivitive is the rate of change from the last error. // A simple derivitive is the rate of change from the last error.
Vector3 derivFactor = (error - LastError) * timeStep; Vector3 derivitive = (error - LastError) * timeStep;
LastError = error; LastError = error;
// Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError) // Correction = (proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
Vector3 ret = -( Vector3 ret = error * timeStep * proportionFactor * FactorMix.X
error * proportionFactor + RunningIntegration * integralFactor * FactorMix.Y
+ RunningIntegration * integralFactor + derivitive * derivFactor * FactorMix.Z
+ derivFactor * derivFactor ;
);
MDetailLog("{0},BSPIDVMotor.step,ts={1},err={2},runnInt={3},deriv={4},ret={5}",
BSScene.DetailLogZero, timeStep, error, RunningIntegration, derivitive, ret);
return ret; return ret;
} }

File diff suppressed because it is too large Load Diff

View File

@ -38,12 +38,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
* Class to wrap all objects. * Class to wrap all objects.
* The rest of BulletSim doesn't need to keep checking for avatars or prims * The rest of BulletSim doesn't need to keep checking for avatars or prims
* unless the difference is significant. * unless the difference is significant.
* *
* Variables in the physicsl objects are in three forms: * Variables in the physicsl objects are in three forms:
* VariableName: used by the simulator and performs taint operations, etc * VariableName: used by the simulator and performs taint operations, etc
* RawVariableName: direct reference to the BulletSim storage for the variable value * RawVariableName: direct reference to the BulletSim storage for the variable value
* ForceVariableName: direct reference (store and fetch) to the value in the physics engine. * ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
* The last two (and certainly the last one) should be referenced only in taint-time. * The last one should only be referenced in taint-time.
*/ */
/* /*
@ -52,9 +52,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
* SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
* SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
* PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
* BS.ApplyCentralForce BS.ApplyTorque * BS.ApplyCentralForce BS.ApplyTorque
*/ */
// Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc.
public enum UpdatedProperties : uint
{
Position = 1 << 0,
Orientation = 1 << 1,
Velocity = 1 << 2,
Acceleration = 1 << 3,
RotationalVelocity = 1 << 4,
EntPropUpdates = Position | Orientation | Velocity | Acceleration | RotationalVelocity,
}
public abstract class BSPhysObject : PhysicsActor public abstract class BSPhysObject : PhysicsActor
{ {
protected BSPhysObject() protected BSPhysObject()
@ -62,41 +72,61 @@ public abstract class BSPhysObject : PhysicsActor
} }
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName) protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
{ {
PhysicsScene = parentScene; PhysScene = parentScene;
LocalID = localID; LocalID = localID;
PhysObjectName = name; PhysObjectName = name;
Name = name; // PhysicsActor also has the name of the object. Someday consolidate.
TypeName = typeName; TypeName = typeName;
// The collection of things that push me around
PhysicalActors = new BSActorCollection(PhysScene);
// Initialize variables kept in base.
GravModifier = 1.0f;
Gravity = new OMV.Vector3(0f, 0f, BSParam.Gravity);
HoverActive = false;
// We don't have any physical representation yet. // We don't have any physical representation yet.
PhysBody = new BulletBody(localID); PhysBody = new BulletBody(localID);
PhysShape = new BulletShape(); PhysShape = new BSShapeNull();
// A linkset of just me PrimAssetState = PrimAssetCondition.Unknown;
Linkset = BSLinkset.Factory(PhysicsScene, this);
LastAssetBuildFailed = false;
// Default material type // Default material type. Also sets Friction, Restitution and Density.
Material = MaterialAttributes.Material.Wood; SetMaterial((int)MaterialAttributes.Material.Wood);
CollisionCollection = new CollisionEventUpdate(); CollisionCollection = new CollisionEventUpdate();
CollisionsLastReported = CollisionCollection;
CollisionsLastTick = new CollisionEventUpdate();
CollisionsLastTickStep = -1;
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
CollidingStep = 0; CollidingStep = 0;
CollidingGroundStep = 0; CollidingGroundStep = 0;
CollisionAccumulation = 0;
ColliderIsMoving = false;
CollisionScore = 0;
// All axis free.
LockedLinearAxis = LockedAxisFree;
LockedAngularAxis = LockedAxisFree;
} }
// Tell the object to clean up. // Tell the object to clean up.
public virtual void Destroy() public virtual void Destroy()
{ {
UnRegisterAllPreStepActions(); PhysicalActors.Enable(false);
PhysScene.TaintedObject("BSPhysObject.Destroy", delegate()
{
PhysicalActors.Dispose();
});
} }
public BSScene PhysicsScene { get; protected set; } public BSScene PhysScene { get; protected set; }
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
public string PhysObjectName { get; protected set; } public string PhysObjectName { get; protected set; }
public string TypeName { get; protected set; } public string TypeName { get; protected set; }
public BSLinkset Linkset { get; set; }
public BSLinksetInfo LinksetInfo { get; set; }
// Return the object mass without calculating it or having side effects // Return the object mass without calculating it or having side effects
public abstract float RawMass { get; } public abstract float RawMass { get; }
@ -104,26 +134,26 @@ public abstract class BSPhysObject : PhysicsActor
// 'inWorld' true if the object has already been added to the dynamic world. // 'inWorld' true if the object has already been added to the dynamic world.
public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld); public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
// The gravity being applied to the object. A function of default grav, GravityModifier and Buoyancy.
public virtual OMV.Vector3 Gravity { get; set; }
// The last value calculated for the prim's inertia // The last value calculated for the prim's inertia
public OMV.Vector3 Inertia { get; set; } public OMV.Vector3 Inertia { get; set; }
// Reference to the physical body (btCollisionObject) of this object // Reference to the physical body (btCollisionObject) of this object
public BulletBody PhysBody; public BulletBody PhysBody;
// Reference to the physical shape (btCollisionShape) of this object // Reference to the physical shape (btCollisionShape) of this object
public BulletShape PhysShape; public BSShape PhysShape;
// 'true' if the mesh's underlying asset failed to build. // The physical representation of the prim might require an asset fetch.
// This will keep us from looping after the first time the build failed. // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'.
public bool LastAssetBuildFailed { get; set; } public enum PrimAssetCondition
{
Unknown, Waiting, Failed, Fetched
}
public PrimAssetCondition PrimAssetState { get; set; }
// The objects base shape information. Null if not a prim type shape. // The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; } public PrimitiveBaseShape BaseShape { get; protected set; }
// Some types of objects have preferred physical representations.
// Returns SHAPE_UNKNOWN if there is no preference.
public virtual BSPhysicsShapeType PreferredPhysicalShape
{
get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
}
// When the physical properties are updated, an EntityProperty holds the update values. // When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences // Keep the current and last EntityProperties to enable computation of differences
@ -132,23 +162,35 @@ public abstract class BSPhysObject : PhysicsActor
public EntityProperties LastEntityProperties { get; set; } public EntityProperties LastEntityProperties { get; set; }
public virtual OMV.Vector3 Scale { get; set; } public virtual OMV.Vector3 Scale { get; set; }
// It can be confusing for an actor to know if it should move or update an object
// depeneding on the setting of 'selected', 'physical, ...
// This flag is the true test -- if true, the object is being acted on in the physical world
public abstract bool IsPhysicallyActive { get; }
// Detailed state of the object.
public abstract bool IsSolid { get; } public abstract bool IsSolid { get; }
public abstract bool IsStatic { get; } public abstract bool IsStatic { get; }
public abstract bool IsSelected { get; }
// Materialness // Materialness
public MaterialAttributes.Material Material { get; private set; } public MaterialAttributes.Material Material { get; private set; }
public override void SetMaterial(int material) public override void SetMaterial(int material)
{ {
Material = (MaterialAttributes.Material)material; Material = (MaterialAttributes.Material)material;
// Setting the material sets the material attributes also.
MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
Friction = matAttrib.friction;
Restitution = matAttrib.restitution;
Density = matAttrib.density / BSParam.DensityScaleFactor;
// DetailLog("{0},{1}.SetMaterial,Mat={2},frict={3},rest={4},den={5}", LocalID, TypeName, Material, Friction, Restitution, Density);
} }
// Stop all physical motion. // Stop all physical motion.
public abstract void ZeroMotion(bool inTaintTime); public abstract void ZeroMotion(bool inTaintTime);
public abstract void ZeroAngularMotion(bool inTaintTime); public abstract void ZeroAngularMotion(bool inTaintTime);
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
public virtual void StepVehicle(float timeStep) { }
// Update the physical location and motion of the object. Called with data from Bullet. // Update the physical location and motion of the object. Called with data from Bullet.
public abstract void UpdateProperties(EntityProperties entprop); public abstract void UpdateProperties(EntityProperties entprop);
@ -158,25 +200,122 @@ public abstract class BSPhysObject : PhysicsActor
public abstract OMV.Quaternion RawOrientation { get; set; } public abstract OMV.Quaternion RawOrientation { get; set; }
public abstract OMV.Quaternion ForceOrientation { get; set; } public abstract OMV.Quaternion ForceOrientation { get; set; }
// The system is telling us the velocity it wants to move at. public OMV.Vector3 RawVelocity { get; set; }
// protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
public override OMV.Vector3 TargetVelocity
{
get { return m_targetVelocity; }
set
{
m_targetVelocity = value;
Velocity = value;
}
}
public abstract OMV.Vector3 ForceVelocity { get; set; } public abstract OMV.Vector3 ForceVelocity { get; set; }
public OMV.Vector3 RawForce { get; set; }
public OMV.Vector3 RawTorque { get; set; }
public override void AddAngularForce(OMV.Vector3 force, bool pushforce)
{
AddAngularForce(force, pushforce, false);
}
public abstract void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime);
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
public abstract float ForceBuoyancy { get; set; } public abstract float ForceBuoyancy { get; set; }
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
public override bool PIDActive { set { MoveToTargetActive = value; } }
public override OMV.Vector3 PIDTarget { set { MoveToTargetTarget = value; } }
public override float PIDTau { set { MoveToTargetTau = value; } }
public bool MoveToTargetActive { get; set; }
public OMV.Vector3 MoveToTargetTarget { get; set; }
public float MoveToTargetTau { get; set; }
// Used for llSetHoverHeight and maybe vehicle height. Hover Height will override MoveTo target's Z
public override bool PIDHoverActive { set { HoverActive = value; } }
public override float PIDHoverHeight { set { HoverHeight = value; } }
public override PIDHoverType PIDHoverType { set { HoverType = value; } }
public override float PIDHoverTau { set { HoverTau = value; } }
public bool HoverActive { get; set; }
public float HoverHeight { get; set; }
public PIDHoverType HoverType { get; set; }
public float HoverTau { get; set; }
// For RotLookAt
public override OMV.Quaternion APIDTarget { set { return; } }
public override bool APIDActive { set { return; } }
public override float APIDStrength { set { return; } }
public override float APIDDamping { set { return; } }
// The current velocity forward
public virtual float ForwardSpeed
{
get
{
OMV.Vector3 characterOrientedVelocity = RawVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
return characterOrientedVelocity.X;
}
}
// The forward speed we are trying to achieve (TargetVelocity)
public virtual float TargetVelocitySpeed
{
get
{
OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
return characterOrientedVelocity.X;
}
}
// The user can optionally set the center of mass. The user's setting will override any
// computed center-of-mass (like in linksets).
// Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass.
public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; }
public OMV.Vector3 LockedLinearAxis { get; set; } // zero means locked. one means free.
public OMV.Vector3 LockedAngularAxis { get; set; } // zero means locked. one means free.
public const float FreeAxis = 1f;
public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(FreeAxis, FreeAxis, FreeAxis); // All axis are free
// Enable physical actions. Bullet will keep sleeping non-moving physical objects so
// they need waking up when parameters are changed.
// Called in taint-time!!
public void ActivateIfPhysical(bool forceIt)
{
if (IsPhysical && PhysBody.HasPhysicalBody)
PhysScene.PE.Activate(PhysBody, forceIt);
}
// 'actors' act on the physical object to change or constrain its motion. These can range from
// hovering to complex vehicle motion.
// May be called at non-taint time as this just adds the actor to the action list and the real
// work is done during the simulation step.
// Note that, if the actor is already in the list and we are disabling same, the actor is just left
// in the list disabled.
public delegate BSActor CreateActor();
public void EnableActor(bool enableActor, string actorName, CreateActor creator)
{
lock (PhysicalActors)
{
BSActor theActor;
if (PhysicalActors.TryGetActor(actorName, out theActor))
{
// The actor already exists so just turn it on or off
DetailLog("{0},BSPhysObject.EnableActor,enablingExistingActor,name={1},enable={2}", LocalID, actorName, enableActor);
theActor.Enabled = enableActor;
}
else
{
// The actor does not exist. If it should, create it.
if (enableActor)
{
DetailLog("{0},BSPhysObject.EnableActor,creatingActor,name={1}", LocalID, actorName);
theActor = creator();
PhysicalActors.Add(actorName, theActor);
theActor.Enabled = true;
}
else
{
DetailLog("{0},BSPhysObject.EnableActor,notCreatingActorSinceNotEnabled,name={1}", LocalID, actorName);
}
}
}
}
#region Collisions #region Collisions
// Requested number of milliseconds between collision events. Zero means disabled. // Requested number of milliseconds between collision events. Zero means disabled.
@ -191,41 +330,56 @@ public abstract class BSPhysObject : PhysicsActor
protected long CollidingObjectStep { get; set; } protected long CollidingObjectStep { get; set; }
// The collision flags we think are set in Bullet // The collision flags we think are set in Bullet
protected CollisionFlags CurrentCollisionFlags { get; set; } protected CollisionFlags CurrentCollisionFlags { get; set; }
// On a collision, check the collider and remember if the last collider was moving
// Used to modify the standing of avatars (avatars on stationary things stand still)
public bool ColliderIsMoving;
// Used by BSCharacter to manage standing (and not slipping)
public bool IsStationary;
// Count of collisions for this object
protected long CollisionAccumulation { get; set; }
public override bool IsColliding { public override bool IsColliding {
get { return (CollidingStep == PhysicsScene.SimulationStep); } get { return (CollidingStep == PhysScene.SimulationStep); }
set { set {
if (value) if (value)
CollidingStep = PhysicsScene.SimulationStep; CollidingStep = PhysScene.SimulationStep;
else else
CollidingStep = 0; CollidingStep = 0;
} }
} }
public override bool CollidingGround { public override bool CollidingGround {
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } get { return (CollidingGroundStep == PhysScene.SimulationStep); }
set set
{ {
if (value) if (value)
CollidingGroundStep = PhysicsScene.SimulationStep; CollidingGroundStep = PhysScene.SimulationStep;
else else
CollidingGroundStep = 0; CollidingGroundStep = 0;
} }
} }
public override bool CollidingObj { public override bool CollidingObj {
get { return (CollidingObjectStep == PhysicsScene.SimulationStep); } get { return (CollidingObjectStep == PhysScene.SimulationStep); }
set { set {
if (value) if (value)
CollidingObjectStep = PhysicsScene.SimulationStep; CollidingObjectStep = PhysScene.SimulationStep;
else else
CollidingObjectStep = 0; CollidingObjectStep = 0;
} }
} }
// The collisions that have been collected this tick // The collisions that have been collected for the next collision reporting (throttled by subscription)
protected CollisionEventUpdate CollisionCollection; protected CollisionEventUpdate CollisionCollection;
// This is the collision collection last reported to the Simulator.
public CollisionEventUpdate CollisionsLastReported;
// Remember the collisions recorded in the last tick for fancy collision checking
// (like a BSCharacter walking up stairs).
public CollisionEventUpdate CollisionsLastTick;
private long CollisionsLastTickStep = -1;
// The simulation step is telling this object about a collision. // The simulation step is telling this object about a collision.
// Return 'true' if a collision was processed and should be sent up. // Return 'true' if a collision was processed and should be sent up.
// Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision.
// Called at taint time from within the Step() function // Called at taint time from within the Step() function
public virtual bool Collide(uint collidingWith, BSPhysObject collidee, public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
@ -233,27 +387,35 @@ public abstract class BSPhysObject : PhysicsActor
bool ret = false; bool ret = false;
// The following lines make IsColliding(), CollidingGround() and CollidingObj work // The following lines make IsColliding(), CollidingGround() and CollidingObj work
CollidingStep = PhysicsScene.SimulationStep; CollidingStep = PhysScene.SimulationStep;
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID)
{ {
CollidingGroundStep = PhysicsScene.SimulationStep; CollidingGroundStep = PhysScene.SimulationStep;
} }
else else
{ {
CollidingObjectStep = PhysicsScene.SimulationStep; CollidingObjectStep = PhysScene.SimulationStep;
} }
// prims in the same linkset cannot collide with each other CollisionAccumulation++;
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
// For movement tests, remember if we are colliding with an object that is moving.
ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false;
// Make a collection of the collisions that happened the last simulation tick.
// This is different than the collection created for sending up to the simulator as it is cleared every tick.
if (CollisionsLastTickStep != PhysScene.SimulationStep)
{ {
return ret; CollisionsLastTick = new CollisionEventUpdate();
CollisionsLastTickStep = PhysScene.SimulationStep;
} }
CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
// if someone has subscribed for collision events.... // If someone has subscribed for collision events log the collision so it will be reported up
if (SubscribedEvents()) { if (SubscribedEvents()) {
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}",
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving);
ret = true; ret = true;
} }
@ -267,13 +429,14 @@ public abstract class BSPhysObject : PhysicsActor
public virtual bool SendCollisions() public virtual bool SendCollisions()
{ {
bool ret = true; bool ret = true;
// If the 'no collision' call, force it to happen right now so quick collision_end // If the 'no collision' call, force it to happen right now so quick collision_end
bool force = (CollisionCollection.Count == 0); bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0);
// throttle the collisions to the number of milliseconds specified in the subscription // throttle the collisions to the number of milliseconds specified in the subscription
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime))
{ {
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs; NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs;
// We are called if we previously had collisions. If there are no collisions // We are called if we previously had collisions. If there are no collisions
// this time, send up one last empty event so OpenSim can sense collision end. // this time, send up one last empty event so OpenSim can sense collision end.
@ -283,12 +446,15 @@ public abstract class BSPhysObject : PhysicsActor
ret = false; ret = false;
} }
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
base.SendCollisionUpdate(CollisionCollection); base.SendCollisionUpdate(CollisionCollection);
// Remember the collisions from this tick for some collision specific processing.
CollisionsLastReported = CollisionCollection;
// The CollisionCollection instance is passed around in the simulator. // The CollisionCollection instance is passed around in the simulator.
// Make sure we don't have a handle to that one and that a new one is used for next time. // Make sure we don't have a handle to that one and that a new one is used for next time.
// This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
// a race condition is created for the other users of this instance. // a race condition is created for the other users of this instance.
CollisionCollection = new CollisionEventUpdate(); CollisionCollection = new CollisionEventUpdate();
} }
@ -305,10 +471,10 @@ public abstract class BSPhysObject : PhysicsActor
// make sure first collision happens // make sure first collision happens
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() PhysScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
}); });
} }
else else
@ -320,66 +486,53 @@ public abstract class BSPhysObject : PhysicsActor
public override void UnSubscribeEvents() { public override void UnSubscribeEvents() {
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() PhysScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
{ {
// Make sure there is a body there because sometimes destruction happens in an un-ideal order. // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
}); });
} }
// Return 'true' if the simulator wants collision events // Return 'true' if the simulator wants collision events
public override bool SubscribedEvents() { public override bool SubscribedEvents() {
return (SubscribedEventsMs > 0); return (SubscribedEventsMs > 0);
} }
// Because 'CollisionScore' is called many times while sorting, it should not be recomputed
// each time called. So this is built to be light weight for each collision and to do
// all the processing when the user asks for the info.
public void ComputeCollisionScore()
{
// Scale the collision count by the time since the last collision.
// The "+1" prevents dividing by zero.
long timeAgo = PhysScene.SimulationStep - CollidingStep + 1;
CollisionScore = CollisionAccumulation / timeAgo;
}
public override float CollisionScore { get; set; }
#endregion // Collisions #endregion // Collisions
#region Per Simulation Step actions #region Per Simulation Step actions
// There are some actions that must be performed for a physical object before each simulation step.
// These actions are optional so, rather than scanning all the physical objects and asking them public BSActorCollection PhysicalActors;
// if they have anything to do, a physical object registers for an event call before the step is performed.
// This bookkeeping makes it easy to add, remove and clean up after all these registrations. // When an update to the physical properties happens, this event is fired to let
private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>(); // different actors to modify the update before it is passed around
protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn) public delegate void PreUpdatePropertyAction(ref EntityProperties entprop);
public event PreUpdatePropertyAction OnPreUpdateProperty;
protected void TriggerPreUpdatePropertyAction(ref EntityProperties entprop)
{ {
string identifier = op + "-" + id.ToString(); PreUpdatePropertyAction actions = OnPreUpdateProperty;
RegisteredActions[identifier] = actn; if (actions != null)
PhysicsScene.BeforeStep += actn; actions(ref entprop);
DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
} }
// Unregister a pre step action. Safe to call if the action has not been registered.
protected void UnRegisterPreStepAction(string op, uint id)
{
string identifier = op + "-" + id.ToString();
bool removed = false;
if (RegisteredActions.ContainsKey(identifier))
{
PhysicsScene.BeforeStep -= RegisteredActions[identifier];
RegisteredActions.Remove(identifier);
removed = true;
}
DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
}
protected void UnRegisterAllPreStepActions()
{
foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
{
PhysicsScene.BeforeStep -= kvp.Value;
}
RegisteredActions.Clear();
DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
}
#endregion // Per Simulation Step actions #endregion // Per Simulation Step actions
// High performance detailed logging routine used by the physical objects. // High performance detailed logging routine used by the physical objects.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {
if (PhysicsScene.PhysicsLogging.Enabled) if (PhysScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args); PhysScene.DetailLog(msg, args);
} }
} }

View File

@ -59,7 +59,7 @@ public class BSPlugin : IPhysicsPlugin
{ {
if (_mScene == null) if (_mScene == null)
{ {
_mScene = new BSScene(sceneIdentifier); _mScene = new BSScene(GetName(), sceneIdentifier);
} }
return (_mScene); return (_mScene);
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
/*
* 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.
*
* The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
* are Copyright (c) 2009 Linden Research, Inc and are used under their license
* of Creative Commons Attribution-Share Alike 3.0
* (http://creativecommons.org/licenses/by-sa/3.0/).
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSPrimDisplaced : BSPrim
{
// The purpose of this module is to do any mapping between what the simulator thinks
// the prim position and orientation is and what the physical position/orientation.
// This difference happens because Bullet assumes the center-of-mass is the <0,0,0>
// of the prim/linkset. The simulator tracks the location of the prim/linkset by
// the location of the root prim. So, if center-of-mass is anywhere but the origin
// of the root prim, the physical origin is displaced from the simulator origin.
//
// This routine works by capturing the Force* setting of position/orientation/... and
// adjusting the simulator values (being set) into the physical values.
// The conversion is also done in the opposite direction (physical origin -> simulator origin).
//
// The updateParameter call is also captured and the values from the physics engine
// are converted into simulator origin values before being passed to the base
// class.
public virtual OMV.Vector3 PositionDisplacement { get; set; }
public virtual OMV.Quaternion OrientationDisplacement { get; set; }
public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
: base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical)
{
ClearDisplacement();
}
public void ClearDisplacement()
{
PositionDisplacement = OMV.Vector3.Zero;
OrientationDisplacement = OMV.Quaternion.Identity;
}
// Set this sets and computes the displacement from the passed prim to the center-of-mass.
// A user set value for center-of-mass overrides whatever might be passed in here.
// The displacement is in local coordinates (relative to root prim in linkset oriented coordinates).
public virtual void SetEffectiveCenterOfMassDisplacement(Vector3 centerOfMassDisplacement)
{
Vector3 comDisp;
if (UserSetCenterOfMassDisplacement.HasValue)
comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement;
else
comDisp = centerOfMassDisplacement;
DetailLog("{0},BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement,userSet={1},comDisp={2}",
LocalID, UserSetCenterOfMassDisplacement.HasValue, comDisp);
if (comDisp == Vector3.Zero)
{
// If there is no diplacement. Things get reset.
PositionDisplacement = OMV.Vector3.Zero;
OrientationDisplacement = OMV.Quaternion.Identity;
}
else
{
// Remember the displacement from root as well as the origional rotation of the
// new center-of-mass.
PositionDisplacement = comDisp;
OrientationDisplacement = OMV.Quaternion.Identity;
}
}
public override Vector3 ForcePosition
{
get { return base.ForcePosition; }
set
{
if (PositionDisplacement != OMV.Vector3.Zero)
{
OMV.Vector3 displacedPos = value - (PositionDisplacement * RawOrientation);
DetailLog("{0},BSPrimDisplaced.ForcePosition,val={1},disp={2},newPos={3}", LocalID, value, PositionDisplacement, displacedPos);
base.ForcePosition = displacedPos;
}
else
{
base.ForcePosition = value;
}
}
}
public override Quaternion ForceOrientation
{
get { return base.ForceOrientation; }
set
{
// TODO:
base.ForceOrientation = value;
}
}
// TODO: decide if this is the right place for these variables.
// Somehow incorporate the optional settability by the user.
// Is this used?
public override OMV.Vector3 CenterOfMass
{
get { return RawPosition; }
}
// Is this used?
public override OMV.Vector3 GeometricCenter
{
get { return RawPosition; }
}
public override void UpdateProperties(EntityProperties entprop)
{
// Undo any center-of-mass displacement that might have been done.
if (PositionDisplacement != OMV.Vector3.Zero || OrientationDisplacement != OMV.Quaternion.Identity)
{
// Correct for any rotation around the center-of-mass
// TODO!!!
OMV.Vector3 displacedPos = entprop.Position + (PositionDisplacement * entprop.Rotation);
DetailLog("{0},BSPrimDisplaced.ForcePosition,physPos={1},disp={2},newPos={3}", LocalID, entprop.Position, PositionDisplacement, displacedPos);
entprop.Position = displacedPos;
// entprop.Rotation = something;
}
base.UpdateProperties(entprop);
}
}
}

View File

@ -0,0 +1,192 @@
/*
* 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 copyrightD
* 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.Linq;
using System.Text;
using OpenSim.Framework;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public class BSPrimLinkable : BSPrimDisplaced
{
public BSLinkset Linkset { get; set; }
// The index of this child prim.
public int LinksetChildIndex { get; set; }
public BSLinksetInfo LinksetInfo { get; set; }
public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
: base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical)
{
Linkset = BSLinkset.Factory(PhysScene, this);
PhysScene.TaintedObject("BSPrimLinksetCompound.Refresh", delegate()
{
Linkset.Refresh(this);
});
}
public override void Destroy()
{
Linkset = Linkset.RemoveMeFromLinkset(this);
base.Destroy();
}
public override void link(Manager.PhysicsActor obj)
{
BSPrimLinkable parent = obj as BSPrimLinkable;
if (parent != null)
{
BSPhysObject parentBefore = Linkset.LinksetRoot;
int childrenBefore = Linkset.NumberOfChildren;
Linkset = parent.Linkset.AddMeToLinkset(this);
DetailLog("{0},BSPrimLinkset.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}",
LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
}
return;
}
public override void delink()
{
// TODO: decide if this parent checking needs to happen at taint time
// Race condition here: if link() and delink() in same simulation tick, the delink will not happen
BSPhysObject parentBefore = Linkset.LinksetRoot;
int childrenBefore = Linkset.NumberOfChildren;
Linkset = Linkset.RemoveMeFromLinkset(this);
DetailLog("{0},BSPrimLinkset.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
return;
}
// When simulator changes position, this might be moving a child of the linkset.
public override OMV.Vector3 Position
{
get { return base.Position; }
set
{
base.Position = value;
PhysScene.TaintedObject("BSPrimLinkset.setPosition", delegate()
{
Linkset.UpdateProperties(UpdatedProperties.Position, this);
});
}
}
// When simulator changes orientation, this might be moving a child of the linkset.
public override OMV.Quaternion Orientation
{
get { return base.Orientation; }
set
{
base.Orientation = value;
PhysScene.TaintedObject("BSPrimLinkset.setOrientation", delegate()
{
Linkset.UpdateProperties(UpdatedProperties.Orientation, this);
});
}
}
public override float TotalMass
{
get { return Linkset.LinksetMass; }
}
public override void UpdatePhysicalParameters()
{
base.UpdatePhysicalParameters();
// Recompute any linkset parameters.
// When going from non-physical to physical, this re-enables the constraints that
// had been automatically disabled when the mass was set to zero.
// For compound based linksets, this enables and disables interactions of the children.
if (Linkset != null) // null can happen during initialization
Linkset.Refresh(this);
}
protected override void MakeDynamic(bool makeStatic)
{
base.MakeDynamic(makeStatic);
if (makeStatic)
Linkset.MakeStatic(this);
else
Linkset.MakeDynamic(this);
}
// Body is being taken apart. Remove physical dependencies and schedule a rebuild.
protected override void RemoveDependencies()
{
Linkset.RemoveDependencies(this);
base.RemoveDependencies();
}
public override void UpdateProperties(EntityProperties entprop)
{
if (Linkset.IsRoot(this))
{
// Properties are only updated for the roots of a linkset.
// TODO: this will have to change when linksets are articulated.
base.UpdateProperties(entprop);
}
/*
else
{
// For debugging, report the movement of children
DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
entprop.Acceleration, entprop.RotationalVelocity);
}
*/
// The linkset might like to know about changing locations
Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
}
public override bool Collide(uint collidingWith, BSPhysObject collidee,
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
{
// prims in the same linkset cannot collide with each other
BSPrimLinkable convCollidee = collidee as BSPrimLinkable;
if (convCollidee != null && (this.Linkset.LinksetID == convCollidee.Linkset.LinksetID))
{
return false;
}
// TODO: handle collisions of other objects with with children of linkset.
// This is a problem for LinksetCompound since the children are packed into the root.
return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth);
}
}
}

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -81,14 +82,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
internal long m_simulationStep = 0; internal long m_simulationStep = 0;
internal float NominalFrameRate { get; set; } internal float NominalFrameRate { get; set; }
public long SimulationStep { get { return m_simulationStep; } } public long SimulationStep { get { return m_simulationStep; } }
internal int m_taintsToProcessPerStep;
internal float LastTimeStep { get; private set; } internal float LastTimeStep { get; private set; }
// Physical objects can register for prestep or poststep events // Physical objects can register for prestep or poststep events
public delegate void PreStepAction(float timeStep); public delegate void PreStepAction(float timeStep);
public delegate void PostStepAction(float timeStep); public delegate void PostStepAction(float timeStep);
public event PreStepAction BeforeStep; public event PreStepAction BeforeStep;
public event PreStepAction AfterStep; public event PostStepAction AfterStep;
// A value of the time now so all the collision and update routines do not have to get their own // A value of the time now so all the collision and update routines do not have to get their own
// Set to 'now' just before all the prims and actors are called for collisions and updates // Set to 'now' just before all the prims and actors are called for collisions and updates
@ -161,17 +161,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
private int m_physicsLoggingFileMinutes; private int m_physicsLoggingFileMinutes;
private bool m_physicsLoggingDoFlush; private bool m_physicsLoggingDoFlush;
private bool m_physicsPhysicalDumpEnabled; private bool m_physicsPhysicalDumpEnabled;
public float PhysicsMetricDumpFrames { get; set; } public int PhysicsMetricDumpFrames { get; set; }
// 'true' of the vehicle code is to log lots of details // 'true' of the vehicle code is to log lots of details
public bool VehicleLoggingEnabled { get; private set; } public bool VehicleLoggingEnabled { get; private set; }
public bool VehiclePhysicalLoggingEnabled { get; private set; } public bool VehiclePhysicalLoggingEnabled { get; private set; }
#region Construction and Initialization #region Construction and Initialization
public BSScene(string identifier) public BSScene(string engineType, string identifier)
{ {
m_initialized = false; m_initialized = false;
// we are passed the name of the region we're working for.
// The name of the region we're working for is passed to us. Keep for identification.
RegionName = identifier; RegionName = identifier;
// Set identifying variables in the PhysicsScene interface.
EngineType = engineType;
Name = EngineType + "/" + RegionName;
} }
public override void Initialise(IMesher meshmerizer, IConfigSource config) public override void Initialise(IMesher meshmerizer, IConfigSource config)
@ -309,9 +314,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
case "bulletunmanaged": case "bulletunmanaged":
ret = new BSAPIUnman(engineName, this); ret = new BSAPIUnman(engineName, this);
break; break;
/*
case "bulletxna": case "bulletxna":
ret = new BSAPIXNA(engineName, this); ret = new BSAPIXNA(engineName, this);
// Disable some features that are not implemented in BulletXNA
m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader);
BSParam.ShouldUseBulletHACD = false;
BSParam.ShouldUseSingleConvexHullForPrims = false;
break; break;
*/
} }
if (ret == null) if (ret == null)
@ -382,12 +393,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return null; if (!m_initialized) return null;
BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
lock (PhysObjects) PhysObjects.Add(localID, actor); lock (PhysObjects)
PhysObjects.Add(localID, actor);
// TODO: Remove kludge someday. // TODO: Remove kludge someday.
// We must generate a collision for avatars whether they collide or not. // We must generate a collision for avatars whether they collide or not.
// This is required by OpenSim to update avatar animations, etc. // This is required by OpenSim to update avatar animations, etc.
lock (m_avatars) m_avatars.Add(actor); lock (m_avatars)
m_avatars.Add(actor);
return actor; return actor;
} }
@ -403,9 +416,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
{ {
try try
{ {
lock (PhysObjects) PhysObjects.Remove(actor.LocalID); lock (PhysObjects)
PhysObjects.Remove(bsactor.LocalID);
// Remove kludge someday // Remove kludge someday
lock (m_avatars) m_avatars.Remove(bsactor); lock (m_avatars)
m_avatars.Remove(bsactor);
} }
catch (Exception e) catch (Exception e)
{ {
@ -414,13 +429,18 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
bsactor.Destroy(); bsactor.Destroy();
// bsactor.dispose(); // bsactor.dispose();
} }
else
{
m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
LogHeader, actor.LocalID, actor.GetType().Name);
}
} }
public override void RemovePrim(PhysicsActor prim) public override void RemovePrim(PhysicsActor prim)
{ {
if (!m_initialized) return; if (!m_initialized) return;
BSPrim bsprim = prim as BSPrim; BSPhysObject bsprim = prim as BSPhysObject;
if (bsprim != null) if (bsprim != null)
{ {
DetailLog("{0},RemovePrim,call", bsprim.LocalID); DetailLog("{0},RemovePrim,call", bsprim.LocalID);
@ -449,9 +469,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return null; if (!m_initialized) return null;
DetailLog("{0},AddPrimShape,call", localID); // DetailLog("{0},BSScene.AddPrimShape,call", localID);
BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical);
lock (PhysObjects) PhysObjects.Add(localID, prim); lock (PhysObjects) PhysObjects.Add(localID, prim);
return prim; return prim;
} }
@ -486,6 +506,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
ProcessTaints(); ProcessTaints();
// Some of the physical objects requre individual, pre-step calls // Some of the physical objects requre individual, pre-step calls
// (vehicles and avatar movement, in particular)
TriggerPreStepEvent(timeStep); TriggerPreStepEvent(timeStep);
// the prestep actions might have added taints // the prestep actions might have added taints
@ -527,7 +548,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
collidersCount = 0; collidersCount = 0;
} }
if ((m_simulationStep % PhysicsMetricDumpFrames) == 0) if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
PE.DumpPhysicsStatistics(World); PE.DumpPhysicsStatistics(World);
// Get a value for 'now' so all the collision and update routines don't have to get their own. // Get a value for 'now' so all the collision and update routines don't have to get their own.
@ -543,8 +564,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
uint cB = m_collisionArray[ii].bID; uint cB = m_collisionArray[ii].bID;
Vector3 point = m_collisionArray[ii].point; Vector3 point = m_collisionArray[ii].point;
Vector3 normal = m_collisionArray[ii].normal; Vector3 normal = m_collisionArray[ii].normal;
SendCollision(cA, cB, point, normal, 0.01f); float penetration = m_collisionArray[ii].penetration;
SendCollision(cB, cA, point, -normal, 0.01f); SendCollision(cA, cB, point, normal, penetration);
SendCollision(cB, cA, point, -normal, penetration);
} }
} }
@ -682,7 +704,21 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
public override Dictionary<uint, float> GetTopColliders() public override Dictionary<uint, float> GetTopColliders()
{ {
return new Dictionary<uint, float>(); Dictionary<uint, float> topColliders;
lock (PhysObjects)
{
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
{
kvp.Value.ComputeCollisionScore();
}
List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
orderedPrims.OrderByDescending(p => p.CollisionScore);
topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
}
return topColliders;
} }
public override bool IsThreaded { get { return false; } } public override bool IsThreaded { get { return false; } }
@ -694,8 +730,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// TriggerPreStepEvent // TriggerPreStepEvent
// DoOneTimeTaints // DoOneTimeTaints
// Step() // Step()
// ProcessAndForwardCollisions // ProcessAndSendToSimulatorCollisions
// ProcessAndForwardPropertyUpdates // ProcessAndSendToSimulatorPropertyUpdates
// TriggerPostStepEvent // TriggerPostStepEvent
// Calls to the PhysicsActors can't directly call into the physics engine // Calls to the PhysicsActors can't directly call into the physics engine
@ -733,7 +769,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
private void TriggerPostStepEvent(float timeStep) private void TriggerPostStepEvent(float timeStep)
{ {
PreStepAction actions = AfterStep; PostStepAction actions = AfterStep;
if (actions != null) if (actions != null)
actions(timeStep); actions(timeStep);
@ -825,15 +861,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
{ {
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
Util.PrintCallStack(DetailLog); // Util.PrintCallStack(DetailLog);
} }
return InTaintTime; return InTaintTime;
} }
#endregion // Taints #endregion // Taints
#region INI and command line parameter processing
#region IPhysicsParameters #region IPhysicsParameters
// Get the list of parameters this physics engine supports // Get the list of parameters this physics engine supports
public PhysParameterEntry[] GetParameterList() public PhysParameterEntry[] GetParameterList()
@ -848,64 +882,65 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// will use the next time since it's pinned and shared memory. // will use the next time since it's pinned and shared memory.
// Some of the values require calling into the physics engine to get the new // Some of the values require calling into the physics engine to get the new
// value activated ('terrainFriction' for instance). // value activated ('terrainFriction' for instance).
public bool SetPhysicsParameter(string parm, float val, uint localID) public bool SetPhysicsParameter(string parm, string val, uint localID)
{ {
bool ret = false; bool ret = false;
BSParam.ParameterDefn theParam;
BSParam.ParameterDefnBase theParam;
if (BSParam.TryGetParameter(parm, out theParam)) if (BSParam.TryGetParameter(parm, out theParam))
{ {
theParam.setter(this, parm, localID, val); // Set the value in the C# code
theParam.SetValue(this, val);
// Optionally set the parameter in the unmanaged code
if (theParam.HasSetOnObject)
{
// update all the localIDs specified
// If the local ID is APPLY_TO_NONE, just change the default value
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
// If the localID is a specific object, apply the parameter change to only that object
List<uint> objectIDs = new List<uint>();
switch (localID)
{
case PhysParameterEntry.APPLY_TO_NONE:
// This will cause a call into the physical world if some operation is specified (SetOnObject).
objectIDs.Add(TERRAIN_ID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
case PhysParameterEntry.APPLY_TO_ALL:
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
TaintedUpdateParameter(parm, objectIDs, val);
break;
default:
// setting only one localID
objectIDs.Add(localID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
}
}
ret = true; ret = true;
} }
return ret; return ret;
} }
// update all the localIDs specified
// If the local ID is APPLY_TO_NONE, just change the default value
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
// If the localID is a specific object, apply the parameter change to only that object
internal delegate void AssignVal(float x);
internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
{
List<uint> objectIDs = new List<uint>();
switch (localID)
{
case PhysParameterEntry.APPLY_TO_NONE:
setDefault(val); // setting only the default value
// This will cause a call into the physical world if some operation is specified (SetOnObject).
objectIDs.Add(TERRAIN_ID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
case PhysParameterEntry.APPLY_TO_ALL:
setDefault(val); // setting ALL also sets the default value
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
TaintedUpdateParameter(parm, objectIDs, val);
break;
default:
// setting only one localID
objectIDs.Add(localID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
}
}
// schedule the actual updating of the paramter to when the phys engine is not busy // schedule the actual updating of the paramter to when the phys engine is not busy
private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val) private void TaintedUpdateParameter(string parm, List<uint> lIDs, string val)
{ {
float xval = val; string xval = val;
List<uint> xlIDs = lIDs; List<uint> xlIDs = lIDs;
string xparm = parm; string xparm = parm;
TaintedObject("BSScene.UpdateParameterSet", delegate() { TaintedObject("BSScene.UpdateParameterSet", delegate() {
BSParam.ParameterDefn thisParam; BSParam.ParameterDefnBase thisParam;
if (BSParam.TryGetParameter(xparm, out thisParam)) if (BSParam.TryGetParameter(xparm, out thisParam))
{ {
if (thisParam.onObject != null) if (thisParam.HasSetOnObject)
{ {
foreach (uint lID in xlIDs) foreach (uint lID in xlIDs)
{ {
BSPhysObject theObject = null; BSPhysObject theObject = null;
PhysObjects.TryGetValue(lID, out theObject); if (PhysObjects.TryGetValue(lID, out theObject))
thisParam.onObject(this, theObject, xval); thisParam.SetOnObject(this, theObject);
} }
} }
} }
@ -914,14 +949,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// Get parameter. // Get parameter.
// Return 'false' if not able to get the parameter. // Return 'false' if not able to get the parameter.
public bool GetPhysicsParameter(string parm, out float value) public bool GetPhysicsParameter(string parm, out string value)
{ {
float val = 0f; string val = String.Empty;
bool ret = false; bool ret = false;
BSParam.ParameterDefn theParam; BSParam.ParameterDefnBase theParam;
if (BSParam.TryGetParameter(parm, out theParam)) if (BSParam.TryGetParameter(parm, out theParam))
{ {
val = theParam.getter(this); val = theParam.GetValue(this);
ret = true; ret = true;
} }
value = val; value = val;
@ -930,8 +965,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
#endregion IPhysicsParameters #endregion IPhysicsParameters
#endregion Runtime settable parameters
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
public void DetailLog(string msg, params Object[] args) public void DetailLog(string msg, params Object[] args)
{ {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z // This minCoords and maxCoords passed in give the size of the terrain (min and max Z
// are the high and low points of the heightmap). // are the high and low points of the heightmap).
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords) Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id) : base(physicsScene, regionBase, id)
{ {
@ -92,7 +92,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
private void BuildHeightmapTerrain() private void BuildHeightmapTerrain()
{ {
// Create the terrain shape from the mapInfo // Create the terrain shape from the mapInfo
m_mapInfo.terrainShape = PhysicsScene.PE.CreateTerrainShape( m_mapInfo.ID, m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID,
new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ, new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ,
m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin);
@ -103,26 +103,26 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f); centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f); centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f);
m_mapInfo.terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape,
m_mapInfo.ID, centerPos, Quaternion.Identity); m_mapInfo.ID, centerPos, Quaternion.Identity);
// Set current terrain attributes // Set current terrain attributes
PhysicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); m_physicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction);
PhysicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); m_physicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction);
PhysicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); m_physicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution);
PhysicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); m_physicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects // Return the new terrain to the world of physical objects
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_mapInfo.terrainBody); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_mapInfo.terrainBody);
// redo its bounding box now that it is in the world // redo its bounding box now that it is in the world
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_mapInfo.terrainBody); m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_mapInfo.terrainBody);
m_mapInfo.terrainBody.collisionType = CollisionType.Terrain; m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
m_mapInfo.terrainBody.ApplyCollisionMask(PhysicsScene); m_mapInfo.terrainBody.ApplyCollisionMask(m_physicsScene);
// Make it so the terrain will not move or be considered for movement. // Make it so the terrain will not move or be considered for movement.
PhysicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); m_physicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION);
return; return;
} }
@ -134,9 +134,9 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
{ {
if (m_mapInfo.terrainBody.HasPhysicalBody) if (m_mapInfo.terrainBody.HasPhysicalBody)
{ {
PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_mapInfo.terrainBody); m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_mapInfo.terrainBody);
// Frees both the body and the shape. // Frees both the body and the shape.
PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_mapInfo.terrainBody); m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_mapInfo.terrainBody);
} }
} }
m_mapInfo = null; m_mapInfo = null;
@ -155,7 +155,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
catch catch
{ {
// Sometimes they give us wonky values of X and Y. Give a warning and return something. // Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, m_mapInfo.terrainRegionBase, pos); LogHeader, m_mapInfo.terrainRegionBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
} }
@ -165,7 +165,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
// The passed position is relative to the base of the region. // The passed position is relative to the base of the region.
public override float GetWaterLevelAtXYZ(Vector3 pos) public override float GetWaterLevelAtXYZ(Vector3 pos)
{ {
return PhysicsScene.SimpleWaterLevel; return m_physicsScene.SimpleWaterLevel;
} }
} }
} }

View File

@ -50,14 +50,14 @@ public abstract class BSTerrainPhys : IDisposable
Mesh = 1 Mesh = 1
} }
public BSScene PhysicsScene { get; private set; } protected BSScene m_physicsScene { get; private set; }
// Base of the region in world coordinates. Coordinates inside the region are relative to this. // Base of the region in world coordinates. Coordinates inside the region are relative to this.
public Vector3 TerrainBase { get; private set; } public Vector3 TerrainBase { get; private set; }
public uint ID { get; private set; } public uint ID { get; private set; }
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id) public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
{ {
PhysicsScene = physicsScene; m_physicsScene = physicsScene;
TerrainBase = regionBase; TerrainBase = regionBase;
ID = id; ID = id;
} }
@ -86,7 +86,7 @@ public sealed class BSTerrainManager : IDisposable
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// The scene that I am part of // The scene that I am part of
private BSScene PhysicsScene { get; set; } private BSScene m_physicsScene { get; set; }
// The ground plane created to keep thing from falling to infinity. // The ground plane created to keep thing from falling to infinity.
private BulletBody m_groundPlane; private BulletBody m_groundPlane;
@ -113,7 +113,7 @@ public sealed class BSTerrainManager : IDisposable
public BSTerrainManager(BSScene physicsScene) public BSTerrainManager(BSScene physicsScene)
{ {
PhysicsScene = physicsScene; m_physicsScene = physicsScene;
m_terrains = new Dictionary<Vector3,BSTerrainPhys>(); m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
// Assume one region of default size // Assume one region of default size
@ -132,32 +132,37 @@ public sealed class BSTerrainManager : IDisposable
// safe to call Bullet in real time. We hope no one is moving prims around yet. // safe to call Bullet in real time. We hope no one is moving prims around yet.
public void CreateInitialGroundPlaneAndTerrain() public void CreateInitialGroundPlaneAndTerrain()
{ {
DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName);
// The ground plane is here to catch things that are trying to drop to negative infinity // The ground plane is here to catch things that are trying to drop to negative infinity
BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin);
m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity); BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity);
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane);
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane); m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_groundPlane);
// Ground plane does not move // Ground plane does not move
PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); m_physicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION);
// Everything collides with the ground plane. // Everything collides with the ground plane.
m_groundPlane.collisionType = CollisionType.Groundplane; m_groundPlane.collisionType = CollisionType.Groundplane;
m_groundPlane.ApplyCollisionMask(PhysicsScene); m_groundPlane.ApplyCollisionMask(m_physicsScene);
// Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. BSTerrainPhys initialTerrain = new BSTerrainHeightmap(m_physicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); lock (m_terrains)
m_terrains.Add(Vector3.Zero, initialTerrain); {
// Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
m_terrains.Add(Vector3.Zero, initialTerrain);
}
} }
// Release all the terrain structures we might have allocated // Release all the terrain structures we might have allocated
public void ReleaseGroundPlaneAndTerrain() public void ReleaseGroundPlaneAndTerrain()
{ {
DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName);
if (m_groundPlane.HasPhysicalBody) if (m_groundPlane.HasPhysicalBody)
{ {
if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane)) if (m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_groundPlane))
{ {
PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_groundPlane); m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_groundPlane);
} }
m_groundPlane.Clear(); m_groundPlane.Clear();
} }
@ -183,7 +188,7 @@ public sealed class BSTerrainManager : IDisposable
float[] localHeightMap = heightMap; float[] localHeightMap = heightMap;
// If there are multiple requests for changes to the same terrain between ticks, // If there are multiple requests for changes to the same terrain between ticks,
// only do that last one. // only do that last one.
PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() m_physicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
{ {
if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
{ {
@ -193,11 +198,9 @@ public sealed class BSTerrainManager : IDisposable
// the terrain is added to our parent // the terrain is added to our parent
if (MegaRegionParentPhysicsScene is BSScene) if (MegaRegionParentPhysicsScene is BSScene)
{ {
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax);
BSScene.DetailLogZero, m_worldOffset, m_worldMax); ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.AddMegaRegionChildTerrain(
((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain( BSScene.CHILDTERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize);
BSScene.CHILDTERRAIN_ID, localHeightMap,
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
} }
else else
@ -205,26 +208,36 @@ public sealed class BSTerrainManager : IDisposable
// If not doing the mega-prim thing, just change the terrain // If not doing the mega-prim thing, just change the terrain
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize);
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
} }
}); });
} }
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain // Another region is calling this region and passing a terrain.
// A region that is not the mega-region root will pass its terrain to the root region so the root region
// physics engine will have all the terrains.
private void AddMegaRegionChildTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
{
// Since we are called by another region's thread, the action must be rescheduled onto our processing thread.
m_physicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate()
{
UpdateTerrain(id, heightMap, minCoords, maxCoords);
});
}
// If called for terrain has has not been previously allocated, a new terrain will be built
// based on the passed information. The 'id' should be either the terrain id or // based on the passed information. The 'id' should be either the terrain id or
// BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
// The latter feature is for creating child terrains for mega-regions. // The latter feature is for creating child terrains for mega-regions.
// If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new // If there is an existing terrain body, a new
// terrain shape is created and added to the body. // terrain shape is created and added to the body.
// This call is most often used to update the heightMap and parameters of the terrain. // This call is most often used to update the heightMap and parameters of the terrain.
// (The above does suggest that some simplification/refactoring is in order.) // (The above does suggest that some simplification/refactoring is in order.)
// Called during taint-time. // Called during taint-time.
private void UpdateTerrain(uint id, float[] heightMap, private void UpdateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
{ {
DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}", DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1},minC={2},maxC={3}",
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); BSScene.DetailLogZero, id, minCoords, maxCoords);
// Find high and low points of passed heightmap. // Find high and low points of passed heightmap.
// The min and max passed in is usually the area objects can be in (maximum // The min and max passed in is usually the area objects can be in (maximum
@ -253,7 +266,7 @@ public sealed class BSTerrainManager : IDisposable
if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys)) if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
{ {
// There is already a terrain in this spot. Free the old and build the new. // There is already a terrain in this spot. Free the old and build the new.
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords); BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
// Remove old terrain from the collection // Remove old terrain from the collection
@ -263,6 +276,7 @@ public sealed class BSTerrainManager : IDisposable
if (MegaRegionParentPhysicsScene == null) if (MegaRegionParentPhysicsScene == null)
{ {
// This terrain is not part of the mega-region scheme. Create vanilla terrain.
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
m_terrains.Add(terrainRegionBase, newTerrainPhys); m_terrains.Add(terrainRegionBase, newTerrainPhys);
@ -291,8 +305,8 @@ public sealed class BSTerrainManager : IDisposable
if (newTerrainID >= BSScene.CHILDTERRAIN_ID) if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
newTerrainID = ++m_terrainCount; newTerrainID = ++m_terrainCount;
DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}", DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); BSScene.DetailLogZero, newTerrainID, minCoords, maxCoords);
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
m_terrains.Add(terrainRegionBase, newTerrainPhys); m_terrains.Add(terrainRegionBase, newTerrainPhys);
@ -304,26 +318,26 @@ public sealed class BSTerrainManager : IDisposable
// TODO: redo terrain implementation selection to allow other base types than heightMap. // TODO: redo terrain implementation selection to allow other base types than heightMap.
private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
{ {
PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
LogHeader, PhysicsScene.RegionName, terrainRegionBase, LogHeader, m_physicsScene.RegionName, terrainRegionBase,
(BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
BSTerrainPhys newTerrainPhys = null; BSTerrainPhys newTerrainPhys = null;
switch ((int)BSParam.TerrainImplementation) switch ((int)BSParam.TerrainImplementation)
{ {
case (int)BSTerrainPhys.TerrainImplementation.Heightmap: case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, newTerrainPhys = new BSTerrainHeightmap(m_physicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords); heightMap, minCoords, maxCoords);
break; break;
case (int)BSTerrainPhys.TerrainImplementation.Mesh: case (int)BSTerrainPhys.TerrainImplementation.Mesh:
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id, newTerrainPhys = new BSTerrainMesh(m_physicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords); heightMap, minCoords, maxCoords);
break; break;
default: default:
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", m_physicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
LogHeader, LogHeader,
(int)BSParam.TerrainImplementation, (int)BSParam.TerrainImplementation,
BSParam.TerrainImplementation, BSParam.TerrainImplementation,
PhysicsScene.RegionName, terrainRegionBase); m_physicsScene.RegionName, terrainRegionBase);
break; break;
} }
return newTerrainPhys; return newTerrainPhys;
@ -337,6 +351,53 @@ public sealed class BSTerrainManager : IDisposable
return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ); return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
} }
// Return a new position that is over known terrain if the position is outside our terrain.
public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos)
{
Vector3 ret = pPos;
// First, base addresses are never negative so correct for that possible problem.
if (ret.X < 0f || ret.Y < 0f)
{
ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}",
BSScene.DetailLogZero, pPos, ret);
}
// Can't do this function if we don't know about any terrain.
if (m_terrains.Count == 0)
return ret;
int loopPrevention = 10;
Vector3 terrainBaseXYZ;
BSTerrainPhys physTerrain;
while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ))
{
// The passed position is not within a known terrain area.
// NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region.
// Must be off the top of a region. Find an adjacent region to move into.
Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ);
ret.X = Math.Min(ret.X, adjacentTerrainBase.X + (ret.X % DefaultRegionSize.X));
ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + (ret.X % DefaultRegionSize.Y));
DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}",
BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret);
if (loopPrevention-- < 0f)
{
// The 'while' is a little dangerous so this prevents looping forever if the
// mapping of the terrains ever gets messed up (like nothing at <0,0>) or
// the list of terrains is in transition.
DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,suppressingFindAdjacentRegionLoop", BSScene.DetailLogZero);
break;
}
}
return ret;
}
// Given an X and Y, find the height of the terrain. // Given an X and Y, find the height of the terrain.
// Since we could be handling multiple terrains for a mega-region, // Since we could be handling multiple terrains for a mega-region,
// the base of the region is calcuated assuming all regions are // the base of the region is calcuated assuming all regions are
@ -368,8 +429,8 @@ public sealed class BSTerrainManager : IDisposable
} }
else else
{ {
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
LogHeader, PhysicsScene.RegionName, tX, tY); LogHeader, m_physicsScene.RegionName, tX, tY);
DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}", DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
BSScene.DetailLogZero, pos, terrainBaseXYZ); BSScene.DetailLogZero, pos, terrainBaseXYZ);
} }
@ -390,8 +451,8 @@ public sealed class BSTerrainManager : IDisposable
} }
else else
{ {
PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", m_physicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret); LogHeader, m_physicsScene.RegionName, pos, terrainBaseXYZ, ret);
} }
return ret; return ret;
} }
@ -400,18 +461,69 @@ public sealed class BSTerrainManager : IDisposable
// the descriptor class and the 'base' fo the addresses therein. // the descriptor class and the 'base' fo the addresses therein.
private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase) private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
{ {
int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; bool ret = false;
int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f); Vector3 terrainBaseXYZ = Vector3.Zero;
if (pos.X < 0f || pos.Y < 0f)
{
// We don't handle negative addresses so just make up a base that will not be found.
terrainBaseXYZ = new Vector3(-DefaultRegionSize.X, -DefaultRegionSize.Y, 0f);
}
else
{
int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
}
BSTerrainPhys physTerrain = null; BSTerrainPhys physTerrain = null;
lock (m_terrains) lock (m_terrains)
{ {
m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain); ret = m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
} }
outTerrainBase = terrainBaseXYZ; outTerrainBase = terrainBaseXYZ;
outPhysTerrain = physTerrain; outPhysTerrain = physTerrain;
return (physTerrain != null); return ret;
}
// Given a terrain base, return a terrain base for a terrain that is closer to <0,0> than
// this one. Usually used to return an out of bounds object to a known place.
private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase)
{
Vector3 ret = pTerrainBase;
// Can't do this function if we don't know about any terrain.
if (m_terrains.Count == 0)
return ret;
// Just some sanity
ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
ret.Z = 0f;
lock (m_terrains)
{
// Once down to the <0,0> region, we have to be done.
while (ret.X > 0f || ret.Y > 0f)
{
if (ret.X > 0f)
{
ret.X = Math.Max(0f, ret.X - DefaultRegionSize.X);
DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingX,terrainBase={1}", BSScene.DetailLogZero, ret);
if (m_terrains.ContainsKey(ret))
break;
}
if (ret.Y > 0f)
{
ret.Y = Math.Max(0f, ret.Y - DefaultRegionSize.Y);
DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingY,terrainBase={1}", BSScene.DetailLogZero, ret);
if (m_terrains.ContainsKey(ret))
break;
}
}
}
return ret;
} }
// Although no one seems to check this, I do support combining. // Although no one seems to check this, I do support combining.
@ -452,7 +564,7 @@ public sealed class BSTerrainManager : IDisposable
private void DetailLog(string msg, params Object[] args) private void DetailLog(string msg, params Object[] args)
{ {
PhysicsScene.PhysicsLogging.Write(msg, args); m_physicsScene.PhysicsLogging.Write(msg, args);
} }
} }
} }

View File

@ -51,7 +51,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
BulletShape m_terrainShape; BulletShape m_terrainShape;
BulletBody m_terrainBody; BulletBody m_terrainBody;
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
: base(physicsScene, regionBase, id) : base(physicsScene, regionBase, id)
{ {
} }
@ -62,7 +62,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
} }
// Create terrain mesh from a heightmap. // Create terrain mesh from a heightmap.
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
Vector3 minCoords, Vector3 maxCoords) Vector3 minCoords, Vector3 maxCoords)
: base(physicsScene, regionBase, id) : base(physicsScene, regionBase, id)
{ {
@ -76,27 +76,43 @@ public sealed class BSTerrainMesh : BSTerrainPhys
m_sizeX = (int)(maxCoords.X - minCoords.X); m_sizeX = (int)(maxCoords.X - minCoords.X);
m_sizeY = (int)(maxCoords.Y - minCoords.Y); m_sizeY = (int)(maxCoords.Y - minCoords.Y);
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap, bool meshCreationSuccess = false;
m_sizeX, m_sizeY, if (BSParam.TerrainMeshMagnification == 1)
(float)m_sizeX, (float)m_sizeY, {
Vector3.Zero, 1.0f, // If a magnification of one, use the old routine that is tried and true.
out indicesCount, out indices, out verticesCount, out vertices)) meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(m_physicsScene,
initialMap, m_sizeX, m_sizeY, // input size
Vector3.Zero, // base for mesh
out indicesCount, out indices, out verticesCount, out vertices);
}
else
{
// Other magnifications use the newer routine
meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene,
initialMap, m_sizeX, m_sizeY, // input size
BSParam.TerrainMeshMagnification,
physicsScene.TerrainManager.DefaultRegionSize,
Vector3.Zero, // base for mesh
out indicesCount, out indices, out verticesCount, out vertices);
}
if (!meshCreationSuccess)
{ {
// DISASTER!! // DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID); m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID);
PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); m_physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future. // Something is very messed up and a crash is in our future.
return; return;
} }
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
ID, indicesCount, indices.Length, verticesCount, vertices.Length);
m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices); m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}",
BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length);
m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices);
if (!m_terrainShape.HasPhysicalShape) if (!m_terrainShape.HasPhysicalShape)
{ {
// DISASTER!! // DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID);
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future. // Something is very messed up and a crash is in our future.
return; return;
} }
@ -104,44 +120,54 @@ public sealed class BSTerrainMesh : BSTerrainPhys
Vector3 pos = regionBase; Vector3 pos = regionBase;
Quaternion rot = Quaternion.Identity; Quaternion rot = Quaternion.Identity;
m_terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); m_terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot);
if (!m_terrainBody.HasPhysicalBody) if (!m_terrainBody.HasPhysicalBody)
{ {
// DISASTER!! // DISASTER!!
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future. // Something is very messed up and a crash is in our future.
return; return;
} }
physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin);
// Set current terrain attributes // Set current terrain attributes
PhysicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction);
PhysicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction);
PhysicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution);
PhysicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold);
m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT);
// Static objects are not very massive. // Static objects are not very massive.
PhysicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); m_physicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero);
// Put the new terrain to the world of physical objects // Put the new terrain to the world of physical objects
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_terrainBody); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_terrainBody);
// Redo its bounding box now that it is in the world // Redo its bounding box now that it is in the world
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_terrainBody); m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_terrainBody);
m_terrainBody.collisionType = CollisionType.Terrain; m_terrainBody.collisionType = CollisionType.Terrain;
m_terrainBody.ApplyCollisionMask(PhysicsScene); m_terrainBody.ApplyCollisionMask(m_physicsScene);
if (BSParam.UseSingleSidedMeshes)
{
m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id);
m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
}
// Make it so the terrain will not move or be considered for movement. // Make it so the terrain will not move or be considered for movement.
PhysicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); m_physicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION);
} }
public override void Dispose() public override void Dispose()
{ {
if (m_terrainBody.HasPhysicalBody) if (m_terrainBody.HasPhysicalBody)
{ {
PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_terrainBody); m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_terrainBody);
// Frees both the body and the shape. // Frees both the body and the shape.
PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_terrainBody); m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_terrainBody);
m_terrainBody.Clear();
m_terrainShape.Clear();
} }
} }
@ -159,7 +185,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
catch catch
{ {
// Sometimes they give us wonky values of X and Y. Give a warning and return something. // Sometimes they give us wonky values of X and Y. Give a warning and return something.
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
LogHeader, TerrainBase, pos); LogHeader, TerrainBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
} }
@ -169,17 +195,14 @@ public sealed class BSTerrainMesh : BSTerrainPhys
// The passed position is relative to the base of the region. // The passed position is relative to the base of the region.
public override float GetWaterLevelAtXYZ(Vector3 pos) public override float GetWaterLevelAtXYZ(Vector3 pos)
{ {
return PhysicsScene.SimpleWaterLevel; return m_physicsScene.SimpleWaterLevel;
} }
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
// Return 'true' if successfully created. // Return 'true' if successfully created.
public static bool ConvertHeightmapToMesh( public static bool ConvertHeightmapToMesh( BSScene physicsScene,
BSScene physicsScene,
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
float extentX, float extentY, // zero based range for output vertices
Vector3 extentBase, // base to be added to all vertices Vector3 extentBase, // base to be added to all vertices
float magnification, // number of vertices to create between heightMap coords
out int indicesCountO, out int[] indicesO, out int indicesCountO, out int[] indicesO,
out int verticesCountO, out float[] verticesO) out int verticesCountO, out float[] verticesO)
{ {
@ -200,16 +223,15 @@ public sealed class BSTerrainMesh : BSTerrainPhys
// of the heightmap. // of the heightmap.
try try
{ {
// One vertice per heightmap value plus the vertices off the top and bottom edge. // One vertice per heightmap value plus the vertices off the side and bottom edge.
int totalVertices = (sizeX + 1) * (sizeY + 1); int totalVertices = (sizeX + 1) * (sizeY + 1);
vertices = new float[totalVertices * 3]; vertices = new float[totalVertices * 3];
int totalIndices = sizeX * sizeY * 6; int totalIndices = sizeX * sizeY * 6;
indices = new int[totalIndices]; indices = new int[totalIndices];
float magX = (float)sizeX / extentX; if (physicsScene != null)
float magY = (float)sizeY / extentY; physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3}",
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", BSScene.DetailLogZero, totalVertices, totalIndices, extentBase);
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
float minHeight = float.MaxValue; float minHeight = float.MaxValue;
// Note that sizeX+1 vertices are created since there is land between this and the next region. // Note that sizeX+1 vertices are created since there is land between this and the next region.
for (int yy = 0; yy <= sizeY; yy++) for (int yy = 0; yy <= sizeY; yy++)
@ -222,8 +244,8 @@ public sealed class BSTerrainMesh : BSTerrainPhys
if (xx == sizeX) offset -= 1; if (xx == sizeX) offset -= 1;
float height = heightMap[offset]; float height = heightMap[offset];
minHeight = Math.Min(minHeight, height); minHeight = Math.Min(minHeight, height);
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X; vertices[verticesCount + 0] = (float)xx + extentBase.X;
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y; vertices[verticesCount + 1] = (float)yy + extentBase.Y;
vertices[verticesCount + 2] = height + extentBase.Z; vertices[verticesCount + 2] = height + extentBase.Z;
verticesCount += 3; verticesCount += 3;
} }
@ -250,7 +272,161 @@ public sealed class BSTerrainMesh : BSTerrainPhys
} }
catch (Exception e) catch (Exception e)
{ {
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", if (physicsScene != null)
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
LogHeader, physicsScene.RegionName, extentBase, e);
}
indicesCountO = indicesCount;
indicesO = indices;
verticesCountO = verticesCount;
verticesO = vertices;
return ret;
}
private class HeightMapGetter
{
private float[] m_heightMap;
private int m_sizeX;
private int m_sizeY;
public HeightMapGetter(float[] pHeightMap, int pSizeX, int pSizeY)
{
m_heightMap = pHeightMap;
m_sizeX = pSizeX;
m_sizeY = pSizeY;
}
// The heightmap is extended as an infinite plane at the last height
public float GetHeight(int xx, int yy)
{
int offset = 0;
// Extend the height with the height from the last row or column
if (yy >= m_sizeY)
if (xx >= m_sizeX)
offset = (m_sizeY - 1) * m_sizeX + (m_sizeX - 1);
else
offset = (m_sizeY - 1) * m_sizeX + xx;
else
if (xx >= m_sizeX)
offset = yy * m_sizeX + (m_sizeX - 1);
else
offset = yy * m_sizeX + xx;
return m_heightMap[offset];
}
}
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
// Version that handles magnification.
// Return 'true' if successfully created.
public static bool ConvertHeightmapToMesh2( BSScene physicsScene,
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
int magnification, // number of vertices per heighmap step
Vector3 extent, // dimensions of the output mesh
Vector3 extentBase, // base to be added to all vertices
out int indicesCountO, out int[] indicesO,
out int verticesCountO, out float[] verticesO)
{
bool ret = false;
int indicesCount = 0;
int verticesCount = 0;
int[] indices = new int[0];
float[] vertices = new float[0];
HeightMapGetter hmap = new HeightMapGetter(heightMap, sizeX, sizeY);
// The vertices dimension of the output mesh
int meshX = sizeX * magnification;
int meshY = sizeY * magnification;
// The output size of one mesh step
float meshXStep = extent.X / meshX;
float meshYStep = extent.Y / meshY;
// Create an array of vertices that is meshX+1 by meshY+1 (note the loop
// from zero to <= meshX). The triangle indices are then generated as two triangles
// per heightmap point. There are meshX by meshY of these squares. The extra row and
// column of vertices are used to complete the triangles of the last row and column
// of the heightmap.
try
{
// Vertices for the output heightmap plus one on the side and bottom to complete triangles
int totalVertices = (meshX + 1) * (meshY + 1);
vertices = new float[totalVertices * 3];
int totalIndices = meshX * meshY * 6;
indices = new int[totalIndices];
if (physicsScene != null)
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,inSize={1},outSize={2},totVert={3},totInd={4},extentBase={5}",
BSScene.DetailLogZero, new Vector2(sizeX, sizeY), new Vector2(meshX, meshY),
totalVertices, totalIndices, extentBase);
float minHeight = float.MaxValue;
// Note that sizeX+1 vertices are created since there is land between this and the next region.
// Loop through the output vertices and compute the mediun height in between the input vertices
for (int yy = 0; yy <= meshY; yy++)
{
for (int xx = 0; xx <= meshX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
{
float offsetY = (float)yy * (float)sizeY / (float)meshY; // The Y that is closest to the mesh point
int stepY = (int)offsetY;
float fractionalY = offsetY - (float)stepY;
float offsetX = (float)xx * (float)sizeX / (float)meshX; // The X that is closest to the mesh point
int stepX = (int)offsetX;
float fractionalX = offsetX - (float)stepX;
// physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,xx={1},yy={2},offX={3},stepX={4},fractX={5},offY={6},stepY={7},fractY={8}",
// BSScene.DetailLogZero, xx, yy, offsetX, stepX, fractionalX, offsetY, stepY, fractionalY);
// get the four corners of the heightmap square the mesh point is in
float heightUL = hmap.GetHeight(stepX , stepY );
float heightUR = hmap.GetHeight(stepX + 1, stepY );
float heightLL = hmap.GetHeight(stepX , stepY + 1);
float heightLR = hmap.GetHeight(stepX + 1, stepY + 1);
// bilinear interplolation
float height = heightUL * (1 - fractionalX) * (1 - fractionalY)
+ heightUR * fractionalX * (1 - fractionalY)
+ heightLL * (1 - fractionalX) * fractionalY
+ heightLR * fractionalX * fractionalY;
// physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,heightUL={1},heightUR={2},heightLL={3},heightLR={4},heightMap={5}",
// BSScene.DetailLogZero, heightUL, heightUR, heightLL, heightLR, height);
minHeight = Math.Min(minHeight, height);
vertices[verticesCount + 0] = (float)xx * meshXStep + extentBase.X;
vertices[verticesCount + 1] = (float)yy * meshYStep + extentBase.Y;
vertices[verticesCount + 2] = height + extentBase.Z;
verticesCount += 3;
}
}
// The number of vertices generated
verticesCount /= 3;
// Loop through all the heightmap squares and create indices for the two triangles for that square
for (int yy = 0; yy < meshY; yy++)
{
for (int xx = 0; xx < meshX; xx++)
{
int offset = yy * (meshX + 1) + xx;
// Each vertices is presumed to be the upper left corner of a box of two triangles
indices[indicesCount + 0] = offset;
indices[indicesCount + 1] = offset + 1;
indices[indicesCount + 2] = offset + meshX + 1; // accounting for the extra column
indices[indicesCount + 3] = offset + 1;
indices[indicesCount + 4] = offset + meshX + 2;
indices[indicesCount + 5] = offset + meshX + 1;
indicesCount += 6;
}
}
ret = true;
}
catch (Exception e)
{
if (physicsScene != null)
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
LogHeader, physicsScene.RegionName, extentBase, e); LogHeader, physicsScene.RegionName, extentBase, e);
} }

View File

@ -104,18 +104,20 @@ public class BulletShape
{ {
public BulletShape() public BulletShape()
{ {
type = BSPhysicsShapeType.SHAPE_UNKNOWN; shapeType = BSPhysicsShapeType.SHAPE_UNKNOWN;
shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
isNativeShape = false; isNativeShape = false;
} }
public BSPhysicsShapeType type; public BSPhysicsShapeType shapeType;
public System.UInt64 shapeKey; public System.UInt64 shapeKey;
public bool isNativeShape; public bool isNativeShape;
public virtual void Clear() { } public virtual void Clear() { }
public virtual bool HasPhysicalShape { get { return false; } } public virtual bool HasPhysicalShape { get { return false; } }
// Make another reference to this physical object. // Make another reference to this physical object.
public virtual BulletShape Clone() { return new BulletShape(); } public virtual BulletShape Clone() { return new BulletShape(); }
// Return 'true' if this and other refer to the same physical object // Return 'true' if this and other refer to the same physical object
public virtual bool ReferenceSame(BulletShape xx) { return false; } public virtual bool ReferenceSame(BulletShape xx) { return false; }
@ -131,7 +133,7 @@ public class BulletShape
buff.Append("<p="); buff.Append("<p=");
buff.Append(AddrString); buff.Append(AddrString);
buff.Append(",s="); buff.Append(",s=");
buff.Append(type.ToString()); buff.Append(shapeType.ToString());
buff.Append(",k="); buff.Append(",k=");
buff.Append(shapeKey.ToString("X")); buff.Append(shapeKey.ToString("X"));
buff.Append(",n="); buff.Append(",n=");
@ -215,45 +217,49 @@ public static class BulletSimData
{ {
// Map of collisionTypes to flags for collision groups and masks. // Map of collisionTypes to flags for collision groups and masks.
// An object's 'group' is the collison groups this object belongs to
// An object's 'filter' is the groups another object has to belong to in order to collide with me
// A collision happens if ((obj1.group & obj2.filter) != 0) || ((obj2.group & obj1.filter) != 0)
//
// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code // As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
// but, instead, use references to this dictionary. Finding and debugging // but, instead, use references to this dictionary. Finding and debugging
// collision flag problems will be made easier. // collision flag problems will be made easier.
public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
= new Dictionary<CollisionType, CollisionTypeFilterGroup>() = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
{ {
{ CollisionType.Avatar, { CollisionType.Avatar,
new CollisionTypeFilterGroup(CollisionType.Avatar, new CollisionTypeFilterGroup(CollisionType.Avatar,
(uint)CollisionFilterGroups.BCharacterGroup, (uint)CollisionFilterGroups.BCharacterGroup,
(uint)CollisionFilterGroups.BAllGroup) (uint)CollisionFilterGroups.BAllGroup)
}, },
{ CollisionType.Groundplane, { CollisionType.Groundplane,
new CollisionTypeFilterGroup(CollisionType.Groundplane, new CollisionTypeFilterGroup(CollisionType.Groundplane,
(uint)CollisionFilterGroups.BGroundPlaneGroup, (uint)CollisionFilterGroups.BGroundPlaneGroup,
(uint)CollisionFilterGroups.BAllGroup) (uint)CollisionFilterGroups.BAllGroup)
}, },
{ CollisionType.Terrain, { CollisionType.Terrain,
new CollisionTypeFilterGroup(CollisionType.Terrain, new CollisionTypeFilterGroup(CollisionType.Terrain,
(uint)CollisionFilterGroups.BTerrainGroup, (uint)CollisionFilterGroups.BTerrainGroup,
(uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup)) (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
}, },
{ CollisionType.Static, { CollisionType.Static,
new CollisionTypeFilterGroup(CollisionType.Static, new CollisionTypeFilterGroup(CollisionType.Static,
(uint)CollisionFilterGroups.BStaticGroup, (uint)CollisionFilterGroups.BStaticGroup,
(uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
}, },
{ CollisionType.Dynamic, { CollisionType.Dynamic,
new CollisionTypeFilterGroup(CollisionType.Dynamic, new CollisionTypeFilterGroup(CollisionType.Dynamic,
(uint)CollisionFilterGroups.BSolidGroup, (uint)CollisionFilterGroups.BSolidGroup,
(uint)(CollisionFilterGroups.BAllGroup)) (uint)(CollisionFilterGroups.BAllGroup))
}, },
{ CollisionType.VolumeDetect, { CollisionType.VolumeDetect,
new CollisionTypeFilterGroup(CollisionType.VolumeDetect, new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
(uint)CollisionFilterGroups.BSensorTrigger, (uint)CollisionFilterGroups.BSensorTrigger,
(uint)(~CollisionFilterGroups.BSensorTrigger)) (uint)(~CollisionFilterGroups.BSensorTrigger))
}, },
{ CollisionType.LinksetChild, { CollisionType.LinksetChild,
new CollisionTypeFilterGroup(CollisionType.LinksetChild, new CollisionTypeFilterGroup(CollisionType.LinksetChild,
(uint)CollisionFilterGroups.BLinksetChildGroup, (uint)CollisionFilterGroups.BLinksetChildGroup,
(uint)(CollisionFilterGroups.BNoneGroup)) (uint)(CollisionFilterGroups.BNoneGroup))
// (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) // (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
}, },

View File

@ -1,68 +1,113 @@
PROBLEMS TO LOOK INTO
=================================================
Nebadon vehicle ride, get up, ride again. Second time vehicle does not act correctly.
Have to rez new vehicle and delete the old to fix situation.
Hitting RESET on Nebadon's vehicle while riding causes vehicle to get into odd
position state where it will not settle onto ground properly, etc
Two of Nebadon vehicles in a sim max the CPU. This is new.
A sitting, active vehicle bobs up and down a small amount.
CURRENT PRIORITIES CURRENT PRIORITIES
================================================= =================================================
Redo BulletSimAPI to allow native C# implementation of Bullet option. Use the HACD convex hull routine in Bullet rather than the C# version.
Avatar movement Speed up hullifying large meshes.
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle
walking up stairs is not calibrated correctly (stairs out of Kepler cabin)
avatar capsule rotation completed
llMoveToTarget
Enable vehicle border crossings (at least as poorly as ODE) Enable vehicle border crossings (at least as poorly as ODE)
Terrain skirts Terrain skirts
Avatar created in previous region and not new region when crossing border Avatar created in previous region and not new region when crossing border
Vehicle recreated in new sim at small Z value (offset from root value?) (DONE) Vehicle recreated in new sim at small Z value (offset from root value?) (DONE)
Vehicle movement on terrain smoothness Deleting a linkset while standing on the root will leave the physical shape of the root behind.
Not sure if it is because standing on it. Done with large prim linksets.
Linkset child rotations.
Nebadon spiral tube has middle sections which are rotated wrong.
Select linked spiral tube. Delink and note where the middle section ends up.
Refarb compound linkset creation to create a pseudo-root for center-of-mass
Let children change their shape to physical indendently and just add shapes to compound
Vehicle angular vertical attraction
vehicle angular banking
Center-of-gravity
Vehicle angular deflection
Preferred orientation angular correction fix
when should angular and linear motor targets be zeroed? when selected?
Need a vehicle.clear()? Or an 'else' in prestep if not physical.
Teravus llMoveToTarget script debug
Mixing of hover, buoyancy/gravity, moveToTarget, into one force
Setting hover height to zero disables hover even if hover flags are on (from SL wiki)
limitMotorUp calibration (more down?)
llRotLookAt
llLookAt
Avatars walking up stairs (HALF DONE)
Avatar movement
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE)
walking up stairs is not calibrated correctly (stairs out of Kepler cabin) (DONE)
avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution)
Vehicle script tuning/debugging Vehicle script tuning/debugging
Avanti speed script Avanti speed script
Weapon shooter script Weapon shooter script
limitMotorUp calibration (more down?) Move material definitions (friction, ...) into simulator.
Boats float low in the water
Add material densities to the material types. Add material densities to the material types.
One sided meshes? Should terrain be built into a closed shape?
CRASHES When meshes get partially wedged into the terrain, they cannot push themselves out.
================================================= It is possible that Bullet processes collisions whether entering or leaving a mesh.
20121129.1411: editting/moving phys object across region boundries causes crash Ref: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4869
getPos-> btRigidBody::upcast -> getBodyType -> BOOM
20121128.1600: mesh object not rezzing (no physics mesh).
Causes many errors. Doesn't stop after first error with box shape.
Eventually crashes when deleting the object.
20121206.1434: rez Sam-pan into OSGrid BulletSim11 region
Immediate simulator crash. Mono does not output any stacktrace and
log just stops after reporting taint-time linking of the linkset.
VEHICLES TODO LIST: VEHICLES TODO LIST:
================================================= =================================================
Angular motor direction is global coordinates rather than local coordinates LINEAR_MOTOR_DIRECTION values should be clamped to reasonable numbers.
What are the limits in SL?
Same for other velocity settings.
UBit improvements to remove rubber-banding of avatars sitting on vehicle child prims:
https://github.com/UbitUmarov/Ubit-opensim
Border crossing with linked vehicle causes crash Border crossing with linked vehicle causes crash
20121129.1411: editting/moving phys object across region boundries causes crash
getPos-> btRigidBody::upcast -> getBodyType -> BOOM
Vehicles (Move smoothly) Vehicles (Move smoothly)
Add vehicle collisions so IsColliding is properly reported.
Needed for banking, limitMotorUp, movementLimiting, ...
VehicleAddForce is not scaled by the simulation step but it is only
applied for one step. Should it be scaled?
Some vehicles should not be able to turn if no speed or off ground. Some vehicles should not be able to turn if no speed or off ground.
What to do if vehicle and prim buoyancy differ?
Cannot edit/move a vehicle being ridden: it jumps back to the origional position. Cannot edit/move a vehicle being ridden: it jumps back to the origional position.
Neb car jiggling left and right Neb car jiggling left and right
Happens on terrain and any other mesh object. Flat cubes are much smoother. Happens on terrain and any other mesh object. Flat cubes are much smoother.
This has been reduced but not eliminated. This has been reduced but not eliminated.
Implement referenceFrame for all the motion routines. Implement referenceFrame for all the motion routines.
Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE. For limitMotorUp, use raycast down to find if vehicle is in the air.
Verify that angular motion specified around Z moves in the vehicle coordinates.
Verify llGetVel() is returning a smooth and good value for vehicle movement. Verify llGetVel() is returning a smooth and good value for vehicle movement.
llGetVel() should return the root's velocity if requested in a child prim. llGetVel() should return the root's velocity if requested in a child prim.
Implement function efficiency for lineaar and angular motion. Implement function efficiency for lineaar and angular motion.
After getting off a vehicle, the root prim is phantom (can be walked through) After getting off a vehicle, the root prim is phantom (can be walked through)
Need to force a position update for the root prim after compound shape destruction Need to force a position update for the root prim after compound shape destruction
Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint) Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint)
For limitMotorUp, use raycast down to find if vehicle is in the air.
Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties(). Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties().
A kludge that isn't fixing the real problem of Bullet adding extra motion. A kludge that isn't fixing the real problem of Bullet adding extra motion.
Incorporate inter-relationship of angular corrections. For instance, angularDeflection Incorporate inter-relationship of angular corrections. For instance, angularDeflection
and angularMotorUp will compute same X or Y correction. When added together and angularMotorUp will compute same X or Y correction. When added together
creates over-correction and over-shoot and wabbling. creates over-correction and over-shoot and wabbling.
Vehicle attributes are not restored when a vehicle is rezzed on region creation
Create vehicle, setup vehicle properties, restart region, vehicle is not reinitialized.
BULLETSIM TODO LIST: GENERAL TODO LIST:
================================================= =================================================
Explore btGImpactMeshShape as alternative to convex hulls for simplified physical objects.
Regular triangle meshes don't do physical collisions.
Resitution of a prim works on another prim but not on terrain.
The dropped prim doesn't bounce properly on the terrain.
Add a sanity check for PIDTarget location.
Level-of-detail for mesh creation. Prims with circular interiors require lod of 32.
Is much saved with lower LODs? At the moment, all set to 32.
Collisions are inconsistant: arrows are supposed to hit and report collision. Often don't.
If arrow show at prim, collision reported about 1/3 of time. If collision reported,
both arrow and prim report it. The arrow bounces off the prim 9 out of 10 times.
Shooting 5m sphere "arrows" at 60m/s.
llMoveToTarget objects are not effected by gravity until target is removed.
Compute CCD parameters based on body size
Can solver iterations be changed per body/shape? Can be for constraints but what
about regular vehicles?
Implement llSetPhysicalMaterial.
extend it with Center-of-mass, rolling friction, density
Implement llSetForceAndTorque.
Change BSPrim.moveToTarget to used forces rather than changing position
Changing position allows one to move through walls
Implement an avatar mesh shape. The Bullet capsule is way too limited. Implement an avatar mesh shape. The Bullet capsule is way too limited.
Consider just hand creating a vertex/index array in a new BSShapeAvatar. Consider just hand creating a vertex/index array in a new BSShapeAvatar.
Verify/fix phantom, volume-detect objects do not fall to infinity. Should stop at terrain.
Revisit CollisionMargin. Builders notice the 0.04 spacing between prims. Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
Duplicating a physical prim causes old prim to jump away Duplicating a physical prim causes old prim to jump away
Dup a phys prim and the original become unselected and thus interacts w/ selected prim. Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
@ -86,6 +131,8 @@ setForce should set a constant force. Different than AddImpulse.
Implement raycast. Implement raycast.
Implement ShapeCollection.Dispose() Implement ShapeCollection.Dispose()
Implement water as a plain so raycasting and collisions can happen with same. Implement water as a plain so raycasting and collisions can happen with same.
Add collision penetration return
Add field passed back by BulletSim.dll and fill with info in ManifoldConstact.GetDistance()
Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE
Also osGetPhysicsEngineVerion() maybe. Also osGetPhysicsEngineVerion() maybe.
Linkset.Position and Linkset.Orientation requre rewrite to properly return Linkset.Position and Linkset.Orientation requre rewrite to properly return
@ -107,6 +154,12 @@ Physical and phantom will drop through the terrain
LINKSETS LINKSETS
====================================================== ======================================================
Child prims do not report collisions
Allow children of a linkset to be phantom:
http://opensim-dev.2196679.n2.nabble.com/Setting-a-single-child-prim-to-Phantom-tp7578513.html
Add OS_STATUS_PHANTOM_PRIM to llSetLinkPrimitaveParamsFast.
Editing a child of a linkset causes the child to go phantom
Move a child prim once when it is physical and can never move it again without it going phantom
Offset the center of the linkset to be the geometric center of all the prims Offset the center of the linkset to be the geometric center of all the prims
Not quite the same as the center-of-gravity Not quite the same as the center-of-gravity
Linksets should allow collisions to individual children Linksets should allow collisions to individual children
@ -117,11 +170,9 @@ LinksetCompound: when one of the children changes orientation (like tires
Verify/think through scripts in children of linksets. What do they reference Verify/think through scripts in children of linksets. What do they reference
and return when getting position, velocity, ... and return when getting position, velocity, ...
Confirm constraint linksets still work after making all the changes for compound linksets. Confirm constraint linksets still work after making all the changes for compound linksets.
Use PostTaint callback to do rebuilds for constraint linksets to reduce rebuilding
Add 'changed' flag or similar to reduce the number of times a linkset is rebuilt. Add 'changed' flag or similar to reduce the number of times a linkset is rebuilt.
For compound linksets, add ability to remove or reposition individual child shapes. For compound linksets, add ability to remove or reposition individual child shapes.
Disable activity of passive linkset children.
Since the linkset is a compound object, the old prims are left lying
around and need to be phantomized so they don't collide, ...
Speed up creation of large physical linksets Speed up creation of large physical linksets
For instance, sitting in Neb's car (130 prims) takes several seconds to become physical. For instance, sitting in Neb's car (130 prims) takes several seconds to become physical.
REALLY bad for very large physical linksets (freezes the sim for many seconds). REALLY bad for very large physical linksets (freezes the sim for many seconds).
@ -131,25 +182,28 @@ Eliminate collisions between objects in a linkset. (LinksetConstraint)
MORE MORE
====================================================== ======================================================
Test avatar walking up stairs. How does compare with SL. Compute avatar size and scale correctly. Now it is a bit off from the capsule size.
Radius of the capsule affects ability to climb edges. Create tests for different interface components
Have test objects/scripts measure themselves and turn color if correct/bad
Test functions in SL and calibrate correctness there
Create auto rezzer and tracker to run through the tests
Do we need to do convex hulls all the time? Can complex meshes be left meshes?
There is some problem with meshes and collisions
Hulls are not as detailed as meshes. Hulled vehicles insides are different shape.
Debounce avatar contact so legs don't keep folding up when standing. Debounce avatar contact so legs don't keep folding up when standing.
Implement LSL physics controls. Like STATUS_ROTATE_X. Implement LSL physics controls. Like STATUS_ROTATE_X.
Add border extensions to terrain to help region crossings and objects leaving region. Add border extensions to terrain to help region crossings and objects leaving region.
Use a different capsule shape for avatar when sitting Use a different capsule shape for avatar when sitting
LL uses a pyrimidal shape scaled by the avatar's bounding box LL uses a pyrimidal shape scaled by the avatar's bounding box
http://wiki.secondlife.com/wiki/File:Avmeshforms.png http://wiki.secondlife.com/wiki/File:Avmeshforms.png
Performance test with lots of avatars. Can BulletSim support a thousand? Performance test with lots of avatars. Can BulletSim support a thousand?
Optimize collisions in C++: only send up to the object subscribed to collisions. Optimize collisions in C++: only send up to the object subscribed to collisions.
Use collision subscription and remove the collsion(A,B) and collision(B,A) Use collision subscription and remove the collsion(A,B) and collision(B,A)
Check whether SimMotionState needs large if statement (see TODO). Check whether SimMotionState needs large if statement (see TODO).
Implement 'top colliders' info. Implement 'top colliders' info.
Avatar jump Avatar jump
Performance measurement and changes to make quicker. Performance measurement and changes to make quicker.
Implement detailed physics stats (GetStats()). Implement detailed physics stats (GetStats()).
Measure performance improvement from hulls Measure performance improvement from hulls
Test not using ghost objects for volume detect implementation. Test not using ghost objects for volume detect implementation.
Performance of closures and delegates for taint processing Performance of closures and delegates for taint processing
@ -157,9 +211,7 @@ Performance of closures and delegates for taint processing
Is any slowdown introduced by the existing implementation significant? Is any slowdown introduced by the existing implementation significant?
Is there are more efficient method of implementing pre and post step actions? Is there are more efficient method of implementing pre and post step actions?
See http://www.codeproject.com/Articles/29922/Weak-Events-in-C See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
Physics Arena central pyramid: why is one side permiable? Physics Arena central pyramid: why is one side permiable?
In SL, perfect spheres don't seem to have rolling friction. Add special case. In SL, perfect spheres don't seem to have rolling friction. Add special case.
Enforce physical parameter min/max: Enforce physical parameter min/max:
Gravity: [-1, 28] Gravity: [-1, 28]
@ -168,9 +220,12 @@ Enforce physical parameter min/max:
Restitution [0, 1] Restitution [0, 1]
http://wiki.secondlife.com/wiki/Physics_Material_Settings_test http://wiki.secondlife.com/wiki/Physics_Material_Settings_test
Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html
Keep avatar scaling correct. http://pennycow.blogspot.fr/2011/07/matter-of-scale.html
INTERNAL IMPROVEMENT/CLEANUP INTERNAL IMPROVEMENT/CLEANUP
================================================= =================================================
Can the 'inTaintTime' flag be cleaned up and used? For instance, a call to
BSScene.TaintedObject() could immediately execute the callback if already in taint time.
Create the physical wrapper classes (BulletBody, BulletShape) by methods on Create the physical wrapper classes (BulletBody, BulletShape) by methods on
BSAPITemplate and make their actual implementation Bullet engine specific. BSAPITemplate and make their actual implementation Bullet engine specific.
For the short term, just call the existing functions in ShapeCollection. For the short term, just call the existing functions in ShapeCollection.
@ -190,22 +245,19 @@ Generalize Dynamics and PID with standardized motors.
Generalize Linkset and vehicles into PropertyManagers Generalize Linkset and vehicles into PropertyManagers
Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies
Potentially add events for shape destruction, etc. Potentially add events for shape destruction, etc.
Complete implemention of preStepActions Better mechanism for resetting linkset set and vehicle parameters when body rebuilt.
Replace vehicle step call with prestep event. BSPrim.CreateGeomAndObject is kludgy with the callbacks, etc.
Is there a need for postStepActions? postStepTaints?
Implement linkset by setting position of children when root updated. (LinksetManual) Implement linkset by setting position of children when root updated. (LinksetManual)
Linkset implementation using manual prim movement. Linkset implementation using manual prim movement.
LinkablePrim class? Would that simplify/centralize the linkset logic? LinkablePrim class? Would that simplify/centralize the linkset logic?
BSScene.UpdateParameterSet() is broken. How to set params on objects? BSScene.UpdateParameterSet() is broken. How to set params on objects?
Remove HeightmapInfo from terrain specification
Since C++ code does not need terrain height, this structure et al are not needed.
Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will
bob at the water level. BSPrim.PositionSanityCheck(). bob at the water level. BSPrim.PositionSanityCheck()
Should taints check for existance or activeness of target? Should taints check for existance or activeness of target?
When destroying linksets/etc, taints can be generated for objects that are When destroying linksets/etc, taints can be generated for objects that are
actually gone when the taint happens. Crashes don't happen because the taint closure actually gone when the taint happens. Crashes don't happen because the taint closure
keeps the object from being freed, but that is just an accident. keeps the object from being freed, but that is just an accident.
Possibly have and 'active' flag that is checked by the taint processor? Possibly have an 'active' flag that is checked by the taint processor?
Parameters for physics logging should be moved from BSScene to BSParam (at least boolean ones) Parameters for physics logging should be moved from BSScene to BSParam (at least boolean ones)
Can some of the physical wrapper classes (BulletBody, BulletWorld, BulletShape) be 'sealed'? Can some of the physical wrapper classes (BulletBody, BulletWorld, BulletShape) be 'sealed'?
There are TOO MANY interfaces from BulletSim core to Bullet itself There are TOO MANY interfaces from BulletSim core to Bullet itself
@ -270,3 +322,43 @@ llSetBuoyancy() (DONE)
(Resolution: Bullet resets object gravity when added to world. Moved set gravity) (Resolution: Bullet resets object gravity when added to world. Moved set gravity)
Avatar density is WAY off. Compare and calibrate with what's in SL. (DONE) Avatar density is WAY off. Compare and calibrate with what's in SL. (DONE)
(Resolution: set default density to 3.5 (from 60) which is closer to SL) (Resolution: set default density to 3.5 (from 60) which is closer to SL)
Redo BulletSimAPI to allow native C# implementation of Bullet option (DONE)
(Resolution: added BSAPITemplate and then interfaces for C++ Bullet and C# BulletXNA
Meshes rendering as bounding boxes (DONE)
(Resolution: Added test for mesh/sculpties in native shapes so it didn't think it was a box)
llMoveToTarget (Resolution: added simple motor to update the position.)
Angular motor direction is global coordinates rather than local coordinates (DONE)
Add vehicle collisions so IsColliding is properly reported. (DONE)
Needed for banking, limitMotorUp, movementLimiting, ...
(Resolution: added CollisionFlags.BS_VEHICLE_COLLISION and code to use it)
VehicleAddForce is not scaled by the simulation step but it is only
applied for one step. Should it be scaled? (DONE)
(Resolution: use force for timed things, Impulse for immediate, non-timed things)
Complete implemention of preStepActions (DONE)
Replace vehicle step call with prestep event.
Is there a need for postStepActions? postStepTaints?
Disable activity of passive linkset children. (DONE)
Since the linkset is a compound object, the old prims are left lying
around and need to be phantomized so they don't collide, ...
Remove HeightmapInfo from terrain specification (DONE)
Since C++ code does not need terrain height, this structure et al are not needed.
Surfboard go wonky when turning (DONE)
Angular motor direction is global coordinates rather than local coordinates?
(Resolution: made angular motor direction correct coordinate system)
Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040 (DONE)
Msg Kayaker on OSGrid when working
(Resolution: LINEAR_DIRECTION is in vehicle coords. Test script does the
same in SL as in OS/BulletSim)
Boats float low in the water (DONE)
Boats floating at proper level (DONE)
When is force introduced by SetForce removed? The prestep action could go forever. (DONE)
(Resolution: setForce registers a prestep action which keeps applying the force)
Child movement in linkset (don't rebuild linkset) (DONE 20130122))
Avatar standing on a moving object should start to move with the object. (DONE 20130125)
Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE.
Verify that angular motion specified around Z moves in the vehicle coordinates.
DONE 20130120: BulletSim properly applies force in vehicle relative coordinates.
Nebadon vehicles turning funny in arena (DONE)
Lock axis (DONE 20130401)
Terrain detail: double terrain mesh detail (DONE)

View File

@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number // Build Number
// Revision // Revision
// //
[assembly: AssemblyVersion("0.7.5.*")] [assembly: AssemblyVersion("0.7.6.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,150 @@
/*
* 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.Linq;
using System.Text;
using NUnit.Framework;
using log4net;
using OpenSim.Framework;
using OpenSim.Region.Physics.BulletSPlugin;
using OpenSim.Region.Physics.Manager;
using OpenSim.Tests.Common;
using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin.Tests
{
[TestFixture]
public class BasicVehicles : OpenSimTestCase
{
// Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1
// Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1
BSScene PhysicsScene { get; set; }
BSPrim TestVehicle { get; set; }
Vector3 TestVehicleInitPosition { get; set; }
float simulationTimeStep = 0.089f;
[TestFixtureSetUp]
public void Init()
{
Dictionary<string, string> engineParams = new Dictionary<string, string>();
PhysicsScene = BulletSimTestsUtil.CreateBasicPhysicsEngine(engineParams);
PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateSphere();
Vector3 pos = new Vector3(100.0f, 100.0f, 0f);
pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2f;
TestVehicleInitPosition = pos;
Vector3 size = new Vector3(1f, 1f, 1f);
pbs.Scale = size;
Quaternion rot = Quaternion.Identity;
bool isPhys = false;
uint localID = 123;
PhysicsScene.AddPrimShape("testPrim", pbs, pos, size, rot, isPhys, localID);
TestVehicle = (BSPrim)PhysicsScene.PhysObjects[localID];
// The actual prim shape creation happens at taint time
PhysicsScene.ProcessTaints();
}
[TestFixtureTearDown]
public void TearDown()
{
if (PhysicsScene != null)
{
// The Dispose() will also free any physical objects in the scene
PhysicsScene.Dispose();
PhysicsScene = null;
}
}
[TestCase(2f, 0.2f, 0.25f, 0.25f, 0.25f)]
[TestCase(2f, 0.2f, -0.25f, 0.25f, 0.25f)]
[TestCase(2f, 0.2f, 0.25f, -0.25f, 0.25f)]
[TestCase(2f, 0.2f, -0.25f, -0.25f, 0.25f)]
// [TestCase(2f, 0.2f, 0.785f, 0.0f, 0.25f) /*, "Leaning 45 degrees to the side" */]
// [TestCase(2f, 0.2f, 1.650f, 0.0f, 0.25f) /*, "Leaning more than 90 degrees to the side" */]
// [TestCase(2f, 0.2f, 2.750f, 0.0f, 0.25f) /*, "Almost upside down, tipped right" */]
// [TestCase(2f, 0.2f,-2.750f, 0.0f, 0.25f) /*, "Almost upside down, tipped left" */]
// [TestCase(2f, 0.2f, 0.0f, 0.785f, 0.25f) /*, "Tipped back 45 degrees" */]
// [TestCase(2f, 0.2f, 0.0f, 1.650f, 0.25f) /*, "Tipped back more than 90 degrees" */]
// [TestCase(2f, 0.2f, 0.0f, 2.750f, 0.25f) /*, "Almost upside down, tipped back" */]
// [TestCase(2f, 0.2f, 0.0f,-2.750f, 0.25f) /*, "Almost upside down, tipped forward" */]
public void AngularVerticalAttraction(float timeScale, float efficiency, float initRoll, float initPitch, float initYaw)
{
// Enough simulation steps to cover the timescale the operation should take
int simSteps = (int)(timeScale / simulationTimeStep) + 1;
// Tip the vehicle
Quaternion initOrientation = Quaternion.CreateFromEulers(initRoll, initPitch, initYaw);
TestVehicle.Orientation = initOrientation;
TestVehicle.Position = TestVehicleInitPosition;
// The vehicle controller is not enabled directly (by setting a vehicle type).
// Instead the appropriate values are set and calls are made just the parts of the
// controller we want to exercise. Stepping the physics engine then applies
// the actions of that one feature.
TestVehicle.VehicleActor.ProcessFloatVehicleParam(Vehicle.VERTICAL_ATTRACTION_EFFICIENCY, efficiency);
TestVehicle.VehicleActor.ProcessFloatVehicleParam(Vehicle.VERTICAL_ATTRACTION_TIMESCALE, timeScale);
TestVehicle.VehicleActor.enableAngularVerticalAttraction = true;
TestVehicle.IsPhysical = true;
PhysicsScene.ProcessTaints();
// Step the simulator a bunch of times and vertical attraction should orient the vehicle up
for (int ii = 0; ii < simSteps; ii++)
{
TestVehicle.VehicleActor.ForgetKnownVehicleProperties();
TestVehicle.VehicleActor.ComputeAngularVerticalAttraction();
TestVehicle.VehicleActor.PushKnownChanged();
PhysicsScene.Simulate(simulationTimeStep);
}
TestVehicle.IsPhysical = false;
PhysicsScene.ProcessTaints();
// After these steps, the vehicle should be upright
/*
float finalRoll, finalPitch, finalYaw;
TestVehicle.Orientation.GetEulerAngles(out finalRoll, out finalPitch, out finalYaw);
Assert.That(finalRoll, Is.InRange(-0.01f, 0.01f));
Assert.That(finalPitch, Is.InRange(-0.01f, 0.01f));
Assert.That(finalYaw, Is.InRange(initYaw - 0.1f, initYaw + 0.1f));
*/
Vector3 upPointer = Vector3.UnitZ * TestVehicle.Orientation;
Assert.That(upPointer.Z, Is.GreaterThan(0.99f));
}
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.Linq;
using System.Text;
using NUnit.Framework;
using log4net;
using OpenSim.Tests.Common;
namespace OpenSim.Region.Physics.BulletSPlugin.Tests
{
[TestFixture]
public class BulletSimTests : OpenSimTestCase
{
// Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1
// Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1
[TestFixtureSetUp]
public void Init()
{
}
[TestFixtureTearDown]
public void TearDown()
{
}
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Region.Physics.BulletSPlugin;
using OpenSim.Region.Physics.Meshing;
namespace OpenSim.Region.Physics.BulletSPlugin.Tests
{
// Utility functions for building up and tearing down the sample physics environments
public static class BulletSimTestsUtil
{
// 'engineName' is the Bullet engine to use. Either null (for unmanaged), "BulletUnmanaged" or "BulletXNA"
// 'params' is a set of keyValue pairs to set in the engine's configuration file (override defaults)
// May be 'null' if there are no overrides.
public static BSScene CreateBasicPhysicsEngine(Dictionary<string,string> paramOverrides)
{
IConfigSource openSimINI = new IniConfigSource();
IConfig startupConfig = openSimINI.AddConfig("Startup");
startupConfig.Set("physics", "BulletSim");
startupConfig.Set("meshing", "Meshmerizer");
startupConfig.Set("cacheSculptMaps", "false"); // meshmerizer shouldn't save maps
IConfig bulletSimConfig = openSimINI.AddConfig("BulletSim");
// If the caller cares, specify the bullet engine otherwise it will default to "BulletUnmanaged".
// bulletSimConfig.Set("BulletEngine", "BulletUnmanaged");
// bulletSimConfig.Set("BulletEngine", "BulletXNA");
bulletSimConfig.Set("MeshSculptedPrim", "false");
bulletSimConfig.Set("ForceSimplePrimMeshing", "true");
if (paramOverrides != null)
{
foreach (KeyValuePair<string, string> kvp in paramOverrides)
{
bulletSimConfig.Set(kvp.Key, kvp.Value);
}
}
// If a special directory exists, put detailed logging therein.
// This allows local testing/debugging without having to worry that the build engine will output logs.
if (Directory.Exists("physlogs"))
{
bulletSimConfig.Set("PhysicsLoggingDir","./physlogs");
bulletSimConfig.Set("PhysicsLoggingEnabled","True");
bulletSimConfig.Set("PhysicsLoggingDoFlush","True");
bulletSimConfig.Set("VehicleLoggingEnabled","True");
}
BSPlugin bsPlugin = new BSPlugin();
BSScene bsScene = (BSScene)bsPlugin.GetScene("BSTestRegion");
// Since the asset requestor is not initialized, any mesh or sculptie will be a cube.
// In the future, add a fake asset fetcher to get meshes and sculpts.
// bsScene.RequestAssetMethod = ???;
Meshing.Meshmerizer mesher = new Meshmerizer(openSimINI);
bsScene.Initialise(mesher, openSimINI);
return bsScene;
}
}
}

View File

@ -59,6 +59,7 @@ namespace OpenSim.Region.Physics.Manager
List<Vector3> getVertexList(); List<Vector3> getVertexList();
int[] getIndexListAsInt(); int[] getIndexListAsInt();
int[] getIndexListAsIntLocked(); int[] getIndexListAsIntLocked();
float[] getVertexListAsFloat();
float[] getVertexListAsFloatLocked(); float[] getVertexListAsFloatLocked();
void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount); void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount);
void getVertexListAsPtrToFloatArray(out IntPtr vertexList, out int vertexStride, out int vertexCount); void getVertexListAsPtrToFloatArray(out IntPtr vertexList, out int vertexStride, out int vertexCount);

View File

@ -60,14 +60,14 @@ namespace OpenSim.Region.Physics.Manager
// Set parameter on a specific or all instances. // Set parameter on a specific or all instances.
// Return 'false' if not able to set the parameter. // Return 'false' if not able to set the parameter.
bool SetPhysicsParameter(string parm, float value, uint localID); bool SetPhysicsParameter(string parm, string value, uint localID);
// Get parameter. // Get parameter.
// Return 'false' if not able to get the parameter. // Return 'false' if not able to get the parameter.
bool GetPhysicsParameter(string parm, out float value); bool GetPhysicsParameter(string parm, out string value);
// Get parameter from a particular object // Get parameter from a particular object
// TODO: // TODO:
// bool GetPhysicsParameter(string parm, out float value, uint localID); // bool GetPhysicsParameter(string parm, out string value, uint localID);
} }
} }

View File

@ -147,6 +147,8 @@ namespace OpenSim.Region.Physics.Manager
public abstract Vector3 Size { get; set; } public abstract Vector3 Size { get; set; }
public virtual byte PhysicsShapeType { get; set; }
public abstract PrimitiveBaseShape Shape { set; } public abstract PrimitiveBaseShape Shape { set; }
uint m_baseLocalID; uint m_baseLocalID;
@ -218,9 +220,11 @@ namespace OpenSim.Region.Physics.Manager
handler(e); handler(e);
} }
public virtual void SetMaterial (int material) public virtual void SetMaterial (int material) { }
{ public virtual float Density { get; set; }
} public virtual float GravModifier { get; set; }
public virtual float Friction { get; set; }
public virtual float Restitution { get; set; }
/// <summary> /// <summary>
/// Position of this actor. /// Position of this actor.

View File

@ -43,6 +43,35 @@ namespace OpenSim.Region.Physics.Manager
public delegate void JointDeactivated(PhysicsJoint joint); public delegate void JointDeactivated(PhysicsJoint joint);
public delegate void JointErrorMessage(PhysicsJoint joint, string message); // this refers to an "error message due to a problem", not "amount of joint constraint violation" public delegate void JointErrorMessage(PhysicsJoint joint, string message); // this refers to an "error message due to a problem", not "amount of joint constraint violation"
public enum RayFilterFlags : ushort
{
// the flags
water = 0x01,
land = 0x02,
agent = 0x04,
nonphysical = 0x08,
physical = 0x10,
phantom = 0x20,
volumedtc = 0x40,
// ray cast colision control (may only work for meshs)
ContactsUnImportant = 0x2000,
BackFaceCull = 0x4000,
ClosestHit = 0x8000,
// some combinations
LSLPhantom = phantom | volumedtc,
PrimsNonPhantom = nonphysical | physical,
PrimsNonPhantomAgents = nonphysical | physical | agent,
AllPrims = nonphysical | phantom | volumedtc | physical,
AllButLand = agent | nonphysical | physical | phantom | volumedtc,
ClosestAndBackCull = ClosestHit | BackFaceCull,
All = 0x3f
}
public delegate void RequestAssetDelegate(UUID assetID, AssetReceivedDelegate callback); public delegate void RequestAssetDelegate(UUID assetID, AssetReceivedDelegate callback);
public delegate void AssetReceivedDelegate(AssetBase asset); public delegate void AssetReceivedDelegate(AssetBase asset);
@ -62,13 +91,20 @@ namespace OpenSim.Region.Physics.Manager
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary> /// <summary>
/// Name of this scene. Useful in debug messages to distinguish one OdeScene instance from another. /// A unique identifying string for this instance of the physics engine.
/// Useful in debug messages to distinguish one OdeScene instance from another.
/// Usually set to include the region name that the physics engine is acting for.
/// </summary> /// </summary>
public string Name { get; protected set; } public string Name { get; protected set; }
/// <summary>
/// A string identifying the family of this physics engine. Most common values returned
/// are "OpenDynamicsEngine" and "BulletSim" but others are possible.
/// </summary>
public string EngineType { get; protected set; }
// The only thing that should register for this event is the SceneGraph // The only thing that should register for this event is the SceneGraph
// Anything else could cause problems. // Anything else could cause problems.
public event physicsCrash OnPhysicsCrash; public event physicsCrash OnPhysicsCrash;
public static PhysicsScene Null public static PhysicsScene Null
@ -130,6 +166,12 @@ namespace OpenSim.Region.Physics.Manager
public abstract PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, public abstract PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
Vector3 size, Quaternion rotation, bool isPhysical, uint localid); Vector3 size, Quaternion rotation, bool isPhysical, uint localid);
public virtual PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, byte shapetype, uint localid)
{
return AddPrimShape(primName, pbs, position, size, rotation, isPhysical, localid);
}
public virtual float TimeDilation public virtual float TimeDilation
{ {
get { return 1.0f; } get { return 1.0f; }
@ -279,5 +321,15 @@ namespace OpenSim.Region.Physics.Manager
{ {
return new List<ContactResult>(); return new List<ContactResult>();
} }
public virtual object RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags filter)
{
return null;
}
public virtual bool SupportsRaycastWorldFiltered()
{
return false;
}
} }
} }

View File

@ -152,7 +152,7 @@ namespace OpenSim.Region.Physics.Meshing
return result; return result;
} }
private float[] getVertexListAsFloat() public float[] getVertexListAsFloat()
{ {
if (m_vertices == null) if (m_vertices == null)
throw new NotSupportedException(); throw new NotSupportedException();

View File

@ -66,6 +66,7 @@ using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
using System.Reflection; using System.Reflection;
using System.Linq;
namespace OpenSim.Region.ScriptEngine.Shared.Api namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
@ -110,6 +111,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp. protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp.
protected ISoundModule m_SoundModule = null; protected ISoundModule m_SoundModule = null;
//An array of HTTP/1.1 headers that are not allowed to be used
//as custom headers by llHTTPRequest.
private string[] HttpStandardHeaders =
{
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language",
"Accept-Ranges", "Age", "Allow", "Authorization", "Cache-Control",
"Connection", "Content-Encoding", "Content-Language",
"Content-Length", "Content-Location", "Content-MD5",
"Content-Range", "Content-Type", "Date", "ETag", "Expect",
"Expires", "From", "Host", "If-Match", "If-Modified-Since",
"If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified",
"Location", "Max-Forwards", "Pragma", "Proxy-Authenticate",
"Proxy-Authorization", "Range", "Referer", "Retry-After", "Server",
"TE", "Trailer", "Transfer-Encoding", "Upgrade", "User-Agent",
"Vary", "Via", "Warning", "WWW-Authenticate"
};
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item) public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item)
{ {
m_ScriptEngine = ScriptEngine; m_ScriptEngine = ScriptEngine;
@ -1522,7 +1540,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (tex.FaceTextures[i] != null) if (tex.FaceTextures[i] != null)
{ {
tex.FaceTextures[i].Shiny = sval; tex.FaceTextures[i].Shiny = sval;
tex.FaceTextures[i].Bump = bump;; tex.FaceTextures[i].Bump = bump;
} }
tex.DefaultTexture.Shiny = sval; tex.DefaultTexture.Shiny = sval;
tex.DefaultTexture.Bump = bump; tex.DefaultTexture.Bump = bump;
@ -1631,7 +1649,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f); texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f);
tex.DefaultTexture.RGBA = texcolor; tex.DefaultTexture.RGBA = texcolor;
} }
part.UpdateTextureEntry(tex.GetBytes()); part.UpdateTextureEntry(tex.GetBytes());
return; return;
} }
@ -1668,10 +1686,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
part.Shape.FlexiForceX = (float)Force.x; part.Shape.FlexiForceX = (float)Force.x;
part.Shape.FlexiForceY = (float)Force.y; part.Shape.FlexiForceY = (float)Force.y;
part.Shape.FlexiForceZ = (float)Force.z; part.Shape.FlexiForceZ = (float)Force.z;
part.Shape.PathCurve = 0x80; part.Shape.PathCurve = (byte)Extrusion.Flexible;
part.ParentGroup.HasGroupChanged = true;
part.ScheduleFullUpdate();
} }
else
{
// Other values not set, they do not seem to be sent to the viewer
// Setting PathCurve appears to be what actually toggles the check box and turns Flexi on and off
part.Shape.PathCurve = (byte)Extrusion.Straight;
part.Shape.FlexiEntry = false;
}
part.ParentGroup.HasGroupChanged = true;
part.ScheduleFullUpdate();
} }
/// <summary> /// <summary>
@ -1745,7 +1770,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
rgb.x = texcolor.R; rgb.x = texcolor.R;
rgb.y = texcolor.G; rgb.y = texcolor.G;
rgb.z = texcolor.B; rgb.z = texcolor.B;
return rgb; return rgb;
} }
else else
@ -1777,12 +1802,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
UUID textureID = new UUID(); UUID textureID = new UUID();
textureID = InventoryKey(texture, (int)AssetType.Texture); textureID = InventoryKey(texture, (int)AssetType.Texture);
if (textureID == UUID.Zero) if (textureID == UUID.Zero)
{ {
if (!UUID.TryParse(texture, out textureID)) if (!UUID.TryParse(texture, out textureID))
return; return;
} }
Primitive.TextureEntry tex = part.Shape.Textures; Primitive.TextureEntry tex = part.Shape.Textures;
@ -1979,7 +2004,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
// IF YOU GET REGION CROSSINGS WORKING WITH THIS FUNCTION, REPLACE THE WORKAROUND. // IF YOU GET REGION CROSSINGS WORKING WITH THIS FUNCTION, REPLACE THE WORKAROUND.
// //
// This workaround is to prevent silent failure of this function. // This workaround is to prevent silent failure of this function.
// According to the specification on the SL Wiki, providing a position outside of the // According to the specification on the SL Wiki, providing a position outside of the
if (pos.x < 0 || pos.x > Constants.RegionSize || pos.y < 0 || pos.y > Constants.RegionSize) if (pos.x < 0 || pos.x > Constants.RegionSize || pos.y < 0 || pos.y > Constants.RegionSize)
{ {
return 0; return 0;
@ -2188,7 +2213,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
return llGetRootRotation(); return llGetRootRotation();
} }
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
Quaternion q = m_host.GetWorldRotation(); Quaternion q = m_host.GetWorldRotation();
return new LSL_Rotation(q.X, q.Y, q.Z, q.W); return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
@ -2875,7 +2900,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
// we need to convert from a vector describing // we need to convert from a vector describing
// the angles of rotation in radians into rotation value // the angles of rotation in radians into rotation value
LSL_Rotation rot = llEuler2Rot(angle); LSL_Rotation rot = llEuler2Rot(angle);
// Per discussion with Melanie, for non-physical objects llLookAt appears to simply // Per discussion with Melanie, for non-physical objects llLookAt appears to simply
// set the rotation of the object, copy that behavior // set the rotation of the object, copy that behavior
PhysicsActor pa = m_host.PhysActor; PhysicsActor pa = m_host.PhysActor;
@ -2951,7 +2976,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (!UUID.TryParse(id, out objectID)) if (!UUID.TryParse(id, out objectID))
objectID = UUID.Zero; objectID = UUID.Zero;
if (objectID == UUID.Zero && name == "") if (objectID == UUID.Zero && name == "")
return; return;
@ -3007,7 +3032,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// <summary> /// <summary>
/// Attach the object containing this script to the avatar that owns it. /// Attach the object containing this script to the avatar that owns it.
/// </summary> /// </summary>
/// <param name='attachment'>The attachment point (e.g. ATTACH_CHEST)</param> /// <param name='attachmentPoint'>
/// The attachment point (e.g. <see cref="OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass.ATTACH_CHEST">ATTACH_CHEST</see>)
/// </param>
/// <returns>true if the attach suceeded, false if it did not</returns> /// <returns>true if the attach suceeded, false if it did not</returns>
public bool AttachToAvatar(int attachmentPoint) public bool AttachToAvatar(int attachmentPoint)
{ {
@ -3133,19 +3160,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
msg.ParentEstateID = 0; //ParentEstateID; msg.ParentEstateID = 0; //ParentEstateID;
msg.Position = new Vector3(m_host.AbsolutePosition); msg.Position = new Vector3(m_host.AbsolutePosition);
msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid; msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid;
msg.binaryBucket msg.binaryBucket
= Util.StringToBytes256( = Util.StringToBytes256(
"{0}/{1}/{2}/{3}", "{0}/{1}/{2}/{3}",
World.RegionInfo.RegionName, World.RegionInfo.RegionName,
(int)Math.Floor(m_host.AbsolutePosition.X), (int)Math.Floor(m_host.AbsolutePosition.X),
(int)Math.Floor(m_host.AbsolutePosition.Y), (int)Math.Floor(m_host.AbsolutePosition.Y),
(int)Math.Floor(m_host.AbsolutePosition.Z)); (int)Math.Floor(m_host.AbsolutePosition.Z));
if (m_TransferModule != null) if (m_TransferModule != null)
{ {
m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); m_TransferModule.SendInstantMessage(msg, delegate(bool success) {});
} }
ScriptSleep(2000); ScriptSleep(2000);
} }
@ -3270,7 +3297,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llRotLookAt(LSL_Rotation target, double strength, double damping) public void llRotLookAt(LSL_Rotation target, double strength, double damping)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
// Per discussion with Melanie, for non-physical objects llLookAt appears to simply // Per discussion with Melanie, for non-physical objects llLookAt appears to simply
// set the rotation of the object, copy that behavior // set the rotation of the object, copy that behavior
PhysicsActor pa = m_host.PhysActor; PhysicsActor pa = m_host.PhysActor;
@ -3339,7 +3366,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (animID == UUID.Zero) if (animID == UUID.Zero)
presence.Animator.RemoveAnimation(anim); presence.Animator.RemoveAnimation(anim);
else else
presence.Animator.RemoveAnimation(animID); presence.Animator.RemoveAnimation(animID, true);
} }
} }
} }
@ -3410,21 +3437,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
else else
{ {
bool sitting = false; if (m_host.ParentGroup.GetSittingAvatars().Contains(agentID))
if (m_host.SitTargetAvatar == agentID)
{
sitting = true;
}
else
{
foreach (SceneObjectPart p in m_host.ParentGroup.Parts)
{
if (p.SitTargetAvatar == agentID)
sitting = true;
}
}
if (sitting)
{ {
// When agent is sitting, certain permissions are implicit if requested from sitting agent // When agent is sitting, certain permissions are implicit if requested from sitting agent
implicitPerms = ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION | implicitPerms = ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION |
@ -3463,7 +3476,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
INPCModule npcModule = World.RequestModuleInterface<INPCModule>(); INPCModule npcModule = World.RequestModuleInterface<INPCModule>();
if (npcModule != null && npcModule.IsNPC(agentID, World)) if (npcModule != null && npcModule.IsNPC(agentID, World))
{ {
if (agentID == m_host.ParentGroup.OwnerID || npcModule.GetOwner(agentID) == m_host.ParentGroup.OwnerID) if (npcModule.CheckPermissions(agentID, m_host.OwnerID))
{ {
lock (m_host.TaskInventory) lock (m_host.TaskInventory)
{ {
@ -3736,33 +3749,47 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_String llGetLinkKey(int linknum) public LSL_String llGetLinkKey(int linknum)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
List<UUID> keytable = new List<UUID>();
// parse for sitting avatare-uuids
World.ForEachRootScenePresence(delegate(ScenePresence presence)
{
if (presence.ParentID != 0 && m_host.ParentGroup.ContainsPart(presence.ParentID))
keytable.Add(presence.UUID);
});
int totalprims = m_host.ParentGroup.PrimCount + keytable.Count; if (linknum < 0)
if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims)
{ {
return keytable[totalprims - linknum].ToString(); if (linknum == ScriptBaseClass.LINK_THIS)
return m_host.UUID.ToString();
else
return ScriptBaseClass.NULL_KEY;
} }
if (linknum == 1 && m_host.ParentGroup.PrimCount == 1 && keytable.Count == 1) int actualPrimCount = m_host.ParentGroup.PrimCount;
{ List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars();
return m_host.UUID.ToString(); int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count;
}
SceneObjectPart part = m_host.ParentGroup.GetLinkNumPart(linknum); // Special case for a single prim. In this case the linknum is zero. However, this will not match a single
if (part != null) // prim that has any avatars sat upon it (in which case the root prim is link 1).
if (linknum == 0)
{ {
return part.UUID.ToString(); if (actualPrimCount == 1 && sittingAvatarIds.Count == 0)
return m_host.UUID.ToString();
return ScriptBaseClass.NULL_KEY;
}
// Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but
// here we must match 1 (ScriptBaseClass.LINK_ROOT).
else if (linknum == 1 && actualPrimCount == 1)
{
if (sittingAvatarIds.Count > 0)
return m_host.ParentGroup.RootPart.UUID.ToString();
else
return ScriptBaseClass.NULL_KEY;
}
else if (linknum <= adjustedPrimCount)
{
if (linknum <= actualPrimCount)
return m_host.ParentGroup.GetLinkNumPart(linknum).UUID.ToString();
else
return sittingAvatarIds[linknum - actualPrimCount - 1].ToString();
} }
else else
{ {
return UUID.Zero.ToString(); return ScriptBaseClass.NULL_KEY;
} }
} }
@ -3808,62 +3835,56 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_String llGetLinkName(int linknum) public LSL_String llGetLinkName(int linknum)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
// simplest case, this prims link number
if (linknum == m_host.LinkNum || linknum == ScriptBaseClass.LINK_THIS)
return m_host.Name;
// parse for sitting avatare-names if (linknum < 0)
List<String> nametable = new List<String>();
World.ForEachRootScenePresence(delegate(ScenePresence presence)
{ {
SceneObjectPart sitPart = presence.ParentPart; if (linknum == ScriptBaseClass.LINK_THIS)
if (sitPart != null && m_host.ParentGroup.ContainsPart(sitPart.LocalId))
nametable.Add(presence.ControllingClient.Name);
});
int totalprims = m_host.ParentGroup.PrimCount + nametable.Count;
if (totalprims > m_host.ParentGroup.PrimCount)
{
// sitting Avatar-Name with negativ linknum / SinglePrim
if (linknum < 0 && m_host.ParentGroup.PrimCount == 1 && nametable.Count == 1)
return nametable[0];
// Prim-Name / SinglePrim Sitting Avatar
if (linknum == 1 && m_host.ParentGroup.PrimCount == 1 && nametable.Count == 1)
return m_host.Name;
// LinkNumber > of Real PrimSet = AvatarName
if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims)
return nametable[totalprims - linknum];
}
// Single prim
if (m_host.LinkNum == 0)
{
if (linknum == 0 || linknum == ScriptBaseClass.LINK_ROOT)
return m_host.Name; return m_host.Name;
else else
return UUID.Zero.ToString(); return ScriptBaseClass.NULL_KEY;
} }
// Link set int actualPrimCount = m_host.ParentGroup.PrimCount;
SceneObjectPart part = null; List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars();
if (m_host.LinkNum == 1) // this is the Root prim int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count;
// Special case for a single prim. In this case the linknum is zero. However, this will not match a single
// prim that has any avatars sat upon it (in which case the root prim is link 1).
if (linknum == 0)
{ {
if (linknum < 0) if (actualPrimCount == 1 && sittingAvatarIds.Count == 0)
part = m_host.ParentGroup.GetLinkNumPart(2); return m_host.Name;
else
part = m_host.ParentGroup.GetLinkNumPart(linknum); return ScriptBaseClass.NULL_KEY;
} }
else // this is a child prim // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but
// here we must match 1 (ScriptBaseClass.LINK_ROOT).
else if (linknum == 1 && actualPrimCount == 1)
{ {
if (linknum < 2) if (sittingAvatarIds.Count > 0)
part = m_host.ParentGroup.GetLinkNumPart(1); return m_host.ParentGroup.RootPart.Name;
else else
part = m_host.ParentGroup.GetLinkNumPart(linknum); return ScriptBaseClass.NULL_KEY;
}
else if (linknum <= adjustedPrimCount)
{
if (linknum <= actualPrimCount)
{
return m_host.ParentGroup.GetLinkNumPart(linknum).Name;
}
else
{
ScenePresence sp = World.GetScenePresence(sittingAvatarIds[linknum - actualPrimCount - 1]);
if (sp != null)
return sp.Name;
else
return ScriptBaseClass.NULL_KEY;
}
} }
if (part != null)
return part.Name;
else else
return UUID.Zero.ToString(); {
return ScriptBaseClass.NULL_KEY;
}
} }
public LSL_Integer llGetInventoryNumber(int type) public LSL_Integer llGetInventoryNumber(int type)
@ -4350,7 +4371,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llCollisionSound(string impact_sound, double impact_volume) public void llCollisionSound(string impact_sound, double impact_volume)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
// TODO: Parameter check logic required. // TODO: Parameter check logic required.
m_host.CollisionSound = KeyOrName(impact_sound, AssetType.Sound); m_host.CollisionSound = KeyOrName(impact_sound, AssetType.Sound);
m_host.CollisionSoundVolume = (float)impact_volume; m_host.CollisionSoundVolume = (float)impact_volume;
@ -5040,7 +5061,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
// SL spits out an empty string for types other than key & string // SL spits out an empty string for types other than key & string
// At the time of patching, LSL_Key is currently LSL_String, // At the time of patching, LSL_Key is currently LSL_String,
// so the OR check may be a little redundant, but it's being done // so the OR check may be a little redundant, but it's being done
// for completion and should LSL_Key ever be implemented // for completion and should LSL_Key ever be implemented
// as it's own struct // as it's own struct
else if (!(src.Data[index] is LSL_String || else if (!(src.Data[index] is LSL_String ||
src.Data[index] is LSL_Key)) src.Data[index] is LSL_Key))
@ -5176,8 +5197,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
return string.Join(", ", return string.Join(", ",
(new List<object>(src.Data)).ConvertAll<string>(o => (new List<object>(src.Data)).ConvertAll<string>(o =>
{ {
return o.ToString(); return o.ToString();
}).ToArray()); }).ToArray());
@ -5418,9 +5439,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
/// <summary> /// <summary>
/// Insert the list identified by <src> into the /// Insert the list identified by <paramref name="src"/> into the
/// list designated by <dest> such that the first /// list designated by <paramref name="dest"/> such that the first
/// new element has the index specified by <index> /// new element has the index specified by <paramref name="index"/>
/// </summary> /// </summary>
public LSL_List llListInsertList(LSL_List dest, LSL_List src, int index) public LSL_List llListInsertList(LSL_List dest, LSL_List src, int index)
@ -5774,13 +5795,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (parcelOwned && land.LandData.OwnerID == id || if (parcelOwned && land.LandData.OwnerID == id ||
parcel && land.LandData.GlobalID == id) parcel && land.LandData.GlobalID == id)
{ {
result.Add(ssp.UUID.ToString()); result.Add(new LSL_Key(ssp.UUID.ToString()));
} }
} }
} }
else else
{ {
result.Add(ssp.UUID.ToString()); result.Add(new LSL_Key(ssp.UUID.ToString()));
} }
} }
// Maximum of 100 results // Maximum of 100 results
@ -6198,6 +6219,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
ps.BurstSpeedMax = 1.0f; ps.BurstSpeedMax = 1.0f;
ps.BurstRate = 0.1f; ps.BurstRate = 0.1f;
ps.PartMaxAge = 10.0f; ps.PartMaxAge = 10.0f;
ps.BurstPartCount = 1;
return ps; return ps;
} }
@ -6219,9 +6241,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
SetParticleSystem(m_host, rules); SetParticleSystem(m_host, rules);
} }
private void SetParticleSystem(SceneObjectPart part, LSL_List rules) { private void SetParticleSystem(SceneObjectPart part, LSL_List rules)
{
if (rules.Length == 0) if (rules.Length == 0)
{ {
part.RemoveParticleSystem(); part.RemoveParticleSystem();
@ -6457,7 +6478,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_host.OwnerID, m_host.Name, destID, m_host.OwnerID, m_host.Name, destID,
(byte)InstantMessageDialog.TaskInventoryOffered, (byte)InstantMessageDialog.TaskInventoryOffered,
false, string.Format("'{0}'", category), false, string.Format("'{0}'", category),
// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 // We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06
// false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z), // false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z),
folderID, false, pos, folderID, false, pos,
bucket, false); bucket, false);
@ -6572,12 +6593,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_String llAvatarOnLinkSitTarget(int linknum) public LSL_String llAvatarOnLinkSitTarget(int linknum)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
if(linknum == ScriptBaseClass.LINK_SET || if(linknum == ScriptBaseClass.LINK_SET ||
linknum == ScriptBaseClass.LINK_ALL_CHILDREN || linknum == ScriptBaseClass.LINK_ALL_CHILDREN ||
linknum == ScriptBaseClass.LINK_ALL_OTHERS) return UUID.Zero.ToString(); linknum == ScriptBaseClass.LINK_ALL_OTHERS) return UUID.Zero.ToString();
List<SceneObjectPart> parts = GetLinkParts(linknum); List<SceneObjectPart> parts = GetLinkParts(linknum);
if (parts.Count == 0) return UUID.Zero.ToString(); if (parts.Count == 0) return UUID.Zero.ToString();
return parts[0].SitTargetAvatar.ToString(); return parts[0].SitTargetAvatar.ToString();
} }
@ -6949,7 +6970,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
hollow = 0.70f; hollow = 0.70f;
} }
} }
// Otherwise, hollow is limited to 95%. // Otherwise, hollow is limited to 95%.
else else
{ {
if (hollow > 0.95f) if (hollow > 0.95f)
@ -7881,14 +7902,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_Integer llGetNumberOfPrims() public LSL_Integer llGetNumberOfPrims()
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
int avatarCount = 0;
World.ForEachRootScenePresence(delegate(ScenePresence presence)
{
if (presence.ParentID != 0 && m_host.ParentGroup.ContainsPart(presence.ParentID))
avatarCount++;
});
return m_host.ParentGroup.PrimCount + avatarCount; return m_host.ParentGroup.PrimCount + m_host.ParentGroup.GetSittingAvatarsCount();
} }
/// <summary> /// <summary>
@ -8136,16 +8151,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
res.Add(new LSL_Vector(Shape.PathTaperX / 100.0, Shape.PathTaperY / 100.0, 0)); res.Add(new LSL_Vector(Shape.PathTaperX / 100.0, Shape.PathTaperY / 100.0, 0));
// float revolutions // float revolutions
res.Add(new LSL_Float(Math.Round(Shape.PathRevolutions * 0.015d, 2, MidpointRounding.AwayFromZero)) + 1.0d); res.Add(new LSL_Float(Math.Round(Shape.PathRevolutions * 0.015d, 2, MidpointRounding.AwayFromZero)) + 1.0d);
// Slightly inaccurate, because an unsigned byte is being used to represent // Slightly inaccurate, because an unsigned byte is being used to represent
// the entire range of floating-point values from 1.0 through 4.0 (which is how // the entire range of floating-point values from 1.0 through 4.0 (which is how
// SL does it). // SL does it).
// //
// Using these formulas to store and retrieve PathRevolutions, it is not // Using these formulas to store and retrieve PathRevolutions, it is not
// possible to use all values between 1.00 and 4.00. For instance, you can't // possible to use all values between 1.00 and 4.00. For instance, you can't
// represent 1.10. You can represent 1.09 and 1.11, but not 1.10. So, if you // represent 1.10. You can represent 1.09 and 1.11, but not 1.10. So, if you
// use llSetPrimitiveParams to set revolutions to 1.10 and then retreive them // use llSetPrimitiveParams to set revolutions to 1.10 and then retreive them
// with llGetPrimitiveParams, you'll retrieve 1.09. You can also see a similar // with llGetPrimitiveParams, you'll retrieve 1.09. You can also see a similar
// behavior in the viewer as you cannot set 1.10. The viewer jumps to 1.11. // behavior in the viewer as you cannot set 1.10. The viewer jumps to 1.11.
// In SL, llSetPrimitveParams and llGetPrimitiveParams can set and get a value // In SL, llSetPrimitveParams and llGetPrimitiveParams can set and get a value
// such as 1.10. So, SL must store and retreive the actual user input rather // such as 1.10. So, SL must store and retreive the actual user input rather
@ -10267,9 +10282,60 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
IHttpRequestModule httpScriptMod = IHttpRequestModule httpScriptMod =
m_ScriptEngine.World.RequestModuleInterface<IHttpRequestModule>(); m_ScriptEngine.World.RequestModuleInterface<IHttpRequestModule>();
List<string> param = new List<string>(); List<string> param = new List<string>();
foreach (object o in parameters.Data) bool ok;
Int32 flag;
for (int i = 0; i < parameters.Data.Length; i += 2)
{ {
param.Add(o.ToString()); ok = Int32.TryParse(parameters.Data[i].ToString(), out flag);
if (!ok || flag < 0 ||
flag > (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE)
{
throw new ScriptException("Parameter " + i.ToString() + " is an invalid flag");
}
param.Add(parameters.Data[i].ToString()); //Add parameter flag
if (flag != (int)HttpRequestConstants.HTTP_CUSTOM_HEADER)
{
param.Add(parameters.Data[i+1].ToString()); //Add parameter value
}
else
{
//Parameters are in pairs and custom header takes
//arguments in pairs so adjust for header marker.
++i;
//Maximum of 8 headers are allowed based on the
//Second Life documentation for llHTTPRequest.
for (int count = 1; count <= 8; ++count)
{
//Enough parameters remaining for (another) header?
if (parameters.Data.Length - i < 2)
{
//There must be at least one name/value pair for custom header
if (count == 1)
throw new ScriptException("Missing name/value for custom header at parameter " + i.ToString());
break;
}
if (HttpStandardHeaders.Contains(parameters.Data[i].ToString(), StringComparer.OrdinalIgnoreCase))
throw new ScriptException("Name is invalid as a custom header at parameter " + i.ToString());
param.Add(parameters.Data[i].ToString());
param.Add(parameters.Data[i+1].ToString());
//Have we reached the end of the list of headers?
//End is marked by a string with a single digit.
if (i+2 >= parameters.Data.Length ||
Char.IsDigit(parameters.Data[i].ToString()[0]))
{
break;
}
i += 2;
}
}
} }
Vector3 position = m_host.AbsolutePosition; Vector3 position = m_host.AbsolutePosition;
@ -10379,12 +10445,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_Integer llGetParcelPrimCount(LSL_Vector pos, int category, int sim_wide) public LSL_Integer llGetParcelPrimCount(LSL_Vector pos, int category, int sim_wide)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
ILandObject lo = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y); ILandObject lo = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y);
if (lo == null) if (lo == null)
return 0; return 0;
IPrimCounts pc = lo.PrimCounts; IPrimCounts pc = lo.PrimCounts;
if (sim_wide != ScriptBaseClass.FALSE) if (sim_wide != ScriptBaseClass.FALSE)
@ -10414,7 +10480,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
else if (category == ScriptBaseClass.PARCEL_COUNT_TEMP) else if (category == ScriptBaseClass.PARCEL_COUNT_TEMP)
return 0; // counts not implemented yet return 0; // counts not implemented yet
} }
return 0; return 0;
} }
@ -10577,6 +10643,35 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
case ScriptBaseClass.OBJECT_PHYSICS_COST: case ScriptBaseClass.OBJECT_PHYSICS_COST:
ret.Add(new LSL_Float(0)); ret.Add(new LSL_Float(0));
break; break;
case ScriptBaseClass.OBJECT_CHARACTER_TIME: // Pathfinding
ret.Add(new LSL_Float(0));
break;
case ScriptBaseClass.OBJECT_ROOT:
SceneObjectPart p = av.ParentPart;
if (p != null)
{
ret.Add(new LSL_String(p.ParentGroup.RootPart.UUID.ToString()));
}
else
{
ret.Add(new LSL_String(id));
}
break;
case ScriptBaseClass.OBJECT_ATTACHED_POINT:
ret.Add(new LSL_Integer(0));
break;
case ScriptBaseClass.OBJECT_PATHFINDING_TYPE: // Pathfinding
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_AVATAR));
break;
case ScriptBaseClass.OBJECT_PHYSICS:
ret.Add(new LSL_Integer(0));
break;
case ScriptBaseClass.OBJECT_PHANTOM:
ret.Add(new LSL_Integer(0));
break;
case ScriptBaseClass.OBJECT_TEMP_ON_REZ:
ret.Add(new LSL_Integer(0));
break;
default: default:
// Invalid or unhandled constant. // Invalid or unhandled constant.
ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL)); ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL));
@ -10672,6 +10767,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
// The value returned in SL for normal prims looks like the prim count // The value returned in SL for normal prims looks like the prim count
ret.Add(new LSL_Float(0)); ret.Add(new LSL_Float(0));
break; break;
case ScriptBaseClass.OBJECT_CHARACTER_TIME: // Pathfinding
ret.Add(new LSL_Float(0));
break;
case ScriptBaseClass.OBJECT_ROOT:
ret.Add(new LSL_String(obj.ParentGroup.RootPart.UUID.ToString()));
break;
case ScriptBaseClass.OBJECT_ATTACHED_POINT:
ret.Add(new LSL_Integer(obj.ParentGroup.AttachmentPoint));
break;
case ScriptBaseClass.OBJECT_PATHFINDING_TYPE:
byte pcode = obj.Shape.PCode;
if (obj.ParentGroup.AttachmentPoint != 0
|| pcode == (byte)PCode.Grass
|| pcode == (byte)PCode.Tree
|| pcode == (byte)PCode.NewTree)
{
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_OTHER));
}
else
{
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_LEGACY_LINKSET));
}
break;
case ScriptBaseClass.OBJECT_PHYSICS:
if (obj.ParentGroup.AttachmentPoint != 0)
{
ret.Add(new LSL_Integer(0)); // Always false if attached
}
else
{
ret.Add(new LSL_Integer(obj.ParentGroup.UsesPhysics ? 1 : 0));
}
break;
case ScriptBaseClass.OBJECT_PHANTOM:
if (obj.ParentGroup.AttachmentPoint != 0)
{
ret.Add(new LSL_Integer(0)); // Always false if attached
}
else
{
ret.Add(new LSL_Integer(obj.ParentGroup.IsPhantom ? 1 : 0));
}
break;
case ScriptBaseClass.OBJECT_TEMP_ON_REZ:
ret.Add(new LSL_Integer(obj.ParentGroup.IsTemporary ? 1 : 0));
break;
default: default:
// Invalid or unhandled constant. // Invalid or unhandled constant.
ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL)); ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL));
@ -10682,7 +10823,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return ret; return ret;
} }
} }
return new LSL_List(); return new LSL_List();
} }
@ -11309,7 +11450,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
else else
{ {
ScenePresence sp = World.GetScenePresence(result.ConsumerID); ScenePresence sp = World.GetScenePresence(result.ConsumerID);
/// It it a boy? a girl? /// It it a boy? a girl?
if (sp != null) if (sp != null)
itemID = sp.UUID; itemID = sp.UUID;
} }
@ -11520,7 +11661,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// Get a notecard line. /// Get a notecard line.
/// </summary> /// </summary>
/// <param name="assetID"></param> /// <param name="assetID"></param>
/// <param name="line">Lines start at index 0</param> /// <param name="lineNumber">Lines start at index 0</param>
/// <returns></returns> /// <returns></returns>
public static string GetLine(UUID assetID, int lineNumber) public static string GetLine(UUID assetID, int lineNumber)
{ {
@ -11549,9 +11690,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// Get a notecard line. /// Get a notecard line.
/// </summary> /// </summary>
/// <param name="assetID"></param> /// <param name="assetID"></param>
/// <param name="line">Lines start at index 0</param> /// <param name="lineNumber">Lines start at index 0</param>
/// <param name="maxLength">Maximum length of the returned line. Longer lines will be truncated</para> /// <param name="maxLength">
/// <returns></returns> /// Maximum length of the returned line.
/// </param>
/// <returns>
/// If the line length is longer than <paramref name="maxLength"/>,
/// the return string will be truncated.
/// </returns>
public static string GetLine(UUID assetID, int lineNumber, int maxLength) public static string GetLine(UUID assetID, int lineNumber, int maxLength)
{ {
string line = GetLine(assetID, lineNumber); string line = GetLine(assetID, lineNumber);

View File

@ -974,7 +974,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if (animID == UUID.Zero) if (animID == UUID.Zero)
target.Animator.RemoveAnimation(animation); target.Animator.RemoveAnimation(animation);
else else
target.Animator.RemoveAnimation(animID); target.Animator.RemoveAnimation(animID, true);
} }
} }
} }

View File

@ -42,6 +42,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
{ {
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Used by one-off and repeated sensors
/// </summary>
public class SensorInfo
{
public uint localID;
public UUID itemID;
public double interval;
public DateTime next;
public string name;
public UUID keyID;
public int type;
public double range;
public double arc;
public SceneObjectPart host;
public SensorInfo Clone()
{
return (SensorInfo)this.MemberwiseClone();
}
}
public AsyncCommandManager m_CmdManager; public AsyncCommandManager m_CmdManager;
/// <summary> /// <summary>
@ -78,24 +101,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
private double maximumRange = 96.0; private double maximumRange = 96.0;
private int maximumToReturn = 16; private int maximumToReturn = 16;
//
// SenseRepeater and Sensors
//
private class SenseRepeatClass
{
public uint localID;
public UUID itemID;
public double interval;
public DateTime next;
public string name;
public UUID keyID;
public int type;
public double range;
public double arc;
public SceneObjectPart host;
}
// //
// Sensed entity // Sensed entity
// //
@ -128,7 +133,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
/// ///
/// Always lock SenseRepeatListLock when updating this list. /// Always lock SenseRepeatListLock when updating this list.
/// </remarks> /// </remarks>
private List<SenseRepeatClass> SenseRepeaters = new List<SenseRepeatClass>(); private List<SensorInfo> SenseRepeaters = new List<SensorInfo>();
private object SenseRepeatListLock = new object(); private object SenseRepeatListLock = new object();
public void SetSenseRepeatEvent(uint m_localID, UUID m_itemID, public void SetSenseRepeatEvent(uint m_localID, UUID m_itemID,
@ -142,7 +147,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
return; return;
// Add to timer // Add to timer
SenseRepeatClass ts = new SenseRepeatClass(); SensorInfo ts = new SensorInfo();
ts.localID = m_localID; ts.localID = m_localID;
ts.itemID = m_itemID; ts.itemID = m_itemID;
ts.interval = sec; ts.interval = sec;
@ -161,11 +166,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
AddSenseRepeater(ts); AddSenseRepeater(ts);
} }
private void AddSenseRepeater(SenseRepeatClass senseRepeater) private void AddSenseRepeater(SensorInfo senseRepeater)
{ {
lock (SenseRepeatListLock) lock (SenseRepeatListLock)
{ {
List<SenseRepeatClass> newSenseRepeaters = new List<SenseRepeatClass>(SenseRepeaters); List<SensorInfo> newSenseRepeaters = new List<SensorInfo>(SenseRepeaters);
newSenseRepeaters.Add(senseRepeater); newSenseRepeaters.Add(senseRepeater);
SenseRepeaters = newSenseRepeaters; SenseRepeaters = newSenseRepeaters;
} }
@ -176,8 +181,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
// Remove from timer // Remove from timer
lock (SenseRepeatListLock) lock (SenseRepeatListLock)
{ {
List<SenseRepeatClass> newSenseRepeaters = new List<SenseRepeatClass>(); List<SensorInfo> newSenseRepeaters = new List<SensorInfo>();
foreach (SenseRepeatClass ts in SenseRepeaters) foreach (SensorInfo ts in SenseRepeaters)
{ {
if (ts.localID != m_localID || ts.itemID != m_itemID) if (ts.localID != m_localID || ts.itemID != m_itemID)
{ {
@ -192,7 +197,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
public void CheckSenseRepeaterEvents() public void CheckSenseRepeaterEvents()
{ {
// Go through all timers // Go through all timers
foreach (SenseRepeatClass ts in SenseRepeaters) foreach (SensorInfo ts in SenseRepeaters)
{ {
// Time has passed? // Time has passed?
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
@ -209,7 +214,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
double range, double arc, SceneObjectPart host) double range, double arc, SceneObjectPart host)
{ {
// Add to timer // Add to timer
SenseRepeatClass ts = new SenseRepeatClass(); SensorInfo ts = new SensorInfo();
ts.localID = m_localID; ts.localID = m_localID;
ts.itemID = m_itemID; ts.itemID = m_itemID;
ts.interval = 0; ts.interval = 0;
@ -225,7 +230,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
SensorSweep(ts); SensorSweep(ts);
} }
private void SensorSweep(SenseRepeatClass ts) private void SensorSweep(SensorInfo ts)
{ {
if (ts.host == null) if (ts.host == null)
{ {
@ -301,7 +306,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
} }
} }
private List<SensedEntity> doObjectSensor(SenseRepeatClass ts) private List<SensedEntity> doObjectSensor(SensorInfo ts)
{ {
List<EntityBase> Entities; List<EntityBase> Entities;
List<SensedEntity> sensedEntities = new List<SensedEntity>(); List<SensedEntity> sensedEntities = new List<SensedEntity>();
@ -450,7 +455,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
return sensedEntities; return sensedEntities;
} }
private List<SensedEntity> doAgentSensor(SenseRepeatClass ts) private List<SensedEntity> doAgentSensor(SensorInfo ts)
{ {
List<SensedEntity> sensedEntities = new List<SensedEntity>(); List<SensedEntity> sensedEntities = new List<SensedEntity>();
@ -626,7 +631,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
{ {
List<Object> data = new List<Object>(); List<Object> data = new List<Object>();
foreach (SenseRepeatClass ts in SenseRepeaters) foreach (SensorInfo ts in SenseRepeaters)
{ {
if (ts.itemID == itemID) if (ts.itemID == itemID)
{ {
@ -656,7 +661,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
while (idx < data.Length) while (idx < data.Length)
{ {
SenseRepeatClass ts = new SenseRepeatClass(); SensorInfo ts = new SensorInfo();
ts.localID = localID; ts.localID = localID;
ts.itemID = itemID; ts.itemID = itemID;
@ -677,5 +682,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
idx += 6; idx += 6;
} }
} }
public List<SensorInfo> GetSensorInfo()
{
List<SensorInfo> retList = new List<SensorInfo>();
lock (SenseRepeatListLock)
{
foreach (SensorInfo i in SenseRepeaters)
retList.Add(i.Clone());
}
return retList;
}
} }
} }

View File

@ -35,6 +35,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
{ {
public class Timer public class Timer
{ {
public class TimerInfo
{
public uint localID;
public UUID itemID;
//public double interval;
public long interval;
//public DateTime next;
public long next;
public TimerInfo Clone()
{
return (TimerInfo)this.MemberwiseClone();
}
}
public AsyncCommandManager m_CmdManager; public AsyncCommandManager m_CmdManager;
public int TimersCount public int TimersCount
@ -59,17 +74,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
return localID.ToString() + itemID.ToString(); return localID.ToString() + itemID.ToString();
} }
private class TimerClass private Dictionary<string,TimerInfo> Timers = new Dictionary<string,TimerInfo>();
{
public uint localID;
public UUID itemID;
//public double interval;
public long interval;
//public DateTime next;
public long next;
}
private Dictionary<string,TimerClass> Timers = new Dictionary<string,TimerClass>();
private object TimerListLock = new object(); private object TimerListLock = new object();
public void SetTimerEvent(uint m_localID, UUID m_itemID, double sec) public void SetTimerEvent(uint m_localID, UUID m_itemID, double sec)
@ -81,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
} }
// Add to timer // Add to timer
TimerClass ts = new TimerClass(); TimerInfo ts = new TimerInfo();
ts.localID = m_localID; ts.localID = m_localID;
ts.itemID = m_itemID; ts.itemID = m_itemID;
ts.interval = Convert.ToInt64(sec * 10000000); // How many 100 nanoseconds (ticks) should we wait ts.interval = Convert.ToInt64(sec * 10000000); // How many 100 nanoseconds (ticks) should we wait
@ -121,8 +126,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
lock (TimerListLock) lock (TimerListLock)
{ {
// Go through all timers // Go through all timers
Dictionary<string, TimerClass>.ValueCollection tvals = Timers.Values; Dictionary<string, TimerInfo>.ValueCollection tvals = Timers.Values;
foreach (TimerClass ts in tvals) foreach (TimerInfo ts in tvals)
{ {
// Time has passed? // Time has passed?
if (ts.next < DateTime.Now.Ticks) if (ts.next < DateTime.Now.Ticks)
@ -147,8 +152,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
lock (TimerListLock) lock (TimerListLock)
{ {
Dictionary<string, TimerClass>.ValueCollection tvals = Timers.Values; Dictionary<string, TimerInfo>.ValueCollection tvals = Timers.Values;
foreach (TimerClass ts in tvals) foreach (TimerInfo ts in tvals)
{ {
if (ts.itemID == itemID) if (ts.itemID == itemID)
{ {
@ -167,7 +172,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
while (idx < data.Length) while (idx < data.Length)
{ {
TimerClass ts = new TimerClass(); TimerInfo ts = new TimerInfo();
ts.localID = localID; ts.localID = localID;
ts.itemID = itemID; ts.itemID = itemID;
@ -181,5 +186,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
} }
} }
} }
public List<TimerInfo> GetTimersInfo()
{
List<TimerInfo> retList = new List<TimerInfo>();
lock (TimerListLock)
{
foreach (TimerInfo i in Timers.Values)
retList.Add(i.Clone());
}
return retList;
}
} }
} }

View File

@ -355,6 +355,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int HTTP_MIMETYPE = 1; public const int HTTP_MIMETYPE = 1;
public const int HTTP_BODY_MAXLENGTH = 2; public const int HTTP_BODY_MAXLENGTH = 2;
public const int HTTP_VERIFY_CERT = 3; public const int HTTP_VERIFY_CERT = 3;
public const int HTTP_VERBOSE_THROTTLE = 4;
public const int HTTP_CUSTOM_HEADER = 5;
public const int HTTP_PRAGMA_NO_CACHE = 6;
public const int PRIM_MATERIAL = 2; public const int PRIM_MATERIAL = 2;
public const int PRIM_PHYSICS = 3; public const int PRIM_PHYSICS = 3;
@ -556,6 +559,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int OBJECT_SERVER_COST = 14; public const int OBJECT_SERVER_COST = 14;
public const int OBJECT_STREAMING_COST = 15; public const int OBJECT_STREAMING_COST = 15;
public const int OBJECT_PHYSICS_COST = 16; public const int OBJECT_PHYSICS_COST = 16;
public const int OBJECT_CHARACTER_TIME = 17;
public const int OBJECT_ROOT = 18;
public const int OBJECT_ATTACHED_POINT = 19;
public const int OBJECT_PATHFINDING_TYPE = 20;
public const int OBJECT_PHYSICS = 21;
public const int OBJECT_PHANTOM = 22;
public const int OBJECT_TEMP_ON_REZ = 23;
// Pathfinding types
public const int OPT_OTHER = -1;
public const int OPT_LEGACY_LINKSET = 0;
public const int OPT_AVATAR = 1;
public const int OPT_CHARACTER = 2;
public const int OPT_WALKABLE = 3;
public const int OPT_STATIC_OBSTACLE = 4;
public const int OPT_MATERIAL_VOLUME = 5;
public const int OPT_EXCLUSION_VOLUME = 6;
// for llGetAgentList // for llGetAgentList
public const int AGENT_LIST_PARCEL = 1; public const int AGENT_LIST_PARCEL = 1;
@ -618,7 +638,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int TOUCH_INVALID_FACE = -1; public const int TOUCH_INVALID_FACE = -1;
public static readonly vector TOUCH_INVALID_TEXCOORD = new vector(-1.0, -1.0, 0.0); public static readonly vector TOUCH_INVALID_TEXCOORD = new vector(-1.0, -1.0, 0.0);
public static readonly vector TOUCH_INVALID_VECTOR = ZERO_VECTOR; public static readonly vector TOUCH_INVALID_VECTOR = ZERO_VECTOR;
// constants for llGetPrimMediaParams/llSetPrimMediaParams // constants for llGetPrimMediaParams/llSetPrimMediaParams
public const int PRIM_MEDIA_ALT_IMAGE_ENABLE = 0; public const int PRIM_MEDIA_ALT_IMAGE_ENABLE = 0;
public const int PRIM_MEDIA_CONTROLS = 1; public const int PRIM_MEDIA_CONTROLS = 1;
@ -635,15 +655,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int PRIM_MEDIA_WHITELIST = 12; public const int PRIM_MEDIA_WHITELIST = 12;
public const int PRIM_MEDIA_PERMS_INTERACT = 13; public const int PRIM_MEDIA_PERMS_INTERACT = 13;
public const int PRIM_MEDIA_PERMS_CONTROL = 14; public const int PRIM_MEDIA_PERMS_CONTROL = 14;
public const int PRIM_MEDIA_CONTROLS_STANDARD = 0; public const int PRIM_MEDIA_CONTROLS_STANDARD = 0;
public const int PRIM_MEDIA_CONTROLS_MINI = 1; public const int PRIM_MEDIA_CONTROLS_MINI = 1;
public const int PRIM_MEDIA_PERM_NONE = 0; public const int PRIM_MEDIA_PERM_NONE = 0;
public const int PRIM_MEDIA_PERM_OWNER = 1; public const int PRIM_MEDIA_PERM_OWNER = 1;
public const int PRIM_MEDIA_PERM_GROUP = 2; public const int PRIM_MEDIA_PERM_GROUP = 2;
public const int PRIM_MEDIA_PERM_ANYONE = 4; public const int PRIM_MEDIA_PERM_ANYONE = 4;
// extra constants for llSetPrimMediaParams // extra constants for llSetPrimMediaParams
public static readonly LSLInteger LSL_STATUS_OK = new LSLInteger(0); public static readonly LSLInteger LSL_STATUS_OK = new LSLInteger(0);
public static readonly LSLInteger LSL_STATUS_MALFORMED_PARAMS = new LSLInteger(1000); public static readonly LSLInteger LSL_STATUS_MALFORMED_PARAMS = new LSLInteger(1000);
@ -660,7 +680,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const string TEXTURE_PLYWOOD = "89556747-24cb-43ed-920b-47caed15465f"; public const string TEXTURE_PLYWOOD = "89556747-24cb-43ed-920b-47caed15465f";
public const string TEXTURE_TRANSPARENT = "8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"; public const string TEXTURE_TRANSPARENT = "8dcd4a48-2d37-4909-9f78-f7a9eb4ef903";
public const string TEXTURE_MEDIA = "8b5fec65-8d8d-9dc5-cda8-8fdf2716e361"; public const string TEXTURE_MEDIA = "8b5fec65-8d8d-9dc5-cda8-8fdf2716e361";
// Constants for osGetRegionStats // Constants for osGetRegionStats
public const int STATS_TIME_DILATION = 0; public const int STATS_TIME_DILATION = 0;
public const int STATS_SIM_FPS = 1; public const int STATS_SIM_FPS = 1;
@ -713,7 +733,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public static readonly LSLInteger RC_GET_ROOT_KEY = 2; public static readonly LSLInteger RC_GET_ROOT_KEY = 2;
public static readonly LSLInteger RC_GET_LINK_NUM = 4; public static readonly LSLInteger RC_GET_LINK_NUM = 4;
public static readonly LSLInteger RCERR_UNKNOWN = -1; public static readonly LSLInteger RCERR_UNKNOWN = -1;
public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2; public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2;
public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3; public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3;

View File

@ -166,5 +166,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
Assert.That(copiedItems[0].Name, Is.EqualTo(inventoryItemName)); Assert.That(copiedItems[0].Name, Is.EqualTo(inventoryItemName));
} }
} }
[Test]
public void TestLlRemoteLoadScriptPin()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
UUID user1Id = TestHelpers.ParseTail(0x1);
UUID user2Id = TestHelpers.ParseTail(0x2);
SceneObjectGroup sourceSo = SceneHelpers.AddSceneObject(m_scene, "sourceSo", user1Id);
m_scene.AddSceneObject(sourceSo);
LSL_Api api = new LSL_Api();
api.Initialize(m_engine, sourceSo.RootPart, null);
TaskInventoryHelpers.AddScript(m_scene, sourceSo.RootPart, "script", "Hello World");
SceneObjectGroup targetSo = SceneHelpers.AddSceneObject(m_scene, "targetSo", user1Id);
SceneObjectGroup otherOwnedTargetSo = SceneHelpers.AddSceneObject(m_scene, "otherOwnedTargetSo", user2Id);
// Test that we cannot load a script when the target pin has never been set (i.e. it is zero)
api.llRemoteLoadScriptPin(targetSo.UUID.ToString(), "script", 0, 0, 0);
Assert.IsNull(targetSo.RootPart.Inventory.GetInventoryItem("script"));
// Test that we cannot load a script when the given pin does not match the target
targetSo.RootPart.ScriptAccessPin = 5;
api.llRemoteLoadScriptPin(targetSo.UUID.ToString(), "script", 3, 0, 0);
Assert.IsNull(targetSo.RootPart.Inventory.GetInventoryItem("script"));
// Test that we cannot load into a prim with a different owner
otherOwnedTargetSo.RootPart.ScriptAccessPin = 3;
api.llRemoteLoadScriptPin(otherOwnedTargetSo.UUID.ToString(), "script", 3, 0, 0);
Assert.IsNull(otherOwnedTargetSo.RootPart.Inventory.GetInventoryItem("script"));
// Test that we can load a script when given pin and dest pin match.
targetSo.RootPart.ScriptAccessPin = 3;
api.llRemoteLoadScriptPin(targetSo.UUID.ToString(), "script", 3, 0, 0);
TaskInventoryItem insertedItem = targetSo.RootPart.Inventory.GetInventoryItem("script");
Assert.IsNotNull(insertedItem);
// Test that we can no longer load if access pin is unset
targetSo.RootPart.Inventory.RemoveInventoryItem(insertedItem.ItemID);
Assert.IsNull(targetSo.RootPart.Inventory.GetInventoryItem("script"));
targetSo.RootPart.ScriptAccessPin = 0;
api.llRemoteLoadScriptPin(otherOwnedTargetSo.UUID.ToString(), "script", 3, 0, 0);
Assert.IsNull(otherOwnedTargetSo.RootPart.Inventory.GetInventoryItem("script"));
}
} }
} }

View File

@ -0,0 +1,126 @@
/*
* 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 OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.ScriptEngine.Interfaces;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Api.Plugins;
namespace OpenSim.Region.ScriptEngine.XEngine
{
public class ScriptEngineConsoleCommands
{
IScriptEngine m_engine;
public ScriptEngineConsoleCommands(IScriptEngine engine)
{
m_engine = engine;
}
public void RegisterCommands()
{
MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "show script sensors", "show script sensors", "Show script sensors information",
HandleShowSensors);
MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "show script timers", "show script timers", "Show script sensors information",
HandleShowTimers);
}
private bool IsSceneSelected()
{
return MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_engine.World;
}
private void HandleShowSensors(string module, string[] cmdparams)
{
if (!IsSceneSelected())
return;
SensorRepeat sr = AsyncCommandManager.GetSensorRepeatPlugin(m_engine);
if (sr == null)
{
MainConsole.Instance.Output("Plugin not yet initialized");
return;
}
List<SensorRepeat.SensorInfo> sensorInfo = sr.GetSensorInfo();
ConsoleDisplayTable cdt = new ConsoleDisplayTable();
cdt.AddColumn("Part name", 40);
cdt.AddColumn("Script item ID", 36);
cdt.AddColumn("Type", 4);
cdt.AddColumn("Interval", 8);
cdt.AddColumn("Range", 8);
cdt.AddColumn("Arc", 8);
foreach (SensorRepeat.SensorInfo s in sensorInfo)
{
cdt.AddRow(s.host.Name, s.itemID, s.type, s.interval, s.range, s.arc);
}
MainConsole.Instance.Output(cdt.ToString());
MainConsole.Instance.OutputFormat("Total: {0}", sensorInfo.Count);
}
private void HandleShowTimers(string module, string[] cmdparams)
{
if (!IsSceneSelected())
return;
Timer timerPlugin = AsyncCommandManager.GetTimerPlugin(m_engine);
if (timerPlugin == null)
{
MainConsole.Instance.Output("Plugin not yet initialized");
return;
}
List<Timer.TimerInfo> timersInfo = timerPlugin.GetTimersInfo();
ConsoleDisplayTable cdt = new ConsoleDisplayTable();
cdt.AddColumn("Part local ID", 13);
cdt.AddColumn("Script item ID", 36);
cdt.AddColumn("Interval", 10);
cdt.AddColumn("Next", 8);
foreach (Timer.TimerInfo t in timersInfo)
{
// Convert from 100 ns ticks back to seconds
cdt.AddRow(t.localID, t.itemID, (double)t.interval / 10000000, t.next);
}
MainConsole.Instance.Output(cdt.ToString());
MainConsole.Instance.OutputFormat("Total: {0}", timersInfo.Count);
}
}
}

View File

@ -47,7 +47,6 @@ using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Region.ScriptEngine.Shared.CodeTools; using OpenSim.Region.ScriptEngine.Shared.CodeTools;
using OpenSim.Region.ScriptEngine.Shared.Instance; using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.Api;
@ -169,6 +168,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
IWorkItemResult m_CurrentCompile = null; IWorkItemResult m_CurrentCompile = null;
private Dictionary<UUID, int> m_CompileDict = new Dictionary<UUID, int>(); private Dictionary<UUID, int> m_CompileDict = new Dictionary<UUID, int>();
private ScriptEngineConsoleCommands m_consoleCommands;
public string ScriptEngineName public string ScriptEngineName
{ {
get { return "XEngine"; } get { return "XEngine"; }
@ -318,50 +319,53 @@ namespace OpenSim.Region.ScriptEngine.XEngine
OnObjectRemoved += m_XmlRpcRouter.ObjectRemoved; OnObjectRemoved += m_XmlRpcRouter.ObjectRemoved;
} }
m_consoleCommands = new ScriptEngineConsoleCommands(this);
m_consoleCommands.RegisterCommands();
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "xengine status", "xengine status", "Show status information", "Scripts", false, "xengine status", "xengine status", "Show status information",
"Show status information on the script engine.", "Show status information on the script engine.",
HandleShowStatus); HandleShowStatus);
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "scripts show", "scripts show [<script-item-uuid>]", "Show script information", "Scripts", false, "scripts show", "scripts show [<script-item-uuid>+]", "Show script information",
"Show information on all scripts known to the script engine.\n" "Show information on all scripts known to the script engine.\n"
+ "If a <script-item-uuid> is given then only information on that script will be shown.", + "If one or more <script-item-uuid>s are given then only information on that script will be shown.",
HandleShowScripts); HandleShowScripts);
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "show scripts", "show scripts [<script-item-uuid>]", "Show script information", "Scripts", false, "show scripts", "show scripts [<script-item-uuid>+]", "Show script information",
"Synonym for scripts show command", HandleShowScripts); "Synonym for scripts show command", HandleShowScripts);
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "scripts suspend", "scripts suspend [<script-item-uuid>]", "Suspends all running scripts", "Scripts", false, "scripts suspend", "scripts suspend [<script-item-uuid>+]", "Suspends all running scripts",
"Suspends all currently running scripts. This only suspends event delivery, it will not suspend a" "Suspends all currently running scripts. This only suspends event delivery, it will not suspend a"
+ " script that is currently processing an event.\n" + " script that is currently processing an event.\n"
+ "Suspended scripts will continue to accumulate events but won't process them.\n" + "Suspended scripts will continue to accumulate events but won't process them.\n"
+ "If a <script-item-uuid> is given then only that script will be suspended. Otherwise, all suitable scripts are suspended.", + "If one or more <script-item-uuid>s are given then only that script will be suspended. Otherwise, all suitable scripts are suspended.",
(module, cmdparams) => HandleScriptsAction(cmdparams, HandleSuspendScript)); (module, cmdparams) => HandleScriptsAction(cmdparams, HandleSuspendScript));
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "scripts resume", "scripts resume [<script-item-uuid>]", "Resumes all suspended scripts", "Scripts", false, "scripts resume", "scripts resume [<script-item-uuid>+]", "Resumes all suspended scripts",
"Resumes all currently suspended scripts.\n" "Resumes all currently suspended scripts.\n"
+ "Resumed scripts will process all events accumulated whilst suspended.\n" + "Resumed scripts will process all events accumulated whilst suspended.\n"
+ "If a <script-item-uuid> is given then only that script will be resumed. Otherwise, all suitable scripts are resumed.", + "If one or more <script-item-uuid>s are given then only that script will be resumed. Otherwise, all suitable scripts are resumed.",
(module, cmdparams) => HandleScriptsAction(cmdparams, HandleResumeScript)); (module, cmdparams) => HandleScriptsAction(cmdparams, HandleResumeScript));
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "scripts stop", "scripts stop [<script-item-uuid>]", "Stops all running scripts", "Scripts", false, "scripts stop", "scripts stop [<script-item-uuid>+]", "Stops all running scripts",
"Stops all running scripts.\n" "Stops all running scripts.\n"
+ "If a <script-item-uuid> is given then only that script will be stopped. Otherwise, all suitable scripts are stopped.", + "If one or more <script-item-uuid>s are given then only that script will be stopped. Otherwise, all suitable scripts are stopped.",
(module, cmdparams) => HandleScriptsAction(cmdparams, HandleStopScript)); (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStopScript));
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "scripts start", "scripts start [<script-item-uuid>]", "Starts all stopped scripts", "Scripts", false, "scripts start", "scripts start [<script-item-uuid>+]", "Starts all stopped scripts",
"Starts all stopped scripts.\n" "Starts all stopped scripts.\n"
+ "If a <script-item-uuid> is given then only that script will be started. Otherwise, all suitable scripts are started.", + "If one or more <script-item-uuid>s are given then only that script will be started. Otherwise, all suitable scripts are started.",
(module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript));
MainConsole.Instance.Commands.AddCommand( MainConsole.Instance.Commands.AddCommand(
"Scripts", false, "debug script log", "debug scripts log <item-id> <log-level>", "Extra debug logging for a script", "Scripts", false, "debug scripts log", "debug scripts log <item-id> <log-level>", "Extra debug logging for a script",
"Activates or deactivates extra debug logging for the given script.\n" "Activates or deactivates extra debug logging for the given script.\n"
+ "Level == 0, deactivate extra debug logging.\n" + "Level == 0, deactivate extra debug logging.\n"
+ "Level >= 1, log state changes.\n" + "Level >= 1, log state changes.\n"
@ -478,29 +482,31 @@ namespace OpenSim.Region.ScriptEngine.XEngine
return; return;
} }
rawItemId = cmdparams[2]; for (int i = 2; i < cmdparams.Length; i++)
if (!UUID.TryParse(rawItemId, out itemId))
{ {
MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid UUID", rawItemId); rawItemId = cmdparams[i];
return;
} if (!UUID.TryParse(rawItemId, out itemId))
if (itemId != UUID.Zero)
{
IScriptInstance instance = GetInstance(itemId);
if (instance == null)
{ {
// Commented out for now since this will cause false reports on simulators with more than MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid UUID", rawItemId);
// one scene where the current command line set region is 'root' (which causes commands to continue;
// go to both regions... (sigh)
// MainConsole.Instance.OutputFormat("Error - No item found with id {0}", itemId);
return;
} }
else
if (itemId != UUID.Zero)
{ {
action(instance); IScriptInstance instance = GetInstance(itemId);
return; if (instance == null)
{
// Commented out for now since this will cause false reports on simulators with more than
// one scene where the current command line set region is 'root' (which causes commands to
// go to both regions... (sigh)
// MainConsole.Instance.OutputFormat("Error - No item found with id {0}", itemId);
continue;
}
else
{
action(instance);
}
} }
} }
} }
@ -599,7 +605,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Queue eq = instance.EventQueue;
sb.AppendFormat("Script name : {0}\n", instance.ScriptName); sb.AppendFormat("Script name : {0}\n", instance.ScriptName);
sb.AppendFormat("Status : {0}\n", status); sb.AppendFormat("Status : {0}\n", status);
@ -1479,7 +1484,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_MaxScriptQueue = maxScriptQueue; m_MaxScriptQueue = maxScriptQueue;
STPStartInfo startInfo = new STPStartInfo(); STPStartInfo startInfo = new STPStartInfo();
startInfo.IdleTimeout = idleTimeout*1000; // convert to seconds as stated in .ini startInfo.ThreadPoolName = "XEngine";
startInfo.IdleTimeout = idleTimeout * 1000; // convert to seconds as stated in .ini
startInfo.MaxWorkerThreads = maxThreads; startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = minThreads; startInfo.MinWorkerThreads = minThreads;
startInfo.ThreadPriority = threadPriority;; startInfo.ThreadPriority = threadPriority;;
@ -1487,7 +1493,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
startInfo.StartSuspended = true; startInfo.StartSuspended = true;
m_ThreadPool = new SmartThreadPool(startInfo); m_ThreadPool = new SmartThreadPool(startInfo);
m_ThreadPool.Name = "XEngine";
} }
// //

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